Public Member Functions | Private Member Functions | Private Attributes | List of all members
larg4::LArG4 Class Reference

Runs Geant4 simulation and propagation of electrons and photons to readout. More...

Inheritance diagram for larg4::LArG4:
art::EDProducer art::detail::Producer art::detail::LegacyModule art::Modifier art::ModuleBase art::ProductRegistryHelper

Public Member Functions

 LArG4 (fhicl::ParameterSet const &pset)
 
- Public Member Functions inherited from art::EDProducer
 EDProducer (fhicl::ParameterSet const &pset)
 
template<typename Config >
 EDProducer (Table< Config > const &config)
 
std::string workerType () const
 
- Public Member Functions inherited from art::detail::Producer
virtual ~Producer () noexcept
 
 Producer (fhicl::ParameterSet const &)
 
 Producer (Producer const &)=delete
 
 Producer (Producer &&)=delete
 
Produceroperator= (Producer const &)=delete
 
Produceroperator= (Producer &&)=delete
 
void doBeginJob (SharedResources const &resources)
 
void doEndJob ()
 
void doRespondToOpenInputFile (FileBlock const &fb)
 
void doRespondToCloseInputFile (FileBlock const &fb)
 
void doRespondToOpenOutputFiles (FileBlock const &fb)
 
void doRespondToCloseOutputFiles (FileBlock const &fb)
 
bool doBeginRun (RunPrincipal &rp, ModuleContext const &mc)
 
bool doEndRun (RunPrincipal &rp, ModuleContext const &mc)
 
bool doBeginSubRun (SubRunPrincipal &srp, ModuleContext const &mc)
 
bool doEndSubRun (SubRunPrincipal &srp, ModuleContext const &mc)
 
bool doEvent (EventPrincipal &ep, ModuleContext const &mc, std::atomic< std::size_t > &counts_run, std::atomic< std::size_t > &counts_passed, std::atomic< std::size_t > &counts_failed)
 
- Public Member Functions inherited from art::Modifier
 ~Modifier () noexcept
 
 Modifier ()
 
 Modifier (Modifier const &)=delete
 
 Modifier (Modifier &&)=delete
 
Modifieroperator= (Modifier const &)=delete
 
Modifieroperator= (Modifier &&)=delete
 
- Public Member Functions inherited from art::ModuleBase
virtual ~ModuleBase () noexcept
 
 ModuleBase ()
 
ModuleDescription const & moduleDescription () const
 
void setModuleDescription (ModuleDescription const &)
 
std::array< std::vector< ProductInfo >, NumBranchTypes > const & getConsumables () const
 
void sortConsumables (std::string const &current_process_name)
 
template<typename T , BranchType BT>
ViewToken< T > consumesView (InputTag const &tag)
 
template<typename T , BranchType BT>
ViewToken< T > mayConsumeView (InputTag const &tag)
 

Private Member Functions

void produce (art::Event &evt) override
 
void beginJob () override
 
void beginRun (art::Run &run) override
 
std::unique_ptr< util::PositionInVolumeFilterCreateParticleVolumeFilter (std::set< std::string > const &vol_names) const
 Pointer used for correctly updating the clock data state. More...
 

Private Attributes

std::unique_ptr< g4b::G4Helper > fG4Help {nullptr}
 G4 interface object. More...
 
larg4::ParticleListActionfparticleListAction
 Geant4 user action to particle information. More...
 
std::string fG4PhysListName
 predefined physics list to use if not making a custom one More...
 
std::string fG4MacroPath
 
bool fCheckOverlaps
 Whether to use the G4 overlap checker. More...
 
bool fMakeMCParticles
 Whether to keep a sim::MCParticle list. More...
 
bool fdumpParticleList
 Whether each event's sim::ParticleList will be displayed. More...
 
bool fdumpSimChannels
 Whether each event's sim::Channel will be displayed. More...
 
bool fUseLitePhotons
 
bool fStoreReflected {false}
 
int fSmartStacking
 Whether to instantiate and use class to. More...
 
double fOffPlaneMargin = 0.
 
std::vector< std::stringfInputLabels
 
std::vector< std::stringfKeepParticlesInVolumes
 Only write particles that have trajectories through these volumes. More...
 
bool fSparsifyTrajectories
 Sparsify MCParticle Trajectories. More...
 
CLHEP::HepRandomEngine & fEngine
 
detinfo::DetectorPropertiesData fDetProp
 Must outlive fAllPhysicsLists! More...
 
AllPhysicsLists fAllPhysicsLists
 
LArVoxelReadoutGeometryfVoxelReadoutGeometry
 

Additional Inherited Members

- Public Types inherited from art::EDProducer
using ModuleType = EDProducer
 
using WorkerType = WorkerT< EDProducer >
 
- Public Types inherited from art::detail::Producer
template<typename UserConfig , typename KeysToIgnore = void>
using Table = Modifier::Table< UserConfig, KeysToIgnore >
 
- Public Types inherited from art::Modifier
template<typename UserConfig , typename UserKeysToIgnore = void>
using Table = ProducerTable< UserConfig, detail::ModuleConfig, UserKeysToIgnore >
 
- Static Public Member Functions inherited from art::EDProducer
static void commitEvent (EventPrincipal &ep, Event &e)
 
- Protected Member Functions inherited from art::ModuleBase
ConsumesCollectorconsumesCollector ()
 
template<typename T , BranchType = InEvent>
ProductToken< T > consumes (InputTag const &)
 
template<typename Element , BranchType = InEvent>
ViewToken< Element > consumesView (InputTag const &)
 
template<typename T , BranchType = InEvent>
void consumesMany ()
 
template<typename T , BranchType = InEvent>
ProductToken< T > mayConsume (InputTag const &)
 
template<typename Element , BranchType = InEvent>
ViewToken< Element > mayConsumeView (InputTag const &)
 
template<typename T , BranchType = InEvent>
void mayConsumeMany ()
 

Detailed Description

Runs Geant4 simulation and propagation of electrons and photons to readout.

This module collects generated particles from one or more generators and processes them through Geant4.

Input

The module reads the particles to process from simb::MCTruth records. Each particle generator is required to produce a vector of such records: std::vector<simb::MCTruth>.

The module allows two operation modes:

  1. process specific generators: the label of the generator modules to be processed is specified explicitly in LArG4 configuration
  2. process all truth information generated so far: no generator is specified in the LArG4 module configuration, and the module will process all data products of type std::vector<simb::MCTruth>, in a non-specified order

For each simb::MCTruth, a Geant4 run is started. The interface with Geant4 is via a helper class provided by nug4. Only the particles in the truth record which have status code (simb::MCParticle::StatusCode()) equal to 1 are processed. These particles are called, in LArG4 jargon, primaries.

Output

The LArG4 module produces:

Notes on the conventions

Timing

The LArG4 module produces sim::SimChannel objects from generated simb::MCParticle. Each particle ("primary") is assigned the time taken from its vertex (a 4-vector), which is expected to be represented in nanoseconds. The sim::SimChannel object is a collection of sim::IDE in time. The position in the sim::IDE is the location where some ionization occurred. The time associated to a sim::IDE is stored in tick units. The time it represents is the time when the ionization happened, which is the time of the primary particle plus the propagation time to the ionization location, plus the drift time, which the ionized electrons take to reach the anode wire. This time is then shifted to the frame of the electronics time via detinfo::DetectorClocks::G4ToElecTime(), which adds a configurable time offset. The time is converted into ticks via detinfo::DetectorClocks::TPCClock(), and this is the final value associated to the sim::IDE. For a more complete overview, see https://cdcvs.fnal.gov/redmine/projects/larsoft/wiki/Simulation#Simulation-Timing

Randomness

The random number generators used by this process are:

Configuration parameters

Simulation details

Source of the operational parameters

Some of the physical properties have their values set in FHiCL configuration (e.g. detinfo::LArParameters). Then, GEANT4 is informed of them via larg4::MaterialPropertyLoader. The material property table in GEANT4 is then used by other LArSoft components to discover the parameter values.

Among the parameters registered to GEANT4, the scintillation yields, i.e. how many scintillation photons are produced on average by 1 MeV of deposited energy, are also stored by type of ioniziong particle. These scintillation yields do include a prescale factor (that may include, for example, the photomultiplier quantum efficiency), from the ScintPreScale parameter of detinfo::LArPropertiesStandard or equivalent.

Reflectivity to optical photons

Two models are supported for the simulation of (scintillation) light crossing detector surfaces:

  1. the standard one from GEANT4, implemented in G4OpBoundaryProcess
  2. a simplified one, implemented in larg4::OpBoundaryProcessSimple

The model is chosen according to the value of detinfo::DetectorProperties::SimpleBoundary(), and the choice is currently exerted by larg4::OpticalPhysics.

The simplified model is faster and simpler: it only deals with absorption and reflection (both specular and diffues). This is the "default" model used in most contexts.

GEANT4 model is more complete and slower. It may take some art to fully configure all the properties of the materials at the sides of the surfaces. The price is a detailed simulation that includes among others refraction and wavelength shifting.

Scintillation

When using the fast optical simulation, which is the "standard" running mode, energy depositions from GEANT4 are "converted" into a number of scintillation photons by the global larg4::IonizationAndScintillation object instance, which internally utilizes the algorithm set up via configuration parameter IonAndScintCalculator in LArG4Parameters service (at the time of writing, "Separate" is supported and "NEST" is accepted too). The number of scintillation photons per energy unit is read from GEANT4 material properties table. It includes already quantum efficiency ("prescale") and it may depend on the type of ionizing particle, depending on the configuration (LArPropertiesStandard parameter ScintByParticleType). This value ("yield") is used as the average of a Poisson distribution from which the actual number of scintillation photons is extracted case by case. The implementation larg4::ISCalculationSeparate may also include medium saturation effects as well, if configured, but only if the scintillation yield is set not to depend on the type of ionizing particle. The number of scintillation photons is then distributed between the fast and slow component by a yield ratio also set in the material parameters, and the single photons are distributed in time accordingly to their component.

Definition at line 314 of file LArG4_module.cc.

Constructor & Destructor Documentation

larg4::LArG4::LArG4 ( fhicl::ParameterSet const &  pset)
explicit

Definition at line 404 of file LArG4_module.cc.

405  : art::EDProducer{pset}
406  , fG4PhysListName(pset.get<std::string>("G4PhysListName", "larg4::PhysicsList"))
407  , fCheckOverlaps(pset.get<bool>("CheckOverlaps", false))
408  , fMakeMCParticles(pset.get<bool>("MakeMCParticles", true))
409  , fdumpParticleList(pset.get<bool>("DumpParticleList", false))
410  , fdumpSimChannels(pset.get<bool>("DumpSimChannels", false))
411  , fSmartStacking(pset.get<int>("SmartStacking", 0))
412  , fOffPlaneMargin(pset.get<double>("ChargeRecoveryMargin", 0.0))
413  , fKeepParticlesInVolumes(pset.get<std::vector<std::string>>("KeepParticlesInVolumes", {}))
414  , fSparsifyTrajectories(pset.get<bool>("SparsifyTrajectories", false))
416  ->createEngine(*this, "HepJamesRandom", "propagation", pset, "PropagationSeed"))
419  {
420  MF_LOG_DEBUG("LArG4") << "Debug: LArG4()";
422 
423  if (!fMakeMCParticles) { // configuration option consistency
424  if (fdumpParticleList) {
426  << "Option `DumpParticleList` can't be set if `MakeMCParticles` is unset.\n";
427  }
428  if (!fKeepParticlesInVolumes.empty()) {
430  << "Option `KeepParticlesInVolumes` can't be set if `MakeMCParticles` is unset.\n";
431  }
432  } // if
433 
434  if (pset.has_key("Seed")) {
436  << "The configuration of LArG4 module has the discontinued 'Seed' parameter.\n"
437  "Seeds are now controlled by two parameters: 'GEANTSeed' and 'PropagationSeed'.";
438  }
439  // setup the random number service for Geant4, the "G4Engine" label is a
440  // special tag setting up a global engine for use by Geant4/CLHEP;
441  // obtain the random seed from NuRandomService,
442  // unless overridden in configuration with key "Seed" or "GEANTSeed"
443  // FIXME: THIS APPEARS TO BE A NO-OP; IS IT NEEDED?
444  (void)art::ServiceHandle<rndm::NuRandomService>()->createEngine(
445  *this, "G4Engine", "GEANT", pset, "GEANTSeed");
446 
447  //get a list of generators to use, otherwise, we'll end up looking for anything that's
448  //made an MCTruth object
449  bool useInputLabels =
450  pset.get_if_present<std::vector<std::string>>("InputLabels", fInputLabels);
451  if (!useInputLabels) fInputLabels.resize(0);
452 
455 
456  if (!lgp->NoPhotonPropagation()) {
457  try {
460  }
461  catch (art::Exception const& e) {
462  // If the service is not configured, then just keep the default
463  // false for reflected light. If reflected photons are simulated
464  // without PVS they will show up in the regular SimPhotons collection
465  if (e.categoryCode() != art::errors::ServiceNotFound) throw;
466  }
467 
468  if (!fUseLitePhotons) {
469  produces<std::vector<sim::SimPhotons>>();
470  if (fStoreReflected) { produces<std::vector<sim::SimPhotons>>("Reflected"); }
471  }
472  else {
473  produces<std::vector<sim::SimPhotonsLite>>();
474  produces<std::vector<sim::OpDetBacktrackerRecord>>();
475  if (fStoreReflected) {
476  produces<std::vector<sim::SimPhotonsLite>>("Reflected");
477  produces<std::vector<sim::OpDetBacktrackerRecord>>("Reflected");
478  }
479  }
480  }
481 
482  if (lgp->FillSimEnergyDeposits()) {
483  produces<std::vector<sim::SimEnergyDeposit>>("TPCActive");
484  produces<std::vector<sim::SimEnergyDeposit>>("Other");
485  }
486 
487  if (fMakeMCParticles) {
488  produces<std::vector<simb::MCParticle>>();
489  produces<art::Assns<simb::MCTruth, simb::MCParticle, sim::GeneratedParticleInfo>>();
490  }
491  if (!lgp->NoElectronPropagation()) produces<std::vector<sim::SimChannel>>();
492  produces<std::vector<sim::AuxDetSimChannel>>();
493 
494  // constructor decides if initialized value is a path or an environment variable
495  cet::search_path sp("FW_SEARCH_PATH");
496 
497  sp.find_file(pset.get<std::string>("GeantCommandFile"), fG4MacroPath);
498  struct stat sb;
499  if (fG4MacroPath.empty() || stat(fG4MacroPath.c_str(), &sb) != 0)
500  // failed to resolve the file name
501  throw cet::exception("NoG4Macro") << "G4 macro file " << fG4MacroPath << " not found!\n";
502  }
std::vector< std::string > fInputLabels
std::string fG4MacroPath
std::string string
Definition: nybbler.cc:12
void produces(std::string const &instanceName={}, Persistable const persistable=Persistable::Yes)
bool fMakeMCParticles
Whether to keep a sim::MCParticle list.
bool NoPhotonPropagation() const
std::vector< std::string > fKeepParticlesInVolumes
Only write particles that have trajectories through these volumes.
bool fSparsifyTrajectories
Sparsify MCParticle Trajectories.
int fSmartStacking
Whether to instantiate and use class to.
bool fdumpSimChannels
Whether each event&#39;s sim::Channel will be displayed.
const double e
bool NoElectronPropagation() const
bool FillSimEnergyDeposits() const
bool fdumpParticleList
Whether each event&#39;s sim::ParticleList will be displayed.
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
bool fUseLitePhotons
#define MF_LOG_DEBUG(id)
std::string fG4PhysListName
predefined physics list to use if not making a custom one
bool fCheckOverlaps
Whether to use the G4 overlap checker.
bool fStoreReflected
AllPhysicsLists fAllPhysicsLists
detinfo::DetectorPropertiesData fDetProp
Must outlive fAllPhysicsLists!
CLHEP::HepRandomEngine & fEngine
double fOffPlaneMargin
static ServiceHandle< RandomNumberGenerator > & rng()
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
bool UseLitePhotons() const

Member Function Documentation

void larg4::LArG4::beginJob ( )
overrideprivatevirtual

Reimplemented from art::EDProducer.

Definition at line 506 of file LArG4_module.cc.

507  {
508  fG4Help = std::make_unique<g4b::G4Helper>(fG4MacroPath, fG4PhysListName);
509 
510  if (fCheckOverlaps) fG4Help->SetOverlapCheck(true);
511 
513  fG4Help->ConstructDetector(geom->GDMLFile());
514 
515  // Get the logical volume store and assign material properties
517  auto const detProp =
519  mpl.GetPropertiesFromServices(detProp);
520  mpl.UpdateGeometry(G4LogicalVolumeStore::GetInstance());
521 
522  // Tell the detector about the parallel LAr voxel geometry.
523  std::vector<G4VUserParallelWorld*> pworlds;
524 
525  // Intialize G4 physics and primary generator action
526  fG4Help->InitPhysics();
527 
528  // create the ionization and scintillation calculator;
529  // this is a singleton (!) so it does not make sense
530  // to create it in LArVoxelReadoutGeometry
532 
533  // make a parallel world for each TPC in the detector
534  LArVoxelReadoutGeometry::Setup_t readoutGeomSetupData;
535  readoutGeomSetupData.readoutSetup.offPlaneMargin = fOffPlaneMargin;
536  readoutGeomSetupData.readoutSetup.propGen = &fEngine;
537 
539  new LArVoxelReadoutGeometry("LArVoxelReadoutGeometry", readoutGeomSetupData);
540  pworlds.push_back(fVoxelReadoutGeometry);
541  pworlds.push_back(
542  new OpDetReadoutGeometry(geom->OpDetGeoName(), "OpDetReadoutGeometry", fUseLitePhotons));
543  pworlds.push_back(new AuxDetReadoutGeometry("AuxDetReadoutGeometry"));
544 
545  fG4Help->SetParallelWorlds(pworlds);
546 
547  // moved up
548  // Intialize G4 physics and primary generator action
549  fG4Help->InitPhysics();
550 
551  // Use the UserActionManager to handle all the Geant4 user hooks.
552  g4b::UserActionManager* uaManager = g4b::UserActionManager::Instance();
553 
554  // User-action class for accumulating LAr voxels.
556 
557  // User-action class for accumulating particles and trajectories
558  // produced in the detector.
560  lgp->StoreTrajectories(),
561  lgp->KeepEMShowerDaughters(),
563  uaManager->AddAndAdoptAction(fparticleListAction);
564 
565  // UserActionManager is now configured so continue G4 initialization
566  fG4Help->SetUserAction();
567 
568  // With an enormous detector with lots of rock ala LAr34 (nee LAr20)
569  // we need to be smarter about stacking.
570  if (fSmartStacking > 0) {
571  G4UserStackingAction* stacking_action = new LArStackingAction(fSmartStacking);
572  fG4Help->GetRunManager()->SetUserAction(stacking_action);
573  }
574  }
std::unique_ptr< g4b::G4Helper > fG4Help
G4 interface object.
bool KeepEMShowerDaughters() const
std::string fG4MacroPath
Stores material properties and sends them to GEANT4 geometry.
std::string OpDetGeoName(unsigned int c=0) const
Returns gdml string which gives sensitive opdet name.
void GetPropertiesFromServices(detinfo::DetectorPropertiesData const &detProp)
Imports properties from LArSoft services.
bool fMakeMCParticles
Whether to keep a sim::MCParticle list.
bool StoreTrajectories() const
int fSmartStacking
Whether to instantiate and use class to.
std::string GDMLFile() const
Returns the full directory path to the GDML file source.
larg4::ParticleListAction * fparticleListAction
Geant4 user action to particle information.
void UpdateGeometry(G4LogicalVolumeStore *lvs)
Updates the material properties with the collected values.
double ParticleKineticEnergyCut() const
bool fUseLitePhotons
static IonizationAndScintillation * CreateInstance(detinfo::DetectorPropertiesData const &detProp, CLHEP::HepRandomEngine &engine)
std::string fG4PhysListName
predefined physics list to use if not making a custom one
bool fCheckOverlaps
Whether to use the G4 overlap checker.
CLHEP::HepRandomEngine & fEngine
double fOffPlaneMargin
LArVoxelReadoutGeometry * fVoxelReadoutGeometry
void larg4::LArG4::beginRun ( art::Run run)
overrideprivatevirtual

Reimplemented from art::EDProducer.

Definition at line 577 of file LArG4_module.cc.

578  {
579  // prepare the filter object (null if no filtering)
580  std::set<std::string> volnameset(fKeepParticlesInVolumes.begin(),
583  }
std::vector< std::string > fKeepParticlesInVolumes
Only write particles that have trajectories through these volumes.
larg4::ParticleListAction * fparticleListAction
Geant4 user action to particle information.
void ParticleFilter(std::unique_ptr< util::PositionInVolumeFilter > &&filter)
Grabs a particle filter.
std::unique_ptr< util::PositionInVolumeFilter > CreateParticleVolumeFilter(std::set< std::string > const &vol_names) const
Pointer used for correctly updating the clock data state.
std::unique_ptr< util::PositionInVolumeFilter > larg4::LArG4::CreateParticleVolumeFilter ( std::set< std::string > const &  vol_names) const
private

Pointer used for correctly updating the clock data state.

Configures and returns a particle filter

Definition at line 586 of file LArG4_module.cc.

587  {
588  // if we don't have favourite volumes, don't even bother creating a filter
589  if (empty(vol_names)) return {};
590 
591  auto const& geom = *art::ServiceHandle<geo::Geometry const>();
592 
593  std::vector<std::vector<TGeoNode const*>> node_paths = geom.FindAllVolumePaths(vol_names);
594 
595  // collection of interesting volumes
597  GeoVolumePairs.reserve(node_paths.size()); // because we are obsessed
598 
599  //for each interesting volume, follow the node path and collect
600  //total rotations and translations
601  for (size_t iVolume = 0; iVolume < node_paths.size(); ++iVolume) {
602  std::vector<TGeoNode const*> path = node_paths[iVolume];
603 
604  auto pTransl = new TGeoTranslation(0., 0., 0.);
605  auto pRot = new TGeoRotation();
606  for (TGeoNode const* node : path) {
607  TGeoTranslation thistranslate(*node->GetMatrix());
608  TGeoRotation thisrotate(*node->GetMatrix());
609  pTransl->Add(&thistranslate);
610  *pRot = *pRot * thisrotate;
611  }
612 
613  // for some reason, pRot and pTransl don't have tr and rot bits set
614  // correctly make new translations and rotations so bits are set correctly
615  auto pTransl2 = new TGeoTranslation(
616  pTransl->GetTranslation()[0], pTransl->GetTranslation()[1], pTransl->GetTranslation()[2]);
617  double phi = 0., theta = 0., psi = 0.;
618  pRot->GetAngles(phi, theta, psi);
619  auto pRot2 = new TGeoRotation();
620  pRot2->SetAngles(phi, theta, psi);
621 
622  auto pTransf = new TGeoCombiTrans(*pTransl2, *pRot2);
623  GeoVolumePairs.emplace_back(node_paths[iVolume].back()->GetVolume(), pTransf);
624  }
625 
626  return std::make_unique<util::PositionInVolumeFilter>(std::move(GeoVolumePairs));
627  } // CreateParticleVolumeFilter()
std::vector< VolumeInfo_t > AllVolumeInfo_t
def move(depos, offset)
Definition: depos.py:107
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:97
void larg4::LArG4::produce ( art::Event evt)
overrideprivatevirtual

The main routine of this module: Fetch the primary particles from the event, simulate their evolution in the detector, and produce the detector response.

Implements art::EDProducer.

Definition at line 630 of file LArG4_module.cc.

631  {
632  MF_LOG_DEBUG("LArG4") << "produce()";
633  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
634  auto const detProp =
636  LArVoxelReadoutGeometry::Sentry const set_for_event{fVoxelReadoutGeometry, clockData, detProp};
637 
638  // loop over the lists and put the particles and voxels into the event as
639  // collections
640  auto scCol = std::make_unique<std::vector<sim::SimChannel>>();
641  auto adCol = std::make_unique<std::vector<sim::AuxDetSimChannel>>();
642  auto tpassn =
644  std::make_unique<art::Assns<simb::MCTruth, simb::MCParticle, sim::GeneratedParticleInfo>>() :
645  nullptr;
646  auto partCol =
648  std::make_unique<std::vector<simb::MCParticle>>() :
649  nullptr;
650  auto PhotonCol = std::make_unique<std::vector<sim::SimPhotons>>();
651  auto PhotonColRefl = std::make_unique<std::vector<sim::SimPhotons>>();
652  auto LitePhotonCol = std::make_unique<std::vector<sim::SimPhotonsLite>>();
653  auto LitePhotonColRefl = std::make_unique<std::vector<sim::SimPhotonsLite>>();
654  auto cOpDetBacktrackerRecordCol = std::make_unique<std::vector<sim::OpDetBacktrackerRecord>>();
655  auto cOpDetBacktrackerRecordColRefl =
656  std::make_unique<std::vector<sim::OpDetBacktrackerRecord>>();
657 
658  std::optional<art::PtrMaker<simb::MCParticle>> makeMCPartPtr;
659  if (fMakeMCParticles) makeMCPartPtr.emplace(evt);
660 
661  // for energy deposits
662  auto edepCol_TPCActive = std::make_unique<std::vector<sim::SimEnergyDeposit>>();
663  auto edepCol_Other = std::make_unique<std::vector<sim::SimEnergyDeposit>>();
664 
665  // Fetch the lists of LAr voxels and particles.
668 
669  // Clear the detected photon table
671  if (lgp->FillSimEnergyDeposits()) OpDetPhotonTable::Instance()->ClearEnergyDeposits();
672 
673  // reset the track ID offset as we have a new collection of interactions
675 
676  //look to see if there is any MCTruth information for this
677  //event
678  std::vector<art::Handle<std::vector<simb::MCTruth>>> mclists;
679  if (empty(fInputLabels))
680  //evt.getManyByType(mclists);
681  mclists = evt.getMany<std::vector<simb::MCTruth>>();
682  else {
683  mclists.resize(fInputLabels.size());
684  for (size_t i = 0; i < fInputLabels.size(); i++)
685  evt.getByLabel(fInputLabels[i], mclists[i]);
686  }
687 
688  unsigned int nGeneratedParticles = 0;
689 
690  // Need to process Geant4 simulation for each interaction separately.
691  for (size_t mcl = 0; mcl < mclists.size(); ++mcl) {
692 
693  art::Handle<std::vector<simb::MCTruth>> mclistHandle = mclists[mcl];
694 
695  for (size_t m = 0; m < mclistHandle->size(); ++m) {
696  art::Ptr<simb::MCTruth> mct(mclistHandle, m);
697 
698  MF_LOG_DEBUG("LArG4") << *(mct.get());
699 
700  // The following tells Geant4 to track the particles in this interaction.
701  fG4Help->G4Run(mct);
702 
703  if (!partCol) continue;
704  assert(tpassn);
705 
706  // receive the particle list
707  sim::ParticleList particleList = fparticleListAction->YieldList();
708 
709  for (auto const& partPair : particleList) {
710  simb::MCParticle& p = *(partPair.second);
711  ++nGeneratedParticles;
712 
713  // if the particle has been marked as dropped, we don't save it
714  // (as of LArSoft ~v5.6 this does not ever happen because
715  // ParticleListAction has already taken care of deleting them)
716  if (ParticleListAction::isDropped(&p)) continue;
717 
718  sim::GeneratedParticleInfo const truthInfo{
720  if (!truthInfo.hasGeneratedParticleIndex() && (p.Mother() == 0)) {
721  // this means it's primary but with no information; logic error!!
723  error << "Failed to match primary particle:\n";
725  error << "\nwith particles from the truth record '"
726  << mclistHandle.provenance()->inputTag() << "':\n";
727  sim::dump::DumpMCTruth(error, *mct, 2U, " "); // 2 points per line
728  error << "\n";
729  throw error;
730  }
731 
733 
734  partCol->push_back(std::move(p));
735 
736  tpassn->addSingle(mct, (*makeMCPartPtr)(partCol->size() - 1), truthInfo);
737 
738  } // for(particleList)
739 
740  // Has the user request a detailed dump of the output objects?
741  if (fdumpParticleList) {
742  mf::LogInfo("LArG4") << "Dump sim::ParticleList; size()=" << particleList.size() << "\n"
743  << particleList;
744  }
745  }
746 
747  } // end loop over interactions
748 
749  // get the electrons from the LArVoxelReadout sensitive detector
750  // Get the sensitive-detector manager.
751  G4SDManager* sdManager = G4SDManager::GetSDMpointer();
752 
753  // Find the sensitive detector with the name "LArVoxelSD".
754  auto theOpDetDet = dynamic_cast<OpDetSensitiveDetector*>(
755  sdManager->FindSensitiveDetector("OpDetSensitiveDetector"));
756 
757  // Store the contents of the detected photon table
758  //
759  if (theOpDetDet) {
760 
761  if (!lgp->NoPhotonPropagation()) {
762 
763  for (int Reflected = 0; Reflected <= 1; Reflected++) {
764  if (Reflected && !fStoreReflected) continue;
765 
766  if (!fUseLitePhotons) {
767  MF_LOG_DEBUG("Optical") << "Storing OpDet Hit Collection in Event";
768  std::vector<sim::SimPhotons>& ThePhotons =
770  if (Reflected)
771  PhotonColRefl->reserve(ThePhotons.size());
772  else
773  PhotonCol->reserve(ThePhotons.size());
774  for (auto& it : ThePhotons) {
775  if (Reflected)
776  PhotonColRefl->push_back(std::move(it));
777  else
778  PhotonCol->push_back(std::move(it));
779  }
780  }
781  else {
782  MF_LOG_DEBUG("Optical") << "Storing OpDet Hit Collection in Event";
783 
784  std::map<int, std::map<int, int>> ThePhotons =
786 
787  if (size(ThePhotons) > 0) {
788  LitePhotonCol->reserve(ThePhotons.size());
789  for (auto const& [opChannel, detectedPhotons] : ThePhotons) {
791  ph.OpChannel = opChannel;
792  ph.DetectedPhotons = detectedPhotons;
793  if (Reflected)
794  LitePhotonColRefl->push_back(std::move(ph));
795  else
796  LitePhotonCol->push_back(std::move(ph));
797  }
798  }
799  }
800  if (Reflected)
801  *cOpDetBacktrackerRecordColRefl =
803  else
804  *cOpDetBacktrackerRecordCol =
806  }
807  } //end if no photon propagation
808 
809  if (lgp->FillSimEnergyDeposits()) {
810  // we steal the only existing copy of the energy deposit map. Oink!
812  for (auto& [volumeName, edepCol] : edepMap) {
813  // note: constant reference to a (smart) pointer to non-const data
814  auto const& destColl =
815  boost::contains(volumeName, "TPCActive") ? edepCol_TPCActive : edepCol_Other;
816  append(*destColl, std::move(edepCol));
817  } // for
818  }
819  } //end if theOpDetDet
820 
821  if (!lgp->NoElectronPropagation()) {
822 
823  // only put the sim::SimChannels into the event once, not once for every
824  // MCTruth in the event
825 
826  std::set<LArVoxelReadout*> ReadoutList; // to be cleared later on
827 
828  for (unsigned int c = 0; c < geom->Ncryostats(); ++c) {
829 
830  // map to keep track of which channels we already have SimChannels for in scCol
831  // remake this map on each cryostat as channels ought not to be shared between
832  // cryostats, just between TPC's
833 
834  std::map<unsigned int, unsigned int> channelToscCol;
835 
836  unsigned int ntpcs = geom->Cryostat(c).NTPC();
837  for (unsigned int t = 0; t < ntpcs; ++t) {
838  std::string name("LArVoxelSD");
839  std::ostringstream sstr;
840  sstr << name << "_Cryostat" << c << "_TPC" << t;
841 
842  // try first to find the sensitive detector specific for this TPC;
843  // do not bother writing on screen if there is none (yet)
844  G4VSensitiveDetector* sd = sdManager->FindSensitiveDetector(sstr.str(), false);
845  // if there is none, catch the general one (called just "LArVoxelSD")
846  if (!sd) sd = sdManager->FindSensitiveDetector(name, false);
847  // If this didn't work, then a sensitive detector with
848  // the name "LArVoxelSD" does not exist.
849  if (!sd) {
850  throw cet::exception("LArG4")
851  << "Sensitive detector for cryostat " << c << " TPC " << t << " not found (neither '"
852  << sstr.str() << "' nor '" << name << "' exist)\n";
853  }
854 
855  // Convert the G4VSensitiveDetector* to a LArVoxelReadout*.
856  auto larVoxelReadout = dynamic_cast<LArVoxelReadout*>(sd);
857 
858  // If this didn't work, there is a "LArVoxelSD" detector, but
859  // it's not a LArVoxelReadout object.
860  if (!larVoxelReadout) {
861  throw cet::exception("LArG4")
862  << "Sensitive detector '" << sd->GetName() << "' is not a LArVoxelReadout object\n";
863  }
864 
865  LArVoxelReadout::ChannelMap_t& channels = larVoxelReadout->GetSimChannelMap(c, t);
866  if (!empty(channels)) {
867  MF_LOG_DEBUG("LArG4") << "now put " << channels.size() << " SimChannels from C=" << c
868  << " T=" << t << " into the event";
869  }
870 
871  for (auto ch_pair : channels) {
872  sim::SimChannel& sc = ch_pair.second;
873 
874  // push sc onto scCol but only if we haven't already put something in scCol for this channel.
875  // if we have, then merge the ionization deposits. Skip the check if we only have one TPC
876 
877  if (ntpcs > 1) {
878  unsigned int ichan = sc.Channel();
879  auto itertest = channelToscCol.find(ichan);
880  if (itertest == channelToscCol.end()) {
881  channelToscCol[ichan] = scCol->size();
882  scCol->emplace_back(std::move(sc));
883  }
884  else {
885  unsigned int idtest = itertest->second;
886  auto const& tdcideMap = sc.TDCIDEMap();
887  for (auto const& tdcide : tdcideMap) {
888  for (auto const& ide : tdcide.second) {
889  double xyz[3] = {ide.x, ide.y, ide.z};
890  scCol->at(idtest).AddIonizationElectrons(
891  ide.trackID, tdcide.first, ide.numElectrons, xyz, ide.energy);
892  } // end loop to add ionization electrons to scCol->at(idtest)
893  } // end loop over tdc to vector<sim::IDE> map
894  } // end if check to see if we've put SimChannels in for ichan yet or not
895  }
896  else {
897  scCol->emplace_back(std::move(sc));
898  } // end of check if we only have one TPC (skips check for multiple simchannels if we have just one TPC)
899  } // end loop over simchannels for this TPC
900 
901  // mark it for clearing
902  ReadoutList.insert(const_cast<LArVoxelReadout*>(larVoxelReadout));
903 
904  } // end loop over tpcs
905  } // end loop over cryostats
906 
907  for (LArVoxelReadout* larVoxelReadout : ReadoutList) {
908  larVoxelReadout->ClearSimChannels();
909  }
910  } //endif electron prop
911 
912  // only put the sim::AuxDetSimChannels into the event once, not once for every
913  // MCTruth in the event
914 
915  adCol->reserve(geom->NAuxDets());
916  for (unsigned int a = 0; a < geom->NAuxDets(); ++a) {
917 
918  // there should always be at least one senstive volume because
919  // we make one for the full aux det if none are specified in the
920  // gdml file - see AuxDetGeo.cxx
921  for (size_t sv = 0; sv < geom->AuxDet(a).NSensitiveVolume(); ++sv) {
922 
923  // N.B. this name convention is used when creating the
924  // AuxDetReadout SD in AuxDetReadoutGeometry
925  std::stringstream name;
926  name << "AuxDetSD_AuxDet" << a << "_" << sv;
927  G4VSensitiveDetector* sd = sdManager->FindSensitiveDetector(name.str().c_str());
928  if (!sd) {
929  throw cet::exception("LArG4")
930  << "Sensitive detector '" << name.str() << "' does not exist\n";
931  }
932 
933  // Convert the G4VSensitiveDetector* to a AuxDetReadout*.
934  larg4::AuxDetReadout* auxDetReadout = dynamic_cast<larg4::AuxDetReadout*>(sd);
935 
936  MF_LOG_DEBUG("LArG4") << "now put the AuxDetSimTracks in the event";
937 
938  const sim::AuxDetSimChannel adsc = auxDetReadout->GetAuxDetSimChannel();
939  adCol->push_back(adsc);
940  auxDetReadout->clear();
941  }
942  } // Loop over AuxDets
943 
944  if (partCol) {
945  mf::LogInfo("LArG4") << "Geant4 simulated " << nGeneratedParticles << " MC particles, we keep "
946  << partCol->size() << " .";
947  }
948 
949  if (fdumpSimChannels) {
950  mf::LogVerbatim("DumpSimChannels")
951  << "Event " << evt.id() << ": " << scCol->size() << " channels with signal";
952  unsigned int nChannels = 0;
953  for (const sim::SimChannel& sc : *scCol) {
954  mf::LogVerbatim out("DumpSimChannels");
955  out << " #" << nChannels << ": ";
956  // dump indenting with " ", but not on the first line
957  sc.Dump(out, " ");
958  ++nChannels;
959  } // for
960  } // if dump SimChannels
961 
962  if (!lgp->NoElectronPropagation()) evt.put(std::move(scCol));
963 
964  evt.put(std::move(adCol));
965  if (partCol) evt.put(std::move(partCol));
966  if (tpassn) evt.put(std::move(tpassn));
967  if (!lgp->NoPhotonPropagation()) {
968  if (!fUseLitePhotons) {
969  evt.put(std::move(PhotonCol));
970  if (fStoreReflected) evt.put(std::move(PhotonColRefl), "Reflected");
971  }
972  else {
973  evt.put(std::move(LitePhotonCol));
974  evt.put(std::move(cOpDetBacktrackerRecordCol));
975  if (fStoreReflected) {
976  evt.put(std::move(LitePhotonColRefl), "Reflected");
977  evt.put(std::move(cOpDetBacktrackerRecordColRefl), "Reflected");
978  }
979  }
980  }
981 
982  if (lgp->FillSimEnergyDeposits()) {
983  evt.put(std::move(edepCol_TPCActive), "TPCActive");
984  evt.put(std::move(edepCol_Other), "Other");
985  }
986  return;
987  } // LArG4::produce()
std::vector< std::string > fInputLabels
static QCString name
Definition: declinfo.cpp:673
std::unique_ptr< g4b::G4Helper > fG4Help
G4 interface object.
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< sim::OpDetBacktrackerRecord > YieldReflectedOpDetBacktrackerRecords()
Energy deposited on a readout channel by simulated tracks.
Definition: SimChannel.h:140
std::string string
Definition: nybbler.cc:12
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
int Mother() const
Definition: MCParticle.h:213
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:337
bool fMakeMCParticles
Whether to keep a sim::MCParticle list.
error
Definition: include.cc:26
std::unordered_map< std::string, std::vector< sim::SimEnergyDeposit > > YieldSimEnergyDeposits()
Yields the map of energy deposits by volume name, and resets the internal one.
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
int TrackId() const
Definition: MCParticle.h:210
std::map< int, int > DetectedPhotons
Number of photons detected at each given time: time tick -> photons.
Definition: SimPhotons.h:117
bool fSparsifyTrajectories
Sparsify MCParticle Trajectories.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
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.
sim::ParticleList && YieldList()
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
Definition: DataViewImpl.h:633
std::map< unsigned int, sim::SimChannel > ChannelMap_t
Type of map channel -> sim::SimChannel.
std::vector< sim::SimPhotons > & GetPhotons(bool Reflected=false)
static bool isDropped(simb::MCParticle const *p)
returns whether the specified particle has been marked as dropped
Provenance const * provenance() const
Definition: Handle.h:205
const double a
std::vector< Handle< PROD > > getMany(SelectorBase const &selector=MatchAllSelector{}) const
Definition: DataViewImpl.h:479
def move(depos, offset)
Definition: depos.py:107
virtual void clear()
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:345
int OpChannel
Optical detector channel associated to this data.
Definition: SimPhotons.h:114
p
Definition: test.py:223
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:181
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
size_t NSensitiveVolume() const
Definition: AuxDetGeo.h:172
GeneratedParticleIndex_t GetPrimaryTruthIndex(int trackId) const
Returns the index of primary truth (sim::NoGeneratorIndex if none).
bool fdumpParticleList
Whether each event&#39;s sim::ParticleList will be displayed.
AuxDetGeo const & AuxDet(unsigned int const ad=0) const
Returns the specified auxiliary detector.
unsigned int NOpDets() const
Number of OpDets in the whole detector.
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
raw::ChannelID_t Channel() const
Returns the readout channel this object describes.
Definition: SimChannel.h:329
static OpDetPhotonTable * Instance(bool LitePhotons=false)
Compact representation of photons on a channel.
Definition: SimPhotons.h:103
bool fUseLitePhotons
std::vector< sim::OpDetBacktrackerRecord > YieldOpDetBacktrackerRecords()
Contains information about a generated particle.
#define MF_LOG_DEBUG(id)
TDCIDEs_t const & TDCIDEMap() const
Returns all the deposited energy information as stored.
Definition: SimChannel.h:328
bool fStoreReflected
std::map< int, std::map< int, int > > GetLitePhotons(bool Reflected=false)
void SparsifyTrajectory(double margin=0.1, bool keep_second_to_last=false)
Definition: MCParticle.h:265
void ClearTable(size_t nch=0)
EventID id() const
Definition: Event.cc:34
sim::AuxDetSimChannel const GetAuxDetSimChannel() const
Definition: AuxDetReadout.h:73
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:227
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:97
LArVoxelReadoutGeometry * fVoxelReadoutGeometry
unsigned int NAuxDets() const
Returns the number of auxiliary detectors.

Member Data Documentation

AllPhysicsLists larg4::LArG4::fAllPhysicsLists
private

Definition at line 352 of file LArG4_module.cc.

bool larg4::LArG4::fCheckOverlaps
private

Whether to use the G4 overlap checker.

Definition at line 333 of file LArG4_module.cc.

detinfo::DetectorPropertiesData larg4::LArG4::fDetProp
private

Must outlive fAllPhysicsLists!

Definition at line 351 of file LArG4_module.cc.

bool larg4::LArG4::fdumpParticleList
private

Whether each event's sim::ParticleList will be displayed.

Definition at line 335 of file LArG4_module.cc.

bool larg4::LArG4::fdumpSimChannels
private

Whether each event's sim::Channel will be displayed.

Definition at line 336 of file LArG4_module.cc.

CLHEP::HepRandomEngine& larg4::LArG4::fEngine
private

Random-number engine for IonizationAndScintillation initialization

Definition at line 348 of file LArG4_module.cc.

std::unique_ptr<g4b::G4Helper> larg4::LArG4::fG4Help {nullptr}
private

G4 interface object.

Definition at line 326 of file LArG4_module.cc.

std::string larg4::LArG4::fG4MacroPath
private

directory path for Geant4 macro file to be executed before main MC processing.

Definition at line 331 of file LArG4_module.cc.

std::string larg4::LArG4::fG4PhysListName
private

predefined physics list to use if not making a custom one

Definition at line 330 of file LArG4_module.cc.

std::vector<std::string> larg4::LArG4::fInputLabels
private

Definition at line 342 of file LArG4_module.cc.

std::vector<std::string> larg4::LArG4::fKeepParticlesInVolumes
private

Only write particles that have trajectories through these volumes.

Definition at line 344 of file LArG4_module.cc.

bool larg4::LArG4::fMakeMCParticles
private

Whether to keep a sim::MCParticle list.

Definition at line 334 of file LArG4_module.cc.

double larg4::LArG4::fOffPlaneMargin = 0.
private

Off-plane charge recovery margin dictate how tracks are put on stack.

Definition at line 340 of file LArG4_module.cc.

larg4::ParticleListAction* larg4::LArG4::fparticleListAction
private
Initial value:
{
nullptr}

Geant4 user action to particle information.

Definition at line 327 of file LArG4_module.cc.

int larg4::LArG4::fSmartStacking
private

Whether to instantiate and use class to.

Definition at line 339 of file LArG4_module.cc.

bool larg4::LArG4::fSparsifyTrajectories
private

Sparsify MCParticle Trajectories.

Definition at line 346 of file LArG4_module.cc.

bool larg4::LArG4::fStoreReflected {false}
private

Definition at line 338 of file LArG4_module.cc.

bool larg4::LArG4::fUseLitePhotons
private

Definition at line 337 of file LArG4_module.cc.

LArVoxelReadoutGeometry* larg4::LArG4::fVoxelReadoutGeometry
private
Initial value:
{
nullptr}

Definition at line 353 of file LArG4_module.cc.


The documentation for this class was generated from the following file: