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