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