17 #include "Geant4/G4Step.hh" 18 #include "Geant4/G4StepPoint.hh" 19 #include "Geant4/G4ThreeVector.hh" 20 #include "Geant4/G4VPhysicalVolume.hh" 23 #include "cetlib_except/exception.h" 41 #include "CLHEP/Random/RandGauss.h" 55 unsigned int cryostat, tpc;
56 if (std::sscanf(name.c_str(),
"%*19s%1u%*4s%u", &cryostat, &tpc) == 2)
96 MF_LOG_DEBUG(
"LArVoxelReadout") << GetName() <<
" autodetects TPC";
105 "You must use set the clock data pointer at the beginning " 106 "of each module's event-level call. This might be done through the " 107 "LArVoxelReadoutGeometry::Sentry class.");
109 "You must use set the detector-properties data pointer at the beginning " 110 "of each module's event-level call. This might be done through the " 111 "LArVoxelReadoutGeometry::Sentry class.");
114 for (
int i = 0; i < 3; ++i)
156 for (
auto& channelsMap : cryoData)
187 std::vector<sim::SimChannel>
194 std::vector<sim::SimChannel>
197 std::vector<sim::SimChannel>
channels;
199 channels.reserve(chmap.size());
200 for (
const auto& chpair : chmap)
201 channels.push_back(chpair.second);
224 if (step->GetTotalEnergyDeposit() > 0) {
232 G4ThreeVector midPoint =
233 0.5 * (step->GetPreStepPoint()->GetPosition() + step->GetPostStepPoint()->GetPosition());
234 double g4time = step->GetPreStepPoint()->GetGlobalTime();
244 unsigned short int cryostat = 0, tpc = 0;
251 const G4VTouchable* pTouchable = step->GetPreStepPoint()->GetTouchable();
253 throw cet::exception(
"LArG4") <<
"Untouchable step in LArVoxelReadout::ProcessHits()";
261 while (depth < pTouchable->GetHistoryDepth()) {
264 if (!pPVinTPC)
continue;
265 cryostat = pPVinTPC->
ID.Cryostat;
266 tpc = pPVinTPC->
ID.TPC;
270 if (depth < pTouchable->GetHistoryDepth()) {
273 throw cet::exception(
"LArG4") <<
"No TPC ID found in LArVoxelReadout::ProcessHits()";
275 MF_LOG_DEBUG(
"LArVoxelReadoutHit") <<
" hit in C=" << cryostat <<
" T=" << tpc;
319 if ((offPlane.X() == 0.0) && (offPlane.Y() == 0.0))
return pos;
336 G4ThreeVector stepMidPoint,
337 const double simTime,
339 unsigned short int cryostat,
340 unsigned short int tpc)
342 auto const tpcClock = clockData.
TPCClock();
347 CLHEP::RandGauss PropRand(*
fPropGen);
356 static double RecipDriftVel[3] = {
361 double electrons = 0.;
364 add(
double more_energy,
double more_electrons)
366 energy += more_energy;
367 electrons += more_electrons;
372 std::map<raw::ChannelID_t, std::map<unsigned int, Deposit_t>> DepositsToStore;
374 double xyz1[3] = {0.};
376 double const xyz[3] = {
395 if (XDrift < 0.)
return;
399 auto const* SCE = lar::providerFrom<spacecharge::SpaceChargeService>();
400 if (SCE->EnableSimSpatialSCE() ==
true) {
401 posOffsets = SCE->GetPosOffsets({xyz[0], xyz[1], xyz[2]});
406 posOffsets.SetX(-posOffsets.X());
410 XDrift += posOffsets.X();
416 if (XDrift < 0.) XDrift = 0.;
418 TDrift = XDrift * RecipDriftVel[0];
420 TDrift = ((XDrift - tpcg.
PlanePitch(0, 1)) * RecipDriftVel[0] +
424 const double lifetimecorrection = TMath::Exp(TDrift / LifetimeCorr_const);
425 const int nIonizedElectrons =
431 if (nIonizedElectrons <= 0) {
433 <<
"No electrons drifted to readout, " << energy <<
" MeV lost.";
437 const double nElectrons = nIonizedElectrons * lifetimecorrection;
440 double SqrtT = std::sqrt(TDrift);
441 double LDiffSig = SqrtT * LDiff_const;
442 double TDiffSig = SqrtT * TDiff_const;
445 int nClus = (
int)std::ceil(nElectrons / electronclsize);
448 if (electronclsize < 1.0) { electronclsize = 1.0; }
449 nClus = (
int)std::ceil(nElectrons / electronclsize);
453 std::vector<double> XDiff(nClus);
454 std::vector<double> YDiff(nClus);
455 std::vector<double> ZDiff(nClus);
456 std::vector<double> nElDiff(nClus, electronclsize);
457 std::vector<double> nEnDiff(nClus);
460 nElDiff.back() = nElectrons - (nClus - 1) * electronclsize;
462 for (
size_t xx = 0; xx < nElDiff.size(); ++xx) {
464 nEnDiff[xx] = energy / nElectrons * nElDiff[xx];
469 double const avegageYtransversePos = (stepMidPoint.y() /
CLHEP::cm) + posOffsets.Y();
470 double const avegageZtransversePos = (stepMidPoint.z() /
CLHEP::cm) + posOffsets.Z();
474 PropRand.fireArray(nClus, &XDiff[0], 0., LDiffSig);
476 XDiff.assign(nClus, 0.0);
478 if (TDiffSig > 0.0) {
480 PropRand.fireArray(nClus, &YDiff[0], avegageYtransversePos, TDiffSig);
481 PropRand.fireArray(nClus, &ZDiff[0], avegageZtransversePos, TDiffSig);
484 YDiff.assign(nClus, avegageYtransversePos);
485 ZDiff.assign(nClus, avegageZtransversePos);
499 for (
int k = 0;
k < nClus; ++
k) {
501 double TDiff = TDrift + XDiff[
k] * RecipDriftVel[0];
504 for (
size_t ip = 0; ip <
p; ++ip) {
506 tpcg.
PlanePitch(ip, ip + 1) * RecipDriftVel[tpcg.
Nplanes() == 3 ? ip + 1 : ip + 2];
524 xyz1[0] = landingPos.X();
525 xyz1[1] = landingPos.Y();
526 xyz1[2] = landingPos.Z();
534 unsigned int tdc = tpcClock.Ticks(clockData.
G4ToElecTime(TDiff + simTime));
537 DepositsToStore[
channel][tdc].add(nEnDiff[
k], nElDiff[k]);
541 <<
"unable to drift electrons from point (" << xyz[0] <<
"," << xyz[1] <<
"," 542 << xyz[2] <<
") with exception " <<
e;
551 for (
auto const& deposit_per_channel : DepositsToStore) {
556 auto iChannelData = ChannelDataMap.find(channel);
562 sim::SimChannel& channelData = (iChannelData == ChannelDataMap.end()) ?
564 iChannelData->second;
567 for (
auto const& deposit_per_tdc : deposit_per_channel.second) {
569 deposit_per_tdc.first,
570 deposit_per_tdc.second.electrons,
572 deposit_per_tdc.second.energy);
579 MF_LOG_DEBUG(
"LArVoxelReadout") <<
"step cannot be found in a TPC\n" <<
e;
static constexpr double cm
CLHEP::HepRandomEngine * propGen
Random engine for charge propagation.
double NumberIonizationElectrons() const
unsigned int fTPC
which TPC this LArVoxelReadout corresponds to
double fTransverseDiffusion
double PlanePitch(unsigned int p1=0, unsigned int p2=1) const
Collection of what it takes to set a LArVoxelReadout up.
Energy deposited on a readout channel by simulated tracks.
A G4PVPlacement with an additional identificator.
art::ServiceHandle< sim::LArG4Parameters const > fLgpHandle
Handle to the LArG4 parameters service.
bool out_of_bounds(geo::Vector_t const &offset)
unsigned int Nplanes() const
Number of planes in this tpc.
art::ServiceHandle< geo::Geometry const > fGeoHandle
Handle to the Geometry service.
Geometry information for a single TPC.
Coord add(Coord c1, Coord c2)
double Temperature() const
In kelvin.
const ChannelMap_t & GetSimChannelMap() const
Returns the accumulated channel -> SimChannel map for the single TPC.
LArVoxelReadout(std::string const &name)
Constructor. Can detect which TPC to cover by the name.
Point ComposePoint(WireDecomposedVector_t const &decomp) const
Returns the 3D point from composition of projection and distance.
void Setup(Setup_t const &setupData)
Reads all the configuration elements from setupData
WidthDepthProjection_t PointWidthDepthProjection(geo::Point_t const &point) const
Returns the projection of the specified point on the plane.
int fMinNumberOfElCluster
void SetRandomEngines(CLHEP::HepRandomEngine *pPropGen)
Sets the random generators to be used.
double ElectronLifetime() const
detinfo::DetectorClocksData const * fClockData
ROOT::Math::DisplacementVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Vector_t
Type for representation of momenta in 3D space.
Drift towards negative X values.
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
double fLongitudinalDiffusion
void Reset(const G4Step *step)
double Efield(unsigned int planegap=0) const
kV/cm
bool bSingleTPC
true if this readout is associated with a single TPC
ID_t ID
Physical Volume identificator.
geo::Point_t RecoverOffPlaneDeposit(geo::Point_t const &pos, geo::PlaneGeo const &plane) const
Returns the point on the specified plane closest to position.
double TransverseDiffusion() const
void SetSingleTPC(unsigned int cryostat, unsigned int tpc)
Associates this readout to one specific TPC.
std::map< unsigned int, sim::SimChannel > ChannelMap_t
Type of map channel -> sim::SimChannel.
double fElectronClusterSize
bool NoElectronPropagation() const
std::vector< unsigned short int > fSkipWireSignalInTPCs
void DriftIonizationElectrons(detinfo::DetectorClocksData const &clockData, G4ThreeVector stepMidPoint, const double simTime, int trackID, unsigned short int cryostat, unsigned short int tpc)
const std::vector< unsigned short int > & SkipWireSignalInTPCs() const
Utility function for testing if Space Charge offsets are out of bounds.
double ElectronClusterSize() const
Geometry information for a single wire plane.The plane is represented in the geometry by a solid whic...
static IonizationAndScintillation * Instance()
ElecClock const & TPCClock() const noexcept
Borrow a const TPC clock with time set to Trigger time [us].
std::vector< sim::SimChannel > GetSimChannels() const
Creates a list with the accumulated information for the single TPC.
detinfo::DetectorPropertiesData const * fDetProp
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
double DriftVelocity(double efield=0., double temperature=0.) const
cm/us
bool Has(std::vector< unsigned short int > v, unsigned short int tpc) const
virtual G4bool ProcessHits(G4Step *, G4TouchableHistory *)
Definition of data types for geometry description.
double Plane0Pitch(unsigned int p) const
DriftDirection_t DriftDirection() const
Returns an enumerator value describing the drift direction.
std::vector< std::vector< ChannelMap_t > > fChannelMaps
Maps of cryostat, tpc to channel data.
virtual void EndOfEvent(G4HCofThisEvent *)
double fOffPlaneMargin
Charge deposited within this many [cm] from the plane is lead onto it.
void SetDiscoverTPC()
Sets this readout to discover the TPC of each processed hit.
unsigned int NTPC(unsigned int cstat=0) const
Returns the total number of TPCs in the specified cryostat.
void AddIonizationElectrons(TrackID_t trackID, TDC_t tdc, double numberElectrons, double const *xyz, double energy)
Add ionization electrons and energy to this channel.
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
A Geant4 sensitive detector that accumulates voxel information.
double offPlaneMargin
Margin for charge recovery (see LArVoxelReadout).
WidthDepthProjection_t DeltaFromActivePlane(WidthDepthProjection_t const &proj, double wMargin, double dMargin) const
Returns a projection vector that, added to the argument, gives a projection inside (or at the border ...
double G4ToElecTime(double const g4_time) const
Drift towards positive X values.
Contains all timing reference information for the detector.
int MinNumberOfElCluster() const
double DistanceFromPlane(geo::Point_t const &point) const
Returns the distance of the specified point from the wire plane.
unsigned int fCstat
and in which cryostat (if bSingleTPC is true)
void SetOffPlaneChargeRecoveryMargin(double margin)
Sets the margin for recovery of charge drifted off-plane.
for(std::string line;std::getline(inFile, line);)
static int GetCurrentTrackID()
double EnergyDeposit() const
CLHEP::HepRandomEngine * fPropGen
random engine for charge propagation
virtual void Initialize(G4HCofThisEvent *)
Transports energy depositions from GEANT4 to TPC channels.
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
PlaneGeo const & Plane(geo::View_t view) const
Return the plane in the tpc with View_t view.
double LongitudinalDiffusion() const
unsigned int ChannelID_t
Type representing the ID of a readout channel.
raw::ChannelID_t NearestChannel(geo::Point_t const &worldLoc, geo::PlaneID const &planeid) const
Returns the ID of the channel nearest to the specified position.
const double * PlaneLocation(unsigned int p) const
cet::coded_exception< error, detail::translate > exception
QTextStream & endl(QTextStream &s)
bool DisableWireplanes() const
pure virtual base interface for detector clocks