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
36 #include "nurandom/RandomUtils/NuRandomService.h"
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 
75 
77  : EDProducer{pset}
78  , fInputModule{pset.get<std::string>("InputModule")}
79  //, fQE{pset.get<double>("QE")}
80  , fSaturationScale{pset.get<float>("SaturationScale")}
81  , fDarkRate{pset.get<float>("DarkRate")}
82  // create a default random engine; obtain the random seed from NuRandomService,
83  // unless overridden in configuration with key "Seed"
84  , fEngine(art::ServiceHandle<rndm::NuRandomService>{}->createEngine(*this, pset, "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 
100  void OpMCDigi::AddTimedWaveform (int binTime, std::vector<double>& OldPulse, std::vector<double>& NewPulse)
101  {
102 
103  if( (binTime + NewPulse.size() ) > OldPulse.size()) {
104  OldPulse.resize(binTime + NewPulse.size());
105  }
106 
107  // Add shifted NewWaveform to Waveform at pointer
108  for(size_t i = 0; i!=NewPulse.size(); ++i) {
109  OldPulse.at(binTime+i) += NewPulse.at(i);
110  }
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>{}->UseLitePhotons();
121 
122  // Service for determining opdet responses
124 
125  double const TimeBegin_ns = fTimeBegin * 1000;
126  double const TimeEnd_ns = fTimeEnd * 1000;
127  double const SampleFreq_ns = fSampleFreq / 1000;
128 
129  int const nSamples = ( TimeEnd_ns-TimeBegin_ns)*SampleFreq_ns;
130  int const NOpChannels = odresponse->NOpChannels();
131 
132 
133  // This vector will store all the waveforms we will make
134  std::vector<std::vector<double> > PulsesFromDetPhotons(NOpChannels,std::vector<double>(nSamples,0.0));
135 
136  if(!fUseLitePhotons) {
137  // Read in the Sim Photons
139  // For every OpDet:
140  for(auto const& pr : ThePhotCollection) {
141  const sim::SimPhotons& ThePhot=pr.second;
142 
143  int const Ch = ThePhot.OpChannel();
144  int readoutCh;
145 
146  // For every photon in the hit:
147  for(const sim::OnePhoton& Phot: ThePhot) {
148  // Sample a random subset according to QE
149  if(!odresponse->detected(Ch, Phot, readoutCh)) {
150  continue;
151  }
152 
153  // Convert photon arrival time to the appropriate bin,
154  // dictated by fSampleFreq. Photon arrival time is in ns,
155  // beginning time in us, and sample frequency in MHz. Notice
156  // that we have to accommodate for the beginning time
157  if((Phot.Time > TimeBegin_ns) && (Phot.Time < TimeEnd_ns)) {
158  auto const binTime = static_cast<int>((Phot.Time - TimeBegin_ns) * SampleFreq_ns);
159  AddTimedWaveform( binTime, PulsesFromDetPhotons[readoutCh], fSinglePEWaveform );
160  }
161  } // for each Photon in SimPhotons
162  }
163  }
164  else {
165  auto const photons = *evt.getValidHandle<std::vector<sim::SimPhotonsLite>>("largeant");
166  // For every OpDet:
167  for (auto const& photon : photons) {
168  int const Ch=photon.OpChannel;
169  int readoutCh;
170 
171  std::map<int, int> PhotonsMap = photon.DetectedPhotons;
172 
173  // For every photon in the hit:
174  for(auto const& pr : photon.DetectedPhotons) {
175  for(int i = 0; i < pr.second; i++) {
176  // Sample a random subset according to QE
177  if(odresponse->detectedLite(Ch, readoutCh)) {
178  // Convert photon arrival time to the appropriate bin, dictated by fSampleFreq.
179  // Photon arrival time is in ns, beginning time in us, and sample frequency in MHz.
180  // Notice that we have to accommodate for the beginning time
181  if((pr.first > TimeBegin_ns) && (pr.first < TimeEnd_ns)) {
182  auto const binTime = static_cast<int>((pr.first - TimeBegin_ns) * SampleFreq_ns);
183  AddTimedWaveform( binTime, PulsesFromDetPhotons[readoutCh], fSinglePEWaveform );
184  }
185  } // random QE cut
186  }
187  } // for each Photon in SimPhotons
188  }
189  }
190 
191  // Create vector of output objects, add dark noise and apply
192  // saturation
193 
194  std::vector<raw::OpDetPulse*> ThePulses(NOpChannels);
195  for(int iCh=0; iCh!=NOpChannels; ++iCh) {
196  PulsesFromDetPhotons[iCh].resize((TimeEnd_ns - TimeBegin_ns) * SampleFreq_ns);
197 
198  // Add dark noise
199  double const MeanDarkPulses = fDarkRate * (fTimeEnd-fTimeBegin) / 1000000;
200  unsigned const int NumberOfPulses = fPoissonRandom.fire(MeanDarkPulses);
201 
202  for(size_t i=0; i!=NumberOfPulses; ++i) {
203  double const PulseTime = (fTimeEnd-fTimeBegin)*fFlatRandom.fire(1.0);
204  int const binTime = static_cast<int>(PulseTime * fSampleFreq);
205 
206  AddTimedWaveform(binTime, PulsesFromDetPhotons[iCh], fSinglePEWaveform);
207  }
208 
209  // Apply saturation for large signals
210  for(size_t i=0; i!=PulsesFromDetPhotons[iCh].size(); ++i) {
211  if(PulsesFromDetPhotons[iCh].at(i)>fSaturationScale) PulsesFromDetPhotons[iCh].at(i) = fSaturationScale;
212  }
213 
214  // Produce ADC pulse of integers rather than doubles
215 
216  std::vector<short> shortvec;
217 
218  for(size_t i=0; i!=PulsesFromDetPhotons[iCh].size(); ++i) {
219  // Throw randoms to fairly sample +ve and -ve side of doubles
220  int ThisSample = PulsesFromDetPhotons[iCh].at(i);
221  if(ThisSample>0) {
222  if(fFlatRandom.fire(1.0) > (ThisSample - int(ThisSample)))
223  shortvec.push_back(int(ThisSample));
224  else
225  shortvec.push_back(int(ThisSample)+1);
226  }
227  else {
228  if(fFlatRandom.fire(1.0) > (int(ThisSample)-ThisSample))
229  shortvec.push_back(int(ThisSample));
230  else
231  shortvec.push_back(int(ThisSample)-1);
232  }
233  }
234 
235  StoragePtr->emplace_back(iCh, shortvec ,0, fTimeBegin);
236 
237  } // for each OpDet in SimPhotonsCollection
238 
239  evt.put(std::move(StoragePtr));
240  }
241 }
242 
Store parameters for running LArG4.
int OpChannel() const
Returns the optical channel number this object is associated to.
Definition: SimPhotons.h:254
void AddTimedWaveform(int time, std::vector< double > &OldPulse, std::vector< double > &NewPulse)
std::string string
Definition: nybbler.cc:12
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.h:20
All information of a photon entering the sensitive optical detector volume.
Definition: SimPhotons.h:64
void produce(art::Event &) override
virtual bool detected(int OpChannel, const sim::OnePhoton &Phot, int &newOpChannel) const
CLHEP::HepRandomEngine & fEngine
OpMCDigi(const fhicl::ParameterSet &)
Simulation objects for optical detectors.
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
def move(depos, offset)
Definition: depos.py:107
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
Definition: DataViewImpl.h:441
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
virtual bool detectedLite(int OpChannel, int &newOpChannel) const
std::string fInputModule
Index NOpChannels(Index)
Collection of photons which recorded on one channel.
Definition: SimPhotons.h:136
std::vector< double > fSinglePEWaveform
CLHEP::RandFlat fFlatRandom
TCEvent evt
Definition: DataStructs.cxx:7
Collection of sim::SimPhotons, indexed by channel number.
Definition: SimPhotons.h:192
static sim::SimPhotonsCollection GetSimPhotonsCollection(const art::Event &evt, std::string moduleLabel)
bool UseLitePhotons() const
CLHEP::RandPoisson fPoissonRandom