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