LArSoft  v10_04_05
Liquid Argon Software toolkit - https://larsoft.org/
LArG4_module.cc
Go to the documentation of this file.
1 
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
40 #include "cetlib/search_path.h"
41 #include "cetlib_except/exception.h"
42 #include "fhiclcpp/ParameterSet.h"
44 
45 // art extensions
47 
48 // LArSoft Includes
52 #include "larcorealg/CoreUtils/ParticleFilters.h" // util::PositionInVolumeFilter
55 #include "lardataalg/MCDumpers/MCDumpers.h" // sim::dump namespace
79 
80 // G4 Includes
81 #include "Geant4/G4LogicalVolumeStore.hh"
82 #include "Geant4/G4RunManager.hh"
83 #include "Geant4/G4SDManager.hh"
84 #include "Geant4/G4VSensitiveDetector.hh"
85 #include "Geant4/G4VUserDetectorConstruction.hh"
86 
87 //For energy depositions
89 
90 // Boost includes
91 #include "boost/algorithm/string.hpp"
92 
94 namespace larg4 {
95 
96  // Forward declarations within namespace.
97  class LArVoxelListAction;
98 
304  class LArG4 : public art::EDProducer {
305  public:
306  explicit LArG4(fhicl::ParameterSet const& pset);
307 
308  private:
311  void produce(art::Event& evt) override;
312  void beginJob() override;
313  void beginRun(art::Run& run) override;
314 
315  std::unique_ptr<g4b::G4Helper> fG4Help{nullptr};
317  nullptr};
318 
319  std::string fG4PhysListName;
320  std::string fG4MacroPath;
321  bool fCheckOverlaps;
324  bool
329  bool fStoreReflected{false};
331  double fOffPlaneMargin = 0.;
332  std::vector<std::string> fInputLabels;
334  std::vector<std::string>
336 
338 
339  CLHEP::HepRandomEngine& fEngine;
340 
345  nullptr};
346 
348  std::unique_ptr<util::PositionInVolumeFilter> CreateParticleVolumeFilter(
349  std::set<std::string> const& vol_names) const;
350  };
351 
352 } // namespace LArG4
353 
354 namespace {
355 
356  // ---------------------------------------------------------------------------
373  template <typename T>
374  std::vector<T>& append(std::vector<T>& dest, std::vector<T>&& source)
375  {
376  if (empty(dest))
377  dest = std::move(source);
378  else {
379  dest.insert(dest.end(), std::move_iterator{begin(source)}, std::move_iterator{end(source)});
380  source = std::vector<T>{}; // ensure the old memory is released
381  }
382  return dest;
383  }
384  // ---------------------------------------------------------------------------
385 
386 } // local namespace
387 
388 namespace larg4 {
389 
390  //----------------------------------------------------------------------
391  // Constructor
393  : art::EDProducer{pset}
394  , fG4PhysListName(pset.get<std::string>("G4PhysListName", "larg4::PhysicsList"))
395  , fCheckOverlaps(pset.get<bool>("CheckOverlaps", false))
396  , fMakeMCParticles(pset.get<bool>("MakeMCParticles", true))
397  , fStoreDroppedMCParticles(pset.get<bool>("StoreDroppedMCParticles", false))
398  , fdumpParticleList(pset.get<bool>("DumpParticleList", false))
399  , fdumpSimChannels(pset.get<bool>("DumpSimChannels", false))
400  , fSmartStacking(pset.get<int>("SmartStacking", 0))
401  , fOffPlaneMargin(pset.get<double>("ChargeRecoveryMargin", 0.0))
402  , fKeepParticlesInVolumes(pset.get<std::vector<std::string>>("KeepParticlesInVolumes", {}))
403  , fSparsifyTrajectories(pset.get<bool>("SparsifyTrajectories", false))
404  , fEngine(art::ServiceHandle<rndm::NuRandomService>()->registerAndSeedEngine(
405  createEngine(0, "HepJamesRandom", "propagation"),
406  "HepJamesRandom",
407  "propagation",
408  pset,
409  "PropagationSeed"))
412  {
413  MF_LOG_DEBUG("LArG4") << "Debug: LArG4()";
414 
415  if (!fMakeMCParticles) { // configuration option consistency
416  if (fdumpParticleList) {
418  << "Option `DumpParticleList` can't be set if `MakeMCParticles` is unset.\n";
419  }
420  if (!fKeepParticlesInVolumes.empty()) {
422  << "Option `KeepParticlesInVolumes` can't be set if `MakeMCParticles` is unset.\n";
423  }
424  } // if
425 
426  if (pset.has_key("Seed")) {
428  << "The configuration of LArG4 module has the discontinued 'Seed' parameter.\n"
429  "Seeds are now controlled by two parameters: 'GEANTSeed' and 'PropagationSeed'.";
430  }
431  // setup the random number service for Geant4, the "G4Engine" label is a special tag
432  // setting up a global engine for use by Geant4/CLHEP; obtain the random seed from
433  // NuRandomService, unless overridden in configuration with key "Seed" or "GEANTSeed"
434  // FIXME: THIS APPEARS TO BE A NO-OP; IS IT NEEDED?
435  (void)art::ServiceHandle<rndm::NuRandomService>()->registerAndSeedEngine(
436  createEngine(0, "G4Engine", "GEANT"), "G4Engine", "GEANT", pset, "GEANTSeed");
437 
438  // get a list of generators to use, otherwise, we'll end up looking for anything
439  // that's made an MCTruth object
440  bool useInputLabels =
441  pset.get_if_present<std::vector<std::string>>("InputLabels", fInputLabels);
442  if (!useInputLabels) fInputLabels.resize(0);
443 
446 
447  if (!lgp->NoPhotonPropagation()) {
448  try {
451  }
452  catch (art::Exception const& e) {
453  // If the service is not configured, then just keep the default false for
454  // reflected light. If reflected photons are simulated without PVS they will show
455  // up in the regular SimPhotons collection
456  if (e.categoryCode() != art::errors::ServiceNotFound) throw;
457  }
458 
459  if (!fUseLitePhotons) {
460  produces<std::vector<sim::SimPhotons>>();
461  if (fStoreReflected) { produces<std::vector<sim::SimPhotons>>("Reflected"); }
462  }
463  else {
464  produces<std::vector<sim::SimPhotonsLite>>();
465  produces<std::vector<sim::OpDetBacktrackerRecord>>();
466  if (fStoreReflected) {
467  produces<std::vector<sim::SimPhotonsLite>>("Reflected");
468  produces<std::vector<sim::OpDetBacktrackerRecord>>("Reflected");
469  }
470  }
471  }
472 
473  if (lgp->FillSimEnergyDeposits()) {
474  produces<std::vector<sim::SimEnergyDeposit>>("TPCActive");
475  produces<std::vector<sim::SimEnergyDeposit>>("Other");
476  }
477 
478  if (fMakeMCParticles) {
479  produces<std::vector<simb::MCParticle>>();
480  produces<art::Assns<simb::MCTruth, simb::MCParticle, sim::GeneratedParticleInfo>>();
481  }
482  if (fStoreDroppedMCParticles) { produces<std::vector<sim::MCParticleLite>>(); }
483  if (!lgp->NoElectronPropagation()) produces<std::vector<sim::SimChannel>>();
484  produces<std::vector<sim::AuxDetSimChannel>>();
485 
486  // constructor decides if initialized value is a path or an environment variable
487  cet::search_path sp("FW_SEARCH_PATH");
488 
489  sp.find_file(pset.get<std::string>("GeantCommandFile"), fG4MacroPath);
490  struct stat sb;
491  if (fG4MacroPath.empty() || stat(fG4MacroPath.c_str(), &sb) != 0)
492  // failed to resolve the file name
493  throw cet::exception("NoG4Macro") << "G4 macro file " << fG4MacroPath << " not found!\n";
494  }
495 
496  //----------------------------------------------------------------------
498  {
499  fG4Help = std::make_unique<g4b::G4Helper>(fG4MacroPath, fG4PhysListName);
500 
501  if (fCheckOverlaps) fG4Help->SetOverlapCheck(true);
502 
504  fG4Help->ConstructDetector(geom->GDMLFile());
505  auto const& wireReadoutGeom = art::ServiceHandle<geo::WireReadout const>()->Get();
506 
507  // Get the logical volume store and assign material properties
509  auto const detProp =
511  mpl.GetPropertiesFromServices(detProp);
512  mpl.UpdateGeometry(G4LogicalVolumeStore::GetInstance());
513 
514  // Tell the detector about the parallel LAr voxel geometry.
515  std::vector<G4VUserParallelWorld*> pworlds;
516 
517  // Intialize G4 physics and primary generator action
518  fG4Help->InitPhysics();
519 
520  // create the ionization and scintillation calculator; this is a singleton (!) so it
521  // does not make sense to create it in LArVoxelReadoutGeometry
523 
524  // make a parallel world for each TPC in the detector
525  // User-action class for accumulating LAr voxels.
527 
528  LArVoxelReadoutGeometry::Setup_t readoutGeomSetupData{
529  geom.get(), &wireReadoutGeom, lgp.get(), {&fEngine, fOffPlaneMargin}};
530 
532  new LArVoxelReadoutGeometry("LArVoxelReadoutGeometry", readoutGeomSetupData);
533  pworlds.push_back(fVoxelReadoutGeometry);
534  pworlds.push_back(new OpDetReadoutGeometry(
535  geom->Cryostat().OpDetGeoName(), "OpDetReadoutGeometry", fUseLitePhotons));
536  pworlds.push_back(new AuxDetReadoutGeometry(
537  art::ServiceHandle<geo::AuxDetGeometry const>()->GetProviderPtr(), "AuxDetReadoutGeometry"));
538 
539  fG4Help->SetParallelWorlds(pworlds);
540 
541  // Intialize G4 physics and primary generator action
542  fG4Help->InitPhysics();
543 
544  // Use the UserActionManager to handle all the Geant4 user hooks.
546 
547  // User-action class for accumulating particles and trajectories produced in the
548  // detector.
550  lgp->StoreTrajectories(),
551  lgp->KeepEMShowerDaughters(),
555 
556  // UserActionManager is now configured so continue G4 initialization
557  fG4Help->SetUserAction();
558 
559  // With an enormous detector with lots of rock ala LAr34 (nee LAr20) we need to be
560  // smarter about stacking.
561  if (fSmartStacking > 0) {
562  G4UserStackingAction* stacking_action = new LArStackingAction(fSmartStacking);
563  fG4Help->GetRunManager()->SetUserAction(stacking_action);
564  }
565  }
566 
568  {
569  // prepare the filter object (null if no filtering)
570  std::set<std::string> volnameset(fKeepParticlesInVolumes.begin(),
573  }
574 
575  std::unique_ptr<util::PositionInVolumeFilter> LArG4::CreateParticleVolumeFilter(
576  std::set<std::string> const& vol_names) const
577  {
578  // if we don't have favourite volumes, don't even bother creating a filter
579  if (empty(vol_names)) return {};
580 
581  auto const& geom = *art::ServiceHandle<geo::Geometry const>();
582 
583  std::vector<std::vector<TGeoNode const*>> node_paths = geom.FindAllVolumePaths(vol_names);
584 
585  // collection of interesting volumes
587  GeoVolumePairs.reserve(node_paths.size()); // because we are obsessed
588 
589  //for each interesting volume, follow the node path and collect
590  //total rotations and translations
591  for (size_t iVolume = 0; iVolume < node_paths.size(); ++iVolume) {
592  std::vector<TGeoNode const*> path = node_paths[iVolume];
593 
594  auto pTransl = new TGeoTranslation(0., 0., 0.);
595  auto pRot = new TGeoRotation();
596  for (TGeoNode const* node : path) {
597  TGeoTranslation thistranslate(*node->GetMatrix());
598  TGeoRotation thisrotate(*node->GetMatrix());
599  pTransl->Add(&thistranslate);
600  *pRot = *pRot * thisrotate;
601  }
602 
603  // for some reason, pRot and pTransl don't have tr and rot bits set correctly make
604  // new translations and rotations so bits are set correctly
605  auto pTransl2 = new TGeoTranslation(
606  pTransl->GetTranslation()[0], pTransl->GetTranslation()[1], pTransl->GetTranslation()[2]);
607  double phi = 0., theta = 0., psi = 0.;
608  pRot->GetAngles(phi, theta, psi);
609  auto pRot2 = new TGeoRotation();
610  pRot2->SetAngles(phi, theta, psi);
611 
612  auto pTransf = new TGeoCombiTrans(*pTransl2, *pRot2);
613  GeoVolumePairs.emplace_back(node_paths[iVolume].back()->GetVolume(), pTransf);
614  }
615 
616  return std::make_unique<util::PositionInVolumeFilter>(std::move(GeoVolumePairs));
617  } // CreateParticleVolumeFilter()
618 
620  {
621  MF_LOG_DEBUG("LArG4") << "produce()";
622  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
623  auto const detProp =
625  LArVoxelReadoutGeometry::Sentry const set_for_event{fVoxelReadoutGeometry, clockData, detProp};
626 
627  // loop over the lists and put the particles and voxels into the event as collections
628  auto scCol = std::make_unique<std::vector<sim::SimChannel>>();
629  auto adCol = std::make_unique<std::vector<sim::AuxDetSimChannel>>();
630  auto tpassn = fMakeMCParticles ?
631  std::make_unique<
633  nullptr;
634  auto partCol = fMakeMCParticles ? std::make_unique<std::vector<simb::MCParticle>>() : nullptr;
635  auto droppedPartCol =
636  fStoreDroppedMCParticles ? std::make_unique<std::vector<sim::MCParticleLite>>() : nullptr;
637  auto PhotonCol = std::make_unique<std::vector<sim::SimPhotons>>();
638  auto PhotonColRefl = std::make_unique<std::vector<sim::SimPhotons>>();
639  auto LitePhotonCol = std::make_unique<std::vector<sim::SimPhotonsLite>>();
640  auto LitePhotonColRefl = std::make_unique<std::vector<sim::SimPhotonsLite>>();
641  auto cOpDetBacktrackerRecordCol = std::make_unique<std::vector<sim::OpDetBacktrackerRecord>>();
642  auto cOpDetBacktrackerRecordColRefl =
643  std::make_unique<std::vector<sim::OpDetBacktrackerRecord>>();
644 
645  std::optional<art::PtrMaker<simb::MCParticle>> makeMCPartPtr;
646  if (fMakeMCParticles) makeMCPartPtr.emplace(evt);
647 
648  // for energy deposits
649  auto edepCol_TPCActive = std::make_unique<std::vector<sim::SimEnergyDeposit>>();
650  auto edepCol_Other = std::make_unique<std::vector<sim::SimEnergyDeposit>>();
651 
652  // Fetch the lists of LAr voxels and particles.
655  auto const* auxDetGeom = art::ServiceHandle<geo::AuxDetGeometry const>()->GetProviderPtr();
656 
657  // Clear the detected photon table
659  if (lgp->FillSimEnergyDeposits()) OpDetPhotonTable::Instance()->ClearEnergyDeposits();
660 
661  // reset the track ID offset as we have a new collection of interactions
663 
664  // look to see if there is any MCTruth information for this event
665  std::vector<art::Handle<std::vector<simb::MCTruth>>> mclists;
666  if (empty(fInputLabels))
667  mclists = evt.getMany<std::vector<simb::MCTruth>>();
668  else {
669  mclists.resize(fInputLabels.size());
670  for (size_t i = 0; i < fInputLabels.size(); i++)
671  evt.getByLabel(fInputLabels[i], mclists[i]);
672  }
673 
674  unsigned int nGeneratedParticles = 0;
675 
676  // Need to process Geant4 simulation for each interaction separately.
677  for (size_t mcl = 0; mcl < mclists.size(); ++mcl) {
678 
679  art::Handle<std::vector<simb::MCTruth>> mclistHandle = mclists[mcl];
680 
681  for (size_t m = 0; m < mclistHandle->size(); ++m) {
682  art::Ptr<simb::MCTruth> mct(mclistHandle, m);
683 
684  MF_LOG_DEBUG("LArG4") << *(mct.get());
685 
686  // The following tells Geant4 to track the particles in this interaction.
687  fG4Help->G4Run(mct);
688 
689  if (!partCol) continue;
690  assert(tpassn);
691 
692  // receive the particle list
694 
695  for (auto const& partPair : particleList) {
696  simb::MCParticle& p = *(partPair.second);
697  ++nGeneratedParticles;
698 
699  // if the particle has been marked as dropped, we don't save it (as of LArSoft
700  // ~v5.6 this does not ever happen because ParticleListAction has already taken
701  // care of deleting them)
702  if (ParticleListAction::isDropped(&p)) continue;
703 
704  sim::GeneratedParticleInfo const truthInfo{
706  if (!truthInfo.hasGeneratedParticleIndex() && (p.Mother() == 0)) {
707  // this means it's primary but with no information; logic error!!
709  error << "Failed to match primary particle:\n";
710  sim::dump::DumpMCParticle(error, p, " ");
711  error << "\nwith particles from the truth record '"
712  << mclistHandle.provenance()->inputTag() << "':\n";
713  sim::dump::DumpMCTruth(error, *mct, 2U, " "); // 2 points per line
714  error << "\n";
715  throw error;
716  }
717 
719 
720  partCol->push_back(std::move(p));
721 
722  tpassn->addSingle(mct, (*makeMCPartPtr)(partCol->size() - 1), truthInfo);
723 
724  } // for(particleList)
725 
726  if (fStoreDroppedMCParticles && droppedPartCol) {
727  // Request a list of dropped particles. Store them in MCParticleLite format
728  sim::ParticleList droppedParticleList = fparticleListAction->YieldDroppedList();
729  droppedPartCol->reserve(droppedParticleList.size());
730 
731  for (auto const& partPair : droppedParticleList) {
732  simb::MCParticle& p = *(partPair.second);
733  if (ParticleListAction::isDropped(&p)) continue;
734  if (p.StatusCode() != 1) continue;
735 
736  sim::MCParticleLite mini_mcp(p);
737  mini_mcp.Origin(mct->Origin());
738 
739  droppedPartCol->push_back(std::move(mini_mcp));
740  } // for(droppedParticleList)
741  }
742 
743  // Has the user request a detailed dump of the output objects?
744  if (fdumpParticleList) {
745  mf::LogInfo("LArG4") << "Dump sim::ParticleList; size()=" << particleList.size() << "\n"
746  << particleList;
747  }
748  }
749 
750  } // end loop over interactions
751 
752  // get the electrons from the LArVoxelReadout sensitive detector. Get the
753  // sensitive-detector manager.
754  G4SDManager* sdManager = G4SDManager::GetSDMpointer();
755 
756  // Find the sensitive detector with the name "LArVoxelSD".
757  auto theOpDetDet = dynamic_cast<OpDetSensitiveDetector*>(
758  sdManager->FindSensitiveDetector("OpDetSensitiveDetector"));
759 
760  // Store the contents of the detected photon table
761  if (theOpDetDet) {
762 
763  if (!lgp->NoPhotonPropagation()) {
764 
765  for (int Reflected = 0; Reflected <= 1; Reflected++) {
766  if (Reflected && !fStoreReflected) continue;
767 
768  if (!fUseLitePhotons) {
769  MF_LOG_DEBUG("Optical") << "Storing OpDet Hit Collection in Event";
770  std::vector<sim::SimPhotons>& ThePhotons =
772  if (Reflected)
773  PhotonColRefl->reserve(ThePhotons.size());
774  else
775  PhotonCol->reserve(ThePhotons.size());
776  for (auto& it : ThePhotons) {
777  if (Reflected)
778  PhotonColRefl->push_back(std::move(it));
779  else
780  PhotonCol->push_back(std::move(it));
781  }
782  }
783  else {
784  MF_LOG_DEBUG("Optical") << "Storing OpDet Hit Collection in Event";
785 
786  std::map<int, std::map<int, int>> ThePhotons =
788 
789  if (size(ThePhotons) > 0) {
790  LitePhotonCol->reserve(ThePhotons.size());
791  for (auto const& [opChannel, detectedPhotons] : ThePhotons) {
793  ph.OpChannel = opChannel;
794  ph.DetectedPhotons = detectedPhotons;
795  if (Reflected)
796  LitePhotonColRefl->push_back(std::move(ph));
797  else
798  LitePhotonCol->push_back(std::move(ph));
799  }
800  }
801  }
802  if (Reflected)
803  *cOpDetBacktrackerRecordColRefl =
805  else
806  *cOpDetBacktrackerRecordCol =
808  }
809  } //end if no photon propagation
810 
811  if (lgp->FillSimEnergyDeposits()) {
812  // we steal the only existing copy of the energy deposit map. Oink!
814  for (auto& [volumeName, edepCol] : edepMap) {
815  // note: constant reference to a (smart) pointer to non-const data
816  auto const& destColl =
817  boost::contains(volumeName, "TPCActive") ? edepCol_TPCActive : edepCol_Other;
818  append(*destColl, std::move(edepCol));
819  } // for
820  }
821  } //end if theOpDetDet
822 
823  if (!lgp->NoElectronPropagation()) {
824 
825  // only put the sim::SimChannels into the event once, not once for every MCTruth in
826  // the event
827 
828  std::set<LArVoxelReadout*> ReadoutList; // to be cleared later on
829 
830  for (unsigned int c = 0; c < geom->Ncryostats(); ++c) {
831 
832  // map to keep track of which channels we already have SimChannels for in scCol
833  // remake this map on each cryostat as channels ought not to be shared between
834  // cryostats, just between TPC's
835 
836  std::map<unsigned int, unsigned int> channelToscCol;
837 
838  unsigned int ntpcs = geom->Cryostat(geo::CryostatID(c)).NTPC();
839  for (unsigned int t = 0; t < ntpcs; ++t) {
840  std::string name("LArVoxelSD");
841  std::ostringstream sstr;
842  sstr << name << "_Cryostat" << c << "_TPC" << t;
843 
844  // try first to find the sensitive detector specific for this TPC; do not bother
845  // writing on screen if there is none (yet)
846  G4VSensitiveDetector* sd = sdManager->FindSensitiveDetector(sstr.str(), false);
847  // if there is none, catch the general one (called just "LArVoxelSD")
848  if (!sd) sd = sdManager->FindSensitiveDetector(name, false);
849  // If this didn't work, then a sensitive detector with the name "LArVoxelSD"
850  // does not exist.
851  if (!sd) {
852  throw cet::exception("LArG4")
853  << "Sensitive detector for cryostat " << c << " TPC " << t << " not found (neither '"
854  << sstr.str() << "' nor '" << name << "' exist)\n";
855  }
856 
857  // Convert the G4VSensitiveDetector* to a LArVoxelReadout*.
858  auto larVoxelReadout = dynamic_cast<LArVoxelReadout*>(sd);
859 
860  // If this didn't work, there is a "LArVoxelSD" detector, but it's not a
861  // LArVoxelReadout object.
862  if (!larVoxelReadout) {
863  throw cet::exception("LArG4")
864  << "Sensitive detector '" << sd->GetName() << "' is not a LArVoxelReadout object\n";
865  }
866 
867  LArVoxelReadout::ChannelMap_t& channels = larVoxelReadout->GetSimChannelMap(c, t);
868  if (!empty(channels)) {
869  MF_LOG_DEBUG("LArG4") << "now put " << channels.size() << " SimChannels from C=" << c
870  << " T=" << t << " into the event";
871  }
872 
873  for (auto ch_pair : channels) {
874  sim::SimChannel& sc = ch_pair.second;
875 
876  // push sc onto scCol but only if we haven't already put something in scCol
877  // for this channel. if we have, then merge the ionization deposits. Skip
878  // the check if we only have one TPC
879 
880  if (ntpcs > 1) {
881  unsigned int ichan = sc.Channel();
882  auto itertest = channelToscCol.find(ichan);
883  if (itertest == channelToscCol.end()) {
884  channelToscCol[ichan] = scCol->size();
885  scCol->emplace_back(std::move(sc));
886  }
887  else {
888  unsigned int idtest = itertest->second;
889  auto const& tdcideMap = sc.TDCIDEMap();
890  for (auto const& tdcide : tdcideMap) {
891  for (auto const& ide : tdcide.second) {
892  double xyz[3] = {ide.x, ide.y, ide.z};
893  scCol->at(idtest).AddIonizationElectrons(ide.trackID,
894  tdcide.first,
895  ide.numElectrons,
896  xyz,
897  ide.energy,
898  ide.origTrackID);
899  } // end loop to add ionization electrons to scCol->at(idtest)
900  } // end loop over tdc to vector<sim::IDE> map
901  } // end if check to see if we've put SimChannels in for ichan yet or not
902  }
903  else {
904  scCol->emplace_back(std::move(sc));
905  } // end of check if we only have one TPC (skips check for multiple simchannels if we have just one TPC)
906  } // end loop over simchannels for this TPC
907 
908  // mark it for clearing
909  ReadoutList.insert(const_cast<LArVoxelReadout*>(larVoxelReadout));
910 
911  } // end loop over tpcs
912  } // end loop over cryostats
913 
914  for (LArVoxelReadout* larVoxelReadout : ReadoutList) {
915  larVoxelReadout->ClearSimChannels();
916  }
917  } //endif electron prop
918 
919  // only put the sim::AuxDetSimChannels into the event once, not once for every MCTruth
920  // in the event
921 
922  adCol->reserve(auxDetGeom->NAuxDets());
923  for (unsigned int a = 0; a < auxDetGeom->NAuxDets(); ++a) {
924 
925  // there should always be at least one senstive volume because we make one for the
926  // full aux det if none are specified in the gdml file - see AuxDetGeo.cxx
927  auto const& ad = auxDetGeom->AuxDet(a);
928  for (size_t sv = 0; sv < ad.NSensitiveVolume(); ++sv) {
929 
930  // N.B. this name convention is used when creating the AuxDetReadout SD in
931  // AuxDetReadoutGeometry
932  std::stringstream name;
933  name << "AuxDetSD_AuxDet" << a << "_" << sv;
934  G4VSensitiveDetector* sd = sdManager->FindSensitiveDetector(name.str().c_str());
935  if (!sd) {
936  throw cet::exception("LArG4")
937  << "Sensitive detector '" << name.str() << "' does not exist\n";
938  }
939 
940  // Convert the G4VSensitiveDetector* to a AuxDetReadout*.
941  larg4::AuxDetReadout* auxDetReadout = dynamic_cast<larg4::AuxDetReadout*>(sd);
942 
943  MF_LOG_DEBUG("LArG4") << "now put the AuxDetSimTracks in the event";
944 
945  const sim::AuxDetSimChannel adsc = auxDetReadout->GetAuxDetSimChannel();
946  adCol->push_back(adsc);
947  auxDetReadout->clear();
948  }
949  } // Loop over AuxDets
950 
951  if (partCol) {
952  mf::LogInfo("LArG4") << "Geant4 simulated " << nGeneratedParticles
953  << " MC particles, we keep " << partCol->size() << " .";
954  }
955 
956  if (fdumpSimChannels) {
957  mf::LogVerbatim("DumpSimChannels")
958  << "Event " << evt.id() << ": " << scCol->size() << " channels with signal";
959  unsigned int nChannels = 0;
960  for (const sim::SimChannel& sc : *scCol) {
961  mf::LogVerbatim out("DumpSimChannels");
962  out << " #" << nChannels << ": ";
963  // dump indenting with " ", but not on the first line
964  sc.Dump(out, " ");
965  ++nChannels;
966  } // for
967  } // if dump SimChannels
968 
969  if (!lgp->NoElectronPropagation()) evt.put(std::move(scCol));
970 
971  evt.put(std::move(adCol));
972  if (partCol) evt.put(std::move(partCol));
973  if (droppedPartCol) {
974  std::cout << "LArG4 dropped particles length = " << droppedPartCol->size() << std::endl;
975  evt.put(std::move(droppedPartCol));
976  }
977  if (tpassn) evt.put(std::move(tpassn));
978  if (!lgp->NoPhotonPropagation()) {
979  if (!fUseLitePhotons) {
980  evt.put(std::move(PhotonCol));
981  if (fStoreReflected) evt.put(std::move(PhotonColRefl), "Reflected");
982  }
983  else {
984  evt.put(std::move(LitePhotonCol));
985  evt.put(std::move(cOpDetBacktrackerRecordCol));
986  if (fStoreReflected) {
987  evt.put(std::move(LitePhotonColRefl), "Reflected");
988  evt.put(std::move(cOpDetBacktrackerRecordColRefl), "Reflected");
989  }
990  }
991  }
992 
993  if (lgp->FillSimEnergyDeposits()) {
994  evt.put(std::move(edepCol_TPCActive), "TPCActive");
995  evt.put(std::move(edepCol_Other), "Other");
996  }
997  return;
998  } // LArG4::produce()
999 
1000 } // namespace LArG4
1001 
std::vector< std::string > fInputLabels
std::unique_ptr< g4b::G4Helper > fG4Help
G4 interface object.
base_engine_t & createEngine(seed_t seed)
Store parameters for running LArG4.
Collection of all it takes to set up this object.
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< sim::OpDetBacktrackerRecord > YieldReflectedOpDetBacktrackerRecords()
bool fStoreDroppedMCParticles
Whether to keep a sim::MCParticleLite list of dropped particles.
bool KeepEMShowerDaughters() const
std::string fG4MacroPath
Energy deposited on a readout channel by simulated tracks.
Definition: SimChannel.h:136
T * get() const
Definition: ServiceHandle.h:69
Stores material properties and sends them to GEANT4 geometry.
void beginJob() override
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
int Mother() const
Definition: MCParticle.h:214
void GetPropertiesFromServices(detinfo::DetectorPropertiesData const &detProp)
Imports properties from LArSoft services.
std::vector< VolumeInfo_t > AllVolumeInfo_t
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.cc:6
simb::Origin_t Origin() const
Definition: MCTruth.h:74
void produces(std::string const &instanceName={}, Persistable const persistable=Persistable::Yes)
bool fMakeMCParticles
Whether to keep a sim::MCParticle list.
Geant4 interface.
Class def header for MCParticleLite data container.
int StatusCode() const
Definition: MCParticle.h:212
Contains data associated to particles from detector simulation.
cout<< "Opened file "<< fin<< " ixs= "<< ixs<< endl;if(ixs==0) hhh=(TH1F *) fff-> Get("h1")
Definition: AddMC.C:8
Runs Geant4 simulation and propagation of electrons and photons to readout.
std::unordered_map< std::string, std::vector< sim::SimEnergyDeposit > > YieldSimEnergyDeposits()
Yields the map of energy deposits by volume name, and resets the internal one.
bool NoPhotonPropagation() const
Use Geant4 to run the LArSoft detector simulation.
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
Definition: GeometryCore.h:303
std::vector< std::string > fKeepParticlesInVolumes
Only write particles that have trajectories through these volumes.
Definition: Run.h:37
bool StoreTrajectories() const
int TrackId() const
Definition: MCParticle.h:211
std::map< int, int > DetectedPhotons
Number of photons detected at each given time: time tick -> photons.
Definition: SimPhotons.h:109
Define the "parallel" geometry that&#39;s seen by the LAr Voxels.
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Event.h:77
bool fSparsifyTrajectories
Sparsify MCParticle Trajectories.
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
int fSmartStacking
Whether to instantiate and use class to.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
Collection of particles crossing one auxiliary detector cell.
bool fdumpSimChannels
Whether each event&#39;s sim::Channel will be displayed.
std::vector< sim::SimPhotons > & GetPhotons(bool Reflected=false)
larg4::ParticleListAction * fparticleListAction
Geant4 user action to particle information.
Simulation objects for optical detectors.
std::string OpDetGeoName() const
Get name of opdet geometry element.
Definition: CryostatGeo.h:333
sim::ParticleList && YieldList()
std::map< unsigned int, sim::SimChannel > ChannelMap_t
Type of map channel -> sim::SimChannel.
object containing MC truth information necessary for making RawDigits and doing back tracking ...
Use Geant4&#39;s user "hooks" to maintain a list of particles generated by Geant4.
bool NoElectronPropagation() const
Utility functions to print MC truth information.
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:65
static bool isDropped(simb::MCParticle const *p)
returns whether the specified particle has been marked as dropped
static void AddAndAdoptAction(UserAction *a)
Provenance const * provenance() const
Definition: Handle.h:217
LArG4(fhicl::ParameterSet const &pset)
virtual void clear()
art framework interface to geometry description for auxiliary detectors
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:338
bool FillSimEnergyDeposits() const
int OpChannel
Optical detector channel associated to this data.
Definition: SimPhotons.h:106
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:171
sim::ParticleList && YieldDroppedList()
Yields the (dropped) ParticleList accumulated during the current event.
void ParticleFilter(std::unique_ptr< util::PositionInVolumeFilter > &&filter)
Grabs a particle filter.
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.
An art service to assist in the distribution of guaranteed unique seeds to all engines within an art ...
CryostatGeo const & Cryostat(CryostatID const &cryoid=details::cryostat_zero) const
Returns the specified cryostat.
unsigned int NOpDets() const
Number of OpDets in the whole detector.
void UpdateGeometry(G4LogicalVolumeStore *lvs)
Updates the material properties with the collected values.
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
double ParticleKineticEnergyCut() const
raw::ChannelID_t Channel() const
Returns the readout channel this object describes.
Definition: SimChannel.h:323
static OpDetPhotonTable * Instance(bool LitePhotons=false)
A Geant4 sensitive detector that accumulates voxel information.
Define the "parallel" geometry that&#39;s seen by the AuxDet.
Compact representation of photons on a channel.
Definition: SimPhotons.h:98
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
std::unique_ptr< util::PositionInVolumeFilter > CreateParticleVolumeFilter(std::set< std::string > const &vol_names) const
Pointer used for correctly updating the clock data state.
static UserActionManager * Instance()
bool fUseLitePhotons
std::vector< sim::OpDetBacktrackerRecord > YieldOpDetBacktrackerRecords()
Singleton to access a unified treatment of ionization and scintillation in LAr.
Float_t sc
Definition: plot.C:23
Contains information about a generated particle.
contains information for a single step in the detector simulation
const simb::Origin_t & Origin() const
#define MF_LOG_DEBUG(id)
void produce(art::Event &evt) override
static IonizationAndScintillation * CreateInstance(detinfo::DetectorPropertiesData const &detProp, CLHEP::HepRandomEngine &engine)
object containing MC truth information necessary for making RawDigits and doing back tracking ...
std::string fG4PhysListName
predefined physics list to use if not making a custom one
std::string const & GDMLFile() const
Returns the full directory path to the GDML file source.
Definition: GeometryCore.h:130
Definition: MVAAlg.h:12
bool fCheckOverlaps
Whether to use the G4 overlap checker.
Transports energy depositions from GEANT4 to TPC channels.
TDCIDEs_t const & TDCIDEMap() const
Returns all the deposited energy information as stored.
Definition: SimChannel.h:319
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
bool fStoreReflected
Defines classes to filter particles based on their trajectory.
AllPhysicsLists fAllPhysicsLists
TCEvent evt
Definition: DataStructs.cxx:8
std::map< int, std::map< int, int > > GetLitePhotons(bool Reflected=false)
detinfo::DetectorPropertiesData fDetProp
Must outlive fAllPhysicsLists!
Particle list in DetSim contains Monte Carlo particle information.
Float_t e
Definition: plot.C:35
T const * get() const
Definition: Ptr.h:138
CLHEP::HepRandomEngine & fEngine
void SparsifyTrajectory(double margin=0.1, bool keep_second_to_last=false)
Definition: MCParticle.h:266
double fOffPlaneMargin
void beginRun(art::Run &run) override
size_type size() const
Definition: ParticleList.h:313
void ClearTable(size_t nch=0)
A Geant4 sensitive detector that accumulates information.
EventID id() const
Definition: Event.cc:23
sim::AuxDetSimChannel const GetAuxDetSimChannel() const
Definition: AuxDetReadout.h:70
art framework interface to geometry description
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:226
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:109
bool UseLitePhotons() const
LArVoxelReadoutGeometry * fVoxelReadoutGeometry
The data type to uniquely identify a cryostat.
Definition: geo_types.h:187
std::vector< Handle< PROD > > getMany(SelectorBase const &selector=MatchAllSelector{}) const