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