OpDetBacktrackerRecord.cxx
Go to the documentation of this file.
1 ///
2 /// \file Simulation/OpDetBacktrackerRecord.cxx
3 ///
4 ///
5 /// \author jason.stock@mines.sdsmt.edu
6 //
7 // based on the SimChannel object by seligman@nevis.columbia.edu
8 ///
9 ////////////////////////////////////////////////////////////////////////
10 
11 #include <limits> // std::numeric_limits
12 #include <utility>
13 #include <stdexcept>
14 #include <algorithm> // std::lower_bound(), std::max()
15 #include <map>
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)
37  : SDP(sdp)
38  {
39  trackID += offset;
40  }
41 
42  // Default constructor
43  //-------------------------------------------------
45  : iOpDetNum(-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  : iOpDetNum(detNum)
52  {}
53 
54  //-------------------------------------------------
56  timePDclock_t iTimePDclock,
57  double numberPhotons,
58  double const* xyz,
59  double energy)
60  {
61  /**
62  * The iTimePDclock from OpFastScintillation (where OpDetBacktrackerRecords originate) is done with CLHEP::ns (units of nanoseconds).
63  */
64  // look at the collection to see if the current iTimePDclock already
65  // exists, if not, add it, if so, just add a new track id to the
66  // vector, or update the information if track is already present
67 
68  // no photons? no good!
69  if (( numberPhotons < std::numeric_limits<double>::epsilon() )
70  || ( energy<=std::numeric_limits<double>::epsilon() ))
71  {
72  // will throw
73  MF_LOG_ERROR("OpDetBacktrackerRecord")
74  << "AddTrackPhotons() trying to add to iTimePDclock #"
75  << iTimePDclock
76  << " "
77  << numberPhotons
78  << " photons with "
79  << energy
80  << " MeV of energy from track ID="
81  << trackID;
82  return;
83  } // if no photons
84 
85  auto itr = findClosestTimePDclockSDP(iTimePDclock);
86 
87  // check if this iTimePDclock value is in the vector, it is possible that
88  // the lower bound is different from the given TimePDclock, in which case
89  // we need to add something for that TimePDclock
90  if(itr == timePDclockSDPs.end() ||
91  (abs(itr->first - iTimePDclock) > .50 )){
92  //itr->first != iTimePDclock){
93  std::vector<sim::SDP> sdplist;
94  sdplist.emplace_back(trackID,
95  numberPhotons,
96  energy,
97  xyz[0],
98  xyz[1],
99  xyz[2]
100  );
101  timePDclockSDPs.emplace(itr, std::round(iTimePDclock), std::move(sdplist) );
102  }
103  else { // we have that iTimePDclock already; itr points to it
104 
105  // loop over the SDP vector for this iTimePDclock and add the electrons
106  // to the entry with the same track id
107  for(auto& sdp : itr->second){
108 
109  if (sdp.trackID != trackID ) continue;
110 
111  // make a weighted average for the location information
112  double weight = sdp.numPhotons + numberPhotons;
113  sdp.x = (sdp.x * sdp.numPhotons + xyz[0]*numberPhotons)/weight;
114  sdp.y = (sdp.y * sdp.numPhotons + xyz[1]*numberPhotons)/weight;
115  sdp.z = (sdp.z * sdp.numPhotons + xyz[2]*numberPhotons)/weight;
116  sdp.numPhotons = weight;
117  sdp.energy = sdp.energy + energy;
118 
119  // found the track id we wanted, so return;
120  return;
121  } // for
122 
123  // if we never found the track id, then this is the first instance of
124  // the track id for this TimePDclock, so add sdp to the vector
125  itr->second.emplace_back(trackID,
126  numberPhotons,
127  energy,
128  xyz[0],
129  xyz[1],
130  xyz[2]
131  );
132 
133  } // end of else "We have that iTimePDclock already"
134 
135  } // OpDetBacktrackerRecord::AddIonizationElectrons()
136 
137 
138  //-------------------------------------------------
139  double OpDetBacktrackerRecord::Photons(timePDclock_t iTimePDclock) const //Number of photons
140  {
141  double numPhotons = 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() &&
147  itr->first == iTimePDclock){
148 
149  // loop over the list for this iTimePDclock value and add up
150  // the total number of electrons
151  for(auto sdp : itr->second){
152  numPhotons += sdp.numPhotons;
153  } // end loop over sim::SDP for this TimePDclock
154 
155  } // end if this iTimePDclock is represented in the map
156 
157  return numPhotons;
158  }
159 
160  //-------------------------------------------------
161  //Energy in each object is the energy deposited by the track step that left the photons.
163  {
164  double energy = 0.;
165 
166  auto itr = findClosestTimePDclockSDP(iTimePDclock);
167 
168  // check to see if this iTimePDclock value is in the map
169  if(itr != timePDclockSDPs.end() &&
170  itr->first == iTimePDclock){
171 
172  // loop over the list for this iTimePDclock value and add up
173  // the total number of photons
174  for(auto sdp : itr->second ){
175  energy += sdp.energy;
176  } // end loop over sim::SDP for this TimePDclock
177 
178  } // end if this iTimePDclock is represented in the map
179 
180  return energy;
181  }
182 
183  //-----------------------------------------------------------------------
184  // the start and end iTimePDclock values are assumed to be inclusive
185  std::vector<sim::SDP> OpDetBacktrackerRecord::TrackIDsAndEnergies(timePDclock_t startTimePDclock,
186  timePDclock_t endTimePDclock) const
187  {
188  // make a map of track ID values to sim::SDP objects
189 
190  if(startTimePDclock > endTimePDclock ){
191  mf::LogWarning("OpDetBacktrackerRecord") << "requested TimePDclock range is bogus: "
192  << startTimePDclock << " " << endTimePDclock
193  << " return empty vector";
194  return {}; // returns an empty vector
195  }
196 
197  std::map<TrackID_t, sim::SDP> idToSDP;
198 
199  //find the lower bound for this iTimePDclock and then iterate from there
200  auto itr = findClosestTimePDclockSDP(startTimePDclock);
201 
202  while(itr != timePDclockSDPs.end()){
203 
204  // check the iTimePDclock value for the iterator, break the loop if we
205  // are outside the range
206  if(itr->first > endTimePDclock) break;
207 
208  // grab the vector of SDPs for this TimePDclock
209  auto const& sdplist = itr->second;
210  // now loop over them and add their content to the map
211  for(auto const& sdp : sdplist){
212  auto itTrkSDP = idToSDP.find(sdp.trackID);
213  if( itTrkSDP != idToSDP.end() ){
214  // the SDP we are going to update:
215  sim::SDP& trackSDP = itTrkSDP->second;
216 
217  double const nPh1 = trackSDP.numPhotons;
218  double const nPh2 = sdp.numPhotons;
219  double const weight = nPh1 + nPh2;
220 
221  // make a weighted average for the location information
222  trackSDP.x = (sdp.x*nPh2 + trackSDP.x*nPh1)/weight;
223  trackSDP.y = (sdp.y*nPh2 + trackSDP.y*nPh1)/weight;
224  trackSDP.z = (sdp.z*nPh2 + trackSDP.z*nPh1)/weight;
225  trackSDP.numPhotons = weight;
226  } // end if the track id for this one is found
227  else{
228  idToSDP[sdp.trackID] = sim::SDP(sdp);
229  }
230  } // end loop over vector
231 
232  ++itr;
233  } // end loop over iTimePDclock values
234 
235  // now fill the vector with the sdps from the map
236  std::vector<sim::SDP> sdps;
237  sdps.reserve(idToSDP.size());
238  for(auto const& itr : idToSDP){
239  sdps.push_back(itr.second);
240  }
241 
242  return sdps;
243  }
244 
245  //-----------------------------------------------------------------------
246  // the start and end iTimePDclock values are assumed to be inclusive
247  std::vector<sim::TrackSDP> OpDetBacktrackerRecord::TrackSDPs(timePDclock_t startTimePDclock,
248  timePDclock_t endTimePDclock) const
249  {
250 
251  std::vector<sim::TrackSDP> trackSDPs;
252 
253  if(startTimePDclock > endTimePDclock ){
254  mf::LogWarning("OpDetBacktrackerRecord::TrackSDPs") << "requested iTimePDclock range is bogus: "
255  << startTimePDclock << " " << endTimePDclock
256  << " return empty vector";
257  return trackSDPs;
258  }
259 
260  double totalPhotons = 0.;
261  std::vector<sim::SDP> const sdps = TrackIDsAndEnergies(startTimePDclock, endTimePDclock);
262  for (auto const& sdp : sdps)
263  totalPhotons += sdp.numPhotons;
264 
265  // protect against a divide by zero below
266  if(totalPhotons < 1.e-5) totalPhotons = 1.;
267 
268  // loop over the entries in the map and fill the input vectors
269  for (auto const& sdp : sdps){
270  if(sdp.trackID == sim::NoParticleId) continue;
271  trackSDPs.emplace_back(sdp.trackID, sdp.numPhotons/totalPhotons, sdp.numPhotons);
272  }
273 
274  return trackSDPs;
275  }
276 
277 
278  //-----------------------------------------------------------------------
279  // Merge the collection of SDPs from one sim channel to another.
280  // Requires an agreed upon offset for G4 trackID
281  std::pair<OpDetBacktrackerRecord::TrackID_t,OpDetBacktrackerRecord::TrackID_t>
283  int offset)
284  {
285  if( this->OpDetNum() != channel.OpDetNum() )
286  throw std::runtime_error("ERROR OpDetBacktrackerRecord Merge: Trying to merge different channels!");
287 
288  std::pair<TrackID_t,TrackID_t> range_trackID(std::numeric_limits<int>::max(),
290 
291  for(auto const& itr : channel.timePDclockSDPsMap()){
292 
293  auto iTimePDclock = itr.first;
294  auto const& sdps = itr.second;
295 
296  // find the entry from this OpDetBacktrackerRecord corresponding to the iTimePDclock from the other
297  auto itrthis = findClosestTimePDclockSDP(iTimePDclock);
298 
299  // pick which SDP list we have to fill: new one or existing one
300  std::vector<sim::SDP>* curSDPVec;
301  if(itrthis == timePDclockSDPs.end() ||
302  itrthis->first != iTimePDclock){
303  timePDclockSDPs.emplace_back(iTimePDclock, std::vector<sim::SDP>());
304  curSDPVec = &(timePDclockSDPs.back().second);
305  }
306  else
307  curSDPVec = &(itrthis->second);
308 
309  for(auto const& sdp : sdps){
310  curSDPVec->emplace_back(sdp, offset);
311  if( sdp.trackID+offset < range_trackID.first )
312  range_trackID.first = sdp.trackID+offset;
313  if( sdp.trackID+offset > range_trackID.second )
314  range_trackID.second = sdp.trackID+offset;
315  }//end loop over SDPs
316 
317  }//end loop over TimePDclockSDPMap
318 
319 
320  return range_trackID;
321 
322  }
323 
324  //-------------------------------------------------
326 
327  bool operator()
328  (timePDclockSDP_t const& a, timePDclockSDP_t const& b) const
329  { return a.first < b.first; }
330 
331  bool operator()
332  (storedTimePDclock_t a_TimePDclock, timePDclockSDP_t const& b) const
333  { return a_TimePDclock < b.first; }
334 
335  bool operator()
336  (timePDclockSDP_t const& a, storedTimePDclock_t b_TimePDclock) const
337  { return a.first < b_TimePDclock; }
338 
339  }; // struct CompareByTimePDclock
340 
341 
343  {
344  return std::lower_bound
345  (timePDclockSDPs.begin(), timePDclockSDPs.end(), iTimePDclock, CompareByTimePDclock());
346  }
347 
349  (storedTimePDclock_t TimePDclock) const
350  {
351  return std::lower_bound
352  ( timePDclockSDPs.begin(), timePDclockSDPs.end(), TimePDclock, CompareByTimePDclock() );
353  }
354 
355 
356  //-------------------------------------------------
357 
358 
359 }
intermediate_table::iterator iterator
Namespace for general, non-LArSoft-specific utilities.
int iOpDetNum
OpticalDetector where the photons were detected.
float x
x position of ionization [cm]
#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.
uint8_t channel
Definition: CRTFragment.hh:201
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.
weight
Definition: test.py:257
Energy deposited on a readout Optical Detector by simulated tracks.
timePDclockSDPs_t timePDclockSDPs
list of energy deposits for each timePDclock with signal
T abs(T value)
int OpDetNum() const
Returns the readout Optical Detector this object describes.
const double e
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:28
const double a
def move(depos, offset)
Definition: depos.py:107
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.
static int max(int a, int b)
Code to link reconstructed objects back to the MC truth information.
void AddScintillationPhotons(TrackID_t trackID, timePDclock_t timePDclock, double numberPhotons, double const *xyz, double energy)
Add scintillation photons and energy to this OpticalDetector.
float y
y position of ionization [cm]
double Energy(timePDclock_t iTimePDclock) const
Returns the total energy on this Optical Detector in the specified iTimePDclock [MeV].
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
std::pair< double, std::vector< sim::SDP > > timePDclockSDP_t
List of energy deposits at the same time (on this Optical Detector)
float numPhotons
number of photons at the optical detector for this track ID and time
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
static bool * b
Definition: config.cpp:1043
list x
Definition: train.py:276
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
Tools and modules for checking out the basics of the Monte Carlo.
Collection of Physical constants used in LArSoft.
float z
z position of ionization [cm]
timePDclockSDPs_t const & timePDclockSDPsMap() const
Returns all the deposited energy information as stored.