PhotonLibraryPropagation_module.cc
Go to the documentation of this file.
1 /**
2  * @file larsim/PhotonPropagation/PhotonLibraryPropagation_module.cc
3  * @brief Provides `phot:PhotonLibraryPropagation` module.
4  */
5 
6 #include "CLHEP/Random/RandFlat.h"
7 #include "CLHEP/Random/RandPoissonQ.h"
14 #include "cetlib_except/exception.h"
15 #include "fhiclcpp/ParameterSet.h"
25 #include "nurandom/RandomUtils/NuRandomService.h"
26 
27 #include <cmath>
28 #include <memory>
29 
30 using namespace std;
31 
32 namespace {
33 
34  double
35  single_exp(double t, double tau2)
36  {
37  return exp((-1.0 * t) / tau2) / tau2;
38  }
39 
40  double
41  bi_exp(double t, double tau1, double tau2)
42  {
43  return (((exp((-1.0 * t) / tau2) * (1.0 - exp((-1.0 * t) / tau1))) / tau2) / tau2) *
44  (tau1 + tau2);
45  }
46 
47  // Returns the time within the time distribution of the scintillation process, when the photon was created.
48  // Scintillation light has an exponential decay which here is given by the decay time, tau2,
49  // and an exponential increase, which here is given by the rise time, tau1.
50  // randflatscinttime is passed to use the saved seed from the RandomNumberSaver in order to be able to reproduce the same results.
51  double
52  GetScintTime(double tau1, double tau2, CLHEP::RandFlat& randflatscinttime)
53  {
54  // tau1: rise time (originally defaulted to -1) and tau2: decay time
55  //ran1, ran2 = random numbers for the algorithm
56  if ((tau1 == 0.0) || (tau1 == -1.0)) { return -tau2 * log(randflatscinttime()); }
57  while (1) {
58  auto ran1 = randflatscinttime();
59  auto ran2 = randflatscinttime();
60  auto d = (tau1 + tau2) / tau2;
61  auto t = -tau2 * log(1 - ran1);
62  auto g = d * single_exp(t, tau2);
63  if (ran2 <= bi_exp(t, tau1, tau2) / g) { return t; }
64  }
65  }
66 
67  double
68  GetScintYield(sim::SimEnergyDeposit const& edep, detinfo::LArProperties const& larp)
69  {
70  if (!larp.ScintByParticleType()) { return larp.ScintYieldRatio(); }
71  switch (edep.PdgCode()) {
72  case 2212: return larp.ProtonScintYieldRatio();
73  case 13:
74  case -13: return larp.MuonScintYieldRatio();
75  case 211:
76  case -211: return larp.PionScintYieldRatio();
77  case 321:
78  case -321: return larp.KaonScintYieldRatio();
79  case 1000020040: return larp.AlphaScintYieldRatio();
80  case 11:
81  case -11:
82  case 22: return larp.ElectronScintYieldRatio();
83  default: return larp.ElectronScintYieldRatio();
84  }
85  }
86 
87 } // unnamed namespace
88 
89 namespace phot {
90 
91  /**
92  * @brief Fast simulation of propagating the photons created from SimEnergyDeposits.
93  *
94  * This module does a fast simulation of propagating the photons created from SimEnergyDeposits,
95  * which is the Geant4 output after each step, to each of the optical detectors.
96  * This simulation is done using the PhotonLibrary,
97  * which stores the visibilities of each optical channel with respect to each optical voxel in the TPC volume,
98  * to avoid propagating single photons using Geant4.
99  * At the end of this module a collection of the propagated photons either as `sim::SimPhotonsLite` or as `sim::SimPhotons` is placed into the art event.
100  *
101  * Keep in mind that at this stage the LArG4 main module is not capable of running the full optical simulation,
102  * because the necessary code has not yet been written.
103  *
104  * In the future when the PhotonLibrary has the propagation time included,
105  * it could be possible to enhance `sim::SimPhotons` and `sim::SimPhotonsLite` to contain the propagation time.
106  * At this point the time recorded for the photon is the creation time of the photon.
107  *
108  * The steps this module takes are:
109  * - to take `sim::SimEnergyDeposits` produced by larg4Main,
110  * - use Ionisation and Scintillation to calculate the amount of scintillated photons,
111  * - use the PhotonLibrary (visibilities) to determine the amount of visible photons at each optical channel,
112  * - visible photons = the amount of scintillated photons calculated times the visibility
113  * at the middle of the Geant4 step for a given optical channel.
114  * - and if `sim::SimPhotonsLite` produced:
115  * - since a `sim::SimPhotonsLite` only keeps a set of times with the number of photons produced at each time
116  * for a given OpChannel number:
117  * - for each time (as an integer in [ns]) photons are produced along the Geant4 step
118  * (taking into account the rise time and decay time of the fast and slow components of the scintillation process),
119  * - count the amount of photons emitted at that time.
120  * - the total amount of visible photons produced during the current Geant4 step equals the sum of counts for each time.
121  * - the total amount of visible photons produced during the current Geant4 step
122  * is determined by throwing a random number from a Poisson distribution
123  * with a mean of the amount of visible photons calculated above.
124  *
125  * - and if `sim::SimPhotons` produced:
126  * - since a `sim::SimPhotons` keeps a collection of photons (`sim::OnePhoton`) for a given OpChannel number:
127  * - each single photon produced by this algorithm is just a copy containing the same information about:
128  * - energy (set to a constant value = 9.7e-6, which is equivalent to a wavelength of 128 nm,
129  * it should actually be 126.6 nm!!),
130  * - initial position,
131  * - time (as an integer in [ns]) the photon is produced along the Geant4 Step
132  * (taking into account the rise time and decay time of the fast and slow components of the scintillation process),
133  * - the total amount of photon copies produced during the current Geant4 step
134  * is determined by throwing a random number from a Poisson distribution
135  * with a mean of the amount of visible photons calculated above.
136  *
137  * This module should only be run for the fast optical simulation even though it can create `sim::SimPhotonsLite` and `sim::SimPhotons` as data products.
138  * If there is need to create `sim::SimPhotons`, there are some considerations you must be aware of.
139  * Since the amount of `sim::SimPhotons` produced even at low energies and in small geometries quickly exceeds the memory capacity of the job,
140  * right now it is actually impossible to produce `sim::SimPhotons` for any realistic geometry.
141  * A possible way around the problem is to implement a scaling of the produced `sim::SimPhotons`, to only produce a fraction of them.
142  */
144  private:
148  vector<art::InputTag> fEDepTags;
150  CLHEP::HepRandomEngine& fPhotonEngine;
151  CLHEP::HepRandomEngine& fScintTimeEngine;
152 
153  void produce(art::Event&) override;
154 
155  public:
159  PhotonLibraryPropagation& operator=(PhotonLibraryPropagation const&) = delete;
160  PhotonLibraryPropagation& operator=(PhotonLibraryPropagation&&) = delete;
161  };
162 
163  PhotonLibraryPropagation::PhotonLibraryPropagation(fhicl::ParameterSet const& p)
164  : art::EDProducer{p}
165  , fRiseTimeFast{p.get<double>("RiseTimeFast", 0.0)}
166  , fRiseTimeSlow{p.get<double>("RiseTimeSlow", 0.0)}
167  , fDoSlowComponent{p.get<bool>("DoSlowComponent")}
168  , fEDepTags{p.get<vector<art::InputTag>>("EDepModuleLabels")}
170  ->createEngine(*this, "HepJamesRandom", "photon", p, "SeedPhoton"))
172  ->createEngine(*this, "HepJamesRandom", "scinttime", p, "SeedScintTime"))
173  {
175  produces<vector<sim::SimPhotonsLite>>();
176  }
177  else {
178  produces<vector<sim::SimPhotons>>();
179  }
180  }
181 
182  void
184  {
187  auto const* larp = lar::providerFrom<detinfo::LArPropertiesService>();
188  auto const detProp = art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(e);
189  CLHEP::RandPoissonQ randpoisphot{fPhotonEngine};
190  CLHEP::RandFlat randflatscinttime{fScintTimeEngine};
191  auto const nOpChannels = pvs->NOpChannels();
192  unique_ptr<vector<sim::SimPhotons>> photCol{new vector<sim::SimPhotons>{}};
193  auto& photonCollection{*photCol};
194  photonCollection.resize(nOpChannels);
195  unique_ptr<vector<sim::SimPhotonsLite>> photLiteCol{new vector<sim::SimPhotonsLite>{}};
196  auto& photonLiteCollection{*photLiteCol};
197  photonLiteCollection.resize(nOpChannels);
198  for (unsigned int i = 0; i < nOpChannels; ++i) {
199  photonLiteCollection[i].OpChannel = i;
200  photonCollection[i].SetChannel(i);
201  }
202  vector<vector<sim::SimEnergyDeposit> const*> edep_vecs;
203  for (auto label : fEDepTags) {
204  auto const& edep_handle = e.getValidHandle<vector<sim::SimEnergyDeposit>>(label);
205  edep_vecs.push_back(edep_handle);
206  }
207  for (auto const& edeps : edep_vecs) { //loop over modules
208  for (auto const& edep : *edeps) { //loop over energy deposits: one per step
209  //int count_onePhot =0; // unused
210  auto const& p = edep.MidPoint();
211  auto const& Visibilities = pvs->GetAllVisibilities(p);
212  if (!Visibilities) {
213  throw cet::exception("PhotonLibraryPropagation")
214  << "There is no entry in the PhotonLibrary for this position in space. "
215  "Position: "
216  << edep.MidPoint();
217  }
218  auto const isCalcData = fISAlg.CalcIonAndScint(detProp, edep);
219  //total amount of scintillation photons
220  double nphot = static_cast<int>(isCalcData.numPhotons);
221  //amount of scintillated photons created via the fast scintillation process
222  double nphot_fast = static_cast<int>(GetScintYield(edep, *larp) * nphot);
223  //amount of scintillated photons created via the slow scintillation process
224  double nphot_slow = nphot - nphot_fast;
225  for (unsigned int channel = 0; channel < nOpChannels; ++channel) {
226  auto visibleFraction = Visibilities[channel];
227  if (visibleFraction == 0.0) {
228  // Voxel is not visible at this optical channel, skip doing anything for this channel.
229  continue;
230  }
231  if (lgp->UseLitePhotons()) {
232  if (nphot_fast > 0) {
233  //throwing a random number from a poisson distribution with a mean of the amount of photons visible at this channel
234  auto n = static_cast<int>(randpoisphot.fire(nphot_fast * visibleFraction));
235  for (long i = 0; i < n; ++i) {
236  //calculates the time at which the photon was produced
237  auto time = static_cast<int>(edep.T0() + GetScintTime(fRiseTimeFast,
238  larp->ScintFastTimeConst(),
239  randflatscinttime));
240  ++photonLiteCollection[channel].DetectedPhotons[time];
241  }
242  }
243  if ((nphot_slow > 0) && fDoSlowComponent) {
244  //throwing a random number from a poisson distribution with a mean of the amount of photons visible at this channel
245  auto n = randpoisphot.fire(nphot_slow * visibleFraction);
246  for (long i = 0; i < n; ++i) {
247  //calculates the time at which the photon was produced
248  auto time = static_cast<int>(edep.T0() + GetScintTime(fRiseTimeSlow,
249  larp->ScintSlowTimeConst(),
250  randflatscinttime));
251  ++photonLiteCollection[channel].DetectedPhotons[time];
252  }
253  }
254  }
255  else {
256  sim::OnePhoton photon;
257  photon.SetInSD = false;
258  photon.InitialPosition = edep.End();
259  photon.Energy = 9.7e-6;
260  if (nphot_fast > 0) {
261  //throwing a random number from a poisson distribution with a mean of the amount of photons visible at this channel
262  auto n = randpoisphot.fire(nphot_fast * visibleFraction);
263  if (n > 0) {
264  //calculates the time at which the photon was produced
265  photon.Time =
266  edep.T0() +
267  GetScintTime(fRiseTimeFast, larp->ScintFastTimeConst(), randflatscinttime);
268  // add n copies of sim::OnePhoton photon to the photon collection for a given OpChannel
269  photonCollection[channel].insert(photonCollection[channel].end(), n, photon);
270  }
271  }
272  if ((nphot_slow > 0) && fDoSlowComponent) {
273  //throwing a random number from a poisson distribution with a mean of the amount of photons visible at this channel
274  auto n = randpoisphot.fire(nphot_slow * visibleFraction);
275  if (n > 0) {
276  //calculates the time at which the photon was produced
277  photon.Time =
278  edep.T0() +
279  GetScintTime(fRiseTimeSlow, larp->ScintSlowTimeConst(), randflatscinttime);
280  // add n copies of sim::OnePhoton photon to the photon collection for a given OpChannel
281  photonCollection[channel].insert(photonCollection[channel].end(), n, photon);
282  }
283  }
284  }
285  }
286  }
287  }
288  if (lgp->UseLitePhotons()) {
289  // put the photon collection of LitePhotons into the art event
290  e.put(move(photLiteCol));
291  }
292  else {
293  //put the photon collection of SimPhotons into the art event
294  e.put(move(photCol));
295  }
296  }
297 
298 } // namespace phot
299 
end
while True: pbar.update(maxval-len(onlies[E][S])) #print iS, "/", len(onlies[E][S]) found = False for...
virtual double ElectronScintYieldRatio() const =0
Store parameters for running LArG4.
virtual double ScintYieldRatio() const =0
static constexpr double g
Definition: Units.h:144
virtual double AlphaScintYieldRatio() const =0
virtual double ScintSlowTimeConst() const =0
All information of a photon entering the sensitive optical detector volume.
Definition: SimPhotons.h:64
virtual double ScintFastTimeConst() const =0
STL namespace.
G4double tau1[100]
Definition: G4S2Light.cc:61
ISCalcData CalcIonAndScint(detinfo::DetectorPropertiesData const &detProp, sim::SimEnergyDeposit const &edep) override
uint8_t channel
Definition: CRTFragment.hh:201
virtual double ProtonScintYieldRatio() const =0
geo::Point_t InitialPosition
Scintillation position in world coordinates [cm].
Definition: SimPhotons.h:67
virtual double PionScintYieldRatio() const =0
Simulation objects for optical detectors.
geo::Point_t End() const
const double e
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
Fast simulation of propagating the photons created from SimEnergyDeposits.
std::void_t< T > n
def move(depos, offset)
Definition: depos.py:107
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
Definition: DataViewImpl.h:441
p
Definition: test.py:223
geo::Point_t MidPoint() const
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
General LArSoft Utilities.
bool SetInSD
Whether the photon reaches the sensitive detector.
Definition: SimPhotons.h:88
contains information for a single step in the detector simulation
Energy deposition in the active material.
virtual double MuonScintYieldRatio() const =0
float Energy
Scintillation photon energy [GeV].
Definition: SimPhotons.h:82
virtual double KaonScintYieldRatio() const =0
virtual bool ScintByParticleType() const =0
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
bool UseLitePhotons() const