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