MaCh3  2.8.0
Reference Guide
PlotMCMCDiag.cpp
Go to the documentation of this file.
1 // MaCh3 includes
4 
6 #include "THStack.h"
7 #include "TGraphAsymmErrors.h"
8 #include "TLegend.h"
9 #include <TSystem.h>
11 
20 
21 // Utilities
23 double GetMinimumInRange(TH1D *hist, const double minRange, const double maxRange)
24 {
25  double MinVale = 1234567890.;
26  TAxis *xaxis = hist->GetXaxis();
27  for (int x = 1; x <= hist->GetNbinsX(); x++)
28  {
29  if (xaxis->GetBinLowEdge(x) > minRange && xaxis->GetBinUpEdge(x) < maxRange)
30  {
31  if (MinVale > hist->GetBinContent(x))
32  MinVale = hist->GetBinContent(x);
33  }
34  }
35  return MinVale;
36 }
37 
39 bool IsHistogramAllOnes(TH1D *hist, double tolerance = 0.001, int max_failures = 100)
40 {
41  int failure_count = 0;
42 
43  for (int bin = 2; bin <= hist->GetNbinsX(); ++bin)
44  {
45  if (std::fabs(hist->GetBinContent(bin) - 1.0) > tolerance)
46  {
47  if (++failure_count > max_failures)
48  {
49  return false;
50  }
51  }
52  }
53  return true;
54 }
55 
57 void MakeDiagPlot(std::vector<TString> input_files,
58  std::vector<TString> hist_labels)
59 {
60  auto c1 = std::make_unique<TCanvas>("c1", " ", 0, 0, 800, 630);
61  gStyle->SetOptStat(0); // Set 0 to disable statistic box
62  // To avoid TCanvas::Print> messages
63  gErrorIgnoreLevel = kWarning;
64 
65  TKey *key = nullptr;
66  std::vector<TFile*> infiles;
67  bool add_legend = false;
68 
69  for (const auto& fname : input_files)
70  {
71  TFile* f = M3::Open(fname.Data(), "UPDATE", __FILE__, __LINE__);
72  MACH3LOG_INFO("Adding file {}", fname.Data());
73  infiles.push_back(f);
74  add_legend = (input_files.size() > 1);
75  }
76 
77  constexpr int NFiles_hard = 4;
78  constexpr Color_t Colour[NFiles_hard] = {kRed, kBlue, kGreen, kOrange};
79  constexpr Style_t Style[NFiles_hard] = {kSolid, kDashed, kDotted, kDashDotted};
80  if(input_files.size() > NFiles_hard){
81  MACH3LOG_ERROR("Passed {}, while I have nasty hardcoding for max {} filse", input_files.size(), NFiles_hard);
82  throw MaCh3Exception(__FILE__, __LINE__);
83  }
84  TIter next(infiles[0]->GetListOfKeys());
85  while ((key = static_cast<TKey *>(next())))
86  {
87  std::string dirname = std::string(key->GetName());
88  if (std::string(key->GetClassName()) != "TDirectoryFile")
89  continue;
90  // KS: Script will work with LogL and Batched_means, you can comment it if you are interested in it
91  if ((dirname == "LogL") || (dirname == "Batched_means") || (dirname == "PowerSpectrum"))
92  continue;
93  // KS: Trace wo longer chains is super big, the way to avoid is to plot as png but I don't like png,
94  // keep possibility to skip it
95  // if( (dirname == "Trace") ) continue;
96  infiles[0]->cd(dirname.c_str());
97  TIter nextsub(gDirectory->GetListOfKeys());
98  c1->Print(Form("%s_%s.pdf[", hist_labels[0].Data(), dirname.c_str()), "pdf");
99  TKey *subkey;
100  while ((subkey = static_cast<TKey *>(nextsub())))
101  {
102  auto leg = std::make_unique<TLegend>(0.7, 0.7, 0.9, 0.9);
103  leg->SetFillColorAlpha(kWhite, 0.7f);
104  std::string name = std::string(subkey->GetName());
105  name = dirname + "/" + name;
106  MACH3LOG_INFO("{}", name);
107  if (std::string(subkey->GetClassName()) != "TH1D") {
108  continue;
109  } else {
110  MACH3LOG_WARN("continuing along my way for {}", dirname);
111  }
112  if (name == "AccProb/AcceptanceProbability") continue;
113 
114  std::vector<std::unique_ptr<TH1D>> blarb(infiles.size());
115  MACH3LOG_INFO("Looking for {} from file {}", name.c_str(), input_files[0].Data());
116  blarb[0] = M3::Clone(static_cast<TH1D *>(infiles[0]->Get(name.c_str())));
117  // KS: Some fixe params can go crazy
118  if (std::isnan(blarb[0]->GetBinContent(1)))
119  continue;
120 
121  RemoveFitter(blarb[0].get(), "Fitter");
122  if (dirname == "AccProb")
123  blarb[0]->GetYaxis()->SetRangeUser(0, 1.0);
124 
125  blarb[0]->SetLineStyle(Style[0]);
126  blarb[0]->SetLineColor(Colour[0]);
127  blarb[0]->Draw();
128  leg->AddEntry(blarb[0].get(), hist_labels[0].Data(), "l");
129 
130  for (size_t i = 1; i < infiles.size(); ++i)
131  {
132  blarb[i] = M3::Clone(static_cast<TH1D *>(infiles[i]->Get(name.c_str())));
133 
134  blarb[i]->SetLineStyle(Style[i]);
135  blarb[i]->SetLineColor(Colour[i]);
136 
137  blarb[i]->Draw(i == 0 ? "" : "same");
138  leg->AddEntry(blarb[i].get(), hist_labels[i].Data(), "l");
139  }
140  if (add_legend)
141  {
142  leg->Draw();
143  }
144  c1->Print(Form("%s_%s.pdf", hist_labels[0].Data(), dirname.c_str()), "pdf");
145  }
146  gDirectory->cd("..");
147  c1->Print(Form("%s_%s.pdf]", hist_labels[0].Data(), dirname.c_str()), "pdf");
148  }
149 
150  for (size_t i = 0; i < infiles.size(); ++i)
151  {
152  infiles[i]->Close();
153  delete infiles[i];
154  }
155 }
156 
158 void PlotAutoCorr(const std::vector<TString>& fname)
159 {
160  std::vector<TFile*> infile(fname.size());
161  int Nfiles = 0;
162  for(size_t i = 0; i < fname.size(); i++)
163  {
164  infile[i] = M3::Open(fname[i].Data(), "open", __FILE__, __LINE__);
165  Nfiles++;
166  }
167 
168  auto c1 = std::make_unique<TCanvas>("c1", " ", 0, 0, 800, 630);
169  gStyle->SetOptStat(0); // Set 0 to disable statistic box
170  // To avoid TCanvas::Print> messages
171  gErrorIgnoreLevel = kWarning;
172 
173  c1->Print("Auto_Corr_PerFile.pdf[", "pdf");
174  for (int ik = 0; ik < Nfiles; ik++)
175  {
176  TIter next(infile[ik]->GetListOfKeys());
177 
178  TKey *key;
179  // KS: Keep track of histos to delete them
180  std::vector<std::unique_ptr<TH1D>> histos;
181  while ((key = static_cast<TKey *>(next())))
182  {
183  std::string dirname = std::string(key->GetName());
184 
185  // KS: Script We are only interested in auto corr
186  if ((dirname != "Auto_corr"))
187  continue;
188 
189  infile[ik]->cd(dirname.c_str());
190  TIter nextsub(gDirectory->GetListOfKeys());
191 
192  TKey *subkey;
193  bool FirstTime = true;
194  while ((subkey = static_cast<TKey *>(nextsub())))
195  {
196  std::string name = std::string(subkey->GetName());
197  name = dirname + "/" + name;
198 
199  if (std::string(subkey->GetClassName()) != "TH1D")
200  continue;
201  MACH3LOG_DEBUG("{}", name.c_str());
202  histos.push_back(M3::Clone(static_cast<TH1D *>(infile[ik]->Get(name.c_str()))));
203  // KS: Some fixe pramas can go crazy
204  if (std::isnan(histos.back()->GetBinContent(1)))
205  continue;
206  // KS: This is unfortunately hardcoded, need to find better way to write this
207  // histos.back()->GetListOfFunctions()->ls();
208  RemoveFitter(histos.back().get(), "Fitter");
209  // KS: Very arbitrary check if autocorrelation dropped below threshold in half of consider LagL
210  double UpperEdgeBeforeLast = histos.back()->GetXaxis()->GetBinUpEdge(histos.back()->GetNbinsX()/2);
211  double MinValue = GetMinimumInRange(histos.back().get(), 0, UpperEdgeBeforeLast);
212 
213  if (MinValue >= 0.80)
214  histos.back()->SetLineColor(kRed);
215  else if (MinValue >= 0.40 && MinValue < 0.80)
216  histos.back()->SetLineColor(kOrange);
217  else if (MinValue > 0.20 && MinValue < 0.40)
218  histos.back()->SetLineColor(kYellow);
219  else if (MinValue <= 0.20)
220  histos.back()->SetLineColor(kGreen);
221  histos.back()->GetXaxis()->UnZoom();
222 
223  if (FirstTime)
224  histos.back()->SetTitle(Form("Auto_Corr_%s.pdf", fname[ik].Data()));
225 
226  histos.back()->SetLineStyle(kDashed);
227 
228  if (FirstTime)
229  histos.back()->Draw();
230 
231  TString integral = Form("Integral: %.2f", histos.back()->Integral());
232 
233  if (!FirstTime)
234  histos.back()->Draw("same");
235  FirstTime = false;
236  }
237  gDirectory->cd("..");
238  }
239  c1->Print("Auto_Corr_PerFile.pdf", "pdf");
240  }
241  c1->Print("Auto_Corr_PerFile.pdf]", "pdf");
242 }
243 
244 // HW: Utilities for average Auto Correlation plots
245 
247 std::pair<std::unique_ptr<TGraph>, std::unique_ptr<TGraph>> CreateMinMaxBand(TH1D *hist, Color_t color)
248 {
249  int nBins = hist->GetNbinsX();
250  std::vector<double> x(nBins), ymin(nBins), ymax(nBins);
251 
252  for (int i = 0; i < nBins; ++i)
253  {
254  x[i] = hist->GetBinCenter(i + 1);
255  ymin[i] = hist->GetBinContent(i + 1);
256  ymax[i] = hist->GetBinContent(i + 1);
257  }
258 
259  auto minGraph = std::make_unique<TGraph>(nBins, x.data(), ymin.data());
260  auto maxGraph = std::make_unique<TGraph>(nBins, x.data(), ymax.data());
261 
262  minGraph->SetLineColor(color);
263  maxGraph->SetLineColor(color);
264  minGraph->SetFillColorAlpha(color, 0.3f);
265  maxGraph->SetFillColorAlpha(color, 0.3f);
266 
267  return {std::move(minGraph), std::move(maxGraph)};
268 }
269 
270 std::unique_ptr<TGraphAsymmErrors> CalculateMinMaxBand(const std::vector<std::unique_ptr<TH1D>> &histograms, Color_t color)
271 {
272  if (histograms.empty()) {
273  throw MaCh3Exception(__FILE__, __LINE__, "Empty histogram vector provided");
274  }
275 
276  int nBins = histograms[0]->GetNbinsX();
277  std::vector<double> x(nBins), ymin(nBins, 1e10), ymax(nBins, -1e10);
278 
279  for (int bin = 0; bin < nBins; bin++)
280  {
281  x[bin] = histograms[0]->GetBinCenter(bin + 1);
282 
283  for (const auto &hist : histograms)
284  {
285  double content = hist->GetBinContent(bin + 1);
286  ymin[bin] = std::min(ymin[bin], content);
287  ymax[bin] = std::max(ymax[bin], content);
288  }
289  }
290 
291  // Create a band using TGraphAsymmErrors for the shaded region
292  auto band = std::make_unique<TGraphAsymmErrors>(nBins);
293  for (int i = 0; i < nBins; ++i)
294  {
295  band->SetPoint(i, x[i], (ymax[i] + ymin[i]) / 2.0);
296  band->SetPointError(i, 0, 0, (ymax[i] - ymin[i]) / 2.0, (ymax[i] - ymin[i]) / 2.0);
297  }
298 
299  band->SetFillColorAlpha(color, 0.2f);
300  band->SetLineWidth(1);
301 
302  return band; // Return band and min graph (min graph can be used for legend)
303 }
304 
306 void ProcessAutoCorrelationDirectory(TDirectoryFile *autocor_dir,
307  std::unique_ptr<TH1D>& average_hist,
308  int &parameter_count,
309  std::vector<std::unique_ptr<TH1D>> &histograms)
310 {
311  TIter next(autocor_dir->GetListOfKeys());
312  TKey *key;
313 
314  while ((key = dynamic_cast<TKey *>(next())))
315  {
316  TH1D *current_hist = nullptr;
317  autocor_dir->GetObject(key->GetName(), current_hist);
318 
319  if (!current_hist ||
320  current_hist->GetMaximum() <= 0 ||
321  IsHistogramAllOnes(current_hist))
322  {
323  continue;
324  }
325 
326  histograms.push_back(M3::Clone(current_hist));
327  if (!average_hist) {
328  average_hist = M3::Clone(current_hist);
329  } else {
330  average_hist->Add(current_hist);
331  }
332  parameter_count++;
333  }
334 }
335 
336 void ProcessDiagnosticFile(const TString &file_path,
337  std::unique_ptr<TH1D>& average_hist,
338  int &parameter_count,
339  std::vector<std::unique_ptr<TH1D>> &histograms)
340 {
341  std::unique_ptr<TFile> input_file(TFile::Open(file_path));
342  if (!input_file || input_file->IsZombie())
343  {
344  throw MaCh3Exception(__FILE__, __LINE__, "Could not open file: " + std::string(file_path.Data()));
345  }
346 
347  TDirectoryFile *autocor_dir = nullptr;
348  input_file->GetObject("Auto_corr", autocor_dir);
349 
350  if (!autocor_dir)
351  {
352  throw MaCh3Exception(__FILE__, __LINE__,
353  "Auto_corr directory not found in file: " + std::string(file_path.Data()));
354  }
355 
356  ProcessAutoCorrelationDirectory(autocor_dir, average_hist, parameter_count, histograms);
357 }
358 
359 std::unique_ptr<TH1D> AutocorrProcessInputs(const TString &input_file, std::vector<std::unique_ptr<TH1D>> &histograms)
360 {
361  std::unique_ptr<TH1D> average_hist = nullptr;
362  int parameter_count = 0;
363 
364  try {
365  ProcessDiagnosticFile(input_file, average_hist, parameter_count, histograms);
366  } catch (const std::exception &e) {
367  MACH3LOG_ERROR("Error processing file {} : {}", input_file, e.what());
368  }
369 
370  if (average_hist && parameter_count > 0)
371  {
372  MACH3LOG_INFO("Processed {} parameters from {} files", parameter_count, input_file);
373  average_hist->Scale(1.0 / parameter_count);
374  }
375 
376  return average_hist;
377 }
378 
379 void CompareAverageAC(const std::vector<std::vector<std::unique_ptr<TH1D>>> &histograms,
380  const std::vector<std::unique_ptr<TH1D>> &averages,
381  const std::vector<TString> &hist_labels,
382  const TString &output_name,
383  bool draw_min_max = true,
384  bool draw_all = false,
385  bool draw_errors = true)
386 {
387  auto canvas = std::make_unique<TCanvas>("AverageAC", "Average Auto Correlation", 800, 600);
388  canvas->SetGrid();
389 
390  // Setup colour palette
391  constexpr Int_t nb = 255.0;
392  Double_t stops[8] = {0.0, 0.25, 0.5, 0.75};
393  Double_t red[8] = {0.83203125, 0.796875, 0.0, 0.9375};
394  Double_t green[8] = {0.3671875, 0.47265625, 0.4453125, 0.890625};
395  Double_t blue[8] = {0.0, 0.65234375, 0.6953125, 0.2578125};
396  TColor::CreateGradientColorTable(8, stops, red, green, blue, nb);
397 
398  auto leg = std::make_unique<TLegend>(0.5, 0.7, 0.9, 0.9);
399  leg->SetFillColorAlpha(kWhite, 0.7f);
400  if (draw_min_max)
401  {
402  for (size_t i = 0; i < histograms.size(); ++i)
403  {
404  auto colour = static_cast<Color_t>(TColor::GetColorPalette(static_cast<Int_t>(i * nb / histograms.size())));
405  auto band = CalculateMinMaxBand(histograms[i], colour);
406  band->SetLineWidth(0);
407  band->SetTitle("Average Auto Correlation");
408  band->GetXaxis()->SetTitle("lag");
409  band->GetYaxis()->SetTitle("Autocorrelation Function");
410  if (i == 0) {
411  band->Draw("A3");
412  } else {
413  band->Draw("3 SAME");
414  }
415  }
416  }
417  std::vector<std::unique_ptr<TH1D>> error_hist(averages.size());
418  for (size_t i = 0; i < averages.size(); ++i)
419  {
420  auto colour = static_cast<Color_t>(TColor::GetColorPalette(static_cast<Int_t>(i * nb / histograms.size())));
421 
422  if (draw_errors)
423  {
424  error_hist[i] = M3::Clone(averages[i].get());
425  error_hist[i]->SetFillColorAlpha(colour, 0.3f);
426  error_hist[i]->SetLineWidth(0);
427  error_hist[i]->SetTitle("Average Auto Correlation");
428  error_hist[i]->GetXaxis()->SetTitle("lag");
429  error_hist[i]->GetYaxis()->SetTitle("Autocorrelation Function");
430  error_hist[i]->Draw("E3 SAME");
431  }
432  if (draw_all)
433  {
434  for (const auto &hist : histograms[i])
435  {
436  hist->SetLineColorAlpha(colour, 0.05f);
437  hist->SetLineWidth(1);
438  hist->Draw("HIST SAME");
439  }
440  }
441  averages[i]->SetLineColor(colour);
442  averages[i]->SetLineWidth(2);
443  averages[i]->SetTitle("Average Auto Correlation");
444  averages[i]->GetXaxis()->SetTitle("lag");
445  averages[i]->GetYaxis()->SetTitle("Autocorrelation Function");
446  averages[i]->Draw("HIST SAME");
447 
448  TString int_str = TString::Format("Average integrated autocorrelation %.2f", averages[i]->Integral());
449  leg->AddEntry(averages[i].get(), hist_labels[i] + int_str, "l");
450  }
451 
452  if (averages.size() > 1)
453  {
454  leg->Draw();
455  }
456 
457  canvas->SaveAs(output_name + ".pdf");
458 }
459 
461 void PlotAverageACMult(std::vector<TString> input_files,
462  std::vector<TString> hist_labels,
463  const TString &output_name,
464  bool draw_min_max = true,
465  bool draw_all = false,
466  bool draw_errors = true)
467 {
468  // Process first folder
469  std::vector<std::vector<std::unique_ptr<TH1D>>> histograms;
470  std::vector<std::unique_ptr<TH1D>> averages;
471 
472  for (int i = 0; i < static_cast<int>(input_files.size()); i++)
473  {
474  TString folder = input_files[i];
475  MACH3LOG_INFO("Folder : {}", folder);
476 
477  if (folder.IsNull())
478  {
479  MACH3LOG_WARN("Skipping empty or dummy folder: {}", folder.Data());
480  continue;
481  }
482 
483  std::vector<std::unique_ptr<TH1D>> histograms_i;
484  averages.emplace_back((AutocorrProcessInputs(folder, histograms_i)));
485  histograms.push_back(std::move(histograms_i));
486  }
487 
488  CompareAverageAC(histograms, averages, hist_labels, output_name, draw_min_max, draw_all, draw_errors);
489 }
490 
491 int main(int argc, char *argv[])
492 {
494  // Require at least one file-title pair
495  if (argc < 3 || (argc - 1) % 2 != 0)
496  {
497  MACH3LOG_ERROR("Wrong number of arguments ({}) provided", argc);
498  MACH3LOG_ERROR("Usage: {} file1 title1 [file2 title2 ... fileN titleN]", argv[0]);
499  MACH3LOG_ERROR("Example: {} DiagMCMC_Output.root PlotName", argv[0]);
500  throw MaCh3Exception(__FILE__, __LINE__);
501  }
502  std::vector<TString> filename;
503  std::vector<TString> title;
504 
505  std::vector<TString> filenames;
506  std::vector<TString> titles;
507 
508  // Parse all pairs (filename, title)
509  for (int i = 1; i < argc; i += 2)
510  {
511  filenames.emplace_back(argv[i]);
512  titles.emplace_back(argv[i + 1]);
513  }
514 
515  // Log how many pairs we found
516  MACH3LOG_INFO("Processing {} file(s)", filenames.size());
517 
518  // Generate plots
519  MakeDiagPlot(filenames, titles);
520  PlotAutoCorr(filenames);
521  PlotAverageACMult(filenames, titles, Form("%s_Average_Auto_Corr", argv[2]), true);
522  return 0;
523 }
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
void RemoveFitter(TH1D *hist, const std::string &name)
KS: Remove fitted TF1 from hist to make comparison easier.
#define MACH3LOG_DEBUG
Definition: MaCh3Logger.h:34
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
void SetMaCh3LoggerFormat()
Set messaging format of the logger.
Definition: MaCh3Logger.h:60
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
std::unique_ptr< TH1D > AutocorrProcessInputs(const TString &input_file, std::vector< std::unique_ptr< TH1D >> &histograms)
int main(int argc, char *argv[])
void CompareAverageAC(const std::vector< std::vector< std::unique_ptr< TH1D >>> &histograms, const std::vector< std::unique_ptr< TH1D >> &averages, const std::vector< TString > &hist_labels, const TString &output_name, bool draw_min_max=true, bool draw_all=false, bool draw_errors=true)
bool IsHistogramAllOnes(TH1D *hist, double tolerance=0.001, int max_failures=100)
HW: Check if histogram is flat within a given tolerance.
double GetMinimumInRange(TH1D *hist, const double minRange, const double maxRange)
KS: function which looks for minimum in given range.
void PlotAverageACMult(std::vector< TString > input_files, std::vector< TString > hist_labels, const TString &output_name, bool draw_min_max=true, bool draw_all=false, bool draw_errors=true)
Calculate mean AC based on all parameters with error band. Great for comparing AC between different c...
std::pair< std::unique_ptr< TGraph >, std::unique_ptr< TGraph > > CreateMinMaxBand(TH1D *hist, Color_t color)
HW: Create a band of minimum and maximum values from a histogram.
void ProcessAutoCorrelationDirectory(TDirectoryFile *autocor_dir, std::unique_ptr< TH1D > &average_hist, int &parameter_count, std::vector< std::unique_ptr< TH1D >> &histograms)
Loop over directory get histograms into vector and add to averaged hist.
std::unique_ptr< TGraphAsymmErrors > CalculateMinMaxBand(const std::vector< std::unique_ptr< TH1D >> &histograms, Color_t color)
void PlotAutoCorr(const std::vector< TString > &fname)
Plot autocorrelation for all params into single plot with color codding helping whether on average it...
void ProcessDiagnosticFile(const TString &file_path, std::unique_ptr< TH1D > &average_hist, int &parameter_count, std::vector< std::unique_ptr< TH1D >> &histograms)
void MakeDiagPlot(std::vector< TString > input_files, std::vector< TString > hist_labels)
function which loops over Diag MCMC output and prints everything to PDF
constexpr ELineStyle Style[NVars]
Type Get(const YAML::Node &node, const std::string &File, const int Line)
Get content of config file.
Definition: YamlHelper.h:290
Custom exception class used throughout MaCh3.
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.
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.