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