LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
OpMCDigi_module.cc
Go to the documentation of this file.
1 // \file OpMCDigi.h
2 // \author Ben Jones and Christie Chiu, MIT, Sept 2012
3 // bjpjones@mit.edu, cschiu@mit.edu
4 //
5 // This module starts from MC truth sim::OnePhoton objects
6 // and produces a digitized waveform.
7 //
8 // It is assumed that the electronics response is linear,
9 // and the 1PE waveform can be described by a discreate
10 // response shape. The many PE response is then the linear
11 // superposition of the relevant function at the appropriate
12 // arrival times.
13 //
14 
15 // Framework includes
21 #include "fhiclcpp/ParameterSet.h"
22 
23 // LArSoft includes
30 
31 // CLHEP includes
32 #include "CLHEP/Random/RandFlat.h"
33 #include "CLHEP/Random/RandPoisson.h"
34 
35 // nurandom
37 
38 // C++ language includes
39 #include <cstring>
40 
41 namespace opdet {
42 
43  class OpMCDigi : public art::EDProducer {
44  public:
45  explicit OpMCDigi(const fhicl::ParameterSet&);
46 
47  private:
48  void produce(art::Event&) override;
49 
50  // The parameters we'll read from the .fcl file.
51  std::string fInputModule; // Input tag for OpDet collection
52  float fSampleFreq; // in MHz
53  float fTimeBegin; // in us
54  float fTimeEnd; // in us
55  //float fQE; // quantum efficiency of opdet
56  float fSaturationScale; // adc count w/ saturation occurs
57 
58  float fDarkRate; // Noise rate in Hz
59 
60  std::vector<double> fSinglePEWaveform;
61 
62  CLHEP::HepRandomEngine& fEngine;
63  CLHEP::RandFlat fFlatRandom;
64  CLHEP::RandPoisson fPoissonRandom;
65 
66  void AddTimedWaveform(int time, std::vector<double>& OldPulse, std::vector<double>& NewPulse);
67  };
68 }
69 
70 // Debug flag; only used during code development.
71 // const bool debug = true;
72 
73 namespace opdet {
74 
76  : EDProducer{pset}
77  , fInputModule{pset.get<std::string>("InputModule")} //, fQE{pset.get<double>("QE")}
78  , fSaturationScale{pset.get<float>("SaturationScale")}
79  , fDarkRate{pset.get<float>("DarkRate")}
80  // create a default random engine; obtain the random seed from NuRandomService,
81  // unless overridden in configuration with key "Seed"
83  pset,
84  "Seed"))
87  {
88  produces<std::vector<raw::OpDetPulse>>();
89 
91  fSampleFreq = odp->SampleFreq();
92  fTimeBegin = odp->TimeBegin();
93  fTimeEnd = odp->TimeEnd();
94  fSinglePEWaveform = odp->SinglePEWaveform();
95  }
96 
97  //-------------------------------------------------
98 
99  void OpMCDigi::AddTimedWaveform(int binTime,
100  std::vector<double>& OldPulse,
101  std::vector<double>& NewPulse)
102  {
103 
104  if ((binTime + NewPulse.size()) > OldPulse.size()) {
105  OldPulse.resize(binTime + NewPulse.size());
106  }
107 
108  // Add shifted NewWaveform to Waveform at pointer
109  for (size_t i = 0; i != NewPulse.size(); ++i) {
110  OldPulse.at(binTime + i) += NewPulse.at(i);
111  }
112  }
113 
114  //-------------------------------------------------
115 
117  {
118  auto StoragePtr = std::make_unique<std::vector<raw::OpDetPulse>>();
119 
120  bool const fUseLitePhotons = art::ServiceHandle<sim::LArG4Parameters const>
121  {
122  } -> UseLitePhotons();
123 
124  // Service for determining opdet responses
126 
127  double const TimeBegin_ns = fTimeBegin * 1000;
128  double const TimeEnd_ns = fTimeEnd * 1000;
129  double const SampleFreq_ns = fSampleFreq / 1000;
130 
131  int const nSamples = (TimeEnd_ns - TimeBegin_ns) * SampleFreq_ns;
132  int const NOpChannels = odresponse->NOpChannels();
133 
134  // This vector will store all the waveforms we will make
135  std::vector<std::vector<double>> PulsesFromDetPhotons(NOpChannels,
136  std::vector<double>(nSamples, 0.0));
137 
138  if (!fUseLitePhotons) {
139  // Read in the Sim Photons
140  sim::SimPhotonsCollection ThePhotCollection =
142  // For every OpDet:
143  for (auto const& pr : ThePhotCollection) {
144  const sim::SimPhotons& ThePhot = pr.second;
145 
146  int const Ch = ThePhot.OpChannel();
147  int readoutCh;
148 
149  // For every photon in the hit:
150  for (const sim::OnePhoton& Phot : ThePhot) {
151  // Sample a random subset according to QE
152  if (!odresponse->detected(Ch, Phot, readoutCh)) { continue; }
153 
154  // Convert photon arrival time to the appropriate bin,
155  // dictated by fSampleFreq. Photon arrival time is in ns,
156  // beginning time in us, and sample frequency in MHz. Notice
157  // that we have to accommodate for the beginning time
158  if ((Phot.Time > TimeBegin_ns) && (Phot.Time < TimeEnd_ns)) {
159  auto const binTime = static_cast<int>((Phot.Time - TimeBegin_ns) * SampleFreq_ns);
160  AddTimedWaveform(binTime, PulsesFromDetPhotons[readoutCh], fSinglePEWaveform);
161  }
162  } // for each Photon in SimPhotons
163  }
164  }
165  else {
166  auto const photons = *evt.getValidHandle<std::vector<sim::SimPhotonsLite>>("largeant");
167  // For every OpDet:
168  for (auto const& photon : photons) {
169  int const Ch = photon.OpChannel;
170  int readoutCh;
171 
172  std::map<int, int> PhotonsMap = photon.DetectedPhotons;
173 
174  // For every photon in the hit:
175  for (auto const& pr : photon.DetectedPhotons) {
176  for (int i = 0; i < pr.second; i++) {
177  // Sample a random subset according to QE
178  if (odresponse->detectedLite(Ch, readoutCh)) {
179  // Convert photon arrival time to the appropriate bin, dictated by fSampleFreq.
180  // Photon arrival time is in ns, beginning time in us, and sample frequency in MHz.
181  // Notice that we have to accommodate for the beginning time
182  if ((pr.first > TimeBegin_ns) && (pr.first < TimeEnd_ns)) {
183  auto const binTime = static_cast<int>((pr.first - TimeBegin_ns) * SampleFreq_ns);
184  AddTimedWaveform(binTime, PulsesFromDetPhotons[readoutCh], fSinglePEWaveform);
185  }
186  } // random QE cut
187  }
188  } // for each Photon in SimPhotons
189  }
190  }
191 
192  // Create vector of output objects, add dark noise and apply
193  // saturation
194 
195  std::vector<raw::OpDetPulse*> ThePulses(NOpChannels);
196  for (int iCh = 0; iCh != NOpChannels; ++iCh) {
197  PulsesFromDetPhotons[iCh].resize((TimeEnd_ns - TimeBegin_ns) * SampleFreq_ns);
198 
199  // Add dark noise
200  double const MeanDarkPulses = fDarkRate * (fTimeEnd - fTimeBegin) / 1000000;
201  unsigned const int NumberOfPulses = fPoissonRandom.fire(MeanDarkPulses);
202 
203  for (size_t i = 0; i != NumberOfPulses; ++i) {
204  double const PulseTime = (fTimeEnd - fTimeBegin) * fFlatRandom.fire(1.0);
205  int const binTime = static_cast<int>(PulseTime * fSampleFreq);
206 
207  AddTimedWaveform(binTime, PulsesFromDetPhotons[iCh], fSinglePEWaveform);
208  }
209 
210  // Apply saturation for large signals
211  for (size_t i = 0; i != PulsesFromDetPhotons[iCh].size(); ++i) {
212  if (PulsesFromDetPhotons[iCh].at(i) > fSaturationScale)
213  PulsesFromDetPhotons[iCh].at(i) = fSaturationScale;
214  }
215 
216  // Produce ADC pulse of integers rather than doubles
217 
218  std::vector<short> shortvec;
219 
220  for (size_t i = 0; i != PulsesFromDetPhotons[iCh].size(); ++i) {
221  // Throw randoms to fairly sample +ve and -ve side of doubles
222  int ThisSample = PulsesFromDetPhotons[iCh].at(i);
223  if (ThisSample > 0) {
224  if (fFlatRandom.fire(1.0) > (ThisSample - int(ThisSample)))
225  shortvec.push_back(int(ThisSample));
226  else
227  shortvec.push_back(int(ThisSample) + 1);
228  }
229  else {
230  if (fFlatRandom.fire(1.0) > (int(ThisSample) - ThisSample))
231  shortvec.push_back(int(ThisSample));
232  else
233  shortvec.push_back(int(ThisSample) - 1);
234  }
235  }
236 
237  StoragePtr->emplace_back(iCh, shortvec, 0, fTimeBegin);
238 
239  } // for each OpDet in SimPhotonsCollection
240 
241  evt.put(std::move(StoragePtr));
242  }
243 }
244 
base_engine_t & createEngine(seed_t seed)
Store parameters for running LArG4.
int OpChannel() const
Returns the optical channel number this object is associated to.
Definition: SimPhotons.h:239
void AddTimedWaveform(int time, std::vector< double > &OldPulse, std::vector< double > &NewPulse)
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.cc:6
All information of a photon entering the sensitive optical detector volume.
Definition: SimPhotons.h:60
void produce(art::Event &) override
virtual bool detected(int OpChannel, const sim::OnePhoton &Phot, int &newOpChannel) const
CLHEP::HepRandomEngine & fEngine
OpMCDigi(const fhicl::ParameterSet &)
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Event.h:77
Simulation objects for optical detectors.
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:65
An art service to assist in the distribution of guaranteed unique seeds to all engines within an art ...
virtual bool detectedLite(int OpChannel, int &newOpChannel) const
std::string fInputModule
Collection of photons which recorded on one channel.
Definition: SimPhotons.h:127
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
std::vector< double > fSinglePEWaveform
CLHEP::RandFlat fFlatRandom
TCEvent evt
Definition: DataStructs.cxx:8
Collection of sim::SimPhotons, indexed by channel number.
Definition: SimPhotons.h:178
static sim::SimPhotonsCollection GetSimPhotonsCollection(const art::Event &evt, std::string moduleLabel)
CLHEP::RandPoisson fPoissonRandom