All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SimPMTIcarus_module.cc
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/SimPMTIcarus_module.cc
3  * @see `icarus::opdet::PMTsimulationAlg`
4  *
5  * Based on `SimPMTSBND_module.cc` by L. Paulucci and F. Marinho.
6  */
7 
8 
9 // ICARUS libraries
13 
14 // LArSoft libraries
18 #include "lardataalg/Utilities/quantities/spacetime.h" // nanosecond
21 #include "nurandom/RandomUtils/NuRandomService.h"
22 
23 // framework libraries
24 #include "art/Framework/Core/EDProducer.h"
25 #include "art/Framework/Core/ModuleMacros.h"
26 #include "art/Framework/Principal/Event.h"
27 #include "art/Framework/Principal/Handle.h"
28 #include "art/Framework/Services/Registry/ServiceHandle.h"
29 #include "art/Framework/Services/Optional/RandomNumberGenerator.h"
30 #include "art/Utilities/make_tool.h"
31 #include "canvas/Utilities/InputTag.h"
32 #include "messagefacility/MessageLogger/MessageLogger.h"
33 #include "fhiclcpp/types/DelegatedParameter.h"
34 #include "fhiclcpp/types/TableFragment.h"
35 #include "fhiclcpp/types/Atom.h"
36 #include "fhiclcpp/ParameterSet.h"
37 
38 // CLHEP libraries
39 #include "CLHEP/Random/RandEngine.h" // CLHEP::HepRandomEngine
40 
41 // C/C++ standard library
42 #include <vector>
43 #include <atomic> // std::atomic_flag
44 #include <iterator> // std::back_inserter()
45 #include <memory> // std::make_unique()
46 #include <utility> // std::move()
47 #include <optional>
48 
49 
50 namespace icarus::opdet {
51 
52  /**
53  * @brief Simulates the digitization of ICARUS PMT response and trigger.
54  *
55  * The module is a simple interface to the simulation algorithm,
56  * `icarus::opdet::PMTsimulationAlg`.
57  *
58  *
59  * Configuration
60  * ==============
61  *
62  * All the configuration parameters are passed directly to the
63  * `icarus::opdet::PMTsimulationAlg` algorithm, with the following exceptions:
64  * * **InputModule** (input tag): tag of the data product containing the
65  * `sim::SimPhotons` collection to be digitized;
66  * * **SinglePhotonResponse** (configuration table): configuration of the
67  * _art_ tool delivering the single photon response function (see below);
68  * * **EfficiencySeed**, **DarkNoiseSeed** and **ElectronicsNoiseSeed**
69  * (integers, optional): if specified, each number is used to seed the
70  * pertaining random engine; otherwise, the seed is assigned by
71  * `NuRandomService`;
72  * * **ElectronicsNoiseRandomEngine** and **DarkNoiseRandomEngine** (strings,
73  * default: `HepJamesRandom`): name of the random number generation
74  * algorithm to use; valid values are all the ones supported by
75  * `art::RandomNumberGenerator` (which match the random engine classes
76  * derived from `clhep::HepRandomEngine` in CLHEP 2.3 except
77  * `NonRandomEngine` and `RandEngine`);
78  * * **WritePhotons** (boolean, default: `false`): saves in an additional
79  * `sim::SimPhotons` collection the photons effectively contributing to
80  * the waveforms; currently, no selection ever happens and all photons are
81  * contributing, making this collection the same as the input one.
82  *
83  * See the @ref ICARUS_PMTSimulationAlg_RandomEngines "documentation" of
84  * `icarus::PMTsimulationAlg` for the purpose of the three random number
85  * engines.
86  *
87  * Currently, no configuration interface is provided to directly control their
88  * seeds, which is delegated to `rndm::NuRandomService` service.
89  *
90  *
91  * Input
92  * ======
93  *
94  * The module utilizes as input a collection of `sim::SimPhotons`, each
95  * containing the photons propagated to a single optical detector channel.
96  *
97  *
98  * Output
99  * =======
100  *
101  * A collection of optical detector waveforms
102  * (`std::vector<raw::OpDetWaveform>`) is produced.
103  * See `icarus::opdet::PMTsimulationAlg` algorithm documentation for details.
104  *
105  * If `WritePhotons` configuration parameter is set `true`, a collection of
106  * the scintillation photons (`std::vector<sim::SimPhotons>`) which
107  * contributed to the waveforms is also produced.
108  *
109  *
110  * Requirements
111  * =============
112  *
113  * This module currently requires LArSoft services:
114  * * `DetectorClocksService` for timing conversions and settings
115  * * `LArPropertiesService` for the scintillation yield(s)
116  *
117  * Three random streams are also used.
118  *
119  *
120  * Single photon response function tool
121  * -------------------------------------
122  *
123  * The single photon response function passed to the underlying algorithm,
124  * `icarus::opdet::PMTsimulationAlg`, is extracted from an _art_ tool.
125  * The specifications of the tool are:
126  *
127  * * the _art_ tool class must implement the
128  * `icarus::opdet::SinglePhotonPulseFunctionTool` interface;
129  * * the resulting function describes the signal as digitized by the PMT
130  * readout;
131  * * the function must therefore express the level of the response as
132  * digitized ADC count above the baseline; this level is negative if the
133  * signal polarity is negative;
134  * * the time scale of this function has `0` as the photoelectron production
135  * time.
136  *
137  * These requirements imply that the tool (or the underlying function) must
138  * include electronics effects like the the time it takes to form the signal,
139  * the signal shape at PMT output, the delay and distortion of the cables and
140  * the ones of the digitization electronics, but the baseline is assumed flat
141  * at 0.
142  *
143  */
144  class SimPMTIcarus : public art::EDProducer {
145  public:
146 
147  struct Config
148  {
149  using Comment = fhicl::Comment;
150  using Name = fhicl::Name;
151 
152  fhicl::Atom<art::InputTag> inputModuleLabel {
153  Name("InputModule"),
154  Comment("simulated photons to be digitized (sim::SimPhotons)")
155  };
156 
157  fhicl::DelegatedParameter SinglePhotonResponse {
158  fhicl::Name("SinglePhotonResponse"),
159  fhicl::Comment(
160  "parameters describing the single photon response"
161  " (SinglePhotonPulseFunctionTool tool)"
162  )
163  };
164 
165  fhicl::TableFragment<icarus::opdet::PMTsimulationAlgMaker::Config> algoConfig;
166 
167  fhicl::Atom<bool> writePhotons {
168  Name("WritePhotons"),
169  Comment
170  ("writes the scintillation photon contributing to the waveforms"),
171  false
172  };
173 
174  rndm::SeedAtom EfficiencySeed {
175  Name("EfficiencySeed"),
176  Comment("fix the seed for stochastic photon detection efficiency")
177  };
178 
179  rndm::SeedAtom DarkNoiseSeed {
180  Name("DarkNoiseSeed"),
181  Comment("fix the seed for stochastic dark noise generation")
182  };
183 
184  rndm::SeedAtom ElectronicsNoiseSeed {
185  Name("ElectronicsNoiseSeed"),
186  Comment("fix the seed for stochastic electronics noise generation")
187  };
188 
189  fhicl::Atom<std::string> electronicsNoiseRandomEngine {
190  Name("ElectronicsNoiseRandomEngine"),
191  Comment("type of random engine to use for electronics noise"),
192  "HepJamesRandom"
193  };
194 
195  fhicl::Atom<std::string> darkNoiseRandomEngine {
196  Name("DarkNoiseRandomEngine"),
197  Comment("type of random engine to use for dark noise"),
198  "HepJamesRandom"
199  };
200 
201  }; // struct Config
202 
203  using Parameters = art::EDProducer::Table<Config>;
204 
205  explicit SimPMTIcarus(Parameters const& config);
206 
207 
208  // Plugins should not be copied or assigned.
209  SimPMTIcarus(SimPMTIcarus const &) = delete;
210  SimPMTIcarus(SimPMTIcarus &&) = delete;
211  SimPMTIcarus & operator = (SimPMTIcarus const &) = delete;
212  SimPMTIcarus & operator = (SimPMTIcarus &&) = delete;
213 
214  // Required functions.
215  void produce(art::Event & e) override;
216 
217  private:
218 
219  /// Type of single photoelectron response function.
222 
223  /// Input tag for simulated scintillation photons (or photoelectrons).
224  art::InputTag fInputModuleName;
225 
226  bool fWritePhotons { false }; ///< Whether to save contributing photons.
227 
228  /// Single photoelectron response function.
229  std::unique_ptr<SinglePhotonResponseFunc_t> const fSinglePhotonResponseFunc;
230 
231  /// The actual simulation algorithm.
233 
234  CLHEP::HepRandomEngine& fEfficiencyEngine;
235  CLHEP::HepRandomEngine& fDarkNoiseEngine;
236  CLHEP::HepRandomEngine& fElectronicsNoiseEngine;
237 
238 
239  /// True if `firstTime()` has already been called.
240  std::atomic_flag fNotFirstTime;
241 
242  /// Returns whether no other event has been processed yet.
243  bool firstTime() { return !fNotFirstTime.test_and_set(); }
244 
245  }; // class SimPMTIcarus
246 
247 
248  // ---------------------------------------------------------------------------
249  // --- SimPMTIcarus implementation
250  // ---------------------------------------------------------------------------
252  : EDProducer{config}
253  , fInputModuleName(config().inputModuleLabel())
254  , fWritePhotons(config().writePhotons())
255  , fSinglePhotonResponseFunc{
256  art::make_tool<icarus::opdet::SinglePhotonPulseFunctionTool>
257  (config().SinglePhotonResponse.get<fhicl::ParameterSet>())
258  ->getPulseFunction()
259  }
260  , makePMTsimulator(config().algoConfig())
261  , fEfficiencyEngine(art::ServiceHandle<rndm::NuRandomService>()->createEngine
262  (*this, "HepJamesRandom", "Efficiencies", config().EfficiencySeed)
263  )
264  , fDarkNoiseEngine(art::ServiceHandle<rndm::NuRandomService>()->createEngine(
265  *this,
266  config().darkNoiseRandomEngine(),
267  "DarkNoise",
268  config().DarkNoiseSeed
269  ))
270  , fElectronicsNoiseEngine(art::ServiceHandle<rndm::NuRandomService>()->createEngine(
271  *this,
272  config().electronicsNoiseRandomEngine(),
273  "ElectronicsNoise",
274  config().ElectronicsNoiseSeed
275  ))
276  {
277  // Call appropriate produces<>() functions here.
278  produces<std::vector<raw::OpDetWaveform>>();
279  if (fWritePhotons) produces<std::vector<sim::SimPhotons> >();
280 
281  fNotFirstTime.clear(); // superfluous in C++20
282  } // SimPMTIcarus::SimPMTIcarus()
283 
284 
285  // ---------------------------------------------------------------------------
286  void SimPMTIcarus::produce(art::Event & e)
287  {
288  mf::LogDebug("SimPMTIcarus") << e.id();
289 
290  //
291  // fetch the input
292  //
293  auto pulseVecPtr = std::make_unique< std::vector< raw::OpDetWaveform > > ();
294 
295  std::unique_ptr<std::vector<sim::SimPhotons>> simphVecPtr;
296  if (fWritePhotons)
297  simphVecPtr = std::make_unique< std::vector< sim::SimPhotons > > ();
298 
299  //
300  // prepare the output
301  //
302  art::Handle<std::vector<sim::SimPhotons> > pmtVector;
303  art::Handle<std::vector<sim::SimPhotonsLite> > pmtLiteVector;
304  pmtVector = e.getHandle< std::vector<sim::SimPhotons> >(fInputModuleName);
305  if(!pmtVector.isValid())
306  pmtLiteVector = e.getHandle< std::vector<sim::SimPhotonsLite> >(fInputModuleName);
307 
308  auto const clockData =
309  art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(e);
310  //
311  // prepare the algorithm
312  //
313  auto PMTsimulator = makePMTsimulator(
314  *(lar::providerFrom<detinfo::LArPropertiesService>()),
315  clockData,
321  );
322 
323  if (firstTime()) {
324  mf::LogDebug log { "SimPMTIcarus" };
325  log << "PMT simulation configuration (first event):\n";
326  PMTsimulator->printConfiguration(log);
327  } // if first time
328 
329  //
330  // run the algorithm
331  //
332  unsigned int nopch = 0;
333  if(pmtVector.isValid()) {
334  nopch = pmtVector->size();
335  for(auto const& photons : *pmtVector) {
336 
337  // Make an empty SimPhotonsLite with the same channel number.
338 
339  sim::SimPhotonsLite lite_photons(photons.OpChannel());
340 
341  auto const& [ channelWaveforms, photons_used ]
342  = PMTsimulator->simulate(photons, lite_photons);
343  std::move(
344  channelWaveforms.cbegin(), channelWaveforms.cend(),
345  std::back_inserter(*pulseVecPtr)
346  );
347  if (simphVecPtr && photons_used)
348  simphVecPtr->emplace_back(std::move(photons_used.value()));
349 
350  } // for
351  }
352  else if(pmtLiteVector.isValid()) {
353  nopch = pmtLiteVector->size();
354  for(auto const& lite_photons : *pmtLiteVector) {
355 
356  // Make an empty SimPhotons with the same channel number.
357 
358  sim::SimPhotons photons(lite_photons.OpChannel);
359 
360  auto const& [ channelWaveforms, photons_used ]
361  = PMTsimulator->simulate(photons, lite_photons);
362  std::move(
363  channelWaveforms.cbegin(), channelWaveforms.cend(),
364  std::back_inserter(*pulseVecPtr)
365  );
366 
367  } // for
368  }
369 
370  mf::LogInfo("SimPMTIcarus") << "Generated " << pulseVecPtr->size()
371  << " waveforms out of " << nopch << " optical channels.";
372 
373  //
374  // save the result
375  //
376  e.put(std::move(pulseVecPtr));
377  if (simphVecPtr) e.put(std::move(simphVecPtr));
378  } // SimPMTIcarus::produce()
379 
380 
381 // ---------------------------------------------------------------------------
382  DEFINE_ART_MODULE(SimPMTIcarus)
383 
384 
385  // ---------------------------------------------------------------------------
386 
387 } // namespace icarus::opdet
fhicl::Atom< std::string > electronicsNoiseRandomEngine
Simulates the digitization of ICARUS PMT response and trigger.
Utilities related to art service access.
icarus::opdet::PMTsimulationAlgMaker makePMTsimulator
The actual simulation algorithm.
Tool to create a pulse function for PMT single photon response.
CLHEP::HepRandomEngine & fDarkNoiseEngine
Abstract interface of shape of a pulse from one photoelectron.
art::EDProducer::Table< Config > Parameters
fhicl::Atom< std::string > darkNoiseRandomEngine
icarus::opdet::SinglePhotonResponseFunc_t const SinglePhotonResponseFunc_t
Type of single photoelectron response function.
CLHEP::HepRandomEngine & fElectronicsNoiseEngine
std::atomic_flag fNotFirstTime
True if firstTime() has already been called.
Simulation objects for optical detectors.
art::InputTag fInputModuleName
Input tag for simulated scintillation photons (or photoelectrons).
std::unique_ptr< SinglePhotonResponseFunc_t > const fSinglePhotonResponseFunc
Single photoelectron response function.
Interface for a function describing a pulse from a photoelectron.
BEGIN_PROLOG vertical distance to the surface Name
SimPMTIcarus(Parameters const &config)
Collection of photons which recorded on one channel.
Definition: SimPhotons.h:136
Compact representation of photons on a channel.
Definition: SimPhotons.h:103
SimPMTIcarus & operator=(SimPMTIcarus const &)=delete
Dimensioned variables representing space or time quantities.
do i e
CLHEP::HepRandomEngine & fEfficiencyEngine
Algorithms for the simulation of ICARUS PMT channels.
fhicl::TableFragment< icarus::opdet::PMTsimulationAlgMaker::Config > algoConfig
void produce(art::Event &e) override
fhicl::DelegatedParameter SinglePhotonResponse
bool fWritePhotons
Whether to save contributing photons.
bool firstTime()
Returns whether no other event has been processed yet.
fhicl::Atom< art::InputTag > inputModuleLabel
Returns a new PMTsimulationAlg with an updated configuration.