15#include "TFileMerger.h"
34bool ShouldSkipLine(
const std::string& line,
const std::vector<std::string>& SkipVector) {
36 for (
const auto& word : SkipVector) {
38 if (line.find(word) != std::string::npos) {
46bool CompareTwoConfigs(
const std::string& File1,
const std::string& File2,
const std::vector<std::string>& SkipVector) {
47 std::istringstream file1(File1);
48 std::istringstream file2(File2);
50 std::string line1, line2;
54 while (std::getline(file1, line1) && std::getline(file2, line2)) {
68 while (std::getline(file1, line1)) {
69 MACH3LOG_WARN(
"Extra line in {} on line {}: {}", File1, lineNumber, line1);
72 while (std::getline(file2, line2)) {
73 MACH3LOG_WARN(
"Extra line in {} on line {}: {}", File2, lineNumber, line2);
80bool checkSoftwareVersions(TFile *file, TFile *prevFile,
const std::string& ConfigName,
const std::vector<std::string>& SkipVector = {})
82 bool weirdFile =
false;
84 TMacro *versionHeader = file->Get<TMacro>(ConfigName.c_str());
85 TMacro *prevVersionHeader = prevFile->Get<TMacro>(ConfigName.c_str());
89 MACH3LOG_ERROR(
"Looks like the {} embedded config for file {} is different to the previous ones", ConfigName, file->GetName());
90 MACH3LOG_ERROR(
"This strongly suggests that this file was made with different software versions than the previous ones");
100 TDirectory *savdir = gDirectory;
101 TDirectory *adir = savdir->Get<TDirectory>(source->GetName());
105 TIter nextkey(source->GetListOfKeys());
106 while ((key =
static_cast<TKey*
>(nextkey()))) {
107 const char *classname = key->GetClassName();
108 TClass *cl = gROOT->GetClass(classname);
110 if (cl->InheritsFrom(
"TDirectory")) {
111 source->cd(key->GetName());
112 TDirectory *subdir = gDirectory;
116 }
else if (cl->InheritsFrom(
"TTree")) {
117 TTree *T = source->Get<TTree>(key->GetName());
119 TTree *newT = T->CloneTree();
123 TObject *obj = key->ReadObj();
129 adir->SaveSelf(kTRUE);
135bool CompareHistograms(
const TH1* h1,
const TH1* h2,
const std::string& histName,
const std::string& folderName)
138 MACH3LOG_ERROR(
"Null pointer passed to CompareHistograms for '{}'", histName);
142 const double int1 = h1->Integral();
143 const double int2 = h2->Integral();
144 if (std::abs(int1 - int2) > 1e-6) {
145 MACH3LOG_ERROR(
"Histogram '{}' in folder '{}' has different integrals: current = {}, previous = {}",
146 histName, folderName, int1, int2);
153bool CheckFolder(TFile* file, TFile* prevFile,
const std::string& FolderName,
const std::vector<std::string>& SkipVector = {})
155 bool mismatch =
false;
156 TDirectory* dir = file->GetDirectory(FolderName.c_str());
157 TDirectory* prevDir = prevFile->GetDirectory(FolderName.c_str());
159 if (!dir || !prevDir) {
160 MACH3LOG_ERROR(
"Could not find folder '{}' in one or both files", FolderName);
164 TIter nextKey(dir->GetListOfKeys());
167 while ((key =
static_cast<TKey*
>(nextKey()))) {
168 const std::string objName = key->GetName();
169 TObject* obj = key->ReadObj();
173 if (obj->InheritsFrom(
"TH1")) {
174 TH1* hist =
static_cast<TH1*
>(obj);
175 TH1* prevHist =
dynamic_cast<TH1*
>(prevDir->Get(objName.c_str()));
177 MACH3LOG_ERROR(
"Missing histogram '{}' in previous file (folder '{}')", objName, FolderName);
186 else if (obj->InheritsFrom(
"TMacro")) {
187 TMacro* macro =
static_cast<TMacro*
>(obj);
188 TMacro* prevMacro =
dynamic_cast<TMacro*
>(prevDir->Get(objName.c_str()));
190 MACH3LOG_ERROR(
"Missing TMacro '{}' in previous file (folder '{}')", objName, FolderName);
204 TFileMerger *fileMerger =
new TFileMerger();
207 fileMerger->AddObjectNames(
"posteriors");
208 fileMerger->AddObjectNames(
"Settings");
210 MACH3LOG_INFO(
"These objects will be merged: {}", fileMerger->GetObjectNames());
212 std::string outFileOption;
214 else outFileOption =
"CREATE";
223 TFile *prevFile =
nullptr;
228 for(uint fileId = 0; fileId <
inpFileList.size(); fileId++)
231 TFile *file =
new TFile(fileName.c_str());
233 if(file->Get<TTree>(
"posteriors")->GetEntries() == 0){
234 MACH3LOG_WARN(
"Hmmm, file {} Doesn't seem to have any entries", fileName.c_str());
235 MACH3LOG_WARN(
"That's weird but I guess there's no rule that says a file can't be empty");
236 MACH3LOG_WARN(
"I'll skip it but maybe double check that this doesn't indicate some deeper problem");
241 if(prevFile ==
nullptr) {
247 bool weirdFile =
false;
249 if(
checkSoftwareVersions(file, prevFile,
"MaCh3_Config", {
"OutputFile:",
"NSteps:"})) weirdFile =
true;
250 if(
CheckFolder(file, prevFile,
"SampleFolder")) weirdFile =
true;
251 if(
CheckFolder(file, prevFile,
"CovarianceFolder")) weirdFile =
true;
255 MACH3LOG_ERROR(
"=====================================================================================");
256 MACH3LOG_ERROR(
"This is not a great idea and could lead to weird outputs and cause some big headaches");
257 MACH3LOG_ERROR(
"further down the road. But if you reeeeally wanna do it and you know what you're");
261 MACH3LOG_ERROR(
"=====================================================================================");
265 fileMerger->AddFile(file);
271 TFile *outputFile = fileMerger->GetOutputFile();
275 TMacro *MaCh3_Config = prevFile->Get<TMacro>(
"MaCh3_Config");
277 if(MaCh3_Config != NULL) MaCh3_Config->Write();
281 bool mergeSuccess = fileMerger->PartialMerge(TFileMerger::kRegular | TFileMerger::kAll | TFileMerger::kOnlyListed);
290 outputFile =
new TFile(
OutFileName.c_str(),
"UPDATE");
293 TDirectory *MaCh3EngineDir = prevFile->Get<TDirectory>(
"MaCh3Engine");
294 TDirectory *CovarianceFolderDir = prevFile->Get<TDirectory>(
"CovarianceFolder");
295 TDirectory *SampleFolderDir = prevFile->Get<TDirectory>(
"SampleFolder");
307 MACH3LOG_INFO(
"Combine MaCh3 Chains files, very similar to hadd, but will compare embedded version info in the files to avoid accidentally combining files made with different software versions. Also avoids having a hige dump of separate version files in the output that happens with hadd.");
309 MACH3LOG_INFO(
"CombineMaCh3Chains [-h] [-c [0-9]] [-f] [-o <output file>] input1.root [input2.root, input3.root ...]");
310 MACH3LOG_INFO(
"inputX.root : names of individual spline files to combine, can specify any number, need at least one");
311 MACH3LOG_INFO(
"output file : name of combined spline file. optional: if not specified, the app will just use the first input file as the output, the same as hadd'");
312 MACH3LOG_INFO(
"-c : target compression level for the combined file, default is 1, in line with hadd");
313 MACH3LOG_INFO(
"-f : force overwrite the output file if it exists already");
328 c = getopt(argc, argv,
"o:c:hf");
330 while (optind < argc){
332 std::string fName = std::string(argv[optind]);
382int main(
int argc,
char *argv[])
int main(int argc, char *argv[])
void CopyDir(TDirectory *source)
bool CompareHistograms(const TH1 *h1, const TH1 *h2, const std::string &histName, const std::string &folderName)
Compare two histograms if they are identical.
void ParseArg(int argc, char *argv[])
bool checkSoftwareVersions(TFile *file, TFile *prevFile, const std::string &ConfigName, const std::vector< std::string > &SkipVector={})
EM: Will compare the version header contained in the two provided files and shout if they don't match...
std::vector< std::string > inpFileList
bool CompareTwoConfigs(const std::string &File1, const std::string &File2, const std::vector< std::string > &SkipVector)
bool ShouldSkipLine(const std::string &line, const std::vector< std::string > &SkipVector)
KS: This allow us to skip output name etc in config. We expect Output name will be different but this...
bool CheckFolder(TFile *file, TFile *prevFile, const std::string &FolderName, const std::vector< std::string > &SkipVector={})
Loop through TH1 and TMacro objects in FolderName in 'file' and compare with those in 'prevFile'.
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
#define _MaCh3_Safe_Include_End_
KS: Restore warning checking after including external headers.
void SetMaCh3LoggerFormat()
Set messaging format of the logger.
std::string TMacroToString(const TMacro ¯o)
KS: Convert a ROOT TMacro object to a string representation.
Custom exception class for MaCh3 errors.
void MaCh3Welcome()
KS: Prints welcome message with MaCh3 logo.