MaCh3  2.6.0
Reference Guide
Functions
UmbrellaPlotting.cpp File Reference
#include "Fitters/MCMCProcessor.h"
#include "Manager/Manager.h"
#include "Fitters/MulticanonicalMCMCHandler.h"
#include "TSystem.h"
#include "TChain.h"
#include "TSystemDirectory.h"
Include dependency graph for UmbrellaPlotting.cpp:

Go to the source code of this file.

Functions

void UmbrellaPlotting (std::string InputFile, std::string outputDir)
 
int main (int argc, char *argv[])
 

Detailed Description

Author
David Riley

Definition in file UmbrellaPlotting.cpp.

Function Documentation

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 1914 of file UmbrellaPlotting.cpp.

1914  {
1915  std::string InputFile = argv[1];
1916  std::string outputDir = argv[2];
1917 
1918  UmbrellaPlotting(InputFile, outputDir);
1919  return 0;
1920 }
void UmbrellaPlotting(std::string InputFile, std::string outputDir)

◆ UmbrellaPlotting()

void UmbrellaPlotting ( std::string  InputFile,
std::string  outputDir 
)

Definition at line 24 of file UmbrellaPlotting.cpp.

25  {
26  // ============================================
27  // Configuration: Number of bins for each variable
28  // ============================================
29  int nBins_dcp = 200; // Number of bins for delta_cp
30  int nBins_sin2th23 = 100; // Number of bins for sin^2(theta_23)
31  int nBins_sin2th13 = 100;
32  int nBins_sin2th12 = 100;
33  int nBins_delm23 = 3000; // Number of bins for delta_m23^2
34  int nBins_delm12 = 100;
35 
36  // Unified plotting ranges requested for all sin2th23 and delm23 views.
37  double sin2th23_min = 0.45;
38  double sin2th23_max = 0.57;
39  double sin2th13_min = 0.015;
40  double sin2th13_max = 0.03;
41  double sin2th12_min = 0.24;
42  double sin2th12_max = 0.36;
43  double delm23_no_min = 0.00245;
44  double delm23_no_max = 0.0026;
45  double delm23_io_min = -delm23_no_max;
46  double delm23_io_max = -delm23_no_min;
47  double delm12_min = 6.5e-5;
48  double delm12_max = 8.5e-5;
49 
50  auto normalizeHist = [&](TH1 *h) {
51  if (!h) return;
52  double integral = h->Integral();
53  if (integral > 0.0) h->Scale(1.0 / integral);
54  };
55 
56  auto printHistStats = [&](const std::string &label, TH1 *h) {
57  if (!h) {
58  std::cout << "[DEBUG] " << label << " : histogram is null" << std::endl;
59  return;
60  }
61  std::cout << std::fixed << std::setprecision(6)
62  << "[DEBUG] " << label
63  << " entries=" << h->GetEntries()
64  << " effEntries=" << h->GetEffectiveEntries()
65  << " integral=" << h->Integral()
66  << " mean=" << h->GetMean()
67  << " rms=" << h->GetRMS()
68  << std::endl;
69  };
70 
71  TFile *f = TFile::Open(InputFile.c_str());
72 
73  // ============================================
74  // Open the ROOT file
75  // ===========================================
76  // T2K regerence posterior input
77  //std::string referenceFile = "/home/driley/projects/def-blairt2k/driley/T2K_outputs/reduce_AsimovA22Fit_070723_FinalChain.root";
78  //std::string referenceTreeName = "osc_posteriors";
79  bool use_reference_chain = false;
80  bool use_reference_LLH_scan = true;
81 
82  double asimovSin2th12 = std::numeric_limits<double>::quiet_NaN();
83  double asimovSin2th23 = std::numeric_limits<double>::quiet_NaN();
84  double asimovSin2th13 = std::numeric_limits<double>::quiet_NaN();
85  double asimovDelm12 = std::numeric_limits<double>::quiet_NaN();
86  double asimovDelm23 = std::numeric_limits<double>::quiet_NaN();
87  double asimovDcp = std::numeric_limits<double>::quiet_NaN();
88  double asimovBaseline = std::numeric_limits<double>::quiet_NaN();
89  double asimovElectronDensity = std::numeric_limits<double>::quiet_NaN();
90 
91  // load in reference chain
92  std::string referenceFile = "";
93  std::string referenceTreeName = "";
94 
95  if(use_reference_chain) {
96  referenceFile = "/home/driley/projects/def-blairt2k/driley/T2K_outputs/umbrella_testing/fixing_params/allfixed/all_oscParams_fixed_hadded.root";
97  referenceTreeName = "posteriors";
98  }
99 
100  // setup reference LLH scan input (works best when fit is only over the required variables)
101  std::string referenceLLHScanFile = "";
102  std::string referenceLLHScanDcpHistName = "";
103  std::string referenceLLHScanDcp_NO_histName = "";
104  std::string referenceLLHScanDcp_IO_histName = "";
105 
106  if(use_reference_LLH_scan) {
107  referenceLLHScanFile = "/home/driley/Projects-T2K-MaCh3/umbrella_t2k/mcm_utils/target_1D_histos.root";
108  referenceLLHScanDcpHistName = "dcp";
109  referenceLLHScanDcp_NO_histName = "dcp_NO";
110  referenceLLHScanDcp_IO_histName = "dcp_IO";
111  }
112 
113  // begin loading in fit file
114  if (!f || f->IsZombie()) {
115  std::cerr << "Error: Cannot open file!" << std::endl;
116  return;
117  }
118 
119  // Get the tree
120  TTree *tree = (TTree*)f->Get("posteriors"); // Update with your tree name
121  if (!tree) {
122  std::cerr << "Error: Cannot find tree!" << std::endl;
123  return;
124  }
125 
126  // Read Asimov metadata from MaCh3_Config YAML in the raw chain file.
127  // this is for plotting of asimov point
128  bool isAsimovChain = false;
129 
130  auto calculateJarlskog = [&](double sin2th12, double sin2th23, double sin2th13, double dcp) {
131  if (!std::isfinite(sin2th12) || !std::isfinite(sin2th23) || !std::isfinite(sin2th13) || !std::isfinite(dcp)) {
132  return std::numeric_limits<double>::quiet_NaN();
133  }
134  return std::sqrt(std::max(0.0, sin2th12) * std::max(0.0, 1.0 - sin2th12) *
135  std::max(0.0, sin2th23) * std::max(0.0, 1.0 - sin2th23)) *
136  std::max(0.0, 1.0 - sin2th13) * std::sqrt(std::max(0.0, sin2th13)) *
137  std::sin(dcp);
138  };
139 
140  const double asimovJarlskog = calculateJarlskog(asimovSin2th12, asimovSin2th23, asimovSin2th13, asimovDcp);
141 
142  auto drawAsimovLineWithLegend = [&](double xValue, TLegend *existingLegend = nullptr) {
143  if (!isAsimovChain || !std::isfinite(xValue) || !gPad) return;
144  gPad->Update();
145  const double yMin = gPad->GetUymin();
146  const double yMax = gPad->GetUymax();
147  TLine *asimovLine = new TLine(xValue, yMin, xValue, yMax);
148  asimovLine->SetLineColor(kBlack);
149  asimovLine->SetLineStyle(3);
150  asimovLine->SetLineWidth(2);
151  asimovLine->Draw("SAME");
152 
153  if (existingLegend) {
154  existingLegend->AddEntry(asimovLine, "Asimov point", "l");
155  existingLegend->Draw();
156  } else {
157  TLegend *asimovLegend = new TLegend(0.14, 0.83, 0.34, 0.91);
158  asimovLegend->SetBorderSize(0);
159  asimovLegend->SetFillStyle(0);
160  asimovLegend->AddEntry(asimovLine, "Asimov point", "l");
161  asimovLegend->Draw();
162  }
163  };
164 
165  auto drawAsimovMarkerWithLegend = [&](double xValue, double yValue, TLegend *existingLegend = nullptr) {
166  if (!isAsimovChain || !std::isfinite(xValue) || !std::isfinite(yValue) || !gPad) return;
167  gPad->Update();
168  // small cross sized relative to the axis ranges
169  const double xMin = gPad->GetUxmin();
170  const double xMax = gPad->GetUxmax();
171  const double yMin = gPad->GetUymin();
172  const double yMax = gPad->GetUymax();
173  const double dx = 0.012 * (xMax - xMin);
174  const double dy = 0.012 * (yMax - yMin);
175 
176  TLine *l1 = new TLine(xValue - dx, yValue - dy, xValue + dx, yValue + dy);
177  l1->SetLineColor(kRed);
178  l1->SetLineWidth(2);
179  l1->Draw("SAME");
180 
181  TLine *l2 = new TLine(xValue - dx, yValue + dy, xValue + dx, yValue - dy);
182  l2->SetLineColor(kRed);
183  l2->SetLineWidth(2);
184  l2->Draw("SAME");
185 
186  if (existingLegend) {
187  existingLegend->AddEntry(l1, "Asimov point", "l");
188  existingLegend->Draw();
189  } else {
190  TLegend *lm = new TLegend(0.14, 0.83, 0.34, 0.91);
191  lm->SetBorderSize(0);
192  lm->SetFillStyle(0);
193  lm->AddEntry(l1, "Asimov point", "l");
194  lm->Draw();
195  }
196  };
197 
198 
199  // Build reference histograms from osc_posteriors for direct overlay.
200  bool hasReferenceDcp = false;
201  bool hasReferenceSin2Th23 = false;
202  bool hasReferenceDelm23 = false;
203  TH1D *hRefDcpShape = nullptr;
204  TH1D *hRefSin2Th23Shape = nullptr;
205  TH1D *hRefDelm23Shape = nullptr;
206  TFile *fRef = TFile::Open(referenceFile.c_str());
207  if (!fRef || fRef->IsZombie()) {
208  std::cerr << "Warning: Cannot open reference file: " << referenceFile << std::endl;
209  } else {
210  TTree *refTree = (TTree*)fRef->Get(referenceTreeName.c_str());
211  if (!refTree) {
212  std::cerr << "Warning: Cannot find reference tree: " << referenceTreeName << std::endl;
213  } else if (!refTree->GetBranch("dcp") && !refTree->GetBranch("delta_cp")) {
214  std::cerr << "Warning: dcp branch not found in reference tree." << std::endl;
215  } else if (refTree->GetBranch("delta_cp")) {
216  hRefDcpShape = new TH1D("hRefDcpShape", "", nBins_dcp, -3.1415, 3.1415);
217  refTree->Draw("delta_cp>>hRefDcpShape", "", "goff");
218  hRefDcpShape->SetDirectory(0);
219  hasReferenceDcp = true;
220  std::cout << "Loaded reference dcp histogram from " << referenceTreeName << std::endl;
221  } else {
222  hRefDcpShape = new TH1D("hRefDcpShape", "", nBins_dcp, -3.1415, 3.1415);
223  refTree->Draw("dcp>>hRefDcpShape", "", "goff");
224  hRefDcpShape->SetDirectory(0);
225  hasReferenceDcp = true;
226  std::cout << "Loaded reference dcp histogram from " << referenceTreeName << std::endl;
227  }
228 
229  if (!refTree->GetBranch("theta23") && !refTree->GetBranch("sin2th_23")) {
230  std::cerr << "Warning: theta23 branch not found in reference tree." << std::endl;
231  } else if (refTree->GetBranch("sin2th_23")) {
232  hRefSin2Th23Shape = new TH1D("hRefSin2Th23Shape", "", nBins_sin2th23, sin2th23_min, sin2th23_max);
233  refTree->Draw("sin2th_23>>hRefSin2Th23Shape", "", "goff");
234  hRefSin2Th23Shape->SetDirectory(0);
235  hasReferenceSin2Th23 = true;
236  std::cout << "Loaded reference sin^{2}(theta23) histogram from " << referenceTreeName << std::endl;
237 
238  } else {
239  hRefSin2Th23Shape = new TH1D("hRefSin2Th23Shape", "", nBins_sin2th23, sin2th23_min, sin2th23_max);
240  refTree->Draw("theta23>>hRefSin2Th23Shape", "", "goff");
241  hRefSin2Th23Shape->SetDirectory(0);
242  hasReferenceSin2Th23 = true;
243  std::cout << "Loaded reference sin^{2}(theta23) histogram from " << referenceTreeName << std::endl;
244  }
245 
246  if (!refTree->GetBranch("dm23") && !refTree->GetBranch("delm2_23")) {
247  std::cerr << "Warning: dm23 branch not found in reference tree." << std::endl;
248  } else if (refTree->GetBranch("delm2_23")) {
249  hRefDelm23Shape = new TH1D("hRefDelm23Shape", "", nBins_delm23, delm23_io_min, delm23_no_max);
250  refTree->Draw("delm2_23>>hRefDelm23Shape", "", "goff");
251  TH1D *hRefDelm23IO_dbg = new TH1D("hRefDelm23IO_dbg", "", nBins_delm23, delm23_io_min, delm23_io_max);
252  TH1D *hRefDelm23NO_dbg = new TH1D("hRefDelm23NO_dbg", "", nBins_delm23, delm23_no_min, delm23_no_max);
253  refTree->Draw("delm2_23>>hRefDelm23IO_dbg", "delm2_23 < 0", "goff");
254  refTree->Draw("delm2_23>>hRefDelm23NO_dbg", "delm2_23 > 0", "goff");
255  printHistStats("Reference delm23 IO (raw)", hRefDelm23IO_dbg);
256  printHistStats("Reference delm23 NO (raw)", hRefDelm23NO_dbg);
257  hRefDelm23Shape->SetDirectory(0);
258  normalizeHist(hRefDelm23Shape);
259  hasReferenceDelm23 = true;
260  std::cout << "Loaded reference delm23 histogram from " << referenceTreeName << std::endl;
261 
262  } else {
263  hRefDelm23Shape = new TH1D("hRefDelm23Shape", "", nBins_delm23, delm23_io_min, delm23_no_max);
264  refTree->Draw("dm23>>hRefDelm23Shape", "", "goff");
265  TH1D *hRefDm23IO_dbg = new TH1D("hRefDm23IO_dbg", "", nBins_delm23, delm23_io_min, delm23_io_max);
266  TH1D *hRefDm23NO_dbg = new TH1D("hRefDm23NO_dbg", "", nBins_delm23, delm23_no_min, delm23_no_max);
267  refTree->Draw("dm23>>hRefDm23IO_dbg", "dm23 < 0", "goff");
268  refTree->Draw("dm23>>hRefDm23NO_dbg", "dm23 > 0", "goff");
269  printHistStats("Reference dm23 IO (raw)", hRefDm23IO_dbg);
270  printHistStats("Reference dm23 NO (raw)", hRefDm23NO_dbg);
271  hRefDelm23Shape->SetDirectory(0);
272  normalizeHist(hRefDelm23Shape);
273  hasReferenceDelm23 = true;
274  std::cout << "Loaded reference dm23 histograms from " << referenceTreeName << std::endl;
275  }
276  fRef->Close();
277  }
278 
279  // --------------------------------------------------
280  // Load LLH-scan reference histograms (linear likelihoods)
281  // --------------------------------------------------
282  bool hasReferenceLLH_Dcp = false;
283  bool hasReferenceLLH_Dcp_NO = false;
284  bool hasReferenceLLH_Dcp_IO = false;
285  TH1D *hRefLLH_Dcp = nullptr;
286  TH1D *hRefLLH_Dcp_NO = nullptr;
287  TH1D *hRefLLH_Dcp_IO = nullptr;
288 
289  if (use_reference_LLH_scan && !referenceLLHScanFile.empty()) {
290  TFile *fLLH = TFile::Open(referenceLLHScanFile.c_str());
291  if (!fLLH || fLLH->IsZombie()) {
292  std::cerr << "Warning: Cannot open reference LLH-scan file: " << referenceLLHScanFile << std::endl;
293  } else {
294  // Generic getter that leaves a cloned, detached, normalized histogram
295  auto loadAndNormalize1D = [&](const std::string &name, TH1D *&outHist, bool &outFlag) {
296  TObject *obj = fLLH->Get(name.c_str());
297  if (!obj) {
298  std::cerr << "Warning: LLH-scan histogram '" << name << "' not found in " << referenceLLHScanFile << std::endl;
299  return;
300  }
301  TH1 *htmp = dynamic_cast<TH1*>(obj);
302  if (!htmp) {
303  std::cerr << "Warning: Object '" << name << "' is not a histogram." << std::endl;
304  return;
305  }
306  outHist = (TH1D*)htmp->Clone((std::string("hRefLLH_") + name + "_clone").c_str());
307  outHist->SetDirectory(0);
308  // LLH scans are likelihoods (not log-likelihood) so area-normalize for overlay
309  double I = outHist->Integral();
310  if (I > 0) outHist->Scale(1.0 / I);
311  outFlag = true;
312  printHistStats(std::string("Reference LLH ") + name, outHist);
313  };
314 
315  if (!referenceLLHScanDcpHistName.empty()) loadAndNormalize1D(referenceLLHScanDcpHistName, hRefLLH_Dcp, hasReferenceLLH_Dcp);
316  if (!referenceLLHScanDcp_NO_histName.empty()) loadAndNormalize1D(referenceLLHScanDcp_NO_histName, hRefLLH_Dcp_NO, hasReferenceLLH_Dcp_NO);
317  if (!referenceLLHScanDcp_IO_histName.empty()) loadAndNormalize1D(referenceLLHScanDcp_IO_histName, hRefLLH_Dcp_IO, hasReferenceLLH_Dcp_IO);
318 
319  fLLH->Close();
320  }
321  }
322 
323  bool hasUmbrellaWeight = false;
324  // check if the umbrella weight branch exists
325  if (!tree->GetBranch("umbrella_weight")) {
326  std::cerr << "Warning: umbrella_weight branch not found, plotting as unweighted umbrella" << std::endl;
327  } else {
328  hasUmbrellaWeight = true;
329  }
330 
331 
332 
333  std::cout << "[DEBUG] Chain entries total=" << tree->GetEntries() << std::endl;
334  TH1D *hChainDelm23IO_dbg = new TH1D("hChainDelm23IO_dbg", "", nBins_delm23, delm23_io_min, delm23_io_max);
335  TH1D *hChainDelm23NO_dbg = new TH1D("hChainDelm23NO_dbg", "", nBins_delm23, delm23_no_min, delm23_no_max);
336  tree->Draw("delm2_23>>hChainDelm23IO_dbg", "delm2_23 < 0", "goff");
337  tree->Draw("delm2_23>>hChainDelm23NO_dbg", "delm2_23 > 0", "goff");
338  printHistStats("Chain delm23 IO (raw)", hChainDelm23IO_dbg);
339  printHistStats("Chain delm23 NO (raw)", hChainDelm23NO_dbg);
340 
341  if (hasUmbrellaWeight) {
342  TH1D *hChainDelm23IO_w_dbg = new TH1D("hChainDelm23IO_w_dbg", "", nBins_delm23, delm23_io_min, delm23_io_max);
343  TH1D *hChainDelm23NO_w_dbg = new TH1D("hChainDelm23NO_w_dbg", "", nBins_delm23, delm23_no_min, delm23_no_max);
344  tree->Draw("delm2_23>>hChainDelm23IO_w_dbg", "(delm2_23 < 0) * umbrella_weight", "goff");
345  tree->Draw("delm2_23>>hChainDelm23NO_w_dbg", "(delm2_23 > 0) * umbrella_weight", "goff");
346  printHistStats("Chain delm23 IO (weighted)", hChainDelm23IO_w_dbg);
347  printHistStats("Chain delm23 NO (weighted)", hChainDelm23NO_w_dbg);
348  }
349 
350  // Create canvas
351  TCanvas *c1 = new TCanvas("c1", "Delta CP vs Delta m23", 900, 700);
352  c1->SetRightMargin(0.15);
353  // Create 2D histogram: delta m23 vs delta CP
354  TH2F *h2d = new TH2F("h2d", "#Delta m_{23}^{2} vs #delta_{CP};#delta_{CP};#Delta m_{23}^{2} (eV^{2})",
355  nBins_dcp, -3.14159, 3.14159,
356  nBins_delm23, delm23_io_min, delm23_no_max);
357  // Draw from tree
358  tree->Draw("delm2_23:delta_cp>>h2d", "", "COLZ");
359  normalizeHist(h2d);
360  h2d->Draw("COLZ");
361  // Styling
362  h2d->SetStats(0); // Remove stats box
363  h2d->GetXaxis()->SetTitle("#delta_{CP}");
364  h2d->GetYaxis()->SetTitle("#Delta m_{23}^{2} (eV^{2})");
365  h2d->GetZaxis()->SetTitle("Normalized Entries");
366  //gPad->SetLogz(); // Optional: log scale on z-axis for better visibility
367  c1->Update();
368  // draw Asimov marker and Save plot
369  drawAsimovMarkerWithLegend(asimovDcp, asimovDelm23, nullptr);
370  c1->SaveAs((outputDir + "delm23_vs_dcp.png").c_str());
371 
372  if (hasUmbrellaWeight){
373  tree->Draw("delm2_23:delta_cp>>h2d", "umbrella_weight", "COLZ");
374  normalizeHist(h2d);
375  h2d->Draw("COLZ");
376  c1->Update();
377  drawAsimovMarkerWithLegend(asimovDcp, asimovDelm23, nullptr);
378  c1->SaveAs((outputDir + "delm23_vs_dcp_weighted.png").c_str());
379  }
380 
381 
382  // Create side-by-side canvas for IO (left) and NO (right)
383  TCanvas *c2 = new TCanvas("c2", "Delta m23 IO and NO", 1800, 700);
384  c2->Divide(2, 1);
385 
386  // Left pad: IO
387  c2->cd(1);
388  gPad->SetRightMargin(0.15);
389  // Create 2D histogram: dcp vs delm23
390  TH2F *h2d_IO = new TH2F("h2d_IO", "Inverted Ordering;#Delta m_{23}^{2} (eV^{2});#delta_{CP}",
391  nBins_delm23, delm23_io_min, delm23_io_max, // X-axis: delm23 (IO range)
392  nBins_dcp, -3.1415, 3.1415); // Y-axis: dcp
393  // Draw from tree
394  tree->Draw("delta_cp:delm2_23>>h2d_IO", "delm2_23 < 0", "COLZ");
395  normalizeHist(h2d_IO);
396  h2d_IO->Draw("COLZ");
397  // Styling
398  h2d_IO->SetStats(0);
399  h2d_IO->GetXaxis()->SetTitle("#Delta m_{23}^{2} (eV^{2})");
400  h2d_IO->GetYaxis()->SetTitle("#delta_{CP} / #pi");
401  h2d_IO->GetZaxis()->SetTitle("Normalized Entries");
402  //gPad->SetLogz();
403 
404  // Right pad: NO
405  c2->cd(2);
406  gPad->SetRightMargin(0.15);
407  // Create 2D histogram: dcp vs delm23
408  TH2F *h2d_NO = new TH2F("h2d_NO", "Normal Ordering;#Delta m_{23}^{2} (eV^{2});#delta_{CP}",
409  nBins_delm23, delm23_no_min, delm23_no_max, // X-axis: delm23 (NO range)
410  nBins_dcp, -3.1415, 3.1415); // Y-axis: dcp
411  // Draw from tree
412  tree->Draw("delta_cp:delm2_23>>h2d_NO", "delm2_23 > 0", "COLZ");
413  normalizeHist(h2d_NO);
414  h2d_NO->Draw("COLZ");
415  // Styling
416  h2d_NO->SetStats(0);
417  h2d_NO->GetXaxis()->SetTitle("#Delta m_{23}^{2} (eV^{2})");
418  h2d_NO->GetYaxis()->SetTitle("#delta_{CP} / #pi");
419  h2d_NO->GetZaxis()->SetTitle("Normalized Entries");
420  //gPad->SetLogz();
421 
422  // Update canvas
423  // draw Asimov markers per pad, update and save
424  c2->cd(1);
425  drawAsimovMarkerWithLegend(asimovDelm23, asimovDcp, nullptr);
426  c2->cd(2);
427  drawAsimovMarkerWithLegend(asimovDelm23, asimovDcp, nullptr);
428  c2->Update();
429  c2->SaveAs((outputDir + "delm23_vs_dcp_IO_NO.png").c_str());
430 
431  if (hasUmbrellaWeight){
432  // Left pad: IO weighted
433  c2->cd(1);
434  tree->Draw("delta_cp:delm2_23>>h2d_IO", "(delm2_23 < 0) * umbrella_weight", "COLZ");
435  normalizeHist(h2d_IO);
436  h2d_IO->Draw("COLZ");
437 
438  // Right pad: NO weighted
439  c2->cd(2);
440  tree->Draw("delta_cp:delm2_23>>h2d_NO", "(delm2_23 > 0) * umbrella_weight", "COLZ");
441  normalizeHist(h2d_NO);
442  h2d_NO->Draw("COLZ");
443 
444  // Update canvas
445  // draw Asimov markers per pad, update and save weighted
446  c2->cd(1);
447  drawAsimovMarkerWithLegend(asimovDelm23, asimovDcp, nullptr);
448  c2->cd(2);
449  drawAsimovMarkerWithLegend(asimovDelm23, asimovDcp, nullptr);
450  c2->Update();
451  c2->SaveAs((outputDir + "delm23_vs_dcp_IO_NO_weighted.png").c_str());
452  }
453 
454  // Lambda to calculate HPD contour level for given confidence level (0-1)
455  auto calculateHPDLevel = [](TH2F *hist, double confidenceLevel) -> double {
456  std::vector<double> binContents;
457  for (int i = 1; i <= hist->GetNbinsX(); ++i) {
458  for (int j = 1; j <= hist->GetNbinsY(); ++j) {
459  double content = hist->GetBinContent(i, j);
460  if (content > 0) {
461  binContents.push_back(content);
462  }
463  }
464  }
465 
466  if (binContents.empty()) return 0.0;
467 
468  std::sort(binContents.rbegin(), binContents.rend()); // Sort in descending order
469 
470  double totalSum = 0;
471  for (double val : binContents) totalSum += val;
472 
473  double targetSum = confidenceLevel * totalSum;
474  double runningSum = 0;
475 
476  for (double val : binContents) {
477  runningSum += val;
478  if (runningSum >= targetSum) {
479  return val;
480  }
481  }
482  return binContents.back();
483  };
484 
485  // Lambda to create wrapped histogram for circular delta_cp coordinate
486  auto createWrappedHistogram = [](TH2F *originalHist, const std::string &name) -> TH2F* {
487  int nBinsX = originalHist->GetNbinsX();
488  int nBinsY = originalHist->GetNbinsY();
489 
490  double xMin = originalHist->GetXaxis()->GetBinLowEdge(1);
491  double xMax = originalHist->GetXaxis()->GetBinUpEdge(nBinsX);
492  double yMin = originalHist->GetYaxis()->GetBinLowEdge(1);
493  double yMax = originalHist->GetYaxis()->GetBinUpEdge(nBinsY);
494  double yRange = yMax - yMin;
495 
496  // Extended histogram spanning 3 periods for proper boundary wrapping
497  TH2F *wrapped = new TH2F(name.c_str(), "",
498  nBinsX, xMin, xMax,
499  nBinsY * 3, yMin - yRange, yMax + yRange);
500  wrapped->SetDirectory(0);
501 
502  // Fill with periodically wrapped data
503  for (int i = 1; i <= nBinsX; ++i) {
504  for (int j = 1; j <= nBinsY; ++j) {
505  double content = originalHist->GetBinContent(i, j);
506  // Center period
507  wrapped->SetBinContent(i, j + nBinsY, content);
508  // Lower wrapped period
509  wrapped->SetBinContent(i, j, content);
510  // Upper wrapped period
511  wrapped->SetBinContent(i, j + 2*nBinsY, content);
512  }
513  }
514 
515  return wrapped;
516  };
517 
518  // Helper to compute one-sided bounds (do not draw) — integrates from side with more posterior
519  auto computeOneSidedBoundsValues = [&](TH1 *hist, double targets[3], double outBounds[3], bool &integrateLeftToRight) {
520  for (int i = 0; i < 3; ++i) outBounds[i] = std::numeric_limits<double>::quiet_NaN();
521  integrateLeftToRight = true;
522  if (!hist) return;
523  double total = hist->Integral();
524  if (total <= 0) return;
525  int nBins = hist->GetNbinsX();
526  double leftSum = 0.0;
527  double rightSum = 0.0;
528  for (int b = 1; b <= nBins; ++b) {
529  double center = hist->GetBinCenter(b);
530  double c = hist->GetBinContent(b);
531  if (center < 0) leftSum += c; else rightSum += c;
532  }
533  integrateLeftToRight = (leftSum >= rightSum);
534 
535  for (int t = 0; t < 3; ++t) {
536  double target = targets[t];
537  double cum = 0.0;
538  if (integrateLeftToRight) {
539  for (int b = 1; b <= nBins; ++b) {
540  cum += hist->GetBinContent(b);
541  if (cum / total >= target) {
542  outBounds[t] = hist->GetBinLowEdge(b+1);
543  break;
544  }
545  }
546  if (!std::isfinite(outBounds[t])) outBounds[t] = hist->GetXaxis()->GetXmax();
547  } else {
548  for (int b = nBins; b >= 1; --b) {
549  cum += hist->GetBinContent(b);
550  if (cum / total >= target) {
551  outBounds[t] = hist->GetBinLowEdge(b);
552  break;
553  }
554  }
555  if (!std::isfinite(outBounds[t])) outBounds[t] = hist->GetXaxis()->GetXmin();
556  }
557  }
558  };
559 
560  auto drawAbsDelm23Overlay = [&](const std::string &suffix,
561  const std::string &ioSelection,
562  const std::string &noSelection) {
563  const int nBinsAbsDelm23 = 80;
564  const int nBinsAbsDcp = 60;
565 
566  // Set reversed greyscale palette (grey to black) for this plot only
567  gStyle->SetPalette(kGreyScale);
568  TColor::InvertPalette();
569 
570  TCanvas *cAbs = new TCanvas(("c_abs_" + suffix).c_str(), "Delta m23 magnitude overlay", 900, 700);
571  cAbs->SetRightMargin(0.15);
572  cAbs->SetLeftMargin(0.12);
573  cAbs->SetBottomMargin(0.12);
574 
575  TH2F *hAbsFrame = new TH2F(("hAbsFrame_" + suffix).c_str(),
576  "|#Delta m_{23}^{2}| vs #delta_{CP};|#Delta m_{23}^{2}| (eV^{2});#delta_{CP} / #pi",
577  nBinsAbsDelm23, delm23_no_min, delm23_no_max,
578  nBinsAbsDcp, -3.1415, 3.1415);
579  hAbsFrame->SetStats(0);
580  hAbsFrame->Draw();
581 
582  TH2F *hAbsIO = new TH2F(("hAbsIO_" + suffix).c_str(), "",
583  nBinsAbsDelm23, delm23_no_min, delm23_no_max,
584  nBinsAbsDcp, -3.1415, 3.1415);
585  TH2F *hAbsNO = new TH2F(("hAbsNO_" + suffix).c_str(), "",
586  nBinsAbsDelm23, delm23_no_min, delm23_no_max,
587  nBinsAbsDcp, -3.1415, 3.1415);
588 
589  std::string ioDrawExpr = std::string("delta_cp:TMath::Abs(delm2_23)>>") + hAbsIO->GetName();
590  std::string noDrawExpr = std::string("delta_cp:TMath::Abs(delm2_23)>>") + hAbsNO->GetName();
591  tree->Draw(ioDrawExpr.c_str(), ioSelection.c_str(), "goff");
592  tree->Draw(noDrawExpr.c_str(), noSelection.c_str(), "goff");
593 
594  hAbsIO->SetDirectory(0);
595  hAbsNO->SetDirectory(0);
596  printHistStats("Abs delm23 IO", hAbsIO);
597  printHistStats("Abs delm23 NO", hAbsNO);
598 
599  // Calculate HPD contour levels for 1-sigma (68.3%)
600  double hpdLevel1SigmaNO = calculateHPDLevel(hAbsNO, 0.683);
601 
602  double hpdLevel1SigmaIO = calculateHPDLevel(hAbsIO, 0.683);
603 
604  // Draw density first
605  hAbsNO->SetLineColor(kAzure + 2);
606  hAbsNO->SetLineWidth(1);
607  hAbsNO->Draw("COLZ");
608 
609  hAbsIO->SetLineColor(kOrange + 7);
610  hAbsIO->SetLineWidth(1);
611  hAbsIO->Draw("COLZ SAME");
612 
613  // Smooth the fine histograms for contours
614  TH2F *hNO_contour = (TH2F *)hAbsNO->Clone("hNO_contour");
615  TH2F *hIO_contour = (TH2F *)hAbsIO->Clone("hIO_contour");
616  hNO_contour->SetDirectory(0);
617  hIO_contour->SetDirectory(0);
618 
619  hNO_contour->Smooth(1, "k5a");
620  hIO_contour->Smooth(1, "k5a");
621 
622  double noLevels[1] = {hpdLevel1SigmaNO};
623  hNO_contour->SetContour(1, noLevels);
624  hNO_contour->SetLineColor(kAzure + 2);
625  hNO_contour->SetLineWidth(3);
626  hNO_contour->Draw("CONT3 SAME");
627 
628  double ioLevels[1] = {hpdLevel1SigmaIO};
629  hIO_contour->SetContour(1, ioLevels);
630  hIO_contour->SetLineColor(kOrange + 7);
631  hIO_contour->SetLineWidth(3);
632  hIO_contour->Draw("CONT3 SAME");
633 
634  TLegend *legend = new TLegend(0.68, 0.73, 0.88, 0.91);
635  legend->SetBorderSize(0);
636  legend->SetFillStyle(0);
637  legend->AddEntry(hAbsNO, "NO 1#sigma", "l");
638  legend->AddEntry(hAbsIO, "IO 1#sigma", "l");
639  legend->Draw();
640  // add Asimov marker (abs of delm for this plot) and save
641  drawAsimovMarkerWithLegend(std::abs(asimovDelm23), asimovDcp, legend);
642  cAbs->Update();
643  cAbs->SaveAs((outputDir + "delm23_vs_dcp_abs_IO_NO" + suffix + ".png").c_str());
644  };
645 
646  drawAbsDelm23Overlay("", "delm2_23 < 0", "delm2_23 > 0");
647 
648  if (hasUmbrellaWeight) {
649  drawAbsDelm23Overlay("_weighted",
650  "(delm2_23 < 0) * umbrella_weight",
651  "(delm2_23 > 0) * umbrella_weight");
652  }
653 
654  // Reset palette to default for all other plots
655  gStyle->SetPalette(kBird);
656 
657  // Create canvas for Delta m23 NO vs sin^2(theta23)
658  TCanvas *c4 = new TCanvas("c4", "Delta m23 NO vs sin^{2}(#theta_{23})", 900, 700);
659  c4->SetRightMargin(0.15);
660  // Create 2D histogram: delta m23 vs delta CP
661  TH2F *h2d_sin = new TH2F("h2d_sin", "#Delta m_{23}^{2} vs sin^{2}(#theta_{23});sin^{2}(#theta_{23});#Delta m_{23}^{2} (eV^{2})",
662  nBins_sin2th23, sin2th23_min, sin2th23_max, nBins_delm23, delm23_no_min, delm23_no_max);
663  // Draw from tree (note: y:x format)
664  tree->Draw("delm2_23:sin2th_23>>h2d_sin", "delm2_23 > 0", "COLZ");
665  normalizeHist(h2d_sin);
666  h2d_sin->Draw("COLZ");
667  // Styling
668  h2d_sin->SetStats(0); // Remove stats box
669  h2d_sin->GetXaxis()->SetTitle("sin^{2}(#theta_{23})");
670  h2d_sin->GetYaxis()->SetTitle("#Delta m_{23}^{2} (eV^{2})");
671  h2d_sin->GetZaxis()->SetTitle("Normalized Entries");
672  //gPad->SetLogz(); // Optional: log scale on z-axis for better visibility
673  // Update canvas
674  // draw Asimov marker and Save plot
675  drawAsimovMarkerWithLegend(asimovSin2th23, asimovDelm23, nullptr);
676  c4->Update();
677  c4->SaveAs((outputDir + "th23_vs_delm23_no.png").c_str());
678 
679  if (hasUmbrellaWeight){
680  tree->Draw("delm2_23:sin2th_23>>h2d_sin", "(delm2_23 > 0) * umbrella_weight", "COLZ");
681  normalizeHist(h2d_sin);
682  h2d_sin->Draw("COLZ");
683  drawAsimovMarkerWithLegend(asimovSin2th23, asimovDelm23, nullptr);
684  c4->Update();
685  c4->SaveAs((outputDir + "th23_vs_delm23_no_weighted.png").c_str());
686  }
687 
688  // Create canvas for Delta m23 NO vs sin^2(theta23)
689  TCanvas *c5 = new TCanvas("c5", "Delta m23 IO vs sin^{2}(#theta_{23})", 900, 700);
690  c5->SetRightMargin(0.15);
691  // Create 2D histogram: delta m23 vs delta CP
692  TH2F *h2d_sin_IO = new TH2F("h2d_sin_IO", "#Delta m_{23}^{2} vs sin^{2}(#theta_{23});sin^{2}(#theta_{23});#Delta m_{23}^{2} (eV^{2})",
693  nBins_sin2th23, sin2th23_min, sin2th23_max,
694  nBins_delm23, delm23_io_min, delm23_io_max);
695  // Draw from tree (note: y:x format)
696  tree->Draw("delm2_23:sin2th_23>>h2d_sin_IO", "delm2_23 < 0", "COLZ");
697  normalizeHist(h2d_sin_IO);
698  h2d_sin_IO->Draw("COLZ");
699  // Styling
700  h2d_sin_IO->SetStats(0); // Remove stats box
701  h2d_sin_IO->GetXaxis()->SetTitle("sin^{2}(#theta_{23})");
702  h2d_sin_IO->GetYaxis()->SetTitle("#Delta m_{23}^{2} (eV^{2})");
703  h2d_sin_IO->GetZaxis()->SetTitle("Normalized Entries");
704  //gPad->SetLogz(); // Optional: log scale on z-axis for better visibility
705  // Update canvas
706  drawAsimovMarkerWithLegend(asimovSin2th23, asimovDelm23, nullptr);
707  c5->Update();
708  c5->SaveAs((outputDir + "th23_vs_delm23_io.png").c_str());
709 
710  if (hasUmbrellaWeight){
711  tree->Draw("delm2_23:sin2th_23>>h2d_sin_IO", "(delm2_23 < 0)*umbrella_weight", "COLZ");
712  normalizeHist(h2d_sin_IO);
713  h2d_sin_IO->Draw("COLZ");
714  drawAsimovMarkerWithLegend(asimovSin2th23, asimovDelm23, nullptr);
715  c5->Update();
716  c5->SaveAs((outputDir + "th23_vs_delm23_io_weighted.png").c_str());
717  }
718 
719  // Create canvas for delta_cp vs sin^2(theta23)
720  TCanvas *c6 = new TCanvas("c6", "Delta_cp vs sin^{2}(#theta_{23})", 900, 700);
721  c6->SetRightMargin(0.15);
722  // Create 2D histogram: delta m23 vs delta CP
723  TH2F *h2d_delta_cp = new TH2F("h2d_delta_cp", "#delta_{CP} vs sin^{2}(#theta_{23});#delta_{CP};sin^{2}(#theta_{23})",
724  nBins_dcp, -3.1415, 3.1415,
725  nBins_sin2th23, sin2th23_min, sin2th23_max);
726  // Draw from tree
727  tree->Draw("sin2th_23:delta_cp>>h2d_delta_cp", "", "COLZ");
728  normalizeHist(h2d_delta_cp);
729  h2d_delta_cp->Draw("COLZ");
730  // Styling
731  h2d_delta_cp->SetStats(0); // Remove stats box
732  h2d_delta_cp->GetYaxis()->SetTitle("sin^{2}(#theta_{23})");
733  h2d_delta_cp->GetXaxis()->SetTitle("#delta_{CP}");
734  h2d_delta_cp->GetZaxis()->SetTitle("Normalized Entries");
735  //gPad->SetLogz(); // Optional: log scale on z-axis for better visibility
736  // Update canvas
737  drawAsimovMarkerWithLegend(asimovDcp, asimovSin2th23, nullptr);
738  c6->Update();
739  c6->SaveAs((outputDir + "th23_vs_delta_cp.png").c_str());
740 
741  if (hasUmbrellaWeight){
742  tree->Draw("sin2th_23:delta_cp>>h2d_delta_cp", "umbrella_weight", "COLZ");
743  normalizeHist(h2d_delta_cp);
744  h2d_delta_cp->Draw("COLZ");
745  drawAsimovMarkerWithLegend(asimovDcp, asimovSin2th23, nullptr);
746  c6->Update();
747  c6->SaveAs((outputDir + "th23_vs_delta_cp_weighted.png").c_str());
748  }
749 
750  // Create canvas for delta_cp vs sin^2(theta13)
751  TCanvas *cth13 = new TCanvas("cth13", "Delta_cp vs sin^{2}(#theta_{13})", 900, 700);
752  cth13->SetRightMargin(0.15);
753  // Create 2D histogram: delta m23 vs delta CP
754  TH2F *h2d_delta_cp_13 = new TH2F("h2d_delta_cp_13", "#delta_{CP} vs sin^{2}(#theta_{13});#delta_{CP};sin^{2}(#theta_{13})",
755  nBins_dcp, -3.1415, 3.1415,
756  nBins_sin2th13, sin2th13_min, sin2th13_max);
757  // Draw from tree
758  tree->Draw("sin2th_13:delta_cp>>h2d_delta_cp_13", "", "COLZ");
759  normalizeHist(h2d_delta_cp_13);
760  h2d_delta_cp_13->Draw("COLZ");
761  // Styling
762  h2d_delta_cp_13->SetStats(0); // Remove stats box
763  h2d_delta_cp_13->GetYaxis()->SetTitle("sin^{2}(#theta_{13})");
764  h2d_delta_cp_13->GetXaxis()->SetTitle("#delta_{CP}");
765  h2d_delta_cp_13->GetZaxis()->SetTitle("Normalized Entries");
766  //gPad->SetLogz(); // Optional: log scale on z-axis for better visibility
767  // Update canvas
768  drawAsimovMarkerWithLegend(asimovDcp, asimovSin2th13, nullptr);
769  cth13->Update();
770  cth13->SaveAs((outputDir + "th13_vs_delta_cp.png").c_str());
771 
772  if (hasUmbrellaWeight){
773  tree->Draw("sin2th_13:delta_cp>>h2d_delta_cp_13", "umbrella_weight", "COLZ");
774  normalizeHist(h2d_delta_cp_13);
775  h2d_delta_cp_13->Draw("COLZ");
776  drawAsimovMarkerWithLegend(asimovDcp, asimovSin2th13, nullptr);
777  cth13->Update();
778  cth13->SaveAs((outputDir + "th13_vs_delta_cp_weighted.png").c_str());
779  }
780 
781  // plot 1D histograms of delta_cp, delm23, theta23, theta13, theta12, delm12
782  TCanvas *c7 = new TCanvas("c7", "1D Histograms", 3200, 1200);
783  c7->Divide(4, 2);
784 
785  // delta_cp unweighted
786  c7->cd(1);
787  TH1F *h1d_delta_cp_unweighted = new TH1F("h1d_delta_cp_unweighted", "#delta_{CP};#delta_{CP};Entries", nBins_dcp, -3.1415, 3.1415);
788  tree->Draw("delta_cp>>h1d_delta_cp_unweighted", "", "");
789  normalizeHist(h1d_delta_cp_unweighted);
790  h1d_delta_cp_unweighted->SetMinimum(0);
791  h1d_delta_cp_unweighted->SetStats(0);
792  h1d_delta_cp_unweighted->Draw("HIST");
793  h1d_delta_cp_unweighted->Draw("E SAME");
794  drawAsimovLineWithLegend(asimovDcp);
795 
796  c7->cd(2);
797  TH1F *h1d_delm23_all_unweighted = new TH1F("h1d_delm23_all_unweighted", "#Delta m_{23}^{2} (full range);#Delta m_{23}^{2} (eV^{2});Normalized Entries", nBins_delm23, delm23_io_min, delm23_no_max);
798  tree->Draw("delm2_23>>h1d_delm23_all_unweighted", "", "");
799  normalizeHist(h1d_delm23_all_unweighted);
800  h1d_delm23_all_unweighted->SetStats(0);
801  h1d_delm23_all_unweighted->Draw("HIST");
802  h1d_delm23_all_unweighted->Draw("E SAME");
803  drawAsimovLineWithLegend(asimovDelm23);
804 
805  c7->cd(3);
806  TH1F *h1d_delm23_unweighted = (TH1F*)h1d_delm23_all_unweighted->Clone("h1d_delm23_IO_unweighted");
807  h1d_delm23_unweighted->SetTitle("#Delta m_{23}^{2} (IO)");
808  h1d_delm23_unweighted->SetStats(0);
809  h1d_delm23_unweighted->GetXaxis()->SetRangeUser(delm23_io_min, delm23_io_max);
810  h1d_delm23_unweighted->Draw("HIST");
811  h1d_delm23_unweighted->Draw("E SAME");
812  drawAsimovLineWithLegend(asimovDelm23);
813 
814  c7->cd(4);
815  TH1F *h1d_delm23_NO_unweighted = (TH1F*)h1d_delm23_all_unweighted->Clone("h1d_delm23_NO_unweighted");
816  h1d_delm23_unweighted->SetTitle("#Delta m_{23}^{2} (NO)");
817  h1d_delm23_NO_unweighted->SetStats(0);
818  h1d_delm23_NO_unweighted->GetXaxis()->SetRangeUser(delm23_no_min, delm23_no_max);
819  h1d_delm23_NO_unweighted->Draw("HIST");
820  h1d_delm23_NO_unweighted->Draw("E SAME");
821  drawAsimovLineWithLegend(asimovDelm23);
822 
823  c7->cd(5);
824  TH1F *h1d_sinth23_unweighted = new TH1F("h1d_sinth23_unweighted", "sin^{2}(#theta_{23});sin^{2}(#theta_{23});Entries", nBins_sin2th23, sin2th23_min, sin2th23_max);
825  tree->Draw("sin2th_23>>h1d_sinth23_unweighted", "", "");
826  normalizeHist(h1d_sinth23_unweighted);
827  h1d_sinth23_unweighted->SetStats(0);
828  h1d_sinth23_unweighted->Draw("HIST");
829  h1d_sinth23_unweighted->Draw("E SAME");
830  drawAsimovLineWithLegend(asimovSin2th23);
831 
832  c7->cd(6);
833  TH1F *h1d_sinth13_unweighted = new TH1F("h1d_sinth13_unweighted", "sin^{2}(#theta_{13});sin^{2}(#theta_{13});Entries", nBins_sin2th13, sin2th13_min, sin2th13_max);
834  tree->Draw("sin2th_13>>h1d_sinth13_unweighted", "", "");
835  normalizeHist(h1d_sinth13_unweighted);
836  h1d_sinth13_unweighted->SetStats(0);
837  h1d_sinth13_unweighted->Draw("HIST");
838  h1d_sinth13_unweighted->Draw("E SAME");
839  drawAsimovLineWithLegend(asimovSin2th13);
840 
841  c7->cd(7);
842  TH1F *h1d_sinth12_unweighted = new TH1F("h1d_sinth12_unweighted", "sin^{2}(#theta_{12});sin^{2}(#theta_{12});Entries", nBins_sin2th12, sin2th12_min, sin2th12_max);
843  tree->Draw("sin2th_12>>h1d_sinth12_unweighted", "", "");
844  normalizeHist(h1d_sinth12_unweighted);
845  h1d_sinth12_unweighted->SetStats(0);
846  h1d_sinth12_unweighted->Draw("HIST");
847  h1d_sinth12_unweighted->Draw("E SAME");
848  drawAsimovLineWithLegend(asimovSin2th12);
849 
850  c7->cd(8);
851  TH1F *h1d_delm12_unweighted = new TH1F("h1d_delm12_unweighted", "#Delta m_{12}^{2};#Delta m_{12}^{2} (eV^{2});Entries", nBins_delm12, delm12_min, delm12_max);
852  tree->Draw("delm2_12>>h1d_delm12_unweighted", "", "");
853  normalizeHist(h1d_delm12_unweighted);
854  h1d_delm12_unweighted->SetStats(0);
855  h1d_delm12_unweighted->Draw("HIST");
856  h1d_delm12_unweighted->Draw("E SAME");
857  drawAsimovLineWithLegend(asimovDelm12);
858 
859  c7->Update();
860  c7->SaveAs((outputDir + "1D_histograms_unweighted.png").c_str());
861 
862  // Now create weighted version with sigma calculations
863  TCanvas *c7_weighted = new TCanvas("c7_weighted", "1D Histograms Weighted", 3200, 1200);
864  c7_weighted->Divide(4, 2);
865 
866  // delta_cp weighted
867  c7_weighted->cd(1);
868  TH1F *h1d_delta_cp = new TH1F("h1d_delta_cp", "#delta_{CP};#delta_{CP};Weighted Entries", nBins_dcp, -3.1415, 3.1415);
869  if (hasUmbrellaWeight) {
870  tree->Draw("delta_cp>>h1d_delta_cp", "umbrella_weight", "");
871  } else {
872  tree->Draw("delta_cp>>h1d_delta_cp", "", "");
873  std::cout << "WARNING:::::No umbrella weights found, plotting unweighted delta_cp histogram." << std::endl;
874  }
875  normalizeHist(h1d_delta_cp);
876  h1d_delta_cp->SetStats(0);
877  h1d_delta_cp->Sumw2(); // Enable proper error calculation for weighted histograms
878  h1d_delta_cp->Draw("HIST");
879  h1d_delta_cp->Draw("E SAME");
880 
881 
882 
883  // add 1,3,5 sigma contour lines
884  double sigma1 = 0.6827;
885  double sigma3 = 0.9973;
886  double sigma5 = 0.99994;
887 
888  double sigma1_upper;
889  double sigma1_lower;
890  double sigma3_upper;
891  double sigma3_lower;
892  double sigma5_upper;
893  double sigma5_lower;
894 
895  // Find the HPD (Highest Posterior Density) bin
896  int hpdBin = 1;
897  double maxEntries = 0;
898  for (int j = 1; j <= h1d_delta_cp->GetNbinsX(); j++) {
899  if (h1d_delta_cp->GetBinContent(j) > maxEntries) {
900  maxEntries = h1d_delta_cp->GetBinContent(j);
901  hpdBin = j;
902  }
903  }
904 
905  std::cout << "HPD bin: " << hpdBin << " with " << maxEntries << " entries" << std::endl;
906 
907  // Create a copy of the histogram to work with
908  TH1F *h1d_copy = (TH1F*)h1d_delta_cp->Clone("h1d_copy");
909 
910  // Track which bins have been included
911  std::vector<bool> included(h1d_delta_cp->GetNbinsX() + 1, false);
912  included[hpdBin] = true;
913 
914  // Helper function to find contiguous regions with a gap tolerance
915  auto findContiguousRegions = [&](int gapTolerance = 2) {
916  std::vector<std::pair<int, int>> regions; // pairs of (lower_bin, upper_bin)
917 
918  int start = -1;
919  int gapCount = 0;
920 
921  for (int k = 1; k <= h1d_delta_cp->GetNbinsX(); k++) {
922  if (included[k]) {
923  if (start == -1) {
924  start = k; // start of a new region
925  }
926  gapCount = 0; // reset gap counter
927  } else if (start != -1) {
928  gapCount++;
929  if (gapCount > gapTolerance) {
930  // end of region
931  regions.push_back({start, k - gapCount});
932  start = -1;
933  gapCount = 0;
934  }
935  }
936  }
937 
938  // Close last region if it exists
939  if (start != -1) {
940  regions.push_back({start, h1d_delta_cp->GetNbinsX()});
941  }
942 
943  return regions;
944  };
945 
946  // loop over the bins from highest to lowest summing the bins until we reach the desired sigma levels
947  double totalEntries = h1d_delta_cp->Integral();
948  double sumEntries = h1d_delta_cp->GetBinContent(hpdBin);
949  double level1 = 0;
950  double level3 = 0;
951  double level5 = 0;
952 
953  std::vector<std::pair<double, double>> sigma1_regions;
954  std::vector<std::pair<double, double>> sigma3_regions;
955  std::vector<std::pair<double, double>> sigma5_regions;
956 
957  for (int i = 0; i < h1d_delta_cp->GetNbinsX(); i++) {
958  // find the bin with the highest entries that hasn't been included yet
959  int maxBin = 0;
960  double maxBinEntries = 0;
961  for (int j = 1; j <= h1d_delta_cp->GetNbinsX(); j++) {
962  if (!included[j] && h1d_copy->GetBinContent(j) > maxBinEntries) {
963  maxBinEntries = h1d_copy->GetBinContent(j);
964  maxBin = j;
965  }
966  }
967 
968  if (maxBin == 0) break; // no more bins to add
969 
970  sumEntries += maxBinEntries;
971  included[maxBin] = true;
972  double frac = sumEntries / totalEntries;
973 
974  std::cout << "Added bin " << maxBin << " with " << maxBinEntries << " entries. Fraction: " << frac << std::endl;
975 
976  if (frac >= sigma1 && level1 == 0) {
977  level1 = maxBinEntries;
978  std::cout << "1 sigma level: " << level1 << std::endl;
979  // Find all contiguous regions
980  auto regions = findContiguousRegions(2);
981  std::cout << "1 sigma regions (" << regions.size() << " mode(s)):" << std::endl;
982  for (const auto& region : regions) {
983  double lower = h1d_delta_cp->GetBinLowEdge(region.first);
984  double upper = h1d_delta_cp->GetBinLowEdge(region.second + 1);
985  sigma1_regions.push_back({lower, upper});
986  std::cout << " [" << lower << ", " << upper << "]" << std::endl;
987  }
988  }
989  if (frac >= sigma3 && level3 == 0) {
990  level3 = maxBinEntries;
991  std::cout << "3 sigma level: " << level3 << std::endl;
992  // Find all contiguous regions
993  auto regions = findContiguousRegions(2);
994  std::cout << "3 sigma regions (" << regions.size() << " mode(s)):" << std::endl;
995  for (const auto& region : regions) {
996  double lower = h1d_delta_cp->GetBinLowEdge(region.first);
997  double upper = h1d_delta_cp->GetBinLowEdge(region.second + 1);
998  sigma3_regions.push_back({lower, upper});
999  std::cout << " [" << lower << ", " << upper << "]" << std::endl;
1000  }
1001  }
1002  if (frac >= sigma5 && level5 == 0) {
1003  level5 = maxBinEntries;
1004  std::cout << "5 sigma level: " << level5 << std::endl;
1005  // Find all contiguous regions
1006  auto regions = findContiguousRegions(2);
1007  std::cout << "5 sigma regions (" << regions.size() << " mode(s)):" << std::endl;
1008  for (const auto& region : regions) {
1009  double lower = h1d_delta_cp->GetBinLowEdge(region.first);
1010  double upper = h1d_delta_cp->GetBinLowEdge(region.second + 1);
1011  sigma5_regions.push_back({lower, upper});
1012  std::cout << " [" << lower << ", " << upper << "]" << std::endl;
1013  }
1014  }
1015  }
1016 
1017  // Draw vertical lines at the sigma bounds for all regions
1018  double ymax = h1d_delta_cp->GetMaximum();
1019 
1020  // 1 sigma bounds (yellow/orange) - draw all regions
1021  for (const auto& region : sigma1_regions) {
1022  TLine *line_lower = new TLine(region.first, 0, region.first, ymax);
1023  line_lower->SetLineColor(kOrange+1);
1024  line_lower->SetLineStyle(2);
1025  line_lower->SetLineWidth(2);
1026  line_lower->Draw("same");
1027 
1028  TLine *line_upper = new TLine(region.second, 0, region.second, ymax);
1029  line_upper->SetLineColor(kOrange+1);
1030  line_upper->SetLineStyle(2);
1031  line_upper->SetLineWidth(2);
1032  line_upper->Draw("same");
1033  }
1034 
1035  // 3 sigma bounds (blue) - draw all regions
1036  for (const auto& region : sigma3_regions) {
1037  TLine *line_lower = new TLine(region.first, 0, region.first, ymax);
1038  line_lower->SetLineColor(kAzure+2);
1039  line_lower->SetLineStyle(2);
1040  line_lower->SetLineWidth(2);
1041  line_lower->Draw("same");
1042 
1043  TLine *line_upper = new TLine(region.second, 0, region.second, ymax);
1044  line_upper->SetLineColor(kAzure+2);
1045  line_upper->SetLineStyle(2);
1046  line_upper->SetLineWidth(2);
1047  line_upper->Draw("same");
1048  }
1049 
1050  // 5 sigma bounds (magenta) - draw all regions
1051  for (const auto& region : sigma5_regions) {
1052  TLine *line_lower = new TLine(region.first, 0, region.first, ymax);
1053  line_lower->SetLineColor(kMagenta+2);
1054  line_lower->SetLineStyle(2);
1055  line_lower->SetLineWidth(2);
1056  line_lower->Draw("same");
1057 
1058  TLine *line_upper = new TLine(region.second, 0, region.second, ymax);
1059  line_upper->SetLineColor(kMagenta+2);
1060  line_upper->SetLineStyle(2);
1061  line_upper->SetLineWidth(2);
1062  line_upper->Draw("same");
1063  }
1064 
1065  TLegend *legRef = nullptr;
1066  if (hasReferenceDcp && hRefDcpShape) {
1067  std::cout << "Overlaying reference delta_cp shape on weighted histogram" << std::endl;
1068  TH1D *hRefOverlay = (TH1D*)hRefDcpShape->Clone("hRefDcpShape_overlay_linear");
1069  hRefOverlay->SetDirectory(0);
1070  double refIntegral = hRefOverlay->Integral();
1071  if (refIntegral > 0) {
1072  hRefOverlay->Scale(h1d_delta_cp->Integral() / refIntegral);
1073  }
1074  hRefOverlay->SetLineColor(kMagenta + 2);
1075  hRefOverlay->SetLineWidth(3);
1076  hRefOverlay->SetLineStyle(1);
1077  hRefOverlay->SetFillStyle(0);
1078  hRefOverlay->Draw("HIST SAME");
1079 
1080  legRef = new TLegend(0.15, 0.74, 0.48, 0.88);
1081  legRef->SetBorderSize(0);
1082  legRef->SetFillStyle(0);
1083  legRef->AddEntry(h1d_delta_cp, "Umbrella weighted", "l");
1084  legRef->AddEntry(hRefOverlay, "Reference dcp (shape, area-normalized)", "l");
1085  legRef->Draw();
1086  }
1087  drawAsimovLineWithLegend(asimovDcp, hasReferenceDcp ? legRef : nullptr);
1088 
1089  // If LLH-scan reference histograms were provided, overlay them (they were area-normalized above)
1090  if (hasReferenceLLH_Dcp && hRefLLH_Dcp) {
1091  std::cout << "Overlaying LLH-scan reference dcp histogram on weighted histogram (resampled to umbrella bins)" << std::endl;
1092  // Resample LLH histogram to match umbrella binning
1093  TH1D *hLLHResamp = new TH1D("hRefLLH_dcp_resamp", "", h1d_delta_cp->GetNbinsX(), h1d_delta_cp->GetXaxis()->GetXmin(), h1d_delta_cp->GetXaxis()->GetXmax());
1094  hLLHResamp->SetDirectory(0);
1095  for (int i = 1; i <= hLLHResamp->GetNbinsX(); ++i) {
1096  double xlow = hLLHResamp->GetBinLowEdge(i);
1097  double xhigh = hLLHResamp->GetBinLowEdge(i+1);
1098  double val = 0.0;
1099  int nOrig = hRefLLH_Dcp->GetNbinsX();
1100  for (int k = 1; k <= nOrig; ++k) {
1101  double origLow = hRefLLH_Dcp->GetXaxis()->GetBinLowEdge(k);
1102  double origHigh = origLow + hRefLLH_Dcp->GetBinWidth(k);
1103  double overlap = std::max(0.0, std::min(origHigh, xhigh) - std::max(origLow, xlow));
1104  if (overlap > 0.0) {
1105  double origWidth = hRefLLH_Dcp->GetBinWidth(k);
1106  double contrib = hRefLLH_Dcp->GetBinContent(k) * (overlap / origWidth);
1107  val += contrib;
1108  }
1109  }
1110  hLLHResamp->SetBinContent(i, val);
1111  }
1112  // Now scale resampled LLH to umbrella histogram area
1113  double llhInt = hLLHResamp->Integral();
1114  if (llhInt > 0) hLLHResamp->Scale(h1d_delta_cp->Integral() / llhInt);
1115  hLLHResamp->SetLineColor(kBlack);
1116  hLLHResamp->SetLineWidth(3);
1117  hLLHResamp->SetLineStyle(2);
1118  hLLHResamp->SetFillStyle(0);
1119  hLLHResamp->Draw("HIST SAME");
1120 
1121  TLegend *legLLH = new TLegend(0.15, 0.60, 0.50, 0.74);
1122  legLLH->SetBorderSize(0);
1123  legLLH->SetFillStyle(0);
1124  legLLH->AddEntry(h1d_delta_cp, "Umbrella weighted", "l");
1125  legLLH->AddEntry(hLLHResamp, "LLH scan (resampled)", "l");
1126  legLLH->Draw();
1127  }
1128 
1129  c7_weighted->cd(2);
1130  TH1F *h1d_delm23_all = new TH1F("h1d_delm23_all", "#Delta m_{23}^{2} (full range);#Delta m_{23}^{2} (eV^{2});Normalized Entries", nBins_delm23, delm23_io_min, delm23_no_max);
1131  if (hasUmbrellaWeight) {
1132  tree->Draw("delm2_23>>h1d_delm23_all", "umbrella_weight", "");
1133  std::cout << "Applying umbrella weights to delm23 histogram" << std::endl;
1134  } else {
1135  tree->Draw("delm2_23>>h1d_delm23_all", "", "");
1136  std::cout << "WARNING:::::No umbrella weights found, plotting unweighted #Delta m_{23}^{2} histogram." << std::endl;
1137  }
1138  normalizeHist(h1d_delm23_all);
1139  h1d_delm23_all->SetStats(0);
1140  h1d_delm23_all->Draw("HIST");
1141  h1d_delm23_all->Draw("E SAME");
1142 
1143  TLegend *legRefDelm23All = nullptr;
1144  if (hasReferenceDelm23 && hRefDelm23Shape) {
1145  TH1D *hRefDelm23AllOverlay = (TH1D*)hRefDelm23Shape->Clone("hRefDelm23All_overlay_linear");
1146  hRefDelm23AllOverlay->SetDirectory(0);
1147  double refIntegral = hRefDelm23AllOverlay->Integral();
1148  if (refIntegral > 0) {
1149  hRefDelm23AllOverlay->Scale(h1d_delm23_all->Integral() / refIntegral);
1150  }
1151  hRefDelm23AllOverlay->SetLineColor(kMagenta + 2);
1152  hRefDelm23AllOverlay->SetLineWidth(3);
1153  hRefDelm23AllOverlay->SetLineStyle(1);
1154  hRefDelm23AllOverlay->SetFillStyle(0);
1155  hRefDelm23AllOverlay->Draw("HIST SAME");
1156 
1157  legRefDelm23All = new TLegend(0.50, 0.74, 0.88, 0.88);
1158  legRefDelm23All->SetBorderSize(0);
1159  legRefDelm23All->SetFillStyle(0);
1160  legRefDelm23All->AddEntry(h1d_delm23_all, "Umbrella weighted", "l");
1161  legRefDelm23All->AddEntry(hRefDelm23AllOverlay, "Reference #Delta m_{23}^{2} (full range)", "l");
1162  legRefDelm23All->Draw();
1163  }
1164  drawAsimovLineWithLegend(asimovDelm23, hasReferenceDelm23 ? legRefDelm23All : nullptr);
1165 
1166  c7_weighted->cd(3);
1167  TH1F *h1d_delm23 = (TH1F*)h1d_delm23_all->Clone("h1d_delm23_IO");
1168  h1d_delm23->SetStats(0);
1169  h1d_delm23->Sumw2();
1170  h1d_delm23->GetXaxis()->SetRangeUser(delm23_io_min, delm23_io_max);
1171  h1d_delm23->Draw("HIST");
1172  h1d_delm23->Draw("E SAME");
1173 
1174  TLegend *legRefDelm23IO = nullptr;
1175  if (hasReferenceDelm23 && hRefDelm23Shape) {
1176  TH1D *hRefDelm23IOOverlay = (TH1D*)hRefDelm23Shape->Clone("hRefDelm23IO_overlay_linear");
1177  hRefDelm23IOOverlay->SetDirectory(0);
1178  hRefDelm23IOOverlay->GetXaxis()->SetRangeUser(delm23_io_min, delm23_io_max);
1179  hRefDelm23IOOverlay->SetLineColor(kMagenta + 2);
1180  hRefDelm23IOOverlay->SetLineWidth(3);
1181  hRefDelm23IOOverlay->SetLineStyle(1);
1182  hRefDelm23IOOverlay->SetFillStyle(0);
1183  hRefDelm23IOOverlay->Draw("HIST SAME");
1184 
1185  legRefDelm23IO = new TLegend(0.50, 0.74, 0.88, 0.88);
1186  legRefDelm23IO->SetBorderSize(0);
1187  legRefDelm23IO->SetFillStyle(0);
1188  legRefDelm23IO->AddEntry(h1d_delm23, "Umbrella weighted", "l");
1189  legRefDelm23IO->AddEntry(hRefDelm23IOOverlay, "Reference #Delta m_{23}^{2} (IO)", "l");
1190  legRefDelm23IO->Draw();
1191  }
1192  drawAsimovLineWithLegend(asimovDelm23, hasReferenceDelm23 ? legRefDelm23IO : nullptr);
1193 
1194  c7_weighted->cd(4);
1195  TH1F *h1d_delm23_NO = (TH1F*)h1d_delm23_all->Clone("h1d_delm23_NO");
1196  h1d_delm23_NO->SetStats(0);
1197  h1d_delm23_NO->Sumw2();
1198  h1d_delm23_NO->GetXaxis()->SetRangeUser(delm23_no_min, delm23_no_max);
1199  h1d_delm23_NO->Draw("HIST");
1200  h1d_delm23_NO->Draw("E SAME");
1201 
1202  TLegend *legRefDelm23NO = nullptr;
1203  if (hasReferenceDelm23 && hRefDelm23Shape) {
1204  std::cout << "Drawing reference delm23 shape for NO" << std::endl;
1205  TH1D *hRefDelm23NOOverlay = (TH1D*)hRefDelm23Shape->Clone("hRefDelm23NO_overlay_linear");
1206  hRefDelm23NOOverlay->SetDirectory(0);
1207  hRefDelm23NOOverlay->GetXaxis()->SetRangeUser(delm23_no_min, delm23_no_max);
1208  hRefDelm23NOOverlay->SetLineColor(kMagenta + 2);
1209  hRefDelm23NOOverlay->SetLineWidth(3);
1210  hRefDelm23NOOverlay->SetLineStyle(1);
1211  hRefDelm23NOOverlay->SetFillStyle(0);
1212  hRefDelm23NOOverlay->Draw("HIST SAME");
1213 
1214  legRefDelm23NO = new TLegend(0.50, 0.74, 0.88, 0.88);
1215  legRefDelm23NO->SetBorderSize(0);
1216  legRefDelm23NO->SetFillStyle(0);
1217  legRefDelm23NO->AddEntry(h1d_delm23_NO, "Umbrella weighted", "l");
1218  legRefDelm23NO->AddEntry(hRefDelm23NOOverlay, "Reference #Delta m_{23}^{2} (NO)", "l");
1219  legRefDelm23NO->Draw();
1220  }
1221  drawAsimovLineWithLegend(asimovDelm23, hasReferenceDelm23 ? legRefDelm23NO : nullptr);
1222 
1223  // Compute and print mass ordering preference (NO/IO) using the globally-normalized full-range histogram
1224  // Use `h1d_delm23_all` which has been normalized so its integral == 1, then sum bins over IO/NO ranges.
1225  if (h1d_delm23_all) {
1226  int binIO_low = h1d_delm23_all->GetXaxis()->FindBin(delm23_io_min);
1227  int binIO_high = h1d_delm23_all->GetXaxis()->FindBin(delm23_io_max);
1228  int binNO_low = h1d_delm23_all->GetXaxis()->FindBin(delm23_no_min);
1229  int binNO_high = h1d_delm23_all->GetXaxis()->FindBin(delm23_no_max);
1230 
1231  double fracIO = h1d_delm23_all->Integral(binIO_low, binIO_high);
1232  double fracNO = h1d_delm23_all->Integral(binNO_low, binNO_high);
1233 
1234  if (fracIO > 0) {
1235  double ratioNOtoIO = fracNO / fracIO;
1236  std::cout << "Mass ordering preference (NO/IO) = " << ratioNOtoIO
1237  << " (NO frac=" << fracNO << ", IO frac=" << fracIO << ")" << std::endl;
1238  } else {
1239  std::cout << "Mass ordering preference: IO fraction is zero; cannot compute ratio. "
1240  << "NO_frac=" << fracNO << " IO_frac=" << fracIO << std::endl;
1241  }
1242  } else {
1243  std::cout << "Mass ordering preference: h1d_delm23_all is null; cannot compute." << std::endl;
1244  }
1245 
1246  c7_weighted->cd(5);
1247  TH1F *h1d_sinth23 = new TH1F("h1d_sinth23", "sin^{2}(#theta_{23});sin^{2}(#theta_{23});Weighted Entries", nBins_sin2th23, sin2th23_min, sin2th23_max);
1248  if (hasUmbrellaWeight) {
1249  tree->Draw("sin2th_23>>h1d_sinth23", "umbrella_weight", "");
1250  } else {
1251  tree->Draw("sin2th_23>>h1d_sinth23", "", "");
1252  std::cout << "WARNING:::::No umbrella weights found, plotting unweighted sin^2(theta23) histogram." << std::endl;
1253  }
1254  normalizeHist(h1d_sinth23);
1255  h1d_sinth23->SetStats(0);
1256  h1d_sinth23->Sumw2();
1257  h1d_sinth23->Draw("HIST");
1258  h1d_sinth23->Draw("E SAME");
1259 
1260  TLegend *legRefSin = nullptr;
1261  if (hasReferenceSin2Th23 && hRefSin2Th23Shape) {
1262  TH1D *hRefSinOverlay = (TH1D*)hRefSin2Th23Shape->Clone("hRefSin2Th23_overlay_linear");
1263  hRefSinOverlay->SetDirectory(0);
1264  double refIntegral = hRefSinOverlay->Integral();
1265  if (refIntegral > 0) {
1266  hRefSinOverlay->Scale(h1d_sinth23->Integral() / refIntegral);
1267  }
1268  hRefSinOverlay->SetLineColor(kMagenta + 2);
1269  hRefSinOverlay->SetLineWidth(3);
1270  hRefSinOverlay->SetLineStyle(1);
1271  hRefSinOverlay->SetFillStyle(0);
1272  hRefSinOverlay->Draw("HIST SAME");
1273 
1274  legRefSin = new TLegend(0.50, 0.74, 0.88, 0.88);
1275  legRefSin->SetBorderSize(0);
1276  legRefSin->SetFillStyle(0);
1277  legRefSin->AddEntry(h1d_sinth23, "Umbrella weighted", "l");
1278  legRefSin->AddEntry(hRefSinOverlay, "Reference sin^{2}(#theta_{23})", "l");
1279  legRefSin->Draw();
1280  }
1281  drawAsimovLineWithLegend(asimovSin2th23, hasReferenceSin2Th23 ? legRefSin : nullptr);
1282 
1283  c7_weighted->cd(6);
1284  TH1F *h1d_sinth13 = new TH1F("h1d_sinth13", "sin^{2}(#theta_{13});sin^{2}(#theta_{13});Weighted Entries", nBins_sin2th13, sin2th13_min, sin2th13_max);
1285  if (hasUmbrellaWeight) {
1286  tree->Draw("sin2th_13>>h1d_sinth13", "umbrella_weight", "");
1287  } else {
1288  tree->Draw("sin2th_13>>h1d_sinth13", "", "");
1289  std::cout << "WARNING:::::No umbrella weights found, plotting unweighted sin^2(theta13) histogram." << std::endl;
1290  }
1291  normalizeHist(h1d_sinth13);
1292  h1d_sinth13->SetStats(0);
1293  h1d_sinth13->Sumw2();
1294  h1d_sinth13->Draw("HIST");
1295  h1d_sinth13->Draw("E SAME");
1296  drawAsimovLineWithLegend(asimovSin2th13);
1297 
1298  c7_weighted->cd(7);
1299  TH1F *h1d_sinth12 = new TH1F("h1d_sinth12", "sin^{2}(#theta_{12});sin^{2}(#theta_{12});Weighted Entries", nBins_sin2th12, sin2th12_min, sin2th12_max);
1300  if (hasUmbrellaWeight) {
1301  tree->Draw("sin2th_12>>h1d_sinth12", "umbrella_weight", "");
1302  } else {
1303  tree->Draw("sin2th_12>>h1d_sinth12", "", "");
1304  std::cout << "WARNING:::::No umbrella weights found, plotting unweighted sin^2(theta12) histogram." << std::endl;
1305  }
1306  normalizeHist(h1d_sinth12);
1307  h1d_sinth12->SetStats(0);
1308  h1d_sinth12->Sumw2();
1309  h1d_sinth12->Draw("HIST");
1310  h1d_sinth12->Draw("E SAME");
1311  drawAsimovLineWithLegend(asimovSin2th12);
1312 
1313  c7_weighted->cd(8);
1314  TH1F *h1d_delm12 = new TH1F("h1d_delm12", "#Delta m_{12}^{2};#Delta m_{12}^{2} (eV^{2});Weighted Entries", nBins_delm12, delm12_min, delm12_max);
1315  if (hasUmbrellaWeight) {
1316  tree->Draw("delm2_12>>h1d_delm12", "umbrella_weight", "");
1317  } else {
1318  tree->Draw("delm2_12>>h1d_delm12", "", "");
1319  std::cout << "WARNING:::::No umbrella weights found, plotting unweighted #Delta m_{12}^{2} histogram." << std::endl;
1320  }
1321  normalizeHist(h1d_delm12);
1322  h1d_delm12->SetStats(0);
1323  h1d_delm12->Sumw2();
1324  h1d_delm12->Draw("HIST");
1325  h1d_delm12->Draw("E SAME");
1326  drawAsimovLineWithLegend(asimovDelm12);
1327 
1328  c7_weighted->Update();
1329  c7_weighted->SaveAs((outputDir + "1D_histograms_weighted.png").c_str());
1330 
1331  // Create separate canvas for delta_cp with log scale
1332  TCanvas *c8 = new TCanvas("c8", "Delta CP Log Scale", 900, 700);
1333  c8->SetLogy();
1334  TH1F *h1d_delta_cp_log = (TH1F*)h1d_delta_cp->Clone("h1d_delta_cp_log");
1335  h1d_delta_cp_log->SetTitle("#delta_{CP} (Log Scale);#delta_{CP};Weighted Entries");
1336  h1d_delta_cp_log->Draw("HIST");
1337  h1d_delta_cp_log->Draw("E SAME");
1338 
1339  // Draw vertical lines at the sigma bounds for all regions on log scale plot
1340  double ymax_log = h1d_delta_cp_log->GetMaximum();
1341  double ymin_log = h1d_delta_cp_log->GetMinimum();
1342  if (ymin_log <= 0) ymin_log = 0.1; // avoid log(0)
1343 
1344  // 1 sigma bounds (user colors) - draw one-sided lines and HPD regions
1345  for (const auto& region : sigma1_regions) {
1346  TLine *line_lower = new TLine(region.first, ymin_log, region.first, ymax_log);
1347  line_lower->SetLineColor(kRed);
1348  line_lower->SetLineStyle(2);
1349  line_lower->SetLineWidth(2);
1350  line_lower->Draw("same");
1351 
1352  TLine *line_upper = new TLine(region.second, ymin_log, region.second, ymax_log);
1353  line_upper->SetLineColor(kRed);
1354  line_upper->SetLineStyle(2);
1355  line_upper->SetLineWidth(2);
1356  line_upper->Draw("same");
1357  }
1358 
1359  // 3 sigma bounds (turquoise/darker blue) - draw all regions
1360  for (const auto& region : sigma3_regions) {
1361  TLine *line_lower = new TLine(region.first, ymin_log, region.first, ymax_log);
1362  line_lower->SetLineColor(kAzure+2);
1363  line_lower->SetLineStyle(2);
1364  line_lower->SetLineWidth(2);
1365  line_lower->Draw("same");
1366 
1367  TLine *line_upper = new TLine(region.second, ymin_log, region.second, ymax_log);
1368  line_upper->SetLineColor(kAzure+2);
1369  line_upper->SetLineStyle(2);
1370  line_upper->SetLineWidth(2);
1371  line_upper->Draw("same");
1372  }
1373 
1374  // 5 sigma bounds (purple) - draw all regions
1375  for (const auto& region : sigma5_regions) {
1376  TLine *line_lower = new TLine(region.first, ymin_log, region.first, ymax_log);
1377  line_lower->SetLineColor(kMagenta+2);
1378  line_lower->SetLineStyle(2);
1379  line_lower->SetLineWidth(2);
1380  line_lower->Draw("same");
1381 
1382  TLine *line_upper = new TLine(region.second, ymin_log, region.second, ymax_log);
1383  line_upper->SetLineColor(kMagenta+2);
1384  line_upper->SetLineStyle(2);
1385  line_upper->SetLineWidth(2);
1386  line_upper->Draw("same");
1387  }
1388 
1389  TLegend *legRefLog = nullptr;
1390  if (hasReferenceDcp && hRefDcpShape) {
1391  TH1D *hRefOverlayLog = (TH1D*)hRefDcpShape->Clone("hRefDcpShape_overlay_log");
1392  hRefOverlayLog->SetDirectory(0);
1393  double refIntegral = hRefOverlayLog->Integral();
1394  if (refIntegral > 0) {
1395  hRefOverlayLog->Scale(h1d_delta_cp_log->Integral() / refIntegral);
1396  }
1397  hRefOverlayLog->SetLineColor(kMagenta + 2);
1398  hRefOverlayLog->SetLineWidth(3);
1399  hRefOverlayLog->SetLineStyle(1);
1400  hRefOverlayLog->SetFillStyle(0);
1401  hRefOverlayLog->Draw("HIST SAME");
1402 
1403  legRefLog = new TLegend(0.50, 0.74, 0.88, 0.88);
1404  legRefLog->SetBorderSize(0);
1405  legRefLog->SetFillStyle(0);
1406  legRefLog->AddEntry(h1d_delta_cp_log, "Umbrella weighted", "l");
1407  legRefLog->AddEntry(hRefOverlayLog, "Reference dcp (shape, area-normalized)", "l");
1408  legRefLog->Draw();
1409  }
1410  if (hasReferenceLLH_Dcp && hRefLLH_Dcp) {
1411  // Resample LLH histogram to match log-plot binning
1412  TH1D *hLLHResampLog = new TH1D("hRefLLH_dcp_resamp_log", "", h1d_delta_cp_log->GetNbinsX(), h1d_delta_cp_log->GetXaxis()->GetXmin(), h1d_delta_cp_log->GetXaxis()->GetXmax());
1413  hLLHResampLog->SetDirectory(0);
1414  for (int i = 1; i <= hLLHResampLog->GetNbinsX(); ++i) {
1415  double xlow = hLLHResampLog->GetBinLowEdge(i);
1416  double xhigh = hLLHResampLog->GetBinLowEdge(i+1);
1417  double val = 0.0;
1418  int nOrig = hRefLLH_Dcp->GetNbinsX();
1419  for (int k = 1; k <= nOrig; ++k) {
1420  double origLow = hRefLLH_Dcp->GetXaxis()->GetBinLowEdge(k);
1421  double origHigh = origLow + hRefLLH_Dcp->GetBinWidth(k);
1422  double overlap = std::max(0.0, std::min(origHigh, xhigh) - std::max(origLow, xlow));
1423  if (overlap > 0.0) {
1424  double origWidth = hRefLLH_Dcp->GetBinWidth(k);
1425  double contrib = hRefLLH_Dcp->GetBinContent(k) * (overlap / origWidth);
1426  val += contrib;
1427  }
1428  }
1429  hLLHResampLog->SetBinContent(i, val);
1430  }
1431  double llhI = hLLHResampLog->Integral();
1432  if (llhI > 0) hLLHResampLog->Scale(h1d_delta_cp_log->Integral() / llhI);
1433  hLLHResampLog->SetLineColor(kBlue + 2);
1434  hLLHResampLog->SetLineWidth(3);
1435  hLLHResampLog->SetLineStyle(2);
1436  hLLHResampLog->SetFillStyle(0);
1437  hLLHResampLog->Draw("HIST SAME");
1438  TLegend *legLLHLog = new TLegend(0.50, 0.60, 0.88, 0.74);
1439  legLLHLog->SetBorderSize(0);
1440  legLLHLog->SetFillStyle(0);
1441  legLLHLog->AddEntry(h1d_delta_cp_log, "Umbrella weighted", "l");
1442  legLLHLog->AddEntry(hLLHResampLog, "LLH scan (resampled & area-normalized)", "l");
1443  legLLHLog->Draw();
1444  }
1445  drawAsimovLineWithLegend(asimovDcp, hasReferenceDcp ? legRefLog : nullptr);
1446 
1447  c8->Update();
1448  c8->SaveAs((outputDir + "delta_cp_log_scale_weighted.png").c_str());
1449 
1450  // Create separate two-panel log-scale delta_cp plots marginalized over IO and NO.
1451  TCanvas *c9 = new TCanvas("c9", "Delta CP Log Scale Marginalized by Ordering", 1800, 700);
1452  c9->Divide(2, 1);
1453 
1454  std::string ioSelection = hasUmbrellaWeight ? "(delm2_23 < 0) * umbrella_weight" : "(delm2_23 < 0)";
1455  std::string noSelection = hasUmbrellaWeight ? "(delm2_23 > 0) * umbrella_weight" : "(delm2_23 > 0)";
1456 
1457  // IO panel
1458  c9->cd(1);
1459  gPad->SetLogy();
1460  TH1F *h1d_delta_cp_io_log = new TH1F("h1d_delta_cp_io_log", "#delta_{CP} marginalized over IO;#delta_{CP};Normalized Entries", nBins_dcp, -3.1415, 3.1415);
1461  tree->Draw("delta_cp>>h1d_delta_cp_io_log", ioSelection.c_str(), "");
1462  normalizeHist(h1d_delta_cp_io_log);
1463  h1d_delta_cp_io_log->SetStats(0);
1464  double ioMinPositive = 1.0;
1465  bool ioFoundPositive = false;
1466  for (int b = 1; b <= h1d_delta_cp_io_log->GetNbinsX(); ++b) {
1467  double y = h1d_delta_cp_io_log->GetBinContent(b);
1468  if (y > 0.0) {
1469  if (!ioFoundPositive || y < ioMinPositive) {
1470  ioMinPositive = y;
1471  }
1472  ioFoundPositive = true;
1473  }
1474  }
1475  h1d_delta_cp_io_log->SetMinimum(ioFoundPositive ? 0.5 * ioMinPositive : 1e-8);
1476  h1d_delta_cp_io_log->Draw("HIST");
1477  h1d_delta_cp_io_log->Draw("E SAME");
1478  drawAsimovLineWithLegend(asimovDcp);
1479  if (hasReferenceLLH_Dcp_IO && hRefLLH_Dcp_IO) {
1480  TH1D *hLLHIO = new TH1D("hRefLLH_dcp_io_resamp", "", h1d_delta_cp_io_log->GetNbinsX(), h1d_delta_cp_io_log->GetXaxis()->GetXmin(), h1d_delta_cp_io_log->GetXaxis()->GetXmax());
1481  hLLHIO->SetDirectory(0);
1482  for (int i = 1; i <= hLLHIO->GetNbinsX(); ++i) {
1483  double xlow = hLLHIO->GetBinLowEdge(i);
1484  double xhigh = hLLHIO->GetBinLowEdge(i+1);
1485  double val = 0.0;
1486  int nOrig = hRefLLH_Dcp_IO->GetNbinsX();
1487  for (int k = 1; k <= nOrig; ++k) {
1488  double origLow = hRefLLH_Dcp_IO->GetXaxis()->GetBinLowEdge(k);
1489  double origHigh = origLow + hRefLLH_Dcp_IO->GetBinWidth(k);
1490  double overlap = std::max(0.0, std::min(origHigh, xhigh) - std::max(origLow, xlow));
1491  if (overlap > 0.0) {
1492  double origWidth = hRefLLH_Dcp_IO->GetBinWidth(k);
1493  double contrib = hRefLLH_Dcp_IO->GetBinContent(k) * (overlap / origWidth);
1494  val += contrib;
1495  }
1496  }
1497  hLLHIO->SetBinContent(i, val);
1498  }
1499  double li = hLLHIO->Integral();
1500  if (li > 0) hLLHIO->Scale(h1d_delta_cp_io_log->Integral() / li);
1501  hLLHIO->SetLineColor(kBlue + 2);
1502  hLLHIO->SetLineWidth(2);
1503  hLLHIO->SetLineStyle(2);
1504  hLLHIO->Draw("HIST SAME");
1505  TLegend *legLLHIO = new TLegend(0.50, 0.60, 0.88, 0.74);
1506  legLLHIO->SetBorderSize(0);
1507  legLLHIO->SetFillStyle(0);
1508  legLLHIO->AddEntry(h1d_delta_cp_io_log, "Umbrella weighted (IO)", "l");
1509  legLLHIO->AddEntry(hLLHIO, "LLH scan IO (resampled & area-normalized)", "l");
1510  legLLHIO->Draw();
1511  }
1512 
1513  // NO panel
1514  c9->cd(2);
1515  gPad->SetLogy();
1516  TH1F *h1d_delta_cp_no_log = new TH1F("h1d_delta_cp_no_log", "#delta_{CP} marginalized over NO;#delta_{CP};Normalized Entries", nBins_dcp, -3.1415, 3.1415);
1517  tree->Draw("delta_cp>>h1d_delta_cp_no_log", noSelection.c_str(), "");
1518  normalizeHist(h1d_delta_cp_no_log);
1519  h1d_delta_cp_no_log->SetStats(0);
1520  double noMinPositive = 1.0;
1521  bool noFoundPositive = false;
1522  for (int b = 1; b <= h1d_delta_cp_no_log->GetNbinsX(); ++b) {
1523  double y = h1d_delta_cp_no_log->GetBinContent(b);
1524  if (y > 0.0) {
1525  if (!noFoundPositive || y < noMinPositive) {
1526  noMinPositive = y;
1527  }
1528  noFoundPositive = true;
1529  }
1530  }
1531  h1d_delta_cp_no_log->SetMinimum(noFoundPositive ? 0.5 * noMinPositive : 1e-8);
1532  h1d_delta_cp_no_log->Draw("HIST");
1533  h1d_delta_cp_no_log->Draw("E SAME");
1534  drawAsimovLineWithLegend(asimovDcp);
1535  if (hasReferenceLLH_Dcp_NO && hRefLLH_Dcp_NO) {
1536  TH1D *hLLHNO = new TH1D("hRefLLH_dcp_no_resamp", "", h1d_delta_cp_no_log->GetNbinsX(), h1d_delta_cp_no_log->GetXaxis()->GetXmin(), h1d_delta_cp_no_log->GetXaxis()->GetXmax());
1537  hLLHNO->SetDirectory(0);
1538  for (int i = 1; i <= hLLHNO->GetNbinsX(); ++i) {
1539  double xlow = hLLHNO->GetBinLowEdge(i);
1540  double xhigh = hLLHNO->GetBinLowEdge(i+1);
1541  double val = 0.0;
1542  int nOrig = hRefLLH_Dcp_NO->GetNbinsX();
1543  for (int k = 1; k <= nOrig; ++k) {
1544  double origLow = hRefLLH_Dcp_NO->GetXaxis()->GetBinLowEdge(k);
1545  double origHigh = origLow + hRefLLH_Dcp_NO->GetBinWidth(k);
1546  double overlap = std::max(0.0, std::min(origHigh, xhigh) - std::max(origLow, xlow));
1547  if (overlap > 0.0) {
1548  double origWidth = hRefLLH_Dcp_NO->GetBinWidth(k);
1549  double contrib = hRefLLH_Dcp_NO->GetBinContent(k) * (overlap / origWidth);
1550  val += contrib;
1551  }
1552  }
1553  hLLHNO->SetBinContent(i, val);
1554  }
1555  double ln = hLLHNO->Integral();
1556  if (ln > 0) hLLHNO->Scale(h1d_delta_cp_no_log->Integral() / ln);
1557  hLLHNO->SetLineColor(kBlue + 2);
1558  hLLHNO->SetLineWidth(2);
1559  hLLHNO->SetLineStyle(2);
1560  hLLHNO->Draw("HIST SAME");
1561  TLegend *legLLHNO = new TLegend(0.50, 0.60, 0.88, 0.74);
1562  legLLHNO->SetBorderSize(0);
1563  legLLHNO->SetFillStyle(0);
1564  legLLHNO->AddEntry(h1d_delta_cp_no_log, "Umbrella weighted (NO)", "l");
1565  legLLHNO->AddEntry(hLLHNO, "LLH scan NO (resampled & area-normalized)", "l");
1566  legLLHNO->Draw();
1567  }
1568 
1569  c9->Update();
1570  c9->SaveAs((outputDir + "delta_cp_log_scale_marginalized_io_no.png").c_str());
1571 
1572  // Jarlskog invariant plots (unitless)
1573  const double jarlskogMin = -0.06;
1574  const double jarlskogMax = 0.06;
1575  const int nBinsJarlskog = 160;
1576 
1577  TCanvas *c10 = new TCanvas("c10", "Jarlskog Invariant", 900, 700);
1578  TH1F *h1d_jarlskog_unweighted = new TH1F("h1d_jarlskog_unweighted", "Jarlskog invariant;Jarlskog invariant;Entries",
1579  nBinsJarlskog, jarlskogMin, jarlskogMax);
1580  tree->Draw("TMath::Sqrt(TMath::Max(0.,sin2th_12)*TMath::Max(0.,1.-sin2th_12)*TMath::Max(0.,sin2th_23)*TMath::Max(0.,1.-sin2th_23))*TMath::Max(0.,1.-sin2th_13)*TMath::Sqrt(TMath::Max(0.,sin2th_13))*TMath::Sin(delta_cp)>>h1d_jarlskog_unweighted",
1581  "", "");
1582  normalizeHist(h1d_jarlskog_unweighted);
1583  h1d_jarlskog_unweighted->SetStats(0);
1584  h1d_jarlskog_unweighted->Draw("HIST");
1585  h1d_jarlskog_unweighted->Draw("E SAME");
1586  drawAsimovLineWithLegend(asimovJarlskog);
1587  // One-sided confidence intervals for Jarlskog (unweighted)
1588  auto computeOneSidedBounds = [&](TH1 *hist, double targets[3], double outBounds[3]) {
1589  // targets: fractions (e.g., 0.6827, 0.9973, 0.99994)
1590  // outBounds: boundaries in x where cumulative from chosen side reaches target
1591  for (int i = 0; i < 3; ++i) outBounds[i] = std::numeric_limits<double>::quiet_NaN();
1592  if (!hist) return;
1593  double total = hist->Integral();
1594  if (total <= 0) return;
1595  int nBins = hist->GetNbinsX();
1596  // determine which side of zero holds more posterior (simple count of bin content)
1597  double leftSum = 0.0;
1598  double rightSum = 0.0;
1599  for (int b = 1; b <= nBins; ++b) {
1600  double center = hist->GetBinCenter(b);
1601  double c = hist->GetBinContent(b);
1602  if (center < 0) leftSum += c; else rightSum += c;
1603  }
1604  bool integrateLeftToRight = (leftSum >= rightSum);
1605 
1606  for (int t = 0; t < 3; ++t) {
1607  double target = targets[t];
1608  double cum = 0.0;
1609  if (integrateLeftToRight) {
1610  for (int b = 1; b <= nBins; ++b) {
1611  cum += hist->GetBinContent(b);
1612  if (cum / total >= target) {
1613  // upper edge of bin b
1614  outBounds[t] = hist->GetBinLowEdge(b+1);
1615  break;
1616  }
1617  }
1618  // if not reached, set to max
1619  if (!std::isfinite(outBounds[t])) outBounds[t] = hist->GetXaxis()->GetXmax();
1620  } else {
1621  for (int b = nBins; b >= 1; --b) {
1622  cum += hist->GetBinContent(b);
1623  if (cum / total >= target) {
1624  // lower edge of bin b
1625  outBounds[t] = hist->GetBinLowEdge(b);
1626  break;
1627  }
1628  }
1629  if (!std::isfinite(outBounds[t])) outBounds[t] = hist->GetXaxis()->GetXmin();
1630  }
1631  }
1632  // draw lines on current pad
1633  double hist_ymax = hist->GetMaximum();
1634  // colors: 1sigma yellow-orangey, 3sigma turquoise/darker blue, 5sigma purple
1635  int colors[3] = {kOrange+1, kAzure+2, kMagenta+2};
1636  for (int t = 0; t < 3; ++t) {
1637  if (!std::isfinite(outBounds[t])) continue;
1638  TLine *line = nullptr;
1639  if (integrateLeftToRight) {
1640  // region is [xmin, outBounds[t]] -> draw vertical at outBounds[t]
1641  line = new TLine(outBounds[t], 0, outBounds[t], hist_ymax);
1642  } else {
1643  // region is [outBounds[t], xmax] -> draw vertical at outBounds[t]
1644  line = new TLine(outBounds[t], 0, outBounds[t], hist_ymax);
1645  }
1646  line->SetLineColor(colors[t]);
1647  line->SetLineStyle(2);
1648  line->SetLineWidth(2);
1649  line->Draw("same");
1650  }
1651  };
1652  // (no one-sided bounds for unweighted histograms)
1653  c10->Update();
1654  c10->SaveAs((outputDir + "jarlskog_unweighted.png").c_str());
1655 
1656  TCanvas *c10_log = new TCanvas("c10_log", "Jarlskog Invariant Log Scale", 900, 700);
1657  c10_log->SetLogy();
1658  TH1F *h1d_jarlskog_unweighted_log = (TH1F*)h1d_jarlskog_unweighted->Clone("h1d_jarlskog_unweighted_log");
1659  h1d_jarlskog_unweighted_log->SetTitle("Jarlskog invariant (Log Scale);Jarlskog invariant;Entries");
1660  h1d_jarlskog_unweighted_log->Draw("HIST");
1661  h1d_jarlskog_unweighted_log->Draw("E SAME");
1662  drawAsimovLineWithLegend(asimovJarlskog);
1663  // (no one-sided bounds for unweighted histograms)
1664  c10_log->Update();
1665  c10_log->SaveAs((outputDir + "jarlskog_unweighted_log.png").c_str());
1666 
1667  TCanvas *c10_weighted = new TCanvas("c10_weighted", "Jarlskog Invariant Weighted", 900, 700);
1668  TH1F *h1d_jarlskog_weighted = new TH1F("h1d_jarlskog_weighted", "Jarlskog invariant;Jarlskog invariant;Weighted Entries",
1669  nBinsJarlskog, jarlskogMin, jarlskogMax);
1670  if (hasUmbrellaWeight) {
1671  tree->Draw("TMath::Sqrt(TMath::Max(0.,sin2th_12)*TMath::Max(0.,1.-sin2th_12)*TMath::Max(0.,sin2th_23)*TMath::Max(0.,1.-sin2th_23))*TMath::Max(0.,1.-sin2th_13)*TMath::Sqrt(TMath::Max(0.,sin2th_13))*TMath::Sin(delta_cp)>>h1d_jarlskog_weighted",
1672  "umbrella_weight", "");
1673  } else {
1674  tree->Draw("TMath::Sqrt(TMath::Max(0.,sin2th_12)*TMath::Max(0.,1.-sin2th_12)*TMath::Max(0.,sin2th_23)*TMath::Max(0.,1.-sin2th_23))*TMath::Max(0.,1.-sin2th_13)*TMath::Sqrt(TMath::Max(0.,sin2th_13))*TMath::Sin(delta_cp)>>h1d_jarlskog_weighted",
1675  "", "");
1676  std::cout << "WARNING:::::No umbrella weights found, plotting unweighted Jarlskog histogram." << std::endl;
1677  }
1678  normalizeHist(h1d_jarlskog_weighted);
1679  h1d_jarlskog_weighted->SetStats(0);
1680  h1d_jarlskog_weighted->Sumw2();
1681  h1d_jarlskog_weighted->Draw("HIST");
1682  h1d_jarlskog_weighted->Draw("E SAME");
1683  drawAsimovLineWithLegend(asimovJarlskog);
1684  // One-sided confidence intervals for Jarlskog (weighted)
1685  double targets[3] = {sigma1, sigma3, sigma5};
1686  double bounds[3];
1687  computeOneSidedBounds(h1d_jarlskog_weighted, targets, bounds);
1688  // create legend entries for one-sided CI lines (weighted)
1689  double ymax_w = h1d_jarlskog_weighted->GetMaximum();
1690  TLine *legLine1 = nullptr;
1691  TLine *legLine3 = nullptr;
1692  TLine *legLine5 = nullptr;
1693  if (std::isfinite(bounds[0])) legLine1 = new TLine(bounds[0], 0, bounds[0], ymax_w);
1694  if (std::isfinite(bounds[1])) legLine3 = new TLine(bounds[1], 0, bounds[1], ymax_w);
1695  if (std::isfinite(bounds[2])) legLine5 = new TLine(bounds[2], 0, bounds[2], ymax_w);
1696  if (legLine1) { legLine1->SetLineColor(kOrange+1); legLine1->SetLineStyle(2); legLine1->SetLineWidth(2); }
1697  if (legLine3) { legLine3->SetLineColor(kAzure+2); legLine3->SetLineStyle(2); legLine3->SetLineWidth(2); }
1698  if (legLine5) { legLine5->SetLineColor(kMagenta+2); legLine5->SetLineStyle(2); legLine5->SetLineWidth(2); }
1699  TLegend *legCI = new TLegend(0.15, 0.60, 0.50, 0.74);
1700  legCI->SetBorderSize(0);
1701  legCI->SetFillStyle(0);
1702  if (legLine1) legCI->AddEntry(legLine1, "1 #sigma (one-sided)", "l");
1703  if (legLine3) legCI->AddEntry(legLine3, "3 #sigma (one-sided)", "l");
1704  if (legLine5) legCI->AddEntry(legLine5, "5 #sigma (one-sided)", "l");
1705  legCI->Draw();
1706  c10_weighted->Update();
1707  c10_weighted->SaveAs((outputDir + "jarlskog_weighted.png").c_str());
1708 
1709  TCanvas *c10_weighted_log = new TCanvas("c10_weighted_log", "Jarlskog Invariant Weighted Log Scale", 900, 700);
1710  c10_weighted_log->SetLogy();
1711  TH1F *h1d_jarlskog_weighted_log = (TH1F*)h1d_jarlskog_weighted->Clone("h1d_jarlskog_weighted_log");
1712  h1d_jarlskog_weighted_log->SetTitle("Jarlskog invariant (Log Scale);Jarlskog invariant;Weighted Entries");
1713  h1d_jarlskog_weighted_log->Draw("HIST");
1714  h1d_jarlskog_weighted_log->Draw("E SAME");
1715  drawAsimovLineWithLegend(asimovJarlskog);
1716  // Draw previously computed one-sided bounds on the log canvas as well
1717  for (int t = 0; t < 3; ++t) {
1718  if (!std::isfinite(bounds[t])) continue;
1719  double hist_ymax = h1d_jarlskog_weighted_log->GetMaximum();
1720  int colors_log[3] = {kOrange+1, kAzure+2, kMagenta+2};
1721  TLine *line = new TLine(bounds[t], 0, bounds[t], hist_ymax);
1722  line->SetLineColor(colors_log[t]);
1723  line->SetLineStyle(2);
1724  line->SetLineWidth(2);
1725  line->Draw("same");
1726  }
1727  // legend for log canvas
1728  TLegend *legCI_log = new TLegend(0.15, 0.60, 0.50, 0.74);
1729  legCI_log->SetBorderSize(0);
1730  legCI_log->SetFillStyle(0);
1731  if (legLine1) legCI_log->AddEntry(legLine1, "1 #sigma (one-sided)", "l");
1732  if (legLine3) legCI_log->AddEntry(legLine3, "3 #sigma (one-sided)", "l");
1733  if (legLine5) legCI_log->AddEntry(legLine5, "5 #sigma (one-sided)", "l");
1734  legCI_log->Draw();
1735  c10_weighted_log->Update();
1736  c10_weighted_log->SaveAs((outputDir + "jarlskog_weighted_log.png").c_str());
1737 
1738  struct TriangleVar {
1739  std::string name;
1740  std::string title;
1741  std::string branch;
1742  int bins;
1743  double min;
1744  double max;
1745  double asimov;
1746  };
1747 
1748  std::vector<TriangleVar> triangleVars = {
1749  {"delta_cp", "#delta_{CP}", "delta_cp", nBins_dcp, -3.1415, 3.1415, asimovDcp},
1750  {"sin2th_23", "sin^{2}(#theta_{23})", "sin2th_23", nBins_sin2th23, sin2th23_min, sin2th23_max, asimovSin2th23},
1751  {"sin2th_13", "sin^{2}(#theta_{13})", "sin2th_13", nBins_sin2th13, sin2th13_min, sin2th13_max, asimovSin2th13},
1752  {"sin2th_12", "sin^{2}(#theta_{12})", "sin2th_12", nBins_sin2th12, sin2th12_min, sin2th12_max, asimovSin2th12},
1753  {"delm2_23", "#Delta m_{23}^{2} (eV^{2})", "delm2_23", nBins_delm23, delm23_io_min, delm23_no_max, asimovDelm23},
1754  {"delm2_12", "#Delta m_{12}^{2} (eV^{2})", "delm2_12", nBins_delm12, delm12_min, delm12_max, asimovDelm12}
1755  };
1756 
1757  auto drawAsimovLineNoLegend = [&](double xValue) {
1758  if (!isAsimovChain || !std::isfinite(xValue) || !gPad) return;
1759  gPad->Update();
1760  const double yMin = gPad->GetUymin();
1761  const double yMax = gPad->GetUymax();
1762  TLine *asimovLine = new TLine(xValue, yMin, xValue, yMax);
1763  asimovLine->SetLineColor(kBlack);
1764  asimovLine->SetLineStyle(3);
1765  asimovLine->SetLineWidth(2);
1766  asimovLine->Draw("SAME");
1767  };
1768 
1769  auto drawAsimovMarkerNoLegend = [&](double xValue, double yValue) {
1770  if (!isAsimovChain || !std::isfinite(xValue) || !std::isfinite(yValue) || !gPad) return;
1771  gPad->Update();
1772  const double xMin = gPad->GetUxmin();
1773  const double xMax = gPad->GetUxmax();
1774  const double yMin = gPad->GetUymin();
1775  const double yMax = gPad->GetUymax();
1776  const double dx = 0.012 * (xMax - xMin);
1777  const double dy = 0.012 * (yMax - yMin);
1778 
1779  TLine *l1 = new TLine(xValue - dx, yValue - dy, xValue + dx, yValue + dy);
1780  l1->SetLineColor(kRed);
1781  l1->SetLineWidth(2);
1782  l1->Draw("SAME");
1783 
1784  TLine *l2 = new TLine(xValue - dx, yValue + dy, xValue + dx, yValue - dy);
1785  l2->SetLineColor(kRed);
1786  l2->SetLineWidth(2);
1787  l2->Draw("SAME");
1788  };
1789 
1790  auto drawTrianglePlot = [&](const std::string &weightExpr, const std::string &suffix, int mo = 0) {
1791  if (mo == 1) {
1792  std::cout << "Drawing triangle plot for IO" << std::endl;
1793  triangleVars[4].min = delm23_no_min;
1794  triangleVars[4].max = delm23_no_max;
1795  } else if (mo == -1) {
1796  std::cout << "Drawing triangle plot for NO" << std::endl;
1797  triangleVars[4].min = delm23_io_min;
1798  triangleVars[4].max = delm23_io_max;
1799  } else {
1800  std::cout << "Drawing triangle plot for both MO" << std::endl;
1801  }
1802 
1803  const int nVars = static_cast<int>(triangleVars.size());
1804  TCanvas *cTri = new TCanvas(("c_triangle" + suffix).c_str(), "Triangle Plot", 1400, 1400);
1805  cTri->SetFillStyle(0);
1806  cTri->cd();
1807 
1808  // Use MCMCProcessor-style pad math for non-uniform margins
1809  const double TPm[4] = {.07, .07, .05, .05};
1810  const double Pm[2] = {.2, .1};
1811 
1812  const double TPw = 1.0 - TPm[0] - TPm[2];
1813  const double a_x = (Pm[0] * TPw) / (1.0 * nVars + Pm[0] * (1.0 - 1.0 * nVars));
1814  const double b_x = (TPw - a_x) / (1.0 * nVars);
1815 
1816  std::vector<double> X_Min(nVars), X_Max(nVars);
1817  X_Min[0] = TPm[0];
1818  X_Max[0] = X_Min[0] + a_x + b_x;
1819  for (int i = 1; i < nVars; ++i) {
1820  X_Min[i] = X_Max[i - 1];
1821  X_Max[i] = X_Min[i] + b_x;
1822  }
1823 
1824  const double TPh = 1.0 - TPm[1] - TPm[3];
1825  const double a_y = (Pm[1] * TPh) / (1.0 * nVars + Pm[1] * (1.0 - 1.0 * nVars));
1826  const double b_y = (TPh - a_y) / (1.0 * nVars);
1827 
1828  std::vector<double> Y_Min(nVars), Y_Max(nVars);
1829  Y_Min[nVars - 1] = TPm[1];
1830  Y_Max[nVars - 1] = Y_Min[nVars - 1] + a_y + b_y;
1831  for (int i = nVars - 2; i >= 0; --i) {
1832  Y_Min[i] = Y_Max[i + 1];
1833  Y_Max[i] = Y_Min[i] + b_y;
1834  }
1835 
1836  for (int row = 0; row < nVars; ++row) {
1837  for (int col = 0; col <= row; ++col) {
1838  const double x1 = X_Min[col];
1839  const double x2 = X_Max[col];
1840  const double y1 = Y_Min[row];
1841  const double y2 = Y_Max[row];
1842 
1843  TPad *pad = new TPad(Form("tri_pad_%s_%d_%d", suffix.c_str(), row, col), "", x1, y1, x2, y2);
1844  pad->SetFillStyle(0);
1845  pad->SetRightMargin(0.02);
1846  pad->SetTopMargin(0.02);
1847  pad->SetLeftMargin(col == 0 ? 0.26 : 0.06);
1848  pad->SetBottomMargin(row == nVars - 1 ? 0.18 : 0.06);
1849  pad->Draw();
1850  pad->cd();
1851 
1852  const TriangleVar &xVar = triangleVars[col];
1853  const TriangleVar &yVar = triangleVars[row];
1854 
1855  if (row == col) {
1856  TH1F *h1 = new TH1F(Form("h_tri_1d_%s_%s", suffix.c_str(), xVar.name.c_str()),
1857  "", xVar.bins, xVar.min, xVar.max);
1858  std::string drawExpr = xVar.branch + ">>" + h1->GetName();
1859  tree->Draw(drawExpr.c_str(), weightExpr.c_str(), "goff");
1860  normalizeHist(h1);
1861  h1->SetStats(0);
1862  h1->GetXaxis()->SetTitle(row == nVars - 1 ? xVar.title.c_str() : "");
1863  h1->GetYaxis()->SetTitle(col == 0 ? "Entries" : "");
1864  h1->GetXaxis()->SetLabelSize(row == nVars - 1 ? 0.07 : 0.0);
1865  h1->GetYaxis()->SetLabelSize(col == 0 ? 0.07 : 0.0);
1866  h1->GetXaxis()->SetTitleSize(row == nVars - 1 ? 0.08 : 0.0);
1867  h1->GetYaxis()->SetTitleSize(col == 0 ? 0.08 : 0.0);
1868  h1->Draw("HIST");
1869  h1->Draw("E SAME");
1870  drawAsimovLineNoLegend(xVar.asimov);
1871  } else {
1872  TH2F *h2 = new TH2F(Form("h_tri_2d_%s_%s_%s", suffix.c_str(), yVar.name.c_str(), xVar.name.c_str()),
1873  "", xVar.bins, xVar.min, xVar.max, yVar.bins, yVar.min, yVar.max);
1874  std::string drawExpr = yVar.branch + ":" + xVar.branch + ">>" + h2->GetName();
1875  tree->Draw(drawExpr.c_str(), weightExpr.c_str(), "goff");
1876  normalizeHist(h2);
1877  h2->SetStats(0);
1878  h2->GetXaxis()->SetTitle(row == nVars - 1 ? xVar.title.c_str() : "");
1879  h2->GetYaxis()->SetTitle(col == 0 ? yVar.title.c_str() : "");
1880  h2->GetXaxis()->SetLabelSize(row == nVars - 1 ? 0.07 : 0.0);
1881  h2->GetYaxis()->SetLabelSize(col == 0 ? 0.07 : 0.0);
1882  h2->GetXaxis()->SetTitleSize(row == nVars - 1 ? 0.08 : 0.0);
1883  h2->GetYaxis()->SetTitleSize(col == 0 ? 0.08 : 0.0);
1884  h2->Draw("COLZ");
1885  drawAsimovMarkerNoLegend(xVar.asimov, yVar.asimov);
1886  }
1887  // return to canvas for next pad
1888  cTri->cd();
1889  }
1890  }
1891 
1892  cTri->Update();
1893  cTri->SaveAs((outputDir + "triangle" + suffix + ".png").c_str());
1894  };
1895 
1896  drawTrianglePlot("", "_unweighted", 0);
1897  if (hasUmbrellaWeight) {
1898  drawTrianglePlot("umbrella_weight", "_weighted", 0);
1899  } else {
1900  std::cout << "WARNING:::::No umbrella weights found, skipping weighted triangle plot." << std::endl;
1901  }
1902 
1903  drawTrianglePlot("(delm2_23 < 0)","_io_unweighted",-1);
1904  drawTrianglePlot("(delm2_23 > 0)","_no_unweighted",1);
1905  if (hasUmbrellaWeight) {
1906  drawTrianglePlot("(delm2_23 < 0) * umbrella_weight","_io_weighted",-1);
1907  drawTrianglePlot("(delm2_23 > 0) * umbrella_weight","_no_weighted",1);
1908  } else {
1909  std::cout << "WARNING:::::No umbrella weights found, skipping weighted triangle plots marginalized by ordering." << std::endl;
1910  }
1911 }
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.