BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
rebdsimCombine.cc
Go to the documentation of this file.
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) BDSIM Collaboration, 2001 - 2026.
3
4This file is part of BDSIM.
5
6BDSIM is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published
8by the Free Software Foundation version 3 of the License.
9
10BDSIM is distributed in the hope that it will be useful, but
11WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
17*/
21#include "FileMapper.hh"
22#include "Header.hh"
23#include "HistogramAccumulatorMerge.hh"
24#include "HistogramAccumulatorSum.hh"
25#include "RBDSException.hh"
26
27#include "BDSOutputROOTEventHeader.hh"
28
29#include "TDirectory.h"
30#include "TFile.h"
31#include "TH1.h"
32#include "TH2.h"
33#include "TH3.h"
34#include "BDSBH4D.hh"
35#include "TTree.h"
36
37#include <exception>
38#include <iostream>
39#include <set>
40#include <string>
41#include <vector>
42
43int main(int argc, char* argv[])
44{
45 if (argc < 3)
46 {
47 std::cout << "usage: rebdsimCombine result.root file1.root file2.root ..." << std::endl;
48 return 1;
49 }
50
51 // build input file list
52 std::vector<std::string> inputFiles;
53 for (int i = 2; i < argc; ++i)
54 {inputFiles.emplace_back(std::string(argv[i]));}
55
56 // checks
57 if (inputFiles.size() == 1)
58 {
59 if (inputFiles[0].find('*') != std::string::npos) // glob didn't expand in shell - infer this
60 {std::cout << "Glob with * did not match any files" << std::endl;}
61 else
62 {std::cout << "Only one input file provided \"" << inputFiles[0] << "\" - no point." << std::endl;}
63 return 1;
64 }
65
66 std::set<std::string> inputFilesSet = {inputFiles.begin(), inputFiles.end()};
67 if (inputFiles.size() > inputFilesSet.size())
68 {std::cout << "Warning: at least 1 duplicate name in list of files provided to combine." << std::endl;}
69
70 std::string outputFile = std::string(argv[1]);
71 if (outputFile.find('*') != std::string::npos)
72 {
73 std::cerr << "First argument for output file \"" << outputFile << "\" contains an *." << std::endl;
74 std::cerr << "Should only be a singular file - check order of arguments." << std::endl;
75 return 1;
76 }
77
78 // output file must be opened before histograms are created because root does
79 // everything statically behind the scenes
80 TFile* output = new TFile(outputFile.c_str(), "RECREATE");
81
82 // add header for file type and version details
83 output->cd();
85 headerOut->Fill(std::vector<std::string>(), inputFiles); // updates time stamp
86 headerOut->SetFileType("REBDSIMCOMBINE");
87 TTree* headerTree = new TTree("Header", "REBDSIM Header");
88 headerTree->Branch("Header.", "BDSOutputROOTEventHeader", headerOut);
89
90 // ensure new histograms are written to file
91 TH1::AddDirectory(true);
92 TH2::AddDirectory(true);
93 TH3::AddDirectory(true);
94
95 TFile* f = nullptr; // temporary variable
96
97 // initialise file map
98 try
99 {f = new TFile(inputFiles[0].c_str(), "READ");}
100 catch (const std::exception& e)
101 {std::cerr << e.what() << std::endl; return 1;}
102 HistogramMap* histMap = nullptr;
103 try
104 {histMap = new HistogramMap(f, output);} // map out first file
105 catch (const RBDSException& error)
106 {std::cerr << error.what() << std::endl; return 1;}
107 catch (const std::exception& error)
108 {std::cerr << error.what() << std::endl; return 1;}
109
110 // copy the model tree over if it exists - expect the name to be ModelTree
111 TTree* oldModelTree = dynamic_cast<TTree*>(f->Get("ModelTree"));
112 if (!oldModelTree)
113 {oldModelTree = dynamic_cast<TTree*>(f->Get("Model"));}
114 if (oldModelTree)
115 {// TChain can be valid but TTree might not be in corrupt / bad file
116 output->cd();
117 auto newTree = oldModelTree->CloneTree();
118 newTree->SetName("ModelTree");
119 newTree->Write("", TObject::kOverwrite);
120 }
121
122 f->Close();
123 delete f;
124
125 std::vector<RBDS::HistogramPath> histograms = histMap->Histograms();
126
127 unsigned long long int nOriginalEvents = 0;
128 unsigned long long int nEventsInFile = 0;
129 unsigned long long int nEventsInFileSkipped = 0;
130 unsigned long long int nEventsRequested = 0;
131
132 std::cout << "Combination of " << inputFiles.size() << " files beginning" << std::endl;
133 // loop over files and accumulate
134 for (const auto& file : inputFiles)
135 {
136 f = new TFile(file.c_str());
138 {
139 std::cout << "Accumulating> " << file << std::endl;
140 for (const auto& hist : histograms)
141 {
142 std::string histPath = hist.path + hist.name; // histPath has trailing '/'
143
144 TH1* h = dynamic_cast<TH1*>(f->Get(histPath.c_str()));
145
146 if (!h)
147 {RBDS::WarningMissingHistogram(histPath, file); continue;}
148 hist.accumulator->Accumulate(h);
149 }
150
151 Header* h = new Header();
152 TTree* ht = (TTree*)f->Get("Header");
153 h->SetBranchAddress(ht);
154 ht->GetEntry(0);
155 nOriginalEvents += h->header->nOriginalEvents;
156 // Here we exploit the fact that the 0th entry of the header tree has no data for these
157 // two variables. There may however, only ever be 1 entry for older data. We add it up anyway.
158 for (int i = 0; i < (int)ht->GetEntries(); i++)
159 {
160 ht->GetEntry(i);
161 nEventsInFile += h->header->nEventsInFile;
162 nEventsInFileSkipped += h->header->nEventsInFileSkipped;
163 nEventsRequested += h->header->nEventsRequested;
164 }
165 delete h;
166 }
167 else
168 {std::cout << "Skipping " << file << " as not a rebdsim output file" << std::endl;}
169 f->Close();
170 delete f;
171 }
172
173 // terminate and write output
174 for (const auto& hist : histograms)
175 {
176 TH1* result = hist.accumulator->Terminate();
177 result->SetDirectory(hist.outputDir);
178 hist.outputDir->Add(result);
179 delete hist.accumulator; // this removes temporary histograms from the file
180 }
181
182 headerOut->nOriginalEvents = nOriginalEvents;
183 headerOut->nEventsInFile = nEventsInFile;
184 headerOut->nEventsInFileSkipped = nEventsInFileSkipped;
185 headerOut->nEventsRequested = nEventsRequested;
186 headerTree->Fill();
187
188 output->Write(nullptr,TObject::kOverwrite);
189 output->Close();
190 delete output;
191 std::cout << "Combined result of " << inputFiles.size() << " files written to: " << outputFile << std::endl;
192
193 return 0;
194}
Information about the software and the file.
unsigned long long int nEventsRequested
Number of events requested to be simulated from the file.
void SetFileType(const std::string &fileTypeIn)
Update the file type.
unsigned long long int nEventsInFileSkipped
Number of events from distribution file that were skipped due to filters.
unsigned long long int nOriginalEvents
Number of original events if skimmed.
void Fill(const std::vector< std::string > &analysedFilesIn=std::vector< std::string >(), const std::vector< std::string > &combinedFilesIn=std::vector< std::string >())
unsigned long long int nEventsInFile
Number of events in the input distribution file irrespective of filters.
Header loader.
Definition Header.hh:33
BDSOutputROOTEventHeader * header
Member that ROOT can map file data to locally.
Definition Header.hh:43
void SetBranchAddress(TTree *t)
Set the branch addresses to address the contents of the file.
Definition Header.cc:40
Class to map a rebdsim file structure and create duplicate in output.
Definition FileMapper.hh:94
const std::vector< RBDS::HistogramPath > & Histograms() const
Access full vector of histograms.
General exception with possible name of object and message.
const char * what() const noexcept override
Override message in std::exception.
bool IsREBDSIMOrCombineOutputFile(TFile *file)
void WarningMissingHistogram(const std::string &histName, const std::string &fileName)
Common print out method.