LDMX Software
OverlayProducer.cxx
1#include "Recon/OverlayProducer.h"
2
6
7namespace recon {
8
10 params_ = parameters;
11
12 ldmx_log(debug) << "Running configure() ";
13
14 // name of file containing events to be overlaid, and a list of collections to
15 // overlay
16 overlayFileName_ = parameters.getParameter<std::string>("overlayFileName");
17 caloCollections_ = parameters.getParameter<std::vector<std::string>>(
18 "overlayCaloHitCollections");
19 trackerCollections_ = parameters.getParameter<std::vector<std::string>>(
20 "overlayTrackerHitCollections");
21 simPassName_ = parameters.getParameter<std::string>("passName");
22 overlayPassName_ = parameters.getParameter<std::string>("overlayPassName");
23 // overlay specifics:
24 poissonMu_ = parameters.getParameter<double>("totalNumberOfInteractions");
25 doPoissonIT_ = parameters.getParameter<bool>("doPoissonIntime");
26 doPoissonOOT_ = parameters.getParameter<bool>("doPoissonOutoftime");
27 timeSigma_ = parameters.getParameter<double>("timeSpread");
28 timeMean_ = parameters.getParameter<double>("timeMean");
29 nEarlier_ = parameters.getParameter<int>("nEarlierBunchesToSample");
30 nLater_ = parameters.getParameter<int>("nLaterBunchesToSample");
31 bunchSpacing_ = parameters.getParameter<double>("bunchSpacing");
32 verbosity_ = parameters.getParameter<int>("verbosity");
33
35 if (verbosity_) {
36 ldmx_log(info) << "Got parameters \n \t overlayFileName = "
38 << "\n\t sim pass name = " << simPassName_
39 << "\n\t overlay pass name = " << overlayPassName_
40 << "\n\t overlayCaloHitCollections = ";
41 for (const auto &coll : caloCollections_) ldmx_log(info) << coll << "; ";
42
43 ldmx_log(info) << "\n\t overlayTrackerHitCollections = ";
44 for (const std::string &coll : trackerCollections_)
45 ldmx_log(info) << coll << "; ";
46
47 ldmx_log(info) << "\n\t numberOverlaidInteractions = " << poissonMu_
48 << "\n\t nEarlierBunchesToSample = " << nEarlier_
49 << "\n\t nLaterBunchesToSample = " << nLater_
50 << "\n\t bunchSpacing = " << bunchSpacing_
51 << "\n\t doPoissonIntime = " << doPoissonIT_
52 << "\n\t doPoissonOutoftime = " << doPoissonOOT_
53 << "\n\t timeSpread = " << timeSigma_
54 << "\n\t timeMean = " << timeMean_
55 << "\n\t verbosity = " << verbosity_;
56 }
57 return;
58}
59
62 if (rndm_.get() == nullptr) {
63 // not been seeded yet, get it from RNSS
64 const auto &rnss = getCondition<framework::RandomNumberSeedService>(
66 rndm_ = std::make_unique<TRandom2>(rnss.getSeed("OverlayProducer::rndm"));
67 }
68
69 int start_event = rndm_->Uniform(20., 1e4);
70 // EventFile::skipToEvent handles actual number of events in file
71 int evNb = overlayFile_->skipToEvent(start_event);
72 if (evNb < 0) {
73 EXCEPTION_RAISE("BadRead", "Couldn't read to starting offset.");
74 }
76 ldmx_log(info) << "Starting overlay process with pileup event number " << evNb
77 << " (random event number picked was " << start_event << ").";
78}
79
81 // event is the incoming, simulated event/"hard" process
82 // overlayEvent_ is the overlay producer's own event.
83 if (verbosity_ > 1) {
84 ldmx_log(info) << "produce() starts on simulation event "
85 << event.getEventHeader().getEventNumber();
86 }
87
88 if (rndmTime_.get() == nullptr) {
89 // not been seeded yet, get it from RNSS
90 const auto &rnss = getCondition<framework::RandomNumberSeedService>(
92 rndmTime_ =
93 std::make_unique<TRandom2>(rnss.getSeed("OverlayProducer::rndmTime"));
94 }
95
96 // using nextEvent to loop, we need to loop over overlay events and in an
97 // inner loop, loop over collections, and store them. after all pileup events
98 // have been added, the vector of collections is iterated over and added to
99 // the event bus.
100 std::map<std::string, std::vector<ldmx::SimCalorimeterHit>> caloCollectionMap;
101 std::map<std::string, std::vector<ldmx::SimTrackerHit>> trackerCollectionMap;
102 std::map<int, ldmx::SimCalorimeterHit> hitMap;
103
104 // start by copying over all the collections from the sim event
105
106 /* ----------- first do the SimCalorimeterHits ----------- */
107
108 // get the calo hits collections that we want to overlay, by looping over
109 // the list of collections passed to the producer : caloCollections_
110 for (const auto &collName : caloCollections_) {
111 // for now, Ecal and only Ecal uses contribs instead of multiple
112 // SimHitsCalo per channel, meaning, it requires special treatment
113 auto needsContribsAdded{collName.find("Ecal") != std::string::npos ? true
114 : false};
115
116 // start out by just copying the sim hits, unaltered.
117 auto simHitsCalo =
118 event.getCollection<ldmx::SimCalorimeterHit>(collName, simPassName_);
119 // but don't copy ecal hits immediately: for them, wait until overlay
120 // contribs have been added. then add everything through the hitmap
121 if (!needsContribsAdded) {
122 caloCollectionMap[collName + "Overlay"] = simHitsCalo;
123 }
124
125 if (verbosity_ > 2) {
126 ldmx_log(debug) << "in loop: start of collection " << collName
127 << "in loop: printing current sim event: ";
128 }
129 ldmx_log(debug) << "in loop: size of sim hits vector " << collName << " is "
130 << simHitsCalo.size();
131
132 // we don't need to touch the hard process sim hits, really... but we
133 // might need the simhits in the hit map.
134 if (needsContribsAdded || verbosity_ > 2) {
135 for (const ldmx::SimCalorimeterHit &simHit : simHitsCalo) {
136 if (verbosity_ > 2) simHit.Print();
137
138 if (needsContribsAdded) {
139 // this copies the hit, its ID and its coordinates directly
140 hitMap[simHit.getID()] = simHit;
141 }
142
143 } // over calo simhit collection
144 } // if needContribs or very verbose
145
146 } // over calo collections for sim event
147
148 /* ----------- now do the same with SimTrackerHits! ----------- */
149
150 // get the SimTrackerHit collections that we want to overlay, by looping
151 // over the list of collections passed to the producer : trackerCollections_
152 for (const auto &collName : trackerCollections_) {
153 auto simHitsTracker =
154 event.getCollection<ldmx::SimTrackerHit>(collName, simPassName_);
155 trackerCollectionMap[collName + "Overlay"] = simHitsTracker;
156
157 // the rest is printouts for debugging
158 ldmx_log(debug) << "in loop: size of sim hits vector " << collName << " is "
159 << simHitsTracker.size();
160
161 if (verbosity_ > 2) {
162 ldmx_log(debug) << "in loop: start of collection " << collName
163 << "in loop: printing current sim event: ";
164
165 for (const ldmx::SimTrackerHit &simHit : simHitsTracker) simHit.Print();
166 } // if high verbosity
167 } // over tracker collections for sim event
168
169 /* ----------- now do the pileup overlay ----------- */
170
171 // we could shift these by a random number, effectively placing the
172 // sim event at random positions in the interval, preserving the
173 // overall interval length
174 // int simBunch= (int)rndmTime_->Uniform(
175 // -(nEarlier_+1) , nLater_+1); // +1 to get
176 // inclusive interval
177 int startBunch = -nEarlier_;
178 int endBunch = nLater_;
179
180 // TODO -- figure out if we should also randomly shift the time of the sim
181 // event (likely only needed if time bias gets picked up by BDT or ML by way
182 // of pulse behaviour)
183 for (int bunchOffset{startBunch}; bunchOffset <= endBunch; bunchOffset++) {
184 // sample a poisson distribution, or use mu as fixed number of overlay
185 // events
186 int nEvsOverlay =
187 doPoissonOOT_ ? rndm_->Poisson(poissonMu_) : (int)poissonMu_;
188
189 // special case: in-time pileup at bunch 0
190 if (bunchOffset == 0) {
191 if (!doPoissonIT_)
192 nEvsOverlay = (int)poissonMu_; // fix it to the average
193 else if (doPoissonIT_ && !doPoissonOOT_) // then we haven't set this yet
194 nEvsOverlay = rndm_->Poisson(poissonMu_);
195
196 // paticularly useful in the poisson fluctuated case
197 event.getEventHeader().setIntParameter("inTimePU", nEvsOverlay);
198
199 // the total number of events is nPU + 1 (it includes the sim event)
200 nEvsOverlay -= 1; // in any case, subtract the sim event from nOverlay
201 if (verbosity_ > 2) {
202 ldmx_log(debug) << "will overlay " << nEvsOverlay
203 << " events on the simulated one";
204 }
205 }
206
207 // get event wherever nextEvent() left us
208 if (!&overlayEvent_) {
209 ldmx_log(error) << "No overlay event!";
210 return;
211 }
212
213 float bunchTimeOffset = bunchSpacing_ * bunchOffset;
214
215 for (int iEv = 0; iEv < nEvsOverlay; iEv++) {
222 if (!overlayFile_->nextEvent()) {
223 ldmx_log(error) << "At sim event "
224 << event.getEventHeader().getEventNumber()
225 << ": couldn't read next overlay event!";
226 return;
227 }
228
229 // a pileup event wide time offset to be applied to all its hits.
230 float timeOffset = rndmTime_->Gaus(timeMean_, timeSigma_);
231 timeOffset += bunchTimeOffset;
232
233 if (verbosity_ > 2) {
234 ldmx_log(debug) << "in overlay loop: overlaying event "
236 << "which is " << iEv + 1 << " out of " << nEvsOverlay
237 << "\n\thit time offset is " << timeOffset << " ns"
238 << "\n\tbunch position offset is " << bunchOffset
239 << ", leading to a total time offset of "
240 << bunchTimeOffset << " ns";
241 }
242
243 /* ----------- first do the SimCalorimeterHits overlay ----------- */
244
245 // again get the calo hits collections that we want to overlay
246 for (uint iColl = 0; iColl < caloCollections_.size(); iColl++) {
247 // for now, Ecal and only Ecal uses contribs
248 bool needsContribsAdded = false;
249 if (strstr(caloCollections_[iColl].c_str(), "Ecal"))
250 needsContribsAdded = true;
251
252 std::vector<ldmx::SimCalorimeterHit> overlayHits =
255
256 ldmx_log(debug) << "in loop: size of overlay hits vector is "
257 << overlayHits.size();
258
259 std::string outCollName = caloCollections_[iColl] + "Overlay";
260
261 if (verbosity_ > 2) {
262 ldmx_log(debug) << "in loop: printing overlay event: ";
263 }
264
265 for (ldmx::SimCalorimeterHit &overlayHit : overlayHits) {
266 if (verbosity_ > 2) overlayHit.Print();
267
268 const float overlayTime = overlayHit.getTime() + timeOffset;
269 overlayHit.setTime(overlayTime);
270
271 if (needsContribsAdded) { // special treatment for (for now only)
272 // ecal
273 int overlayHitID = overlayHit.getID();
274 if (hitMap.find(overlayHitID) ==
275 hitMap.end()) { // there wasn't already a simhit in this id
276 hitMap[overlayHitID] = ldmx::SimCalorimeterHit();
277 hitMap[overlayHitID].setID(overlayHitID);
278 std::vector<float> hitPos = overlayHit.getPosition();
279 hitMap[overlayHitID].setPosition(hitPos[0], hitPos[1], hitPos[2]);
280 }
281 // add the overlay hit (as a) contrib
282 // incidentID = -1000, trackID = -1000, pdgCode = 0 <-- these are
283 // set in the header for now but could be parameters
284 hitMap[overlayHitID].addContrib(overlayIncidentID_, overlayTrackID_,
285 overlayPdgCode_,
286 overlayHit.getEdep(), overlayTime);
287 } // if add overlay as contribs
288 else {
289 caloCollectionMap[outCollName].push_back(overlayHit);
290 if (verbosity_ > 2)
291 ldmx_log(debug) << "Adding non-Ecal overlay hit to outhit vector "
292 << outCollName;
293 }
294 } // over overlay calo simhit collection
295
296 if (!needsContribsAdded)
297 ldmx_log(debug) << "Nhits in overlay collection " << outCollName
298 << ": " << caloCollectionMap[outCollName].size();
299
300 } // over caloCollections
301
302 /* ----------- now do simtracker hits overlay ----------- */
303
304 // get the SimTrackerHit collections that we want to overlay
305 for (const auto &coll : trackerCollections_) {
306 auto overlayTrackerHits{
309
310 ldmx_log(debug) << "in loop: size of overlay hits vector is "
311 << overlayTrackerHits.size();
312
313 auto outCollName{coll + "Overlay"};
314
315 if (verbosity_ > 2) {
316 ldmx_log(debug) << "in loop: printing overlay event: ";
317 }
318
319 for (auto &overlayHit : overlayTrackerHits) {
320 auto overlayTime{overlayHit.getTime() + timeOffset};
321 overlayHit.setTime(overlayTime);
322 trackerCollectionMap[outCollName].push_back(overlayHit);
323
324 if (verbosity_ > 2) {
325 overlayHit.Print();
326 ldmx_log(debug) << "Adding tracker overlay hit to outhit vector "
327 << outCollName;
328 } // verbose
329 } // over overlay tracker simhit collection
330
331 ldmx_log(debug) << "Nhits in overlay collection " << outCollName << ": "
332 << trackerCollectionMap[outCollName].size();
333
334 } // over trackerCollections
335
336 } // over overlay events
337 } // over bunches
338
339 // after all events are done, the ecal hitmap is final and can be written to
340 // the event output
341 for (uint iColl = 0; iColl < caloCollections_.size(); iColl++) {
342 // loop through collection names to find the right collection name
343 // add overlaid ecal hits as contribs/from hitmap rather than as copied
344 // simhits
345 if (strstr(caloCollections_[iColl].c_str(), "Ecal")) {
346 if (verbosity_ > 2)
347 ldmx_log(debug) << "Hits in hitmap after overlay of "
348 << caloCollections_[iColl] << "Overlay :";
349
350 for (auto &mapHit : hitMap) {
351 if (verbosity_ > 2) mapHit.second.Print();
352
353 if (caloCollectionMap.find(caloCollections_[iColl] + "Overlay") ==
354 caloCollectionMap.end()) {
355 ldmx_log(debug) << "Adding first hit from hit map as first outhit "
356 "vector to caloCollectionMap";
357 caloCollectionMap[caloCollections_[iColl] + "Overlay"] = {
358 mapHit.second};
359 } else
360 caloCollectionMap[caloCollections_[iColl] + "Overlay"].push_back(
361 mapHit.second);
362 }
363 break; // for now we only have one hitMap: for Ecal. so no need looking
364 // further after we got a match
365 } // isEcal
366 } // second loop over collections, to collect hits from hitmap
367
368 // done collecting hits.
369
370 // this should be added to the sim file, so to "event"
371 // once for each hit type
372 for (auto &[name, coll] : caloCollectionMap) {
373 ldmx_log(debug) << "Writing " << name << " to event bus.";
374 if (verbosity_ > 2) {
375 ldmx_log(debug) << "List of hits added: ";
376 for (auto &hit : coll) hit.Print();
377 }
378 event.add(name, coll);
379 }
380 for (auto &[name, coll] : trackerCollectionMap) {
381 ldmx_log(debug) << "Writing " << name << " to event bus.";
382 if (verbosity_ > 2) {
383 ldmx_log(debug) << "List of hits added: ";
384 for (auto &hit : coll) hit.Print();
385 }
386 event.add(name, coll);
387 }
388 return;
389}
390
392 if (verbosity_ > 2) {
393 ldmx_log(debug) << "onProcessStart() ";
394 }
395
396 // replace by this line once the corresponding tweak to EventFile is ready:
398 std::make_unique<framework::EventFile>(params_, overlayFileName_, true);
399 overlayFile_->setupEvent(&overlayEvent_);
400 // we update the iterator at the end of each event. so do this once here to
401 // grab the first event in the processor
402
403 if (verbosity_ > 2) {
404 ldmx_log(debug) << "onProcessStart () successful. Used input file: "
405 << overlayFile_->getFileName();
406 ldmx_log(debug) << "onProcessStart () successful. Got event info: ";
407 overlayFile_->getEvent()->Print();
408 }
409
410 return;
411}
412
413} // namespace recon
414
415DECLARE_PRODUCER_NS(recon, OverlayProducer)
#define DECLARE_PRODUCER_NS(NS, CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Conditions object for random number seeds.
Class which stores simulated calorimeter hit information.
Class which encapsulates information from a hit in a simulated tracking detector.
Implements an event buffer system for storing event data.
Definition Event.h:41
const std::vector< ContentType > & getCollection(const std::string &collectionName, const std::string &passName="") const
Get a collection (std::vector) of objects from the event bus.
Definition Event.h:386
ldmx::EventHeader & getEventHeader()
Get the event header.
Definition Event.h:58
static const std::string CONDITIONS_OBJECT_NAME
Conditions object name.
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
void setEventNumber(int eventNumber)
Set the event number.
int getEventNumber() const
Return the event number.
Definition EventHeader.h:78
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:54
Stores simulated calorimeter hit information.
void Print() const
Print out the object.
Represents a simulated tracker hit in the simulation.
void Print() const
Print a description of this object.
std::string simPassName_
To use for finding the sim event bus passengers, mostly a disambiguation.
std::unique_ptr< framework::EventFile > overlayFile_
Pileup overlay events input file.
std::string overlayPassName_
Pileup overlay events input pass name.
std::string overlayFileName_
Pileup overlay events input file name.
std::vector< std::string > caloCollections_
List of SimCalorimeterHit collection(s) to loop over and add hits from, combining sim and pileup.
int verbosity_
Local control of processor verbosity.
int overlayIncidentID_
For Ecal, overlay hits should be added as contribs.
std::unique_ptr< TRandom2 > rndmTime_
Random number generator for pileup event time offset.
int nEarlier_
Number of bunches before the sim event to pull pileup events from.
void onNewRun(const ldmx::RunHeader &) override
At the start of the run, the pileup overlay file is set up, and the starting event number is chosen,...
double bunchSpacing_
Spacing in time (in [ns]) between electron bunches.
double timeMean_
Average position in time (in [ns]) of pileup bunches, relative to the sim event.
framework::config::Parameters params_
The parameters used to configure this producer.
double poissonMu_
(average) total number of events
framework::Event overlayEvent_
The overlay ldmx event bus.
int nLater_
Number of bunches after the sim event to pull pileup events from.
bool doPoissonIT_
Let the total number of in-time events be poisson distributed, or fix at the chosen value,...
std::unique_ptr< TRandom2 > rndm_
Random number generator for number of overlaid events.
void produce(framework::Event &event) override
Based on the list of collections to overlay, and the desired number of events, loop through all relev...
double timeSigma_
Width of pileup bunch spread in time (in [ns]), specified as a sigma of a Gaussian distribution.
void configure(framework::config::Parameters &parameters) override
Configure the processor with input parameters from the python cofig.
bool doPoissonOOT_
Let the total number of out-of-time events be poisson distributed, or fix at the chosen value,...
void onProcessStart() override
At the start of processing, the pileup overlay file is set up.
std::vector< std::string > trackerCollections_
List of SimTrackerHit collection(s) to loop over and add hits from, combining sim and pileup.