MaCh3  2.6.0
Reference Guide
Classes | Namespaces | Enumerations | Functions
ReweightMCMC.cpp File Reference

This executable allow to reweight MCMC Chain, such technique is used to study impact of different priors without rerunning MCMC. More...

#include "Manager/Manager.h"
#include "Fitters/MCMCProcessor.h"
#include "TFile.h"
#include "TTree.h"
#include "TChain.h"
#include "TMath.h"
#include "TGraph2D.h"
#include "TGraph.h"
#include <memory>
#include <vector>
#include <string>
#include <cmath>
#include <fstream>
#include <map>
Include dependency graph for ReweightMCMC.cpp:

Go to the source code of this file.

Classes

struct  ReweightConfig
 Structure to hold reweight configuration. More...
 

Namespaces

 M3
 Main namespace for MaCh3 software.
 

Enumerations

enum  M3::kReweightType {
  M3::kGaussian , M3::kGaussian , M3::kTGraph , M3::kTGraph2D ,
  M3::kReweightTypes
}
 Types of chain reweighting available. More...
 

Functions

double Graph_interpolateNO (TGraph2D *graph, double theta13, double dm32)
 Function to interpolate 2D graph for Normal Ordering. More...
 
double Graph_interpolateIO (TGraph2D *graph, double theta13, double dm32)
 Function to interpolate 2D graph for Inverted Ordering
More...
 
double Graph_interpolate1D (TGraph *graph, double theta13)
 Function to interpolate 1D graph. More...
 
bool GetParameterInfo (MCMCProcessor *processor, const std::string &paramName, double &mean, double &sigma, bool &isFlat)
 Get parameter information from MCMCProcessor. More...
 
void LoadReweightingSettings (std::vector< ReweightConfig > &reweightConfigs, const YAML::Node &reweight_settings)
 Load reweighting setting like 1D or 2D from YAML config. More...
 
double Get1DWeight (const ReweightConfig &rwConfig, const std::map< std::string, double > &paramValues)
 Calculate 1D weight. More...
 
double Get2DWeight (const ReweightConfig &rwConfig, const std::map< std::string, double > &paramValues)
 Calculate 2D weight. More...
 
void ReweightMCMC (const std::string &configFile, const std::string &inputFile)
 Main executable responsible for reweighting MCMC chains. More...
 
int main (int argc, char *argv[])
 Main function. More...
 

Detailed Description

This executable allow to reweight MCMC Chain, such technique is used to study impact of different priors without rerunning MCMC.

Author
David Riley
Evan Goodman

Definition in file ReweightMCMC.cpp.

Function Documentation

◆ Get1DWeight()

double Get1DWeight ( const ReweightConfig rwConfig,
const std::map< std::string, double > &  paramValues 
)

Calculate 1D weight.

Warning
Big limitation is that code only works for uncorrelated parameters :(

Definition at line 349 of file ReweightMCMC.cpp.

350  {
351  double weight = 1.;
352  if(rwConfig.type == M3::kGaussian) {
353  auto& paramNames = rwConfig.paramNames;
354  //KS: Calculate reweight weight. Weights are multiplicative so we can do several reweights at once.
356  for (unsigned int j = 0; j < paramNames.size(); ++j)
357  {
358  auto name = rwConfig.paramNames.at(j);
359  // Extract means and sigmas from the prior pairs
360  auto newPriorPair = rwConfig.newPriorValues[j];
361  double NewCentral = newPriorPair[0]; // mean
362  double NewError = newPriorPair[1]; // sigma
363 
364  double new_chi = (paramValues.at(name) - NewCentral)/NewError;
365  double new_prior = std::exp(-0.5 * new_chi * new_chi);
366 
367  double old_chi = -1;
368  double old_prior = -1;
369  if(rwConfig.flatPrior[j]) {
370  old_prior = 1.0;
371  } else {
372  auto oldPriorPair = rwConfig.oldPriorValues[j];
373  double OldCentral = newPriorPair[0]; // mean
374  double OldError = newPriorPair[1]; // sigma
375 
376  old_chi = (paramValues.at(name) - OldCentral)/OldError;
377  old_prior = std::exp(-0.5 * old_chi * old_chi);
378  }
379  weight *= new_prior/old_prior;
380  }
381  } else if (rwConfig.type == M3::kTGraph) {
382  double paramValue = paramValues.at(rwConfig.paramNames.at(0));
383  weight = Graph_interpolate1D(rwConfig.graph_1D.get(), paramValue);
384  }
385  return weight;
386 }
double Graph_interpolate1D(TGraph *graph, double theta13)
Function to interpolate 1D graph.
@ kTGraph
Calculates Likelihood based on TGraph.
@ kGaussian
Assumes gaussian prior.
std::vector< std::vector< double > > newPriorValues
new [mean, sigma] pairs
std::unique_ptr< TGraph > graph_1D
1D interpolation graph.
M3::kReweightType type
"Gaussian", "TGraph2D"
std::vector< std::vector< double > > oldPriorValues
new [mean, sigma] pairs
std::vector< std::string > paramNames
Parameter names.
std::vector< bool > flatPrior

◆ Get2DWeight()

double Get2DWeight ( const ReweightConfig rwConfig,
const std::map< std::string, double > &  paramValues 
)

Calculate 2D weight.

Definition at line 389 of file ReweightMCMC.cpp.

390  {
391  double weight = 1.;
392  if (rwConfig.type == M3::kTGraph2D) {
393  double dm32 = paramValues.at(rwConfig.paramNames.at(0));
394  double theta13 = paramValues.at(rwConfig.paramNames.at(1));
395  if (dm32 > 0) {
396  // Normal Ordering
397  if (rwConfig.graph_NO) {
398  weight = Graph_interpolateNO(rwConfig.graph_NO.get(), theta13, dm32);
399  } else {
400  MACH3LOG_ERROR("NO graph not available for {}", rwConfig.key);
401  weight = 0.0;
402  }
403  } else {
404  // Inverted Ordering
405  if (rwConfig.graph_IO) {
406  weight = Graph_interpolateIO(rwConfig.graph_IO.get(), theta13, dm32);
407  } else {
408  MACH3LOG_ERROR("IO graph not available for {}", rwConfig.key);
409  weight = 0.0;
410  }
411  }
412  }
413  return weight;
414 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
double Graph_interpolateNO(TGraph2D *graph, double theta13, double dm32)
Function to interpolate 2D graph for Normal Ordering.
double Graph_interpolateIO(TGraph2D *graph, double theta13, double dm32)
Function to interpolate 2D graph for Inverted Ordering
@ kTGraph2D
Calculates Likelihood based on TGraph2D.
std::unique_ptr< TGraph2D > graph_NO
Normal Ordering graph.
std::string key
The YAML key for this reweight.
std::unique_ptr< TGraph2D > graph_IO
Inverted Ordering graph.

◆ GetParameterInfo()

bool GetParameterInfo ( MCMCProcessor processor,
const std::string &  paramName,
double &  mean,
double &  sigma,
bool &  isFlat 
)

Get parameter information from MCMCProcessor.

Definition at line 151 of file ReweightMCMC.cpp.

153 {
154  // Try to find the parameter index
155  int paramIndex = processor->GetParamIndexFromName(paramName);
156 
157  if (paramIndex == M3::_BAD_INT_) { // This indicate parameter not found
158  return false;
159  }
160 
161  // Get parameter information
162  TString title;
163  processor->GetNthParameter(paramIndex, mean, sigma, title);
164  isFlat = processor->GetParamFlat(paramIndex);
165 
166  return true;
167 }
bool isFlat(TSpline3_red *&spl)
CW: Helper function used in the constructor, tests to see if the spline is flat.
void GetNthParameter(const int param, double &Prior, double &PriorError, TString &Title) const
Get properties of parameter by passing it number.
bool GetParamFlat(const int iParam) const
Get whether param has flat prior or not.
int GetParamIndexFromName(const std::string &Name) const
Get parameter number based on name.
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55

◆ Graph_interpolate1D()

double Graph_interpolate1D ( TGraph *  graph,
double  theta13 
)

Function to interpolate 1D graph.

Todo:
double check implementation of TGraph interpolation for 1D

Definition at line 121 of file ReweightMCMC.cpp.

122 {
124  if (!graph) {
125  MACH3LOG_ERROR("Graph pointer is null");
126  throw MaCh3Exception(__FILE__, __LINE__);
127  }
128 
129  double xmax = -999999999;
130  double xmin = 999999999;
131 
132  for (int i = 0; i < graph->GetN(); i++) {
133  double x = graph->GetX()[i];
134  if (x > xmax) xmax = x;
135  if (x < xmin) xmin = x;
136  }
137 
138  double chiSquared, prior;
139 
140  if (theta13 < xmax && theta13 > xmin) {
141  chiSquared = graph->Eval(theta13);
142  prior = std::exp(-0.5 * chiSquared);
143  } else {
144  prior = 0.0;
145  }
146 
147  return prior;
148 }
Custom exception class used throughout MaCh3.

◆ Graph_interpolateIO()

double Graph_interpolateIO ( TGraph2D *  graph,
double  theta13,
double  dm32 
)

Function to interpolate 2D graph for Inverted Ordering

Definition at line 94 of file ReweightMCMC.cpp.

95 {
96  if (!graph) {
97  MACH3LOG_ERROR("Graph pointer is null");
98  throw MaCh3Exception(__FILE__, __LINE__);
99  }
100 
101  double xmax = graph->GetXmax();
102  double xmin = graph->GetXmin();
103  double ymax = graph->GetYmax();
104  double ymin = graph->GetYmin();
105 
106  // The dm32 value is positive for in the TGraph2D so we should compare the abs value of the -delM32 values to get the chisq
107  double mod_dm32 = std::abs(dm32);
108  double chiSquared, prior;
109 
110  if (theta13 < xmax && theta13 > xmin && mod_dm32 < ymax && mod_dm32 > ymin) {
111  chiSquared = graph->Interpolate(theta13, mod_dm32);
112  prior = std::exp(-0.5 * chiSquared);
113  } else {
114  prior = 0.0;
115  }
116 
117  return prior;
118 }

◆ Graph_interpolateNO()

double Graph_interpolateNO ( TGraph2D *  graph,
double  theta13,
double  dm32 
)

Function to interpolate 2D graph for Normal Ordering.

Definition at line 69 of file ReweightMCMC.cpp.

70 {
71  if (!graph) {
72  MACH3LOG_ERROR("Graph pointer is null");
73  throw MaCh3Exception(__FILE__, __LINE__);
74  }
75 
76  double xmax = graph->GetXmax();
77  double xmin = graph->GetXmin();
78  double ymin = graph->GetYmin();
79  double ymax = graph->GetYmax();
80 
81  double chiSquared, prior;
82 
83  if (theta13 < xmax && theta13 > xmin && dm32 < ymax && dm32 > ymin) {
84  chiSquared = graph->Interpolate(theta13, dm32);
85  prior = std::exp(-0.5 * chiSquared);
86  } else {
87  prior = 0.0;
88  }
89 
90  return prior;
91 }

◆ LoadReweightingSettings()

void LoadReweightingSettings ( std::vector< ReweightConfig > &  reweightConfigs,
const YAML::Node &  reweight_settings 
)

Load reweighting setting like 1D or 2D from YAML config.

Definition at line 170 of file ReweightMCMC.cpp.

170  {
171  // iterate through the keys in the reweighting yaml creating and storing the ReweightConfig as we go
172  for (const auto& reweight : reweight_settings) {
173  const std::string& reweightKey = reweight.first.as<std::string>();
174  const YAML::Node& reweightConfigNode = reweight.second;
175 
176  // Check if this particular reweight is enabled !!! Currently only support one reweight at a time so this defaults to enabled
177  if (!GetFromManager<bool>(reweightConfigNode["Enabled"], true, __FILE__ , __LINE__)) {
178  MACH3LOG_INFO("Skipping disabled reweight: {}", reweightKey);
179  continue;
180  }
181 
182  ReweightConfig reweightConfig;
183  reweightConfig.key = reweightKey;
184  reweightConfig.name = Get<std::string>(reweightConfigNode["ReweightName"], __FILE__ , __LINE__);
185  auto ReweightType = Get<std::string>(reweightConfigNode["ReweightType"], __FILE__ , __LINE__);
186  reweightConfig.dimension = Get<int>(reweightConfigNode["ReweightDim"], __FILE__ , __LINE__);
187 
188  reweightConfig.weightBranchName = reweightKey;
189  reweightConfig.enabled = true;
190 
191  auto paramNames = Get<std::vector<std::string>>(reweightConfigNode["ReweightVar"], __FILE__ , __LINE__);
192  reweightConfig.paramNames = paramNames;
193  reweightConfig.oldPriorValues.resize(paramNames.size());
194  reweightConfig.flatPrior.resize(paramNames.size());
195 
196  // Handle different reweight types as they fill different members
197  if (reweightConfig.dimension == 1) {
198  if (ReweightType == "Gaussian") {
199  reweightConfig.type = M3::kGaussian;
200  // For Gaussian reweights, we need the parameter name(s) and prior values (mean, sigma pairs)
201  // Get prior values - handle both single [mean, sigma] pair and list of pairs for safety
202  auto priorNode = reweightConfigNode["ReweightPrior"];
203  std::vector<std::vector<double>> allPriorValues;
204 
205  if (priorNode.IsSequence() && priorNode.size() > 0) {
206  // Check if first element is a number (single [mean, sigma] pair) or sequence (list of pairs)
207  if (priorNode[0].IsScalar()) {
208  // Single [mean, sigma] pair - convert to list format
209  auto newPriorValues = Get<std::vector<double>>(priorNode, __FILE__ , __LINE__);
210  if (newPriorValues.size() == 2) {
211  allPriorValues.push_back(newPriorValues);
212  }
213  } else {
214  // List of [mean, sigma] pairs
215  for (const auto& priorPair : priorNode) {
216  auto newPriorValues = Get<std::vector<double>>(priorPair, __FILE__ , __LINE__);
217  if (newPriorValues.size() == 2) {
218  allPriorValues.push_back(newPriorValues);
219  }
220  }
221  }
222  }
223 
224  reweightConfig.newPriorValues = allPriorValues;
225 
226  if (paramNames.empty() || allPriorValues.empty() || paramNames.size() != allPriorValues.size()) {
227  MACH3LOG_ERROR("Invalid Gaussian reweight configuration for {}: {} parameters, {} prior pairs",
228  reweightKey, paramNames.size(), allPriorValues.size());
229  continue;
230  }
231  } else if (ReweightType == "TGraph") {
232  reweightConfig.type = M3::kTGraph;
233  // For TGraph reweights, we need the parameter name and the TGraph file and name
234  auto fileName = Get<std::string>(reweightConfigNode["ReweightPrior"]["file"], __FILE__ , __LINE__);
235  auto graphName = Get<std::string>(reweightConfigNode["ReweightPrior"]["graph_name"], __FILE__ , __LINE__);
236  reweightConfig.fileName = fileName;
237  reweightConfig.graphName = graphName;
238 
239  if (paramNames.empty() || paramNames.size() != 1 || fileName.empty() || graphName.empty()) {
240  MACH3LOG_ERROR("Invalid TGraph reweight configuration for {}", reweightKey);
241  continue;
242  }
243 
244  // Load the 1D graph
245  MACH3LOG_INFO("Loading 1D constraint from file: {} (graph: {})", reweightConfig.fileName, reweightConfig.graphName);
246  auto constraintFile = std::unique_ptr<TFile>(TFile::Open(reweightConfig.fileName.c_str(), "READ"));
247  if (!constraintFile || constraintFile->IsZombie()) {
248  MACH3LOG_ERROR("Failed to open constraint file: {}", reweightConfig.fileName);
249  continue;
250  }
251 
252  std::unique_ptr<TGraph> graph(constraintFile->Get<TGraph>(reweightConfig.graphName.c_str()));
253  if (graph) {
254  // Create a completely independent copy
255  auto cloned_graph = static_cast<TGraph*>(graph->Clone());
256  cloned_graph->SetBit(kCanDelete, true); // Allow ROOT to delete it when we're done
257  reweightConfig.graph_1D = std::unique_ptr<TGraph>(cloned_graph);
258  MACH3LOG_INFO("Loaded 1D graph: {}", reweightConfig.graphName);
259  } else {
260  MACH3LOG_ERROR("Failed to load graph: {}", reweightConfig.graphName);
261  continue;
262  }
263  } else {
264  MACH3LOG_ERROR("Unknown 1D reweight type: {} for {}", ReweightType, reweightKey);
265  throw MaCh3Exception(__FILE__, __LINE__);
266  }
267  } else if (reweightConfig.dimension == 2) {
268  // 2D reweights need 2 parameter names
269  if (paramNames.size() != 2) {
270  MACH3LOG_ERROR("2D reweighting requires exactly 2 parameter names for {}", reweightKey);
271  continue;
272  }
273 
274  if (ReweightType == "TGraph2D") {
275  reweightConfig.type = M3::kTGraph2D;
276  auto priorConfig = reweightConfigNode["ReweightPrior"];
277  reweightConfig.fileName = Get<std::string>(priorConfig["file"], __FILE__ , __LINE__);
278  reweightConfig.graphName = Get<std::string>(priorConfig["graph_name"], __FILE__ , __LINE__);
279  reweightConfig.hierarchyType = GetFromManager<std::string>(priorConfig["hierarchy"], "auto", __FILE__ , __LINE__);
280 
281  if (reweightConfig.fileName.empty() || reweightConfig.graphName.empty()) {
282  MACH3LOG_ERROR("Invalid TGraph2D configuration for {}", reweightKey);
283  continue;
284  }
285 
286  // Load the 2D graphs
287  MACH3LOG_INFO("Loading 2D constraint from file: {} (graph: {})", reweightConfig.fileName, reweightConfig.graphName);
288  auto constraintFile = std::unique_ptr<TFile>(TFile::Open(reweightConfig.fileName.c_str(), "READ"));
289  if (!constraintFile || constraintFile->IsZombie()) {
290  MACH3LOG_ERROR("Failed to open constraint file: {}", reweightConfig.fileName);
291  continue;
292  }
293 
294  // Load both NO and IO graphs if hierarchy is auto
295  if (reweightConfig.hierarchyType == "auto" || reweightConfig.hierarchyType == "NO") {
296  std::string graphName_NO = reweightConfig.graphName + "_NO";
297  MACH3LOG_INFO("Loading NO graph: {}", graphName_NO);
298 
299  std::unique_ptr<TGraph2D> graph_NO(constraintFile->Get<TGraph2D>(graphName_NO.c_str()));
300  if (graph_NO) {
301  // Create a completely independent copy
302  auto cloned_graph = static_cast<TGraph2D*>(graph_NO->Clone());
303  cloned_graph->SetDirectory(nullptr); // Detach from file
304  cloned_graph->SetBit(kCanDelete, true); // Allow ROOT to delete it when we're done
305  reweightConfig.graph_NO = std::unique_ptr<TGraph2D>(cloned_graph);
306  MACH3LOG_INFO("Loaded NO graph: {}", graphName_NO);
307  } else {
308  MACH3LOG_ERROR("Failed to load NO graph: {}", graphName_NO);
309  }
310  }
311 
312  if (reweightConfig.hierarchyType == "auto" || reweightConfig.hierarchyType == "IO") {
313  std::string graphName_IO = reweightConfig.graphName + "_IO";
314  MACH3LOG_INFO("Loading IO graph: {}", graphName_IO);
315  std::unique_ptr<TGraph2D> graph_IO(constraintFile->Get<TGraph2D>(graphName_IO.c_str()));
316  if (graph_IO) {
317  // Create a completely independent copy
318  auto cloned_graph = static_cast<TGraph2D*>(graph_IO->Clone());
319  cloned_graph->SetDirectory(nullptr); // Detach from file
320  cloned_graph->SetBit(kCanDelete, true); // Allow ROOT to delete it when we're done
321  reweightConfig.graph_IO = std::unique_ptr<TGraph2D>(cloned_graph);
322  MACH3LOG_INFO("Loaded IO graph: {}", graphName_IO);
323  } else {
324  MACH3LOG_ERROR("Failed to load IO graph: {}", graphName_IO);
325  }
326  }
327 
328  constraintFile->Close();
329  } else {
330  MACH3LOG_ERROR("Unknown 2D reweight type: {} for {}", ReweightType, reweightKey);
331  continue;
332  }
333  } else {
334  MACH3LOG_ERROR("Unsupported reweight dimension: {} for {}", reweightConfig.dimension, reweightKey);
335  continue;
336  } // end check over dimensions
337 
338  reweightConfigs.push_back(std::move(reweightConfig));
339  MACH3LOG_INFO("Added reweight configuration: {} ({}D, type: {})", reweightConfigs.back().name, reweightConfigs.back().dimension, ReweightType);
340  }
341 
342  if (reweightConfigs.empty()) {
343  MACH3LOG_ERROR("No valid reweight configurations found in config file");
344  throw MaCh3Exception(__FILE__, __LINE__);
345  }
346 }
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
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.
Structure to hold reweight configuration.
std::string weightBranchName
Output weight branch name.
std::string fileName
ROOT file containing graph data.
std::string graphName
Graph name in the ROOT file.
std::string name
int dimension
1 or 2
std::string hierarchyType
"NO", "IO", or "auto"

◆ main()

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

Main function.

Definition at line 603 of file ReweightMCMC.cpp.

604 {
606 
607  if (argc != 3) {
608  MACH3LOG_ERROR("How to use: {} <config.yaml> <input_file.root>", argv[0]);
609  throw MaCh3Exception(__FILE__, __LINE__);
610  }
611 
612  std::string configFile = argv[1];
613  std::string inputFile = argv[2];
614 
615  ReweightMCMC(configFile, inputFile);
616 
617  return 0;
618 }
void SetMaCh3LoggerFormat()
Set messaging format of the logger.
Definition: MaCh3Logger.h:60
void ReweightMCMC(const std::string &configFile, const std::string &inputFile)
Main executable responsible for reweighting MCMC chains.

◆ ReweightMCMC()

void ReweightMCMC ( const std::string &  configFile,
const std::string &  inputFile 
)

Main executable responsible for reweighting MCMC chains.

Parameters
inputFileMCMC Chain file path
configFileConfig file with reweighting settings
Author
David Riley
Evan Goodman
Todo:
add a generic 2D reweight that is not dm32 and theta13 specific DWR
Todo:
Get list only of unique parameters, this is repeating unnecessarily when adding more than 1 weight DWR
Todo:
Finish Asimov shifting implementation, for now just warn that Asimovs are not being properly handled
Todo:
add tracking for how many events are outside the graph ranges for diagnostics DWR

Definition at line 422 of file ReweightMCMC.cpp.

423 {
424  MACH3LOG_INFO("File for reweighting: {} with config {}", inputFile, configFile);
425  // Load configuration
426  YAML::Node reweight_yaml = M3OpenConfig(configFile);
427  YAML::Node reweight_settings = reweight_yaml["ReweightMCMC"];
428 
429  // Parse all reweight configurations first
430  std::vector<ReweightConfig> reweightConfigs;
431 
432  LoadReweightingSettings(reweightConfigs, reweight_settings);
433 
434  // Create MCMCProcessor to get parameter information
435  auto processor = std::make_unique<MCMCProcessor>(inputFile);
436  processor->Initialise();
437 
438  // Validate that all required parameters exist in the chain
440  for (auto& rwConfig : reweightConfigs) {
441  for (size_t i = 0; i < rwConfig.paramNames.size(); ++i) {
442  const auto& paramName = rwConfig.paramNames[i];
443  int paramIndex = processor->GetParamIndexFromName(paramName);
444  if (paramIndex == M3::_BAD_INT_) {
445  MACH3LOG_ERROR("Parameter {} not found in MCMC chain", paramName);
446  throw MaCh3Exception(__FILE__, __LINE__);
447  }
448  MACH3LOG_INFO("Parameter {} found in chain", paramName);
449 
450  double mean, sigma;
451  bool isFlat;
452  GetParameterInfo(processor.get(), paramName, mean, sigma, isFlat);
453 
454  rwConfig.oldPriorValues[i] = {mean, sigma};
455  rwConfig.flatPrior[i] = isFlat;
456  }
457  }
458 
460  // Get the settings for the MCMC
461  auto TempFile = std::unique_ptr<TFile>(TFile::Open(inputFile.c_str(), "READ"));
462  if (!TempFile || TempFile->IsZombie()) {
463  MACH3LOG_ERROR("Cannot open MCMC file: {}", inputFile);
464  throw MaCh3Exception(__FILE__ , __LINE__ );
465  }
466  std::unique_ptr<TMacro> Config(TempFile->Get<TMacro>("MaCh3_Config"));
467  if (!Config) {
468  MACH3LOG_ERROR("Didn't find MaCh3_Config tree in MCMC file! {}", inputFile.c_str());
469  TempFile->ls();
470  throw MaCh3Exception(__FILE__ , __LINE__ );
471  }
472  MACH3LOG_INFO("Loading YAML config from MCMC chain");
473  YAML::Node Settings = TMacroToYAML(*Config);
474  bool asimovfit = GetFromManager<bool>(Settings["General"]["Asimov"], false, __FILE__ , __LINE__);
475  if (asimovfit) {
476  MACH3LOG_WARN("MCMC chain was produced from an Asimov fit");
477  MACH3LOG_WARN("ReweightMCMC does not currently handle Asimov shifting, results may be incorrect!");
478  } else {
479  MACH3LOG_INFO("Not an Asimov fit, proceeding with reweighting");
480  }
481 
482  // Open input file and get tree
483  auto inFile = std::unique_ptr<TFile>(TFile::Open(inputFile.c_str(), "READ"));
484  if (!inFile || inFile->IsZombie()) {
485  MACH3LOG_ERROR("Cannot open input file: {}", inputFile);
486  throw MaCh3Exception(__FILE__, __LINE__);
487  }
488 
489  std::unique_ptr<TTree> inTree(inFile->Get<TTree>("posteriors"));
490  if (!inTree) {
491  MACH3LOG_ERROR("Cannot find 'posteriors' tree in input file");
492  throw MaCh3Exception(__FILE__, __LINE__);
493  }
494 
495  // Create output file
496  std::string configString = configFile.substr(configFile.find_last_of('/') + 1, configFile.find_last_of('.') - configFile.find_last_of('/') - 1);
497  std::string outputFile = inputFile.substr(0, inputFile.find_last_of('.')) + "_reweighted_" + configString + ".root";
498  auto outFile = std::unique_ptr<TFile>(TFile::Open(outputFile.c_str(), "RECREATE"));
499  if (!outFile || outFile->IsZombie()) {
500  MACH3LOG_ERROR("Cannot create output file: {}", outputFile);
501  throw MaCh3Exception(__FILE__, __LINE__);
502  }
503 
504  MACH3LOG_INFO("Output file will be: {}", outputFile);
505 
506  // Copy all the remaining objects into the out file (i.e. all but posteriors tree)
507  TIter next(inFile->GetListOfKeys());
508  while (TKey* key = dynamic_cast<TKey*>(next())) {
509  inFile->cd();
510  std::unique_ptr<TObject> obj(key->ReadObj());
511  if (obj->IsA()->InheritsFrom(TDirectory::Class())) {
512  // It's a folder, create and copy its contents
513  TDirectory* srcDir = static_cast<TDirectory*>(obj.get());
514  TDirectory* destDir = outFile->mkdir(srcDir->GetName());
515  TIter nextSubKey(srcDir->GetListOfKeys());
516  while (TKey* subKey = dynamic_cast<TKey*>(nextSubKey())) {
517  srcDir->cd();
518  std::unique_ptr<TObject> subObj(subKey->ReadObj());
519  destDir->cd();
520  subObj->Write();
521  }
522  } else if (std::string(key->GetName()) != "posteriors") {
523  // Regular object, skip "posteriors" tree
524  outFile->cd();
525  obj->Write();
526  }
527  }
528 
529  // Clone the tree structure
530  outFile->cd();
531  std::unique_ptr<TTree> outTree(inTree->CloneTree(0));
532 
533  // Set up parameter reading
534  std::map<std::string, double> paramValues;
535  for (const auto& rwConfig : reweightConfigs) {
536  for (const auto& paramName : rwConfig.paramNames) {
537  if (paramValues.find(paramName) == paramValues.end()) {
538  paramValues[paramName] = 0.0;
539  // KS: Params other than Osc have branch like Param_, so we need to match it
540  auto idx = processor->GetParamIndexFromName(paramName);
541  auto BranchName = processor->GetBranchNames()[idx];
542  inTree->SetBranchAddress(BranchName, &paramValues[paramName]);
543  }
544  }
545  }
546 
547  // Add weight branches
548  std::map<std::string, double> weights;
549  std::map<std::string, TBranch*> weightBranches;
550 
551  for (const auto& rwConfig : reweightConfigs) {
552  weights[rwConfig.weightBranchName] = 1.0;
553  weightBranches[rwConfig.weightBranchName] = outTree->Branch(
554  rwConfig.weightBranchName.c_str(),
555  &weights[rwConfig.weightBranchName],
556  (rwConfig.weightBranchName + "/D").c_str());
557  MACH3LOG_INFO("Added weight branch: {}", rwConfig.weightBranchName);
558  }
559 
560  // For 2D reweight and non-gaussian (ie TGraph) 1D reweight we need to do it ourselves
561  // Process all entries
562  Long64_t nEntries = inTree->GetEntries();
563  MACH3LOG_INFO("Processing {} entries", nEntries);
564 
566  for (Long64_t i = 0; i < nEntries; ++i) {
567  if(i % (nEntries/20) == 0) M3::Utils::PrintProgressBar(i, nEntries);
568 
569  inTree->GetEntry(i);
570 
571  // Calculate weights for all configurations
572  for (const auto& rwConfig : reweightConfigs) {
573  double weight = 1.0;
574  if (rwConfig.dimension == 1) {
575  weight = Get1DWeight(rwConfig, paramValues);
576  } else if (rwConfig.dimension == 2) {
577  weight = Get2DWeight(rwConfig, paramValues);
578  }
579  weights[rwConfig.weightBranchName] = weight;
580  }
581  // Fill the output tree
582  outTree->Fill();
583  } // end loop over entries
584 
585  // Write and close
586  outFile->cd();
587  outTree->Write();
588 
589  // once we have finished the reweight save its configuration (reweightConfigNode) to the root file as a macro
590  TMacro reweightMacro;
591  reweightMacro.SetName("Reweight_Config");
592  reweightMacro.SetTitle("ReweightMCMC configuration");
593  std::stringstream ss;
594  ss << reweight_settings;
595  reweightMacro.AddLine(ss.str().c_str());
596  reweightMacro.Write();
597 
598  MACH3LOG_INFO("Reweighting completed successfully!");
599  MACH3LOG_INFO("Final reweighted file is: {}", outputFile);
600 }
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
double Get1DWeight(const ReweightConfig &rwConfig, const std::map< std::string, double > &paramValues)
Calculate 1D weight.
double Get2DWeight(const ReweightConfig &rwConfig, const std::map< std::string, double > &paramValues)
Calculate 2D weight.
bool GetParameterInfo(MCMCProcessor *processor, const std::string &paramName, double &mean, double &sigma, bool &isFlat)
Get parameter information from MCMCProcessor.
void LoadReweightingSettings(std::vector< ReweightConfig > &reweightConfigs, const YAML::Node &reweight_settings)
Load reweighting setting like 1D or 2D from YAML config.
YAML::Node TMacroToYAML(const TMacro &macro)
KS: Convert a ROOT TMacro object to a YAML node.
Definition: YamlHelper.h:152
#define M3OpenConfig(filename)
Macro to simplify calling LoadYaml with file and line info.
Definition: YamlHelper.h:589
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:229