All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
opDetDigitizerSBND_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 // Class: opDetDigitizerSBND
3 // Module Type: producer
4 // File: opDetDigitizerSBND_module.cc
5 //
6 // This module produces digitized waveforms of the optical detectors
7 // Created by L. Paulucci, F. Marinho, and I.L. de Icaza
8 ////////////////////////////////////////////////////////////////////////
9 
10 #include "art/Framework/Core/EDProducer.h"
11 #include "art/Framework/Core/ModuleMacros.h"
12 #include "art/Framework/Principal/Event.h"
13 #include "art/Framework/Principal/Handle.h"
14 #include "art/Framework/Principal/Run.h"
15 #include "canvas/Utilities/InputTag.h"
16 #include "canvas/Utilities/Exception.h"
17 #include "fhiclcpp/types/Atom.h"
18 #include "fhiclcpp/types/TableFragment.h"
19 #include "messagefacility/MessageLogger/MessageLogger.h"
20 
21 #include "nurandom/RandomUtils/NuRandomService.h"
22 #include "CLHEP/Random/JamesRandom.h"
23 
24 #include <memory>
25 #include <vector>
26 #include <cmath>
27 #include <string>
28 #include <map>
29 #include <unordered_map>
30 #include <set>
31 #include <sstream>
32 #include <fstream>
33 #include <thread>
34 #include <cstdlib>
35 #include <stdexcept>
36 
45 
46 #include "TMath.h"
47 #include "TH1D.h"
48 #include "TRandom3.h"
49 #include "TF1.h"
50 
56 
57 namespace opdet {
58 
59  /*
60  * This module simulates the digitization of SBND photon detectors response.
61  *
62  * The module has an interface to the simulation algorithms for PMTs and Arapucas,
63  * opdet::DigiPMTSBNDAlg e opdet::DigiArapucaSBNDAlg.
64  *
65  * Input
66  * ======
67  * The module utilizes as input a collection of `sim::SimPhotons` or `sim::SimPhotonsLite`, each
68  * containing the photons propagated to a single optical detector channel.
69  *
70  * Output
71  * =======
72  * A collection of optical detector waveforms (`std::vector<raw::OpDetWaveform>`) is produced.
73  *
74  * Requirements
75  * =============
76  * This module currently requires LArSoft services:
77  * * `DetectorClocksService` for timing conversions and settings
78  * * `LArPropertiesService` for the scintillation yield(s)
79  *
80  */
81 
82  class opDetDigitizerSBND;
83 
84  class opDetDigitizerSBND : public art::EDProducer {
85  public:
86  struct Config {
87  using Comment = fhicl::Comment;
88  using Name = fhicl::Name;
89 
90  fhicl::Atom<art::InputTag> InputModuleName {
91  Name("InputModule"),
92  Comment("Simulated photons to be digitized")
93  };
94  fhicl::Atom<double> WaveformSize {
95  Name("WaveformSize"),
96  Comment("Value to initialize the waveform vector in ns. It is resized in the algorithms according to readout window of PDs")
97  };
98  fhicl::Atom<bool> UseSimPhotonsLite {
99  Name("UseSimPhotonsLite"),
100  Comment("Whether SimPhotonsLite or SimPhotons will be used")
101  };
102 
103  fhicl::Atom<bool> ApplyTriggers {
104  Name("ApplyTriggers"),
105  Comment("Whether to apply trigger algorithm to waveforms"),
106  true
107  };
108 
109  fhicl::Atom<unsigned> NThreads {
110  Name("NThreads"),
111  Comment("Number of threads to split waveform process into. Defaults to 1.\
112  Set 0 to autodetect. Autodection will first check $SBNDCODE_OPDETSIM_NTHREADS for number of threads. \
113  If this is not set, then NThreads is set to the number of hardware cores on the host machine."),
114  1
115  };
116 
117  fhicl::TableFragment<opdet::DigiPMTSBNDAlgMaker::Config> pmtAlgoConfig;
118  fhicl::TableFragment<opdet::DigiArapucaSBNDAlgMaker::Config> araAlgoConfig;
119  fhicl::TableFragment<opdet::opDetSBNDTriggerAlg::Config> trigAlgoConfig;
120  }; // struct Config
121 
122  using Parameters = art::EDProducer::Table<Config>;
123 
124  explicit opDetDigitizerSBND(Parameters const& config);
125  // The destructor generated by the compiler is fine for classes
126  // without bare pointers or other resource use.
127  // Add a destructor to deal with random number generator pointer
129 
130  // Plugins should not be copied or assigned.
131  opDetDigitizerSBND(opDetDigitizerSBND const &) = delete;
135 
136  // Required functions.
137  void produce(art::Event & e) override;
138 
139  opdet::sbndPDMapAlg map; //map for photon detector types
140  unsigned int nChannels = map.size();
141  std::vector<raw::OpDetWaveform> fWaveforms; // holder for un-triggered waveforms
142 
143  private:
145  std::unordered_map< raw::Channel_t, std::vector<double> > fFullWaveforms;
146 
148  unsigned fPMTBaseline;
150  unsigned fNThreads;
151  // digitizer workers
152  std::vector<opdet::opDetDigitizerWorker> fWorkers;
153  std::vector<std::vector<raw::OpDetWaveform>> fTriggeredWaveforms;
154  std::vector<std::thread> fWorkerThreads;
155 
156  // product containers
157  std::vector<art::Handle<std::vector<sim::SimPhotonsLite>>> fPhotonLiteHandles;
158  std::vector<art::Handle<std::vector<sim::SimPhotons>>> fPhotonHandles;
159 
160  // sync stuff
163  bool fFinished;
164 
165  // trigger algorithm
167  };
168 
170  : EDProducer{config}
171  , fApplyTriggers(config().ApplyTriggers())
172  , fUseSimPhotonsLite(config().UseSimPhotonsLite())
173  , fPMTBaseline(config().pmtAlgoConfig().pmtbaseline())
174  , fArapucaBaseline(config().araAlgoConfig().baseline())
175  , fTriggerAlg(config().trigAlgoConfig())
176  {
177  opDetDigitizerWorker::Config wConfig( config().pmtAlgoConfig(), config().araAlgoConfig());
178 
179  fNThreads = config().NThreads();
180  if (fNThreads == 0) { // autodetect -- first check env var
181  const char *env = std::getenv("SBNDCODE_OPDETSIM_NTHREADS");
182  // try to parse into positive integer
183  if (env != NULL) {
184  try {
185  int n_threads = std::stoi(env);
186  if (n_threads <= 0) {
187  throw std::invalid_argument("Expect positive integer");
188  }
189  fNThreads = n_threads;
190  }
191  catch (...) {
192  mf::LogError("OpDetDigitizer") << "Unable to parse number of threads "
193  << "in environment variable (SBNDCODE_OPDETSIM_NTHREADS): (" << env << ").\n"
194  << "Setting Number opdet threads to 1." << std::endl;
195  fNThreads = 1;
196  }
197  }
198  }
199 
200  if (fNThreads == 0) { // autodetect -- now try to get number of cpu's
201  fNThreads = std::thread::hardware_concurrency();
202  }
203  if (fNThreads == 0) { // autodetect failed
204  fNThreads = 1;
205  }
206  mf::LogInfo("OpDetDigitizer") << "Digitizing on n threads: " << fNThreads << std::endl;
207 
208  wConfig.nThreads = fNThreads;
209 
210  wConfig.UseSimPhotonsLite = config().UseSimPhotonsLite();
211  wConfig.InputModuleName = config().InputModuleName();
212 
213  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataForJob();
214  auto const detProp = art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataForJob(clockData);
215  wConfig.Sampling = (clockData.OpticalClock().Frequency()) / 1000.0; //in GHz
216  wConfig.Sampling_Daphne = config().araAlgoConfig().DaphneFrequency() / 1000.0; //in GHz
217  wConfig.EnableWindow = fTriggerAlg.TriggerEnableWindow(clockData, detProp); // us
218  wConfig.Nsamples = (wConfig.EnableWindow[1] - wConfig.EnableWindow[0]) * 1000. /*us -> ns*/ * wConfig.Sampling /* GHz */;
219  wConfig.Nsamples_Daphne = (wConfig.EnableWindow[1] - wConfig.EnableWindow[0]) * 1000. /*us -> ns*/ * wConfig.Sampling_Daphne /* GHz */;
220 
221  fFinished = false;
222 
223  fWorkers.reserve(fNThreads);
224  fTriggeredWaveforms.reserve(fNThreads);
225  for (unsigned i = 0; i < fNThreads; i++) {
226  // Set random number gen seed from the NuRandomService
227  art::ServiceHandle<rndm::NuRandomService> seedSvc;
228  CLHEP::HepJamesRandom *engine = new CLHEP::HepJamesRandom;
229  seedSvc->registerEngine(rndm::NuRandomService::CLHEPengineSeeder(engine), "opDetDigitizerSBND" + std::to_string(i));
230 
231  fTriggeredWaveforms.emplace_back();
232 
233  // setup worker
234  fWorkers.emplace_back(i, wConfig, engine, fTriggerAlg);
235  fWorkers[i].SetPhotonLiteHandles(&fPhotonLiteHandles);
236  fWorkers[i].SetPhotonHandles(&fPhotonHandles);
237  fWorkers[i].SetWaveformHandle(&fWaveforms);
238  fWorkers[i].SetTriggeredWaveformHandle(&fTriggeredWaveforms[i]);
239 
240  // start worker thread
241  fWorkerThreads.emplace_back(opdet::opDetDigitizerWorkerThread,
242  std::cref(fWorkers[i]),
243  clockData,
244  std::ref(fSemStart),
245  std::ref(fSemFinish),
246  fApplyTriggers,
247  &fFinished);
248  }
249 
250  // Call appropriate produces<>() functions here.
251  produces< std::vector< raw::OpDetWaveform > >();
252  }
253 
255  {
256  // cleanup all of the workers
257  fFinished = true;
259 
260  // join the threads
261  for (std::thread &thread : fWorkerThreads) thread.join();
262 
263  }
264 
265  void opDetDigitizerSBND::produce(art::Event & e)
266  {
267  std::unique_ptr< std::vector< raw::OpDetWaveform > > pulseVecPtr(std::make_unique< std::vector< raw::OpDetWaveform > > ());
268  // Implementation of required member function here.
269  mf::LogInfo("opDetDigitizer") << "Event: " << e.id().event() << std::endl;
270 
271  // setup the waveforms
272  fWaveforms = std::vector<raw::OpDetWaveform> (nChannels);
273 
274  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(e);
275  auto const detProp = art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(e, clockData);
276 
277  if (fUseSimPhotonsLite) {
278  fPhotonLiteHandles.clear();
279  //Get *ALL* SimPhotonsCollectionLite from Event
280  fPhotonLiteHandles = e.getMany<std::vector<sim::SimPhotonsLite>>();
281  if (fPhotonLiteHandles.size() == 0)
282  mf::LogError("OpDetDigitizer") << "sim::SimPhotonsLite not found -> No Optical Detector Simulation!\n";
283  }
284  else {
285  fPhotonHandles.clear();
286  //Get *ALL* SimPhotonsCollection from Event
287  fPhotonLiteHandles = e.getMany<std::vector<sim::SimPhotonsLite>>();
288  if (fPhotonHandles.size() == 0)
289  mf::LogError("OpDetDigitizer") << "sim::SimPhotons not found -> No Optical Detector Simulation!\n";
290  }
291  // Start the workers!
292  // Run the digitizer over the full readout window
295 
296  if (fApplyTriggers) {
297  // find the trigger locations for the waveforms
298  for (const raw::OpDetWaveform &waveform : fWaveforms) {
299  raw::Channel_t ch = waveform.ChannelNumber();
300  // skip light channels which don't correspond to readout channels
301  if (ch == std::numeric_limits<raw::Channel_t>::max() /* "NULL" value*/) {
302  continue;
303  }
304  raw::ADC_Count_t baseline = (map.isPDType(ch, "pmt_uncoated") || map.isPDType(ch, "pmt_coated")) ?
306  fTriggerAlg.FindTriggerLocations(clockData, detProp, waveform, baseline);
307  }
308 
309  // combine the triggers
311  // Start the workers!
312  // Apply the trigger locations
315 
316  for (std::vector<raw::OpDetWaveform> &waveforms : fTriggeredWaveforms) {
317  // move these waveforms into the pulseVecPtr
318  pulseVecPtr->reserve(pulseVecPtr->size() + waveforms.size());
319  std::move(waveforms.begin(), waveforms.end(), std::back_inserter(*pulseVecPtr));
320  }
321  // clean up the vector
322  for (unsigned i = 0; i < fTriggeredWaveforms.size(); i++) {
323  fTriggeredWaveforms[i] = std::vector<raw::OpDetWaveform>();
324  }
325 
326  // put the waveforms in the event
327  e.put(std::move(pulseVecPtr));
328  // clear out the triggers
329  fTriggerAlg.ClearTriggerLocations();
330 
331  }
332  else {
333  // put the full waveforms in the event
334  for (const raw::OpDetWaveform &waveform : fWaveforms) {
335  if (waveform.ChannelNumber() == std::numeric_limits<raw::Channel_t>::max() /* "NULL" value*/) {
336  continue;
337  }
338  pulseVecPtr->push_back(waveform);
339  }
340  e.put(std::move(pulseVecPtr));
341  }
342 
343  // clear out the full waveforms
344  fWaveforms.clear();
345 
346  }//produce end
347 
348  DEFINE_ART_MODULE(opdet::opDetDigitizerSBND)
349 
350 }//closing namespace
size_t size() const
std::vector< art::Handle< std::vector< sim::SimPhotons > > > fPhotonHandles
void opDetDigitizerWorkerThread(const opDetDigitizerWorker &worker, detinfo::DetectorClocksData const &clockData, opDetDigitizerWorker::Semaphore &sem_start, opDetDigitizerWorker::Semaphore &sem_finish, bool ApplyTriggerLocations, bool *finished)
std::vector< opdet::opDetDigitizerWorker > fWorkers
art::EDProducer::Table< Config > Parameters
fhicl::TableFragment< opdet::DigiPMTSBNDAlgMaker::Config > pmtAlgoConfig
void StartopDetDigitizerWorkers(unsigned n_workers, opDetDigitizerWorker::Semaphore &sem_start)
fhicl::TableFragment< opdet::DigiArapucaSBNDAlgMaker::Config > araAlgoConfig
void FindTriggerLocations(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const raw::OpDetWaveform &waveform, raw::ADC_Count_t baseline)
bool isPDType(size_t ch, std::string pdname) const override
std::vector< art::Handle< std::vector< sim::SimPhotonsLite > > > fPhotonLiteHandles
void WaitopDetDigitizerWorkers(unsigned n_workers, opDetDigitizerWorker::Semaphore &sem_finish)
opdet::opDetDigitizerWorker::Semaphore fSemStart
fhicl::TableFragment< opdet::opDetSBNDTriggerAlg::Config > trigAlgoConfig
Simulation objects for optical detectors.
BEGIN_PROLOG baseline
BEGIN_PROLOG vertical distance to the surface Name
opdet::opDetDigitizerWorker::Semaphore fSemFinish
std::unordered_map< raw::Channel_t, std::vector< double > > fFullWaveforms
std::string to_string(WindowPattern const &pattern)
std::vector< std::thread > fWorkerThreads
object containing MC truth information necessary for making RawDigits and doing back tracking ...
do i e
std::vector< raw::OpDetWaveform > fWaveforms
opDetDigitizerSBND & operator=(opDetDigitizerSBND const &)=delete
void produce(art::Event &e) override
std::vector< std::vector< raw::OpDetWaveform > > fTriggeredWaveforms
opdet::opDetSBNDTriggerAlg fTriggerAlg
fhicl::Atom< art::InputTag > InputModuleName
Tools and modules for checking out the basics of the Monte Carlo.
art framework interface to geometry description
auto const detProp
opDetDigitizerSBND(Parameters const &config)