LArSoft  v10_06_00
Liquid Argon Software toolkit - https://larsoft.org/
OpDetBacktrackerRecord.cxx
Go to the documentation of this file.
1 //
7 // based on the SimChannel object by seligman@nevis.columbia.edu
10 
11 #include <algorithm> // std::lower_bound(), std::max()
12 #include <iostream>
13 #include <limits> // std::numeric_limits
14 #include <map>
15 #include <stdexcept>
16 #include <utility>
17 
21 
23 namespace sim {
24 
26  // std::map<timePDclock_t, std::vector<sim::SDP>> & timePDclockSDPs,
27  TrackID_t trackID,
28  timePDclock_t iTimePDclock,
29  double numberPhotons,
30  double const* xyz,
31  double energy)
32  {
36  // look at the collection to see if the current iTimePDclock already
37  // exists, if not, add it, if so, just add a new track id to the
38  // vector, or update the information if track is already present
39 
40  // no photons? no good!
41  if ((numberPhotons < std::numeric_limits<double>::epsilon()) ||
42  (energy <= std::numeric_limits<double>::epsilon())) {
43  // will throw
44  MF_LOG_ERROR("OpDetBacktrackerRecord")
45  << "AddTrackPhotons() trying to add to iTimePDclock #" << iTimePDclock << " "
46  << numberPhotons << " photons with " << energy
47  << " MeV of energy from track ID=" << trackID;
48  return;
49  } // if no photons
50 
51  int rounded_time = std::round(iTimePDclock);
52 
53  auto itr = fTimePDclockSDPs.lower_bound(rounded_time);
54 
55  // check if this iTimePDclock value is in the vector, it is possible that
56  // the lower bound is different from the given TimePDclock, in which case
57  // we need to add something for that TimePDclock
58  if (itr == fTimePDclockSDPs.end() || fTimePDclockSDPs.key_comp()(rounded_time, itr->first)) {
59  fTimePDclockSDPs.insert(
60  {rounded_time,
61  std::vector<sim::SDP>{sim::SDP(trackID, numberPhotons, energy, xyz[0], xyz[1], xyz[2])}});
62  }
63  else { // we have that iTimePDclock already; itr points to it
64 
65  // loop over the SDP vector for this iTimePDclock and add the electrons
66  // to the entry with the same track id
67  for (auto& sdp : itr->second) {
68 
69  if (sdp.trackID != trackID) continue;
70 
71  // make a weighted average for the location information
72  double weight = sdp.numPhotons + numberPhotons;
73  sdp.x = (sdp.x * sdp.numPhotons + xyz[0] * numberPhotons) / weight;
74  sdp.y = (sdp.y * sdp.numPhotons + xyz[1] * numberPhotons) / weight;
75  sdp.z = (sdp.z * sdp.numPhotons + xyz[2] * numberPhotons) / weight;
76  sdp.numPhotons = weight;
77  sdp.energy = sdp.energy + energy;
78 
79  // found the track id we wanted, so return;
80  return;
81  } // for
82 
83  // if we never found the track id, then this is the first instance of
84  // the track id for this TimePDclock, so add sdp to the vector
85  itr->second.emplace_back(trackID, numberPhotons, energy, xyz[0], xyz[1], xyz[2]);
86 
87  } // end of else "We have that iTimePDclock already"
88 
89  } // OBTRHelper::AddIonizationElectrons()
90 
91  //-------------------------------------------------
92  SDP::SDP() //SDP stands for Scintillation Deposited Photons
93  : trackID(util::kBogusI)
94  , numPhotons(util::kBogusD)
95  , energy(util::kBogusD)
96  , x(util::kBogusD)
97  , y(util::kBogusD)
98  , z(util::kBogusD)
99  {}
100 
101  //-------------------------------------------------
102  SDP::SDP(sim::SDP const& sdp, int offset) : SDP(sdp)
103  {
104  trackID += offset;
105  }
106 
107  // Default constructor
108  //-------------------------------------------------
110  : iOpDetNum(
111  -1) //set an impossible channel number in the case where this is called without an opticalchannel.
112  //The reason for doing this is to follow the structure of SimChannel, which uses kBogusChannel
113  {}
114 
115  //-------------------------------------------------
117 
118  //-------------------------------------------------
120  {
121  auto& sdp_map = helper.fTimePDclockSDPs;
122  for (auto& [channel, vec] : sdp_map) {
123 
124  // std::cout << "Moving SDP vector for channel " << channel << " with size " << vec.size() << std::endl;
125  timePDclockSDPs.emplace_back((double)channel, std::move(vec));
126  // std::cout << "Moved " << vec.size() << " " << timePDclockSDPs.back().second.size() << std::endl;
127  }
128  }
129 
130  //-------------------------------------------------
132  timePDclock_t iTimePDclock,
133  double numberPhotons,
134  double const* xyz,
135  double energy)
136  {
140  // look at the collection to see if the current iTimePDclock already
141  // exists, if not, add it, if so, just add a new track id to the
142  // vector, or update the information if track is already present
143 
144  // no photons? no good!
145  if ((numberPhotons < std::numeric_limits<double>::epsilon()) ||
146  (energy <= std::numeric_limits<double>::epsilon())) {
147  // will throw
148  MF_LOG_ERROR("OpDetBacktrackerRecord")
149  << "AddTrackPhotons() trying to add to iTimePDclock #" << iTimePDclock << " "
150  << numberPhotons << " photons with " << energy
151  << " MeV of energy from track ID=" << trackID;
152  return;
153  } // if no photons
154 
155  // int rounded_time = std::round(iTimePDclock);
156 
157  auto itr = findClosestTimePDclockSDP(iTimePDclock);
158  // auto itr = timePDclockSDPs.lower_bound(rounded_time);
159 
160  // check if this iTimePDclock value is in the vector, it is possible that
161  // the lower bound is different from the given TimePDclock, in which case
162  // we need to add something for that TimePDclock
163  // if (itr == timePDclockSDPs.end() || timePDclockSDPs.key_comp()(rounded_time, itr->first)) {
164  // timePDclockSDPs.insert(
165  // {rounded_time,
166  // std::vector<sim::SDP>{sim::SDP(trackID, numberPhotons, energy, xyz[0], xyz[1], xyz[2])}});
167  if (itr == timePDclockSDPs.end() || (abs(itr->first - iTimePDclock) > .50)) {
168  //itr->first != iTimePDclock){
169  std::vector<sim::SDP> sdplist;
170  sdplist.emplace_back(trackID, numberPhotons, energy, xyz[0], xyz[1], xyz[2]);
171  timePDclockSDPs.emplace(itr, std::round(iTimePDclock), std::move(sdplist));
172  }
173  else { // we have that iTimePDclock already; itr points to it
174 
175  // loop over the SDP vector for this iTimePDclock and add the electrons
176  // to the entry with the same track id
177  for (auto& sdp : itr->second) {
178 
179  if (sdp.trackID != trackID) continue;
180 
181  // make a weighted average for the location information
182  double weight = sdp.numPhotons + numberPhotons;
183  sdp.x = (sdp.x * sdp.numPhotons + xyz[0] * numberPhotons) / weight;
184  sdp.y = (sdp.y * sdp.numPhotons + xyz[1] * numberPhotons) / weight;
185  sdp.z = (sdp.z * sdp.numPhotons + xyz[2] * numberPhotons) / weight;
186  sdp.numPhotons = weight;
187  sdp.energy = sdp.energy + energy;
188 
189  // found the track id we wanted, so return;
190  return;
191  } // for
192 
193  // if we never found the track id, then this is the first instance of
194  // the track id for this TimePDclock, so add sdp to the vector
195  itr->second.emplace_back(trackID, numberPhotons, energy, xyz[0], xyz[1], xyz[2]);
196 
197  } // end of else "We have that iTimePDclock already"
198 
199  } // OpDetBacktrackerRecord::AddIonizationElectrons()
200 
201  //-------------------------------------------------
202  double OpDetBacktrackerRecord::Photons(timePDclock_t iTimePDclock) const //Number of photons
203  {
204  double numPhotons = 0.;
205 
206  auto itr = findClosestTimePDclockSDP(iTimePDclock);
207  // auto itr = timePDclockSDPs.lower_bound(iTimePDclock);
208  // check to see if this iTimePDclock value is in the map
209  if (itr != timePDclockSDPs.end() && itr->first == iTimePDclock) {
210 
211  // loop over the list for this iTimePDclock value and add up
212  // the total number of electrons
213  for (auto sdp : itr->second) {
214  numPhotons += sdp.numPhotons;
215  } // end loop over sim::SDP for this TimePDclock
216 
217  } // end if this iTimePDclock is represented in the map
218 
219  return numPhotons;
220  }
221 
222  //-------------------------------------------------
223  //Energy in each object is the energy deposited by the track step that left the photons.
225  {
226  double energy = 0.;
227 
228  auto itr = findClosestTimePDclockSDP(iTimePDclock);
229  // auto itr = timePDclockSDPs.lower_bound(iTimePDclock);
230 
231  // check to see if this iTimePDclock value is in the map
232  if (itr != timePDclockSDPs.end() && itr->first == iTimePDclock) {
233 
234  // loop over the list for this iTimePDclock value and add up
235  // the total number of photons
236  for (auto sdp : itr->second) {
237  energy += sdp.energy;
238  } // end loop over sim::SDP for this TimePDclock
239 
240  } // end if this iTimePDclock is represented in the map
241 
242  return energy;
243  }
244 
245  //-----------------------------------------------------------------------
246  // the start and end iTimePDclock values are assumed to be inclusive
248  timePDclock_t startTimePDclock,
249  timePDclock_t endTimePDclock) const
250  {
251  // make a map of track ID values to sim::SDP objects
252 
253  if (startTimePDclock > endTimePDclock) {
254  mf::LogWarning("OpDetBacktrackerRecord")
255  << "requested TimePDclock range is bogus: " << startTimePDclock << " " << endTimePDclock
256  << " return empty vector";
257  return {}; // returns an empty vector
258  }
259 
260  std::map<TrackID_t, sim::SDP> idToSDP;
261 
262  //find the lower bound for this iTimePDclock and then iterate from there
263  auto itr = findClosestTimePDclockSDP(startTimePDclock);
264  // auto itr = timePDclockSDPs.lower_bound(startTimePDclock);
265 
266  while (itr != timePDclockSDPs.end()) {
267 
268  // check the iTimePDclock value for the iterator, break the loop if we
269  // are outside the range
270  if (itr->first > endTimePDclock) break;
271 
272  // grab the vector of SDPs for this TimePDclock
273  auto const& sdplist = itr->second;
274  // now loop over them and add their content to the map
275  for (auto const& sdp : sdplist) {
276  auto itTrkSDP = idToSDP.find(sdp.trackID);
277  if (itTrkSDP != idToSDP.end()) {
278  // the SDP we are going to update:
279  sim::SDP& trackSDP = itTrkSDP->second;
280 
281  double const nPh1 = trackSDP.numPhotons;
282  double const nPh2 = sdp.numPhotons;
283  double const weight = nPh1 + nPh2;
284 
285  // make a weighted average for the location information
286  trackSDP.x = (sdp.x * nPh2 + trackSDP.x * nPh1) / weight;
287  trackSDP.y = (sdp.y * nPh2 + trackSDP.y * nPh1) / weight;
288  trackSDP.z = (sdp.z * nPh2 + trackSDP.z * nPh1) / weight;
289  trackSDP.numPhotons = weight;
290  } // end if the track id for this one is found
291  else {
292  idToSDP[sdp.trackID] = sim::SDP(sdp);
293  }
294  } // end loop over vector
295 
296  ++itr;
297  } // end loop over iTimePDclock values
298 
299  // now fill the vector with the sdps from the map
300  std::vector<sim::SDP> sdps;
301  sdps.reserve(idToSDP.size());
302  for (auto const& itr : idToSDP) {
303  sdps.push_back(itr.second);
304  }
305 
306  return sdps;
307  }
308 
309  //-----------------------------------------------------------------------
310  // the start and end iTimePDclock values are assumed to be inclusive
311  std::vector<sim::TrackSDP> OpDetBacktrackerRecord::TrackSDPs(timePDclock_t startTimePDclock,
312  timePDclock_t endTimePDclock) const
313  {
314 
315  std::vector<sim::TrackSDP> trackSDPs;
316 
317  if (startTimePDclock > endTimePDclock) {
318  mf::LogWarning("OpDetBacktrackerRecord::TrackSDPs")
319  << "requested iTimePDclock range is bogus: " << startTimePDclock << " " << endTimePDclock
320  << " return empty vector";
321  return trackSDPs;
322  }
323 
324  double totalPhotons = 0.;
325  std::vector<sim::SDP> const sdps = TrackIDsAndEnergies(startTimePDclock, endTimePDclock);
326  for (auto const& sdp : sdps)
327  totalPhotons += sdp.numPhotons;
328 
329  // protect against a divide by zero below
330  if (totalPhotons < 1.e-5) totalPhotons = 1.;
331 
332  // loop over the entries in the map and fill the input vectors
333  for (auto const& sdp : sdps) {
334  if (sdp.trackID == sim::NoParticleId) continue;
335  trackSDPs.emplace_back(sdp.trackID, sdp.numPhotons / totalPhotons, sdp.numPhotons);
336  }
337 
338  return trackSDPs;
339  }
340 
341  //-----------------------------------------------------------------------
342  // Merge the collection of SDPs from one sim channel to another.
343  // Requires an agreed upon offset for G4 trackID
344  std::pair<OpDetBacktrackerRecord::TrackID_t, OpDetBacktrackerRecord::TrackID_t>
346  int offset)
347  {
348  if (this->OpDetNum() != channel.OpDetNum())
349  throw std::runtime_error(
350  "ERROR OpDetBacktrackerRecord Merge: Trying to merge different channels!");
351 
352  std::pair<TrackID_t, TrackID_t> range_trackID(std::numeric_limits<int>::max(),
353  std::numeric_limits<int>::min());
354 
355  // for (auto const& [iTimePDclock, sdps] : channel.timePDclockSDPsMap()) {
356  for (auto const& itr : channel.timePDclockSDPsMap()) {
357 
358  auto iTimePDclock = itr.first;
359  auto const& sdps = itr.second;
360  // find the entry from this OpDetBacktrackerRecord corresponding to the iTimePDclock from the other
361  // auto itrthis = timePDclockSDPs.lower_bound(iTimePDclock);
362  auto itrthis = findClosestTimePDclockSDP(iTimePDclock);
363 
364  // pick which SDP list we have to fill: new one or existing one
365  std::vector<sim::SDP>* curSDPVec;
366  if (itrthis == timePDclockSDPs.end() || itrthis->first != iTimePDclock) {
367  timePDclockSDPs.emplace_back(iTimePDclock, std::vector<sim::SDP>());
368  curSDPVec = &(timePDclockSDPs.back().second);
369  // curSDPVec =
370  // &(timePDclockSDPs.insert({iTimePDclock, std::vector<sim::SDP>()}).first->second);
371  }
372  else
373  curSDPVec = &(itrthis->second);
374 
375  for (auto const& sdp : sdps) {
376  curSDPVec->emplace_back(sdp, offset);
377  if (sdp.trackID + offset < range_trackID.first) range_trackID.first = sdp.trackID + offset;
378  if (sdp.trackID + offset > range_trackID.second)
379  range_trackID.second = sdp.trackID + offset;
380  } //end loop over SDPs
381 
382  } //end loop over TimePDclockSDPMap
383 
384  return range_trackID;
385  }
386 
387  //-------------------------------------------------
389 
390  bool operator()(timePDclockSDP_t const& a, timePDclockSDP_t const& b) const
391  {
392  return a.first < b.first;
393  }
394 
395  bool operator()(storedTimePDclock_t a_TimePDclock, timePDclockSDP_t const& b) const
396  {
397  return a_TimePDclock < b.first;
398  }
399 
400  bool operator()(timePDclockSDP_t const& a, storedTimePDclock_t b_TimePDclock) const
401  {
402  return a.first < b_TimePDclock;
403  }
404 
405  }; // struct CompareByTimePDclock
406 
409  {
410  return std::lower_bound(
411  timePDclockSDPs.begin(), timePDclockSDPs.end(), iTimePDclock, CompareByTimePDclock());
412  }
413 
416  {
417  return std::lower_bound(
418  timePDclockSDPs.begin(), timePDclockSDPs.end(), TimePDclock, CompareByTimePDclock());
419  }
420 
421  //-------------------------------------------------
422 }
Float_t x
Definition: compare.C:6
intermediate_table::iterator iterator
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
int iOpDetNum
OpticalDetector where the photons were detected.
float x
x position of ionization [cm]
Float_t y
Definition: compare.C:6
Double_t z
Definition: plot.C:276
std::pair< double, std::vector< sim::SDP > > timePDclockSDP_t
List of energy deposits at the same time (on this Optical Detector)
constexpr auto abs(T v)
Returns the absolute value of the argument.
#define MF_LOG_ERROR(category)
double Photons(timePDclock_t iTimePDclock) const
Returns the total number of scintillation photons on this Optical Detector in the specified timePDclo...
constexpr int kBogusI
obviously bogus integer value
intermediate_table::const_iterator const_iterator
std::pair< TrackID_t, TrackID_t > MergeOpDetBacktrackerRecord(const OpDetBacktrackerRecord &opDetNum, int offset)
Merges the deposits from another Optical Detector into this one.
SDP()
Default constructor (sets "bogus" values)
timePDclockSDP_t::first_type storedTimePDclock_t
Type for timePDclock tick used in the internal representation.
std::vector< sim::TrackSDP > TrackSDPs(timePDclock_t startTimePDclock, timePDclock_t endTimePDclock) const
Returns energies collected for each track within a time interval.
Energy deposited on a readout Optical Detector by simulated tracks.
timePDclockSDPs_t timePDclockSDPs
list of energy deposits for each timePDclock with signal
int OpDetNum() const
Returns the readout Optical Detector this object describes.
TrackID_t trackID
Geant4 supplied track ID.
SDP::TrackID_t TrackID_t
Type of track ID (the value comes from Geant4)
static const int NoParticleId
Definition: sim.h:21
timePDclockSDPs_t::iterator findClosestTimePDclockSDP(storedTimePDclock_t timePDclock)
Return the iterator to the first timePDclockSDP not earlier than timePDclock.
double timePDclock_t
Type for iTimePDclock tick used in the interface.
double energy
Definition: plottest35.C:25
Monte Carlo Simulation.
bool operator()(timePDclockSDP_t const &a, timePDclockSDP_t const &b) const
std::map< timePDclock_t, std::vector< sim::SDP > > fTimePDclockSDPs
void AddScintillationPhotonsToMap(TrackID_t trackID, timePDclock_t iTimePDclock, double numberPhotons, double const *xyz, double energy)
void AddScintillationPhotons(TrackID_t trackID, timePDclock_t timePDclock, double numberPhotons, double const *xyz, double energy)
Add scintillation photons and energy to this OpticalDetector.
bool operator()(storedTimePDclock_t a_TimePDclock, timePDclockSDP_t const &b) const
float y
y position of ionization [cm]
double weight
Definition: plottest35.C:25
double Energy(timePDclock_t iTimePDclock) const
Returns the total energy on this Optical Detector in the specified iTimePDclock [MeV].
float numPhotons
number of photons at the optical detector for this track ID and time
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
SDP::TrackID_t TrackID_t
std::vector< sim::SDP > TrackIDsAndEnergies(timePDclock_t startTimePDclock, timePDclock_t endTimePDclock) const
Return all the recorded energy deposition within a time interval.
constexpr double kBogusD
obviously bogus double value
bool operator()(timePDclockSDP_t const &a, storedTimePDclock_t b_TimePDclock) const
Collection of Physical constants used in LArSoft.
Float_t e
Definition: plot.C:35
float z
z position of ionization [cm]
Tools and modules for checking out the basics of the Monte Carlo.
timePDclockSDPs_t const & timePDclockSDPsMap() const
Returns all the deposited energy information as stored.