LArSoft  v09_90_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 <limits> // std::numeric_limits
13 #include <map>
14 #include <stdexcept>
15 #include <utility>
16 
20 
22 
23 namespace sim {
24 
25  //-------------------------------------------------
26  SDP::SDP() //SDP stands for Scintillation Deposited Photons
27  : trackID(util::kBogusI)
28  , numPhotons(util::kBogusD)
29  , energy(util::kBogusD)
30  , x(util::kBogusD)
31  , y(util::kBogusD)
32  , z(util::kBogusD)
33  {}
34 
35  //-------------------------------------------------
36  SDP::SDP(sim::SDP const& sdp, int offset) : SDP(sdp)
37  {
38  trackID += offset;
39  }
40 
41  // Default constructor
42  //-------------------------------------------------
44  : iOpDetNum(
45  -1) //set an impossible channel number in the case where this is called without an opticalchannel.
46  //The reason for doing this is to follow the structure of SimChannel, which uses kBogusChannel
47  {}
48 
49  //-------------------------------------------------
51 
52  //-------------------------------------------------
54  timePDclock_t iTimePDclock,
55  double numberPhotons,
56  double const* xyz,
57  double energy)
58  {
62  // look at the collection to see if the current iTimePDclock already
63  // exists, if not, add it, if so, just add a new track id to the
64  // vector, or update the information if track is already present
65 
66  // no photons? no good!
67  if ((numberPhotons < std::numeric_limits<double>::epsilon()) ||
68  (energy <= std::numeric_limits<double>::epsilon())) {
69  // will throw
70  MF_LOG_ERROR("OpDetBacktrackerRecord")
71  << "AddTrackPhotons() trying to add to iTimePDclock #" << iTimePDclock << " "
72  << numberPhotons << " photons with " << energy
73  << " MeV of energy from track ID=" << trackID;
74  return;
75  } // if no photons
76 
77  auto itr = findClosestTimePDclockSDP(iTimePDclock);
78 
79  // check if this iTimePDclock value is in the vector, it is possible that
80  // the lower bound is different from the given TimePDclock, in which case
81  // we need to add something for that TimePDclock
82  if (itr == timePDclockSDPs.end() || (abs(itr->first - iTimePDclock) > .50)) {
83  //itr->first != iTimePDclock){
84  std::vector<sim::SDP> sdplist;
85  sdplist.emplace_back(trackID, numberPhotons, energy, xyz[0], xyz[1], xyz[2]);
86  timePDclockSDPs.emplace(itr, std::round(iTimePDclock), std::move(sdplist));
87  }
88  else { // we have that iTimePDclock already; itr points to it
89 
90  // loop over the SDP vector for this iTimePDclock and add the electrons
91  // to the entry with the same track id
92  for (auto& sdp : itr->second) {
93 
94  if (sdp.trackID != trackID) continue;
95 
96  // make a weighted average for the location information
97  double weight = sdp.numPhotons + numberPhotons;
98  sdp.x = (sdp.x * sdp.numPhotons + xyz[0] * numberPhotons) / weight;
99  sdp.y = (sdp.y * sdp.numPhotons + xyz[1] * numberPhotons) / weight;
100  sdp.z = (sdp.z * sdp.numPhotons + xyz[2] * numberPhotons) / weight;
101  sdp.numPhotons = weight;
102  sdp.energy = sdp.energy + energy;
103 
104  // found the track id we wanted, so return;
105  return;
106  } // for
107 
108  // if we never found the track id, then this is the first instance of
109  // the track id for this TimePDclock, so add sdp to the vector
110  itr->second.emplace_back(trackID, numberPhotons, energy, xyz[0], xyz[1], xyz[2]);
111 
112  } // end of else "We have that iTimePDclock already"
113 
114  } // OpDetBacktrackerRecord::AddIonizationElectrons()
115 
116  //-------------------------------------------------
117  double OpDetBacktrackerRecord::Photons(timePDclock_t iTimePDclock) const //Number of photons
118  {
119  double numPhotons = 0.;
120 
121  auto itr = findClosestTimePDclockSDP(iTimePDclock);
122 
123  // check to see if this iTimePDclock value is in the map
124  if (itr != timePDclockSDPs.end() && itr->first == iTimePDclock) {
125 
126  // loop over the list for this iTimePDclock value and add up
127  // the total number of electrons
128  for (auto sdp : itr->second) {
129  numPhotons += sdp.numPhotons;
130  } // end loop over sim::SDP for this TimePDclock
131 
132  } // end if this iTimePDclock is represented in the map
133 
134  return numPhotons;
135  }
136 
137  //-------------------------------------------------
138  //Energy in each object is the energy deposited by the track step that left the photons.
140  {
141  double energy = 0.;
142 
143  auto itr = findClosestTimePDclockSDP(iTimePDclock);
144 
145  // check to see if this iTimePDclock value is in the map
146  if (itr != timePDclockSDPs.end() && itr->first == iTimePDclock) {
147 
148  // loop over the list for this iTimePDclock value and add up
149  // the total number of photons
150  for (auto sdp : itr->second) {
151  energy += sdp.energy;
152  } // end loop over sim::SDP for this TimePDclock
153 
154  } // end if this iTimePDclock is represented in the map
155 
156  return energy;
157  }
158 
159  //-----------------------------------------------------------------------
160  // the start and end iTimePDclock values are assumed to be inclusive
162  timePDclock_t startTimePDclock,
163  timePDclock_t endTimePDclock) const
164  {
165  // make a map of track ID values to sim::SDP objects
166 
167  if (startTimePDclock > endTimePDclock) {
168  mf::LogWarning("OpDetBacktrackerRecord")
169  << "requested TimePDclock range is bogus: " << startTimePDclock << " " << endTimePDclock
170  << " return empty vector";
171  return {}; // returns an empty vector
172  }
173 
174  std::map<TrackID_t, sim::SDP> idToSDP;
175 
176  //find the lower bound for this iTimePDclock and then iterate from there
177  auto itr = findClosestTimePDclockSDP(startTimePDclock);
178 
179  while (itr != timePDclockSDPs.end()) {
180 
181  // check the iTimePDclock value for the iterator, break the loop if we
182  // are outside the range
183  if (itr->first > endTimePDclock) break;
184 
185  // grab the vector of SDPs for this TimePDclock
186  auto const& sdplist = itr->second;
187  // now loop over them and add their content to the map
188  for (auto const& sdp : sdplist) {
189  auto itTrkSDP = idToSDP.find(sdp.trackID);
190  if (itTrkSDP != idToSDP.end()) {
191  // the SDP we are going to update:
192  sim::SDP& trackSDP = itTrkSDP->second;
193 
194  double const nPh1 = trackSDP.numPhotons;
195  double const nPh2 = sdp.numPhotons;
196  double const weight = nPh1 + nPh2;
197 
198  // make a weighted average for the location information
199  trackSDP.x = (sdp.x * nPh2 + trackSDP.x * nPh1) / weight;
200  trackSDP.y = (sdp.y * nPh2 + trackSDP.y * nPh1) / weight;
201  trackSDP.z = (sdp.z * nPh2 + trackSDP.z * nPh1) / weight;
202  trackSDP.numPhotons = weight;
203  } // end if the track id for this one is found
204  else {
205  idToSDP[sdp.trackID] = sim::SDP(sdp);
206  }
207  } // end loop over vector
208 
209  ++itr;
210  } // end loop over iTimePDclock values
211 
212  // now fill the vector with the sdps from the map
213  std::vector<sim::SDP> sdps;
214  sdps.reserve(idToSDP.size());
215  for (auto const& itr : idToSDP) {
216  sdps.push_back(itr.second);
217  }
218 
219  return sdps;
220  }
221 
222  //-----------------------------------------------------------------------
223  // the start and end iTimePDclock values are assumed to be inclusive
224  std::vector<sim::TrackSDP> OpDetBacktrackerRecord::TrackSDPs(timePDclock_t startTimePDclock,
225  timePDclock_t endTimePDclock) const
226  {
227 
228  std::vector<sim::TrackSDP> trackSDPs;
229 
230  if (startTimePDclock > endTimePDclock) {
231  mf::LogWarning("OpDetBacktrackerRecord::TrackSDPs")
232  << "requested iTimePDclock range is bogus: " << startTimePDclock << " " << endTimePDclock
233  << " return empty vector";
234  return trackSDPs;
235  }
236 
237  double totalPhotons = 0.;
238  std::vector<sim::SDP> const sdps = TrackIDsAndEnergies(startTimePDclock, endTimePDclock);
239  for (auto const& sdp : sdps)
240  totalPhotons += sdp.numPhotons;
241 
242  // protect against a divide by zero below
243  if (totalPhotons < 1.e-5) totalPhotons = 1.;
244 
245  // loop over the entries in the map and fill the input vectors
246  for (auto const& sdp : sdps) {
247  if (sdp.trackID == sim::NoParticleId) continue;
248  trackSDPs.emplace_back(sdp.trackID, sdp.numPhotons / totalPhotons, sdp.numPhotons);
249  }
250 
251  return trackSDPs;
252  }
253 
254  //-----------------------------------------------------------------------
255  // Merge the collection of SDPs from one sim channel to another.
256  // Requires an agreed upon offset for G4 trackID
257  std::pair<OpDetBacktrackerRecord::TrackID_t, OpDetBacktrackerRecord::TrackID_t>
259  int offset)
260  {
261  if (this->OpDetNum() != channel.OpDetNum())
262  throw std::runtime_error(
263  "ERROR OpDetBacktrackerRecord Merge: Trying to merge different channels!");
264 
265  std::pair<TrackID_t, TrackID_t> range_trackID(std::numeric_limits<int>::max(),
266  std::numeric_limits<int>::min());
267 
268  for (auto const& itr : channel.timePDclockSDPsMap()) {
269 
270  auto iTimePDclock = itr.first;
271  auto const& sdps = itr.second;
272 
273  // find the entry from this OpDetBacktrackerRecord corresponding to the iTimePDclock from the other
274  auto itrthis = findClosestTimePDclockSDP(iTimePDclock);
275 
276  // pick which SDP list we have to fill: new one or existing one
277  std::vector<sim::SDP>* curSDPVec;
278  if (itrthis == timePDclockSDPs.end() || itrthis->first != iTimePDclock) {
279  timePDclockSDPs.emplace_back(iTimePDclock, std::vector<sim::SDP>());
280  curSDPVec = &(timePDclockSDPs.back().second);
281  }
282  else
283  curSDPVec = &(itrthis->second);
284 
285  for (auto const& sdp : sdps) {
286  curSDPVec->emplace_back(sdp, offset);
287  if (sdp.trackID + offset < range_trackID.first) range_trackID.first = sdp.trackID + offset;
288  if (sdp.trackID + offset > range_trackID.second)
289  range_trackID.second = sdp.trackID + offset;
290  } //end loop over SDPs
291 
292  } //end loop over TimePDclockSDPMap
293 
294  return range_trackID;
295  }
296 
297  //-------------------------------------------------
299 
300  bool operator()(timePDclockSDP_t const& a, timePDclockSDP_t const& b) const
301  {
302  return a.first < b.first;
303  }
304 
305  bool operator()(storedTimePDclock_t a_TimePDclock, timePDclockSDP_t const& b) const
306  {
307  return a_TimePDclock < b.first;
308  }
309 
310  bool operator()(timePDclockSDP_t const& a, storedTimePDclock_t b_TimePDclock) const
311  {
312  return a.first < b_TimePDclock;
313  }
314 
315  }; // struct CompareByTimePDclock
316 
319  {
320  return std::lower_bound(
321  timePDclockSDPs.begin(), timePDclockSDPs.end(), iTimePDclock, CompareByTimePDclock());
322  }
323 
326  {
327  return std::lower_bound(
328  timePDclockSDPs.begin(), timePDclockSDPs.end(), TimePDclock, CompareByTimePDclock());
329  }
330 
331  //-------------------------------------------------
332 
333 }
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
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
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.