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 is_output_file, bool isSingleOutput, bool isLoopable)
16 : file_name_(filename),
17 is_output_file_(is_output_file),
18 is_single_output_(isSingleOutput),
19 is_loopable_(isLoopable),
20 parent_(parent) {
21 if (is_output_file_) {
22 // we are writting out so open the file and make sure it is writable
23 file_ = new TFile(file_name_.c_str(), "RECREATE");
24 if (!file_->IsOpen() or !file_->IsWritable()) {
25 EXCEPTION_RAISE("FileError",
26 "Output file '" + file_name_ + "' 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(params.get<int>("compressionSetting", 9));
34
35 if (parent_) {
36 // output file when there are input files
37 // might be drop/keep rules, so we should have these rules to make sure
38 // it works
39
40 // turn everything on
41 // hypothetically could turn everything off? Doesn't work for some
42 // reason?
43 pre_clone_rules_.emplace_back("*", true);
44
45 // except EventHeader (copies over to output)
46 pre_clone_rules_.emplace_back("EventHeader*", true);
47
48 // reactivate all branches so default behavior is drop
49 reactivate_rules_.push_back("*");
50 }
51 } else {
52 // open file with only reading enabled
53 file_ = new TFile(file_name_.c_str());
54 // double check that file is open
55 if (!file_->IsOpen()) {
56 EXCEPTION_RAISE("FileError", "Input file '" + file_name_ +
57 "' is not readable or does not exist.");
58 }
59
60 bool skip_corrupted = params.get<bool>("skipCorruptedInputFiles", false);
61
62 // make sure file is not a zombie file
63 // (i.e. process ended without closing or the file was corrupted some other
64 // way)
65 if (file_->IsZombie()) {
66 if (not skip_corrupted) {
67 EXCEPTION_RAISE("FileError", "Input file '" + file_name_ +
68 "' is corrupted. Framework will not "
69 "attempt to recover this file.");
70 }
71 return;
72 }
73
74 // Get the tree name from the configuration
75 auto tree_name{params.get<std::string>("tree_name")};
76 tree_ = static_cast<TTree *>(file_->Get(tree_name.c_str()));
77 if (!tree_) {
78 if (not skip_corrupted) {
79 EXCEPTION_RAISE("FileError", "File '" + file_name_ +
80 "' does not have a TTree named '" +
81 tree_name + "' in it.");
82 }
83 return;
84 }
85 entries_ = tree_->GetEntriesFast();
86 }
87
89}
90
92 const std::string &filename, bool isLoopable)
93 : EventFile(params, filename, nullptr, false, false, isLoopable) {}
94
96 const std::string &filename)
97 : EventFile(params, filename, nullptr, false, false, false) {}
98
100 const std::string &filename, EventFile *parent,
101 bool isSingleOutput)
102 : EventFile(params, filename, parent, true, isSingleOutput, false) {}
103
105 // Before an output file, the Event tree needs to be written.
106 if (is_output_file_) {
107 // make sure we are in output file before writing
108 file_->cd();
109 tree_->Write();
110 }
111
112 // Close the file
113 file_->Close();
114}
115
117 if (is_output_file_) return file_->IsZombie();
118 return (!tree_ or file_->IsZombie());
119}
120
121void EventFile::addDrop(const std::string &rule) {
122 int offset;
123 bool is_keep = false, is_drop = false, is_ignore = false;
124 size_t i = rule.find("keep");
125 if (i != std::string::npos) {
126 offset = i + 4;
127 is_keep = true;
128 }
129 i = rule.find("drop");
130 if (i != std::string::npos) {
131 offset = i + 4;
132 is_drop = true;
133 }
134 i = rule.find("ignore");
135 if (i != std::string::npos) {
136 offset = i + 6;
137 is_ignore = true;
138 }
139
140 // more than one of (keep,drop,ignore) was provided => not valid rule
141 if (int(is_keep) + int(is_drop) + int(is_ignore) != 1) return;
142
143 std::string srule = rule.substr(offset);
144 for (i = srule.find_first_of(" \t\n\r"); i != std::string::npos;
145 i = srule.find_first_of(" \t\n\r"))
146 srule.erase(i, 1);
147
148 // name of branch is not given
149 if (srule.length() == 0) return;
150
151 // add wild card at end for matching purposes
152 if (srule.back() != '*') srule += ".*"; // add wildcard to back
153
154 if (is_keep) {
155 // turn both the input and output tree's on
156 // root needs . removed otherwise it gets cranky
157 srule.erase(std::remove(srule.begin(), srule.end(), '.'), srule.end());
158 pre_clone_rules_.emplace_back(srule, true);
159 // this branch will then be copied over into output tree and be active
160 } else if (is_ignore) {
161 // don't even read it from the input file
162 // root needs . removed otherwise it gets cranky
163 srule.erase(std::remove(srule.begin(), srule.end(), '.'), srule.end());
164 pre_clone_rules_.emplace_back(srule, false);
165 // these branches won't be copied over into output tree
166 } else if (is_drop) {
167 // drop means allowing it on reading but not writing
168 // pass these regex to event bus so Event::add knows
169 event_->addDrop(srule); // requires event_ to be set
170
171 // root needs . removed otherwise it gets cranky
172 srule.erase(std::remove(srule.begin(), srule.end(), '.'), srule.end());
173 pre_clone_rules_.emplace_back(srule, false);
174 // these branches won't be copied over into output tree
175 // reactivate input branch after clone
176 reactivate_rules_.push_back(srule);
177 }
178}
179
180bool EventFile::nextEvent(bool storeCurrentEvent) {
181 if (ientry_ < 0) {
182 // first entry of this file
183 if (parent_) {
184 // we have a parent file
185 if (!parent_->tree_) {
186 // this should _never_ happen
187 EXCEPTION_RAISE("EventFile", "No event tree in the file");
188 }
189 // Only clone parent tree if either
190 // 1) There is no tree setup yet (first input file)
191 // 2) This is not single output (new input file --> new output file)
192 if (!tree_ or !is_single_output_) {
193 // clones parent_->tree_ to our tree_ keeping drop/keep rules in mind
194 // clone tree (only copies over branches that are active on input tree)
195
196 file_->cd(); // go into output file
197
198 for (auto const &rule_pair : pre_clone_rules_)
199 parent_->tree_->SetBranchStatus(rule_pair.first.c_str(),
200 rule_pair.second);
201
202 tree_ = parent_->tree_->CloneTree(0);
203
204 // reactivate any drop branches (drop) on input tree
205 for (auto const &rule : reactivate_rules_)
206 parent_->tree_->SetBranchStatus(rule.c_str(), 1);
207 }
210 } // we have a parent file
211 } else {
212 // later than first entry of file
213 if (is_output_file_) {
215 if (storeCurrentEvent) // we should store before moving on
216 tree_->Fill(); // fill the clones...
217 } // we are an output file
218
219 // the event bus may not be defined
220 // for this file if we are input file and
221 // there is an output file during this run
222 if (event_) {
223 event_->clear();
225 } // event bus defined
226 } // first or not first entry in this file
227
228 if (parent_) {
229 // we have a parent, follow their lead
230 if (!parent_->nextEvent()) {
231 return false;
232 }
234 entries_++;
235 } else if (is_output_file_) {
236 // we don't have a parent and we
237 // are an output file
238 // Just increment the number of entries
239 // and the index_ of the current entry
240 ientry_++;
241 entries_++;
242 } else {
243 // we don't have a parent and
244 // we aren't an output file
245 // try to load another entry from our tree
246 if (ientry_ + 1 >= entries_) {
247 if (is_loopable_) {
248 // reset the event counter: reuse events from start of pileup tree
249 ientry_ = -1;
250 } else
251 return false;
252 }
253 ientry_++;
254 tree_->GetEntry(ientry_);
255 }
256
257 // if we have an event_
258 // make sure it is iterated as well
259 return event_ ? event_->nextEvent() : true;
260}
261
263 event_ = evt;
264 if (is_output_file_) {
265 // we are an output file
266 if (!tree_ && !parent_) {
267 // we don't have a tree and we don't have a parent
268 // ==> *Production Mode* create a new tree
270 ientry_ = 0;
271 entries_ = 0;
272 }
273
274 if (parent_) {
275 // we have a parent file so give
276 // the parent's tree to the event bus
277 // as the input tree
279 }
280
281 // give our tree to the event as the output tree
283 } else {
284 // we are an input file
285 // so give our tree to the event as input tree
287 } // output or input file
288}
289
290int EventFile::skipToEvent(int offset) {
291 // make sure the event number exists
292 ientry_ = offset % entries_ - 1;
293 return ientry_;
294}
295
297 parent_ = parent;
298
299 // we can assume parent_->tree_ is valid
300 // because (for input files) the tree_ is imported
301 // from the file and then checked if its valid in the
302 // EventFile constructor
303
304 // Enter output file
305 file_->cd();
306
307 // need to turn on/off the same branches as in the initial setup...
308 for (auto const &rule_pair : pre_clone_rules_)
309 parent_->tree_->SetBranchStatus(rule_pair.first.c_str(), rule_pair.second);
310
311 // Copy over addresses from the new parent
312 parent_->tree_->CopyAddresses(tree_);
313
314 // and reactivate any dropping rules
315 for (auto const &rule : reactivate_rules_)
316 parent_->tree_->SetBranchStatus(rule.c_str(), 1);
317
318 // Reset the entry index_ with the new parent index_
320
321 // import run headers from new input file
323
324 return;
325}
326
328 if (not is_output_file_) {
329 EXCEPTION_RAISE("MisCall",
330 "Cannot write the run tree on an input event file.");
331 }
332
333 // store the run map into the output tree
334 // Check for the existence of the run tree in the file.
335 // If it already exists, throw an exception.
336 // TODO: Tree name shouldn't be hardcoded. Is this check really necessary?
337 auto run_tree{static_cast<TTree *>(file_->Get("LDMX_Run"))};
338 if (run_tree) {
339 EXCEPTION_RAISE("RunTree",
340 "RunTree 'LDMX_Run' already exists in output file '" +
341 file_name_ + "'.");
342 }
343
354 file_->cd();
355 run_tree = new TTree("LDMX_Run", "LDMX run header");
356
357 // create the branch on this tree
358 ldmx::RunHeader *the_handle = nullptr;
359 run_tree->Branch("RunHeader", "ldmx::RunHeader", &the_handle, 32000, 3);
360
361 // copy over the run headers into the tree
362 for (auto &[num, header_pair] : run_map_) {
363 the_handle = header_pair.second;
364 run_tree->Fill();
365 if (header_pair.first) delete header_pair.second;
366 }
367
368 run_tree->Write();
369}
370
372 int run_number = run_header.getRunNumber();
373
374 if (run_map_.find(run_number) != run_map_.end()) {
375 EXCEPTION_RAISE("RunMap", "Run map already contains a run with number '" +
376 std::to_string(run_number) + "'.");
377 }
378
379 run_map_[run_number] = std::make_pair(false, &run_header);
380
381 return;
382}
383
385 if (run_map_.find(run_number) != run_map_.end()) {
386 return run_map_.at(run_number).second;
387 }
388 return nullptr;
389}
390
392 ldmx::RunHeader *rh{this->getRunHeaderPtr(run_number)};
393 if (rh != nullptr) {
394 return *rh;
395 }
396 EXCEPTION_RAISE("RunHeader", "Unable to find header for run " +
397 std::to_string(run_number));
398}
399
401 // choose which file to import from
402 auto the_import_file{file_}; // if this is an input file
404 the_import_file = parent_->file_; // output file with input parent
405 else if (is_output_file_)
406 return; // output file, no input parent to read from
407
408 if (the_import_file) {
409 // the file exist
410 TTreeReader old_run_tree("LDMX_Run", the_import_file);
411 TTreeReaderValue<ldmx::RunHeader> old_run_header(old_run_tree, "RunHeader");
412 // TODO check that setup went correctly
413 while (old_run_tree.Next()) {
414 auto *old_run_header_ptr = old_run_header.Get();
415 if (old_run_header_ptr != nullptr) {
416 // copy input run tree into run map
417 // We should consider moving to a shared_ptr instead of 'new'
418 run_map_[old_run_header_ptr->getRunNumber()] =
419 std::make_pair(true, new ldmx::RunHeader(*old_run_header_ptr));
420 }
421 }
422 }
423
424 return;
425}
426} // 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.
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.
void setupEvent(Event *evt)
Set an Event object containing the event data to work with this file.
std::map< int, std::pair< bool, ldmx::RunHeader * > > run_map_
Map of run numbers to RunHeader objects.
Definition EventFile.h:337
Long64_t entries_
The number of entries in the tree.
Definition EventFile.h:281
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.
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
std::vector< std::pair< std::string, bool > > pre_clone_rules_
Pre-clone rules.
Definition EventFile.h:316
void writeRunHeader(ldmx::RunHeader &runHeader)
Write the run header into the run map.
bool is_loopable_
True if this is an input file with pileup overlay events *‍/.
Definition EventFile.h:296
~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.
std::string file_name_
The file name.
Definition EventFile.h:287
ldmx::RunHeader & getRunHeader(int runNumber)
Get the RunHeader for a given run, if it exists in the input file.
bool is_single_output_
True if there is only one output file.
Definition EventFile.h:293
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
std::vector< std::string > reactivate_rules_
Vector of drop rules that have been parsed and need to be used to reactivate these branches on the in...
Definition EventFile.h:325
bool is_output_file_
True if file is an output file being written to disk.
Definition EventFile.h:290
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:42
void clear()
Clear this object's data (including passengers).
Definition Event.cxx:165
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
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:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:57
int getRunNumber() const
Definition RunHeader.h:77
All classes in the ldmx-sw project use this namespace.