All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
LArG4_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file LArG4_module.cc
3 /// \brief Use Geant4 to run the LArSoft detector simulation
4 ///
5 /// \author seligman@nevis.columbia.edu
6 ////////////////////////////////////////////////////////////////////////
7 
8 /// This a module. It has the following functions:
9 ///
10 /// - Initialize Geant4 physics, detector geometry, and other
11 /// processing.
12 ///
13 /// - Accept sim::MCTruth objects from the MC branch of the FMWK
14 /// Event structure.
15 ///
16 /// - Pass the primary particles to the Geant4 simulation to calculate
17 /// "truth" information for the detector response.
18 ///
19 /// - Pass the truth information to the DetSim branch of the FMWK event.
20 
21 #include "nug4/G4Base/G4Helper.h"
22 
23 // C++ Includes
24 #include <cassert>
25 #include <map>
26 #include <set>
27 #include <sstream>
28 #include <sys/stat.h>
29 
30 // Framework includes
31 #include "art/Framework/Core/EDProducer.h"
32 #include "art/Framework/Core/ModuleMacros.h"
33 #include "art/Framework/Principal/Event.h"
34 #include "art/Framework/Principal/Handle.h"
35 #include "art/Framework/Services/Registry/ServiceHandle.h"
36 #include "art/Persistency/Common/PtrMaker.h"
37 #include "canvas/Persistency/Common/Assns.h"
38 #include "canvas/Persistency/Common/Ptr.h"
39 #include "canvas/Utilities/Exception.h"
40 #include "cetlib/search_path.h"
41 #include "cetlib_except/exception.h"
42 #include "fhiclcpp/ParameterSet.h"
43 #include "messagefacility/MessageLogger/MessageLogger.h"
44 
45 // art extensions
46 #include "nurandom/RandomUtils/NuRandomService.h"
47 
48 // LArSoft Includes
50 #include "larcorealg/CoreUtils/ParticleFilters.h" // util::PositionInVolumeFilter
53 #include "lardataalg/MCDumpers/MCDumpers.h" // sim::dump namespace
73 #include "nug4/G4Base/UserActionManager.h"
74 #include "nug4/ParticleNavigation/ParticleList.h"
75 #include "nusimdata/SimulationBase/MCTruth.h"
77 
78 // G4 Includes
79 #include "Geant4/G4LogicalVolumeStore.hh"
80 #include "Geant4/G4RunManager.hh"
81 #include "Geant4/G4SDManager.hh"
82 #include "Geant4/G4VSensitiveDetector.hh"
83 #include "Geant4/G4VUserDetectorConstruction.hh"
84 
85 //For energy depositions
87 
88 // Boost includes
89 #include "boost/algorithm/string.hpp"
90 
91 ///Geant4 interface
92 namespace larg4 {
93 
94  // Forward declarations within namespace.
95  class LArVoxelListAction;
96 
97  /**
98  * @brief Runs Geant4 simulation and propagation of electrons and photons to readout
99  *
100  * This module collects generated particles from one or more generators and
101  * processes them through Geant4.
102  *
103  * Input
104  * ------
105  *
106  * The module reads the particles to process from `simb::MCTruth` records.
107  * Each particle generator is required to produce a vector of such records:
108  * `std::vector<simb::MCTruth>`.
109  *
110  * The module allows two operation modes:
111  * -# process specific generators: the label of the generator modules to be
112  * processed is specified explicitly in `LArG4` configuration
113  * -# process all truth information generated so far: no generator is specified
114  * in the `LArG4` module configuration, and the module will process all
115  * data products of type `std::vector<simb::MCTruth>`, in a non-specified
116  * order
117  *
118  * For each `simb::MCTruth`, a Geant4 run is started.
119  * The interface with Geant4 is via a helper class provided by _nug4_.
120  * Only the particles in the truth record which have status code
121  * (`simb::MCParticle::StatusCode()`) equal to `1` are processed.
122  * These particles are called, in `LArG4` jargon, _primaries_.
123  *
124  *
125  * Output
126  * -------
127  *
128  * The `LArG4` module produces:
129  * * a collection of `sim::SimChannel`: each `sim::SimChannel` represents the
130  * set of energy depositions in liquid argon which drifted and were observed
131  * on a certain channel; it includes physics effects like attenuation,
132  * diffusion, electric field distortion, etc. Information of the generating
133  * Geant4 "track" is retained;
134  * * a collection of `sim::SimPhotons` or `sim::SimPhotonsLite`: each
135  * `sim::SimPhotons` represents the set of individual photons reaching a
136  * channel of the optical detector; it includes physics effects as well as
137  * quantum efficiency of the detector (to reduce data size early in the
138  * process); `sim::SimPhotonsLite` drops the information of the single
139  * photons and stores only collective information (e.g. their number).
140  * * a collection of `sim::OpDetBacktrackerRecord` (to be documented)
141  * * a collection of `sim::AuxDetSimChannel` (to be documented)
142  * * a collection of `simb::MCParticle`: the particles generated in the
143  * interaction of the primary particles with the material in the world
144  * are stored, but minor filtering by geometry and by physics is possible.
145  * An association of them with the originating `simb::MCTruth` object is
146  * also produced.
147  *
148  *
149  * Notes on the conventions
150  * -------------------------
151  *
152  * * all and the particles in the truth record (`simb::MCTruth`) which have
153  * status code (`simb::MCParticle::StatusCode()`) equal to `1` are passed
154  * to Geant4. These particles are called, in `LArG4` jargon, _primaries_.
155  * The interface with Geant4 is via a helper class provided by _nug4_.
156  * * normally, information about each particle that Geant4 propagates (which
157  * Geant4 calls _tracks_), primary or not, is saved as an individual
158  * `simb::MCParticle` object into the output particle list. Each
159  * `simb::MCParticle` includes a Geant4-like track ID which is also recorded
160  * into each `sim::IDE` deposited by that particle. This information can be
161  * used to track all the deposition from a particle, or to backtrack the
162  * particle responsible of a deposition (but see below...).
163  * Note that the stored track ID may be different than the one Geant4 used
164  * (and, in particular, it's guaranteed to be unique within a `sim::LArG4`
165  * instance output).
166  * * there are options (some set in `sim::LArG4Parameters` service) which
167  * allow for Geant4 tracks not to be saved as `simb::MCParticle` (e.g.
168  * `ParticleKineticEnergyCut`, `KeepEMShowerDaughters`). When these
169  * particles have deposited energy, their `sim::IDE` will report the ID of
170  * the first parent Geant4 track which is saved in the `simb::MCParticle`
171  * list, but _with its sign flipped_. Therefore, when tracking or
172  * backtracking (see above), comparisons should be performed using the
173  * absolute value of the `sim::IDE` (e.g. `std::abs(ide.trackID)`).
174  *
175  *
176  * Timing
177  * -------
178  *
179  * The `LArG4` module produces `sim::SimChannel` objects from generated
180  * `simb::MCParticle`. Each particle ("primary") is assigned the time taken
181  * from its vertex (a 4-vector), which is expected to be represented in
182  * nanoseconds.
183  * The `sim::SimChannel` object is a collection of `sim::IDE` in time. The
184  * position in the `sim::IDE` is the location where some ionization occurred.
185  * The time associated to a `sim::IDE` is stored in tick units. The time it
186  * represents is the time when the ionization happened, which is the time of
187  * the primary particle plus the propagation time to the ionization location,
188  * plus the drift time, which the ionized electrons take to reach the anode
189  * wire. This time is then shifted to the frame of the electronics time
190  * via `detinfo::DetectorClocks::G4ToElecTime()`, which adds a configurable
191  * time offset. The time is converted into ticks via
192  * `detinfo::DetectorClocks::TPCClock()`, and this is the final value
193  * associated to the `sim::IDE`. For a more complete overview, see
194  * https://cdcvs.fnal.gov/redmine/projects/larsoft/wiki/Simulation#Simulation-Timing
195  *
196  *
197  * Randomness
198  * -----------
199  *
200  * The random number generators used by this process are:
201  * - 'GEANT' instance: used by Geant4
202  * - 'propagation' instance: used in electron propagation
203  *
204  *
205  * Configuration parameters
206  * -------------------------
207  *
208  * - *G4PhysListName* (string, default: `"larg4::PhysicsList"`):
209  * whether to use the G4 overlap checker, which catches different issues than ROOT
210  * - *CheckOverlaps* (bool, default: `false`):
211  * whether to use the G4 overlap checker
212  * - *DumpParticleList* (bool, default: `false`):
213  * whether to print all MCParticles tracked;
214  * requires `MakeMCParticles` being `true`
215  * - *DumpSimChannels* (bool, default: `false`):
216  * whether to print all depositions on each SimChannel
217  * - *SmartStacking* (int, default: `0`):
218  * whether to use class to dictate how tracks are put on stack (nonzero is on)
219  * - *MakeMCParticles* (flag, default: `true`): keep a list of the particles
220  * seen in the detector, and eventually save it; you almost always want this on
221  * - *KeepParticlesInVolumes* (list of strings, default: _empty_):
222  * list of volumes in which to keep `simb::MCParticle` objects (empty keeps all);
223  * requires `MakeMCParticles` being `true`
224  * - *GeantCommandFile* (string, _required_):
225  * G4 macro file to pass to `G4Helper` for setting G4 command
226  * - *Seed* (integer, not defined by default): if defined, override the seed for
227  * random number generator used in Geant4 simulation (which is obtained from
228  * `NuRandomService` by default)
229  * - *PropagationSeed* (integer, not defined by default): if defined,
230  * override the seed for the random generator used for electrons propagation
231  * to the wire planes (obtained from the `NuRandomService` by default)
232  * - *InputLabels* (list of strings, default: process all truth):
233  * optional list of generator labels whose produced `simb::MCTruth` will
234  * be simulated; if not specified, all `simb::MCTruth` vector data
235  * products are simulated
236  * - *ChargeRecoveryMargin* (double, default: `0`): sets the maximum
237  * distance from a plane for the wire charge recovery to occur, in
238  * centimeters; for details on how it works, see
239  * `larg4::LArVoxelReadout::SetOffPlaneChargeRecoveryMargin()`. A value of
240  * `0` effectively disables this feature. All TPCs will have the same
241  * margin applied.
242  *
243  *
244  * Simulation details
245  * ===================
246  *
247  * Source of the operational parameters
248  * -------------------------------------
249  *
250  * @anchor LArG4_MaterialProperties
251  *
252  * Some of the physical properties have their values set in FHiCL
253  * configuration (e.g. `detinfo::LArParameters`). Then, GEANT4 is informed
254  * of them via `larg4::MaterialPropertyLoader`. The material property table
255  * in GEANT4 is then used by other LArSoft components to discover the
256  * parameter values.
257  *
258  * Among the parameters registered to GEANT4, the scintillation yields, i.e.
259  * how many scintillation photons are produced on average by 1 MeV of
260  * deposited energy, are also stored by type of ioniziong particle.
261  * These scintillation yields _do include a prescale factor_ (that may
262  * include, for example, the photomultiplier quantum efficiency), from the
263  * `ScintPreScale` parameter of `detinfo::LArPropertiesStandard`
264  * or equivalent.
265  *
266  *
267  * Reflectivity to optical photons
268  * --------------------------------
269  *
270  * Two models are supported for the simulation of (scintillation) light
271  * crossing detector surfaces:
272  * -# the standard one from GEANT4, implemented in `G4OpBoundaryProcess`
273  * -# a simplified one, implemented in `larg4::OpBoundaryProcessSimple`
274  *
275  * The model is chosen according to the value of
276  * `detinfo::DetectorProperties::SimpleBoundary()`, and the choice is
277  * currently exerted by `larg4::OpticalPhysics`.
278  *
279  * The simplified model is faster and simpler: it only deals with absorption
280  * and reflection (both specular and diffues).
281  * This is the "default" model used in most contexts.
282  *
283  * GEANT4 model is more complete and slower. It may take some art to fully
284  * configure all the properties of the materials at the sides of the surfaces.
285  * The price is a detailed simulation that includes among others refraction
286  * and wavelength shifting.
287  *
288  *
289  * Scintillation
290  * --------------
291  *
292  * When using the fast optical simulation, which is the "standard" running
293  * mode, energy depositions from GEANT4 are "converted" into a number of
294  * scintillation photons by the global `larg4::IonizationAndScintillation`
295  * object instance, which internally utilizes the algorithm set up via
296  * configuration parameter `IonAndScintCalculator` in `LArG4Parameters`
297  * service (at the time of writing, `"Separate"` is supported and `"NEST"` is
298  * accepted too).
299  * The number of scintillation photons per energy unit is read from GEANT4
300  * @ref LArG4_MaterialProperties "material properties table". It includes
301  * already quantum efficiency ("prescale") and it may depend on the type of
302  * ionizing particle, depending on the configuration (`LArPropertiesStandard`
303  * parameter `ScintByParticleType`). This value ("yield") is used as
304  * the average of a Poisson distribution from which the actual number of
305  * scintillation photons is extracted case by case.
306  * The implementation `larg4::ISCalculationSeparate` may also include medium
307  * saturation effects as well, if configured, but only if the scintillation
308  * yield is set not to depend on the type of ionizing particle.
309  * The number of scintillation photons is then distributed between the fast
310  * and slow component by a yield ratio also set in the material parameters,
311  * and the single photons are distributed in time accordingly to their
312  * component.
313  *
314  */
315  class LArG4 : public art::EDProducer {
316  public:
317  explicit LArG4(fhicl::ParameterSet const& pset);
318 
319  private:
320  /// The main routine of this module: Fetch the primary particles
321  /// from the event, simulate their evolution in the detector, and
322  /// produce the detector response.
323  void produce(art::Event& evt) override;
324  void beginJob() override;
325  void beginRun(art::Run& run) override;
326 
327  std::unique_ptr<g4b::G4Helper> fG4Help{nullptr}; ///< G4 interface object
329  nullptr}; ///< Geant4 user action to particle information.
330 
331  std::string fG4PhysListName; ///< predefined physics list to use if not making a custom one
332  std::string fG4MacroPath; ///< directory path for Geant4 macro file to be
333  ///< executed before main MC processing.
334  bool fCheckOverlaps; ///< Whether to use the G4 overlap checker
335  bool fMakeMCParticles; ///< Whether to keep a `sim::MCParticle` list
336  bool fStoreDroppedMCParticles;///< Whether to keep a `sim::MCParticleLite` list of dropped particles
337  bool fdumpParticleList; ///< Whether each event's sim::ParticleList will be displayed.
338  bool fdumpSimChannels; ///< Whether each event's sim::Channel will be displayed.
340  bool fStoreReflected{false};
341  int fSmartStacking; ///< Whether to instantiate and use class to
342  double fOffPlaneMargin = 0.; ///< Off-plane charge recovery margin
343  ///< dictate how tracks are put on stack.
344  std::vector<std::string> fInputLabels;
345  std::vector<std::string>
346  fKeepParticlesInVolumes; ///<Only write particles that have trajectories through these volumes
347 
348  bool fSparsifyTrajectories; ///< Sparsify MCParticle Trajectories
349 
350  CLHEP::HepRandomEngine& fEngine; ///< Random-number engine for IonizationAndScintillation
351  ///< initialization
352 
353  detinfo::DetectorPropertiesData fDetProp; ///< Must outlive fAllPhysicsLists!
356  nullptr}; /// Pointer used for correctly updating the clock data state.
357 
358  /// Configures and returns a particle filter
359  std::unique_ptr<util::PositionInVolumeFilter> CreateParticleVolumeFilter(
360  std::set<std::string> const& vol_names) const;
361  };
362 
363 } // namespace LArG4
364 
365 namespace {
366 
367  // ---------------------------------------------------------------------------
368  /**
369  * @brief Moves data from `source` to the end of `dest`.
370  * @tparam T type of values in the containers
371  * @param dest collection to append data to
372  * @param source collection to take data from
373  * @return a reference to `dest`
374  *
375  * The data contained in `source` is moved (appended) to the end of `dest`.
376  *
377  * The data is moved from source element by element; as an exception, if
378  * `dest` is still empty, the data is moved in a block.
379  *
380  * The `source` collection is always returned depleted as after being "moved
381  * away" (i.e. after `auto temp = std::move(source);`): that means that the
382  * only thing that can be done with it according to C++ standard is to
383  * destruct it.
384  *
385  */
386  template <typename T>
387  std::vector<T>&
388  append(std::vector<T>& dest, std::vector<T>&& source)
389  {
390  if (empty(dest))
391  dest = std::move(source);
392  else {
393  dest.insert(dest.end(), std::move_iterator{begin(source)}, std::move_iterator{end(source)});
394  source = std::vector<T>{}; // ensure the old memory is released
395  }
396  return dest;
397  }
398  // ---------------------------------------------------------------------------
399 
400 } // local namespace
401 
402 namespace larg4 {
403 
404  //----------------------------------------------------------------------
405  // Constructor
406  LArG4::LArG4(fhicl::ParameterSet const& pset)
407  : art::EDProducer{pset}
408  , fG4PhysListName(pset.get<std::string>("G4PhysListName", "larg4::PhysicsList"))
409  , fCheckOverlaps(pset.get<bool>("CheckOverlaps", false))
410  , fMakeMCParticles(pset.get<bool>("MakeMCParticles", true))
411  , fStoreDroppedMCParticles(pset.get<bool>("StoreDroppedMCParticles", false))
412  , fdumpParticleList(pset.get<bool>("DumpParticleList", false))
413  , fdumpSimChannels(pset.get<bool>("DumpSimChannels", false))
414  , fSmartStacking(pset.get<int>("SmartStacking", 0))
415  , fOffPlaneMargin(pset.get<double>("ChargeRecoveryMargin", 0.0))
416  , fKeepParticlesInVolumes(pset.get<std::vector<std::string>>("KeepParticlesInVolumes", {}))
417  , fSparsifyTrajectories(pset.get<bool>("SparsifyTrajectories", false))
418  , fEngine(art::ServiceHandle<rndm::NuRandomService> {}
419  ->createEngine(*this, "HepJamesRandom", "propagation", pset, "PropagationSeed"))
420  , fDetProp{art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataForJob()}
421  , fAllPhysicsLists{fDetProp}
422  {
423  MF_LOG_DEBUG("LArG4") << "Debug: LArG4()";
424  art::ServiceHandle<art::RandomNumberGenerator const> rng;
425 
426  if (!fMakeMCParticles) { // configuration option consistency
427  if (fdumpParticleList) {
428  throw art::Exception(art::errors::Configuration)
429  << "Option `DumpParticleList` can't be set if `MakeMCParticles` is unset.\n";
430  }
431  if (!fKeepParticlesInVolumes.empty()) {
432  throw art::Exception(art::errors::Configuration)
433  << "Option `KeepParticlesInVolumes` can't be set if `MakeMCParticles` is unset.\n";
434  }
435  } // if
436 
437  if (pset.has_key("Seed")) {
438  throw art::Exception(art::errors::Configuration)
439  << "The configuration of LArG4 module has the discontinued 'Seed' parameter.\n"
440  "Seeds are now controlled by two parameters: 'GEANTSeed' and 'PropagationSeed'.";
441  }
442  // setup the random number service for Geant4, the "G4Engine" label is a
443  // special tag setting up a global engine for use by Geant4/CLHEP;
444  // obtain the random seed from NuRandomService,
445  // unless overridden in configuration with key "Seed" or "GEANTSeed"
446  // FIXME: THIS APPEARS TO BE A NO-OP; IS IT NEEDED?
447  (void)art::ServiceHandle<rndm::NuRandomService>()->createEngine(
448  *this, "G4Engine", "GEANT", pset, "GEANTSeed");
449 
450  //get a list of generators to use, otherwise, we'll end up looking for anything that's
451  //made an MCTruth object
452  bool useInputLabels =
453  pset.get_if_present<std::vector<std::string>>("InputLabels", fInputLabels);
454  if (!useInputLabels) fInputLabels.resize(0);
455 
456  art::ServiceHandle<sim::LArG4Parameters const> lgp;
457  fUseLitePhotons = lgp->UseLitePhotons();
458 
459  if (!lgp->NoPhotonPropagation()) {
460  try {
461  art::ServiceHandle<phot::PhotonVisibilityService const> pvs;
462  fStoreReflected = pvs->StoreReflected();
463  }
464  catch (art::Exception const& e) {
465  // If the service is not configured, then just keep the default
466  // false for reflected light. If reflected photons are simulated
467  // without PVS they will show up in the regular SimPhotons collection
468  if (e.categoryCode() != art::errors::ServiceNotFound) throw;
469  }
470 
471  if (!fUseLitePhotons) {
472  produces<std::vector<sim::SimPhotons>>();
473  if (fStoreReflected) { produces<std::vector<sim::SimPhotons>>("Reflected"); }
474  }
475  else {
476  produces<std::vector<sim::SimPhotonsLite>>();
477  produces<std::vector<sim::OpDetBacktrackerRecord>>();
478  if (fStoreReflected) {
479  produces<std::vector<sim::SimPhotonsLite>>("Reflected");
480  produces<std::vector<sim::OpDetBacktrackerRecord>>("Reflected");
481  }
482  }
483  }
484 
485  if (lgp->FillSimEnergyDeposits()) {
486  produces<std::vector<sim::SimEnergyDeposit>>("TPCActive");
487  produces<std::vector<sim::SimEnergyDeposit>>("Other");
488  }
489 
490  if (fMakeMCParticles) {
491  produces<std::vector<simb::MCParticle>>();
492  produces<art::Assns<simb::MCTruth, simb::MCParticle, sim::GeneratedParticleInfo>>();
493  }
494  if (fStoreDroppedMCParticles) {
495  produces<std::vector<sim::MCParticleLite>>();
496  }
497  if (!lgp->NoElectronPropagation()) produces<std::vector<sim::SimChannel>>();
498  produces<std::vector<sim::AuxDetSimChannel>>();
499 
500  // constructor decides if initialized value is a path or an environment variable
501  cet::search_path sp("FW_SEARCH_PATH");
502 
503  sp.find_file(pset.get<std::string>("GeantCommandFile"), fG4MacroPath);
504  struct stat sb;
505  if (fG4MacroPath.empty() || stat(fG4MacroPath.c_str(), &sb) != 0)
506  // failed to resolve the file name
507  throw cet::exception("NoG4Macro") << "G4 macro file " << fG4MacroPath << " not found!\n";
508  }
509 
510  //----------------------------------------------------------------------
511  void
512  LArG4::beginJob()
513  {
514  fG4Help = std::make_unique<g4b::G4Helper>(fG4MacroPath, fG4PhysListName);
515 
516  if (fCheckOverlaps) fG4Help->SetOverlapCheck(true);
517 
518  art::ServiceHandle<geo::Geometry const> geom;
519  fG4Help->ConstructDetector(geom->GDMLFile());
520 
521  // Get the logical volume store and assign material properties
523  auto const detProp =
524  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataForJob();
526  mpl.UpdateGeometry(G4LogicalVolumeStore::GetInstance());
527 
528  // Tell the detector about the parallel LAr voxel geometry.
529  std::vector<G4VUserParallelWorld*> pworlds;
530 
531  // Intialize G4 physics and primary generator action
532  fG4Help->InitPhysics();
533 
534  // create the ionization and scintillation calculator;
535  // this is a singleton (!) so it does not make sense
536  // to create it in LArVoxelReadoutGeometry
537  IonizationAndScintillation::CreateInstance(detProp, fEngine);
538 
539  // make a parallel world for each TPC in the detector
540  LArVoxelReadoutGeometry::Setup_t readoutGeomSetupData;
541  readoutGeomSetupData.readoutSetup.offPlaneMargin = fOffPlaneMargin;
542  readoutGeomSetupData.readoutSetup.propGen = &fEngine;
543 
544  fVoxelReadoutGeometry =
545  new LArVoxelReadoutGeometry("LArVoxelReadoutGeometry", readoutGeomSetupData);
546  pworlds.push_back(fVoxelReadoutGeometry);
547  pworlds.push_back(
548  new OpDetReadoutGeometry(geom->OpDetGeoName(), "OpDetReadoutGeometry", fUseLitePhotons));
549  pworlds.push_back(new AuxDetReadoutGeometry("AuxDetReadoutGeometry"));
550 
551  fG4Help->SetParallelWorlds(pworlds);
552 
553  // moved up
554  // Intialize G4 physics and primary generator action
555  fG4Help->InitPhysics();
556 
557  // Use the UserActionManager to handle all the Geant4 user hooks.
558  g4b::UserActionManager* uaManager = g4b::UserActionManager::Instance();
559 
560  // User-action class for accumulating LAr voxels.
561  art::ServiceHandle<sim::LArG4Parameters const> lgp;
562 
563  // User-action class for accumulating particles and trajectories
564  // produced in the detector.
565  fparticleListAction = new larg4::ParticleListAction(lgp->ParticleKineticEnergyCut(),
566  lgp->StoreTrajectories(),
567  lgp->KeepEMShowerDaughters(),
568  fMakeMCParticles,
569  fStoreDroppedMCParticles);
570  uaManager->AddAndAdoptAction(fparticleListAction);
571 
572  // UserActionManager is now configured so continue G4 initialization
573  fG4Help->SetUserAction();
574 
575  // With an enormous detector with lots of rock ala LAr34 (nee LAr20)
576  // we need to be smarter about stacking.
577  if (fSmartStacking > 0) {
578  G4UserStackingAction* stacking_action = new LArStackingAction(fSmartStacking);
579  fG4Help->GetRunManager()->SetUserAction(stacking_action);
580  }
581  }
582 
583  void
584  LArG4::beginRun(art::Run& run)
585  {
586  // prepare the filter object (null if no filtering)
587  std::set<std::string> volnameset(fKeepParticlesInVolumes.begin(),
588  fKeepParticlesInVolumes.end());
589  fparticleListAction->ParticleFilter(CreateParticleVolumeFilter(volnameset));
590  }
591 
592  std::unique_ptr<util::PositionInVolumeFilter>
593  LArG4::CreateParticleVolumeFilter(std::set<std::string> const& vol_names) const
594  {
595  // if we don't have favourite volumes, don't even bother creating a filter
596  if (empty(vol_names)) return {};
597 
598  auto const& geom = *art::ServiceHandle<geo::Geometry const>();
599 
600  std::vector<std::vector<TGeoNode const*>> node_paths = geom.FindAllVolumePaths(vol_names);
601 
602  // collection of interesting volumes
604  GeoVolumePairs.reserve(node_paths.size()); // because we are obsessed
605 
606  //for each interesting volume, follow the node path and collect
607  //total rotations and translations
608  for (size_t iVolume = 0; iVolume < node_paths.size(); ++iVolume) {
609  std::vector<TGeoNode const*> path = node_paths[iVolume];
610 
611  auto pTransl = new TGeoTranslation(0., 0., 0.);
612  auto pRot = new TGeoRotation();
613  for (TGeoNode const* node : path) {
614  TGeoTranslation thistranslate(*node->GetMatrix());
615  TGeoRotation thisrotate(*node->GetMatrix());
616  pTransl->Add(&thistranslate);
617  *pRot = *pRot * thisrotate;
618  }
619 
620  // for some reason, pRot and pTransl don't have tr and rot bits set
621  // correctly make new translations and rotations so bits are set correctly
622  auto pTransl2 = new TGeoTranslation(
623  pTransl->GetTranslation()[0], pTransl->GetTranslation()[1], pTransl->GetTranslation()[2]);
624  double phi = 0., theta = 0., psi = 0.;
625  pRot->GetAngles(phi, theta, psi);
626  auto pRot2 = new TGeoRotation();
627  pRot2->SetAngles(phi, theta, psi);
628 
629  auto pTransf = new TGeoCombiTrans(*pTransl2, *pRot2);
630  GeoVolumePairs.emplace_back(node_paths[iVolume].back()->GetVolume(), pTransf);
631  }
632 
633  return std::make_unique<util::PositionInVolumeFilter>(std::move(GeoVolumePairs));
634  } // CreateParticleVolumeFilter()
635 
636  void
637  LArG4::produce(art::Event& evt)
638  {
639  MF_LOG_DEBUG("LArG4") << "produce()";
640  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
641  auto const detProp =
642  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(evt, clockData);
643  LArVoxelReadoutGeometry::Sentry const set_for_event{fVoxelReadoutGeometry, clockData, detProp};
644 
645  // loop over the lists and put the particles and voxels into the event as
646  // collections
647  auto scCol = std::make_unique<std::vector<sim::SimChannel>>();
648  auto adCol = std::make_unique<std::vector<sim::AuxDetSimChannel>>();
649  auto tpassn =
650  fMakeMCParticles ?
651  std::make_unique<art::Assns<simb::MCTruth, simb::MCParticle, sim::GeneratedParticleInfo>>() :
652  nullptr;
653  auto partCol =
654  fMakeMCParticles ?
655  std::make_unique<std::vector<simb::MCParticle>>() :
656  nullptr;
657  auto droppedPartCol =
658  fStoreDroppedMCParticles ?
659  std::make_unique<std::vector<sim::MCParticleLite>>() :
660  nullptr;
661  auto PhotonCol = std::make_unique<std::vector<sim::SimPhotons>>();
662  auto PhotonColRefl = std::make_unique<std::vector<sim::SimPhotons>>();
663  auto LitePhotonCol = std::make_unique<std::vector<sim::SimPhotonsLite>>();
664  auto LitePhotonColRefl = std::make_unique<std::vector<sim::SimPhotonsLite>>();
665  auto cOpDetBacktrackerRecordCol = std::make_unique<std::vector<sim::OpDetBacktrackerRecord>>();
666  auto cOpDetBacktrackerRecordColRefl =
667  std::make_unique<std::vector<sim::OpDetBacktrackerRecord>>();
668 
669  std::optional<art::PtrMaker<simb::MCParticle>> makeMCPartPtr;
670  if (fMakeMCParticles) makeMCPartPtr.emplace(evt);
671 
672  // for energy deposits
673  auto edepCol_TPCActive = std::make_unique<std::vector<sim::SimEnergyDeposit>>();
674  auto edepCol_Other = std::make_unique<std::vector<sim::SimEnergyDeposit>>();
675 
676  // Fetch the lists of LAr voxels and particles.
677  art::ServiceHandle<sim::LArG4Parameters const> lgp;
678  art::ServiceHandle<geo::Geometry const> geom;
679 
680  // Clear the detected photon table
681  OpDetPhotonTable::Instance()->ClearTable(geom->NOpDets());
682  if (lgp->FillSimEnergyDeposits()) OpDetPhotonTable::Instance()->ClearEnergyDeposits();
683 
684  // reset the track ID offset as we have a new collection of interactions
685  fparticleListAction->ResetTrackIDOffset();
686 
687  //look to see if there is any MCTruth information for this
688  //event
689  std::vector<art::Handle<std::vector<simb::MCTruth>>> mclists;
690  if (empty(fInputLabels))
691  //evt.getManyByType(mclists);
692  mclists = evt.getMany<std::vector<simb::MCTruth>>();
693  else {
694  mclists.resize(fInputLabels.size());
695  for (size_t i = 0; i < fInputLabels.size(); i++)
696  evt.getByLabel(fInputLabels[i], mclists[i]);
697  }
698 
699  unsigned int nGeneratedParticles = 0;
700 
701  // Need to process Geant4 simulation for each interaction separately.
702  for (size_t mcl = 0; mcl < mclists.size(); ++mcl) {
703 
704  art::Handle<std::vector<simb::MCTruth>> mclistHandle = mclists[mcl];
705 
706  for (size_t m = 0; m < mclistHandle->size(); ++m) {
707  art::Ptr<simb::MCTruth> mct(mclistHandle, m);
708 
709  MF_LOG_DEBUG("LArG4") << *(mct.get());
710 
711  // The following tells Geant4 to track the particles in this interaction.
712  fG4Help->G4Run(mct);
713 
714  if (!partCol) continue;
715  assert(tpassn);
716 
717  // receive the particle list
718  sim::ParticleList particleList = fparticleListAction->YieldList();
719 
720  for (auto const& partPair : particleList) {
721  simb::MCParticle& p = *(partPair.second);
722  ++nGeneratedParticles;
723 
724  // if the particle has been marked as dropped, we don't save it
725  // (as of LArSoft ~v5.6 this does not ever happen because
726  // ParticleListAction has already taken care of deleting them)
727  if (ParticleListAction::isDropped(&p)) continue;
728 
729  sim::GeneratedParticleInfo const truthInfo{
730  fparticleListAction->GetPrimaryTruthIndex(p.TrackId())};
731  if (!truthInfo.hasGeneratedParticleIndex() && (p.Mother() == 0)) {
732  // this means it's primary but with no information; logic error!!
733  art::Exception error(art::errors::LogicError);
734  error << "Failed to match primary particle:\n";
736  error << "\nwith particles from the truth record '"
737  << mclistHandle.provenance()->inputTag() << "':\n";
738  sim::dump::DumpMCTruth(error, *mct, 2U, " "); // 2 points per line
739  error << "\n";
740  throw error;
741  }
742 
743  if (fSparsifyTrajectories) p.SparsifyTrajectory();
744 
745  partCol->push_back(std::move(p));
746 
747  tpassn->addSingle(mct, (*makeMCPartPtr)(partCol->size() - 1), truthInfo);
748 
749  } // for(particleList)
750 
751  if (fStoreDroppedMCParticles && droppedPartCol) {
752  // Request a list of dropped particles
753  // Store them in MCParticleLite format
754  sim::ParticleList droppedParticleList = fparticleListAction->YieldDroppedList();
755  droppedPartCol->reserve(droppedParticleList.size());
756 
757  for (auto const& partPair : droppedParticleList) {
758  simb::MCParticle& p = *(partPair.second);
759  if (ParticleListAction::isDropped(&p)) continue;
760  if (p.StatusCode() != 1) continue;
761 
762  sim::MCParticleLite mini_mcp(p);
763  mini_mcp.Origin( mct->Origin() );
764 
765  droppedPartCol->push_back(std::move(mini_mcp));
766  } // for(droppedParticleList)
767  }
768 
769  // Has the user request a detailed dump of the output objects?
770  if (fdumpParticleList) {
771  mf::LogInfo("LArG4") << "Dump sim::ParticleList; size()=" << particleList.size() << "\n"
772  << particleList;
773  }
774  }
775 
776  } // end loop over interactions
777 
778  // get the electrons from the LArVoxelReadout sensitive detector
779  // Get the sensitive-detector manager.
780  G4SDManager* sdManager = G4SDManager::GetSDMpointer();
781 
782  // Find the sensitive detector with the name "LArVoxelSD".
783  auto theOpDetDet = dynamic_cast<OpDetSensitiveDetector*>(
784  sdManager->FindSensitiveDetector("OpDetSensitiveDetector"));
785 
786  // Store the contents of the detected photon table
787  //
788  if (theOpDetDet) {
789 
790  if (!lgp->NoPhotonPropagation()) {
791 
792  for (int Reflected = 0; Reflected <= 1; Reflected++) {
793  if (Reflected && !fStoreReflected) continue;
794 
795  if (!fUseLitePhotons) {
796  MF_LOG_DEBUG("Optical") << "Storing OpDet Hit Collection in Event";
797  std::vector<sim::SimPhotons>& ThePhotons =
798  OpDetPhotonTable::Instance()->GetPhotons(Reflected);
799  if (Reflected)
800  PhotonColRefl->reserve(ThePhotons.size());
801  else
802  PhotonCol->reserve(ThePhotons.size());
803  for (auto& it : ThePhotons) {
804  if (Reflected)
805  PhotonColRefl->push_back(std::move(it));
806  else
807  PhotonCol->push_back(std::move(it));
808  }
809  }
810  else {
811  MF_LOG_DEBUG("Optical") << "Storing OpDet Hit Collection in Event";
812 
813  std::map<int, std::map<int, int>> ThePhotons =
814  OpDetPhotonTable::Instance()->GetLitePhotons(Reflected);
815 
816  if (size(ThePhotons) > 0) {
817  LitePhotonCol->reserve(ThePhotons.size());
818  for (auto const& [opChannel, detectedPhotons] : ThePhotons) {
820  ph.OpChannel = opChannel;
821  ph.DetectedPhotons = detectedPhotons;
822  if (Reflected)
823  LitePhotonColRefl->push_back(std::move(ph));
824  else
825  LitePhotonCol->push_back(std::move(ph));
826  }
827  }
828  }
829  if (Reflected)
830  *cOpDetBacktrackerRecordColRefl =
831  OpDetPhotonTable::Instance()->YieldReflectedOpDetBacktrackerRecords();
832  else
833  *cOpDetBacktrackerRecordCol =
834  OpDetPhotonTable::Instance()->YieldOpDetBacktrackerRecords();
835  }
836  } //end if no photon propagation
837 
838  if (lgp->FillSimEnergyDeposits()) {
839  // we steal the only existing copy of the energy deposit map. Oink!
840  auto edepMap = OpDetPhotonTable::Instance()->YieldSimEnergyDeposits();
841  for (auto& [volumeName, edepCol] : edepMap) {
842  // note: constant reference to a (smart) pointer to non-const data
843  auto const& destColl =
844  boost::contains(volumeName, "TPCActive") ? edepCol_TPCActive : edepCol_Other;
845  append(*destColl, std::move(edepCol));
846  } // for
847  }
848  } //end if theOpDetDet
849 
850  if (!lgp->NoElectronPropagation()) {
851 
852  // only put the sim::SimChannels into the event once, not once for every
853  // MCTruth in the event
854 
855  std::set<LArVoxelReadout*> ReadoutList; // to be cleared later on
856 
857  for (unsigned int c = 0; c < geom->Ncryostats(); ++c) {
858 
859  // map to keep track of which channels we already have SimChannels for in scCol
860  // remake this map on each cryostat as channels ought not to be shared between
861  // cryostats, just between TPC's
862 
863  std::map<unsigned int, unsigned int> channelToscCol;
864 
865  unsigned int ntpcs = geom->Cryostat(c).NTPC();
866  for (unsigned int t = 0; t < ntpcs; ++t) {
867  std::string name("LArVoxelSD");
868  std::ostringstream sstr;
869  sstr << name << "_Cryostat" << c << "_TPC" << t;
870 
871  // try first to find the sensitive detector specific for this TPC;
872  // do not bother writing on screen if there is none (yet)
873  G4VSensitiveDetector* sd = sdManager->FindSensitiveDetector(sstr.str(), false);
874  // if there is none, catch the general one (called just "LArVoxelSD")
875  if (!sd) sd = sdManager->FindSensitiveDetector(name, false);
876  // If this didn't work, then a sensitive detector with
877  // the name "LArVoxelSD" does not exist.
878  if (!sd) {
879  throw cet::exception("LArG4")
880  << "Sensitive detector for cryostat " << c << " TPC " << t << " not found (neither '"
881  << sstr.str() << "' nor '" << name << "' exist)\n";
882  }
883 
884  // Convert the G4VSensitiveDetector* to a LArVoxelReadout*.
885  auto larVoxelReadout = dynamic_cast<LArVoxelReadout*>(sd);
886 
887  // If this didn't work, there is a "LArVoxelSD" detector, but
888  // it's not a LArVoxelReadout object.
889  if (!larVoxelReadout) {
890  throw cet::exception("LArG4")
891  << "Sensitive detector '" << sd->GetName() << "' is not a LArVoxelReadout object\n";
892  }
893 
894  LArVoxelReadout::ChannelMap_t& channels = larVoxelReadout->GetSimChannelMap(c, t);
895  if (!empty(channels)) {
896  MF_LOG_DEBUG("LArG4") << "now put " << channels.size() << " SimChannels from C=" << c
897  << " T=" << t << " into the event";
898  }
899 
900  for (auto ch_pair : channels) {
901  sim::SimChannel& sc = ch_pair.second;
902 
903  // push sc onto scCol but only if we haven't already put something in scCol for this channel.
904  // if we have, then merge the ionization deposits. Skip the check if we only have one TPC
905 
906  if (ntpcs > 1) {
907  unsigned int ichan = sc.Channel();
908  auto itertest = channelToscCol.find(ichan);
909  if (itertest == channelToscCol.end()) {
910  channelToscCol[ichan] = scCol->size();
911  scCol->emplace_back(std::move(sc));
912  }
913  else {
914  unsigned int idtest = itertest->second;
915  auto const& tdcideMap = sc.TDCIDEMap();
916  for (auto const& tdcide : tdcideMap) {
917  for (auto const& ide : tdcide.second) {
918  double xyz[3] = {ide.x, ide.y, ide.z};
919  scCol->at(idtest).AddIonizationElectrons(
920  ide.trackID, tdcide.first, ide.numElectrons, xyz, ide.energy, ide.origTrackID);
921  } // end loop to add ionization electrons to scCol->at(idtest)
922  } // end loop over tdc to vector<sim::IDE> map
923  } // end if check to see if we've put SimChannels in for ichan yet or not
924  }
925  else {
926  scCol->emplace_back(std::move(sc));
927  } // end of check if we only have one TPC (skips check for multiple simchannels if we have just one TPC)
928  } // end loop over simchannels for this TPC
929 
930  // mark it for clearing
931  ReadoutList.insert(const_cast<LArVoxelReadout*>(larVoxelReadout));
932 
933  } // end loop over tpcs
934  } // end loop over cryostats
935 
936  for (LArVoxelReadout* larVoxelReadout : ReadoutList) {
937  larVoxelReadout->ClearSimChannels();
938  }
939  } //endif electron prop
940 
941  // only put the sim::AuxDetSimChannels into the event once, not once for every
942  // MCTruth in the event
943 
944  adCol->reserve(geom->NAuxDets());
945  for (unsigned int a = 0; a < geom->NAuxDets(); ++a) {
946 
947  // there should always be at least one senstive volume because
948  // we make one for the full aux det if none are specified in the
949  // gdml file - see AuxDetGeo.cxx
950  for (size_t sv = 0; sv < geom->AuxDet(a).NSensitiveVolume(); ++sv) {
951 
952  // N.B. this name convention is used when creating the
953  // AuxDetReadout SD in AuxDetReadoutGeometry
954  std::stringstream name;
955  name << "AuxDetSD_AuxDet" << a << "_" << sv;
956  G4VSensitiveDetector* sd = sdManager->FindSensitiveDetector(name.str().c_str());
957  if (!sd) {
958  throw cet::exception("LArG4")
959  << "Sensitive detector '" << name.str() << "' does not exist\n";
960  }
961 
962  // Convert the G4VSensitiveDetector* to a AuxDetReadout*.
963  larg4::AuxDetReadout* auxDetReadout = dynamic_cast<larg4::AuxDetReadout*>(sd);
964 
965  MF_LOG_DEBUG("LArG4") << "now put the AuxDetSimTracks in the event";
966 
967  const sim::AuxDetSimChannel adsc = auxDetReadout->GetAuxDetSimChannel();
968  adCol->push_back(adsc);
969  auxDetReadout->clear();
970  }
971  } // Loop over AuxDets
972 
973  if (partCol) {
974  mf::LogInfo("LArG4") << "Geant4 simulated " << nGeneratedParticles << " MC particles, we keep "
975  << partCol->size() << " .";
976  }
977 
978  if (fdumpSimChannels) {
979  mf::LogVerbatim("DumpSimChannels")
980  << "Event " << evt.id() << ": " << scCol->size() << " channels with signal";
981  unsigned int nChannels = 0;
982  for (const sim::SimChannel& sc : *scCol) {
983  mf::LogVerbatim out("DumpSimChannels");
984  out << " #" << nChannels << ": ";
985  // dump indenting with " ", but not on the first line
986  sc.Dump(out, " ");
987  ++nChannels;
988  } // for
989  } // if dump SimChannels
990 
991  if (!lgp->NoElectronPropagation()) evt.put(std::move(scCol));
992 
993  evt.put(std::move(adCol));
994  if (partCol) evt.put(std::move(partCol));
995  if (droppedPartCol) {
996  std::cout << "LArG4 dropped particles length = " << droppedPartCol->size() << std::endl;
997  evt.put(std::move(droppedPartCol));
998  }
999  if (tpassn) evt.put(std::move(tpassn));
1000  if (!lgp->NoPhotonPropagation()) {
1001  if (!fUseLitePhotons) {
1002  evt.put(std::move(PhotonCol));
1003  if (fStoreReflected) evt.put(std::move(PhotonColRefl), "Reflected");
1004  }
1005  else {
1006  evt.put(std::move(LitePhotonCol));
1007  evt.put(std::move(cOpDetBacktrackerRecordCol));
1008  if (fStoreReflected) {
1009  evt.put(std::move(LitePhotonColRefl), "Reflected");
1010  evt.put(std::move(cOpDetBacktrackerRecordColRefl), "Reflected");
1011  }
1012  }
1013  }
1014 
1015  if (lgp->FillSimEnergyDeposits()) {
1016  evt.put(std::move(edepCol_TPCActive), "TPCActive");
1017  evt.put(std::move(edepCol_Other), "Other");
1018  }
1019  return;
1020  } // LArG4::produce()
1021 
1022 } // namespace LArG4
1023 
1024 DEFINE_ART_MODULE(larg4::LArG4)
std::vector< std::string > fInputLabels
std::unique_ptr< g4b::G4Helper > fG4Help
G4 interface object.
Store parameters for running LArG4.
bool fStoreDroppedMCParticles
Whether to keep a sim::MCParticleLite list of dropped particles.
std::string fG4MacroPath
Energy deposited on a readout channel by simulated tracks.
Definition: SimChannel.h:145
Stores material properties and sends them to GEANT4 geometry.
void beginJob() override
do source
void Dump(Stream &&out, std::string indent, std::string first_indent) const
Dumps the full content of the SimChannel into a stream.
Definition: SimChannel.h:343
void GetPropertiesFromServices(detinfo::DetectorPropertiesData const &detProp)
Imports properties from LArSoft services.
std::vector< VolumeInfo_t > AllVolumeInfo_t
pdgs p
Definition: selectors.fcl:22
bool fMakeMCParticles
Whether to keep a sim::MCParticle list.
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
Class def header for MCParticleLite data container.
Contains data associated to particles from detector simulation.
Runs Geant4 simulation and propagation of electrons and photons to readout.
std::unique_ptr< util::PositionInVolumeFilter > CreateParticleVolumeFilter(std::set< std::string > const &vol_names) const
Pointer used for correctly updating the clock data state.
tuple m
now if test mode generate materials, CRT shell, world, gdml header else just generate CRT shell for u...
std::vector< std::string > fKeepParticlesInVolumes
Only write particles that have trajectories through these volumes.
std::map< int, int > DetectedPhotons
Number of photons detected at each given time: time tick -&gt; photons.
Definition: SimPhotons.h:117
Define the &quot;parallel&quot; geometry that&#39;s seen by the LAr Voxels.
process_name gaushit a
bool fSparsifyTrajectories
Sparsify MCParticle Trajectories.
BEGIN_PROLOG triggeremu_data_config_icarus settings PMTADCthresholds sequence::icarus_stage0_multiTPC_TPC physics sequence::icarus_stage0_EastHits_TPC physics sequence::icarus_stage0_WestHits_TPC physics producers purityana0 caloskimCalorimetryCryoE physics caloskimCalorimetryCryoW physics path
int fSmartStacking
Whether to instantiate and use class to.
Collection of particles crossing one auxiliary detector cell.
bool fdumpSimChannels
Whether each event&#39;s sim::Channel will be displayed.
larg4::ParticleListAction * fparticleListAction
Geant4 user action to particle information.
Simulation objects for optical detectors.
object containing MC truth information necessary for making RawDigits and doing back tracking ...
Use Geant4&#39;s user &quot;hooks&quot; to maintain a list of particles generated by Geant4.
Utility functions to print MC truth information.
LArG4(fhicl::ParameterSet const &pset)
virtual void clear()
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
fEngine(art::ServiceHandle< rndm::NuRandomService >() ->createEngine(*this, pset,"Seed"))
void DumpMCTruth(Stream &&out, simb::MCTruth const &truth, unsigned int pointsPerLine, std::string indent, std::string firstIndent)
Dumps the content of the specified MC truth in the output stream.
Definition: MCDumpers.h:346
int OpChannel
Optical detector channel associated to this data.
Definition: SimPhotons.h:114
j template void())
Definition: json.hpp:3108
bool fdumpParticleList
Whether each event&#39;s sim::ParticleList will be displayed.
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
void UpdateGeometry(G4LogicalVolumeStore *lvs)
Updates the material properties with the collected values.
raw::ChannelID_t Channel() const
Returns the readout channel this object describes.
Definition: SimChannel.h:335
A Geant4 sensitive detector that accumulates voxel information.
Define the &quot;parallel&quot; geometry that&#39;s seen by the AuxDet.
fSparsifyTrajectories(p.get< bool >("SparsifyTrajectories", false))
Compact representation of photons on a channel.
Definition: SimPhotons.h:103
bool fUseLitePhotons
Singleton to access a unified treatment of ionization and scintillation in LAr.
Contains information about a generated particle.
contains information for a single step in the detector simulation
void produce(art::Event &evt) override
object containing MC truth information necessary for making RawDigits and doing back tracking ...
do i e
std::string fG4PhysListName
predefined physics list to use if not making a custom one
bool fCheckOverlaps
Whether to use the G4 overlap checker.
then echo fcl name
TDCIDEs_t const & TDCIDEMap() const
Returns all the deposited energy information as stored.
Definition: SimChannel.h:334
bool fStoreReflected
Defines classes to filter particles based on their trajectory.
AllPhysicsLists fAllPhysicsLists
TCEvent evt
Definition: DataStructs.cxx:8
detinfo::DetectorPropertiesData fDetProp
Must outlive fAllPhysicsLists!
CLHEP::HepRandomEngine & fEngine
double fOffPlaneMargin
void beginRun(art::Run &run) override
bool empty(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:555
A Geant4 sensitive detector that accumulates information.
sim::AuxDetSimChannel const GetAuxDetSimChannel() const
Definition: AuxDetReadout.h:73
art framework interface to geometry description
BEGIN_PROLOG could also be cout
auto const detProp
void DumpMCParticle(Stream &&out, simb::MCParticle const &particle, std::string indent, std::string firstIndent)
Dumps the content of the specified particle in the output stream.
Definition: MCDumpers.h:228
LArVoxelReadoutGeometry * fVoxelReadoutGeometry