MaCh3  2.6.0
Reference Guide
Functions
PlotLLHMap.cpp File Reference

Processing n-dimensional LLHMap outputs generating 1D and 2D profiled likelihoods as defined in config. More...

#include "Manager/Manager.h"
#include <ROOT/RDataFrame.hxx>
Include dependency graph for PlotLLHMap.cpp:

Go to the source code of this file.

Functions

std::vector< std::string > GetParams (std::vector< std::string > &PoIs, ROOT::RDataFrame Map)
 
std::pair< bool, std::vector< double > > ExtractBinning (std::string param, YAML::Node Settings, ROOT::RDataFrame Map)
 
int main (int argc, char *argv[])
 

Detailed Description

Processing n-dimensional LLHMap outputs generating 1D and 2D profiled likelihoods as defined in config.

Author
Tomas Nosek

Definition in file PlotLLHMap.cpp.

Function Documentation

◆ ExtractBinning()

std::pair<bool,std::vector<double> > ExtractBinning ( std::string  param,
YAML::Node  Settings,
ROOT::RDataFrame  Map 
)

Definition at line 63 of file PlotLLHMap.cpp.

65 {
66  // Expected number of bins based on the config
67  unsigned int nExpBins = GetFromManager<unsigned int>(Settings["LLHScan"]["LLHScanPoints"], 20, __FILE__, __LINE__);
68  if(CheckNodeExists(Settings,"LLHScan","ScanPoints"))
69  nExpBins = GetFromManager<unsigned int>(Settings["LLHScan"]["ScanPoints"][param], nExpBins, __FILE__, __LINE__);
70 
71 
72  // Preparing a histogram.
73  auto values = Map.Take<double>(param.c_str());
74 
75  // remove duplicates
76  std::set<double> unique(values->begin(), values->end());
77 
78  MACH3LOG_INFO("There are {} values (bins) for parameter {} inside LLHMap.", unique.size(), param);
79 
80  if(nExpBins != static_cast<unsigned int>(unique.size())) {
81  MACH3LOG_WARN("The config expects different number of {} bins for parameter {} than {} values included in LLHMap!", nExpBins, param, unique.size());
82  nExpBins = static_cast<unsigned int>(unique.size());
83  }
84 
85  // Extract minimum and maximum
86  double minx = *unique.begin();
87  double maxx = *unique.rbegin();
88 
89  // What is the minimal difference between two unique values to construct a bin width
90  double minDiff = maxx - minx;
91 
92  for (auto it = std::next(unique.begin()); it != unique.end(); ++it) {
93  auto prev = std::prev(it);
94  minDiff = std::min(minDiff, *it - *prev);
95  }
96 
97  if (unique.size() > 1) {
98  unsigned int bins = static_cast<unsigned int>( 1 + std::abs(maxx-minx) / minDiff );
99  if (nExpBins != bins) {
100  MACH3LOG_WARN("The scan is not uniform! Instead, we will use bins based on unique scan values and minimal {:.3e} distance between them!", minDiff);
101  nExpBins = 0;
102  }
103  }
104 
105  double half_w = std::max(1e-12*minx, .5*minDiff);
106  minx = minx-half_w;
107  maxx = maxx+half_w;
108 
109  std::vector<double> binning = {minx, maxx};
110 
111 
112  if (nExpBins == 0) {
113  for (auto it = std::next(unique.begin()); std::next(it) != unique.end(); ++it)
114  {
115  // first, insert next edge
116  double next_edge = *it + half_w;
117  binning.insert(binning.end()-1, next_edge);
118 
119  // now check, if it aligns with the next unique value
120  auto next = std::next(it);
121  if ( std::abs((next_edge - *next + half_w) / (next_edge + *next - half_w )) < 1e-12 )
122  continue;
123  else
124  binning.insert(binning.end()-1, *next-half_w);
125  }
126  } else {
127  for (auto it = unique.begin(); std::next(it) != unique.end(); ++it)
128  binning.insert(binning.end()-1, *it + half_w);
129  }
130 
131  return std::make_pair(nExpBins>0, binning);
132 }
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
bool CheckNodeExists(const YAML::Node &node, Args... args)
KS: Wrapper function to call the recursive helper.
Definition: YamlHelper.h:60

◆ GetParams()

std::vector<std::string> GetParams ( std::vector< std::string > &  PoIs,
ROOT::RDataFrame  Map 
)

Definition at line 17 of file PlotLLHMap.cpp.

19 {
20  std::vector<std::string> PtPs;
21 
22  if(PoIs.empty())
23  MACH3LOG_INFO("No parameters requested, processing all parameters found in the LLHMap!");
24 
25  // First, filter out all non-parametric (LLH) columns of LLHMap
26  for(auto p : Map.GetColumnNames())
27  {
28  if(p.find("_LLH") != std::string::npos)
29  continue;
30 
31  PtPs.push_back(p);
32  }
33 
34  // Check if the parameters user wants to plot, actually live in the LLHMap
35  // Remove from the list if not
36  for(auto pit = PoIs.begin(); pit != PoIs.end();)
37  {
38  if(std::find(PtPs.begin(), PtPs.end(), *pit) != PtPs.end())
39  {
40  ++pit;
41  } else {
42  MACH3LOG_WARN("Parameter {} not found in the LLHMap!", *pit);
43  pit = PoIs.erase(pit);
44  }
45  }
46 
47  // Check what LLHMap parameters the user wants to plot
48  // Remove from the list if not
49  for(auto pit = PtPs.begin(); pit != PtPs.end();)
50  {
51  if(std::find(PoIs.begin(), PoIs.end(), *pit) != PoIs.end() || PoIs.empty())
52  ++pit;
53  else
54  pit = PtPs.erase(pit);
55  }
56 
57  // Return the vector of parameters we are about to plot
58  return PtPs;
59 }

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 135 of file PlotLLHMap.cpp.

135  {
136 // *******************
139 
140  if(argc < 3)
141  {
142  MACH3LOG_ERROR("No arguments! Usage: {} <config.yaml> <file1.root> <file2.root> ...", argv[0]);
143  throw MaCh3Exception(__FILE__ , __LINE__ );
144  }
145 
146  // Open the settings and output file
147  YAML::Node Settings = M3OpenConfig(std::string(argv[1]));
148  auto OutFileName = GetFromManager<std::string>(Settings["General"]["OutputFile"], "LLHMap.root");
149  auto Plot2D = GetFromManager<bool>(Settings["LLHScan"]["Plot2D"], false, __FILE__, __LINE__);
150 
151  auto OutFile = new TFile(OutFileName.c_str(),"UPDATE");
152  OutFile->cd();
153 
154  // Prepare dirs for profiled LLHs
155  TDirectory* DirProfile1D = OutFile->mkdir("Profiled1D_LLH", "profile1D", true);
156  TDirectory* DirProfile2D;
157  if (Plot2D) DirProfile2D = OutFile->mkdir("Profiled2D_LLH", "profile2D", true);
158 
159  TDirectory* DirMarginal1D = OutFile->mkdir("Marginal1D_L", "marginal1D", true);
160  TDirectory* DirMarginal2D;
161  if (Plot2D) DirMarginal2D = OutFile->mkdir("Marginal2D_L", "marginal2D", true);
162 
163 
164  // Get the list of input files
165  std::vector<std::string> inpFileList;
166  for(int i = 2; i < argc; ++i)
167  {
168  inpFileList.push_back(std::string(argv[i]));
169  MACH3LOG_INFO("Adding file(s): {}", inpFileList.back().c_str());
170  }
171 
172  // Read the llhmap trees from the input files
173  auto Map = ROOT::RDataFrame("llhmap", inpFileList);
174 
175  // Now prepare the L column
176  // TODO: Let the user choose whether they want the Total_LLH or something else... but why?
177  auto LLHMap = Map.Define("L", "exp(-0.5*Total_LLH)");
178 
179  // Process what parameters to plot
180  auto ParamsOfInterest = GetFromManager<std::vector<std::string>>(Settings["LLHScan"]["LLHParameters"],{});
181  std::vector<std::string> ParamsToProfile = GetParams(ParamsOfInterest, Map);
182 
183  MACH3LOG_INFO("... Starting generating 1D profiled and marginalized likelihoods ...");
184  MACH3LOG_WARN("!!! LLHMap numerical marginalization assumes uncorrelated priors !!!");
185 
186  std::map<std::string, std::unique_ptr<TH1D>> hProfiles1d;
187  std::map<std::string, std::unique_ptr<TH1D>> hMarginals1d;
188  for(auto p = ParamsToProfile.begin(); p != ParamsToProfile.end(); ++p)
189  {
190  // Find the binning for the histograms.
191  // ExtractBinning gives a pair of bool, bin_edges.
192  // The bool denotes whether binning is uniform or not.
193  std::pair<bool, std::vector<double>> binning = ExtractBinning(*p, Settings, Map);
194 
195  std::string hProfTitle = *p+" profiled -2LogL";
196  std::string hProfName = *p+"_LLHProf1D";
197  std::string hMargTitle = *p+" marginalized L";
198  std::string hMargName = *p+"_LMarg1D";
199 
200  // Prepare histograms and detach from ROOT
201  auto hprof1d = std::make_unique<TH1D>(hProfName.c_str(), hProfTitle.c_str(), int(binning.second.size()-1), binning.second.data());
202  hprof1d->SetDirectory(nullptr);
203 
204  auto hmarg1d = std::make_unique<TH1D>(hMargName.c_str(), hMargTitle.c_str(), int(binning.second.size()-1), binning.second.data());
205  hmarg1d->SetDirectory(nullptr);
206 
207  if (binning.first)
208  {
209  MACH3LOG_INFO("Initializing 1D profiled -2LogL and marginalized L histograms for {} of {} bins from {:.3e} to {:.3e} (bin center at {:.3e} and {:.3e})", *p, hprof1d->GetNbinsX(), hprof1d->GetXaxis()->GetXmin(), hprof1d->GetXaxis()->GetXmax(), hprof1d->GetBinCenter(1), hprof1d->GetBinCenter(hprof1d->GetNbinsX()));
210  } else {
211  std::string binnies;
212  // TN: Waiting for C++ 20 std::format() function
213  for (auto binnie : binning.second)
214  {
215  std::ostringstream os;
216  os << std::scientific << std::setprecision(4) << binnie;
217  binnies += " "+os.str();
218 
219  }
220 
221  MACH3LOG_INFO("Initializing 1D profiled -2LogL and marginalized L histograms of {} bins with edges{}.", binning.second.size(), binnies);
222  }
223 
224  hProfiles1d[*p] = std::move(hprof1d);
225  hMarginals1d[*p] = std::move(hmarg1d);
226  }
227 
228  for(auto p = ParamsToProfile.begin(); p != ParamsToProfile.end(); ++p)
229  {
230 
231  // Now loop over the bins. For each bin, find the minimal Total_LLH over the rest of the parameter space.
232  // TODO: Switch between different LLHs (sample, xsec, etc.)
233  const int count = hProfiles1d[*p]->GetNbinsX() > 5 ? int(double(hProfiles1d[*p]->GetNbinsX())/double(5)) : 1;
234 
235  // Profile over the rest of the parameters
236  MACH3LOG_INFO("Profiling 1D -2LogL and numerically profiling 1D L for parameter {}!", *p);
237  for(int bidx = 1; bidx < hProfiles1d[*p]->GetNbinsX() + 1; ++bidx)
238  {
239  if (bidx % count == 0)
240  M3::Utils::PrintProgressBar(bidx, hProfiles1d[*p]->GetNbinsX());
241 
242  auto b_lo = hProfiles1d[*p]->GetXaxis()->GetBinLowEdge(bidx);
243  auto b_hi = b_lo + hProfiles1d[*p]->GetXaxis()->GetBinWidth(bidx);
244 
245  double llhmin = LLHMap.Filter(*p+">"+std::to_string(b_lo)+"&&"+*p+"<"+std::to_string(b_hi)).Min("Total_LLH").GetValue();
246 
247  // Rather store a non-sensensical value if out of bounds
248  if(llhmin >= M3::_LARGE_LOGL_) llhmin = -12345;
249  hProfiles1d[*p]->SetBinContent(bidx, llhmin);
250 
251  auto L = LLHMap.Filter(*p+">"+std::to_string(b_lo)+"&&"+*p+"<"+std::to_string(b_hi)).Sum("L");
252 
253  hMarginals1d[*p]->SetBinContent(bidx, *L);
254  }
255 
256  // Save the 1D histograms
257  DirProfile1D->cd();
258  hProfiles1d[*p]->Write(hProfiles1d[*p]->GetName(), TObject::kOverwrite);
259 
260  DirMarginal1D->cd();
261  hMarginals1d[*p]->Scale(1./hMarginals1d[*p]->Integral());
262  hMarginals1d[*p]->Write(hMarginals1d[*p]->GetName(), TObject::kOverwrite);
263  }
264 
265 
266  if (Plot2D)
267  {
268  MACH3LOG_INFO("... Starting generating 2D profiled and marginalized likelihoods ...");
269  MACH3LOG_WARN("!!! THIS MIGHT TAKE QUITE A WHILE !!!");
270  MACH3LOG_WARN("!!! LLHMap numerical marginalization assumes uncorrelated priors !!!");
271 
272  std::vector<std::string> Keys2D;
273  std::vector<std::string> ParamsFiltered;
274  std::map<std::string, std::unique_ptr<TH2D>> hProfiles2d;
275  std::map<std::string, std::unique_ptr<TH2D>> hMarginals2d;
276 
277  for(auto p : ParamsToProfile)
278  {
279  auto h = DirProfile1D->Get<TH1D>((p+"_LLHProf1D").c_str());
280  if(h->GetNbinsX()<2)
281  {
282  MACH3LOG_WARN("There is less than 2 bins for {}, 2D is equivalent to 1D! Removing from 2D plots ...", p);
283  } else {
284  ParamsFiltered.push_back(p);
285  }
286  }
287 
288  // Similar as with 1D profiles, but looping over parameters twice
289  for(auto p1 = ParamsFiltered.begin(); p1 != ParamsFiltered.end(); ++p1)
290  {
291  for(auto p2 = std::next(p1); p2 != ParamsFiltered.end(); ++p2)
292  {
293  auto h1 = DirProfile1D->Get<TH1D>((*p1+"_LLHProf1D").c_str());
294  auto h2 = DirProfile1D->Get<TH1D>((*p2+"_LLHProf1D").c_str());
295 
296  std::string key = *p1+"_"+*p2;
297  Keys2D.push_back(key);
298 
299  MACH3LOG_INFO("Initializing 2D profiled -2LogL and marginalized L histograms for {} vs. {} based on previously generated 1D histograms.", *p1, *p2);
300 
301  std::string hProfTitle = *p1+" vs. "+*p2+" profiled -2LogL";
302  std::string hProfName = key+"_LLHProf2D";
303  auto hprof2d = std::make_unique<TH2D>(
304  hProfName.c_str(), hProfTitle.c_str(),
305  h1->GetXaxis()->GetNbins(), h1->GetXaxis()->GetXbins()->GetArray(),
306  h2->GetXaxis()->GetNbins(), h2->GetXaxis()->GetXbins()->GetArray()
307  );
308  hprof2d->SetDirectory(nullptr);
309 
310  hProfiles2d[key] = std::move(hprof2d);
311 
312  std::string hMargTitle = *p1+" vs. "+*p2+" marginalized L";
313  std::string hMargName = key+"_LMarg1D";
314  auto hmarg2d = std::make_unique<TH2D>(
315  hMargName.c_str(), hMargTitle.c_str(),
316  h1->GetXaxis()->GetNbins(), h1->GetXaxis()->GetXbins()->GetArray(),
317  h2->GetXaxis()->GetNbins(), h2->GetXaxis()->GetXbins()->GetArray()
318  );
319  hmarg2d->SetDirectory(nullptr);
320 
321  hMarginals2d[key] = std::move(hmarg2d);
322  }
323  }
324 
325  for(auto p1 = ParamsFiltered.begin(); p1 != ParamsFiltered.end(); ++p1)
326  {
327  for(auto p2 = std::next(p1); p2 != ParamsFiltered.end(); ++p2)
328  {
329  std::string key = *p1+"_"+*p2;
330 
331  MACH3LOG_INFO("Numerically profiling 2D -2LogL and marginalizing 2D L for parameters {} and {}!",*p1, *p2);
332  const Long64_t nBinsX = static_cast<Long64_t>(hProfiles2d[key]->GetNbinsX());
333  const Long64_t nBinsY = static_cast<Long64_t>(hProfiles2d[key]->GetNbinsY());
334 
335  const Long64_t TotalBins = nBinsX * nBinsY;
336  const int count = TotalBins > 5 ? int(double(TotalBins)/double(5)) : 1;
337 
338  for(int bidx = 1; bidx < nBinsX + 1; ++bidx)
339  {
340  for(int bidy = 1; bidy < nBinsY + 1; ++bidy)
341  {
342  if ( ((bidx-1)*hProfiles2d[key]->GetNbinsY() + bidy) % count == 0)
343  M3::Utils::PrintProgressBar((bidx-1)*hProfiles2d[key]->GetNbinsY() + bidy, TotalBins);
344 
345  auto bx_lo = hProfiles2d[key]->GetXaxis()->GetBinLowEdge(bidx);
346  auto bx_hi = bx_lo + hProfiles2d[key]->GetXaxis()->GetBinWidth(bidx);
347 
348  auto by_lo = hProfiles2d[key]->GetYaxis()->GetBinLowEdge(bidy);
349  auto by_hi = by_lo + hProfiles2d[key]->GetYaxis()->GetBinWidth(bidy);
350 
351  // TODO: Think how to do this smarter and faster
352  double llhmin = LLHMap.Filter(*p1+">"+std::to_string(bx_lo)+"&&"+*p1+"<"+std::to_string(bx_hi)+"&&"+*p2+">"+std::to_string(by_lo)+"&&"+*p2+"<"+std::to_string(by_hi)).Min("Total_LLH").GetValue();
353 
354  // Rather store a non-sensical value if out of bounds
355  if(llhmin >= M3::_LARGE_LOGL_) llhmin = -12345;
356  hProfiles2d[key]->SetBinContent(bidx, bidy, llhmin);
357 
358  auto L = LLHMap.Filter(*p1+">"+std::to_string(bx_lo)+"&&"+*p1+"<"+std::to_string(bx_hi)
359  +"&&"+*p2+">"+std::to_string(by_lo)+"&&"+*p2+"<"+std::to_string(by_hi)).Sum("L");
360 
361  hMarginals2d[key]->SetBinContent(bidx,bidy, *L);
362  } // end bins y loop
363  } // end bins x loop
364 
365  // Save the 2D histograms
366  DirProfile2D->cd();
367  hProfiles2d[key]->Write(hProfiles2d[key]->GetName(), TObject::kOverwrite);
368 
369  DirMarginal2D->cd();
370  hMarginals2d[key]->Scale(1./hMarginals2d[key]->Integral());
371  hMarginals2d[key]->Write(hMarginals2d[key]->GetName(), TObject::kOverwrite);
372  } // end p2 loop
373  } // end p1 loop
374 
375  } // end 2D plot
376 
377  OutFile->Close();
378 
379  return 0;
380 }
std::string OutFileName
std::vector< std::string > inpFileList
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
void SetMaCh3LoggerFormat()
Set messaging format of the logger.
Definition: MaCh3Logger.h:60
std::pair< bool, std::vector< double > > ExtractBinning(std::string param, YAML::Node Settings, ROOT::RDataFrame Map)
Definition: PlotLLHMap.cpp:63
std::vector< std::string > GetParams(std::vector< std::string > &PoIs, ROOT::RDataFrame Map)
Definition: PlotLLHMap.cpp:17
#define M3OpenConfig(filename)
Macro to simplify calling LoadYaml with file and line info.
Definition: YamlHelper.h:589
Custom exception class used throughout MaCh3.
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:229
void MaCh3Welcome()
KS: Prints welcome message with MaCh3 logo.
Definition: Monitor.cpp:13
constexpr static const double _LARGE_LOGL_
Large Likelihood is used it parameter go out of physical boundary, this indicates in MCMC that such s...
Definition: Core.h:80