LDMX Software
EventFile.cxx
1#include <ctime>
2
3#include "TTreeReader.h"
4
5// LDMX
6#include "Framework/Event.h"
7#include "Framework/EventFile.h"
8#include "Framework/Exception/Exception.h"
9#include "Framework/RunHeader.h"
10
11namespace framework {
12
14 const std::string &filename, EventFile *parent,
15 bool isOutputFile, bool isSingleOutput, bool isLoopable)
16 : fileName_(filename),
17 isOutputFile_(isOutputFile),
18 isSingleOutput_(isSingleOutput),
19 isLoopable_(isLoopable),
20 parent_(parent) {
21 if (isOutputFile_) {
22 // we are writting out so open the file and make sure it is writable
23 file_ = new TFile(fileName_.c_str(), "RECREATE");
24 if (!file_->IsOpen() or !file_->IsWritable()) {
25 EXCEPTION_RAISE("FileError",
26 "Output file '" + fileName_ + "' is not writable.");
27 }
28
29 // set compression settings
30 // Check out the TFile constructor for explanation of how this integer is
31 // built Short Reference: setting = 100*algorithem + level algorithm = 0
32 // ==> use global default
33 file_->SetCompressionSettings(
34 params.getParameter<int>("compressionSetting", 9));
35
36 if (parent_) {
37 // output file when there are input files
38 // might be drop/keep rules, so we should have these rules to make sure
39 // it works
40
41 // turn everything on
42 // hypothetically could turn everything off? Doesn't work for some
43 // reason?
44 preCloneRules_.emplace_back("*", true);
45
46 // except EventHeader (copies over to output)
47 preCloneRules_.emplace_back("EventHeader*", true);
48
49 // reactivate all branches so default behavior is drop
50 reactivateRules_.push_back("*");
51 }
52 } else {
53 // open file with only reading enabled
54 file_ = new TFile(fileName_.c_str());
55 // double check that file is open
56 if (!file_->IsOpen()) {
57 EXCEPTION_RAISE("FileError", "Input file '" + fileName_ +
58 "' is not readable or does not exist.");
59 }
60
61 bool skip_corrupted =
62 params.getParameter<bool>("skipCorruptedInputFiles", false);
63
64 // make sure file is not a zombie file
65 // (i.e. process ended without closing or the file was corrupted some other
66 // way)
67 if (file_->IsZombie()) {
68 if (not skip_corrupted) {
69 EXCEPTION_RAISE("FileError", "Input file '" + fileName_ +
70 "' is corrupted. Framework will not "
71 "attempt to recover this file.");
72 }
73 return;
74 }
75
76 // Get the tree name from the configuration
77 auto tree_name{params.getParameter<std::string>("tree_name")};
78 tree_ = static_cast<TTree *>(file_->Get(tree_name.c_str()));
79 if (!tree_) {
80 if (not skip_corrupted) {
81 EXCEPTION_RAISE("FileError", "File '" + fileName_ +
82 "' does not have a TTree named '" +
83 tree_name + "' in it.");
84 }
85 return;
86 }
87 entries_ = tree_->GetEntriesFast();
88 }
89
91}
92
94 const std::string &filename, bool isLoopable)
95 : EventFile(params, filename, nullptr, false, false, isLoopable) {}
96
98 const std::string &filename)
99 : EventFile(params, filename, nullptr, false, false, false) {}
100
102 const std::string &filename, EventFile *parent,
103 bool isSingleOutput)
104 : EventFile(params, filename, parent, true, isSingleOutput, false) {}
105
107 // Before an output file, the Event tree needs to be written.
108 if (isOutputFile_) {
109 // make sure we are in output file before writing
110 file_->cd();
111 tree_->Write();
112 }
113
114 // Close the file
115 file_->Close();
116}
117
119 if (isOutputFile_) return file_->IsZombie();
120 return (!tree_ or file_->IsZombie());
121}
122
123void EventFile::addDrop(const std::string &rule) {
124 int offset;
125 bool isKeep = false, isDrop = false, isIgnore = false;
126 size_t i = rule.find("keep");
127 if (i != std::string::npos) {
128 offset = i + 4;
129 isKeep = true;
130 }
131 i = rule.find("drop");
132 if (i != std::string::npos) {
133 offset = i + 4;
134 isDrop = true;
135 }
136 i = rule.find("ignore");
137 if (i != std::string::npos) {
138 offset = i + 6;
139 isIgnore = true;
140 }
141
142 // more than one of (keep,drop,ignore) was provided => not valid rule
143 if (int(isKeep) + int(isDrop) + int(isIgnore) != 1) return;
144
145 std::string srule = rule.substr(offset);
146 for (i = srule.find_first_of(" \t\n\r"); i != std::string::npos;
147 i = srule.find_first_of(" \t\n\r"))
148 srule.erase(i, 1);
149
150 // name of branch is not given
151 if (srule.length() == 0) return;
152
153 // add wild card at end for matching purposes
154 if (srule.back() != '*') srule += ".*"; // add wildcard to back
155
156 if (isKeep) {
157 // turn both the input and output tree's on
158 // root needs . removed otherwise it gets cranky
159 srule.erase(std::remove(srule.begin(), srule.end(), '.'), srule.end());
160 preCloneRules_.emplace_back(srule, true);
161 // this branch will then be copied over into output tree and be active
162 } else if (isIgnore) {
163 // don't even read it from the input file
164 // root needs . removed otherwise it gets cranky
165 srule.erase(std::remove(srule.begin(), srule.end(), '.'), srule.end());
166 preCloneRules_.emplace_back(srule, false);
167 // these branches won't be copied over into output tree
168 } else if (isDrop) {
169 // drop means allowing it on reading but not writing
170 // pass these regex to event bus so Event::add knows
171 event_->addDrop(srule); // requires event_ to be set
172
173 // root needs . removed otherwise it gets cranky
174 srule.erase(std::remove(srule.begin(), srule.end(), '.'), srule.end());
175 preCloneRules_.emplace_back(srule, false);
176 // these branches won't be copied over into output tree
177 // reactivate input branch after clone
178 reactivateRules_.push_back(srule);
179 }
180}
181
182bool EventFile::nextEvent(bool storeCurrentEvent) {
183 if (ientry_ < 0) {
184 // first entry of this file
185 if (parent_) {
186 // we have a parent file
187 if (!parent_->tree_) {
188 // this should _never_ happen
189 EXCEPTION_RAISE("EventFile", "No event tree in the file");
190 }
191 // Only clone parent tree if either
192 // 1) There is no tree setup yet (first input file)
193 // 2) This is not single output (new input file --> new output file)
194 if (!tree_ or !isSingleOutput_) {
195 // clones parent_->tree_ to our tree_ keeping drop/keep rules in mind
196 // clone tree (only copies over branches that are active on input tree)
197
198 file_->cd(); // go into output file
199
200 for (auto const &rulePair : preCloneRules_)
201 parent_->tree_->SetBranchStatus(rulePair.first.c_str(),
202 rulePair.second);
203
204 tree_ = parent_->tree_->CloneTree(0);
205
206 // reactivate any drop branches (drop) on input tree
207 for (auto const &rule : reactivateRules_)
208 parent_->tree_->SetBranchStatus(rule.c_str(), 1);
209 }
212 } // we have a parent file
213 } else {
214 // later than first entry of file
215 if (isOutputFile_) {
217 if (storeCurrentEvent) // we should store before moving on
218 tree_->Fill(); // fill the clones...
219 } // we are an output file
220
221 // the event bus may not be defined
222 // for this file if we are input file and
223 // there is an output file during this run
224 if (event_) {
225 event_->Clear();
227 } // event bus defined
228 } // first or not first entry in this file
229
230 if (parent_) {
231 // we have a parent, follow their lead
232 if (!parent_->nextEvent()) {
233 return false;
234 }
236 entries_++;
237 } else if (isOutputFile_) {
238 // we don't have a parent and we
239 // are an output file
240 // Just increment the number of entries
241 // and the index of the current entry
242 ientry_++;
243 entries_++;
244 } else {
245 // we don't have a parent and
246 // we aren't an output file
247 // try to load another entry from our tree
248 if (ientry_ + 1 >= entries_) {
249 if (isLoopable_) {
250 // reset the event counter: reuse events from start of pileup tree
251 ientry_ = -1;
252 } else
253 return false;
254 }
255 ientry_++;
256 tree_->GetEntry(ientry_);
257 }
258
259 // if we have an event_
260 // make sure it is iterated as well
261 return event_ ? event_->nextEvent() : true;
262}
263
265 event_ = evt;
266 if (isOutputFile_) {
267 // we are an output file
268 if (!tree_ && !parent_) {
269 // we don't have a tree and we don't have a parent
270 // ==> *Production Mode* create a new tree
272 ientry_ = 0;
273 entries_ = 0;
274 }
275
276 if (parent_) {
277 // we have a parent file so give
278 // the parent's tree to the event bus
279 // as the input tree
281 }
282
283 // give our tree to the event as the output tree
285 } else {
286 // we are an input file
287 // so give our tree to the event as input tree
289 } // output or input file
290}
291
292int EventFile::skipToEvent(int offset) {
293 // make sure the event number exists
294 ientry_ = offset % entries_ - 1;
295 return ientry_;
296}
297
299 parent_ = parent;
300
301 // we can assume parent_->tree_ is valid
302 // because (for input files) the tree_ is imported
303 // from the file and then checked if its valid in the
304 // EventFile constructor
305
306 // Enter output file
307 file_->cd();
308
309 // need to turn on/off the same branches as in the initial setup...
310 for (auto const &rulePair : preCloneRules_)
311 parent_->tree_->SetBranchStatus(rulePair.first.c_str(), rulePair.second);
312
313 // Copy over addresses from the new parent
314 parent_->tree_->CopyAddresses(tree_);
315
316 // and reactivate any dropping rules
317 for (auto const &rule : reactivateRules_)
318 parent_->tree_->SetBranchStatus(rule.c_str(), 1);
319
320 // Reset the entry index with the new parent index
322
323 // import run headers from new input file
325
326 return;
327}
328
330 if (not isOutputFile_) {
331 EXCEPTION_RAISE("MisCall",
332 "Cannot write the run tree on an input event file.");
333 }
334
335 // store the run map into the output tree
336 // Check for the existence of the run tree in the file.
337 // If it already exists, throw an exception.
338 // TODO: Tree name shouldn't be hardcoded. Is this check really necessary?
339 auto runTree{static_cast<TTree *>(file_->Get("LDMX_Run"))};
340 if (runTree) {
341 EXCEPTION_RAISE("RunTree",
342 "RunTree 'LDMX_Run' already exists in output file '" +
343 fileName_ + "'.");
344 }
345
356 file_->cd();
357 runTree = new TTree("LDMX_Run", "LDMX run header");
358
359 // create the branch on this tree
360 ldmx::RunHeader *theHandle = nullptr;
361 runTree->Branch("RunHeader", "ldmx::RunHeader", &theHandle, 32000, 3);
362
363 // copy over the run headers into the tree
364 for (auto &[num, header_pair] : runMap_) {
365 theHandle = header_pair.second;
366 runTree->Fill();
367 if (header_pair.first) delete header_pair.second;
368 }
369
370 runTree->Write();
371}
372
374 int runNumber = runHeader.getRunNumber();
375
376 if (runMap_.find(runNumber) != runMap_.end()) {
377 EXCEPTION_RAISE("RunMap", "Run map already contains a run with number '" +
378 std::to_string(runNumber) + "'.");
379 }
380
381 runMap_[runNumber] = std::make_pair(false, &runHeader);
382
383 return;
384}
385
387 if (runMap_.find(runNumber) != runMap_.end()) {
388 return runMap_.at(runNumber).second;
389 }
390 return nullptr;
391}
392
394 ldmx::RunHeader *rh{this->getRunHeaderPtr(runNumber)};
395 if (rh != nullptr) {
396 return *rh;
397 }
398 EXCEPTION_RAISE("RunHeader",
399 "Unable to find header for run " + std::to_string(runNumber));
400}
401
403 // choose which file to import from
404 auto theImportFile{file_}; // if this is an input file
405 if (isOutputFile_ and parent_ and parent_->file_)
406 theImportFile = parent_->file_; // output file with input parent
407 else if (isOutputFile_)
408 return; // output file, no input parent to read from
409
410 if (theImportFile) {
411 // the file exist
412 TTreeReader oldRunTree("LDMX_Run", theImportFile);
413 TTreeReaderValue<ldmx::RunHeader> oldRunHeader(oldRunTree, "RunHeader");
414 // TODO check that setup went correctly
415 while (oldRunTree.Next()) {
416 auto *oldRunHeaderPtr = oldRunHeader.Get();
417 if (oldRunHeaderPtr != nullptr) {
418 // copy input run tree into run map
419 // We should consider moving to a shared_ptr instead of 'new'
420 runMap_[oldRunHeaderPtr->getRunNumber()] =
421 std::make_pair(true, new ldmx::RunHeader(*oldRunHeader));
422 }
423 }
424 }
425
426 return;
427}
428} // namespace framework
Class implementing an event buffer system for storing event data.
This class manages all ROOT file input/output operations.
Definition EventFile.h:26
void updateParent(EventFile *parent)
Change pointer to different parent file.
bool isSingleOutput_
True if there is only one output file.
Definition EventFile.h:293
TFile * file_
The backing TFile for this EventFile.
Definition EventFile.h:299
void addDrop(const std::string &rule)
Add a rule for dropping collections from the output.
std::string fileName_
The file name.
Definition EventFile.h:287
bool isLoopable_
True if this is an input file with pileup overlay events *‍/.
Definition EventFile.h:296
std::map< int, std::pair< bool, ldmx::RunHeader * > > runMap_
Map of run numbers to RunHeader objects.
Definition EventFile.h:337
void setupEvent(Event *evt)
Set an Event object containing the event data to work with this file.
Long64_t entries_
The number of entries in the tree.
Definition EventFile.h:281
std::vector< std::string > reactivateRules_
Vector of drop rules that have been parsed and need to be used to reactivate these branches on the in...
Definition EventFile.h:325
std::vector< std::pair< std::string, bool > > preCloneRules_
Pre-clone rules.
Definition EventFile.h:316
void writeRunTree()
Write the map of run headers to the file as a TTree of RunHeader.
bool nextEvent(bool storeCurrentEvent=true)
Prepare the next event.
bool isOutputFile_
True if file is an output file being written to disk.
Definition EventFile.h:290
ldmx::RunHeader * getRunHeaderPtr(int runNumber)
Update the RunHeader for a given run, if it exists in the input file.
Long64_t ientry_
The current entry in the tree.
Definition EventFile.h:284
void writeRunHeader(ldmx::RunHeader &runHeader)
Write the run header into the run map.
~EventFile()
Destructor.
void importRunHeaders()
Fill the internal map of run numbers to RunHeader objects from the input file.
int skipToEvent(int offset)
Skip events using an offset.
ldmx::RunHeader & getRunHeader(int runNumber)
Get the RunHeader for a given run, if it exists in the input file.
EventFile(const framework::config::Parameters &params, const std::string &filename, EventFile *parent, bool isOutputFile, bool isSingleOutput, bool isLoopable)
Constructor to make a general file.
Definition EventFile.cxx:13
EventFile * parent_
A parent file containing event data.
Definition EventFile.h:305
TTree * tree_
The tree with event data.
Definition EventFile.h:302
Event * event_
The object containing the actual event data (trees and branches).
Definition EventFile.h:308
bool isCorrupted() const
Check if the file we have is corrupted.
Implements an event buffer system for storing event data.
Definition Event.h:41
TTree * createTree()
Create the output data tree.
Definition Event.cxx:102
void setOutputTree(TTree *tree)
Set the output data tree.
Definition Event.cxx:108
void beforeFill()
Action to be executed before the tree is filled.
Definition Event.cxx:156
void onEndOfEvent()
Perform end of event action (doesn't do anything right now).
Definition Event.cxx:170
void Clear()
Clear this object's data (including passengers).
Definition Event.cxx:165
bool nextEvent()
Go to the next event by retrieving the event header.
Definition Event.cxx:151
void setInputTree(TTree *tree)
Set the input data tree.
Definition Event.cxx:110
void addDrop(const std::string &exp)
Add a drop rule to the list of regex expressions to drop.
Definition Event.cxx:21
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:27
T getParameter(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:89
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:54
int getRunNumber() const
Definition RunHeader.h:74
All classes in the ldmx-sw project use this namespace.
Definition PerfDict.cxx:45