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