MaCh3  2.6.1
Reference Guide
MCMCProcessor.cpp
Go to the documentation of this file.
1 #include "MCMCProcessor.h"
2 
4 #include "TChain.h"
5 #include "TF1.h"
6 #include "TVirtualFFT.h"
7 #include "ROOT/RDataFrame.hxx"
9 
10 //Only if GPU is enabled
11 #ifdef MaCh3_CUDA
13 #endif
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 
18 // ****************************
19 MCMCProcessor::MCMCProcessor(const std::string &InputFile) :
20  Chain(nullptr), StepCut(""), MadePostfit(false) {
21 // ****************************
22  MCMCFile = InputFile;
23 
26  MACH3LOG_INFO("Making post-fit processor for: {}", MCMCFile);
27 
28  ParStep = nullptr;
29  StepNumber = nullptr;
30  ReweightPosterior = false;
31  WeightValue = nullptr;
32 
33  Posterior = nullptr;
34  hviolin = nullptr;
35  hviolin_prior = nullptr;
36 
37  OutputFile = nullptr;
38 
39  BatchedAverages = nullptr;
40  SampleValues = nullptr;
41  SystValues = nullptr;
42  AccProbValues = nullptr;
43  AccProbBatchedAverages = nullptr;
44 
45  //KS:Hardcoded should be a way to get it via config or something
46  plotRelativeToPrior = false;
47  printToPDF = false;
48  plotBinValue = false;
49  PlotFlatPrior = true;
50  CacheMCMC = false;
51  ApplySmoothing = true;
52  FancyPlotNames = true;
53  doDiagMCMC = false;
54 
55  // KS: ROOT can compile FFT code but it will crash during run time. Turn off FFT dynamically
56 #ifdef MaCh3_FFT
57  useFFTAutoCorrelation = true;
58 #else
59  useFFTAutoCorrelation = false;
60 #endif
61  OutputSuffix = "_Process";
62  Post2DPlotThreshold = 1.e-5;
63 
64  nDraw = 0;
65  nEntries = 0;
67  nSteps = 0;
68  nBatches = 0;
69  AutoCorrLag = 0;
70 
71  nBins = 70;
72  DrawRange = 1.5;
73 
74  Posterior1DCut = "";
75  //KS:Those keep basic information for ParameterEnum
79  ParamFlat.resize(kNParameterEnum);
81  nParam.resize(kNParameterEnum);
82  CovPos.resize(kNParameterEnum);
84  CovConfig.resize(kNParameterEnum);
85 
86  ReweightNames = {"Weight"};
87  for(int i = 0; i < kNParameterEnum; i++)
88  {
89  ParamTypeStartPos[i] = 0;
90  nParam[i] = 0;
91  }
92  //Only if GPU is enabled
93  #ifdef MaCh3_CUDA
94  GPUProcessor = std::make_unique<MCMCProcessorGPU>();
95  #endif
96 }
97 
98 // ****************************
99 // The destructor
101 // ****************************
102  // Close the pdf file
103  MACH3LOG_INFO("Closing pdf in MCMCProcessor: {}", CanvasName.Data());
104  CanvasName += "]";
105  if(printToPDF) Posterior->Print(CanvasName);
106 
107  delete Covariance;
108  delete Correlation;
109  delete Central_Value;
110  delete Means;
111  delete Errors;
112  delete Means_Gauss;
113  delete Errors_Gauss;
114  delete Means_HPD;
115  delete Errors_HPD;
116  delete Errors_HPD_Positive;
117  delete Errors_HPD_Negative;
118 
119  if(WeightValue) delete[] WeightValue;
120  for (int i = 0; i < nDraw; ++i)
121  {
122  if(hpost[i] != nullptr) delete hpost[i];
123  }
124  if(CacheMCMC)
125  {
126  for (int i = 0; i < nDraw; ++i)
127  {
128  for (int j = 0; j < nDraw; ++j)
129  {
130  delete hpost2D[i][j];
131  }
132  delete[] ParStep[i];
133  }
134  delete[] ParStep;
135  }
136  if(StepNumber != nullptr) delete[] StepNumber;
137 
138  if(OutputFile != nullptr) OutputFile->Close();
139  if(OutputFile != nullptr) delete OutputFile;
140  delete Chain;
141 }
142 
143 // ***************
145 // ***************
146  // Scan the ROOT file for useful branches
147  ScanInput();
148 
149  // Setup the output
150  SetupOutput();
151 }
152 
153 // ***************
154 void MCMCProcessor::GetPostfit(TVectorD *&Central_PDF, TVectorD *&Errors_PDF, TVectorD *&Central_G, TVectorD *&Errors_G, TVectorD *&Peak_Values) {
155 // ***************
156  // Make the post fit
157  MakePostfit();
158 
159  // We now have the private members
160  Central_PDF = Means;
161  Errors_PDF = Errors;
162  Central_G = Means_Gauss;
163  Errors_G = Errors_Gauss;
164  Peak_Values = Means_HPD;
165 }
166 
167 // ***************
168 // Get post-fits for the ParameterEnum type, e.g. xsec params, ND params or flux params etc
169 void MCMCProcessor::GetPostfit_Ind(TVectorD *&PDF_Central, TVectorD *&PDF_Errors, TVectorD *&Peak_Values, ParameterEnum kParam) {
170 // ***************
171  // Make the post fit
172  MakePostfit();
173 
174  // Loop over the loaded param types
175  const int ParamTypeSize = int(ParamType.size());
176  int ParamNumber = 0;
177  for (int i = 0; i < ParamTypeSize; ++i) {
178  if (ParamType[i] != kParam) continue;
179  (*PDF_Central)(ParamNumber) = (*Means)(i);
180  (*PDF_Errors)(ParamNumber) = (*Errors)(i);
181  (*Peak_Values)(ParamNumber) = (*Means_HPD)(i);
182  ++ParamNumber;
183  }
184 }
185 
186 // ***************
187 void MCMCProcessor::GetCovariance(TMatrixDSym *&Cov, TMatrixDSym *&Corr) {
188 // ***************
190  else MakeCovariance();
191  Cov = static_cast<TMatrixDSym*>(Covariance->Clone());
192  Corr = static_cast<TMatrixDSym*>(Correlation->Clone());
193 }
194 
195 // ***************
197 // ***************
198  //KS: ROOT hates me... but we can create several instances of MCMC Processor, each with own TCanvas ROOT is mad and will delete if there is more than one canvas with the same name, so we add random number to avoid issue
199  auto rand = std::make_unique<TRandom3>(0);
200  const int uniform = int(rand->Uniform(0, 10000));
201  // Open a TCanvas to write the posterior onto
202  Posterior = std::make_unique<TCanvas>(("Posterior" + std::to_string(uniform)).c_str(), ("Posterior" + std::to_string(uniform)).c_str(), 0, 0, 1024, 1024);
203  //KS: No idea why but ROOT changed treatment of violin in R6. If you have non uniform binning this will results in very hard to see violin plots.
204  TCandle::SetScaledViolin(false);
205 
206  Posterior->SetGrid();
207  gStyle->SetOptStat(0);
208  gStyle->SetOptTitle(0);
209  Posterior->SetTickx();
210  Posterior->SetTicky();
211 
212  Posterior->SetBottomMargin(0.1);
213  Posterior->SetTopMargin(0.05);
214  Posterior->SetRightMargin(0.03);
215  Posterior->SetLeftMargin(0.15);
216 
217  //To avoid TCanvas::Print> messages
218  gErrorIgnoreLevel = kWarning;
219 
220  // Output file to write to
221  OutputName = MCMCFile + OutputSuffix +".root";
222 
223  // Output file
224  OutputFile = M3::Open(OutputName, "recreate", __FILE__, __LINE__);
225  OutputFile->cd();
226 }
227 
228 // ***************
229 void MCMCProcessor::DrawPosterior(const int i, TDirectory* PostDir, TDirectory* PostHistDir) {
230 // ***************
231  TString Title = "";
232  double Prior = 1.0, PriorError = 1.0;
233  GetNthParameter(i, Prior, PriorError, Title);
234  bool isFlat = GetParamFlat(i);
235 
236  if(ApplySmoothing) hpost[i]->Smooth();
237 
238  (*Central_Value)(i) = Prior;
239 
240  double Mean, Err, Err_p, Err_m;
241  GetArithmetic(hpost[i], Mean, Err);
242  (*Means)(i) = Mean;
243  (*Errors)(i) = Err;
244 
245  GetGaussian(hpost[i], Gauss.get(), Mean, Err);
246  (*Means_Gauss)(i) = Mean;
247  (*Errors_Gauss)(i) = Err;
248 
249  GetHPD(hpost[i], Mean, Err, Err_p, Err_m);
250  (*Means_HPD)(i) = Mean;
251  (*Errors_HPD)(i) = Err;
252  (*Errors_HPD_Positive)(i) = Err_p;
253  (*Errors_HPD_Negative)(i) = Err_m;
254 
255  // Write the results from the projection into the TVectors and TMatrices
256  (*Covariance)(i,i) = (*Errors)(i)*(*Errors)(i);
257  (*Correlation)(i,i) = 1.0;
258 
259  //KS: This need to be before SetMaximum(), this way plot is nicer as line end at the maximum
260  auto hpd = std::make_unique<TLine>((*Means_HPD)(i), hpost[i]->GetMinimum(), (*Means_HPD)(i), hpost[i]->GetMaximum());
261  SetTLineStyle(hpd.get(), kBlack, 2, kSolid);
262 
263  hpost[i]->SetLineWidth(2);
264  hpost[i]->SetLineColor(kBlue-1);
265  hpost[i]->SetMaximum(hpost[i]->GetMaximum()*DrawRange);
266  hpost[i]->SetTitle(Title);
267  hpost[i]->GetXaxis()->SetTitle(hpost[i]->GetTitle());
268 
269  // Now make the TLine for the Asimov
270  auto Asimov = std::make_unique<TLine>(Prior, hpost[i]->GetMinimum(), Prior, hpost[i]->GetMaximum());
271  SetTLineStyle(Asimov.get(), kRed-3, 2, kDashed);
272 
273  auto leg = std::make_unique<TLegend>(0.15, 0.6, 0.6, 0.95);
274  SetLegendStyle(leg.get(), 0.04);
275  leg->AddEntry(hpost[i], Form("#splitline{PDF}{#mu = %.2f, #sigma = %.2f}", hpost[i]->GetMean(), hpost[i]->GetRMS()), "l");
276  leg->AddEntry(Gauss.get(), Form("#splitline{Gauss}{#mu = %.2f, #sigma = %.2f}", Gauss->GetParameter(1), Gauss->GetParameter(2)), "l");
277  leg->AddEntry(hpd.get(), Form("#splitline{HPD}{#mu = %.2f, #sigma = %.2f (+%.2f-%.2f)}", (*Means_HPD)(i), (*Errors_HPD)(i), (*Errors_HPD_Positive)(i), (*Errors_HPD_Negative)(i)), "l");
278  if(isFlat && !PlotFlatPrior) leg->AddEntry(Asimov.get(), Form("#splitline{Prior}{x = %.2f}", Prior), "l");
279  else leg->AddEntry(Asimov.get(), Form("#splitline{Prior}{x = %.2f , #sigma = %.2f}", Prior, PriorError), "l");
280 
281  // Write to file
282  Posterior->SetName(Title);
283  Posterior->SetTitle(Title);
284 
285  //CW: Don't plot if this is a fixed histogram (i.e. the peak is the whole integral)
286  if (hpost[i]->GetMaximum() == hpost[i]->Integral()*DrawRange)
287  {
288  MACH3LOG_WARN("Found fixed parameter: {} ({}), moving on", Title, i);
289  ParamVaried[i] = false;
290  //KS:Set mean and error to prior for fixed parameters, it looks much better when fixed parameter has mean on prior rather than on 0 with 0 error.
291  (*Means_HPD)(i) = Prior;
292  (*Errors_HPD)(i) = PriorError;
293  (*Errors_HPD_Positive)(i) = PriorError;
294  (*Errors_HPD_Negative)(i) = PriorError;
295 
296  (*Means_Gauss)(i) = Prior;
297  (*Errors_Gauss)(i) = PriorError;
298 
299  (*Means)(i) = Prior;
300  (*Errors)(i) = PriorError;
301  return;
302  }
303 
304  // Store that this parameter is indeed being varied
305  ParamVaried[i] = true;
306 
307  // Draw onto the TCanvas
308  hpost[i]->Draw();
309  hpd->Draw("same");
310  Asimov->Draw("same");
311  leg->Draw("same");
312 
313  if(printToPDF) Posterior->Print(CanvasName);
314 
315  // cd into params directory in root file
316  PostDir->cd();
317  Posterior->Write();
318 
319  hpost[i]->SetName(Title);
320  hpost[i]->SetTitle(Title);
321  PostHistDir->cd();
322  hpost[i]->Write();
323 }
324 
325 // ****************************
326 std::pair<double, double> MCMCProcessor::GetHistRange(const int iParam) const {
327 // ****************************
328  return {
329  hpost[iParam]->GetXaxis()->GetXmin(),
330  hpost[iParam]->GetXaxis()->GetXmax()
331  };
332 }
333 
334 // ****************************
335 //CW: Function to make the post-fit
336 void MCMCProcessor::MakePostfit(const std::map<std::string, std::pair<double, double>>& Edges) {
337 // ****************************
338  // Check if we've already made post-fit
339  if (MadePostfit == true) return;
340  MadePostfit = true;
341 
342  // Check if the output file is ready
343  if (OutputFile == nullptr) MakeOutputFile();
344 
345  MACH3LOG_INFO("Starting {}", __func__);
346  TStopwatch clock;
347  clock.Start();
348 
349  int originalErrorLevel = gErrorIgnoreLevel;
350  gErrorIgnoreLevel = kFatal;
351 
352  // Directory for posteriors
353  TDirectory *PostDir = OutputFile->mkdir("Post");
354  TDirectory *PostHistDir = OutputFile->mkdir("Post_1d_hists");
355 
356  //KS: Apply additional Cuts, like mass ordering
357  std::string CutPosterior1D = "";
358  if(Posterior1DCut != "") {
359  CutPosterior1D = StepCut +" && " + Posterior1DCut;
360  } else CutPosterior1D = StepCut;
361 
362  // Apply reweighting
363  if (ReweightPosterior) {
364  for (const auto& name : ReweightNames) {
365  CutPosterior1D = "(" + CutPosterior1D + ")*(" + name + ")";
366  }
367  }
368  MACH3LOG_DEBUG("Using following cut {}", CutPosterior1D);
369 
370  // nDraw is number of draws we want to do
371  for (int i = 0; i < nDraw; ++i)
372  {
373  if (i % (nDraw/5) == 0) {
375  }
376  OutputFile->cd();
377  TString Title = "";
378  double Prior = 1.0, PriorError = 1.0;
379  GetNthParameter(i, Prior, PriorError, Title);
380 
381  // Get bin edges for histograms
382  double maxi, mini = M3::_BAD_DOUBLE_;
383  if (Edges.find(Title.Data()) != Edges.end()) {
384  mini = Edges.at(Title.Data()).first;
385  maxi = Edges.at(Title.Data()).second;
386  } else {
387  maxi = Chain->GetMaximum(BranchNames[i]);
388  mini = Chain->GetMinimum(BranchNames[i]);
389  }
390  MACH3LOG_DEBUG("Initialising histogram for {} with binning {:.4f}, {:.4f}", Title, mini, maxi);
391  // This holds the posterior density
392  // KS: WARNING do NOT SetDirectory(nullptr) this will cause issue with Project()
393  // I know is tempting to avoid ROOT memory management but please do not.
394  hpost[i] = new TH1D(BranchNames[i], BranchNames[i], nBins, mini, maxi);
395  hpost[i]->SetMinimum(0);
396  hpost[i]->GetYaxis()->SetTitle("Steps");
397  hpost[i]->GetYaxis()->SetNoExponent(false);
398 
399  // Project BranchNames[i] onto hpost, applying stepcut
400  Chain->Project(BranchNames[i], BranchNames[i], CutPosterior1D.c_str());
401 
402  DrawPosterior(i, PostDir, PostHistDir);
403  } // end the for loop over nDraw
404 
405  OutputFile->cd();
406  TTree *SettingsBranch = new TTree("Settings", "Settings");
407  int NDParameters = nParam[kNDPar];
408  SettingsBranch->Branch("NDParameters", &NDParameters);
410  SettingsBranch->Branch("NDParametersStartingPos", &NDParametersStartingPos);
411 
412  SettingsBranch->Branch("NDSamplesBins", &NDSamplesBins);
413  SettingsBranch->Branch("NDSamplesNames", &NDSamplesNames);
414 
415  SettingsBranch->Fill();
416  SettingsBranch->Write();
417  delete SettingsBranch;
418 
419  TDirectory *Names = OutputFile->mkdir("Names");
420  Names->cd();
421  for (std::vector<TString>::iterator it = BranchNames.begin(); it != BranchNames.end(); ++it) {
422  TObjString((*it)).Write();
423  }
424  Names->Close();
425  delete Names;
426 
427  OutputFile->cd();
428  Central_Value->Write("Central_Value");
429  Means->Write("PDF_Means");
430  Errors->Write("PDF_Error");
431  Means_Gauss->Write("Gauss_Means");
432  Errors_Gauss->Write("Gauss_Errors");
433  Means_HPD->Write("Means_HPD");
434  Errors_HPD->Write("Errors_HPD");
435  Errors_HPD_Positive->Write("Errors_HPD_Positive");
436  Errors_HPD_Negative->Write("Errors_HPD_Negative");
437 
438  PostDir->Close();
439  delete PostDir;
440  PostHistDir->Close();
441  delete PostHistDir;
442 
443  clock.Stop();
444  MACH3LOG_INFO("{} took {:.2f}s to", __func__, clock.RealTime());
445 
446  // restore original warning setting
447  gErrorIgnoreLevel = originalErrorLevel;
448 } // Have now written the postfit projections
449 
450 // *******************
451 //CW: Draw the postfit
453 // *******************
454  if (OutputFile == nullptr) MakeOutputFile();
455 
456  // Make the prefit plot
457  std::unique_ptr<TH1D> prefit = MakePrefit();
458 
459  prefit->GetXaxis()->SetTitle("");
460  // cd into the output file
461  OutputFile->cd();
462 
463  std::string CutPosterior1D = "";
464  if(Posterior1DCut != "")
465  {
466  CutPosterior1D = StepCut +" && " + Posterior1DCut;
467  }
468  else CutPosterior1D = StepCut;
469 
470  // Make a TH1D of the central values and the errors
471  std::unique_ptr<TH1D> paramPlot = std::make_unique<TH1D>("paramPlot", "paramPlot", nDraw, 0, nDraw);
472  paramPlot->SetDirectory(nullptr);
473  paramPlot->SetName("mach3params");
474  paramPlot->SetTitle(CutPosterior1D.c_str());
475  paramPlot->SetFillStyle(3001);
476  paramPlot->SetFillColor(kBlue-1);
477  paramPlot->SetMarkerColor(paramPlot->GetFillColor());
478  paramPlot->SetMarkerStyle(20);
479  paramPlot->SetLineColor(paramPlot->GetFillColor());
480  paramPlot->SetMarkerSize(prefit->GetMarkerSize());
481  paramPlot->GetXaxis()->SetTitle("");
482 
483  // Same but with Gaussian output
484  std::unique_ptr<TH1D> paramPlot_Gauss = M3::Clone(paramPlot.get());
485  paramPlot_Gauss->SetMarkerColor(kOrange-5);
486  paramPlot_Gauss->SetMarkerStyle(23);
487  paramPlot_Gauss->SetLineWidth(2);
488  paramPlot_Gauss->SetMarkerSize((prefit->GetMarkerSize())*0.75);
489  paramPlot_Gauss->SetFillColor(paramPlot_Gauss->GetMarkerColor());
490  paramPlot_Gauss->SetFillStyle(3244);
491  paramPlot_Gauss->SetLineColor(paramPlot_Gauss->GetMarkerColor());
492  paramPlot_Gauss->GetXaxis()->SetTitle("");
493 
494  // Same but with Gaussian output
495  std::unique_ptr<TH1D> paramPlot_HPD = M3::Clone(paramPlot.get());
496  paramPlot_HPD->SetMarkerColor(kBlack);
497  paramPlot_HPD->SetMarkerStyle(25);
498  paramPlot_HPD->SetLineWidth(2);
499  paramPlot_HPD->SetMarkerSize((prefit->GetMarkerSize())*0.5);
500  paramPlot_HPD->SetFillColor(0);
501  paramPlot_HPD->SetFillStyle(0);
502  paramPlot_HPD->SetLineColor(paramPlot_HPD->GetMarkerColor());
503  paramPlot_HPD->GetXaxis()->SetTitle("");
504 
505  // Set labels and data
506  for (int i = 0; i < nDraw; ++i)
507  {
508  //Those keep which parameter type we run currently and relative number
509  int ParamEnu = ParamType[i];
510  int ParamNo = i - ParamTypeStartPos[ParameterEnum(ParamEnu)];
511 
512  //KS: Slightly hacky way to get relative to prior or nominal as this is convention we use
513  //This only applies for xsec for other systematic types doesn't matter
514  double CentralValueTemp = 0;
515  double Central, Central_gauss, Central_HPD;
516  double Err, Err_Gauss, Err_HPD;
517 
519  {
520  CentralValueTemp = ParamCentral[ParamEnu][ParamNo];
521  // Normalise the prior relative the nominal/prior, just the way we get our fit results in MaCh3
522  if ( CentralValueTemp != 0)
523  {
524  Central = (*Means)(i) / CentralValueTemp;
525  Err = (*Errors)(i) / CentralValueTemp;
526 
527  Central_gauss = (*Means_Gauss)(i) / CentralValueTemp;
528  Err_Gauss = (*Errors_Gauss)(i) / CentralValueTemp;
529 
530  Central_HPD = (*Means_HPD)(i) / CentralValueTemp;
531  Err_HPD = (*Errors_HPD)(i) / CentralValueTemp;
532  }
533  else {
534  Central = 1+(*Means)(i);
535  Err = (*Errors)(i);
536 
537  Central_gauss = 1+(*Means_Gauss)(i);
538  Err_Gauss = (*Errors_Gauss)(i);
539 
540  Central_HPD = 1+(*Means_HPD)(i) ;
541  Err_HPD = (*Errors_HPD)(i);
542  }
543  }
544  //KS: Just get value of each parameter without dividing by prior
545  else
546  {
547  Central = (*Means)(i);
548  Err = (*Errors)(i);
549 
550  Central_gauss = (*Means_Gauss)(i);
551  Err_Gauss = (*Errors_Gauss)(i);
552 
553  Central_HPD = (*Means_HPD)(i) ;
554  Err_HPD = (*Errors_HPD)(i);
555  }
556 
557  paramPlot->SetBinContent(i+1, Central);
558  paramPlot->SetBinError(i+1, Err);
559 
560  paramPlot_Gauss->SetBinContent(i+1, Central_gauss);
561  paramPlot_Gauss->SetBinError(i+1, Err_Gauss);
562 
563  paramPlot_HPD->SetBinContent(i+1, Central_HPD);
564  paramPlot_HPD->SetBinError(i+1, Err_HPD);
565 
566  paramPlot->GetXaxis()->SetBinLabel(i+1, prefit->GetXaxis()->GetBinLabel(i+1));
567  paramPlot_Gauss->GetXaxis()->SetBinLabel(i+1, prefit->GetXaxis()->GetBinLabel(i+1));
568  paramPlot_HPD->GetXaxis()->SetBinLabel(i+1, prefit->GetXaxis()->GetBinLabel(i+1));
569  }
570  prefit->GetXaxis()->LabelsOption("v");
571  paramPlot->GetXaxis()->LabelsOption("v");\
572  paramPlot_Gauss->GetXaxis()->LabelsOption("v");
573  paramPlot_HPD->GetXaxis()->LabelsOption("v");
574 
575  // Make a TLegend
576  auto CompLeg = std::make_unique<TLegend>(0.33, 0.73, 0.76, 0.95);
577  CompLeg->AddEntry(prefit.get(), "Prefit", "fp");
578  CompLeg->AddEntry(paramPlot.get(), "Postfit PDF", "fp");
579  CompLeg->AddEntry(paramPlot_Gauss.get(), "Postfit Gauss", "fp");
580  CompLeg->AddEntry(paramPlot_HPD.get(), "Postfit HPD", "lfep");
581  CompLeg->SetFillColor(0);
582  CompLeg->SetFillStyle(0);
583  CompLeg->SetLineWidth(0);
584  CompLeg->SetLineStyle(0);
585  CompLeg->SetBorderSize(0);
586 
587  const std::vector<double> Margins = GetMargins(Posterior);
588  Posterior->SetBottomMargin(0.2);
589 
590  OutputFile->cd();
591 
592  // Write the individual ones
593  prefit->Write("param_xsec_prefit");
594  paramPlot->Write("param_xsec");
595  paramPlot_Gauss->Write("param_xsec_gaus");
596  paramPlot_HPD->Write("param_xsec_HPD");
597 
598  // Plot the xsec parameters (0 to ~nXsec-nFlux) nXsec == xsec + flux, quite confusing I know
599  // Have already looked through the branches earlier
600  if(plotRelativeToPrior) prefit->GetYaxis()->SetTitle("Variation rel. prior");
601  else prefit->GetYaxis()->SetTitle("Parameter Value");
602  prefit->GetYaxis()->SetRangeUser(-2.5, 2.5);
603 
604  // And the combined
605  prefit->Draw("e2");
606  paramPlot->Draw("e2, same");
607  paramPlot_Gauss->Draw("e2, same");
608  paramPlot_HPD->Draw("e1, same");
609  CompLeg->Draw("same");
610  Posterior->Write("param_xsec_canv");
611 
612  //KS: Tells how many parameters in one canvas we want
613  constexpr int IntervalsSize = 20;
614  const int NIntervals = nDraw/IntervalsSize;
615 
616  for (int i = 0; i < NIntervals+1; ++i)
617  {
618  int RangeMin = i*IntervalsSize;
619  int RangeMax =RangeMin + IntervalsSize;
620  if(i == NIntervals+1) {
621  RangeMin = i*IntervalsSize;
622  RangeMax = nDraw;
623  }
624  if(RangeMin >= nDraw) break;
625 
626  double ymin = std::numeric_limits<double>::max();
627  double ymax = -std::numeric_limits<double>::max();
628  for (int b = RangeMin; b <= RangeMax; ++b) {
629  // prefit
630  {
631  double val = prefit->GetBinContent(b);
632  double err = prefit->GetBinError(b);
633  ymin = std::min(ymin, val - err);
634  ymax = std::max(ymax, val + err);
635  }
636  // paramPlot_HPD
637  {
638  double val = paramPlot_HPD->GetBinContent(b);
639  double err = paramPlot_HPD->GetBinError(b);
640  ymin = std::min(ymin, val - err);
641  ymax = std::max(ymax, val + err);
642  }
643  }
644 
645  double margin = 0.1 * (ymax - ymin);
646  prefit->GetYaxis()->SetRangeUser(ymin - margin, ymax + margin);
647 
648  prefit->GetXaxis()->SetRangeUser(RangeMin, RangeMax);
649  paramPlot->GetXaxis()->SetRangeUser(RangeMin, RangeMax);
650  paramPlot_Gauss->GetXaxis()->SetRangeUser(RangeMin, RangeMax);
651  paramPlot_HPD->GetXaxis()->SetRangeUser(RangeMin, RangeMax);
652 
653  // And the combined
654  prefit->Draw("e2");
655  paramPlot->Draw("e2, same");
656  paramPlot_Gauss->Draw("e2, same");
657  paramPlot_HPD->Draw("e1, same");
658  CompLeg->Draw("same");
659  if(printToPDF) Posterior->Print(CanvasName);
660  Posterior->Clear();
661  }
662 
663  if(nParam[kNDPar] > 0)
664  {
665  int Start = ParamTypeStartPos[kNDPar];
666  int NDbinCounter = Start;
667  //KS: Make prefit postfit for each ND sample, having all of them at the same plot is unreadable
668  for(unsigned int i = 0; i < NDSamplesNames.size(); i++ )
669  {
670  std::string NDname = NDSamplesNames[i];
671  NDbinCounter += NDSamplesBins[i];
672  OutputFile->cd();
673  prefit->GetYaxis()->SetTitle(("Variation for "+NDname).c_str());
674  prefit->GetYaxis()->SetRangeUser(0.6, 1.4);
675  prefit->GetXaxis()->SetRangeUser(Start, NDbinCounter);
676 
677  paramPlot->GetYaxis()->SetTitle(("Variation for "+NDname).c_str());
678  paramPlot->GetYaxis()->SetRangeUser(0.6, 1.4);
679  paramPlot->GetXaxis()->SetRangeUser(Start, NDbinCounter);
680  paramPlot->SetTitle(CutPosterior1D.c_str());
681 
682  paramPlot_Gauss->GetYaxis()->SetTitle(("Variation for "+NDname).c_str());
683  paramPlot_Gauss->GetYaxis()->SetRangeUser(0.6, 1.4);
684  paramPlot_Gauss->GetXaxis()->SetRangeUser(Start, NDbinCounter);
685  paramPlot_Gauss->SetTitle(CutPosterior1D.c_str());
686 
687  paramPlot_HPD->GetYaxis()->SetTitle(("Variation for "+NDname).c_str());
688  paramPlot_HPD->GetYaxis()->SetRangeUser(0.6, 1.4);
689  paramPlot_HPD->GetXaxis()->SetRangeUser(Start, NDbinCounter);
690  paramPlot_HPD->SetTitle(CutPosterior1D.c_str());
691 
692  prefit->Write(("param_"+NDname+"_prefit").c_str());
693  paramPlot->Write(("param_"+NDname).c_str());
694  paramPlot_Gauss->Write(("param_"+NDname+"_gaus").c_str());
695  paramPlot_HPD->Write(("param_"+NDname+"_HPD").c_str());
696 
697  prefit->Draw("e2");
698  paramPlot->Draw("e2, same");
699  paramPlot_Gauss->Draw("e1, same");
700  paramPlot_HPD->Draw("e1, same");
701  CompLeg->Draw("same");
702  Posterior->Write(("param_"+NDname+"_canv").c_str());
703  if(printToPDF) Posterior->Print(CanvasName);
704  Posterior->Clear();
705  Start += NDSamplesBins[i];
706  }
707  }
708  //KS: Return Margin to default one
709  SetMargins(Posterior, Margins);
710 }
711 
712 // *********************
713 // Make fancy Credible Intervals plots
714 void MCMCProcessor::MakeCredibleIntervals(const std::vector<double>& CredibleIntervals,
715  const std::vector<Color_t>& CredibleIntervalsColours,
716  const bool CredibleInSigmas) {
717 // *********************
718  if(hpost[0] == nullptr) MakePostfit();
719 
720  MACH3LOG_INFO("Starting {}", __func__);
721  const double LeftMargin = Posterior->GetLeftMargin();
722  Posterior->SetLeftMargin(0.15);
723 
724  // KS: Sanity check of size and ordering is correct
725  CheckCredibleIntervalsOrder(CredibleIntervals, CredibleIntervalsColours);
726  const int nCredible = int(CredibleIntervals.size());
727  std::vector<std::unique_ptr<TH1D>> hpost_copy(nDraw);
728  std::vector<std::vector<std::unique_ptr<TH1D>>> hpost_cl(nDraw);
729 
730  //KS: Copy all histograms to be thread safe
731  for (int i = 0; i < nDraw; ++i)
732  {
733  hpost_copy[i] = M3::Clone<TH1D>(hpost[i], Form("hpost_copy_%i", i));
734  hpost_cl[i].resize(nCredible);
735  for (int j = 0; j < nCredible; ++j)
736  {
737  hpost_cl[i][j] = M3::Clone<TH1D>(hpost[i], Form("hpost_copy_%i_CL_%f", i, CredibleIntervals[j]));
738 
739  //KS: Reset to get rid to TF1 otherwise we run into segfault :(
740  hpost_cl[i][j]->Reset("");
741  hpost_cl[i][j]->Fill(0.0, 0.0);
742  }
743  }
744 
745  #ifdef MULTITHREAD
746  #pragma omp parallel for
747  #endif
748  for (int i = 0; i < nDraw; ++i)
749  {
751  hpost_copy[i]->Scale(1. / hpost_copy[i]->Integral());
752  for (int j = 0; j < nCredible; ++j)
753  {
754  // Scale the histograms before getting credible intervals
755  hpost_cl[i][j]->Scale(1. / hpost_cl[i][j]->Integral());
756  GetCredibleIntervalSig(hpost_copy[i], hpost_cl[i][j], CredibleInSigmas, CredibleIntervals[j]);
757 
758  hpost_cl[i][j]->SetFillColor(CredibleIntervalsColours[j]);
759  hpost_cl[i][j]->SetLineWidth(1);
760  }
761  hpost_copy[i]->GetYaxis()->SetTitleOffset(1.8);
762  hpost_copy[i]->SetLineWidth(1);
763  hpost_copy[i]->SetMaximum(hpost_copy[i]->GetMaximum()*1.2);
764  hpost_copy[i]->SetLineWidth(2);
765  hpost_copy[i]->SetLineColor(kBlack);
766  hpost_copy[i]->GetYaxis()->SetTitle("Posterior Probability");
767  }
768 
769  OutputFile->cd();
770  TDirectory *CredibleDir = OutputFile->mkdir("Credible");
771 
772  for (int i = 0; i < nDraw; ++i)
773  {
774  if(!ParamVaried[i]) continue;
775 
776  // Now make the TLine for the Asimov
777  TString Title = "";
778  double Prior = 1.0, PriorError = 1.0;
779  GetNthParameter(i, Prior, PriorError, Title);
780 
781  auto Asimov = std::make_unique<TLine>(Prior, hpost_copy[i]->GetMinimum(), Prior, hpost_copy[i]->GetMaximum());
782  SetTLineStyle(Asimov.get(), kRed-3, 2, kDashed);
783 
784  auto legend = std::make_unique<TLegend>(0.20, 0.7, 0.4, 0.92);
785  SetLegendStyle(legend.get(), 0.03);
786  hpost_copy[i]->Draw("HIST");
787 
788  for (int j = 0; j < nCredible; ++j)
789  hpost_cl[i][j]->Draw("HIST SAME");
790  for (int j = nCredible-1; j >= 0; --j)
791  {
792  if(CredibleInSigmas)
793  legend->AddEntry(hpost_cl[i][j].get(), Form("%.0f#sigma Credible Interval", CredibleIntervals[j]), "f");
794  else
795  legend->AddEntry(hpost_cl[i][j].get(), Form("%.0f%% Credible Interval", CredibleIntervals[j]*100), "f");
796  }
797  legend->AddEntry(Asimov.get(), Form("#splitline{Prior}{x = %.2f , #sigma = %.2f}", Prior, PriorError), "l");
798  legend->Draw("SAME");
799  Asimov->Draw("SAME");
800 
801  // Write to file
802  Posterior->SetName(hpost[i]->GetName());
803  Posterior->SetTitle(hpost[i]->GetTitle());
804 
805  if(printToPDF) Posterior->Print(CanvasName);
806  // cd into directory in root file
807  CredibleDir->cd();
808  Posterior->Write();
809  }
810  CredibleDir->Close();
811  delete CredibleDir;
812 
813  OutputFile->cd();
814 
815  //Set back to normal
816  Posterior->SetLeftMargin(LeftMargin);
817 }
818 
819 // *********************
820 // Make fancy violin plots
822 // *********************
823  //KS: Make sure we have steps
824  if(!CacheMCMC) CacheSteps();
825  MACH3LOG_INFO("Starting {}", __func__);
826 
827  //KS: Find min and max to make histogram in range
828  double maxi_y = -9999;
829  double mini_y = +9999;
830  for (int i = 0; i < nDraw; ++i)
831  {
832  auto range = GetHistRange(i);
833  mini_y = std::min(mini_y, range.first);
834  maxi_y = std::max(maxi_y, range.second);
835  }
836 
837  const int vBins = (maxi_y-mini_y)*25;
838  hviolin = std::make_unique<TH2D>("hviolin", "hviolin", nDraw, 0, nDraw, vBins, mini_y, maxi_y);
839  hviolin->SetDirectory(nullptr);
840  //KS: Prior has larger errors so we increase range and number of bins
841  constexpr int PriorFactor = 4;
842  hviolin_prior = std::make_unique<TH2D>("hviolin_prior", "hviolin_prior", nDraw, 0, nDraw, PriorFactor*vBins, PriorFactor*mini_y, PriorFactor*maxi_y);
843  hviolin_prior->SetDirectory(nullptr);
844 
845  auto rand = std::make_unique<TRandom3>(0);
846  std::vector<double> PriorVec(nDraw);
847  std::vector<double> PriorErrorVec(nDraw);
848  std::vector<bool> PriorFlatVec(nDraw);
849 
850  for (int x = 0; x < nDraw; ++x)
851  {
852  TString Title;
853  double Prior, PriorError;
854 
855  GetNthParameter(x, Prior, PriorError, Title);
856  //Set fancy labels
857  hviolin->GetXaxis()->SetBinLabel(x+1, Title);
858  hviolin_prior->GetXaxis()->SetBinLabel(x+1, Title);
859  PriorVec[x] = Prior;
860  PriorErrorVec[x] = PriorError;
861 
862  PriorFlatVec[x] = GetParamFlat(x);
863  }
864 
865  TStopwatch clock;
866  clock.Start();
867 
868  // nDraw is number of draws we want to do
869  #ifdef MULTITHREAD
870  #pragma omp parallel for
871  #endif
872  for (int x = 0; x < nDraw; ++x)
873  {
874  //KS: Consider another treatment for fixed params
875  //if (ParamVaried[x] == false) continue;
876  for (int k = 0; k < nEntries; ++k)
877  {
878  //KS: Burn in cut
879  if(StepNumber[k] < BurnInCut) continue;
880 
881  //KS: We know exactly which x bin we will end up, find y bin. This allow to avoid costly Fill() and enable multithreading because I am master of faster
882  const double y = hviolin->GetYaxis()->FindBin(ParStep[x][k]);
883  hviolin->SetBinContent(x+1, y, hviolin->GetBinContent(x+1, y)+1);
884  }
885 
886  //KS: If we set option to not plot flat prior and param has flat prior then we skip this step
887  if(!(!PlotFlatPrior && PriorFlatVec[x]))
888  {
889  for (int k = 0; k < nEntries; ++k)
890  {
891  const double Entry = rand->Gaus(PriorVec[x], PriorErrorVec[x]);
892  const double y = hviolin_prior->GetYaxis()->FindBin(Entry);
893  hviolin_prior->SetBinContent(x+1, y, hviolin_prior->GetBinContent(x+1, y)+1);
894  }
895  }
896  } // end the for loop over nDraw
897  clock.Stop();
898  MACH3LOG_INFO("Making Violin plot took {:.2f}s to finish for {} steps", clock.RealTime(), nEntries);
899 
900  //KS: Tells how many parameters in one canvas we want
901  constexpr int IntervalsSize = 10;
902  const int NIntervals = nDraw/IntervalsSize;
903 
904  hviolin->GetYaxis()->SetTitle("Parameter Value");
905  hviolin->GetXaxis()->SetTitle();
906  hviolin->GetXaxis()->LabelsOption("v");
907 
908  hviolin_prior->GetYaxis()->SetTitle("Parameter Value");
909  hviolin_prior->GetXaxis()->SetTitle();
910  hviolin_prior->GetXaxis()->LabelsOption("v");
911 
912  hviolin_prior->SetLineColor(kRed);
913  hviolin_prior->SetMarkerColor(kRed);
914  hviolin_prior->SetFillColorAlpha(kRed, 0.35);
915  hviolin_prior->SetMarkerStyle(20);
916  hviolin_prior->SetMarkerSize(0.5);
917 
918  // These control violin width, if you use larger then 1 they will most likely overlay, so be cautious
919  hviolin_prior->SetBarWidth(1.0);
920  hviolin_prior->SetBarOffset(0);
921 
922  hviolin->SetLineColor(kBlue);
923  hviolin->SetMarkerColor(kBlue);
924  hviolin->SetFillColorAlpha(kBlue, 0.35);
925  hviolin->SetMarkerStyle(20);
926  hviolin->SetMarkerSize(1.0);
927 
928  const double BottomMargin = Posterior->GetBottomMargin();
929  Posterior->SetBottomMargin(0.2);
930 
931  OutputFile->cd();
932  hviolin->Write("param_violin");
933  hviolin_prior->Write("param_violin_prior");
934  //KS: This is mostly for example plots, we have full file in the ROOT file so can do much better plot later
935  hviolin->GetYaxis()->SetRangeUser(-1, +2);
936  hviolin_prior->GetYaxis()->SetRangeUser(-1, +2);
937  for (int i = 0; i < NIntervals+1; ++i)
938  {
939  int RangeMin = i*IntervalsSize;
940  int RangeMax = RangeMin + IntervalsSize;
941  if(i == NIntervals+1) {
942  RangeMin = i*IntervalsSize;
943  RangeMax = nDraw;
944  }
945  if(RangeMin >= nDraw) break;
946 
947  hviolin->GetXaxis()->SetRangeUser(RangeMin, RangeMax);
948  hviolin_prior->GetXaxis()->SetRangeUser(RangeMin, RangeMax);
949 
950  //KS: ROOT6 has some additional options, consider updating it. more https://root.cern/doc/master/classTHistPainter.html#HP140b
951  hviolin_prior->Draw("violinX(03100300)");
952  hviolin->Draw("violinX(03100300) SAME");
953  if(printToPDF) Posterior->Print(CanvasName);
954  }
955  //KS: Return Margin to default one
956  Posterior->SetBottomMargin(BottomMargin);
957 }
958 
959 
960 // *********************
961 // Make the post-fit covariance matrix in all dimensions
963 // *********************
964  if (OutputFile == nullptr) MakeOutputFile();
965 
966  MACH3LOG_INFO("Making post-fit covariances...");
967  // Check that the diagonal entries have been filled
968  // i.e. MakePostfit() has been called
969  for (int i = 0; i < nDraw; ++i) {
970  if ((*Covariance)(i,i) == M3::_BAD_DOUBLE_) {
971  MACH3LOG_INFO("Have not run diagonal elements in covariance, will do so now by calling MakePostfit()");
972  MakePostfit();
973  break;
974  }
975  }
976 
977  TStopwatch clock;
978  clock.Start();
979 
980  TDirectory *PostHistDir = OutputFile->mkdir("Post_2d_hists");
981  PostHistDir->cd();
982  gStyle->SetPalette(55);
983 
984 
985  // Define RDataFrame
986  ROOT::RDataFrame df(*Chain);
987 
988  // Apply selection once
989  ROOT::RDF::RNode dfToUse = df.Filter(StepCut);
990 
991  // Apply reweighting once
992  if (ReweightPosterior)
993  {
994  TString WeightExpression = "1.0";
995  for (const auto &name : ReweightNames) {
996  WeightExpression = "(" + WeightExpression + ")*(" + name + ")";
997  }
998  dfToUse = dfToUse.Define("MCMC_RDF_WEIGHT", WeightExpression.Data());
999  }
1000 
1001  struct CovarianceHistogram {
1002  int i, j;
1003  TString Title_i, Title_j, DrawMe;
1004  ROOT::RDF::RResultPtr<TH2D> Histogram;
1005  };
1006 
1007  std::vector<CovarianceHistogram> Histograms;
1008 
1009  // Number of covariance elements
1010  const int nCov = nDraw * (nDraw - 1) / 2;
1011  Histograms.reserve(nCov);
1012 
1013  MACH3LOG_INFO("Booking {} 2D histograms...", nCov);
1014 
1015  // Now we are sure we have the diagonal elements, let's make the off-diagonals
1016  for (int i = 0; i < nDraw; ++i)
1017  {
1018  TString Title_i = "";
1019  double Prior_i, PriorError;
1020 
1021  GetNthParameter(i, Prior_i, PriorError, Title_i);
1022 
1023  double xmin = hpost[i]->GetXaxis()->GetXmin();
1024  double xmax = hpost[i]->GetXaxis()->GetXmax();
1025 
1026  // Loop over the other parameters to get the correlations
1027  for (int j = 0; j <= i; ++j) {
1028  // Skip the diagonal elements which we've already done above
1029  if (j == i) continue;
1030 
1031  // If this parameter isn't varied
1032  if (ParamVaried[j] == false) {
1033  (*Covariance)(i,j) = 0.0;
1034  (*Covariance)(j,i) = (*Covariance)(i,j);
1035  (*Correlation)(i,j) = 0.0;
1036  (*Correlation)(j,i) = (*Correlation)(i,j);
1037  continue;
1038  }
1039 
1040  TString Title_j = "";
1041  double Prior_j, PriorError_j;
1042  GetNthParameter(j, Prior_j, PriorError_j, Title_j);
1043 
1044  TString DrawMe = BranchNames[j] + ":" + BranchNames[i];
1045  double ymin = hpost[j]->GetXaxis()->GetXmin();
1046  double ymax = hpost[j]->GetXaxis()->GetXmax();
1047 
1048  ROOT::RDF::RResultPtr<TH2D> hpost_2D;
1049 
1050  // Book unweighted histogram
1051  if (!ReweightPosterior)
1052  {
1053  hpost_2D = dfToUse.Histo2D({DrawMe.Data(), DrawMe.Data(),
1054  nBins, xmin, xmax, nBins, ymin, ymax},
1055  BranchNames[i].Data(), BranchNames[j].Data());
1056  }
1057  else // Book weighted histogram[
1058  {
1059  hpost_2D = dfToUse.Histo2D({DrawMe.Data(), DrawMe.Data(),
1060  nBins, xmin, xmax, nBins, ymin, ymax},
1061  BranchNames[i].Data(), BranchNames[j].Data(), "MCMC_RDF_WEIGHT");
1062  }
1063  Histograms.push_back({i, j, Title_i, Title_j, DrawMe, hpost_2D});
1064  } // End j loop
1065  } // End i loop
1066  MACH3LOG_INFO("Finished booking {} histograms. Now executing RDF event loop...", Histograms.size());
1067 
1068  // The first GetPtr() triggers the RDF event loop.
1069  //
1070  // Because ALL histograms have already been booked, RDF fills
1071  // all of them in the same event loop.
1072  if (!Histograms.empty()) {
1073  TH2D *dummy = Histograms[0].Histogram.GetPtr();
1074  (void)dummy;
1075  }
1076 
1077  clock.Stop();
1078 
1079  MACH3LOG_INFO("RDataFrame event loop took {:.2f}s for {} entries",
1080  clock.RealTime(), nEntries);
1081  TStopwatch processingClock;
1082  processingClock.Start();
1083 
1084  for (auto &Entry : Histograms)
1085  {
1086  const int i = Entry.i;
1087  const int j = Entry.j;
1088 
1089  TH2D *hpost_2D = Entry.Histogram.GetPtr();
1090  hpost_2D->SetMinimum(0);
1091  hpost_2D->GetXaxis()->SetTitle(Entry.Title_i);
1092  hpost_2D->GetYaxis()->SetTitle(Entry.Title_j);
1093  hpost_2D->GetZaxis()->SetTitle("Steps");
1094 
1095  if (ApplySmoothing) hpost_2D->Smooth();
1096  // Get the Covariance for these two parameters
1097  (*Covariance)(i,j) = hpost_2D->GetCovariance();
1098  (*Covariance)(j,i) = (*Covariance)(i,j);
1099  (*Correlation)(i,j) = hpost_2D->GetCorrelationFactor();
1100  (*Correlation)(j,i) = (*Correlation)(i,j);
1101 
1102  if(printToPDF)
1103  {
1104  if(std::fabs((*Correlation)(i,j)) > Post2DPlotThreshold)
1105  {
1106  Posterior->cd();
1107  hpost_2D->Draw("colz");
1108  Posterior->SetName(hpost_2D->GetName());
1109  Posterior->SetTitle(hpost_2D->GetTitle());
1110  Posterior->Print(CanvasName);
1111  hpost_2D->Write(hpost_2D->GetTitle());
1112  }
1113  }
1114  }
1115  processingClock.Stop();
1116 
1117  MACH3LOG_INFO("Processing covariance histograms took {:.2f}s", processingClock.RealTime());
1118 
1119  PostHistDir->Close();
1120  delete PostHistDir;
1121  OutputFile->cd();
1122  Covariance->Write("Covariance");
1123  Correlation->Write("Correlation");
1124 }
1125 
1126 // ***************
1127 //KS: Cache all steps to allow multithreading, hit RAM quite a bit
1129 // ***************
1130  if(CacheMCMC == true) return;
1131 
1132  CacheMCMC = true;
1133 
1134  if(ParStep != nullptr)
1135  {
1136  MACH3LOG_ERROR("It look like ParStep was already filled ");
1137  MACH3LOG_ERROR("Even though it is used for MakeCovariance_MP and for DiagMCMC ");
1138  MACH3LOG_ERROR("it has different structure in both for cache hits, sorry ");
1139  throw MaCh3Exception(__FILE__ , __LINE__ );
1140  }
1141 
1142  MACH3LOG_INFO("Caching input tree...");
1143  MACH3LOG_INFO("Allocating {:.2f} MB", double(sizeof(M3::float_t)*nDraw*nEntries)/1.E6);
1144  TStopwatch clock;
1145  clock.Start();
1146 
1147  ParStep = new M3::float_t*[nDraw];
1148  StepNumber = new unsigned int[nEntries];
1149 
1150  hpost2D.resize(nDraw);
1151  for (int i = 0; i < nDraw; ++i)
1152  {
1153  ParStep[i] = new M3::float_t[nEntries];
1154  hpost2D[i].resize(nDraw);
1155  for (int j = 0; j < nEntries; ++j)
1156  {
1157  ParStep[i][j] = -999.99;
1158  //KS: Set this only once
1159  if(i == 0) StepNumber[j] = 0;
1160  }
1161  }
1162 
1163  // Set all the branches to off
1164  Chain->SetBranchStatus("*", false);
1165  unsigned int stepBranch = 0;
1166  std::vector<double> ParValBranch(nDraw);
1167  // Turn on the branches which we want for parameters
1168  for (int i = 0; i < nDraw; ++i)
1169  {
1170  Chain->SetBranchStatus(BranchNames[i].Data(), true);
1171  Chain->SetBranchAddress(BranchNames[i].Data(), &ParValBranch[i]);
1172  }
1173  Chain->SetBranchStatus("step", true);
1174  Chain->SetBranchAddress("step", &stepBranch);
1175 
1176  std::vector<double> ReweightWeight(ReweightNames.size(), 1.0);
1177  if (ReweightPosterior)
1178  {
1179  WeightValue = new double[nEntries]();
1180  for (size_t i = 0; i < ReweightNames.size(); ++i) {
1181  Chain->SetBranchStatus(ReweightNames[i].c_str(), true);
1182  Chain->SetBranchAddress(ReweightNames[i].c_str(), &ReweightWeight[i]);
1183  }
1184  }
1185 
1186  const Long64_t countwidth = nEntries/10;
1187 
1188  // Loop over the entries
1189  //KS: This is really a bottleneck right now, thus revisit with ROOT6 https://pep-root6.github.io/docs/analysis/parallell/root.html
1190  for (Long64_t j = 0; j < nEntries; ++j)
1191  {
1192  if (j % countwidth == 0) {
1195  } else {
1196  Chain->GetEntry(j);
1197  }
1198  StepNumber[j] = stepBranch;
1199  // Set the branch addresses for params
1200  for (int i = 0; i < nDraw; ++i) {
1201  ParStep[i][j] = ParValBranch[i];
1202  }
1203  if (ReweightPosterior) {
1204  WeightValue[j] = 1.0;
1205  for (size_t i = 0; i < ReweightWeight.size(); ++i) {
1206  WeightValue[j] *= ReweightWeight[i];
1207  }
1208  }
1209  }
1210  // Set all the branches to on
1211  Chain->SetBranchStatus("*", true);
1212 
1213  // Calculate the total number of TH2D objects
1214  size_t nHistograms = nDraw * (nDraw + 1) / 2;
1215  MACH3LOG_INFO("Caching 2D posterior histograms...");
1216  MACH3LOG_INFO("Allocating {:.2f} MB for {} 2D Posteriors (each {}x{} bins)",
1217  double(nHistograms * nBins * nBins * sizeof(double)) / 1.E6, nHistograms, nBins, nBins);
1218  // Cache max and min in chain for covariance matrix
1219  for (int i = 0; i < nDraw; ++i)
1220  {
1221  TString Title_i = "";
1222  double Prior_i, PriorError_i;
1223  GetNthParameter(i, Prior_i, PriorError_i, Title_i);
1224 
1225  for (int j = 0; j <= i; ++j)
1226  {
1227  TString Title_j = "";
1228  double Prior_j, PriorError_j;
1229  GetNthParameter(j, Prior_j, PriorError_j, Title_j);
1230 
1231  auto range_x = GetHistRange(i);
1232  auto range_y = GetHistRange(j);
1233  // TH2D to hold the Correlation
1234  hpost2D[i][j] = new TH2D((Title_i + "_" + Title_j).Data(), (Title_i + "_" + Title_j).Data(),
1235  nBins, range_x.first, range_x.second,
1236  nBins, range_y.first, range_y.second);
1237  hpost2D[i][j]->SetMinimum(0);
1238  hpost2D[i][j]->GetXaxis()->SetTitle(Title_i);
1239  hpost2D[i][j]->GetYaxis()->SetTitle(Title_j);
1240  hpost2D[i][j]->GetZaxis()->SetTitle("Steps");
1241  }
1242  }
1243  clock.Stop();
1244  MACH3LOG_INFO("Caching steps took {:.2f}s to finish for {} steps", clock.RealTime(), nEntries );
1245 }
1246 
1247 // *********************
1248 // Make the post-fit covariance matrix in all dimensions
1249 void MCMCProcessor::MakeCovariance_MP(const bool Mute) {
1250 // *********************
1251  if (OutputFile == nullptr) MakeOutputFile();
1252 
1253  if(!CacheMCMC) CacheSteps();
1254 
1255  // Check that the diagonal entries have been filled
1256  // i.e. MakePostfit() has been called
1257  for (int i = 0; i < nDraw; ++i) {
1258  if ((*Covariance)(i,i) == M3::_BAD_DOUBLE_) {
1259  MACH3LOG_WARN("Have not run diagonal elements in covariance, will do so now by calling MakePostfit()");
1260  MakePostfit();
1261  break;
1262  }
1263  }
1264 
1265  TStopwatch clock;
1266  TDirectory *PostHistDir = nullptr;
1267  if(!Mute)
1268  {
1269  MACH3LOG_INFO("Calculating covariance matrix");
1270  clock.Start();
1271  PostHistDir = OutputFile->mkdir("Post_2d_hists");
1272  PostHistDir->cd();
1273  }
1274 
1275  if(!Mute)
1276 
1277  gStyle->SetPalette(55);
1278  // Now we are sure we have the diagonal elements, let's make the off-diagonals
1279  #ifdef MULTITHREAD
1280  #pragma omp parallel for
1281  #endif
1282  for (int i = 0; i < nDraw; ++i)
1283  {
1284  for (int j = 0; j <= i; ++j)
1285  {
1286  // Skip the diagonal elements which we've already done above
1287  if (j == i) continue;
1288 
1289  // If this parameter isn't varied
1290  if (ParamVaried[j] == false) {
1291  (*Covariance)(i,j) = 0.0;
1292  (*Covariance)(j,i) = (*Covariance)(i,j);
1293  (*Correlation)(i,j) = 0.0;
1294  (*Correlation)(j,i) = (*Correlation)(i,j);
1295  continue;
1296  }
1297  hpost2D[i][j]->SetMinimum(0);
1298 
1299  for (int k = 0; k < nEntries; ++k)
1300  {
1301  //KS: Burn in cut
1302  if(StepNumber[k] < BurnInCut) continue;
1303 
1304  const double Weight = ReweightPosterior ? WeightValue[i] : 1.;
1305  //KS: Fill histogram with cached steps
1306  hpost2D[i][j]->Fill(ParStep[i][k], ParStep[j][k], Weight);
1307  }
1308  if(ApplySmoothing) hpost2D[i][j]->Smooth();
1309 
1310  // Get the Covariance for these two parameters
1311  (*Covariance)(i,j) = hpost2D[i][j]->GetCovariance();
1312  (*Covariance)(j,i) = (*Covariance)(i,j);
1313 
1314  //KS: Since we already have covariance consider calculating correlation using it, right now we effectively calculate covariance twice
1315  //https://root.cern.ch/doc/master/TH2_8cxx_source.html#l01099
1316  (*Correlation)(i,j) = hpost2D[i][j]->GetCorrelationFactor();
1317  (*Correlation)(j,i) = (*Correlation)(i,j);
1318  }// End j loop
1319  }// End i loop
1320 
1321  if(!Mute) {
1322  clock.Stop();
1323  MACH3LOG_INFO("Making Covariance took {:.2f}s to finish for {} steps", clock.RealTime(), nEntries);
1324  if(printToPDF)
1325  {
1326  Posterior->cd();
1327  for (int i = 0; i < nDraw; ++i)
1328  {
1329  for (int j = 0; j <= i; ++j)
1330  {
1331  // Skip the diagonal elements which we've already done above
1332  if (j == i) continue;
1333  if (ParamVaried[j] == false) continue;
1334 
1335  if(ParamType[i] == kXSecPar && ParamType[j] == kXSecPar)
1336  {
1337  if(std::fabs((*Correlation)(i,j)) > Post2DPlotThreshold)
1338  {
1339  hpost2D[i][j]->Draw("colz");
1340  Posterior->SetName(hpost2D[i][j]->GetName());
1341  Posterior->SetTitle(hpost2D[i][j]->GetTitle());
1342  Posterior->Print(CanvasName);
1343  hpost2D[i][j]->Write(hpost2D[i][j]->GetTitle());
1344  }
1345  }
1346  //if( std::fabs((*Correlation)(i,j)) > Post2DPlotThreshold) hpost2D[i][j]->Write();
1347  }// End j loop
1348  }// End i loop
1349  } //end if pdf
1350  PostHistDir->Close();
1351  delete PostHistDir;
1352  OutputFile->cd();
1353  Covariance->Write("Covariance");
1354  Correlation->Write("Correlation");
1355  } // end if not mute
1356 }
1357 
1358 // *********************
1359 // Based on @cite roberts2009adaptive
1360 // all credits for finding and studying it goes to Henry
1361 void MCMCProcessor::MakeSubOptimality(const int NIntervals) {
1362 // *********************
1363  //Save burn in cut, at the end of the loop we will return to default values
1364  const int DefaultUpperCut = UpperCut;
1365  const int DefaultBurnInCut = BurnInCut;
1366  bool defaultPrintToPDF = printToPDF;
1367  BurnInCut = 0;
1368  UpperCut = 0;
1369  printToPDF = false;
1370 
1371  //Set via config in future
1372  int MaxStep = nSteps;
1373  int MinStep = 0;
1374  const int IntervalsSize = nSteps/NIntervals;
1375 
1376  MACH3LOG_INFO("Making Suboptimality");
1377  TStopwatch clock;
1378  clock.Start();
1379 
1380  std::unique_ptr<TH1D> SubOptimality = std::make_unique<TH1D>("Suboptimality", "Suboptimality", NIntervals, MinStep, MaxStep);
1381  SubOptimality->SetDirectory(nullptr);
1382  SubOptimality->GetXaxis()->SetTitle("Step");
1383  SubOptimality->GetYaxis()->SetTitle("Suboptimality");
1384  SubOptimality->SetLineWidth(2);
1385  SubOptimality->SetLineColor(kBlue);
1386 
1387  for(int i = 0; i < NIntervals; ++i)
1388  {
1389  //Reset our cov matrix
1391 
1392  //Set threshold for calculating new matrix
1393  UpperCut = i*IntervalsSize;
1394  //Calculate cov matrix
1395  MakeCovariance_MP(true);
1396 
1397  //Calculate eigen values
1398  TMatrixDSymEigen eigen(*Covariance);
1399  TVectorD eigen_values;
1400  eigen_values.ResizeTo(eigen.GetEigenValues());
1401  eigen_values = eigen.GetEigenValues();
1402 
1403  //KS: Converting from ROOT to vector as to make using other libraires (Eigen) easier in future
1404  std::vector<double> EigenValues(eigen_values.GetNrows());
1405  for(unsigned int j = 0; j < EigenValues.size(); j++)
1406  {
1407  EigenValues[j] = eigen_values(j);
1408  }
1409  const double SubOptimalityValue = GetSubOptimality(EigenValues, nDraw);
1410  SubOptimality->SetBinContent(i+1, SubOptimalityValue);
1411  }
1412  clock.Stop();
1413  MACH3LOG_INFO("Making Suboptimality took {:.2f}s to finish for {} steps", clock.RealTime(), nEntries);
1414 
1415  UpperCut = DefaultUpperCut;
1416  BurnInCut = DefaultBurnInCut;
1417  printToPDF = defaultPrintToPDF;
1418 
1419  SubOptimality->Draw("l");
1420  Posterior->SetName(SubOptimality->GetName());
1421  Posterior->SetTitle(SubOptimality->GetTitle());
1422 
1423  if(printToPDF) Posterior->Print(CanvasName);
1424  // Write it to root file
1425  OutputFile->cd();
1426  Posterior->Write();
1427 }
1428 
1429 // *********************
1430 // Make the covariance plots
1432 // *********************
1433  const double RightMargin = Posterior->GetRightMargin();
1434  Posterior->SetRightMargin(0.15);
1435 
1436  // The Covariance matrix from the fit
1437  auto hCov = std::make_unique<TH2D>("hCov", "hCov", nDraw, 0, nDraw, nDraw, 0, nDraw);
1438  hCov->GetZaxis()->SetTitle("Covariance");
1439  hCov->SetDirectory(nullptr);
1440  // The Covariance matrix square root, with correct sign
1441  auto hCovSq = std::make_unique<TH2D>("hCovSq", "hCovSq", nDraw, 0, nDraw, nDraw, 0, nDraw);
1442  hCovSq->SetDirectory(nullptr);
1443  hCovSq->GetZaxis()->SetTitle("Covariance");
1444  // The Correlation
1445  auto hCorr = std::make_unique<TH2D>("hCorr", "hCorr", nDraw, 0, nDraw, nDraw, 0, nDraw);
1446  hCorr->SetDirectory(nullptr);
1447  hCorr->GetZaxis()->SetTitle("Correlation");
1448  hCorr->SetMinimum(-1);
1449  hCorr->SetMaximum(1);
1450  hCov->GetXaxis()->SetLabelSize(0.015);
1451  hCov->GetYaxis()->SetLabelSize(0.015);
1452  hCovSq->GetXaxis()->SetLabelSize(0.015);
1453  hCovSq->GetYaxis()->SetLabelSize(0.015);
1454  hCorr->GetXaxis()->SetLabelSize(0.015);
1455  hCorr->GetYaxis()->SetLabelSize(0.015);
1456 
1457  // Loop over the Covariance matrix entries
1458  for (int i = 0; i < nDraw; ++i)
1459  {
1460  TString titlex = "";
1461  double nom, err;
1462  GetNthParameter(i, nom, err, titlex);
1463 
1464  hCov->GetXaxis()->SetBinLabel(i+1, titlex);
1465  hCovSq->GetXaxis()->SetBinLabel(i+1, titlex);
1466  hCorr->GetXaxis()->SetBinLabel(i+1, titlex);
1467 
1468  for (int j = 0; j < nDraw; ++j)
1469  {
1470  // The value of the Covariance
1471  const double cov = (*Covariance)(i,j);
1472  const double corr = (*Correlation)(i,j);
1473 
1474  hCov->SetBinContent(i+1, j+1, cov);
1475  hCovSq->SetBinContent(i+1, j+1, ((cov > 0) - (cov < 0))*std::sqrt(std::fabs(cov)));
1476  hCorr->SetBinContent(i+1, j+1, corr);
1477 
1478  TString titley = "";
1479  double nom_j, err_j;
1480  GetNthParameter(j, nom_j, err_j, titley);
1481 
1482  hCov->GetYaxis()->SetBinLabel(j+1, titley);
1483  hCovSq->GetYaxis()->SetBinLabel(j+1, titley);
1484  hCorr->GetYaxis()->SetBinLabel(j+1, titley);
1485  }
1486  }
1487 
1488  // Take away the stat box
1489  gStyle->SetOptStat(0);
1490  if(plotBinValue)gStyle->SetPaintTextFormat("4.1f"); //Precision of value in matrix element
1491  // Make pretty Correlation colors (red to blue)
1492  constexpr int NRGBs = 5;
1493  TColor::InitializeColors();
1494  Double_t stops[NRGBs] = { 0.00, 0.25, 0.50, 0.75, 1.00 };
1495  Double_t red[NRGBs] = { 0.00, 0.25, 1.00, 1.00, 0.50 };
1496  Double_t green[NRGBs] = { 0.00, 0.25, 1.00, 0.25, 0.00 };
1497  Double_t blue[NRGBs] = { 0.50, 1.00, 1.00, 0.25, 0.00 };
1498  TColor::CreateGradientColorTable(5, stops, red, green, blue, 255);
1499  gStyle->SetNumberContours(255);
1500 
1501  // cd into the correlation directory
1502  OutputFile->cd();
1503 
1504  Posterior->cd();
1505  Posterior->Clear();
1506  if(plotBinValue) hCov->Draw("colz text");
1507  else hCov->Draw("colz");
1508  if(printToPDF) Posterior->Print(CanvasName);
1509 
1510  Posterior->cd();
1511  Posterior->Clear();
1512  if(plotBinValue) hCorr->Draw("colz text");
1513  else hCorr->Draw("colz");
1514  if(printToPDF) Posterior->Print(CanvasName);
1515 
1516  hCov->Write("Covariance_plot");
1517  hCovSq->Write("Covariance_sq_plot");
1518  hCorr->Write("Correlation_plot");
1519 
1520  //Back to normal
1521  Posterior->SetRightMargin(RightMargin);
1522  DrawCorrelationsGroup(hCorr);
1524 }
1525 
1526 // *********************
1527 void MCMCProcessor::MakeCovarianceYAML(const std::string& OutputYAMLFile, const std::string& MeansMethod) const {
1528 // *********************
1529  MACH3LOG_INFO("Making covariance matrix YAML file");
1530 
1531  if (ParamNames[kXSecPar].size() != static_cast<size_t>(nDraw)) {
1532  MACH3LOG_ERROR("Using Legacy Parameters i.e. not one from Parameter Handler Generic, this will not work");
1533  throw MaCh3Exception(__FILE__, __LINE__);
1534  }
1535  std::vector<double> MeanArray(nDraw);
1536  std::vector<double> ErrorArray(nDraw);
1537  std::vector<std::vector<double>> CorrelationMatrix(nDraw, std::vector<double>(nDraw, 0.0));
1538 
1539  TVectorD* means_vec;
1540  TVectorD* errors_vec;
1541 
1542  if (MeansMethod == "Arithmetic") {
1543  means_vec = Means;
1544  errors_vec = Errors;
1545  } else if (MeansMethod == "Gaussian") {
1546  means_vec = Means_Gauss;
1547  errors_vec = Errors_Gauss;
1548  } else if (MeansMethod == "HPD") {
1549  means_vec = Means_HPD;
1550  errors_vec = Errors_HPD;
1551  } else {
1552  MACH3LOG_ERROR("Unknown means method: {}, should be either 'Arithmetic', 'Gaussian', or 'HPD'.", MeansMethod);
1553  throw MaCh3Exception(__FILE__, __LINE__);
1554  }
1555 
1556  //Make vectors of mean, error, and correlations
1557  for (int i = 0; i < nDraw; i++)
1558  {
1559  MeanArray[i] = (*means_vec)(i);
1560  ErrorArray[i] = (*errors_vec)(i);
1561  for (int j = 0; j <= i; j++)
1562  {
1563  CorrelationMatrix[i][j] = (*Correlation)(i,j);
1564  if(i != j) CorrelationMatrix[j][i] = (*Correlation)(i,j);
1565  }
1566  }
1567 
1568  //Make std::string param name vector
1569  std::vector<std::string> ParamStrings(ParamNames[kXSecPar].size());
1570  for (size_t i = 0; i < ParamNames[kXSecPar].size(); ++i) {
1571  ParamStrings[i] = static_cast<std::string>(ParamNames[kXSecPar][i]);
1572  }
1573 
1574  YAML::Node XSecFile = CovConfig[kXSecPar];
1575  M3::MakeCorrelationMatrix(XSecFile, MeanArray, ErrorArray, CorrelationMatrix, OutputYAMLFile, ParamStrings);
1576 }
1577 
1578 // *********************
1579 // Inspired by plot in Ewan thesis see https://www.t2k.org/docs/thesis/152/Thesis#page=147
1580 void MCMCProcessor::DrawCorrelationsGroup(const std::unique_ptr<TH2D>& CorrMatrix) const {
1581 // *********************
1582  MACH3LOG_INFO("Starting {}", __func__);
1583  const double RightMargin = Posterior->GetRightMargin();
1584  Posterior->SetRightMargin(0.15);
1585  auto MatrixCopy = M3::Clone(CorrMatrix.get());
1586 
1587  std::vector<std::string> GroupName;
1588  std::vector<int> GroupStart;
1589  std::vector<int> GroupEnd;
1590 
1591  // Loop over the Covariance matrix entries
1592  for (int iPar = 0; iPar < nDraw; ++iPar)
1593  {
1594  std::string GroupNameCurr;
1595  if(ParamType[iPar] == kXSecPar){
1596  const int InternalNumeration = iPar - ParamTypeStartPos[kXSecPar];
1597  GroupNameCurr = ParameterGroup[InternalNumeration];
1598  } else {
1599  GroupNameCurr = "Other"; // Use Other for all legacy params
1600  }
1601 
1602  if(iPar == 0) {
1603  GroupName.push_back(GroupNameCurr);
1604  GroupStart.push_back(0);
1605  } else if(GroupName.back() != GroupNameCurr ){
1606  GroupName.push_back(GroupNameCurr);
1607  GroupEnd.push_back(iPar);
1608  GroupStart.push_back(iPar);
1609  }
1610 
1611  MatrixCopy->GetXaxis()->SetBinLabel(iPar+1, "");
1612  MatrixCopy->GetYaxis()->SetBinLabel(iPar+1, "");
1613  }
1614  GroupEnd.push_back(nDraw);
1615 
1616  for(size_t iPar = 0; iPar < GroupName.size(); iPar++) {
1617  MACH3LOG_INFO("Group name {} from {} to {}", GroupName[iPar], GroupStart[iPar], GroupEnd[iPar]);
1618  }
1619  Posterior->cd();
1620  Posterior->Clear();
1621  MatrixCopy->Draw("colz");
1622 
1623  std::vector<std::unique_ptr<TLine>> groupLines; //((GroupStart.size() - 1) * 2);
1624 
1625  int nBinsX = MatrixCopy->GetNbinsX();
1626  int nBinsY = MatrixCopy->GetNbinsY();
1627 
1628  // Axis bounds from the histogram itself
1629  double xMin = MatrixCopy->GetXaxis()->GetBinLowEdge(1);
1630  double xMax = MatrixCopy->GetXaxis()->GetBinUpEdge(nBinsX);
1631  double yMin = MatrixCopy->GetYaxis()->GetBinLowEdge(1);
1632  double yMax = MatrixCopy->GetYaxis()->GetBinUpEdge(nBinsY);
1633 
1634  for (size_t g = 1; g < GroupStart.size(); ++g) {
1635  const double posX = MatrixCopy->GetXaxis()->GetBinLowEdge(GroupStart[g] + 1);
1636  const double posY = MatrixCopy->GetYaxis()->GetBinLowEdge(GroupStart[g] + 1);
1637 
1638  // Vertical line at group start
1639  auto vLine = std::make_unique<TLine>(posX, yMin, posX, yMax);
1640  vLine->SetLineColor(kBlack);
1641  vLine->SetLineWidth(2);
1642  vLine->Draw();
1643  groupLines.push_back(std::move(vLine));
1644 
1645  // Horizontal line at group start
1646  auto hLine = std::make_unique<TLine>(xMin, posY, xMax, posY);
1647  hLine->SetLineColor(kBlack);
1648  hLine->SetLineWidth(2);
1649  hLine->Draw();
1650  groupLines.push_back(std::move(hLine));
1651  }
1652 
1653  std::vector<std::unique_ptr<TText>> groupLabels(GroupName.size() * 2);
1654  const double yOffsetBelow = 0.05 * (yMax - yMin); // space below x-axis
1655  const double xOffsetRight = 0.02 * (xMax - xMin); // space right of y-axis
1656 
1657  for (size_t g = 0; g < GroupName.size(); ++g) {
1658  const int startBin = GroupStart[g] + 1; // hist bins start at 1
1659  const int endBin = GroupEnd[g];
1660 
1661  const double xStart = MatrixCopy->GetXaxis()->GetBinLowEdge(startBin);
1662  const double xEnd = MatrixCopy->GetXaxis()->GetBinUpEdge(endBin);
1663  const double xMid = 0.5 * (xStart + xEnd);
1664 
1665  const double yStart = MatrixCopy->GetYaxis()->GetBinLowEdge(startBin);
1666  const double yEnd = MatrixCopy->GetYaxis()->GetBinUpEdge(endBin);
1667  const double yMid = 0.5 * (yStart + yEnd);
1668 
1669  // Label along X-axis (below histogram)
1670  auto labelX = std::make_unique<TText>(xMid, yMin - yOffsetBelow, GroupName[g].c_str());
1671  labelX->SetTextAlign(23); // center horizontally, top-aligned vertically
1672  labelX->SetTextSize(0.025);
1673  labelX->Draw();
1674  groupLabels.push_back(std::move(labelX));
1675 
1676  // Label along Y-axis (left of histogram)
1677  auto labelY = std::make_unique<TText>(xMin - xOffsetRight, yMid, GroupName[g].c_str());
1678  labelY->SetTextAlign(32); // right-aligned horizontally, center vertically
1679  labelY->SetTextSize(0.025);
1680  labelY->Draw();
1681  groupLabels.push_back(std::move(labelY));
1682  }
1683 
1684  if(printToPDF) Posterior->Print(CanvasName);
1685  Posterior->SetRightMargin(RightMargin);
1686 }
1687 
1688 // *********************
1689 //KS: Make the 1D projections of Correlations inspired by Henry's slides (page 28) https://www.t2k.org/asg/oagroup/meeting/2023/2023-07-10-oa-pre-meeting/MaCh3FDUpdate
1691 // *********************
1692  //KS: Store it as we go back to them at the end
1693  const std::vector<double> Margins = GetMargins(Posterior);
1694  const int OptTitle = gStyle->GetOptTitle();
1695 
1696  Posterior->SetTopMargin(0.1);
1697  Posterior->SetBottomMargin(0.2);
1698  gStyle->SetOptTitle(1);
1699 
1700  constexpr int Nhists = 3;
1701  //KS: Highest value is just meant bo be sliglhy higher than 1 to catch >,
1702  constexpr double Thresholds[Nhists+1] = {0, 0.25, 0.5, 1.0001};
1703  constexpr Color_t CorrColours[Nhists] = {kRed-10, kRed-6, kRed};
1704 
1705  //KS: This store necessary entries for stripped covariance which store only "meaningful correlations
1706  std::vector<std::vector<double>> CorrOfInterest;
1707  CorrOfInterest.resize(nDraw);
1708  std::vector<std::vector<std::string>> NameCorrOfInterest;
1709  NameCorrOfInterest.resize(nDraw);
1710 
1711  std::vector<std::vector<std::unique_ptr<TH1D>>> Corr1DHist(nDraw);
1712  //KS: Initialising ROOT objects is never safe in MP loop
1713  for(int i = 0; i < nDraw; ++i)
1714  {
1715  TString Title = "";
1716  double Prior = 1.0, PriorError = 1.0;
1717  GetNthParameter(i, Prior, PriorError, Title);
1718 
1719  Corr1DHist[i].resize(Nhists);
1720  for(int j = 0; j < Nhists; ++j)
1721  {
1722  Corr1DHist[i][j] = std::make_unique<TH1D>(Form("Corr1DHist_%i_%i", i, j), Form("Corr1DHist_%i_%i", i, j), nDraw, 0, nDraw);
1723  Corr1DHist[i][j]->SetTitle(Form("%s",Title.Data()));
1724  Corr1DHist[i][j]->SetDirectory(nullptr);
1725  Corr1DHist[i][j]->GetYaxis()->SetTitle("Correlation");
1726  Corr1DHist[i][j]->SetFillColor(CorrColours[j]);
1727  Corr1DHist[i][j]->SetLineColor(kBlack);
1728 
1729  for (int k = 0; k < nDraw; ++k)
1730  {
1731  TString Title_y = "";
1732  double Prior_y = 1.0;
1733  double PriorError_y = 1.0;
1734  GetNthParameter(k, Prior_y, PriorError_y, Title_y);
1735  Corr1DHist[i][j]->GetXaxis()->SetBinLabel(k+1, Title_y.Data());
1736  }
1737  }
1738  }
1739 
1740  // KS: Do not add collapse(2) otherwise one can intorduce race condition :(
1741  #ifdef MULTITHREAD
1742  #pragma omp parallel for
1743  #endif
1744  for(int i = 0; i < nDraw; ++i)
1745  {
1746  for(int j = 0; j < nDraw; ++j)
1747  {
1748  for(int k = 0; k < Nhists; ++k)
1749  {
1750  const double TempEntry = std::fabs((*Correlation)(i,j));
1751  if(Thresholds[k+1] > TempEntry && TempEntry >= Thresholds[k])
1752  {
1753  Corr1DHist[i][k]->SetBinContent(j+1, (*Correlation)(i,j));
1754  }
1755  }
1756  if(std::fabs((*Correlation)(i,j)) > Post2DPlotThreshold && i != j)
1757  {
1758  CorrOfInterest[i].push_back((*Correlation)(i,j));
1759  NameCorrOfInterest[i].push_back(Corr1DHist[i][0]->GetXaxis()->GetBinLabel(j+1));
1760  }
1761  }
1762  }
1763 
1764  TDirectory *CorrDir = OutputFile->mkdir("Corr1D");
1765  CorrDir->cd();
1766 
1767  for(int i = 0; i < nDraw; i++)
1768  {
1769  if (ParamVaried[i] == false) continue;
1770 
1771  Corr1DHist[i][0]->GetXaxis()->LabelsOption("v");
1772  Corr1DHist[i][0]->SetMaximum(+1.);
1773  Corr1DHist[i][0]->SetMinimum(-1.);
1774  Corr1DHist[i][0]->Draw();
1775  for(int k = 1; k < Nhists; k++) {
1776  Corr1DHist[i][k]->Draw("SAME");
1777  }
1778 
1779  auto leg = std::make_unique<TLegend>(0.3, 0.75, 0.6, 0.90);
1780  SetLegendStyle(leg.get(), 0.02);
1781  for(int k = 0; k < Nhists; k++) {
1782  leg->AddEntry(Corr1DHist[i][k].get(), Form("%.2f > |Corr| >= %.2f", Thresholds[k+1], Thresholds[k]), "f");
1783  }
1784  leg->Draw("SAME");
1785 
1786  Posterior->Write(Corr1DHist[i][0]->GetTitle());
1787  if(printToPDF) Posterior->Print(CanvasName);
1788  }
1789 
1790  //KS: Plot only meaningful correlations
1791  for(int i = 0; i < nDraw; i++)
1792  {
1793  const int size = int(CorrOfInterest[i].size());
1794 
1795  if(size == 0) continue;
1796  auto Corr1DHist_Reduced = std::make_unique<TH1D>("Corr1DHist_Reduced", "Corr1DHist_Reduced", size, 0, size);
1797  Corr1DHist_Reduced->SetDirectory(nullptr);
1798  Corr1DHist_Reduced->SetTitle(Corr1DHist[i][0]->GetTitle());
1799  Corr1DHist_Reduced->GetYaxis()->SetTitle("Correlation");
1800  Corr1DHist_Reduced->SetFillColor(kBlue);
1801  Corr1DHist_Reduced->SetLineColor(kBlue);
1802 
1803  for (int j = 0; j < size; ++j)
1804  {
1805  Corr1DHist_Reduced->GetXaxis()->SetBinLabel(j+1, NameCorrOfInterest[i][j].c_str());
1806  Corr1DHist_Reduced->SetBinContent(j+1, CorrOfInterest[i][j]);
1807  }
1808  Corr1DHist_Reduced->GetXaxis()->LabelsOption("v");
1809 
1810  Corr1DHist_Reduced->SetMaximum(+1.);
1811  Corr1DHist_Reduced->SetMinimum(-1.);
1812  Corr1DHist_Reduced->Draw();
1813 
1814  Posterior->Write(Form("%s_Red", Corr1DHist_Reduced->GetTitle()));
1815  if(printToPDF) Posterior->Print(CanvasName);
1816  }
1817 
1818  CorrDir->Close();
1819  delete CorrDir;
1820  OutputFile->cd();
1821 
1822  SetMargins(Posterior, Margins);
1823  gStyle->SetOptTitle(OptTitle);
1824 }
1825 
1826 
1827 // *********************
1828 // Convert posterior likelihood to Delta Chi2 used for comparison with frequentists fitter
1829 void MCMCProcessor::ProduceChi2(const std::string& GroupName) const {
1830 // *********************
1831  if(GroupName == "") return;
1832  MACH3LOG_INFO("Starting {}", __func__);
1833  TDirectory* Chi2Folder = OutputFile->mkdir("DeltaChi2");
1834 
1835  Chi2Folder->cd();
1836  for (int iPar = 0; iPar < nDraw; iPar++)
1837  {
1838  std::string GroupNameCurr;
1839  if(ParamType[iPar] == kXSecPar){
1840  const int InternalNumeration = iPar - ParamTypeStartPos[kXSecPar];
1841  GroupNameCurr = ParameterGroup[InternalNumeration];
1842  } else {
1843  GroupNameCurr = "Other"; // Use Other for all legacy params
1844  }
1845  if (ParamVaried[iPar] == false) continue;
1846  if (GroupName != "All" && GroupNameCurr != GroupName) continue;
1847 
1848  auto Chi2 = GetDeltaChi2(hpost[iPar]);
1849  RemoveFitter(Chi2.get(), "Gauss");
1850 
1851  Chi2->Write();
1852  }
1853  Chi2Folder->Close();
1854  delete Chi2Folder;
1855  OutputFile->cd();
1856 }
1857 
1858 // *********************
1859 // Make fancy Credible Intervals plots
1860 void MCMCProcessor::MakeCredibleRegions(const std::vector<double>& CredibleRegions,
1861  const std::vector<Style_t>& CredibleRegionStyle,
1862  const std::vector<Color_t>& CredibleRegionColor,
1863  const bool CredibleInSigmas,
1864  const bool Draw2DPosterior,
1865  const bool DrawBestFit) {
1866 // *********************
1867  if(hpost2D.size() == 0) MakeCovariance_MP();
1868  MACH3LOG_INFO("Making Credible Regions");
1869 
1870  CheckCredibleRegionsOrder(CredibleRegions, CredibleRegionStyle, CredibleRegionColor);
1871  const int nCredible = int(CredibleRegions.size());
1872 
1873  std::vector<std::vector<std::unique_ptr<TH2D>>> hpost_2D_copy(nDraw);
1874  std::vector<std::vector<std::vector<std::unique_ptr<TH2D>>>> hpost_2D_cl(nDraw);
1875  //KS: Copy all histograms to be thread safe
1876  for (int i = 0; i < nDraw; ++i)
1877  {
1878  hpost_2D_copy[i].resize(nDraw);
1879  hpost_2D_cl[i].resize(nDraw);
1880  for (int j = 0; j <= i; ++j)
1881  {
1882  hpost_2D_copy[i][j] = M3::Clone<TH2D>(hpost2D[i][j], Form("hpost_copy_%i_%i", i, j));
1883  hpost_2D_cl[i][j].resize(nCredible);
1884  for (int k = 0; k < nCredible; ++k)
1885  {
1886  hpost_2D_cl[i][j][k] = M3::Clone<TH2D>(hpost2D[i][j], Form("hpost_copy_%i_%i_CL_%f", i, j, CredibleRegions[k]));
1887  }
1888  }
1889  }
1890 
1891  #ifdef MULTITHREAD
1892  #pragma omp parallel for
1893  #endif
1894  //Calculate credible histogram
1895  for (int i = 0; i < nDraw; ++i)
1896  {
1897  for (int j = 0; j <= i; ++j)
1898  {
1899  for (int k = 0; k < nCredible; ++k)
1900  {
1901  GetCredibleRegionSig(hpost_2D_cl[i][j][k], CredibleInSigmas, CredibleRegions[k]);
1902  hpost_2D_cl[i][j][k]->SetLineColor(CredibleRegionColor[k]);
1903  hpost_2D_cl[i][j][k]->SetLineWidth(2);
1904  hpost_2D_cl[i][j][k]->SetLineStyle(CredibleRegionStyle[k]);
1905  }
1906  }
1907  }
1908 
1909  gStyle->SetPalette(51);
1910  for (int i = 0; i < nDraw; ++i)
1911  {
1912  for (int j = 0; j <= i; ++j)
1913  {
1914  // Skip the diagonal elements which we've already done above
1915  if (j == i) continue;
1916  if (ParamVaried[j] == false) continue;
1917 
1918  auto legend = std::make_unique<TLegend>(0.20, 0.7, 0.4, 0.92);
1919  legend->SetTextColor(kRed);
1920  SetLegendStyle(legend.get(), 0.03);
1921 
1922  //Get Best point
1923  auto bestfitM = std::make_unique<TGraph>(1);
1924  const int MaxBin = hpost_2D_copy[i][j]->GetMaximumBin();
1925  int Mbx, Mby, Mbz;
1926  hpost_2D_copy[i][j]->GetBinXYZ(MaxBin, Mbx, Mby, Mbz);
1927  const double Mx = hpost_2D_copy[i][j]->GetXaxis()->GetBinCenter(Mbx);
1928  const double My = hpost_2D_copy[i][j]->GetYaxis()->GetBinCenter(Mby);
1929 
1930  bestfitM->SetPoint(0, Mx, My);
1931  bestfitM->SetMarkerStyle(22);
1932  bestfitM->SetMarkerSize(1);
1933  bestfitM->SetMarkerColor(kMagenta);
1934 
1935  //Plot default 2D posterior
1936 
1937  if(Draw2DPosterior){
1938  hpost_2D_copy[i][j]->Draw("COLZ");
1939  } else{
1940  hpost_2D_copy[i][j]->Draw("AXIS");
1941  }
1942 
1943  //Now credible regions
1944  for (int k = 0; k < nCredible; ++k)
1945  hpost_2D_cl[i][j][k]->Draw("CONT3 SAME");
1946  for (int k = nCredible-1; k >= 0; --k)
1947  {
1948  if(CredibleInSigmas)
1949  legend->AddEntry(hpost_2D_cl[i][j][k].get(), Form("%.0f#sigma Credible Interval", CredibleRegions[k]), "l");
1950  else
1951  legend->AddEntry(hpost_2D_cl[i][j][k].get(), Form("%.0f%% Credible Region", CredibleRegions[k]*100), "l");
1952  }
1953  legend->Draw("SAME");
1954 
1955  if(DrawBestFit){
1956  legend->AddEntry(bestfitM.get(),"Best Fit","p");
1957  bestfitM->Draw("SAME.P");
1958  }
1959 
1960  // Write to file
1961  Posterior->SetName(hpost2D[i][j]->GetName());
1962  Posterior->SetTitle(hpost2D[i][j]->GetTitle());
1963 
1964  //KS: Print only regions with correlation greater than specified value, by default 0.2. This is done to avoid dumping thousands of plots
1965  if(printToPDF && std::fabs((*Correlation)(i,j)) > Post2DPlotThreshold) Posterior->Print(CanvasName);
1966  // Write it to root file
1967  //OutputFile->cd();
1968  //if( std::fabs((*Correlation)(i,j)) > Post2DPlotThreshold ) Posterior->Write();
1969  }
1970  }
1971 
1972  OutputFile->cd();
1973 }
1974 
1975 // *********************
1976 // Make fancy triangle plot for selected parameters
1977 void MCMCProcessor::MakeTrianglePlot(const std::vector<std::string>& ParNames,
1978  // 1D
1979  const std::vector<double>& CredibleIntervals,
1980  const std::vector<Color_t>& CredibleIntervalsColours,
1981  //2D
1982  const std::vector<double>& CredibleRegions,
1983  const std::vector<Style_t>& CredibleRegionStyle,
1984  const std::vector<Color_t>& CredibleRegionColor,
1985  // Other
1986  const bool CredibleInSigmas) {
1987 // *********************
1988  if(hpost2D.size() == 0) MakeCovariance_MP();
1989 
1990  const int nParamPlot = int(ParNames.size());
1991  std::vector<int> ParamNumber;
1992  std::string ParamInfoNames = "Making Triangle Plot for { ";
1993  for(int j = 0; j < nParamPlot; ++j)
1994  {
1995  ParamInfoNames += fmt::format("{} ", ParNames[j]);
1996  int ParamNo = GetParamIndexFromName(ParNames[j]);
1997  if(ParamNo == M3::_BAD_INT_)
1998  {
1999  MACH3LOG_WARN("Couldn't find param {}. Will not plot Triangle plot", ParNames[j]);
2000  return;
2001  }
2002  ParamNumber.push_back(ParamNo);
2003  }
2004  ParamInfoNames += "}";
2005  MACH3LOG_INFO("{}", ParamInfoNames);
2006 
2007  //KS: Store it as we go back to them at the end
2008  const std::vector<double> Margins = GetMargins(Posterior);
2009  Posterior->SetTopMargin(0.001);
2010  Posterior->SetBottomMargin(0.001);
2011  Posterior->SetLeftMargin(0.001);
2012  Posterior->SetRightMargin(0.001);
2013 
2014  // KS: We later format hist several times so make one unfired lambda
2015  auto FormatHistogram = [](auto& hist) {
2016  hist->GetXaxis()->SetTitle("");
2017  hist->GetYaxis()->SetTitle("");
2018  hist->SetTitle("");
2019 
2020  hist->GetXaxis()->SetLabelSize(0.1);
2021  hist->GetYaxis()->SetLabelSize(0.1);
2022 
2023  hist->GetXaxis()->SetNdivisions(4);
2024  hist->GetYaxis()->SetNdivisions(4);
2025  };
2026 
2027  Posterior->cd();
2028  Posterior->Clear();
2029  Posterior->Update();
2030 
2031  //KS: We sort to have parameters from highest to lowest, this is related to how we make 2D projections in MakeCovariance_MP
2032  std::sort(ParamNumber.begin(), ParamNumber.end(), std::greater<int>());
2033 
2034  //KS: Calculate how many pads/plots we need
2035  int Npad = 0;
2036  for(int j = 1; j < nParamPlot+1; j++) Npad += j;
2037  Posterior->cd();
2038  // KS: Sanity check of size and ordering is correct
2039  CheckCredibleIntervalsOrder(CredibleIntervals, CredibleIntervalsColours);
2040  CheckCredibleRegionsOrder(CredibleRegions, CredibleRegionStyle, CredibleRegionColor);
2041 
2042  const int nCredibleIntervals = int(CredibleIntervals.size());
2043  const int nCredibleRegions = int(CredibleRegions.size());
2044 
2045  //KS: Initialise Tpad histograms etc we will need
2046  std::vector<TPad*> TrianglePad(Npad);
2047  //KS: 1D copy of posterior, we need it as we modify them
2048  std::vector<std::unique_ptr<TH1D>> hpost_copy(nParamPlot);
2049  std::vector<std::vector<std::unique_ptr<TH1D>>> hpost_cl(nParamPlot);
2050  std::vector<std::unique_ptr<TText>> TriangleText(nParamPlot * 2);
2051  std::vector<std::unique_ptr<TH2D>> hpost_2D_copy(Npad-nParamPlot);
2052  std::vector<std::vector<std::unique_ptr<TH2D>>> hpost_2D_cl(Npad-nParamPlot);
2053  gStyle->SetPalette(51);
2054 
2055  //KS: Super convoluted way of calculating ranges for our pads, trust me it works...
2056  std::vector<double> X_Min(nParamPlot);
2057  std::vector<double> X_Max(nParamPlot);
2058 
2059  //TN:
2060  // n = number of params (nParamPlot)
2061  // a_x = width of the left margin space for pad axis labels on the left in canvas coordinates
2062  // a_y = height of the bottom margin space for pad axis labels on the bottom in canvas coordinates
2063  // b_x = pad plot width in canvas coordinates
2064  // b_y = pad plot height in canvas coordinates
2065  // Pm = a/(a+b) = actual margin within the first plot from the left (a_x,b_x) or bottom (a_y,b_y); Pm = {left,bottom}
2066  // TPm = desired margin of the whole triangle plot in canvas coordinates; TPm = {left, bottom, right, top}
2067  // TPw = 1.-TPm[0]-TPm[2] width of the triangle plot in canvas coordinates
2068  // TPh = 1.-TPm[1]-TPm[3] height of the triangle plot in canvas coordinates
2069  // Then a_x+n*b_x = TPw = 1.-TPm[0]-TPm[1]
2070  // Hence from that:
2071  // a_x = Pm[0]*(a_x+b_x) = Pm[0]*(a_x+(TPw-a_x)/n) => a_x = (Pm[0]*TPw)/(n+Pm[0]*(1-n))
2072  // b_x = (TPw-a_x)/n
2073 
2074  // The inputs:
2075  const double TPm[4] = {.07,.07,.05,.05};
2076  const double Pm[2] = {.2,.1};
2077 
2078  // Auxiliary x-direction:
2079  const double TPw = 1. - TPm[0] - TPm[2];
2080  const double a_x = ( Pm[0] * TPw ) / ( 1. * nParamPlot + Pm[0] * ( 1. - 1.*nParamPlot ) );
2081  const double b_x = ( TPw - a_x ) / ( 1. * nParamPlot );
2082 
2083  X_Min[0] = TPm[0];
2084  X_Max[0] = X_Min[0] + a_x + b_x;
2085  for(int i = 1; i < nParamPlot; i++)
2086  {
2087  X_Min[i] = X_Max[i-1];
2088  X_Max[i] = X_Min[i]+b_x;
2089  }
2090 
2091  std::vector<double> Y_Min(nParamPlot);
2092  std::vector<double> Y_Max(nParamPlot);
2093 
2094  // Auxiliary y-direction:
2095  const double TPh = 1. - TPm[1] - TPm[3];
2096  const double a_y = ( Pm[1] * TPh ) / ( 1. * nParamPlot + Pm[1] * ( 1. - 1.*nParamPlot ) );
2097  const double b_y = ( TPh - a_y ) / ( 1. * nParamPlot );
2098 
2099  Y_Min[nParamPlot-1] = TPm[1];
2100  Y_Max[nParamPlot-1] = Y_Min[nParamPlot-1] + a_y + b_y;
2101  for(int i = nParamPlot-2; i >= 0; i--)
2102  {
2103  Y_Min[i] = Y_Max[i+1];
2104  Y_Max[i] = Y_Min[i]+b_y;
2105  }
2106 
2107  //KS: We store as numbering of isn't straightforward
2108  int counterPad = 0, counterText = 0, counterPost = 0, counter2DPost = 0;
2109  //KS: We start from top of the plot, might be confusing but works very well
2110  for(int y = 0; y < nParamPlot; y++)
2111  {
2112  //KS: start from left and go right, depending on y
2113  for(int x = 0; x <= y; x++)
2114  {
2115  //KS: Need to go to canvas every time to have our pads in the same canvas, not pads in the pads
2116  Posterior->cd();
2117  TrianglePad[counterPad] = new TPad(Form("TPad_%i", counterPad), Form("TPad_%i", counterPad), X_Min[x], Y_Min[y], X_Max[x], Y_Max[y]);
2118 
2119  TrianglePad[counterPad]->SetTopMargin(0);
2120  TrianglePad[counterPad]->SetRightMargin(0);
2121 
2122  TrianglePad[counterPad]->SetGrid();
2123  TrianglePad[counterPad]->SetFrameBorderMode(0);
2124  TrianglePad[counterPad]->SetBorderMode(0);
2125  TrianglePad[counterPad]->SetBorderSize(0);
2126 
2127  //KS: Corresponds to bottom part of the plot, need margins for labels
2128  TrianglePad[counterPad]->SetBottomMargin(y == (nParamPlot - 1) ? Pm[1] : 0);
2129  //KS: Corresponds to left part, need margins for labels
2130  TrianglePad[counterPad]->SetLeftMargin(x == 0 ? Pm[0] : 0);
2131 
2132  TrianglePad[counterPad]->Draw();
2133  TrianglePad[counterPad]->cd();
2134 
2135  //KS:if diagonal plot main posterior
2136  if(x == y)
2137  {
2138  hpost_copy[counterPost] = M3::Clone<TH1D>(hpost[ParamNumber[x]], Form("hpost_copy_%i", ParamNumber[x]));
2139  hpost_cl[counterPost].resize(nCredibleIntervals);
2141  hpost_copy[counterPost]->Scale(1. / hpost_copy[counterPost]->Integral());
2142  for (int j = 0; j < nCredibleIntervals; ++j)
2143  {
2144  hpost_cl[counterPost][j] = M3::Clone<TH1D>(hpost[ParamNumber[x]], Form("hpost_copy_%i_CL_%f", ParamNumber[x], CredibleIntervals[j]));
2145  //KS: Reset to get rid to TF1 otherwise we run into segfault :(
2146  hpost_cl[counterPost][j]->Reset("");
2147  hpost_cl[counterPost][j]->Fill(0.0, 0.0);
2148 
2149  // Scale the histograms before gettindg credible intervals
2150  hpost_cl[counterPost][j]->Scale(1. / hpost_cl[counterPost][j]->Integral());
2151  GetCredibleIntervalSig(hpost_copy[counterPost], hpost_cl[counterPost][j], CredibleInSigmas, CredibleIntervals[j]);
2152 
2153  hpost_cl[counterPost][j]->SetFillColor(CredibleIntervalsColours[j]);
2154  hpost_cl[counterPost][j]->SetLineWidth(1);
2155  }
2156 
2157  hpost_copy[counterPost]->SetMaximum(hpost_copy[counterPost]->GetMaximum()*1.2);
2158  hpost_copy[counterPost]->SetLineWidth(2);
2159  hpost_copy[counterPost]->SetLineColor(kBlack);
2160 
2161  //KS: Don't want any titles
2162  FormatHistogram(hpost_copy[counterPost]);
2163 
2164  //TN: Scale the size of labels with the plots size.
2165  //Unfortunately, this needs to managed through absolute sizes
2166  //as each pad is of different size.
2167  hpost_copy[counterPost]->GetXaxis()->SetLabelFont(133);
2168  hpost_copy[counterPost]->GetXaxis()->SetLabelSize(.08*(a_y+b_y)*Posterior->GetWh());
2169 
2170  hpost_copy[counterPost]->GetYaxis()->SetLabelFont(133);
2171  hpost_copy[counterPost]->GetYaxis()->SetLabelSize(.08*(a_y+b_y)*Posterior->GetWh());
2172 
2173  hpost_copy[counterPost]->Draw("HIST");
2174  for (int j = 0; j < nCredibleIntervals; ++j){
2175  hpost_cl[counterPost][j]->Draw("HIST SAME");
2176  }
2177  counterPost++;
2178  }
2179  //KS: Here we plot 2D credible regions
2180  else
2181  {
2182  hpost_2D_copy[counter2DPost] = M3::Clone<TH2D>(hpost2D[ParamNumber[x]][ParamNumber[y]],
2183  Form("hpost_copy_%i_%i", ParamNumber[x], ParamNumber[y]));
2184  hpost_2D_cl[counter2DPost].resize(nCredibleRegions);
2185  //KS: Now copy for every credible region
2186  for (int k = 0; k < nCredibleRegions; ++k)
2187  {
2188  hpost_2D_cl[counter2DPost][k] = M3::Clone<TH2D>(hpost2D[ParamNumber[x]][ParamNumber[y]],
2189  Form("hpost_copy_%i_%i_CL_%f", ParamNumber[x], ParamNumber[y], CredibleRegions[k]));
2190  GetCredibleRegionSig(hpost_2D_cl[counter2DPost][k], CredibleInSigmas, CredibleRegions[k]);
2191 
2192  hpost_2D_cl[counter2DPost][k]->SetLineColor(CredibleRegionColor[k]);
2193  hpost_2D_cl[counter2DPost][k]->SetLineWidth(2);
2194  hpost_2D_cl[counter2DPost][k]->SetLineStyle(CredibleRegionStyle[k]);
2195  }
2196  //KS: Don't want any titles
2197  FormatHistogram(hpost_2D_copy[counter2DPost]);
2198 
2199  //TN: Scale the size of labels with the plots size.
2200  //Unfortunately, this needs to managed through absolute sizes
2201  //as each pad is of different size.
2202  hpost_2D_copy[counter2DPost]->GetXaxis()->SetLabelFont(133);
2203  hpost_2D_copy[counter2DPost]->GetXaxis()->SetLabelSize(.08*(a_y+b_y)*Posterior->GetWh());
2204 
2205  hpost_2D_copy[counter2DPost]->GetYaxis()->SetLabelFont(133);
2206  hpost_2D_copy[counter2DPost]->GetYaxis()->SetLabelSize(.08*(a_y+b_y)*Posterior->GetWh());
2207 
2208  hpost_2D_copy[counter2DPost]->Draw("COL");
2209  //Now credible regions
2210  for (int k = 0; k < nCredibleRegions; ++k){
2211  hpost_2D_cl[counter2DPost][k]->Draw("CONT3 SAME");
2212  }
2213  counter2DPost++;
2214  }
2215  //KS: Corresponds to bottom part of the plot
2216  if(y == (nParamPlot-1))
2217  {
2218  Posterior->cd();
2219  TriangleText[counterText] = std::make_unique<TText>(X_Min[x] + (X_Max[x]-X_Min[x]+(x == 0 ? a_x : .0))/2., .05, hpost[ParamNumber[x]]->GetTitle());
2220  //KS: Unfortunately for many plots or long names this can go out of bounds :(
2221  //TN: Align the axis titles and scale them with the size of the plots
2222  TriangleText[counterText]->SetTextAlign(22);
2223  TriangleText[counterText]->SetTextSize(.08*(a_y+b_y));
2224  TriangleText[counterText]->SetNDC(true);
2225  TriangleText[counterText]->Draw();
2226  counterText++;
2227  }
2228  //KS: Corresponds to left part
2229  if(x == 0)
2230  {
2231  Posterior->cd();
2232  TriangleText[counterText] = std::make_unique<TText>(.05, Y_Min[y] + (Y_Max[y]-Y_Min[y]+(y == nParamPlot-1 ? a_y : .0))/2., hpost[ParamNumber[y]]->GetTitle());
2233  //KS: Rotate as this is y axis
2234  TriangleText[counterText]->SetTextAngle(90);
2235  //KS: Unfortunately for many plots or long names this can go out of bounds :(
2236  //TN: Align the axis titles and scale them with the size of the plots
2237  TriangleText[counterText]->SetTextAlign(22);
2238  TriangleText[counterText]->SetTextSize(.08*(a_y+b_y));
2239  TriangleText[counterText]->SetNDC(true);
2240  TriangleText[counterText]->Draw();
2241  counterText++;
2242  }
2243  Posterior->Update();
2244  counterPad++;
2245  }
2246  }
2247 
2248  Posterior->cd();
2249  auto legend = std::make_unique<TLegend>(0.60, 0.7, 0.9, 0.9);
2250  SetLegendStyle(legend.get(), 0.03);
2251  //KS: Legend is shared so just take first histograms
2252  for (int j = nCredibleIntervals-1; j >= 0; --j)
2253  {
2254  if(CredibleInSigmas)
2255  legend->AddEntry(hpost_cl[0][j].get(), Form("%.0f#sigma Credible Interval", CredibleIntervals[j]), "f");
2256  else
2257  legend->AddEntry(hpost_cl[0][j].get(), Form("%.0f%% Credible Interval", CredibleRegions[j]*100), "f");
2258  }
2259  for (int k = nCredibleRegions-1; k >= 0; --k)
2260  {
2261  if(CredibleInSigmas)
2262  legend->AddEntry(hpost_2D_cl[0][k].get(), Form("%.0f#sigma Credible Region", CredibleRegions[k]), "l");
2263  else
2264  legend->AddEntry(hpost_2D_cl[0][k].get(), Form("%.0f%% Credible Region", CredibleRegions[k]*100), "l");
2265  }
2266  legend->Draw("SAME");
2267  Posterior->Update();
2268 
2269  // Write to file
2270  Posterior->SetName("TrianglePlot");
2271  Posterior->SetTitle("TrianglePlot");
2272 
2273  if(printToPDF) Posterior->Print(CanvasName);
2274  // Write it to root file
2275  OutputFile->cd();
2276  Posterior->Write();
2277 
2278  //KS: Remove allocated structures
2279  for(int i = 0; i < Npad; i++) delete TrianglePad[i];
2280 
2281  //KS: Restore margin
2282  SetMargins(Posterior, Margins);
2283 }
2284 
2285 // **************************
2286 // Scan the input trees
2288 // **************************
2289  // KS: This can reduce time necessary for caching even by half
2290  #ifdef MULTITHREAD
2291  //ROOT::EnableImplicitMT();
2292  #endif
2293 
2294  // Open the Chain
2295  Chain = new TChain("posteriors","posteriors");
2296  Chain->Add(MCMCFile.c_str());
2297 
2298  nEntries = int(Chain->GetEntries());
2299 
2300  //Only is suboptimality we might want to change it, therefore set it high enough so it doesn't affect other functionality
2301  UpperCut = nEntries+1;
2302 
2303  // Get the list of branches
2304  TObjArray* brlis = Chain->GetListOfBranches();
2305 
2306  // Get the number of branches
2307  nBranches = brlis->GetEntries();
2308 
2309  BranchNames.reserve(nBranches);
2310  ParamType.reserve(nBranches);
2311 
2312  // Read the input Covariances
2313  ReadInputCov();
2314 
2315  // Set all the branches to off
2316  Chain->SetBranchStatus("*", false);
2317 
2318  // Loop over the number of branches
2319  // Find the name and how many of each systematic we have
2320  for (int i = 0; i < nBranches; i++)
2321  {
2322  // Get the TBranch and its name
2323  TBranch* br = static_cast<TBranch*>(brlis->At(i));
2324  if(!br){
2325  MACH3LOG_ERROR("Invalid branch at position {}", i);
2326  throw MaCh3Exception(__FILE__,__LINE__);
2327  }
2328  TString bname = br->GetName();
2329 
2330  //KS: Exclude parameter types
2331  bool rejected = false;
2332  for(unsigned int ik = 0; ik < ExcludedTypes.size(); ++ik )
2333  {
2334  if(bname.BeginsWith(ExcludedTypes[ik]))
2335  {
2336  rejected = true;
2337  break;
2338  }
2339  }
2340  if(rejected) continue;
2341 
2342  // Turn on the branches which we want for parameters
2343  Chain->SetBranchStatus(bname.Data(), true);
2344 
2345  if (bname.BeginsWith("ndd_"))
2346  {
2347  BranchNames.push_back(bname);
2348  ParamType.push_back(kNDPar);
2349  nParam[kNDPar]++;
2350  }
2351  else if (bname.BeginsWith("skd_joint_"))
2352  {
2353  BranchNames.push_back(bname);
2354  ParamType.push_back(kFDDetPar);
2355  nParam[kFDDetPar]++;
2356  }
2357 
2358  //KS: as a bonus get LogL systematic
2359  if (bname.BeginsWith("LogL_sample_")) {
2360  SampleName_v.push_back(bname);
2361  }
2362  else if (bname.BeginsWith("LogL_systematic_")) {
2363  SystName_v.push_back(bname);
2364  }
2365  }
2366  nDraw = int(BranchNames.size());
2367 
2368  // Read the input Covariances
2370 
2371  // Check order of parameter types
2373 
2374  ParamVaried.resize(nDraw, true);
2375 
2376  // Print useful Info
2377  PrintInfo();
2378 
2379  nSteps = Chain->GetMaximum("step");
2380  // Set the step cut to be 20%
2381  int cut = nSteps/5;
2382  SetStepCut(cut);
2383 
2384  // Basically allow loading oscillation parameters
2386 }
2387 
2388 // ****************************
2389 // Set up the output files and canvases
2391 // ****************************
2392  // Make sure we can read files located anywhere and strip the .root ending
2393  MCMCFile = MCMCFile.substr(0, MCMCFile.find(".root"));
2394 
2395  // Check if the output file is ready
2396  if (OutputFile == nullptr) MakeOutputFile();
2397 
2398  CanvasName = MCMCFile + OutputSuffix + ".pdf[";
2399  if(printToPDF) Posterior->Print(CanvasName);
2400 
2401  // Once the pdf file is open no longer need to bracket
2402  CanvasName.ReplaceAll("[","");
2403 
2404  // We fit with this Gaussian
2405  Gauss = std::make_unique<TF1>("Gauss", "[0]/sqrt(2.0*3.14159)/[2]*TMath::Exp(-0.5*pow(x-[1],2)/[2]/[2])", -5, 5);
2406  Gauss->SetLineWidth(2);
2407  Gauss->SetLineColor(kOrange-5);
2408 
2409  // Declare the TVectors
2410  Covariance = new TMatrixDSym(nDraw);
2411  Correlation = new TMatrixDSym(nDraw);
2412  Central_Value = new TVectorD(nDraw);
2413  Means = new TVectorD(nDraw);
2414  Errors = new TVectorD(nDraw);
2415  Means_Gauss = new TVectorD(nDraw);
2416  Errors_Gauss = new TVectorD(nDraw);
2417  Means_HPD = new TVectorD(nDraw);
2418  Errors_HPD = new TVectorD(nDraw);
2419  Errors_HPD_Positive = new TVectorD(nDraw);
2420  Errors_HPD_Negative = new TVectorD(nDraw);
2421 
2422  // Initialise to something silly
2423  #ifdef MULTITHREAD
2424  #pragma omp parallel for
2425  #endif
2426  for (int i = 0; i < nDraw; ++i)
2427  {
2428  (*Central_Value)(i) = M3::_BAD_DOUBLE_;
2429  (*Means)(i) = M3::_BAD_DOUBLE_;
2430  (*Errors)(i) = M3::_BAD_DOUBLE_;
2431  (*Means_Gauss)(i) = M3::_BAD_DOUBLE_;
2432  (*Errors_Gauss)(i) = M3::_BAD_DOUBLE_;
2433  (*Means_HPD)(i) = M3::_BAD_DOUBLE_;
2434  (*Errors_HPD)(i) = M3::_BAD_DOUBLE_;
2435  (*Errors_HPD_Positive)(i) = M3::_BAD_DOUBLE_;
2436  (*Errors_HPD_Negative)(i) = M3::_BAD_DOUBLE_;
2437  for (int j = 0; j < nDraw; ++j) {
2438  (*Covariance)(i, j) = M3::_BAD_DOUBLE_;
2439  (*Correlation)(i, j) = M3::_BAD_DOUBLE_;
2440  }
2441  }
2442  hpost.resize(nDraw);
2443 }
2444 
2445 // ****************************
2446 // Check order of parameter types
2448 // *****************************
2449  for(int i = 0; i < kNParameterEnum; i++)
2450  {
2451  for(unsigned int j = 0; j < ParamType.size(); j++)
2452  {
2453  if(ParamType[j] == ParameterEnum(i))
2454  {
2455  //KS: When we find that i-th parameter types start at j, save and move to the next parameter.
2456  ParamTypeStartPos[i] = j;
2457  break;
2458  }
2459  }
2460  }
2461 }
2462 
2463 // *****************************
2464 // Make the prefit plots
2465 std::unique_ptr<TH1D> MCMCProcessor::MakePrefit() {
2466 // *****************************
2467  if (OutputFile == nullptr) MakeOutputFile();
2468 
2469  auto PreFitPlot = std::make_unique<TH1D>("Prefit", "Prefit", nDraw, 0, nDraw);
2470  PreFitPlot->SetDirectory(nullptr);
2471  for (int i = 0; i < PreFitPlot->GetNbinsX() + 1; ++i) {
2472  PreFitPlot->SetBinContent(i+1, 0);
2473  PreFitPlot->SetBinError(i+1, 0);
2474  }
2475 
2476  //KS: Slightly hacky way to get relative to prior or nominal as this is convention we use,
2477  //Only applies for xsec, for other systematic it make no difference
2478  double CentralValueTemp, Central, Error;
2479 
2480  // Set labels and data
2481  for (int i = 0; i < nDraw; ++i)
2482  {
2483  //Those keep which parameter type we run currently and relative number
2484  int ParamEnum = ParamType[i];
2485  int ParamNo = i - ParamTypeStartPos[ParameterEnum(ParamEnum)];
2486  CentralValueTemp = ParamCentral[ParamEnum][ParamNo];
2487  if(plotRelativeToPrior)
2488  {
2489  // Normalise the prior relative the nominal/prior, just the way we get our fit results in MaCh3
2490  if ( CentralValueTemp != 0) {
2491  Central = ParamCentral[ParamEnum][ParamNo] / CentralValueTemp;
2492  Error = ParamErrors[ParamEnum][ParamNo]/CentralValueTemp;
2493  } else {
2494  Central = CentralValueTemp + 1.0;
2495  Error = ParamErrors[ParamEnum][ParamNo];
2496  }
2497  }
2498  else
2499  {
2500  Central = CentralValueTemp;
2501  Error = ParamErrors[ParamEnum][ParamNo];
2502  }
2503  //KS: If plotting error for param with flat prior is turned off and given param really has flat prior set error to 0
2504  if(!PlotFlatPrior && ParamFlat[ParamEnum][ParamNo]) {
2505  Error = 0.;
2506  }
2507  PreFitPlot->SetBinContent(i+1, Central);
2508  PreFitPlot->SetBinError(i+1, Error);
2509  PreFitPlot->GetXaxis()->SetBinLabel(i+1, ParamNames[ParamEnum][ParamNo]);
2510  }
2511  PreFitPlot->SetDirectory(nullptr);
2512 
2513  PreFitPlot->SetFillStyle(1001);
2514  PreFitPlot->SetFillColor(kRed-3);
2515  PreFitPlot->SetMarkerStyle(21);
2516  PreFitPlot->SetMarkerSize(2.4);
2517  PreFitPlot->SetMarkerColor(kWhite);
2518  PreFitPlot->SetLineColor(PreFitPlot->GetFillColor());
2519  PreFitPlot->GetXaxis()->LabelsOption("v");
2520 
2521  return PreFitPlot;
2522 }
2523 
2524 // **************************
2525 //CW: Read the input Covariance matrix entries
2526 // Get stuff like parameter input errors, names, and so on
2528 // **************************
2529  FindInputFiles();
2530  if(CovPos[kXSecPar].back() != "none") ReadModelFile();
2531 }
2532 
2533 // **************************
2534 //CW: Read the input Covariance matrix entries
2535 // Get stuff like parameter input errors, names, and so on
2537 // **************************
2539  if(nParam[kNDPar] > 0) ReadNDFile();
2540  if(nParam[kFDDetPar] > 0) ReadFDFile();
2541 }
2542 
2543 // **************************
2544 // Read the output MCMC file and find what inputs were used
2546 // **************************
2547  // Now read the MCMC file
2548  TFile *TempFile = M3::Open(MCMCFile, "open", __FILE__, __LINE__);
2549  TDirectory* CovarianceFolder = TempFile->Get<TDirectory>("CovarianceFolder");
2550 
2551  // Get the settings for the MCMC
2552  TMacro *Config = TempFile->Get<TMacro>("MaCh3_Config");
2553 
2554  if (Config == nullptr) {
2555  MACH3LOG_ERROR("Didn't find MaCh3_Config tree in MCMC file! {}", MCMCFile);
2556  TempFile->ls();
2557  throw MaCh3Exception(__FILE__ , __LINE__ );
2558  }
2559  MACH3LOG_INFO("Loading YAML config from MCMC chain");
2560 
2561  YAML::Node Settings = TMacroToYAML(*Config);
2562 
2563  bool InputNotFound = false;
2564  //CW: Get the xsec Covariance matrix
2565  CovPos[kXSecPar] = GetFromManager<std::vector<std::string>>(Settings["General"]["Systematics"]["XsecCovFile"], {"none"}, __FILE__ , __LINE__);
2566  if(CovPos[kXSecPar].back() == "none")
2567  {
2568  MACH3LOG_WARN("Couldn't find XsecCov branch in output");
2569  InputNotFound = true;
2570  }
2571 
2572  TMacro *XsecConfig = M3::GetConfigMacroFromChain(CovarianceFolder);
2573  if (XsecConfig == nullptr) {
2574  MACH3LOG_WARN("Didn't find Config_xsec_cov tree in MCMC file! {}", MCMCFile);
2575  } else {
2576  CovConfig[kXSecPar] = TMacroToYAML(*XsecConfig);
2577  }
2578  if(InputNotFound) M3::Utils::PrintConfig(Settings);
2579 
2580  for(size_t i = 0; i < CovPos[kXSecPar].size(); i++)
2582 
2583  // Delete the TTrees and the input file handle since we've now got the settings we need
2584  delete Config;
2585  delete XsecConfig;
2586 
2587  TMacro *ReweightConfig = TempFile->Get<TMacro>("Reweight_Config");
2588  TMacro *UmbrellaConfig = TempFile->Get<TMacro>("Umbrella_Config");
2589 
2590  YAML::Node ReweightSettings;
2591  YAML::Node UmbrellaSettings;
2592 
2593  if (ReweightConfig != nullptr) {
2594  ReweightSettings = TMacroToYAML(*ReweightConfig);
2595  }
2596 
2597  if (UmbrellaConfig != nullptr) {
2598  UmbrellaSettings = TMacroToYAML(*UmbrellaConfig);
2599  }
2600 
2601  ReweightPosterior = true;
2602  for (const auto &name : ReweightNames) {
2603  bool found = false;
2604  // KS: For now umbrella_weight is specialised
2605  if (name == "umbrella_weight") {
2606  found = UmbrellaConfig != nullptr;
2607  } else {
2608  found = (ReweightConfig != nullptr) && ReweightSettings[name];
2609  }
2610 
2611  if (found) {
2612  MACH3LOG_INFO("Found reweight config for {}", name);
2613  } else {
2614  MACH3LOG_WARN("Missing reweight config for {}", name);
2615  ReweightPosterior = false;
2616  break;
2617  }
2618  }
2619 
2620  if (ReweightPosterior) {
2621  MACH3LOG_INFO("Enabling reweighting with configured weights.");
2622  }
2623 
2624  if (ReweightConfig != nullptr) {
2625  M3::Utils::PrintConfig(ReweightSettings);
2626  }
2627 
2628  if (UmbrellaConfig != nullptr) {
2629  M3::Utils::PrintConfig(UmbrellaSettings);
2630  }
2631 
2632  // Delete the MCMCFile pointer we're reading
2633  CovarianceFolder->Close();
2634  delete CovarianceFolder;
2635  TempFile->Close();
2636  delete TempFile;
2637 }
2638 
2639 // **************************
2640 // Read the output MCMC file and find what inputs were used
2642 // **************************
2643  // Now read the MCMC file
2644  TFile *TempFile = M3::Open(MCMCFile, "open", __FILE__, __LINE__);
2645  // Get the settings for the MCMC
2646  TMacro *Config = TempFile->Get<TMacro>("MaCh3_Config");
2647 
2648  if (Config == nullptr) {
2649  MACH3LOG_ERROR("Didn't find MaCh3_Config tree in MCMC file! {}", MCMCFile);
2650  TempFile->ls();
2651  throw MaCh3Exception(__FILE__ , __LINE__ );
2652  }
2653  YAML::Node Settings = TMacroToYAML(*Config);
2654 
2655  //CW: And the ND Covariance matrix
2656  CovPos[kNDPar].push_back(GetFromManager<std::string>(Settings["General"]["Systematics"]["NDCovFile"], "none", __FILE__ , __LINE__));
2657 
2658  if(CovPos[kNDPar].back() == "none") {
2659  MACH3LOG_WARN("Couldn't find NDCov (legacy) branch in output");
2660  } else{
2661  //If the FD Cov is not none, then you need the name of the covariance object to grab
2662  CovNamePos[kNDPar] = GetFromManager<std::string>(Settings["General"]["Systematics"]["NDCovName"], "none", __FILE__ , __LINE__);
2663  MACH3LOG_INFO("Given NDCovFile {} and NDCovName {}", CovPos[kNDPar].back(), CovNamePos[kNDPar]);
2664  }
2665 
2666  //CW: And the FD Covariance matrix
2667  CovPos[kFDDetPar].push_back(GetFromManager<std::string>(Settings["General"]["Systematics"]["FDCovFile"], "none", __FILE__ , __LINE__));
2668 
2669  if(CovPos[kFDDetPar].back() == "none") {
2670  MACH3LOG_WARN("Couldn't find FDCov (legacy) branch in output");
2671  } else {
2672  //If the FD Cov is not none, then you need the name of the covariance object to grab
2673  CovNamePos[kFDDetPar] = GetFromManager<std::string>(Settings["General"]["Systematics"]["FDCovName"], "none", __FILE__ , __LINE__);
2674  MACH3LOG_INFO("Given FDCovFile {} and FDCovName {}", CovPos[kFDDetPar].back(), CovNamePos[kFDDetPar]);
2675  }
2676 
2677  for(size_t i = 0; i < CovPos[kNDPar].size(); i++)
2678  M3::AddPath(CovPos[kNDPar][i]);
2679 
2680  for(size_t i = 0; i < CovPos[kFDDetPar].size(); i++)
2682 
2683  TempFile->Close();
2684  delete TempFile;
2685 }
2686 
2687 // ***************
2688 // Read the model file and get the input central values and errors
2690 // ***************
2691  YAML::Node XSecFile = CovConfig[kXSecPar];
2692 
2693  auto systematics = XSecFile["Systematics"];
2694  int paramIndex = 0;
2695  for (auto it = systematics.begin(); it != systematics.end(); ++it, ++paramIndex )
2696  {
2697  auto const &param = *it;
2698  // Push back the name
2699  std::string ParName = (param["Systematic"]["Names"]["FancyName"].as<std::string>());
2700  std::string Group = param["Systematic"]["ParameterGroup"].as<std::string>();
2701 
2702  bool rejected = false;
2703  for (unsigned int ik = 0; ik < ExcludedNames.size(); ++ik)
2704  {
2705  if (M3::CaseInsentiveMatch(ParName, ExcludedNames[ik]))
2706  {
2707  MACH3LOG_DEBUG("Excluding param {}, from group {}", ParName, Group);
2708  rejected = true;
2709  break;
2710  }
2711  }
2712  for (unsigned int ik = 0; ik < ExcludedGroups.size(); ++ik)
2713  {
2714  if (Group == ExcludedGroups[ik])
2715  {
2716  MACH3LOG_DEBUG("Excluding param {}, from group {}", ParName, Group);
2717  rejected = true;
2718  break;
2719  }
2720  }
2721  if(rejected) continue;
2722 
2723  ParamNames[kXSecPar].push_back(ParName);
2724  ParamCentral[kXSecPar].push_back(param["Systematic"]["ParameterValues"]["PreFitValue"].as<double>());
2725  ParamErrors[kXSecPar].push_back(param["Systematic"]["Error"].as<double>() );
2726  ParamFlat[kXSecPar].push_back(GetFromManager<bool>(param["Systematic"]["FlatPrior"], false, __FILE__ , __LINE__));
2727 
2728  ParameterGroup.push_back(Group);
2729 
2730  nParam[kXSecPar]++;
2731  ParamType.push_back(kXSecPar);
2732  // Params from osc group have branch name equal to fancy name while all others are basically xsec_0 for example
2733  if(ParameterGroup.back() == "Osc") {
2734  BranchNames.push_back(ParamNames[kXSecPar].back());
2735  } else {
2736  BranchNames.push_back("param_" + std::to_string(paramIndex));
2737  }
2738 
2739  // Check that the branch exists before setting address
2740  if (!Chain->GetBranch(BranchNames.back())) {
2741  MACH3LOG_WARN("Couldn't find branch '{}', if you are not planning to draw posteriors this might be fine", BranchNames.back());
2742  }
2743  }
2744 }
2745 
2746 // ***************
2747 // Read the ND cov file and get the input central values and errors
2749 // ***************
2750  // Do the same for the ND280
2751  TFile *NDdetFile = M3::Open(CovPos[kNDPar].back(), "open", __FILE__, __LINE__);
2752  NDdetFile->cd();
2753 
2754  TMatrixDSym *NDdetMatrix = NDdetFile->Get<TMatrixDSym>(CovNamePos[kNDPar].c_str());
2755  TVectorD *NDdetNominal = NDdetFile->Get<TVectorD>("det_weights");
2756  TDirectory *BinningDirectory = NDdetFile->Get<TDirectory>("Binning");
2757 
2758  for (int i = 0; i < NDdetNominal->GetNrows(); ++i)
2759  {
2760  ParamCentral[kNDPar].push_back( (*NDdetNominal)(i) );
2761 
2762  ParamErrors[kNDPar].push_back( std::sqrt((*NDdetMatrix)(i,i)) );
2763  ParamNames[kNDPar].push_back( Form("ND Det %i", i) );
2764  //KS: Currently we can only set it via config, change it in future
2765  ParamFlat[kNDPar].push_back( false );
2766  }
2767 
2768  TIter next(BinningDirectory->GetListOfKeys());
2769  TKey *key = nullptr;
2770  // Loop through all entries
2771  while ((key = static_cast<TKey*>(next())))
2772  {
2773  std::string name = std::string(key->GetName());
2774  TH2Poly* RefPoly = BinningDirectory->Get<TH2Poly>((name).c_str());
2775  int size = RefPoly->GetNumberOfBins();
2776  NDSamplesBins.push_back(size);
2777  NDSamplesNames.push_back(RefPoly->GetTitle());
2778  }
2779 
2780  NDdetFile->Close();
2781  delete NDdetFile;
2782 }
2783 
2784 // ***************
2785 // Read the FD cov file and get the input central values and errors
2787 // ***************
2788  // Do the same for the FD
2789  TFile *FDdetFile = M3::Open(CovPos[kFDDetPar].back(), "open", __FILE__, __LINE__);
2790  FDdetFile->cd();
2791 
2792  TMatrixD *FDdetMatrix = FDdetFile->Get<TMatrixD>(CovNamePos[kFDDetPar].c_str());
2793 
2794  for (int i = 0; i < FDdetMatrix->GetNrows(); ++i)
2795  {
2796  //KS: FD parameters start at 1. in contrary to ND280
2797  ParamCentral[kFDDetPar].push_back(1.);
2798 
2799  ParamErrors[kFDDetPar].push_back( std::sqrt((*FDdetMatrix)(i,i)) );
2800  ParamNames[kFDDetPar].push_back( Form("FD Det %i", i) );
2801 
2802  //KS: Currently we can only set it via config, change it in future
2803  ParamFlat[kFDDetPar].push_back( false );
2804  }
2805  //KS: The last parameter is p scale
2806  //ETA: we need to be careful here, this is only true for SK in the T2K beam analysis...
2807  if(FancyPlotNames) ParamNames[kFDDetPar].back() = "Momentum Scale";
2808 
2809  FDdetFile->Close();
2810  delete FDdetFile;
2811  delete FDdetMatrix;
2812 }
2813 
2814 // ***************
2815 // Make the step cut from a string
2816 void MCMCProcessor::SetStepCut(const std::string& Cuts) {
2817 // ***************
2818  StepCut = Cuts;
2819  BurnInCut = std::stoi( Cuts );
2820 
2821  CheckStepCut();
2822 }
2823 
2824 // ***************
2825 // Make the step cut from an int
2826 void MCMCProcessor::SetStepCut(const int Cuts) {
2827 // ***************
2828  std::stringstream TempStream;
2829  TempStream << "step > " << Cuts;
2830  StepCut = TempStream.str();
2831  BurnInCut = Cuts;
2832  CheckStepCut();
2833 }
2834 
2835 // ***************
2836 // Make the step cut from an int
2838 // ***************
2839  const unsigned int maxNsteps = Chain->GetMaximum("step");
2840  if(BurnInCut > maxNsteps){
2841  MACH3LOG_ERROR("StepCut({}) is larger than highest value of step({})", BurnInCut, maxNsteps);
2842  throw MaCh3Exception(__FILE__ , __LINE__ );
2843  }
2844 }
2845 
2846 // ***************
2847 // Pass central value
2848 void MCMCProcessor::GetNthParameter(const int param, double &Prior, double &PriorError, TString &Title) const {
2849 // **************************
2850  ParameterEnum ParType = ParamType[param];
2851  int ParamNo = M3::_BAD_INT_;
2852  ParamNo = param - ParamTypeStartPos[ParType];
2853 
2854  Prior = ParamCentral[ParType][ParamNo];
2855  PriorError = ParamErrors[ParType][ParamNo];
2856  Title = ParamNames[ParType][ParamNo];
2857 }
2858 
2859 // ***************
2860 // Find Param Index based on name
2861 int MCMCProcessor::GetParamIndexFromName(const std::string& Name) const {
2862 // **************************
2863  int ParamNo = M3::_BAD_INT_;
2864  for (int i = 0; i < nDraw; ++i)
2865  {
2866  TString Title = "";
2867  double Prior = 1.0, PriorError = 1.0;
2868  GetNthParameter(i, Prior, PriorError, Title);
2869 
2870  if(Name == Title)
2871  {
2872  ParamNo = i;
2873  break;
2874  }
2875  }
2876  return ParamNo;
2877 }
2878 
2879 // **************************************************
2880 // Helper function to reset histograms
2882 // **************************************************
2883  #ifdef MULTITHREAD
2884  #pragma omp parallel for
2885  #endif
2886  for (int i = 0; i < nDraw; ++i)
2887  {
2888  for (int j = 0; j <= i; ++j)
2889  {
2890  // TH2D to hold the Correlation
2891  hpost2D[i][j]->Reset("");
2892  hpost2D[i][j]->Fill(0.0, 0.0, 0.0);
2893  }
2894  }
2895 }
2896 
2897 // **************************
2898 // KS: Get Super Fancy Polar Plot
2899 void MCMCProcessor::GetPolarPlot(const std::vector<std::string>& ParNames){
2900 // **************************
2901  if(hpost[0] == nullptr) MakePostfit();
2902 
2903  std::vector<double> Margins = GetMargins(Posterior);
2904 
2905  Posterior->SetTopMargin(0.1);
2906  Posterior->SetBottomMargin(0.1);
2907  Posterior->SetLeftMargin(0.1);
2908  Posterior->SetRightMargin(0.1);
2909  Posterior->Update();
2910 
2911  MACH3LOG_INFO("Calculating Polar Plot");
2912  TDirectory *PolarDir = OutputFile->mkdir("PolarDir");
2913  PolarDir->cd();
2914 
2915  for(unsigned int k = 0; k < ParNames.size(); ++k)
2916  {
2917  //KS: First we need to find parameter number based on name
2918  int ParamNo = GetParamIndexFromName(ParNames[k]);
2919  if(ParamNo == M3::_BAD_INT_)
2920  {
2921  MACH3LOG_WARN("Couldn't find param {}. Will not calculate Polar Plot", ParNames[k]);
2922  continue;
2923  }
2924 
2925  TString Title = "";
2926  double Prior = 1.0, PriorError = 1.0;
2927  GetNthParameter(ParamNo, Prior, PriorError, Title);
2928 
2929  std::vector<double> x_val(nBins);
2930  std::vector<double> y_val(nBins);
2931 
2932  constexpr double xmin = 0;
2933  constexpr double xmax = 2*TMath::Pi();
2934 
2935  double Integral = hpost[ParamNo]->Integral();
2936  for (Int_t ipt = 0; ipt < nBins; ipt++)
2937  {
2938  x_val[ipt] = ipt*(xmax-xmin)/nBins+xmin;
2939  y_val[ipt] = hpost[ParamNo]->GetBinContent(ipt+1)/Integral;
2940  }
2941 
2942  auto PolarGraph = std::make_unique<TGraphPolar>(nBins, x_val.data(), y_val.data());
2943  PolarGraph->SetLineWidth(2);
2944  PolarGraph->SetFillStyle(3001);
2945  PolarGraph->SetLineColor(kRed);
2946  PolarGraph->SetFillColor(kRed);
2947  PolarGraph->Draw("AFL");
2948 
2949  auto Text = std::make_unique<TText>(0.6, 0.1, Title);
2950  Text->SetTextSize(0.04);
2951  Text->SetNDC(true);
2952  Text->Draw("");
2953 
2954  Posterior->Print(CanvasName);
2955  Posterior->Write(Title);
2956  } //End loop over parameters
2957 
2958  PolarDir->Close();
2959  delete PolarDir;
2960 
2961  OutputFile->cd();
2962 
2963  SetMargins(Posterior, Margins);
2964 }
2965 
2966 // **************************
2967 // Get Bayes Factor for particular parameter
2968 void MCMCProcessor::GetBayesFactor(const std::vector<std::string>& ParNames,
2969  const std::vector<std::vector<double>>& Model1Bounds,
2970  const std::vector<std::vector<double>>& Model2Bounds,
2971  const std::vector<std::vector<std::string>>& ModelNames){
2972 // **************************
2973  if(hpost[0] == nullptr) MakePostfit();
2974 
2975  MACH3LOG_INFO("Calculating Bayes Factor");
2976  if((ParNames.size() != Model1Bounds.size()) || (Model2Bounds.size() != Model1Bounds.size()) || (Model2Bounds.size() != ModelNames.size()))
2977  {
2978  MACH3LOG_ERROR("Size doesn't match");
2979  throw MaCh3Exception(__FILE__ , __LINE__ );
2980  }
2981  for(unsigned int k = 0; k < ParNames.size(); ++k)
2982  {
2983  //KS: First we need to find parameter number based on name
2984  int ParamNo = GetParamIndexFromName(ParNames[k]);
2985  if(ParamNo == M3::_BAD_INT_)
2986  {
2987  MACH3LOG_WARN("Couldn't find param {}. Will not calculate Bayes Factor", ParNames[k]);
2988  continue;
2989  }
2990 
2991  const double M1_min = Model1Bounds[k][0];
2992  const double M2_min = Model2Bounds[k][0];
2993  const double M1_max = Model1Bounds[k][1];
2994  const double M2_max = Model2Bounds[k][1];
2995 
2996  long double IntegralMode1 = hpost[ParamNo]->Integral(hpost[ParamNo]->FindFixBin(M1_min), hpost[ParamNo]->FindFixBin(M1_max));
2997  long double IntegralMode2 = hpost[ParamNo]->Integral(hpost[ParamNo]->FindFixBin(M2_min), hpost[ParamNo]->FindFixBin(M2_max));
2998 
2999  double BayesFactor = 0.;
3000  std::string Name = "";
3001  //KS: Calc Bayes Factor
3002  //If M1 is more likely
3003  if(IntegralMode1 >= IntegralMode2)
3004  {
3005  BayesFactor = IntegralMode1/IntegralMode2;
3006  Name = "\\mathfrak{B}(" + ModelNames[k][0]+ "/" + ModelNames[k][1] + ") = " + std::to_string(BayesFactor);
3007  }
3008  else //If M2 is more likely
3009  {
3010  BayesFactor = IntegralMode2/IntegralMode1;
3011  Name = "\\mathfrak{B}(" + ModelNames[k][1]+ "/" + ModelNames[k][0] + ") = " + std::to_string(BayesFactor);
3012  }
3013  std::string JeffreysScale = GetJeffreysScale(BayesFactor);
3014  std::string DunneKabothScale = GetDunneKaboth(BayesFactor);
3015 
3016  MACH3LOG_INFO("{} for {}", Name, ParNames[k]);
3017  MACH3LOG_INFO("Following Jeffreys Scale = {}", JeffreysScale);
3018  MACH3LOG_INFO("Following Dunne-Kaboth Scale = {}", DunneKabothScale);
3019  MACH3LOG_INFO("");
3020  }
3021 }
3022 
3023 // **************************
3024 // KS: Get Savage Dickey point hypothesis test
3025 void MCMCProcessor::GetSavageDickey(const std::vector<std::string>& ParNames,
3026  const std::vector<double>& EvaluationPoint,
3027  const std::vector<std::vector<double>>& Bounds){
3028 // **************************
3029  if((ParNames.size() != EvaluationPoint.size()) || (Bounds.size() != EvaluationPoint.size()))
3030  {
3031  MACH3LOG_ERROR("Size doesn't match");
3032  throw MaCh3Exception(__FILE__ , __LINE__ );
3033  }
3034 
3035  if(hpost[0] == nullptr) MakePostfit();
3036 
3037  MACH3LOG_INFO("Calculating Savage Dickey");
3038  TDirectory *SavageDickeyDir = OutputFile->mkdir("SavageDickey");
3039  SavageDickeyDir->cd();
3040 
3041  for(unsigned int k = 0; k < ParNames.size(); ++k)
3042  {
3043  //KS: First we need to find parameter number based on name
3044  int ParamNo = GetParamIndexFromName(ParNames[k]);
3045  if(ParamNo == M3::_BAD_INT_)
3046  {
3047  MACH3LOG_WARN("Couldn't find param {}. Will not calculate SavageDickey", ParNames[k]);
3048  continue;
3049  }
3050 
3051  TString Title = "";
3052  double Prior = 1.0, PriorError = 1.0;
3053  GetNthParameter(ParamNo, Prior, PriorError, Title);
3054  bool FlatPrior = GetParamFlat(ParamNo);
3055 
3056  auto PosteriorHist = M3::Clone<TH1D>(hpost[ParamNo], std::string(Title));
3057  RemoveFitter(PosteriorHist.get(), "Gauss");
3058 
3059  std::unique_ptr<TH1D> PriorHist;
3060  //KS: If flat prior we need to have well defined bounds otherwise Prior distribution will not make sense
3061  if(FlatPrior)
3062  {
3063  int NBins = PosteriorHist->GetNbinsX();
3064  if(Bounds[k][0] > Bounds[k][1])
3065  {
3066  MACH3LOG_ERROR("Lower bound is higher than upper bound");
3067  throw MaCh3Exception(__FILE__ , __LINE__ );
3068  }
3069  PriorHist = std::make_unique<TH1D>("PriorHist", Title, NBins, Bounds[k][0], Bounds[k][1]);
3070  PriorHist->SetDirectory(nullptr);
3071  double FlatProb = ( Bounds[k][1] - Bounds[k][0]) / NBins;
3072  for (int g = 0; g < NBins + 1; ++g)
3073  {
3074  PriorHist->SetBinContent(g+1, FlatProb);
3075  }
3076  }
3077  else //KS: Otherwise throw from Gaussian
3078  {
3079  PriorHist = M3::Clone<TH1D>(PosteriorHist.get(), "Prior");
3080  PriorHist->Reset("");
3081  PriorHist->Fill(0.0, 0.0);
3082 
3083  auto rand = std::make_unique<TRandom3>(0);
3084  //KS: Throw nice gaussian, just need big number to have smooth distribution
3085  for(int g = 0; g < 1000000; ++g)
3086  {
3087  PriorHist->Fill(rand->Gaus(Prior, PriorError));
3088  }
3089  }
3090  SavageDickeyPlot(PriorHist, PosteriorHist, std::string(Title), EvaluationPoint[k]);
3091  } //End loop over parameters
3092 
3093  SavageDickeyDir->Close();
3094  delete SavageDickeyDir;
3095 
3096  OutputFile->cd();
3097 }
3098 
3099 // **************************
3100 // KS: Get Savage Dickey point hypothesis test
3101 void MCMCProcessor::SavageDickeyPlot(std::unique_ptr<TH1D>& PriorHist,
3102  std::unique_ptr<TH1D>& PosteriorHist,
3103  const std::string& Title,
3104  const double EvaluationPoint) const {
3105 // **************************
3106  // Area normalise the distributions
3107  PriorHist->Scale(1./PriorHist->Integral(), "width");
3108  PosteriorHist->Scale(1./PosteriorHist->Integral(), "width");
3109 
3110  PriorHist->SetLineColor(kRed);
3111  PriorHist->SetMarkerColor(kRed);
3112  PriorHist->SetFillColorAlpha(kRed, 0.35);
3113  PriorHist->SetFillStyle(1001);
3114  PriorHist->GetXaxis()->SetTitle(Title.c_str());
3115  PriorHist->GetYaxis()->SetTitle("Posterior Probability");
3116  PriorHist->SetMaximum(PosteriorHist->GetMaximum()*1.5);
3117  PriorHist->GetYaxis()->SetLabelOffset(999);
3118  PriorHist->GetYaxis()->SetLabelSize(0);
3119  PriorHist->SetLineWidth(2);
3120  PriorHist->SetLineStyle(kSolid);
3121 
3122  PosteriorHist->SetLineColor(kBlue);
3123  PosteriorHist->SetMarkerColor(kBlue);
3124  PosteriorHist->SetFillColorAlpha(kBlue, 0.35);
3125  PosteriorHist->SetFillStyle(1001);
3126 
3127  PriorHist->Draw("hist");
3128  PosteriorHist->Draw("hist same");
3129 
3130  double ProbPrior = PriorHist->GetBinContent(PriorHist->FindBin(EvaluationPoint));
3131  //KS: In case we go so far away that prior is 0, set this to small value to avoid dividing by 0
3132  if(ProbPrior < 0) ProbPrior = 0.00001;
3133  double ProbPosterior = PosteriorHist->GetBinContent(PosteriorHist->FindBin(EvaluationPoint));
3134  double SavageDickey = ProbPosterior/ProbPrior;
3135 
3136  std::string DunneKabothScale = GetDunneKaboth(SavageDickey);
3137  //Get Best point
3138  auto PostPoint = std::make_unique<TGraph>(1);
3139  PostPoint->SetPoint(0, EvaluationPoint, ProbPosterior);
3140  PostPoint->SetMarkerStyle(20);
3141  PostPoint->SetMarkerSize(1);
3142  PostPoint->Draw("P same");
3143 
3144  auto PriorPoint = std::make_unique<TGraph>(1);
3145  PriorPoint->SetPoint(0, EvaluationPoint, ProbPrior);
3146  PriorPoint->SetMarkerStyle(20);
3147  PriorPoint->SetMarkerSize(1);
3148  PriorPoint->Draw("P same");
3149 
3150  auto legend = std::make_unique<TLegend>(0.12, 0.6, 0.6, 0.97);
3151  SetLegendStyle(legend.get(), 0.04);
3152  legend->AddEntry(PriorHist.get(), "Prior", "l");
3153  legend->AddEntry(PosteriorHist.get(), "Posterior", "l");
3154  legend->AddEntry(PostPoint.get(), Form("SavageDickey = %.2f, (%s)", SavageDickey, DunneKabothScale.c_str()),"");
3155  legend->Draw("same");
3156 
3157  Posterior->Print(CanvasName);
3158  Posterior->Write(Title.c_str());
3159 }
3160 
3161 // **************************
3162 // KS: Smear contours
3163 void MCMCProcessor::SmearChain(const std::vector<std::string>& Names,
3164  const std::vector<double>& Error,
3165  const bool& SaveBranch) const {
3166 // **************************
3167  MACH3LOG_INFO("Starting {}", __func__);
3168 
3169  if( (Names.size() != Error.size()))
3170  {
3171  MACH3LOG_ERROR("Size of passed vectors doesn't match in {}", __func__);
3172  throw MaCh3Exception(__FILE__ , __LINE__ );
3173  }
3174  std::vector<int> Param;
3175 
3176  //KS: First we need to find parameter number based on name
3177  for(unsigned int k = 0; k < Names.size(); ++k)
3178  {
3179  //KS: First we need to find parameter number based on name
3180  int ParamNo = GetParamIndexFromName(Names[k]);
3181  if(ParamNo == M3::_BAD_INT_)
3182  {
3183  MACH3LOG_WARN("Couldn't find param {}. Can't Smear", Names[k]);
3184  return;
3185  }
3186 
3187  TString Title = "";
3188  double Prior = 1.0, PriorError = 1.0;
3189  GetNthParameter(ParamNo, Prior, PriorError, Title);
3190 
3191  Param.push_back(ParamNo);
3192  }
3193  std::string InputFile = MCMCFile+".root";
3194  std::string OutputFilename = MCMCFile + "_smeared.root";
3195 
3196  //KS: Simply create copy of file and add there new branch
3197  int ret = system(("cp " + InputFile + " " + OutputFilename).c_str());
3198  if (ret != 0)
3199  MACH3LOG_WARN("Error: system call to copy file failed with code {}", ret);
3200 
3201  TFile *OutputChain = M3::Open(OutputFilename, "UPDATE", __FILE__, __LINE__);
3202  OutputChain->cd();
3203  TTree *post = OutputChain->Get<TTree>("posteriors");
3204  TTree *treeNew = post->CloneTree(0);
3205 
3206  std::vector<double> NewParameter(Names.size());
3207  for(size_t i = 0; i < Param.size(); i++) {
3208  post->SetBranchAddress(BranchNames[Param[i]], &NewParameter[i]);
3209  }
3210 
3211  std::vector<double> Unsmeared_Parameter;
3212  if(SaveBranch){
3213  Unsmeared_Parameter.resize(Param.size());
3214  for(size_t i = 0; i < Param.size(); i++) {
3215  treeNew->Branch((BranchNames[Param[i]] + "_unsmeared"), &Unsmeared_Parameter[i]);
3216  }
3217  }
3218 
3219  auto rand = std::make_unique<TRandom3>(0);
3220  Long64_t AllEntries = post->GetEntries();
3221  for (Long64_t i = 0; i < AllEntries; ++i) {
3222  // Entry from the old chain
3223  post->GetEntry(i);
3224 
3225  if(SaveBranch){
3226  for(size_t iPar = 0; iPar < Param.size(); iPar++) {
3227  Unsmeared_Parameter[iPar] = NewParameter[iPar];
3228  }
3229  }
3230  // Smear it
3231  for(size_t iPar = 0; iPar < Param.size(); iPar++) {
3232  NewParameter[iPar] = NewParameter[iPar] + rand->Gaus(0, Error[iPar]);
3233  }
3234  // Fill to the new chain
3235  treeNew->Fill();
3236  }
3237 
3238  OutputChain->cd();
3239  treeNew->Write("posteriors", TObject::kOverwrite);
3240 
3241  // KS: Save smearing metadata
3242  YAML::Node yaml_node;
3243  yaml_node["Smearing"].SetStyle(YAML::EmitterStyle::Block);
3244 
3245  for (size_t k = 0; k < Names.size(); ++k) {
3246  YAML::Node entry;
3247  entry.SetStyle(YAML::EmitterStyle::Flow);
3248 
3249  entry.push_back(Error[k]);
3250  entry.push_back("Gauss");
3251 
3252  yaml_node["Smearing"][Names[k]] = entry;
3253  }
3254  TMacro ConfigSave = YAMLtoTMacro(yaml_node, "Smearing_Config");
3255  ConfigSave.Write();
3256 
3257  OutputChain->Close();
3258  delete OutputChain;
3259 }
3260 
3261 // **************************
3262 // Diagnose the MCMC
3263 void MCMCProcessor::ParameterEvolution(const std::vector<std::string>& Names,
3264  const std::vector<int>& NIntervals) {
3265 // **************************
3266  MACH3LOG_INFO("Starting {}", __func__);
3267 
3268  //KS: First we need to find parameter number based on name
3269  for(unsigned int k = 0; k < Names.size(); ++k)
3270  {
3271  //KS: First we need to find parameter number based on name
3272  int ParamNo = GetParamIndexFromName(Names[k]);
3273  if(ParamNo == M3::_BAD_INT_)
3274  {
3275  MACH3LOG_WARN("Couldn't find param {}. Can't reweight Prior", Names[k]);
3276  continue;
3277  }
3278 
3279  const int IntervalsSize = nSteps/NIntervals[k];
3280  // ROOT won't overwrite gifs so we need to delete the file if it's there already
3281  std::string filename = Names[k] + ".gif";
3282  std::ifstream f(filename);
3283  if (f.good()) {
3284  f.close();
3285  int ret = system(fmt::format("rm {}", filename).c_str());
3286  if (ret != 0) {
3287  MACH3LOG_WARN("Error: system call to delete {} failed with code {}", filename, ret);
3288  }
3289  }
3290 
3291  int Counter = 0;
3292  for(int i = NIntervals[k]-1; i >= 0; --i)
3293  {
3294  // This holds the posterior density
3295  // KS: WARNING do not change to smart pointer, it breaks and I don't know why
3296  TH1D* EvePlot = new TH1D(BranchNames[ParamNo], BranchNames[ParamNo], nBins,
3297  hpost[ParamNo]->GetXaxis()->GetXmin(), hpost[ParamNo]->GetXaxis()->GetXmax());
3298  EvePlot->SetMinimum(0);
3299  EvePlot->GetYaxis()->SetTitle("PDF");
3300  EvePlot->GetYaxis()->SetNoExponent(false);
3301 
3302  //KS: Apply additional Cuts, like mass ordering
3303  std::string CutPosterior1D = "step > " + std::to_string(i*IntervalsSize+IntervalsSize);
3304 
3305  // If Posterior1DCut is not empty, append it
3306  if (!Posterior1DCut.empty()) {
3307  CutPosterior1D += " && " + Posterior1DCut;
3308  }
3309 
3310  // Apply reweighting if requested
3311  if (ReweightPosterior) {
3312  for (const auto& name : ReweightNames) {
3313  CutPosterior1D = "(" + CutPosterior1D + ")*(" + name + ")";
3314  }
3315  }
3316 
3317  std::string TextTitle = "Steps = 0 - "+std::to_string(Counter*IntervalsSize+IntervalsSize);
3318  // Project BranchNames[ParamNo] onto hpost, applying stepcut
3319  Chain->Project(BranchNames[ParamNo], BranchNames[ParamNo], CutPosterior1D.c_str());
3320 
3321  EvePlot->SetLineWidth(2);
3322  EvePlot->SetLineColor(kBlue-1);
3323  EvePlot->SetTitle(Names[k].c_str());
3324  EvePlot->GetXaxis()->SetTitle(EvePlot->GetTitle());
3325  EvePlot->GetYaxis()->SetLabelOffset(1000);
3326  if(ApplySmoothing) EvePlot->Smooth();
3327 
3328  EvePlot->Scale(1. / EvePlot->Integral());
3329  EvePlot->Draw("HIST");
3330 
3331  TText text(0.3, 0.8, TextTitle.c_str());
3332  text.SetTextFont (43);
3333  text.SetTextSize (40);
3334  text.SetNDC(true);
3335  text.Draw("SAME");
3336 
3337  if(i == 0) Posterior->Print((Names[k] + ".gif++20").c_str()); // produces infinite loop animated GIF
3338  else Posterior->Print((Names[k] + ".gif+20").c_str()); // add picture to .gif
3339  delete EvePlot;
3340  Counter++;
3341  }
3342  }
3343 }
3344 
3345 // **************************
3346 // Diagnose the MCMC
3348 // **************************
3349  // Prepare branches etc for DiagMCMC
3350  PrepareDiagMCMC();
3351 
3352  // Draw the simple trace matrices
3353  ParamTraces();
3354 
3355  // Get the batched means
3356  BatchedMeans();
3357 
3358  // Draw the auto-correlations
3359  if (useFFTAutoCorrelation) {
3361  } else {
3362  AutoCorrelation();
3363  }
3364 
3365  // Calculate Power Spectrum for each param
3367 
3368  // Get Geweke Z score helping select burn-in
3369  GewekeDiagnostic();
3370 
3371  // Draw acceptance Probability
3373 }
3374 
3375 // Check if all entries in StepNumber are unique
3376 bool AllUnique(unsigned int* StepNumber, size_t size) {
3377  std::unordered_set<unsigned int> s(StepNumber, StepNumber + size);
3378  return s.size() == size;
3379 }
3380 
3381 // **************************
3382 //CW: Prepare branches etc. for DiagMCMC
3384 // **************************
3385  doDiagMCMC = true;
3386 
3387  if(ParStep != nullptr) {
3388  MACH3LOG_ERROR("It look like ParStep was already filled ");
3389  MACH3LOG_ERROR("Even though it is used for MakeCovariance_MP and for DiagMCMC");
3390  MACH3LOG_ERROR("it has different structure in both for cache hits, sorry ");
3391  throw MaCh3Exception(__FILE__ , __LINE__ );
3392  }
3393  if(nBatches == 0) {
3394  MACH3LOG_ERROR("nBatches is equal to 0");
3395  MACH3LOG_ERROR("please use SetnBatches to set other value fore example 20");
3396  throw MaCh3Exception(__FILE__ , __LINE__ );
3397  }
3398 
3399  // Initialise ParStep
3400  ParStep = new M3::float_t*[nDraw]();
3401  for (int j = 0; j < nDraw; ++j) {
3402  ParStep[j] = new M3::float_t[nEntries]();
3403  for (int i = 0; i < nEntries; ++i) {
3404  ParStep[j][i] = -999.99;
3405  }
3406  }
3407 
3408  SampleValues = new double*[nEntries]();
3409  SystValues = new double*[nEntries]();
3410  AccProbValues = new double[nEntries]();
3411  StepNumber = new unsigned int[nEntries]();
3412  for (int i = 0; i < nEntries; ++i) {
3413  SampleValues[i] = new double[SampleName_v.size()]();
3414  SystValues[i] = new double[SystName_v.size()]();
3415 
3416  for (size_t j = 0; j < SampleName_v.size(); ++j) {
3417  SampleValues[i][j] = -999.99;
3418  }
3419  for (size_t j = 0; j < SystName_v.size(); ++j) {
3420  SystValues[i][j] = -999.99;
3421  }
3422  AccProbValues[i] = -999.99;
3423  StepNumber[i] = 0;
3424  }
3425 
3426  MACH3LOG_INFO("Reading input tree...");
3427  TStopwatch clock;
3428  clock.Start();
3429 
3430  // Set all the branches to off
3431  Chain->SetBranchStatus("*", false);
3432 
3433  // 10 entries output
3434  const int countwidth = nEntries/10;
3435 
3436  // Can also do the batched means here to minimize excessive loops
3437  // The length of each batch
3438  const int BatchLength = nEntries/nBatches+1;
3439  BatchedAverages = new double*[nBatches]();
3440  AccProbBatchedAverages = new double[nBatches]();
3441  for (int i = 0; i < nBatches; ++i) {
3442  BatchedAverages[i] = new double[nDraw];
3443  AccProbBatchedAverages[i] = 0;
3444  for (int j = 0; j < nDraw; ++j) {
3445  BatchedAverages[i][j] = 0.0;
3446  }
3447  }
3448  std::vector<double> ParStepBranch(nDraw);
3449  std::vector<double> SampleValuesBranch(SampleName_v.size());
3450  std::vector<double> SystValuesBranch(SystName_v.size());
3451  unsigned int StepNumberBranch = 0;
3452  double AccProbValuesBranch = 0;
3453  // Set the branch addresses for params
3454  for (int j = 0; j < nDraw; ++j) {
3455  Chain->SetBranchStatus(BranchNames[j].Data(), true);
3456  Chain->SetBranchAddress(BranchNames[j].Data(), &ParStepBranch[j]);
3457  }
3458  // Set the branch addresses for samples
3459  for (size_t j = 0; j < SampleName_v.size(); ++j) {
3460  Chain->SetBranchStatus(SampleName_v[j].Data(), true);
3461  Chain->SetBranchAddress(SampleName_v[j].Data(), &SampleValuesBranch[j]);
3462  }
3463  // Set the branch addresses for systematics
3464  for (size_t j = 0; j < SystName_v.size(); ++j) {
3465  Chain->SetBranchStatus(SystName_v[j].Data(), true);
3466  Chain->SetBranchAddress(SystName_v[j].Data(), &SystValuesBranch[j]);
3467  }
3468  // Only needed for Geweke right now
3469  Chain->SetBranchStatus("step", true);
3470  Chain->SetBranchAddress("step", &StepNumberBranch);
3471  // Turn on the branches which we want for acc prob
3472  Chain->SetBranchStatus("accProb", true);
3473  Chain->SetBranchAddress("accProb", &AccProbValuesBranch);
3474 
3475  // Loop over the entries
3476  //KS: This is really a bottleneck right now, thus revisit with ROOT6 https://pep-root6.github.io/docs/analysis/parallell/root.html
3477  for (int i = 0; i < nEntries; ++i) {
3478  // Fill up the arrays
3479  Chain->GetEntry(i);
3480 
3481  if (i % countwidth == 0)
3483 
3484  // Set the branch addresses for params
3485  for (int j = 0; j < nDraw; ++j) {
3486  ParStep[j][i] = ParStepBranch[j];
3487  }
3488  // Set the branch addresses for samples
3489  for (size_t j = 0; j < SampleName_v.size(); ++j) {
3490  SampleValues[i][j] = SampleValuesBranch[j];
3491  }
3492  // Set the branch addresses for systematics
3493  for (size_t j = 0; j < SystName_v.size(); ++j) {
3494  SystValues[i][j] = SystValuesBranch[j];
3495  }
3496 
3497  // Set the branch addresses for Acceptance Probability
3498  AccProbValues[i] = AccProbValuesBranch;
3499  StepNumber[i] = StepNumberBranch;
3500 
3501  // Find which batch the event belongs in
3502  int BatchNumber = -1;
3503  // I'm so lazy! But it's OK, the major overhead here is GetEntry: saved by ROOT!
3504  for (int j = 0; j < nBatches; ++j) {
3505  if (i < (j+1)*BatchLength) {
3506  BatchNumber = j;
3507  break;
3508  }
3509  }
3510  // Fill up the sum for each j param
3511  for (int j = 0; j < nDraw; ++j) {
3512  BatchedAverages[BatchNumber][j] += ParStep[j][i];
3513  }
3514 
3515  //KS: Could easily add this to above loop but I accProb is different beast so better keep it like this
3516  AccProbBatchedAverages[BatchNumber] += AccProbValues[i];
3517  }
3518  clock.Stop();
3519  MACH3LOG_INFO("Took {:.2f}s to finish caching statistic for Diag MCMC with {} steps", clock.RealTime(), nEntries);
3520 
3521  if(AllUnique(StepNumber, nEntries) == false){
3522  MACH3LOG_ERROR("Found steps with duplicate StepNumber, this indicate merged chain has been passed to DiagMCMC");
3523  MACH3LOG_ERROR("Code hasn't been optimised to work with merged chains, results may be unintended");
3524  throw MaCh3Exception(__FILE__ , __LINE__ );
3525  }
3526  // Make the sums into average
3527  #ifdef MULTITHREAD
3528  #pragma omp parallel for
3529  #endif
3530  for (int i = 0; i < nDraw; ++i) {
3531  for (int j = 0; j < nBatches; ++j) {
3532  // Divide by the total number of events in the batch
3533  BatchedAverages[j][i] /= BatchLength;
3534  if(i == 0) AccProbBatchedAverages[j] /= BatchLength; //KS: we have only one accProb, keep it like this for now
3535  }
3536  }
3537 
3538  // And make our sweet output file
3539  if (OutputFile == nullptr) MakeOutputFile();
3540 }
3541 
3542 // *****************
3543 //CW: Draw trace plots of the parameters i.e. parameter vs step
3545 // *****************
3546  if (ParStep == nullptr) PrepareDiagMCMC();
3547  MACH3LOG_INFO("Making trace plots...");
3548  // Make the TH1Ds
3549  std::vector<std::unique_ptr<TH1D>> TraceParamPlots(nDraw);
3550  std::vector<std::unique_ptr<TH1D>> TraceSamplePlots(SampleName_v.size());
3551  std::vector<std::unique_ptr<TH1D>> TraceSystsPlots(SystName_v.size());
3552 
3553  // Set the titles and limits for TH2Ds
3554  for (int j = 0; j < nDraw; ++j) {
3555  TString Title = "";
3556  double Prior = 1.0, PriorError = 1.0;
3557 
3558  GetNthParameter(j, Prior, PriorError, Title);
3559  std::string HistName = Form("%s_%s_Trace", Title.Data(), BranchNames[j].Data());
3560  TraceParamPlots[j] = std::make_unique<TH1D>(HistName.c_str(), HistName.c_str(), nEntries, 0, nEntries);
3561  TraceParamPlots[j]->SetDirectory(nullptr);
3562  TraceParamPlots[j]->GetXaxis()->SetTitle("Step");
3563  TraceParamPlots[j]->GetYaxis()->SetTitle("Parameter Variation");
3564  }
3565 
3566  for (size_t j = 0; j < SampleName_v.size(); ++j) {
3567  std::string HistName = SampleName_v[j].Data();
3568  TraceSamplePlots[j] = std::make_unique<TH1D>(HistName.c_str(), HistName.c_str(), nEntries, 0, nEntries);
3569  TraceSamplePlots[j]->SetDirectory(nullptr);
3570  TraceSamplePlots[j]->GetXaxis()->SetTitle("Step");
3571  TraceSamplePlots[j]->GetYaxis()->SetTitle("Sample -logL");
3572  }
3573 
3574  for (size_t j = 0; j < SystName_v.size(); ++j) {
3575  std::string HistName = SystName_v[j].Data();
3576  TraceSystsPlots[j] = std::make_unique<TH1D>(HistName.c_str(), HistName.c_str(), nEntries, 0, nEntries);
3577  TraceSystsPlots[j]->SetDirectory(nullptr);
3578  TraceSystsPlots[j]->GetXaxis()->SetTitle("Step");
3579  TraceSystsPlots[j]->GetYaxis()->SetTitle("Systematic -logL");
3580  }
3581 
3582  // Have now made the empty TH1Ds, now for writing content to them!
3583  // Loop over the number of parameters to draw their traces
3584  // Each histogram
3585  #ifdef MULTITHREAD
3586  #pragma omp parallel for
3587  #endif
3588  for (int i = 0; i < nEntries; ++i) {
3589  // Set bin content for the ith bin to the parameter values
3590  for (int j = 0; j < nDraw; ++j) {
3591  TraceParamPlots[j]->SetBinContent(i, ParStep[j][i]);
3592  }
3593  for (size_t j = 0; j < SampleName_v.size(); ++j) {
3594  TraceSamplePlots[j]->SetBinContent(i, SampleValues[i][j]);
3595  }
3596  for (size_t j = 0; j < SystName_v.size(); ++j) {
3597  TraceSystsPlots[j]->SetBinContent(i, SystValues[i][j]);
3598  }
3599  }
3600 
3601  // Write the output and delete the TH2Ds
3602  TDirectory *TraceDir = OutputFile->mkdir("Trace");
3603  TraceDir->cd();
3604  for (int j = 0; j < nDraw; ++j) {
3605  // Fit a linear function to the traces
3606  auto Fitter = std::make_unique<TF1>("Fitter", "[0]", nEntries/2, nEntries);
3607  Fitter->SetLineColor(kRed);
3608  TraceParamPlots[j]->Fit("Fitter","Rq");
3609  TraceParamPlots[j]->Write();
3610  }
3611 
3612  TDirectory *LLDir = OutputFile->mkdir("LogL");
3613  LLDir->cd();
3614  for (size_t j = 0; j < SampleName_v.size(); ++j) {
3615  TraceSamplePlots[j]->Write();
3616  delete[] SampleValues[j];
3617  }
3618  delete[] SampleValues;
3619 
3620  for (size_t j = 0; j < SystName_v.size(); ++j) {
3621  TraceSystsPlots[j]->Write();
3622  delete SystValues[j];
3623  }
3624  delete[] SystValues;
3625 
3626  TraceDir->Close();
3627  delete TraceDir;
3628 
3629  OutputFile->cd();
3630 }
3631 
3632 // *********************************
3633 std::vector <double> MCMCProcessor::GetParameterSums() {
3634 // *********************************
3635  // Initialise the sums
3636  std::vector <double> ParamSums(nDraw,0);
3637 
3638  #ifdef MULTITHREAD
3639  #pragma omp parallel for
3640  #endif
3641  for (int j = 0; j < nDraw; ++j) {
3642  for (int i = 0; i < nEntries; ++i) {
3643  ParamSums[j] += ParStep[j][i];
3644  }
3645  }
3646  // Make the sums into average
3647  #ifdef MULTITHREAD
3648  #pragma omp parallel for
3649  #endif
3650  for (int i = 0; i < nDraw; ++i) {
3651  ParamSums[i] /= double(nEntries);
3652  }
3653  return ParamSums;
3654 }
3655 
3656 // *********************************
3657 // MJR: Calculate autocorrelations using the FFT algorithm.
3658 // Fast, even on CPU, and get all lags for free.
3660 // *********************************
3661  if (ParStep == nullptr) PrepareDiagMCMC();
3662 
3663  TStopwatch clock;
3664  clock.Start();
3665  const int nLags = AutoCorrLag;
3666  MACH3LOG_INFO("Making auto-correlations for nLags = {}", nLags);
3667 
3668  // Prep outputs
3669  OutputFile->cd();
3670  TDirectory* AutoCorrDir = OutputFile->mkdir("Auto_corr");
3671  std::vector<std::unique_ptr<TH1D>> LagKPlots(nDraw);
3672  std::vector<std::vector<double>> LagL(nDraw);
3673 
3674  // Arrays needed to perform FFT using ROOT
3675  std::vector<double> ACFFT(nEntries, 0.0); // Main autocorrelation array
3676  std::vector<double> ParVals(nEntries, 0.0); // Param values for full chain
3677  std::vector<double> ParValsFFTR(nEntries, 0.0); // FFT Real part
3678  std::vector<double> ParValsFFTI(nEntries, 0.0); // FFT Imaginary part
3679  std::vector<double> ParValsFFTSquare(nEntries, 0.0); // FFT Absolute square
3680  std::vector<double> ParValsComplex(nEntries, 0.0); // Input Imaginary values (0)
3681 
3682  auto ParamSums = GetParameterSums();
3683  // Create forward and reverse FFT objects. I don't love using ROOT here,
3684  // but it works so I can't complain
3685  TVirtualFFT* fftf = TVirtualFFT::FFT(1, &nEntries, "C2CFORWARD");
3686  TVirtualFFT* fftb = TVirtualFFT::FFT(1, &nEntries, "C2CBACKWARD");
3687 
3688  // Loop over all pars and calculate the full autocorrelation function using FFT
3689  for (int j = 0; j < nDraw; ++j) {
3690  // Initialize
3691  LagL[j].resize(nLags);
3692  for (int i = 0; i < nEntries; ++i) {
3693  ParVals[i] = ParStep[j][i]-ParamSums[j]; // Subtract the mean to make it numerically tractable
3694  ParValsComplex[i] = 0.; // Reset dummy array
3695  }
3696 
3697  // Transform
3698  fftf->SetPointsComplex(ParVals.data(), ParValsComplex.data());
3699  fftf->Transform();
3700  fftf->GetPointsComplex(ParValsFFTR.data(), ParValsFFTI.data());
3701 
3702  // Square the results to get the power spectrum
3703  for (int i = 0; i < nEntries; ++i) {
3704  ParValsFFTSquare[i] = ParValsFFTR[i]*ParValsFFTR[i] + ParValsFFTI[i]*ParValsFFTI[i];
3705  }
3706 
3707  // Transforming back gives the autocovariance
3708  fftb->SetPointsComplex(ParValsFFTSquare.data(), ParValsComplex.data());
3709  fftb->Transform();
3710  fftb->GetPointsComplex(ACFFT.data(), ParValsComplex.data());
3711 
3712  // Divide by norm to get autocorrelation
3713  double normAC = ACFFT[0];
3714  for (int i = 0; i < nEntries; ++i) {
3715  ACFFT[i] /= normAC;
3716  }
3717 
3718  // Get plotting info
3719  TString Title = "";
3720  double Prior = 1.0, PriorError = 1.0;
3721  GetNthParameter(j, Prior, PriorError, Title);
3722  std::string HistName = Form("%s_%s_Lag", Title.Data(), BranchNames[j].Data());
3723 
3724  // Initialize Lag plot
3725  LagKPlots[j] = std::make_unique<TH1D>(HistName.c_str(), HistName.c_str(), nLags, 0.0, nLags);
3726  LagKPlots[j]->SetDirectory(nullptr);
3727  LagKPlots[j]->GetXaxis()->SetTitle("Lag");
3728  LagKPlots[j]->GetYaxis()->SetTitle("Auto-correlation function");
3729 
3730  // Fill plot
3731  for (int k = 0; k < nLags; ++k) {
3732  LagL[j][k] = ACFFT[k];
3733  LagKPlots[j]->SetBinContent(k, ACFFT[k]);
3734  }
3735 
3736  // Write and clean up
3737  AutoCorrDir->cd();
3738  LagKPlots[j]->Write();
3739  }
3740 
3741  //KS: This is different diagnostic however it relies on calculated Lag, thus we call it before we delete LagKPlots
3742  CalculateESS(nLags, LagL);
3743 
3744  AutoCorrDir->Close();
3745  delete AutoCorrDir;
3746 
3747  OutputFile->cd();
3748 
3749  clock.Stop();
3750  MACH3LOG_INFO("Making auto-correlations took {:.2f}s", clock.RealTime());
3751 }
3752 
3753 // *********************************
3754 //KS: Calculate autocorrelations supports both OpenMP and CUDA :)
3756 // *********************************
3757  if (ParStep == nullptr) PrepareDiagMCMC();
3758 
3759  TStopwatch clock;
3760  clock.Start();
3761  const int nLags = AutoCorrLag;
3762  MACH3LOG_INFO("Making auto-correlations for nLags = {}", nLags);
3763 
3764  // The sum of (Y-Ymean)^2 over all steps for each parameter
3765  std::vector<std::vector<double>> DenomSum(nDraw);
3766  std::vector<std::vector<double>> NumeratorSum(nDraw);
3767  std::vector<std::vector<double>> LagL(nDraw);
3768  auto ParamSums = GetParameterSums();
3769  for (int j = 0; j < nDraw; ++j) {
3770  DenomSum[j].resize(nLags);
3771  NumeratorSum[j].resize(nLags);
3772  LagL[j].resize(nLags);
3773  }
3774  std::vector<std::unique_ptr<TH1D>> LagKPlots(nDraw);
3775  // Loop over the parameters of interest
3776  for (int j = 0; j < nDraw; ++j)
3777  {
3778  // Loop over each lag
3779  for (int k = 0; k < nLags; ++k) {
3780  NumeratorSum[j][k] = 0.0;
3781  DenomSum[j][k] = 0.0;
3782  LagL[j][k] = 0.0;
3783  }
3784 
3785  // Make TH1Ds for each parameter which hold the lag
3786  TString Title = "";
3787  double Prior = 1.0, PriorError = 1.0;
3788 
3789  GetNthParameter(j, Prior, PriorError, Title);
3790  std::string HistName = Form("%s_%s_Lag", Title.Data(), BranchNames[j].Data());
3791  LagKPlots[j] = std::make_unique<TH1D>(HistName.c_str(), HistName.c_str(), nLags, 0.0, nLags);
3792  LagKPlots[j]->SetDirectory(nullptr);
3793  LagKPlots[j]->GetXaxis()->SetTitle("Lag");
3794  LagKPlots[j]->GetYaxis()->SetTitle("Auto-correlation function");
3795  }
3796 //KS: If CUDA is not enabled do calculations on CPU
3797 #ifndef MaCh3_CUDA
3798  // Loop over the lags
3799  //CW: Each lag is independent so might as well multi-thread them!
3800  #ifdef MULTITHREAD
3801  MACH3LOG_INFO("Using multi-threading...");
3802  #pragma omp parallel for collapse(2)
3803  #endif // Loop over the number of parameters
3804  for (int j = 0; j < nDraw; ++j) {
3805  for (int k = 0; k < nLags; ++k) {
3806  // Loop over the number of entries
3807  for (int i = 0; i < nEntries; ++i) {
3808  const double Diff = ParStep[j][i]-ParamSums[j];
3809 
3810  // Only sum the numerator up to i = N-k
3811  if (i < nEntries-k) {
3812  const double LagTerm = ParStep[j][i+k]-ParamSums[j];
3813  const double Product = Diff*LagTerm;
3814  NumeratorSum[j][k] += Product;
3815  }
3816  // Square the difference to form the denominator
3817  const double Denom = Diff*Diff;
3818  DenomSum[j][k] += Denom;
3819  }
3820  }
3821  }
3822 #else //NOW GPU specific code
3823  MACH3LOG_INFO("Using GPU");
3825  float* ParStep_cpu = nullptr;
3826  float* NumeratorSum_cpu = nullptr;
3827  float* ParamSums_cpu = nullptr;
3828  float* DenomSum_cpu = nullptr;
3829 
3830  //KS: This allocates memory and copy data from CPU to GPU
3831  PrepareGPU_AutoCorr(nLags, ParamSums, ParStep_cpu, NumeratorSum_cpu, ParamSums_cpu, DenomSum_cpu);
3832 
3833  //KS: This runs the main kernel and copy results back to CPU
3834  GPUProcessor->RunGPU_AutoCorr(NumeratorSum_cpu,
3835  DenomSum_cpu);
3836 
3837  #ifdef MULTITHREAD
3838  #pragma omp parallel for collapse(2)
3839  #endif
3840  //KS: Now that that we received data from GPU convert it to CPU-like format
3841  for (int j = 0; j < nDraw; ++j)
3842  {
3843  for (int k = 0; k < nLags; ++k)
3844  {
3845  const int temp_index = j*nLags+k;
3846  NumeratorSum[j][k] = NumeratorSum_cpu[temp_index];
3847  DenomSum[j][k] = DenomSum_cpu[temp_index];
3848  }
3849  }
3850  //delete auxiliary variables
3851  if(NumeratorSum_cpu) delete[] NumeratorSum_cpu;
3852  if(DenomSum_cpu) delete[] DenomSum_cpu;
3853  if(ParStep_cpu) delete[] ParStep_cpu;
3854  if(ParamSums_cpu) delete[] ParamSums_cpu;
3855 
3856  //KS: Delete stuff at GPU as well
3857  GPUProcessor->CleanupGPU_AutoCorr();
3858 
3859 //KS: End of GPU specific code
3860 #endif
3861 
3862  OutputFile->cd();
3863  TDirectory *AutoCorrDir = OutputFile->mkdir("Auto_corr");
3864  // Now fill the LagK auto-correlation plots
3865  for (int j = 0; j < nDraw; ++j) {
3866  for (int k = 0; k < nLags; ++k) {
3867  LagL[j][k] = NumeratorSum[j][k]/DenomSum[j][k];
3868  LagKPlots[j]->SetBinContent(k, NumeratorSum[j][k]/DenomSum[j][k]);
3869  }
3870  AutoCorrDir->cd();
3871  LagKPlots[j]->Write();
3872  }
3873 
3874  //KS: This is different diagnostic however it relies on calculated Lag, thus we call it before we delete LagKPlots
3875  CalculateESS(nLags, LagL);
3876 
3877  AutoCorrDir->Close();
3878  delete AutoCorrDir;
3879 
3880  OutputFile->cd();
3881 
3882  clock.Stop();
3883  MACH3LOG_INFO("Making auto-correlations took {:.2f}s", clock.RealTime());
3884 }
3885 
3886 #ifdef MaCh3_CUDA
3887 // **************************
3888 //KS: Allocates memory and copy data from CPU to GPU
3889 void MCMCProcessor::PrepareGPU_AutoCorr(const int nLags, const std::vector<double>& ParamSums, float*& ParStep_cpu,
3890  float*& NumeratorSum_cpu, float*& ParamSums_cpu, float*& DenomSum_cpu) {
3891 // **************************
3892  //KS: Create temporary arrays that will communicate with GPU code
3893  ParStep_cpu = new float[nDraw*nEntries];
3894  NumeratorSum_cpu = new float[nDraw*nLags];
3895  DenomSum_cpu = new float[nDraw*nLags];
3896  ParamSums_cpu = new float[nDraw];
3897 
3898  #ifdef MULTITHREAD
3899  //KS: Open parallel region
3900  #pragma omp parallel
3901  {
3902  #endif
3903  //KS: Operations are independent thus we are using nowait close
3904  #ifdef MULTITHREAD
3905  #pragma omp for nowait
3906  #endif
3907  for (int i = 0; i < nDraw; ++i)
3908  {
3909  //KS: We basically need this to convert from double to float for GPU
3910  ParamSums_cpu[i] = ParamSums[i];
3911  }
3912 
3913  #ifdef MULTITHREAD
3914  #pragma omp for collapse(2) nowait
3915  #endif
3916  for (int j = 0; j < nDraw; ++j)
3917  {
3918  for (int k = 0; k < nLags; ++k)
3919  {
3920  const int temp = j*nLags+k;
3921  NumeratorSum_cpu[temp] = 0.0;
3922  DenomSum_cpu[temp] = 0.0;
3923  }
3924  }
3925 
3926  #ifdef MULTITHREAD
3927  #pragma omp for collapse(2)
3928  #endif
3929  for (int j = 0; j < nDraw; ++j)
3930  {
3931  for (int i = 0; i < nEntries; ++i)
3932  {
3933  const int temp = j*nEntries+i;
3934  ParStep_cpu[temp] = ParStep[j][i];
3935  }
3936  }
3937  #ifdef MULTITHREAD
3938  //KS: End parallel region
3939  }
3940  #endif
3941 
3942  //KS: First allocate memory on GPU
3943  GPUProcessor->InitGPU_AutoCorr(nEntries,
3944  nDraw,
3945  nLags);
3946 
3947 
3948  //KS: Now copy from CPU to GPU
3949  GPUProcessor->CopyToGPU_AutoCorr(ParStep_cpu,
3950  NumeratorSum_cpu,
3951  ParamSums_cpu,
3952  DenomSum_cpu);
3953 }
3954 #endif
3955 
3956 
3957 // **************************
3958 // KS: calc Effective Sample Size Following @cite StanManual
3959 // Furthermore we calculate Sampling efficiency following @cite hanson2008mcmc
3960 // Rule of thumb is to have efficiency above 25%
3961 void MCMCProcessor::CalculateESS(const int nLags, const std::vector<std::vector<double>>& LagL) {
3962 // **************************
3963  if(LagL.size() == 0)
3964  {
3965  MACH3LOG_ERROR("Size of LagL is {}", LagL.size());
3966  throw MaCh3Exception(__FILE__ , __LINE__ );
3967  }
3968  MACH3LOG_INFO("Making ESS plots...");
3969  TVectorD* EffectiveSampleSize = new TVectorD(nDraw);
3970  TVectorD* SamplingEfficiency = new TVectorD(nDraw);
3971  std::vector<double> TempDenominator(nDraw);
3972 
3973  constexpr int Nhists = 5;
3974  constexpr double Thresholds[Nhists + 1] = {1, 0.02, 0.005, 0.001, 0.0001, 0.0};
3975  constexpr Color_t ESSColours[Nhists] = {kGreen, kGreen + 2, kYellow, kOrange, kRed};
3976 
3977  //KS: This histogram is inspired by the following: @cite gabry2024visual
3978  std::vector<std::unique_ptr<TH1D>> EffectiveSampleSizeHist(Nhists);
3979  for(int i = 0; i < Nhists; ++i)
3980  {
3981  EffectiveSampleSizeHist[i] =
3982  std::make_unique<TH1D>(Form("EffectiveSampleSizeHist_%i", i), Form("EffectiveSampleSizeHist_%i", i), nDraw, 0, nDraw);
3983  EffectiveSampleSizeHist[i]->SetDirectory(nullptr);
3984  EffectiveSampleSizeHist[i]->GetYaxis()->SetTitle("N_{eff}/N");
3985  EffectiveSampleSizeHist[i]->SetFillColor(ESSColours[i]);
3986  EffectiveSampleSizeHist[i]->SetLineColor(ESSColours[i]);
3987  EffectiveSampleSizeHist[i]->Sumw2();
3988  for (int j = 0; j < nDraw; ++j)
3989  {
3990  TString Title = "";
3991  double Prior = 1.0, PriorError = 1.0;
3992  GetNthParameter(j, Prior, PriorError, Title);
3993  EffectiveSampleSizeHist[i]->GetXaxis()->SetBinLabel(j+1, Title.Data());
3994  }
3995  }
3996 
3997  #ifdef MULTITHREAD
3998  #pragma omp parallel for
3999  #endif
4000  //KS: Calculate ESS and MCMC efficiency for each parameter
4001  for (int j = 0; j < nDraw; ++j)
4002  {
4003  (*EffectiveSampleSize)(j) = M3::_BAD_DOUBLE_;
4004  (*SamplingEfficiency)(j) = M3::_BAD_DOUBLE_;
4005  TempDenominator[j] = 0.;
4006  //KS: Firs sum over all Calculated autocorrelations
4007  for (int k = 0; k < nLags; ++k)
4008  {
4009  TempDenominator[j] += LagL[j][k];
4010  }
4011  TempDenominator[j] = 1+2*TempDenominator[j];
4012  (*EffectiveSampleSize)(j) = double(nEntries)/TempDenominator[j];
4013  // 100 because we convert to percentage
4014  (*SamplingEfficiency)(j) = 100 * 1/TempDenominator[j];
4015 
4016  for(int i = 0; i < Nhists; ++i)
4017  {
4018  EffectiveSampleSizeHist[i]->SetBinContent(j+1, 0);
4019  EffectiveSampleSizeHist[i]->SetBinError(j+1, 0);
4020 
4021  const double TempEntry = std::fabs((*EffectiveSampleSize)(j)) / double(nEntries);
4022  if(Thresholds[i] >= TempEntry && TempEntry > Thresholds[i+1])
4023  {
4024  if( std::isnan((*EffectiveSampleSize)(j)) ) continue;
4025  EffectiveSampleSizeHist[i]->SetBinContent(j+1, TempEntry);
4026  }
4027  }
4028  }
4029 
4030  //KS Write to the output tree
4031  //Save to file
4032  OutputFile->cd();
4033  EffectiveSampleSize->Write("EffectiveSampleSize");
4034  SamplingEfficiency->Write("SamplingEfficiency");
4035 
4036  EffectiveSampleSizeHist[0]->SetTitle("Effective Sample Size");
4037  EffectiveSampleSizeHist[0]->Draw();
4038  for(int i = 1; i < Nhists; ++i)
4039  {
4040  EffectiveSampleSizeHist[i]->Draw("SAME");
4041  }
4042 
4043  auto leg = std::make_unique<TLegend>(0.2, 0.7, 0.6, 0.95);
4044  SetLegendStyle(leg.get(), 0.03);
4045  for(int i = 0; i < Nhists; ++i)
4046  {
4047  leg->AddEntry(EffectiveSampleSizeHist[i].get(), Form("%.4f >= N_{eff}/N > %.4f", Thresholds[i], Thresholds[i+1]), "f");
4048  } leg->Draw("SAME");
4049 
4050  Posterior->Write("EffectiveSampleSizeCanvas");
4051 
4052  //Delete all variables
4053  delete EffectiveSampleSize;
4054  delete SamplingEfficiency;
4055 }
4056 
4057 // **************************
4058 //CW: Batched means, literally read from an array and chuck into TH1D
4060 // **************************
4061  if (BatchedAverages == nullptr) PrepareDiagMCMC();
4062  MACH3LOG_INFO("Making BatchedMeans plots...");
4063 
4064  std::vector<std::unique_ptr<TH1D>> BatchedParamPlots(nDraw);
4065  for (int j = 0; j < nDraw; ++j) {
4066  TString Title = "";
4067  double Prior = 1.0, PriorError = 1.0;
4068 
4069  GetNthParameter(j, Prior, PriorError, Title);
4070 
4071  std::string HistName = Form("%s_%s_batch", Title.Data(), BranchNames[j].Data());
4072  BatchedParamPlots[j] = std::make_unique<TH1D>(HistName.c_str(), HistName.c_str(), nBatches, 0, nBatches);
4073  BatchedParamPlots[j]->SetDirectory(nullptr);
4074  }
4075 
4076  #ifdef MULTITHREAD
4077  #pragma omp parallel for
4078  #endif
4079  for (int j = 0; j < nDraw; ++j) {
4080  for (int i = 0; i < nBatches; ++i) {
4081  BatchedParamPlots[j]->SetBinContent(i+1, BatchedAverages[i][j]);
4082  const int BatchRangeLow = double(i)*double(nEntries)/double(nBatches);
4083  const int BatchRangeHigh = double(i+1)*double(nEntries)/double(nBatches);
4084  std::stringstream ss;
4085  ss << BatchRangeLow << " - " << BatchRangeHigh;
4086  BatchedParamPlots[j]->GetXaxis()->SetBinLabel(i+1, ss.str().c_str());
4087  }
4088  }
4089 
4090  TDirectory *BatchDir = OutputFile->mkdir("Batched_means");
4091  BatchDir->cd();
4092  for (int j = 0; j < nDraw; ++j) {
4093  auto Fitter = std::make_unique<TF1>("Fitter", "[0]", 0, nBatches);
4094  Fitter->SetLineColor(kRed);
4095  BatchedParamPlots[j]->Fit("Fitter","Rq");
4096  BatchedParamPlots[j]->Write();
4097  }
4098 
4099  //KS: Get the batched means variance estimation and variable indicating if number of batches is sensible
4100  // We do this before deleting BatchedAverages
4101  BatchedAnalysis();
4102 
4103  for (int i = 0; i < nBatches; ++i) {
4104  delete BatchedAverages[i];
4105  }
4106  delete[] BatchedAverages;
4107 
4108  BatchDir->Close();
4109  delete BatchDir;
4110 
4111  OutputFile->cd();
4112 }
4113 
4114 // **************************
4115 // Get the batched means variance estimation and variable indicating if number of batches is sensible
4117 // **************************
4118  if(BatchedAverages == nullptr)
4119  {
4120  MACH3LOG_ERROR("BatchedAverages haven't been initialises or have been deleted something is wrong");
4121  MACH3LOG_ERROR("I need it and refuse to go further");
4122  throw MaCh3Exception(__FILE__ , __LINE__ );
4123  }
4124 
4125  // Calculate variance estimator using batched means following @cite chakraborty2019estimating see Eq. 1.2
4126  TVectorD* BatchedVariance = new TVectorD(nDraw);
4127  //KS: The hypothesis is rejected if C > z α for a given confidence level α. If the batch means do not pass the test, Correlated is reported for the half-width on the statistical reports following @cite rossetti2024batch alternatively for more old-school see Alexopoulos and Seila 1998 section 3.4.3
4128  TVectorD* C_Test_Statistics = new TVectorD(nDraw);
4129 
4130  std::vector<double> OverallBatchMean(nDraw);
4131  std::vector<double> C_Rho_Nominator(nDraw);
4132  std::vector<double> C_Rho_Denominator(nDraw);
4133  std::vector<double> C_Nominator(nDraw);
4134  std::vector<double> C_Denominator(nDraw);
4135  const int BatchLength = nEntries/nBatches+1;
4136 //KS: Start parallel region
4137 #ifdef MULTITHREAD
4138 #pragma omp parallel
4139 {
4140 #endif
4141  #ifdef MULTITHREAD
4142  #pragma omp for
4143  #endif
4144  //KS: First calculate mean of batched means for each param and Initialise everything to 0
4145  for (int j = 0; j < nDraw; ++j)
4146  {
4147  OverallBatchMean[j] = 0.0;
4148  C_Rho_Nominator[j] = 0.0;
4149  C_Rho_Denominator[j] = 0.0;
4150  C_Nominator[j] = 0.0;
4151  C_Denominator[j] = 0.0;
4152 
4153  (*BatchedVariance)(j) = 0.0;
4154  (*C_Test_Statistics)(j) = 0.0;
4155  for (int i = 0; i < nBatches; ++i)
4156  {
4157  OverallBatchMean[j] += BatchedAverages[i][j];
4158  }
4159  OverallBatchMean[j] /= nBatches;
4160  }
4161 
4162  #ifdef MULTITHREAD
4163  #pragma omp for nowait
4164  #endif
4165  //KS: next loop is completely independent thus nowait clause
4166  for (int j = 0; j < nDraw; ++j)
4167  {
4168  for (int i = 0; i < nBatches; ++i)
4169  {
4170  (*BatchedVariance)(j) += (OverallBatchMean[j] - BatchedAverages[i][j])*(OverallBatchMean[j] - BatchedAverages[i][j]);
4171  }
4172  (*BatchedVariance)(j) = (BatchLength/(nBatches-1))* (*BatchedVariance)(j);
4173  }
4174 
4175  //KS: Now we focus on C test statistic, again use nowait as next is calculation is independent
4176  #ifdef MULTITHREAD
4177  #pragma omp for nowait
4178  #endif
4179  for (int j = 0; j < nDraw; ++j)
4180  {
4181  C_Nominator[j] = (OverallBatchMean[j] - BatchedAverages[0][j])*(OverallBatchMean[j] - BatchedAverages[0][j]) +
4182  (OverallBatchMean[j] - BatchedAverages[nBatches-1][j])*(OverallBatchMean[j] - BatchedAverages[nBatches-1][j]);
4183  for (int i = 0; i < nBatches; ++i)
4184  {
4185  C_Denominator[j] += (OverallBatchMean[j] - BatchedAverages[i][j])*(OverallBatchMean[j] - BatchedAverages[i][j]);
4186  }
4187  C_Denominator[j] = 2*C_Denominator[j];
4188  }
4189 
4190  //KS: We still calculate C and for this we need rho wee need autocorrelations between batches
4191  #ifdef MULTITHREAD
4192  #pragma omp for
4193  #endif
4194  for (int j = 0; j < nDraw; ++j)
4195  {
4196  for (int i = 0; i < nBatches-1; ++i)
4197  {
4198  C_Rho_Nominator[j] += (OverallBatchMean[j] - BatchedAverages[i][j])*(OverallBatchMean[j] - BatchedAverages[i+1][j]);
4199  }
4200 
4201  for (int i = 0; i < nBatches; ++i)
4202  {
4203  C_Rho_Denominator[j] += (OverallBatchMean[j] - BatchedAverages[i][j])*(OverallBatchMean[j] - BatchedAverages[i][j]);
4204  }
4205  }
4206 
4207  //KS: Final calculations of C
4208  #ifdef MULTITHREAD
4209  #pragma omp for
4210  #endif
4211  for (int j = 0; j < nDraw; ++j)
4212  {
4213  (*C_Test_Statistics)(j) = std::sqrt((nBatches*nBatches - 1)/(nBatches-2)) * ( C_Rho_Nominator[j]/C_Rho_Denominator[j] + C_Nominator[j]/ C_Denominator[j]);
4214  }
4215 #ifdef MULTITHREAD
4216 } //End parallel region
4217 #endif
4218 
4219  //Save to file
4220  OutputFile->cd();
4221  BatchedVariance->Write("BatchedMeansVariance");
4222  C_Test_Statistics->Write("C_Test_Statistics");
4223 
4224  //Delete all variables
4225  delete BatchedVariance;
4226  delete C_Test_Statistics;
4227 }
4228 
4229 // **************************
4230 // RC: Perform spectral analysis of MCMC based on @cite Dunkley:2004sv
4232 // **************************
4233  TStopwatch clock;
4234  clock.Start();
4235 
4236  //KS: Store it as we go back to them at the end
4237  const double TopMargin = Posterior->GetTopMargin();
4238  const int OptTitle = gStyle->GetOptTitle();
4239 
4240  Posterior->SetTopMargin(0.1);
4241  gStyle->SetOptTitle(1);
4242 
4243  MACH3LOG_INFO("Making Power Spectrum plots...");
4244 
4245  // This is only to reduce number of computations...
4246  const int N_Coeffs = std::min(10000, nEntries);
4247  const int start = -(N_Coeffs/2-1);
4248  const int end = N_Coeffs/2-1;
4249  const int v_size = end - start;
4250 
4251  int nPrams = nDraw;
4253  nPrams = 1;
4254 
4255  std::vector<std::vector<float>> k_j(nPrams, std::vector<float>(v_size, 0.0));
4256  std::vector<std::vector<float>> P_j(nPrams, std::vector<float>(v_size, 0.0));
4257 
4258  int _N = nEntries;
4259  if (_N % 2 != 0) _N -= 1; // N must be even
4260 
4261  //This is being used a lot so calculate it once to increase performance
4262  const double two_pi_over_N = 2 * TMath::Pi() / static_cast<double>(_N);
4263 
4264  // KS: This could be moved to GPU I guess
4265  #ifdef MULTITHREAD
4266  #pragma omp parallel for collapse(2)
4267  #endif
4268  // RC: equation 11: for each value of j coef, from range -N/2 -> N/2
4269  for (int j = 0; j < nPrams; ++j)
4270  {
4271  for (int jj = start; jj < end; ++jj)
4272  {
4273  std::complex<M3::float_t> a_j = 0.0;
4274  const double two_pi_over_N_jj = two_pi_over_N * jj;
4275  for (int n = 0; n < _N; ++n)
4276  {
4277  //if(StepNumber[n] < BurnInCut) continue;
4278  std::complex<M3::float_t> exp_temp(0, two_pi_over_N_jj * n);
4279  a_j += ParStep[j][n] * std::exp(exp_temp);
4280  }
4281  a_j /= std::sqrt(float(_N));
4282  const int _c = jj - start;
4283 
4284  k_j[j][_c] = two_pi_over_N_jj;
4285  // Equation 13
4286  P_j[j][_c] = std::norm(a_j);
4287  }
4288  }
4289 
4290  TDirectory *PowerDir = OutputFile->mkdir("PowerSpectrum");
4291  PowerDir->cd();
4292 
4293  TVectorD* PowerSpectrumStepSize = new TVectorD(nPrams);
4294  for (int j = 0; j < nPrams; ++j)
4295  {
4296  auto plot = std::make_unique<TGraph>(v_size, k_j[j].data(), P_j[j].data());
4297 
4298  TString Title = "";
4299  double Prior = 1.0, PriorError = 1.0;
4300  GetNthParameter(j, Prior, PriorError, Title);
4301 
4302  std::string name = Form("Power Spectrum of %s;k;P(k)", Title.Data());
4303 
4304  plot->SetTitle(name.c_str());
4305  name = Form("%s_power_spectrum", Title.Data());
4306  plot->SetName(name.c_str());
4307  plot->SetMarkerStyle(7);
4308 
4309  // Equation 18
4310  auto func = std::make_unique<TF1>("power_template", "[0]*( ([1] / x)^[2] / (([1] / x)^[2] +1) )", 0.0, 1.0);
4311  // P0 gives the amplitude of the white noise spectrum in the k → 0 limit
4312  func->SetParameter(0, 10.0);
4313  // k* indicates the position of the turnover to a different power law behaviour
4314  func->SetParameter(1, 0.1);
4315  // alpha free parameter
4316  func->SetParameter(2, 2.0);
4317 
4318  // Set parameter limits for stability
4319  func->SetParLimits(0, 0.0, 100.0); // Amplitude should be non-negative
4320  func->SetParLimits(1, 0.001, 1.0); // k* should be within a reasonable range
4321  func->SetParLimits(2, 0.0, 5.0); // alpha should be positive
4322 
4323  plot->Fit("power_template","Rq");
4324 
4325  Posterior->SetLogx();
4326  Posterior->SetLogy();
4327  Posterior->SetGrid();
4328  plot->Draw("AL");
4329  func->Draw("SAME");
4330 
4331  //KS: I have no clue what is the reason behind this. Found this in Rick Calland code...
4332  (*PowerSpectrumStepSize)(j) = std::sqrt(func->GetParameter(0)/float(v_size*0.5));
4333  }
4334 
4335  PowerSpectrumStepSize->Write("PowerSpectrumStepSize");
4336  delete PowerSpectrumStepSize;
4337  PowerDir->Close();
4338  delete PowerDir;
4339 
4340  clock.Stop();
4341  MACH3LOG_INFO("Making Power Spectrum took {:.2f}s", clock.RealTime());
4342 
4343  Posterior->SetTopMargin(TopMargin);
4344  gStyle->SetOptTitle(OptTitle);
4345 }
4346 
4347 // **************************
4348 // Geweke Diagnostic based on
4349 // @cite Fang2014GewekeDiagnostics
4350 // @cite karlsbakk2011 Chapter 3.1
4352 // **************************
4353  MACH3LOG_INFO("Making Geweke Diagnostic");
4354  //KS: Up refers to upper limit we check, it stays constant, in literature it is mostly 50% thus using 0.5 for threshold
4355  std::vector<double> MeanUp(nDraw, 0.0);
4356  std::vector<double> SpectralVarianceUp(nDraw, 0.0);
4357  std::vector<int> DenomCounterUp(nDraw, 0);
4358  const double Threshold = 0.5 * nSteps;
4359 
4360  //KS: Select values between which you want to scan, for example 0 means 0% burn in and 1 100% burn in.
4361  constexpr double LowerThreshold = 0;
4362  constexpr double UpperThreshold = 1.0;
4363  // Tells how many intervals between thresholds we want to check
4364  constexpr int NChecks = 100;
4365  constexpr double Division = (UpperThreshold - LowerThreshold)/NChecks;
4366 
4367  std::vector<std::unique_ptr<TH1D>> GewekePlots(nDraw);
4368  for (int j = 0; j < nDraw; ++j)
4369  {
4370  TString Title = "";
4371  double Prior = 1.0, PriorError = 1.0;
4372  GetNthParameter(j, Prior, PriorError, Title);
4373  std::string HistName = Form("%s_%s_Geweke", Title.Data(), BranchNames[j].Data());
4374  GewekePlots[j] = std::make_unique<TH1D>(HistName.c_str(), HistName.c_str(), NChecks, 0.0, 100 * UpperThreshold);
4375  GewekePlots[j]->SetDirectory(nullptr);
4376  GewekePlots[j]->GetXaxis()->SetTitle("Burn-In (%)");
4377  GewekePlots[j]->GetYaxis()->SetTitle("Geweke T score");
4378  }
4379 
4380 //KS: Start parallel region
4381 #ifdef MULTITHREAD
4382 #pragma omp parallel
4383 {
4384 #endif
4385  //KS: First we calculate mean and spectral variance for the upper limit, this doesn't change and in literature is most often 50%
4386  #ifdef MULTITHREAD
4387  #pragma omp for
4388  #endif
4389  for (int j = 0; j < nDraw; ++j)
4390  {
4391  for(int i = 0; i < nEntries; ++i)
4392  {
4393  if(StepNumber[i] > Threshold)
4394  {
4395  MeanUp[j] += ParStep[j][i];
4396  DenomCounterUp[j]++;
4397  }
4398  }
4399  MeanUp[j] = MeanUp[j]/DenomCounterUp[j];
4400  }
4401 
4402  //KS: now Spectral variance which in this case is sample variance
4403  #ifdef MULTITHREAD
4404  #pragma omp for collapse(2)
4405  #endif
4406  for (int j = 0; j < nDraw; ++j)
4407  {
4408  for(int i = 0; i < nEntries; ++i)
4409  {
4410  if(StepNumber[i] > Threshold)
4411  {
4412  SpectralVarianceUp[j] += (ParStep[j][i] - MeanUp[j])*(ParStep[j][i] - MeanUp[j]);
4413  }
4414  }
4415  }
4416 
4417  //Loop over how many intervals we calculate
4418  #ifdef MULTITHREAD
4419  #pragma omp for
4420  #endif
4421  for (int k = 1; k < NChecks+1; ++k)
4422  {
4423  //KS each thread has it's own
4424  std::vector<double> MeanDown(nDraw, 0.0);
4425  std::vector<double> SpectralVarianceDown(nDraw, 0.0);
4426  std::vector<int> DenomCounterDown(nDraw, 0);
4427 
4428  const unsigned int ThresholsCheck = Division*k*nSteps;
4429  //KS: First mean
4430  for (int j = 0; j < nDraw; ++j)
4431  {
4432  for(int i = 0; i < nEntries; ++i)
4433  {
4434  if(StepNumber[i] < ThresholsCheck)
4435  {
4436  MeanDown[j] += ParStep[j][i];
4437  DenomCounterDown[j]++;
4438  }
4439  }
4440  MeanDown[j] = MeanDown[j]/DenomCounterDown[j];
4441  }
4442  //Now spectral variance
4443  for (int j = 0; j < nDraw; ++j)
4444  {
4445  for(int i = 0; i < nEntries; ++i)
4446  {
4447  if(StepNumber[i] < ThresholsCheck)
4448  {
4449  SpectralVarianceDown[j] += (ParStep[j][i] - MeanDown[j])*(ParStep[j][i] - MeanDown[j]);
4450  }
4451  }
4452  }
4453  //Lastly calc T score and fill histogram entry
4454  for (int j = 0; j < nDraw; ++j)
4455  {
4456  double T_score = std::fabs((MeanDown[j] - MeanUp[j])/std::sqrt(SpectralVarianceDown[j]/DenomCounterDown[j] + SpectralVarianceUp[j]/DenomCounterUp[j]));
4457  GewekePlots[j]->SetBinContent(k, T_score);
4458  }
4459  } //end loop over intervals
4460 #ifdef MULTITHREAD
4461 } //End parallel region
4462 #endif
4463 
4464  //Finally save it to TFile
4465  OutputFile->cd();
4466  TDirectory *GewekeDir = OutputFile->mkdir("Geweke");
4467  for (int j = 0; j < nDraw; ++j)
4468  {
4469  GewekeDir->cd();
4470  GewekePlots[j]->Write();
4471  }
4472  for (int i = 0; i < nDraw; ++i) {
4473  delete[] ParStep[i];
4474  }
4475  delete[] ParStep;
4476 
4477  GewekeDir->Close();
4478  delete GewekeDir;
4479  OutputFile->cd();
4480 }
4481 
4482 // **************************
4483 // Acceptance Probability
4485 // **************************
4486  if (AccProbBatchedAverages == nullptr) PrepareDiagMCMC();
4487 
4488  MACH3LOG_INFO("Making AccProb plots...");
4489 
4490  // Set the titles and limits for TH1Ds
4491  auto AcceptanceProbPlot = std::make_unique<TH1D>("AcceptanceProbability", "Acceptance Probability", nEntries, 0, nEntries);
4492  AcceptanceProbPlot->SetDirectory(nullptr);
4493  AcceptanceProbPlot->GetXaxis()->SetTitle("Step");
4494  AcceptanceProbPlot->GetYaxis()->SetTitle("Acceptance Probability");
4495 
4496  auto BatchedAcceptanceProblot = std::make_unique<TH1D>("AcceptanceProbability_Batch", "AcceptanceProbability_Batch", nBatches, 0, nBatches);
4497  BatchedAcceptanceProblot->SetDirectory(nullptr);
4498  BatchedAcceptanceProblot->GetYaxis()->SetTitle("Acceptance Probability");
4499 
4500  for (int i = 0; i < nBatches; ++i) {
4501  BatchedAcceptanceProblot->SetBinContent(i+1, AccProbBatchedAverages[i]);
4502  const int BatchRangeLow = double(i)*double(nEntries)/double(nBatches);
4503  const int BatchRangeHigh = double(i+1)*double(nEntries)/double(nBatches);
4504  std::stringstream ss;
4505  ss << BatchRangeLow << " - " << BatchRangeHigh;
4506  BatchedAcceptanceProblot->GetXaxis()->SetBinLabel(i+1, ss.str().c_str());
4507  }
4508 
4509  #ifdef MULTITHREAD
4510  #pragma omp parallel for
4511  #endif
4512  for (int i = 0; i < nEntries; ++i) {
4513  // Set bin content for the i-th bin to the parameter values
4514  AcceptanceProbPlot->SetBinContent(i, AccProbValues[i]);
4515  }
4516 
4517  TDirectory *probDir = OutputFile->mkdir("AccProb");
4518  probDir->cd();
4519 
4520  AcceptanceProbPlot->Write();
4521  BatchedAcceptanceProblot->Write();
4522  delete[] AccProbValues;
4523  delete[] AccProbBatchedAverages;
4524 
4525  probDir->Close();
4526  delete probDir;
4527 
4528  OutputFile->cd();
4529 }
4530 
4531 // **************************
4532 void MCMCProcessor::CheckCredibleIntervalsOrder(const std::vector<double>& CredibleIntervals, const std::vector<Color_t>& CredibleIntervalsColours) const {
4533 // **************************
4534  if (CredibleIntervals.size() != CredibleIntervalsColours.size()) {
4535  MACH3LOG_ERROR("size of CredibleIntervals is not equal to size of CredibleIntervalsColours");
4536  throw MaCh3Exception(__FILE__, __LINE__);
4537  }
4538  if (CredibleIntervals.size() > 1) {
4539  for (unsigned int i = 1; i < CredibleIntervals.size(); i++) {
4540  if (CredibleIntervals[i] > CredibleIntervals[i - 1]) {
4541  MACH3LOG_ERROR("Interval {} is smaller than {}", i, i - 1);
4542  MACH3LOG_ERROR("{:.2f} {:.2f}", CredibleIntervals[i], CredibleIntervals[i - 1]);
4543  MACH3LOG_ERROR("They should be grouped in decreasing order");
4544  throw MaCh3Exception(__FILE__, __LINE__);
4545  }
4546  }
4547  }
4548 }
4549 
4550 // **************************
4551 void MCMCProcessor::CheckCredibleRegionsOrder(const std::vector<double>& CredibleRegions,
4552  const std::vector<Style_t>& CredibleRegionStyle,
4553  const std::vector<Color_t>& CredibleRegionColor) {
4554 // **************************
4555  if ((CredibleRegions.size() != CredibleRegionStyle.size()) || (CredibleRegionStyle.size() != CredibleRegionColor.size())) {
4556  MACH3LOG_ERROR("size of CredibleRegions is not equal to size of CredibleRegionStyle or CredibleRegionColor");
4557  throw MaCh3Exception(__FILE__, __LINE__);
4558  }
4559  for (unsigned int i = 1; i < CredibleRegions.size(); i++) {
4560  if (CredibleRegions[i] > CredibleRegions[i - 1]) {
4561  MACH3LOG_ERROR("Interval {} is smaller than {}", i, i - 1);
4562  MACH3LOG_ERROR("{:.2f} {:.2f}", CredibleRegions[i], CredibleRegions[i - 1]);
4563  MACH3LOG_ERROR("They should be grouped in decreasing order");
4564  throw MaCh3Exception(__FILE__, __LINE__);
4565  }
4566  }
4567 }
4568 
4569 // **************************
4570 int MCMCProcessor::GetGroup(const std::string& name) const {
4571 // **************************
4572  // Lambda to compare strings case-insensitively
4573  auto caseInsensitiveCompare = [](const std::string& a, const std::string& b) {
4574  return std::equal(a.begin(), a.end(), b.begin(), b.end(),
4575  [](char c1, char c2) { return std::tolower(c1) == std::tolower(c2); });
4576  };
4577  int numerator = 0;
4578  for (const auto& groupName : ParameterGroup) {
4579  if (caseInsensitiveCompare(groupName, name)) {
4580  numerator++;
4581  }
4582  }
4583  return numerator;
4584 }
4585 
4586 // **************************
4588 // **************************
4589  // KS: Create a map to store the counts of unique strings
4590  std::unordered_map<std::string, int> paramCounts;
4591  std::vector<std::string> orderedKeys;
4592 
4593  for (const std::string& param : ParameterGroup) {
4594  if (paramCounts[param] == 0) {
4595  orderedKeys.push_back(param); // preserve order of first appearance
4596  }
4597  paramCounts[param]++;
4598  }
4599 
4600  MACH3LOG_INFO("************************************************");
4601  MACH3LOG_INFO("Scanning output branches...");
4602  MACH3LOG_INFO("# Useful entries in tree: \033[1;32m {} \033[0m ", nDraw);
4603  MACH3LOG_INFO("# Model params: \033[1;32m {} starting at {} \033[0m ", nParam[kXSecPar], ParamTypeStartPos[kXSecPar]);
4604  MACH3LOG_INFO("# With following groups: ");
4605  for (const std::string& key : orderedKeys) {
4606  MACH3LOG_INFO(" # {} params: {}", key, paramCounts[key]);
4607  }
4608  MACH3LOG_INFO("# ND params (legacy): \033[1;32m {} starting at {} \033[0m ", nParam[kNDPar], ParamTypeStartPos[kNDPar]);
4609  MACH3LOG_INFO("# FD params (legacy): \033[1;32m {} starting at {} \033[0m ", nParam[kFDDetPar], ParamTypeStartPos[kFDDetPar]);
4610  MACH3LOG_INFO("************************************************");
4611 }
4612 
4613 // **************************
4614 std::vector<double> MCMCProcessor::GetMargins(const std::unique_ptr<TCanvas>& Canv) const {
4615 // **************************
4616  return std::vector<double>{Canv->GetTopMargin(), Canv->GetBottomMargin(),
4617  Canv->GetLeftMargin(), Canv->GetRightMargin()};
4618 }
4619 
4620 // **************************
4621 void MCMCProcessor::SetMargins(std::unique_ptr<TCanvas>& Canv, const std::vector<double>& margins) {
4622 // **************************
4623  if (!Canv) {
4624  MACH3LOG_ERROR("Canv is nullptr");
4625  throw MaCh3Exception(__FILE__, __LINE__);
4626  }
4627  if (margins.size() != 4) {
4628  MACH3LOG_ERROR("Margin vector must have exactly 4 elements");
4629  throw MaCh3Exception(__FILE__, __LINE__);
4630  }
4631  Canv->SetTopMargin(margins[0]);
4632  Canv->SetBottomMargin(margins[1]);
4633  Canv->SetLeftMargin(margins[2]);
4634  Canv->SetRightMargin(margins[3]);
4635 }
4636 
4637 // **************************
4638 void MCMCProcessor::SetTLineStyle(TLine* Line, const Color_t Colour, const Width_t Width, const ELineStyle Style) const {
4639 // **************************
4640  Line->SetLineColor(Colour);
4641  Line->SetLineWidth(Width);
4642  Line->SetLineStyle(Style);
4643 }
4644 
4645 // **************************
4646 void MCMCProcessor::SetLegendStyle(TLegend* Legend, const double size) const {
4647 // **************************
4648  Legend->SetTextSize(size);
4649  Legend->SetLineColor(0);
4650  Legend->SetLineStyle(0);
4651  Legend->SetFillColor(0);
4652  Legend->SetFillStyle(0);
4653  Legend->SetBorderSize(0);
4654 }
4655 
4656 // **************************
4657 bool MCMCProcessor::GetParamFlat(const int iParam) const {
4658 // **************************
4659  ParameterEnum ParType = ParamType[iParam];
4660  int ParamTemp = iParam - ParamTypeStartPos[ParType];
4661  return ParamFlat[ParType][ParamTemp];
4662 }
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
int NDParametersStartingPos
int NDParameters
void RemoveFitter(TH1D *hist, const std::string &name)
KS: Remove fitted TF1 from hist to make comparison easier.
bool AllUnique(unsigned int *StepNumber, size_t size)
ParameterEnum
Definition: MCMCProcessor.h:45
@ kNDPar
Definition: MCMCProcessor.h:47
@ kXSecPar
Definition: MCMCProcessor.h:46
@ kNParameterEnum
Definition: MCMCProcessor.h:50
@ kFDDetPar
Definition: MCMCProcessor.h:48
#define MACH3LOG_DEBUG
Definition: MaCh3Logger.h:34
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
void SetMaCh3LoggerFormat()
Set messaging format of the logger.
Definition: MaCh3Logger.h:60
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
constexpr ELineStyle Style[NVars]
bool isFlat(TSpline3_red *&spl)
CW: Helper function used in the constructor, tests to see if the spline is flat.
double GetSubOptimality(const std::vector< double > &EigenValues, const int TotalTarameters)
Based on .
void GetGaussian(TH1D *&hist, TF1 *gauss, double &Mean, double &Error)
CW: Fit Gaussian to posterior.
void GetCredibleIntervalSig(const std::unique_ptr< TH1D > &hist, std::unique_ptr< TH1D > &hpost_copy, const bool CredibleInSigmas, const double coverage)
KS: Get 1D histogram within credible interval, hpost_copy has to have the same binning,...
void GetHPD(TH1D *const hist, double &Mean, double &Error, double &Error_p, double &Error_m, const double coverage)
Get Highest Posterior Density (HPD)
void GetCredibleRegionSig(std::unique_ptr< TH2D > &hist2D, const bool CredibleInSigmas, const double coverage)
KS: Set 2D contour within some coverage.
void GetArithmetic(TH1D *const hist, double &Mean, double &Error)
CW: Get Arithmetic mean from posterior.
std::string GetDunneKaboth(const double BayesFactor)
Convert a Bayes factor into an approximate particle-physics significance level using the Dunne–Kaboth...
_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ std::string GetJeffreysScale(const double BayesFactor)
KS: Following H. Jeffreys .
std::unique_ptr< TH1D > GetDeltaChi2(TH1D *posterior_probability_hist)
Convert a posterior probability histogram into a distribution. Using the likelihood-ratio definition...
TMacro YAMLtoTMacro(const YAML::Node &yaml_node, const std::string &name)
Convert a YAML node to a ROOT TMacro object.
Definition: YamlHelper.h:167
YAML::Node TMacroToYAML(const TMacro &macro)
KS: Convert a ROOT TMacro object to a YAML node.
Definition: YamlHelper.h:152
void ScanParameterOrder()
Scan order of params from a different groups.
int nBatches
Number of batches for Batched Mean.
void CheckStepCut() const
Check if step cut isn't larger than highest values of step in a chain.
void GewekeDiagnostic()
Geweke Diagnostic based on the methods described by Fang (2014) and Karlsbakk (2011)....
TMatrixDSym * Correlation
Posterior Correlation Matrix.
void MakeViolin()
Make and Draw Violin.
void GetNthParameter(const int param, double &Prior, double &PriorError, TString &Title) const
Get properties of parameter by passing it number.
void Reset2DPosteriors()
Reset 2D posteriors, in case we would like to calculate in again with different BurnInCut.
void PrintInfo() const
Print info like how many params have been loaded etc.
void MakeCredibleIntervals(const std::vector< double > &CredibleIntervals={0.99, 0.90, 0.68 }, const std::vector< Color_t > &CredibleIntervalsColours={kCyan+4, kCyan-2, kCyan-10}, const bool CredibleInSigmas=false)
Make and Draw Credible intervals.
void ReadModelFile()
Read the xsec file and get the input central values and errors.
std::vector< double > GetMargins(const std::unique_ptr< TCanvas > &Canv) const
Get TCanvas margins, to be able to reset them if particular function need different margins.
M3::float_t ** ParStep
Array holding values for all parameters.
std::unique_ptr< TF1 > Gauss
Gaussian fitter.
void Initialise()
Scan chain, what parameters we have and load information from covariance matrices.
MCMCProcessor(const std::string &InputFile)
Constructs an MCMCProcessor object with the specified input file and options.
double Post2DPlotThreshold
KS: Set Threshold when to plot 2D posterior as by default we get a LOT of plots.
void AcceptanceProbabilities()
Acceptance Probability.
std::vector< std::string > ReweightNames
Name of branch used for chain reweighting.
double ** SampleValues
Holds the sample values.
void BatchedAnalysis()
Get the batched means variance estimation and variable indicating if number of batches is sensible .
void AutoCorrelation()
KS: Calculate autocorrelations supports both OpenMP and CUDA :)
TVectorD * Means_HPD
Vector with mean values using Highest Posterior Density.
std::vector< TString > SampleName_v
Vector of each sample PDF object.
void GetBayesFactor(const std::vector< std::string > &ParName, const std::vector< std::vector< double >> &Model1Bounds, const std::vector< std::vector< double >> &Model2Bounds, const std::vector< std::vector< std::string >> &ModelNames)
Calculate Bayes factor for vector of params, and model boundaries.
double * WeightValue
Stores value of weight for each step.
std::vector< std::vector< double > > ParamCentral
Parameters central values which we are going to analyse.
std::vector< std::vector< double > > ParamErrors
Uncertainty on a single parameter.
void MakeTrianglePlot(const std::vector< std::string > &ParNames, const std::vector< double > &CredibleIntervals={0.99, 0.90, 0.68 }, const std::vector< Color_t > &CredibleIntervalsColours={kCyan+4, kCyan-2, kCyan-10}, const std::vector< double > &CredibleRegions={0.99, 0.90, 0.68}, const std::vector< Style_t > &CredibleRegionStyle={kDashed, kSolid, kDotted}, const std::vector< Color_t > &CredibleRegionColor={kGreen-3, kGreen-10, kGreen}, const bool CredibleInSigmas=false)
Make fancy triangle plot for selected parameters.
std::string OutputName
Name of output files.
std::unique_ptr< TH2D > hviolin_prior
Holds prior violin plot for all dials,.
bool GetParamFlat(const int iParam) const
Get whether param has flat prior or not.
std::vector< int > nParam
Number of parameters per type.
int GetGroup(const std::string &name) const
Number of params from a given group, for example flux.
void DrawPosterior(const int i, TDirectory *PostDir, TDirectory *PostHistDir)
Perform plot of 1d marginalised posterior with HPD etc.
std::vector< std::string > CovNamePos
Covariance matrix name position.
double DrawRange
Drawrange for SetMaximum.
std::vector< std::vector< bool > > ParamFlat
Whether Param has flat prior or not.
std::vector< YAML::Node > CovConfig
Covariance matrix config.
double ** SystValues
Holds the systs values.
virtual ~MCMCProcessor()
Destroys the MCMCProcessor object.
TVectorD * Errors
Vector with errors values using RMS.
double * AccProbBatchedAverages
Holds all accProb in batches.
void MakePostfit(const std::map< std::string, std::pair< double, double >> &Edges={})
Make 1D projection for each parameter and prepare structure.
bool useFFTAutoCorrelation
MJR: Use FFT-based autocorrelation algorithm (save time & resources)?
int GetParamIndexFromName(const std::string &Name) const
Get parameter number based on name.
void MakeCredibleRegions(const std::vector< double > &CredibleRegions={0.99, 0.90, 0.68}, const std::vector< Style_t > &CredibleRegionStyle={kDashed, kSolid, kDotted}, const std::vector< Color_t > &CredibleRegionColor={kGreen-3, kGreen-10, kGreen}, const bool CredibleInSigmas=false, const bool Draw2DPosterior=true, const bool DrawBestFit=true)
Make and Draw Credible Regions.
std::string StepCut
BurnIn Cuts.
void GetPostfit_Ind(TVectorD *&Central, TVectorD *&Errors, TVectorD *&Peaks, ParameterEnum kParam)
Or the individual post-fits.
void DrawCorrelations1D()
Draw 1D correlations which might be more helpful than looking at huge 2D Corr matrix.
void GetPostfit(TVectorD *&Central, TVectorD *&Errors, TVectorD *&Central_Gauss, TVectorD *&Errors_Gauss, TVectorD *&Peaks)
Get the post-fit results (arithmetic and Gaussian)
TVectorD * Means_Gauss
Vector with mean values using Gaussian fit.
unsigned int * StepNumber
Step number for step, important if chains were merged.
int AutoCorrLag
LagL used in AutoCorrelation.
std::unique_ptr< TCanvas > Posterior
Fancy canvas used for our beautiful plots.
TFile * OutputFile
The output file.
void ReadNDFile()
Read the ND cov file and get the input central values and errors.
void ProduceChi2(const std::string &GroupName) const
Convert posterior likelihood to Delta Chi2 used for comparison with frequentists fitter.
bool ApplySmoothing
Apply smoothing for 2D histos using root algorithm.
unsigned int UpperCut
KS: Used only for SubOptimality.
int nBins
Number of bins.
TChain * Chain
Main chain storing all steps etc.
std::string MCMCFile
Name of MCMC file.
bool ReweightPosterior
Whether to apply reweighting weight or not.
void SetLegendStyle(TLegend *Legend, const double size) const
Configures the style of a TLegend object.
std::vector< std::string > ExcludedNames
std::unique_ptr< TH1D > MakePrefit()
Prepare prefit histogram for parameter overlay plot.
std::vector< TH1D * > hpost
Holds 1D Posterior Distributions.
TVectorD * Errors_HPD_Negative
Vector with negative error (left hand side) values using Highest Posterior Density.
std::vector< std::vector< std::string > > CovPos
Covariance matrix file name position.
void DrawCorrelationsGroup(const std::unique_ptr< TH2D > &CorrMatrix) const
Produces correlation matrix but instead of giving name for each param it only give name for param gro...
void DiagMCMC()
KS: Perform MCMC diagnostic including Autocorrelation, Trace etc.
std::vector< std::string > ExcludedGroups
std::string Posterior1DCut
Cut used when making 1D Posterior distribution.
double * AccProbValues
Holds all accProb.
void FindInputFilesLegacy()
std::vector< double > GetParameterSums()
Computes the average of each parameter across all MCMC entries. Useful for autocorrelation.
void DrawCovariance()
Draw the post-fit covariances.
void SetMargins(std::unique_ptr< TCanvas > &Canv, const std::vector< double > &margins)
Set TCanvas margins to specified values.
void ParamTraces()
CW: Draw trace plots of the parameters i.e. parameter vs step.
void PrepareDiagMCMC()
CW: Prepare branches etc. for DiagMCMC.
std::unique_ptr< TH2D > hviolin
Holds violin plot for all dials.
int nDraw
Number of all parameters used in the analysis.
std::string OutputSuffix
Output file suffix useful when running over same file with different settings.
void SetupOutput()
Prepare all objects used for output.
void MakeOutputFile()
prepare output root file and canvas to which we will save EVERYTHING
bool plotBinValue
If true it will print value on each bin of covariance matrix.
TVectorD * Errors_Gauss
Vector with error values using Gaussian fit.
void CheckCredibleIntervalsOrder(const std::vector< double > &CredibleIntervals, const std::vector< Color_t > &CredibleIntervalsColours) const
Checks the order and size consistency of the CredibleIntervals and CredibleIntervalsColours vectors.
void CalculateESS(const int nLags, const std::vector< std::vector< double >> &LagL)
KS: calc Effective Sample Size.
std::vector< ParameterEnum > ParamType
Make an enum for which class this parameter belongs to so we don't have to keep string comparing.
std::vector< std::string > NDSamplesNames
virtual void LoadAdditionalInfo()
allow loading additional info for example used for oscillation parameters
TVectorD * Central_Value
Vector with central value for each parameter.
std::vector< std::string > ExcludedTypes
int nSteps
KS: For merged chains number of entries will be different from nSteps.
void CheckCredibleRegionsOrder(const std::vector< double > &CredibleRegions, const std::vector< Style_t > &CredibleRegionStyle, const std::vector< Color_t > &CredibleRegionColor)
Checks the order and size consistency of the CredibleRegions, CredibleRegionStyle,...
std::vector< int > NDSamplesBins
void MakeCovariance_MP(const bool Mute=false)
Calculate covariance by making 2D projection of each combination of parameters using multithreading.
double ** BatchedAverages
Values of batched average for every param and batch.
void DrawPostfit()
Draw the post-fit comparisons.
TString CanvasName
Name of canvas which help to save to the sample pdf.
std::vector< std::string > ParameterGroup
TVectorD * Errors_HPD
Vector with error values using Highest Posterior Density.
void AutoCorrelation_FFT()
MJR: Autocorrelation function using FFT algorithm for extra speed.
void SetTLineStyle(TLine *Line, const Color_t Colour, const Width_t Width, const ELineStyle Style) const
Configures a TLine object with the specified style parameters.
void ReadInputCovLegacy()
void GetPolarPlot(const std::vector< std::string > &ParNames)
Make funny polar plot.
std::vector< std::vector< TH2D * > > hpost2D
Holds 2D Posterior Distributions.
void ParameterEvolution(const std::vector< std::string > &Names, const std::vector< int > &NIntervals)
Make .gif of parameter evolution.
void GetCovariance(TMatrixDSym *&Cov, TMatrixDSym *&Corr)
Get the post-fit covariances and correlations.
TVectorD * Means
Vector with mean values using Arithmetic Mean.
std::vector< TString > SystName_v
Vector of each systematic.
void CacheSteps()
KS:By caching each step we use multithreading.
std::vector< TString > BranchNames
std::vector< bool > ParamVaried
Is the ith parameter varied.
bool PlotFlatPrior
Whether we plot flat prior or not, we usually provide error even for flat prior params.
bool FancyPlotNames
Whether we want fancy plot names or not.
void SetStepCut(const std::string &Cuts)
Set the step cutting by string.
bool printToPDF
Will plot all plot to PDF not only to root file.
void GetSavageDickey(const std::vector< std::string > &ParName, const std::vector< double > &EvaluationPoint, const std::vector< std::vector< double >> &Bounds)
Calculate Bayes factor for point like hypothesis using SavageDickey.
std::pair< double, double > GetHistRange(const int iParam) const
Get Min/Max ranges for single parameter.
std::vector< std::vector< TString > > ParamNames
Name of parameters which we are going to analyse.
bool doDiagMCMC
Doing MCMC Diagnostic.
void ReadFDFile()
Read the FD cov file and get the input central values and errors.
void FindInputFiles()
Read the output MCMC file and find what inputs were used.
bool CacheMCMC
MCMC Chain has been cached.
std::vector< int > ParamTypeStartPos
bool MadePostfit
Sanity check if Postfit is already done to not make several times.
void PowerSpectrumAnalysis()
RC: Perform spectral analysis of MCMC .
void ScanInput()
Scan Input etc.
void BatchedMeans()
CW: Batched means, literally read from an array and chuck into TH1D.
int nEntries
KS: For merged chains number of entries will be different from nSteps.
void SavageDickeyPlot(std::unique_ptr< TH1D > &PriorHist, std::unique_ptr< TH1D > &PosteriorHist, const std::string &Title, const double EvaluationPoint) const
Produce Savage Dickey plot.
int nBranches
Number of branches in a TTree.
TVectorD * Errors_HPD_Positive
Vector with positive error (right hand side) values using Highest Posterior Density.
TMatrixDSym * Covariance
Posterior Covariance Matrix.
void MakeSubOptimality(const int NIntervals=10)
Make and Draw SubOptimality .
void MakeCovarianceYAML(const std::string &OutputYAMLFile, const std::string &MeansMethod) const
Make YAML file from post-fit covariance.
bool plotRelativeToPrior
Whether we plot relative to prior or nominal, in most cases is prior.
void MakeCovariance()
Calculate covariance by making 2D projection of each combination of parameters.
unsigned int BurnInCut
Value of burn in cut.
void SmearChain(const std::vector< std::string > &Names, const std::vector< double > &Error, const bool &SaveBranch) const
Smear chain contours.
void ReadInputCov()
CW: Read the input Covariance matrix entries. Get stuff like parameter input errors,...
Custom exception class used throughout MaCh3.
void EstimateDataTransferRate(TChain *chain, const Long64_t entry)
KS: Check what CPU you are using.
Definition: Monitor.cpp:212
void PrintConfig(const YAML::Node &node)
KS: Print Yaml config using logger.
Definition: Monitor.cpp:311
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:229
void MaCh3Welcome()
KS: Prints welcome message with MaCh3 logo.
Definition: Monitor.cpp:13
std::unique_ptr< ObjectType > Clone(const ObjectType *obj, const std::string &name="")
KS: Creates a copy of a ROOT-like object and wraps it in a smart pointer.
constexpr static const double _BAD_DOUBLE_
Default value used for double initialisation.
Definition: Core.h:53
double float_t
Definition: Core.h:37
bool CaseInsentiveMatch(std::string Text, std::string Pattern)
Matches a string against a simple wildcard Pattern using regex. Is not case sensitive.
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.
void MakeCorrelationMatrix(YAML::Node &root, const std::vector< double > &Values, const std::vector< double > &Errors, const std::vector< std::vector< double >> &Correlation, const std::string &OutYAMLName, const std::vector< std::string > &FancyNames={})
KS: Replace correlation matrix and tune values in YAML covariance matrix.
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55
void AddPath(std::string &FilePath)
Prepends the MACH3 environment path to FilePath if it is not already present.
Definition: Monitor.cpp:382
TMacro * GetConfigMacroFromChain(TDirectory *CovarianceFolder)
KS: We store configuration macros inside the chain. In the past, multiple configs were stored,...
Structure to hold reweight configuration.