MaCh3  2.4.2
Reference Guide
PredictiveThrower.cpp
Go to the documentation of this file.
1 #include "PredictiveThrower.h"
4 #include "TH3.h"
5 
6 // *************************
8 // *************************
9  AlgorithmName = "PredictiveThrower";
10  if(!CheckNodeExists(fitMan->raw(), "Predictive")) {
11  MACH3LOG_ERROR("Predictive is missing in your main yaml config");
12  throw MaCh3Exception(__FILE__ , __LINE__ );
13  }
14 
15  ModelSystematic = nullptr;
16  // Use the full likelihood for the Prior/Posterior predictive pvalue
17  FullLLH = GetFromManager<bool>(fitMan->raw()["Predictive"]["FullLLH"], false, __FILE__, __LINE__ );
18  NModelParams = 0;
19 
20  Is_PriorPredictive = Get<bool>(fitMan->raw()["Predictive"]["PriorPredictive"], __FILE__, __LINE__);
21  Ntoys = Get<int>(fitMan->raw()["Predictive"]["Ntoy"], __FILE__, __LINE__);
22 
23  ReweightWeight.resize(Ntoys);
24  PenaltyTerm.resize(Ntoys);
25 }
26 
27 // *************************
28 // Destructor:
30 // *************************
31 
32 }
33 
34 // *************************
36 // *************************
37  // WARNING This should be removed in the future
38  auto DoNotThrowLegacyCov = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["DoNotThrowLegacyCov"], {}, __FILE__, __LINE__);
40  for (size_t i = 0; i < DoNotThrowLegacyCov.size(); ++i) {
41  for (size_t s = 0; s < systematics.size(); ++s) {
42  if (systematics[s]->GetName() == DoNotThrowLegacyCov[i]) {
43  systematics[s]->SetParameters();
44  break;
45  }
46  }
47  }
48 
49  // Set groups to prefit values if they were set to not be varies
50  if(ModelSystematic && ParameterGroupsNotVaried.size() > 0) {
52  }
53 
55  if (ModelSystematic && !ParameterOnlyToVary.empty()) {
56  for (int i = 0; i < ModelSystematic->GetNumParams(); ++i) {
57  // KS: If parameter is in map then we are skipping this, otherwise for params that we don't want to vary we simply set it to prior
58  if (ParameterOnlyToVary.find(i) == ParameterOnlyToVary.end()) {
60  }
61  }
62  }
63 }
64 
65 // *************************
67 // *************************
69  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
70  {
71  auto* MaCh3Sample = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
72  if (!MaCh3Sample) {
73  MACH3LOG_ERROR("Sample {} do not inherit from SampleHandlerFD this is not implemented", samples[iPDF]->GetName());
74  throw MaCh3Exception(__FILE__, __LINE__);
75  }
76  TotalNumberOfSamples += samples[iPDF]->GetNsamples();
77  }
78 
84 
86 
87 
88  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
89  MC_Hist_Toy[sample].resize(Ntoys);
90  W2_Hist_Toy[sample].resize(Ntoys);
91  }
92  int counter = 0;
93  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++) {
94  for (int SampleIndex = 0; SampleIndex < samples[iPDF]->GetNsamples(); ++SampleIndex) {
95  SampleInfo[counter].Name = samples[iPDF]->GetSampleTitle(SampleIndex);
96  SampleInfo[counter].LocalId = SampleIndex;
97  SampleInfo[counter].SamHandler = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
98  SampleInfo[counter].Binning = SampleInfo[counter].SamHandler->GetBinningHandler();
99  SampleInfo[counter].Dimenstion = SampleInfo[counter].SamHandler->GetNDim(SampleIndex);
100  counter++;
101  }
102  }
103  SampleInfo[TotalNumberOfSamples].Name= "Total";
104 }
105 
106 // *************************
107 // Produce MaCh3 toys:
109 // *************************
110  int counter = 0;
111  for (size_t s = 0; s < systematics.size(); ++s) {
112  auto* MaCh3Params = dynamic_cast<ParameterHandlerGeneric*>(systematics[s]);
113  if(MaCh3Params) {
114  ModelSystematic = MaCh3Params;
115  counter++;
116  }
117  }
118 
120 
121  if(Is_PriorPredictive) {
122  MACH3LOG_INFO("You've chosen to run Prior Predictive Distribution");
123  } else {
124  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
125  //KS: We use MCMCProcessor to get names of covariances that were actually used to produce given chain
126  MCMCProcessor Processor(PosteriorFileName);
127  Processor.Initialise();
128 
129  // For throwing FD predictions from ND-only chain we have to allow having different yaml configs
130  auto AllowDifferentConfigs = GetFromManager<bool>(fitMan->raw()["Predictive"]["AllowDifferentConfigs"], false, __FILE__, __LINE__);
131 
133  YAML::Node ConfigInChain = Processor.GetCovConfig(kXSecPar);
134  if(ModelSystematic) {
135  YAML::Node ConfigNow = ModelSystematic->GetConfig();
136  if (!compareYAMLNodes(ConfigNow, ConfigInChain))
137  {
138  if(AllowDifferentConfigs){
139  MACH3LOG_WARN("Yaml configs used for your ParameterHandler for chain you want sample from ({}) and one currently initialised are different", PosteriorFileName);
140  } else {
141  MACH3LOG_ERROR("Yaml configs used for your ParameterHandler for chain you want sample from ({}) and one currently initialised are different", PosteriorFileName);
142  throw MaCh3Exception(__FILE__ , __LINE__ );
143  }
144  }
145  }
146  }
147  if(counter > 1) {
148  MACH3LOG_ERROR("Found {} ParmaterHandler inheriting from ParameterHandlerGeneric, I can accept at most 1", counter);
149  throw MaCh3Exception(__FILE__, __LINE__);
150  }
151 
152  for (size_t s = 0; s < systematics.size(); ++s) {
153  NModelParams += systematics[s]->GetNumParams();
154  }
155 
156  if (ModelSystematic) {
157  auto ThrowParamGroupOnly = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["ThrowParamGroupOnly"], {}, __FILE__, __LINE__);
158  auto UniqueParamGroup = ModelSystematic->GetUniqueParameterGroups();
159  auto ParameterOnlyToVaryString = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["ThrowSinlgeParams"], {}, __FILE__, __LINE__);
160 
161  if (!ThrowParamGroupOnly.empty() && !ParameterOnlyToVaryString.empty()) {
162  MACH3LOG_ERROR("Can't use ThrowParamGroupOnly and ThrowSinlgeParams at the same time");
163  throw MaCh3Exception(__FILE__, __LINE__);
164  }
165 
166  if (!ParameterOnlyToVaryString.empty()) {
167  MACH3LOG_INFO("I will throw only: {}", fmt::join(ParameterOnlyToVaryString, ", "));
168  std::vector<int> ParameterVary(ParameterOnlyToVaryString.size());
169 
170  for (size_t i = 0; i < ParameterOnlyToVaryString.size(); ++i) {
171  ParameterVary[i] = ModelSystematic->GetParIndex(ParameterOnlyToVaryString[i]);
172  if (ParameterVary[i] == M3::_BAD_INT_) {
173  MACH3LOG_ERROR("Can't proceed if param {} is missing", ParameterOnlyToVaryString[i]);
174  throw MaCh3Exception(__FILE__, __LINE__);
175  }
176  }
177  ParameterOnlyToVary = std::unordered_set<int>(ParameterVary.begin(), ParameterVary.end());
178  } else {
179  MACH3LOG_INFO("I have following parameter groups: {}", fmt::join(UniqueParamGroup, ", "));
180  if (ThrowParamGroupOnly.empty()) {
181  MACH3LOG_INFO("I will vary all");
182  } else {
183  std::unordered_set<std::string> throwOnlySet(ThrowParamGroupOnly.begin(), ThrowParamGroupOnly.end());
184  ParameterGroupsNotVaried.clear();
185 
186  for (const auto& group : UniqueParamGroup) {
187  if (throwOnlySet.find(group) == throwOnlySet.end()) {
188  ParameterGroupsNotVaried.push_back(group);
189  }
190  }
191 
192  MACH3LOG_INFO("I will vary: {}", fmt::join(ThrowParamGroupOnly, ", "));
193  MACH3LOG_INFO("Exclude: {}", fmt::join(ParameterGroupsNotVaried, ", "));
194  }
195  }
196  }
197 }
198 
199 // *************************
200 // Try loading toys
202 // *************************
203  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
204  // Open the ROOT file
205  int originalErrorWarning = gErrorIgnoreLevel;
206  gErrorIgnoreLevel = kFatal;
207  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
208 
209  gErrorIgnoreLevel = originalErrorWarning;
210  TDirectory* ToyDir = nullptr;
211  if (!file || file->IsZombie()) {
212  return false;
213  } else {
214  // Check for the "toys" directory
215  if ((ToyDir = file->GetDirectory("Toys"))) {
216  MACH3LOG_INFO("Found toys in Posterior file will attempt toy reading");
217  } else {
218  file->Close();
219  delete file;
220  return false;
221  }
222  }
223 
224  // Finally get the TTree branch with the penalty vectors for each of the toy throws
225  TTree* PenaltyTree = static_cast<TTree*>(file->Get("ToySummary"));
226  if (!PenaltyTree) {
227  MACH3LOG_WARN("ToySummary TTree not found in file.");
228  file->Close();
229  delete file;
230  return false;
231  }
232 
233  Ntoys = static_cast<int>(PenaltyTree->GetEntries());
234  int ConfigNtoys = Get<int>(fitMan->raw()["Predictive"]["Ntoy"], __FILE__, __LINE__);;
235  if (Ntoys != ConfigNtoys) {
236  MACH3LOG_WARN("Found different number of toys in saved file than asked to run!");
237  MACH3LOG_INFO("Will read _ALL_ toys in the file");
238  MACH3LOG_INFO("Ntoys in file: {}", Ntoys);
239  MACH3LOG_INFO("Ntoys specified: {}", ConfigNtoys);
240  }
241 
242  PenaltyTerm.resize(Ntoys);
243  ReweightWeight.resize(Ntoys);
244 
245  double Penalty = 0, Weight = 1;
246  PenaltyTree->SetBranchAddress("Penalty", &Penalty);
247  PenaltyTree->SetBranchAddress("Weight", &Weight);
248  PenaltyTree->SetBranchAddress("NModelParams", &NModelParams);
249 
250  for (int i = 0; i < Ntoys; ++i) {
251  PenaltyTree->GetEntry(i);
252  if (FullLLH) {
253  PenaltyTerm[i] = Penalty;
254  } else {
255  PenaltyTerm[i] = 0.0;
256  }
257 
258  ReweightWeight[i] = Weight;
259  }
260  // Resize all vectors and get sample names
262 
263  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
264  TH1* DataHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_data").c_str()));
265  Data_Hist[sample] = M3::Clone(DataHist1D);
266 
267  TH1* MCHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_mc").c_str()));
268  MC_Nom_Hist[sample] = M3::Clone(MCHist1D);
269 
270  TH1* W2Hist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_w2").c_str()));
271  W2_Nom_Hist[sample] = M3::Clone(W2Hist1D);
272  }
273 
274 
275  for (int iToy = 0; iToy < Ntoys; ++iToy)
276  {
277  if (iToy % 100 == 0) MACH3LOG_INFO(" Loaded toy {}", iToy);
278 
279  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
280  TH1* MCHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_mc_" + std::to_string(iToy)).c_str()));
281  TH1* W2Hist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_w2_" + std::to_string(iToy)).c_str()));
282 
283  MC_Hist_Toy[sample][iToy] = M3::Clone(MCHist1D);
284  W2_Hist_Toy[sample][iToy] = M3::Clone(W2Hist1D);
285  }
286  }
287 
288  file->Close();
289  delete file;
290  return true;
291 }
292 
293 // *************************
294 std::vector<std::string> PredictiveThrower::GetStoredFancyName(ParameterHandlerBase* Systematics) const {
295 // *************************
296  TDirectory * ogdir = gDirectory;
297 
298  std::vector<std::string> FancyNames;
299  std::string Name = std::string("Config_") + Systematics->GetName();
300  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
301 
302  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
303  TDirectory* CovarianceFolder = file->GetDirectory("CovarianceFolder");
304 
305  TMacro* FoundMacro = static_cast<TMacro*>(CovarianceFolder->Get(Name.c_str()));
306  if(FoundMacro == nullptr) {
307  file->Close();
308  delete file;
309  if(ogdir){ ogdir->cd(); }
310 
311  return FancyNames;
312  }
313  MACH3LOG_DEBUG("Found config for {}", Name);
314  YAML::Node Settings = TMacroToYAML(*FoundMacro);
315 
316  int params = int(Settings["Systematics"].size());
317  FancyNames.resize(params);
318  int iPar = 0;
319  for (auto const &param : Settings["Systematics"]) {
320  FancyNames[iPar] = Get<std::string>(param["Systematic"]["Names"]["FancyName"], __FILE__ , __LINE__);
321  iPar++;
322  }
323  file->Close();
324  delete file;
325  if(ogdir){ ogdir->cd(); }
326  return FancyNames;
327 }
328 
329 // *************************
330 // Produce MaCh3 toys:
332 // *************************
334  if(LoadToys()) return;
335 
338 
339  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
340 
341  MACH3LOG_INFO("Starting {}", __func__);
342 
343  outputFile->cd();
344  double Penalty = 0, Weight = 1;
345  int Draw = 0;
346 
347  TTree *ToyTree = new TTree("ToySummary", "ToySummary");
348  ToyTree->Branch("Penalty", &Penalty, "Penalty/D");
349  ToyTree->Branch("Weight", &Weight, "Weight/D");
350  ToyTree->Branch("Draw", &Draw, "Draw/I");
351  ToyTree->Branch("NModelParams", &NModelParams, "NModelParams/I");
352 
353  // KS: define branches so we can keep track of what params we are throwing
354  std::vector<double> ParamValues(NModelParams);
355  std::vector<const double*> ParampPointers(NModelParams);
356  int ParamCounter = 0;
357  for (size_t iSys = 0; iSys < systematics.size(); iSys++)
358  {
359  for (int iPar = 0; iPar < systematics[iSys]->GetNumParams(); iPar++)
360  {
361  ParampPointers[ParamCounter] = systematics[iSys]->RetPointer(iPar);
362  std::string Name = systematics[iSys]->GetParFancyName(iPar);
363  //CW: Also strip out - signs because it messes up TBranches
364  while (Name.find("-") != std::string::npos) {
365  Name.replace(Name.find("-"), 1, std::string("_"));
366  }
367  ToyTree->Branch(Name.c_str(), &ParamValues[ParamCounter], (Name + "/D").c_str());
368  ParamCounter++;
369  }
370  }
371  TDirectory* ToyDirectory = outputFile->mkdir("Toys");
372  ToyDirectory->cd();
373  int SampleCounter = 0;
374  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
375  {
376  auto* MaCh3Sample = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
377  for (int SampleIndex = 0; SampleIndex < MaCh3Sample->GetNsamples(); ++SampleIndex)
378  {
379  // Get nominal spectra and event rates
380  TH1* DataHist = MaCh3Sample->GetDataHist(SampleIndex);
381  Data_Hist[SampleCounter] = M3::Clone(DataHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_data");
382  Data_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_data").c_str());
383 
384  TH1* MCHist = MaCh3Sample->GetMCHist(SampleIndex);
385  MC_Nom_Hist[SampleCounter] = M3::Clone(MCHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc");
386  MC_Nom_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc").c_str());
387 
388  TH1* W2Hist = MaCh3Sample->GetW2Hist(SampleIndex);
389  W2_Nom_Hist[SampleCounter] = M3::Clone(W2Hist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2");
390  W2_Nom_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2").c_str());
391  SampleCounter++;
392  }
393  }
394 
396  std::vector<std::vector<double>> branch_vals(systematics.size());
397  std::vector<std::vector<std::string>> branch_name(systematics.size());
398 
399  TChain* PosteriorFile = nullptr;
400  unsigned int burn_in = 0;
401  unsigned int maxNsteps = 0;
402  unsigned int Step = 0;
403  if(!Is_PriorPredictive)
404  {
405  PosteriorFile = new TChain("posteriors");
406  PosteriorFile->Add(PosteriorFileName.c_str());
407 
408  PosteriorFile->SetBranchAddress("step", &Step);
409  if (PosteriorFile->GetBranch("Weight")) {
410  PosteriorFile->SetBranchStatus("Weight", true);
411  PosteriorFile->SetBranchAddress("Weight", &Weight);
412  } else {
413  MACH3LOG_WARN("Not applying reweighting weight");
414  Weight = 1.0;
415  }
416 
417  for (size_t s = 0; s < systematics.size(); ++s) {
418  auto fancy_names = GetStoredFancyName(systematics[s]);
419  systematics[s]->MatchMaCh3OutputBranches(PosteriorFile, branch_vals[s], branch_name[s], fancy_names);
420  }
421 
422  //Get the burn-in from the config
423  burn_in = Get<unsigned int>(fitMan->raw()["Predictive"]["BurnInSteps"], __FILE__, __LINE__);
424 
425  //DL: Adding sanity check for chains shorter than burn in
426  maxNsteps = static_cast<unsigned int>(PosteriorFile->GetMaximum("step"));
427  if(burn_in >= maxNsteps)
428  {
429  MACH3LOG_ERROR("You are running on a chain shorter than burn in cut");
430  MACH3LOG_ERROR("Maximal value of nSteps: {}, burn in cut {}", maxNsteps, burn_in);
431  MACH3LOG_ERROR("You will run into infinite loop");
432  MACH3LOG_ERROR("You can make new chain or modify burn in cut");
433  throw MaCh3Exception(__FILE__,__LINE__);
434  }
435  }
436 
437  TStopwatch TempClock;
438  TempClock.Start();
439  for(int i = 0; i < Ntoys; i++)
440  {
441  if(Ntoys >= 10 && i % (Ntoys/10) == 0) {
443  }
444 
445  if(!Is_PriorPredictive){
446  int entry = 0;
447  Step = 0;
448 
449  //YSP: Ensures you get an entry from the mcmc even when burn_in is set to zero (Although not advised :p ).
450  //Take 200k burn in steps, WP: Eb C in 1st peaky
451  // If we have combined chains by hadd need to check the step in the chain
452  // Note, entry is not necessarily same as step due to merged ROOT files, so can't choose entry in the range BurnIn - nEntries :(
453  while(Step < burn_in){
454  entry = random->Integer(static_cast<unsigned int>(PosteriorFile->GetEntries()));
455  PosteriorFile->GetEntry(entry);
456  }
457  Draw = entry;
458  }
459  for (size_t s = 0; s < systematics.size(); ++s)
460  {
461  //KS: Below line can help you get prior predictive distributions which are helpful for getting pre and post ND fit spectra
462  //YSP: If not set in the config, the code runs SK Posterior Predictive distributions by default. If true, then the code runs SK prior predictive.
463  if(Is_PriorPredictive) {
464  systematics[s]->ThrowParameters();
465  } else {
466  systematics[s]->SetParameters(branch_vals[s]);
467  }
468  }
469 
470  // This set some params to prior value this way you can evaluate errors from subset of errors
471  SetParamters();
472 
473  Penalty = 0;
474  if(FullLLH) {
475  for (size_t s = 0; s < systematics.size(); ++s) {
476  //KS: do times 2 because banff reports chi2
477  Penalty = 2.0 * systematics[s]->GetLikelihood();
478  }
479  }
480 
481  PenaltyTerm[i] = Penalty;
482  ReweightWeight[i] = Weight;
483 
484  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++) {
485  samples[iPDF]->Reweight();
486  }
487 
488  SampleCounter = 0;
489  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
490  {
491  auto* MaCh3Sample = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
492  for (int SampleIndex = 0; SampleIndex < MaCh3Sample->GetNsamples(); ++SampleIndex)
493  {
494  TH1* MCHist = MaCh3Sample->GetMCHist(SampleIndex);
495  MC_Hist_Toy[SampleCounter][i] = M3::Clone(MCHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc_" + std::to_string(i));
496  MC_Hist_Toy[SampleCounter][i]->Write();
497 
498  TH1* W2Hist = MaCh3Sample->GetW2Hist(SampleIndex);
499  W2_Hist_Toy[SampleCounter][i] = M3::Clone(W2Hist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2_" + std::to_string(i));
500  W2_Hist_Toy[SampleCounter][i]->Write();
501  SampleCounter++;
502  }
503  }
504 
505  // Fill parameter value so we know throw values
506  for (size_t iPar = 0; iPar < ParamValues.size(); iPar++) {
507  ParamValues[iPar] = *ParampPointers[iPar];
508  }
509 
510  ToyTree->Fill();
511  }//end of toys loop
512  TempClock.Stop();
513 
514  if(PosteriorFile) delete PosteriorFile;
515  ToyDirectory->Close();
516  delete ToyDirectory;
517 
518  outputFile->cd();
519  ToyTree->Write();
520  delete ToyTree;
521 
522  MACH3LOG_INFO("{} took {:.2f}s to finish for {} toys", __func__, TempClock.RealTime(), Ntoys);
523 }
524 
525 // *************************
526 std::vector<std::vector<std::unique_ptr<TH2D>>> PredictiveThrower::ProduceSpectra(
527  const std::vector<std::vector<std::unique_ptr<TH1>>>& Toys,
528  const std::vector<TDirectory*>& SampleDirectories,
529  const std::string suffix) {
530 // *************************
531  std::vector<std::vector<double>> MaxValue(TotalNumberOfSamples);
532  //Projections[sample][toy][dim]
533  std::vector<std::vector<std::vector<std::unique_ptr<TH1>>>> Projections(TotalNumberOfSamples);
534 
535  // 1. Create 1D projections, this is not thread safe, also we will use it a lot later on
536  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
537  SampleDirectories[sample]->cd();
538 
539  const int nDims = SampleInfo[sample].Dimenstion;
540  MaxValue[sample].assign(nDims, 0);
541  Projections[sample].resize(Ntoys);
542  for (int toy = 0; toy < Ntoys; ++toy) {
543  if (nDims == 1) {
544  // For 1D histograms, no projection needed.
545  // Leave Projections[sample][toy][0] == nullptr
546  } else if (nDims == 2) {
547  Projections[sample][toy].resize(nDims);
548  TH2* h2 = dynamic_cast<TH2*>(Toys[sample][toy].get());
549  if (!h2) {
550  MACH3LOG_ERROR("Histogram is not TH2 for nDims==2");
551  throw MaCh3Exception(__FILE__, __LINE__);
552  }
553  auto px = h2->ProjectionX();
554  px->SetDirectory(nullptr);
555  Projections[sample][toy][0] = M3::Clone(px);
556 
557  auto py = h2->ProjectionY();
558  py->SetDirectory(nullptr);
559  Projections[sample][toy][1] = M3::Clone(py);
560 
561  delete px;
562  delete py;
563  } else {
564  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
565  throw MaCh3Exception(__FILE__, __LINE__);
566  }
567  }
568  }
569 
570  // 2. Find maximum entries over all toys
571  #ifdef MULTITHREAD
572  #pragma omp parallel for collapse(2)
573  #endif
574  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
575  for (int toy = 0; toy < Ntoys; ++toy) {
576  const int nDims = Toys[sample][0]->GetDimension();
577  for (int dim = 0; dim < nDims; dim++) {
578  double max_val = 0;
579  if (nDims == 1) {
580  max_val = Toys[sample][toy]->GetMaximum();
581  }
582  else if (nDims == 2) {
583  if (dim == 0) {
584  max_val = Projections[sample][toy][0]->GetMaximum();
585  } else {
586  max_val = Projections[sample][toy][1]->GetMaximum();
587  }
588  } else {
589  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
590  throw MaCh3Exception(__FILE__, __LINE__);
591  }
592  MaxValue[sample][dim] = std::max(MaxValue[sample][dim], max_val);
593  }
594  }
595  }
596 
597  // 3. Make actual spectra histogram (this is because making ROOT histograms is not save)
598  // And we now have actual max values
599  std::vector<std::vector<std::unique_ptr<TH2D>>> Spectra(TotalNumberOfSamples);
600  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
601  const int nDims = SampleInfo[sample].Dimenstion;
602  Spectra[sample].resize(nDims);
603  for (int dim = 0; dim < nDims; dim++) {
604  // Get MC histogram x-axis binning
605  TH1D* refHist = nullptr;
606  if (nDims == 1) {
607  refHist = static_cast<TH1D*>(Toys[sample][0].get());
608  }
609  else if (nDims == 2) {
610  if (dim == 0) {
611  refHist = static_cast<TH1D*>(Projections[sample][0][0].get());
612  } else {
613  refHist = static_cast<TH1D*>(Projections[sample][0][1].get());
614  }
615  }
616  else {
617  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
618  throw MaCh3Exception(__FILE__, __LINE__);
619  }
620 
621  if (!refHist) {
622  MACH3LOG_ERROR("Failed to cast to {} dimensions in {}!", nDims, __func__);
623  throw MaCh3Exception(__FILE__, __LINE__);
624  }
625 
626  const int n_bins_x = refHist->GetNbinsX();
627  std::vector<double> x_bin_edges(n_bins_x + 1);
628  for (int b = 0; b <= n_bins_x; ++b) {
629  x_bin_edges[b] = refHist->GetXaxis()->GetBinLowEdge(b + 1); // ROOT bins start at 1
630  }
631  // Last edge is upper edge of last bin:
632  x_bin_edges[n_bins_x] = refHist->GetXaxis()->GetBinUpEdge(n_bins_x);
633 
634  constexpr int n_bins_y = 400;
635  constexpr double y_min = 0.0;
636  const double y_max = MaxValue[sample][dim] * 1.05;
637 
638  // Create TH2D with variable binning on x axis
639  Spectra[sample][dim] = std::make_unique<TH2D>(
640  (SampleInfo[sample].Name + "_" + suffix + "_dim" + std::to_string(dim)).c_str(), // name
641  (SampleInfo[sample].Name + "_" + suffix + "_dim" + std::to_string(dim)).c_str(), // title
642  n_bins_x, x_bin_edges.data(), // x axis bins
643  n_bins_y, y_min, y_max // y axis bins
644  );
645 
646  Spectra[sample][dim]->GetXaxis()->SetTitle(refHist->GetXaxis()->GetTitle());
647  Spectra[sample][dim]->GetYaxis()->SetTitle("Events");
648 
649  Spectra[sample][dim]->SetDirectory(nullptr);
650  Spectra[sample][dim]->Sumw2(true);
651  }
652  }
653 
654  // 4. now we can actually fill our projections
655  #ifdef MULTITHREAD
656  #pragma omp parallel for
657  #endif
658  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
659  const int nDims = SampleInfo[sample].Dimenstion;
660  for (int dim = 0; dim < nDims; dim++) {
661  for (int toy = 0; toy < Ntoys; ++toy) {
662  if (nDims == 1) {
663  FastViolinFill(Spectra[sample][dim].get(), static_cast<TH1D*>(Toys[sample][toy].get()));
664  }
665  else if (nDims == 2) {
666  FastViolinFill(Spectra[sample][dim].get(), static_cast<TH1D*>(Projections[sample][toy][dim].get()));
667  }
668  else {
669  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
670  throw MaCh3Exception(__FILE__, __LINE__);
671  }
672  }
673  }
674  }
675 
676  // 5. Save histograms which is not thread save
677  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
678  SampleDirectories[sample]->cd();
679  for (long unsigned int dim = 0; dim < Spectra[sample].size(); dim++) {
680  Spectra[sample][dim]->Write();
681  }
682  }
683 
684  return Spectra;
685 }
686 
687 
688 // *************************
689 std::vector<std::unique_ptr<TH1>> PredictiveThrower::MakePredictive(const std::vector<std::vector<std::unique_ptr<TH1>>>& Toys,
690  const std::vector<TDirectory*>& Directory,
691  const std::string& suffix,
692  const bool DebugHistograms) {
693 // *************************
695  std::vector<std::unique_ptr<TH1>> PostPred_mc(TotalNumberOfSamples);
696 
697  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
698  Directory[sample]->cd();
699  const int nDims = SampleInfo[sample].Dimenstion;
700  const std::string Sample_Name = SampleInfo[sample].Name;
701  if (nDims == 1) {
702  int nbinsx = Toys[sample][0]->GetNbinsX();
703  const double* xbins = Toys[sample][0]->GetXaxis()->GetXbins()->GetArray();
704  auto PredictiveHist = std::make_unique<TH1D>(
705  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
706  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
707  nbinsx, xbins
708  );
709  PredictiveHist->GetXaxis()->SetTitle(Toys[sample][0]->GetXaxis()->GetTitle());
710  PredictiveHist->GetYaxis()->SetTitle("Events");
711  PredictiveHist->SetDirectory(nullptr);
712 
713  for (int i = 1; i <= nbinsx; ++i) {
714  std::string ProjName = fmt::format("{} {} Bin: {}",
715  SampleInfo[sample].Name, suffix,
716  SampleInfo[sample].Binning->GetBinName(SampleInfo[sample].LocalId, i-1));
717  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
718  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
719  auto PosteriorHist = std::make_unique<TH1D>(ProjName.c_str(), ProjName.c_str(), 100, 1, -1);
720  PosteriorHist->SetDirectory(nullptr);
721  PosteriorHist->GetXaxis()->SetTitle("Events");
722 
723  for (size_t iToy = 0; iToy < Toys[sample].size(); ++iToy) {
724  double content = Toys[sample][iToy]->GetBinContent(i);
725  PosteriorHist->Fill(content, ReweightWeight[iToy]);
726  }
727 
728  if (DebugHistograms) PosteriorHist->Write();
729 
730  PredictiveHist->SetBinContent(i, PosteriorHist->GetMean());
731  PredictiveHist->SetBinError(i, PosteriorHist->GetRMS());
732  }
733  PredictiveHist->Write();
734  PostPred_mc[sample] = std::move(PredictiveHist);
735  }
736  else if (nDims == 2) {
737  int nbinsx = Toys[sample][0]->GetNbinsX();
738  int nbinsy = Toys[sample][0]->GetNbinsY();
739  const double* xbins = Toys[sample][0]->GetXaxis()->GetXbins()->GetArray();
740  const double* ybins = Toys[sample][0]->GetYaxis()->GetXbins()->GetArray();
741 
742  // Create 2D predictive histogram with same binning as Toys[sample][0]
743  auto PredictiveHist = std::make_unique<TH2D>(
744  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
745  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
746  nbinsx, xbins,
747  nbinsy, ybins
748  );
749  PredictiveHist->GetXaxis()->SetTitle(Toys[sample][0]->GetXaxis()->GetTitle());
750  PredictiveHist->GetYaxis()->SetTitle(Toys[sample][0]->GetYaxis()->GetTitle());
751  PredictiveHist->SetDirectory(nullptr);
752 
753  for (int iy = 1; iy <= nbinsy; ++iy) {
754  for (int ix = 1; ix <= nbinsx; ++ix) {
755  // MaCh3 vs ROOT conventions, above loop should be over MaCh3 bins
756  const int MaCh3Bin = SampleInfo[sample].Binning->GetBinSafe(SampleInfo[sample].LocalId, {ix-1, iy-1});
757  std::string ProjName = fmt::format("{} {} Bin: {}",
758  SampleInfo[sample].Name, suffix,
759  SampleInfo[sample].Binning->GetBinName(SampleInfo[sample].LocalId, MaCh3Bin));
760  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
761  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
762  auto PosteriorHist = std::make_unique<TH1D>(ProjName.c_str(), ProjName.c_str(), 100, 1, -1);
763  PosteriorHist->SetDirectory(nullptr);
764  PosteriorHist->GetXaxis()->SetTitle("Events");
765  int bin = Toys[sample][0]->GetBin(ix, iy);
766  for (size_t iToy = 0; iToy < Toys[sample].size(); ++iToy) {
767  double content = Toys[sample][iToy]->GetBinContent(bin);
768  PosteriorHist->Fill(content, ReweightWeight[iToy]);
769  }
770 
771  if (DebugHistograms) PosteriorHist->Write();
772 
773  PredictiveHist->SetBinContent(ix, iy, PosteriorHist->GetMean());
774  PredictiveHist->SetBinError(ix, iy, PosteriorHist->GetRMS());
775  }
776  }
777  PredictiveHist->Write();
778  PostPred_mc[sample] = std::move(PredictiveHist);
779  }
780  else {
781  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
782  throw MaCh3Exception(__FILE__, __LINE__);
783  }
784  }
785  return PostPred_mc;
786 }
787 
788 // *************************
789 // Perform predictive analysis
791 // *************************
792  MACH3LOG_INFO("Starting {}", __func__);
793  TStopwatch TempClock;
794  TempClock.Start();
795 
796  auto DebugHistograms = GetFromManager<bool>(fitMan->raw()["Predictive"]["DebugHistograms"], false, __FILE__, __LINE__);
797 
798  TDirectory* PredictiveDir = outputFile->mkdir("Predictive");
799  std::vector<TDirectory*> SampleDirectories;
800  SampleDirectories.resize(TotalNumberOfSamples+1);
801 
802  // open directory for every sample
803  for (int sample = 0; sample < TotalNumberOfSamples+1; ++sample) {
804  SampleDirectories[sample] = PredictiveDir->mkdir(SampleInfo[sample].Name.c_str());
805  }
806 
807  // Produce Violin style spectra
808  auto Spectra_mc = ProduceSpectra(MC_Hist_Toy, SampleDirectories, "mc");
809  // Produce posterior predictive distribution
810  auto PostPred_mc = MakePredictive(MC_Hist_Toy, SampleDirectories, "mc", DebugHistograms);
811  // Calculate Posterior Predictive $p$-value
812  PosteriorPredictivepValue(PostPred_mc, SampleDirectories);
813 
814  // Close directories
815  for (int sample = 0; sample < TotalNumberOfSamples+1; ++sample) {
816  SampleDirectories[sample]->Close();
817  delete SampleDirectories[sample];
818  }
819  // Perform beta analysis for mc statical uncertainty
820  StudyBetaParameters(PredictiveDir);
821 
822  PredictiveDir->Close();
823  delete PredictiveDir;
824 
825  outputFile->cd();
826 
827  TempClock.Stop();
828  MACH3LOG_INFO("{} took {:.2f}s to finish for {} toys", __func__, TempClock.RealTime(), Ntoys);
829 }
830 
831 // *************************
832 double PredictiveThrower::GetLLH(const std::unique_ptr<TH1>& DatHist,
833  const std::unique_ptr<TH1>& MCHist,
834  const std::unique_ptr<TH1>& W2Hist,
835  const SampleHandlerBase* SampleHandler) {
836 // *************************
837  double llh = 0.0;
838  for (int i = 1; i <= DatHist->GetXaxis()->GetNbins(); ++i)
839  {
840  const double data = DatHist->GetBinContent(i);
841  const double mc = MCHist->GetBinContent(i);
842  const double w2 = W2Hist->GetBinContent(i);
843  llh += SampleHandler->GetTestStatLLH(data, mc, w2);
844  }
845  //KS: do times 2 because banff reports chi2
846  return 2*llh;
847 }
848 
849 // *************************
850 void PredictiveThrower::PosteriorPredictivepValue(const std::vector<std::unique_ptr<TH1>>& PostPred_mc,
851  const std::vector<TDirectory*>& SampleDir) {
852 // *************************
853  //(void) PostPred_w2;
854  // [Toys][Sample]
855  std::vector<std::vector<double>> chi2_dat_vec(Ntoys);
856  std::vector<std::vector<double>> chi2_mc_vec(Ntoys);
857  std::vector<std::vector<double>> chi2_pred_vec(Ntoys);
858 
859  for(int iToy = 0; iToy < Ntoys; iToy++) {
860  chi2_dat_vec[iToy].resize(TotalNumberOfSamples+1, 0);
861  chi2_mc_vec[iToy].resize(TotalNumberOfSamples+1, 0);
862  chi2_pred_vec[iToy].resize(TotalNumberOfSamples+1, 0);
863 
864  chi2_dat_vec[iToy].back() = PenaltyTerm[iToy];
865  chi2_mc_vec[iToy].back() = PenaltyTerm[iToy];
866  chi2_pred_vec[iToy].back() = PenaltyTerm[iToy];
867 
869  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
870  const int nDims = SampleInfo[iSample].Dimenstion;
871 
872  auto DrawFluctHist = M3::Clone(MC_Hist_Toy[iSample][iToy].get());
873  auto PredFluctHist = M3::Clone(PostPred_mc[iSample].get());
874  if (nDims == 1) {
875  MakeFluctuatedHistogramAlternative(static_cast<TH1D*>(DrawFluctHist.get()), static_cast<TH1D*>(MC_Hist_Toy[iSample][iToy].get()), random.get());
876  MakeFluctuatedHistogramAlternative(static_cast<TH1D*>(PredFluctHist.get()), static_cast<TH1D*>(PostPred_mc[iSample].get()), random.get());
877  }
878  else if (nDims == 2) {
879  MakeFluctuatedHistogramAlternative(static_cast<TH2D*>(DrawFluctHist.get()), static_cast<TH2D*>(MC_Hist_Toy[iSample][iToy].get()), random.get());
880  MakeFluctuatedHistogramAlternative(static_cast<TH2D*>(PredFluctHist.get()), static_cast<TH2D*>(PostPred_mc[iSample].get()), random.get());
881  }
882  else {
883  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
884  throw MaCh3Exception(__FILE__, __LINE__);
885  }
886 
887  // Okay now we can do our chi2 calculation for our sample
888  chi2_dat_vec[iToy][iSample] = GetLLH(Data_Hist[iSample], MC_Hist_Toy[iSample][iToy], W2_Hist_Toy[iSample][iToy], SampleInfo[iSample].SamHandler);
889  chi2_mc_vec[iToy][iSample] = GetLLH(DrawFluctHist, MC_Hist_Toy[iSample][iToy], W2_Hist_Toy[iSample][iToy], SampleInfo[iSample].SamHandler);
890  chi2_pred_vec[iToy][iSample] = GetLLH(PredFluctHist, MC_Hist_Toy[iSample][iToy], W2_Hist_Toy[iSample][iToy], SampleInfo[iSample].SamHandler);
891 
892  chi2_dat_vec[iToy].back() += chi2_dat_vec[iToy][iSample];
893  chi2_mc_vec[iToy].back() += chi2_mc_vec[iToy][iSample];
894  chi2_pred_vec[iToy].back() += chi2_pred_vec[iToy][iSample];
895  }
896  }
897 
898  MakeChi2Plots(chi2_mc_vec, "-2LLH (Draw Fluc, Draw)", chi2_dat_vec, "-2LLH (Data, Draw)", SampleDir, "_drawfluc_draw");
899  MakeChi2Plots(chi2_pred_vec, "-2LLH (Pred Fluc, Draw)", chi2_dat_vec, "-2LLH (Data, Draw)", SampleDir, "_predfluc_draw");
900 }
901 
902 // *************************
903 void PredictiveThrower::MakeChi2Plots(const std::vector<std::vector<double>>& Chi2_x,
904  const std::string& Chi2_x_title,
905  const std::vector<std::vector<double>>& Chi2_y,
906  const std::string& Chi2_y_title,
907  const std::vector<TDirectory*>& SampleDir,
908  const std::string Title) {
909 // *************************
910  for (int iSample = 0; iSample < TotalNumberOfSamples+1; ++iSample) {
911  SampleDir[iSample]->cd();
912 
913  // Transpose to extract chi2 values for a given sample across all toys
914  std::vector<double> chi2_y_sample(Ntoys);
915  std::vector<double> chi2_x_per_sample(Ntoys);
916 
917  for (int iToy = 0; iToy < Ntoys; ++iToy) {
918  chi2_y_sample[iToy] = Chi2_y[iToy][iSample];
919  chi2_x_per_sample[iToy] = Chi2_x[iToy][iSample];
920  }
921 
922  const double min_val = std::min(*std::min_element(chi2_y_sample.begin(), chi2_y_sample.end()),
923  *std::min_element(chi2_x_per_sample.begin(), chi2_x_per_sample.end()));
924  const double max_val = std::max(*std::max_element(chi2_y_sample.begin(), chi2_y_sample.end()),
925  *std::max_element(chi2_x_per_sample.begin(), chi2_x_per_sample.end()));
926 
927  auto chi2_hist = std::make_unique<TH2D>((SampleInfo[iSample].Name+ Title).c_str(),
928  (SampleInfo[iSample].Name+ Title).c_str(),
929  100, min_val, max_val, 100, min_val, max_val);
930  chi2_hist->SetDirectory(nullptr);
931  chi2_hist->GetXaxis()->SetTitle(Chi2_x_title.c_str());
932  chi2_hist->GetYaxis()->SetTitle(Chi2_y_title.c_str());
933 
934  for (int iToy = 0; iToy < Ntoys; ++iToy) {
935  chi2_hist->Fill(chi2_x_per_sample[iToy], chi2_y_sample[iToy]);
936  }
937 
938  Get2DBayesianpValue(chi2_hist.get());
939  chi2_hist->Write();
940  }
941 }
942 
943 // *************************
944 // Study Beta Parameters
945 void PredictiveThrower::StudyBetaParameters(TDirectory* PredictiveDir) {
946 // *************************
947  bool StudyBeta = GetFromManager<bool>(fitMan->raw()["Predictive"]["StudyBetaParameters"], true, __FILE__, __LINE__ );
948  if (StudyBeta == false) return;
949 
950  MACH3LOG_INFO("Starting {}", __func__);
951  TDirectory* BetaDir = PredictiveDir->mkdir("BetaParameters");
952  std::vector<std::vector<std::unique_ptr<TH1D>>> BetaHist(TotalNumberOfSamples);
953  std::vector<TDirectory *> DirBeta(TotalNumberOfSamples);
954  // initialise directory for each sample
955  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
956  BetaDir->cd();
957  DirBeta[sample] = BetaDir->mkdir(SampleInfo[sample].Name.c_str());
958  }
959 
961  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
962  BetaHist[iSample].resize(SampleInfo[iSample].Binning->GetNBins(SampleInfo[iSample].LocalId));
963 
964  for (int iBin = 0; iBin < SampleInfo[iSample].Binning->GetNBins(SampleInfo[iSample].LocalId); iBin++ ) {
965  std::string title = fmt::format("#beta param, {} Bin: {}",
966  SampleInfo[iSample].Name,
967  SampleInfo[iSample].Binning->GetBinName(SampleInfo[iSample].LocalId, iBin));
968  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
969  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
970  BetaHist[iSample][iBin] = std::make_unique<TH1D>(title.c_str(), title.c_str(), 100, 1, -1);
971  BetaHist[iSample][iBin]->SetDirectory(nullptr);
972  BetaHist[iSample][iBin]->GetXaxis()->SetTitle("beta parameter");
973  }
974  }
975 
977  #ifdef MULTITHREAD
978  #pragma omp parallel for
979  #endif
980  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
981  const int nDims = SampleInfo[iSample].Dimenstion;
982  if (nDims == 1) {
983  int nbinsx = Data_Hist[iSample]->GetNbinsX();
984  for (int iBinX = 0; iBinX < nbinsx; ++iBinX) {
985  const int RootBin = iBinX+1;
986  for (int iToy = 0; iToy < Ntoys; ++iToy) {
988  const double Data = Data_Hist[iSample]->GetBinContent(RootBin);
989  const double MC = MC_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
990  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
991  const auto likelihood = SampleInfo[iSample].SamHandler->GetTestStatistic();
992 
993  const double BetaParam = GetBetaParameter(Data, MC, w2, likelihood);
994  BetaHist[iSample][iBinX]->Fill(BetaParam, ReweightWeight[iToy]);
995  }
996  }
997  } else if (nDims == 2) {
998  const int nX = Data_Hist[iSample]->GetNbinsX();
999  const int nY = Data_Hist[iSample]->GetNbinsY();
1000  for (int y = 0; y < nY; ++y) {
1001  for (int x = 0; x < nX; ++x) {
1002  const int RootBin = Data_Hist[iSample]->GetBin(x+1, y+1);
1003  const int MaCh3Bin = SampleInfo[iSample].Binning->GetBinSafe(SampleInfo[iSample].LocalId, {x, y});
1004 
1005  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1006  const double Data = Data_Hist[iSample]->GetBinContent(RootBin);
1007  const double MC = MC_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
1008  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
1009  const auto likelihood = SampleInfo[iSample].SamHandler->GetTestStatistic();
1010 
1011  const double BetaParam = GetBetaParameter(Data, MC, w2, likelihood);
1012  BetaHist[iSample][MaCh3Bin]->Fill(BetaParam, ReweightWeight[iToy]);
1013  }
1014  }
1015  }
1016  } else {
1017  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
1018  throw MaCh3Exception(__FILE__, __LINE__);
1019  }
1020  }
1021 
1023  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1024  for (int iBin = 0; iBin < SampleInfo[iSample].Binning->GetNBins(SampleInfo[iSample].LocalId); iBin++ ) {
1025  DirBeta[iSample]->cd();
1026  BetaHist[iSample][iBin]->Write();
1027  }
1028  DirBeta[iSample]->Close();
1029  delete DirBeta[iSample];
1030  }
1031  BetaDir->Close();
1032  delete BetaDir;
1033 
1034  PredictiveDir->cd();
1035 }
void MakeFluctuatedHistogramAlternative(TH1D *FluctHist, TH1D *PolyHist, TRandom3 *rand)
Make Poisson fluctuation of TH1D hist using slow method which is only for cross-check.
void FastViolinFill(TH2D *violin, TH1D *hist_1d)
KS: Fill Violin histogram with entry from a toy.
@ kXSecPar
Definition: MCMCProcessor.h:46
#define MACH3LOG_DEBUG
Definition: MaCh3Logger.h:34
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
double GetBetaParameter(const double data, const double mc, const double w2, const TestStatistic TestStat)
KS: Calculate Beta parameter which will be different based on specified test statistic.
void Get2DBayesianpValue(TH2D *Histogram)
Calculates the 2D Bayesian p-value and generates a visualization.
YAML::Node TMacroToYAML(const TMacro &macro)
KS: Convert a ROOT TMacro object to a YAML node.
Definition: YamlHelper.h:152
bool compareYAMLNodes(const YAML::Node &node1, const YAML::Node &node2, bool Mute=false)
Compare if yaml nodes are identical.
Definition: YamlHelper.h:186
bool CheckNodeExists(const YAML::Node &node, Args... args)
KS: Wrapper function to call the recursive helper.
Definition: YamlHelper.h:60
Base class for implementing fitting algorithms.
Definition: FitterBase.h:26
std::string GetName() const
Get name of class.
Definition: FitterBase.h:70
std::unique_ptr< TRandom3 > random
Random number.
Definition: FitterBase.h:144
std::vector< SampleHandlerBase * > samples
Sample holder.
Definition: FitterBase.h:129
TFile * outputFile
Output.
Definition: FitterBase.h:147
std::string AlgorithmName
Name of fitting algorithm that is being used.
Definition: FitterBase.h:168
Manager * fitMan
The manager for configuration handling.
Definition: FitterBase.h:108
std::vector< ParameterHandlerBase * > systematics
Systematic holder.
Definition: FitterBase.h:134
Class responsible for processing MCMC chains, performing diagnostics, generating plots,...
Definition: MCMCProcessor.h:58
void Initialise()
Scan chain, what parameters we have and load information from covariance matrices.
YAML::Node GetCovConfig(const int i) const
Get Yaml config obtained from a Chain.
Custom exception class used throughout MaCh3.
The manager class is responsible for managing configurations and settings.
Definition: Manager.h:16
YAML::Node const & raw() const
Return config.
Definition: Manager.h:41
Base class responsible for handling of systematic error parameters. Capable of using PCA or using ada...
int GetNumParams() const
Get total number of parameters.
void SetParProp(const int i, const double val)
Set proposed parameter value.
int GetParIndex(const std::string &name) const
Get index based on name.
std::string GetName() const
Get name of covariance.
double GetParInit(const int i) const
Get prior parameter value.
YAML::Node GetConfig() const
Getter to return a copy of the YAML node.
Class responsible for handling of systematic error parameters with different types defined in the con...
void SetGroupOnlyParameters(const std::string &Group, const std::vector< double > &Pars={})
KS Function to set to prior parameters of a given group or values from vector.
std::vector< std::string > GetUniqueParameterGroups()
KS: Get names of all unique parameter groups.
void SetParamters()
This set some params to prior value this way you can evaluate errors from subset of errors.
std::vector< double > PenaltyTerm
Penalty term values for each toy by default 0.
virtual ~PredictiveThrower()
Destructor.
std::vector< std::vector< std::unique_ptr< TH2D > > > ProduceSpectra(const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &Director, const std::string suffix)
Produce Violin style spectra.
bool FullLLH
KS: Use Full LLH or only sample contribution based on discussion with Asher we almost always only wan...
void RunPredictiveAnalysis()
Main routine responsible for producing posterior predictive distributions and $p$-value.
bool LoadToys()
Load existing toys.
void PosteriorPredictivepValue(const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< TDirectory * > &SampleDir)
Calculate Posterior Predictive $p$-value.
std::vector< std::unique_ptr< TH1 > > MakePredictive(const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &Director, const std::string &suffix, const bool DebugHistograms)
Produce posterior predictive distribution.
std::vector< std::string > GetStoredFancyName(ParameterHandlerBase *Systematics) const
Get Fancy parameters stored in mcmc chains for passed ParameterHandler.
std::vector< std::unique_ptr< TH1 > > W2_Nom_Hist
Vector of W2 histograms.
std::unordered_set< int > ParameterOnlyToVary
KS: Index of parameters that will be varied.
std::vector< std::vector< std::unique_ptr< TH1 > > > W2_Hist_Toy
double GetLLH(const std::unique_ptr< TH1 > &DatHist, const std::unique_ptr< TH1 > &MCHist, const std::unique_ptr< TH1 > &W2Hist, const SampleHandlerBase *SampleHandler)
Helper functions to calculate likelihoods using TH1.
bool Is_PriorPredictive
Whether it is Prior or Posterior predictive.
std::vector< std::string > ParameterGroupsNotVaried
KS: Names of parameter groups that will not be varied.
int NModelParams
KS: Count total number of model parameters which can be used for stuff like BIC.
int Ntoys
Number of toys we are generating analysing.
void SetupToyGeneration()
Setup useful variables etc before stating toy generation.
void MakeChi2Plots(const std::vector< std::vector< double >> &Chi2_x, const std::string &Chi2_x_title, const std::vector< std::vector< double >> &Chi2_y, const std::string &Chi2_y_title, const std::vector< TDirectory * > &SampleDir, const std::string Title)
Produce Chi2 plot for a single sample based on which $p$-value is calculated.
void SetupSampleInformation()
Setup sample information.
std::vector< std::unique_ptr< TH1 > > MC_Nom_Hist
Vector of MC histograms.
int TotalNumberOfSamples
Number of toys we are generating analysing.
void ProduceToys()
Produce toys by throwing from MCMC.
void StudyBetaParameters(TDirectory *PredictiveDir)
Evaluate prior/post predictive distribution for beta parameters (used for evaluating impact MC statis...
std::vector< std::unique_ptr< TH1 > > Data_Hist
Vector of Data histograms.
ParameterHandlerGeneric * ModelSystematic
Pointer to El Generico.
std::vector< double > ReweightWeight
Reweighting factors applied for each toy, by default 1.
PredictiveThrower(Manager *const fitMan)
Constructor.
std::vector< std::vector< std::unique_ptr< TH1 > > > MC_Hist_Toy
Class responsible for handling implementation of samples used in analysis, reweighting and returning ...
double GetTestStatLLH(const double data, const double mc, const double w2) const
Calculate test statistic for a single bin. Calculation depends on setting of fTestStatistic....
Class responsible for handling implementation of samples used in analysis, reweighting and returning ...
TH1 * GetDataHist(const int Sample) override
Get Data histogram.
TH1 * GetMCHist(const int Sample) override
Get MC histogram.
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.
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:228
KS: Store info about MC sample.