LArSoft  v10_04_05
Liquid Argon Software toolkit - https://larsoft.org/
LArVoxelReadout.cxx
Go to the documentation of this file.
1 
8 // C/C++ standard library
9 #include <cassert>
10 #include <cmath> // std::ceil()
11 #include <cstdio> // std::sscanf()
12 #include <map>
13 #include <string>
14 #include <utility> // std::move()
15 
16 // GEANT
17 #include "Geant4/G4Step.hh"
18 #include "Geant4/G4StepPoint.hh"
19 #include "Geant4/G4ThreeVector.hh"
20 #include "Geant4/G4VPhysicalVolume.hh"
21 
22 // framework libraries
23 #include "cetlib_except/exception.h"
25 
26 // LArSoft code
30 #include "larcoreobj/SimpleTypesAndConstants/RawTypes.h" // raw::ChannelID_t
42 
43 // CLHEP
44 #include "CLHEP/Random/RandGauss.h"
45 
46 namespace larg4 {
47 
48  //---------------------------------------------------------------------------------------
49  // Constructor. Note that we force the name of this sensitive detector to be the value
50  // expected by LArVoxelListAction.
51  LArVoxelReadout::LArVoxelReadout(std::string const& name,
52  geo::GeometryCore const* geom,
53  geo::WireReadoutGeom const* wireReadoutGeom,
54  sim::LArG4Parameters const* lgp)
55  : G4VSensitiveDetector(name), fGeo{geom}, fWireReadoutGeom{wireReadoutGeom}, fLgp{lgp}
56  {
57  // Initialize values for the electron-cluster calculation.
59 
60  // the standard name contains cryostat and TPC; if we don't find it, we will detect
61  // the TPC at each Geant hit
62  unsigned int cryostat, tpc;
63  if (std::sscanf(name.c_str(), "%*19s%1u%*4s%u", &cryostat, &tpc) == 2)
64  SetSingleTPC(cryostat, tpc);
65  else
67  }
68 
69  //---------------------------------------------------------------------------------------
70  // Constructor. Note that we force the name of this sensitive detector to be the value
71  // expected by LArVoxelListAction.
72  LArVoxelReadout::LArVoxelReadout(std::string const& name,
73  geo::GeometryCore const* geom,
74  geo::WireReadoutGeom const* wireReadoutGeom,
75  sim::LArG4Parameters const* lgp,
76  unsigned int cryostat,
77  unsigned int tpc)
78  : LArVoxelReadout(name, geom, wireReadoutGeom, lgp)
79  {
80  SetSingleTPC(cryostat, tpc);
81  }
82 
83  //---------------------------------------------------------------------------------------
84  void LArVoxelReadout::Setup(Setup_t const& setupData)
85  {
87  SetRandomEngines(setupData.propGen);
88  }
89 
90  //---------------------------------------------------------------------------------------
91  void LArVoxelReadout::SetSingleTPC(unsigned int cryostat, unsigned int tpc)
92  {
93  bSingleTPC = true;
94  fCstat = cryostat;
95  fTPC = tpc;
96  MF_LOG_DEBUG("LArVoxelReadout") << GetName() << "covers C=" << fCstat << " T=" << fTPC;
97  }
98 
100  {
101  bSingleTPC = false;
102  fCstat = 0;
103  fTPC = 0;
104  MF_LOG_DEBUG("LArVoxelReadout") << GetName() << " autodetects TPC";
105  }
106 
107  //---------------------------------------------------------------------------------------
108  // Called at the start of each event.
110  {
111  assert(fClockData != nullptr &&
112  "You must use set the clock data pointer at the beginning "
113  "of each module's event-level call. This might be done through the "
114  "LArVoxelReadoutGeometry::Sentry class.");
115  assert(fDetProp != nullptr &&
116  "You must use set the detector-properties data pointer at the beginning "
117  "of each module's event-level call. This might be done through the "
118  "LArVoxelReadoutGeometry::Sentry class.");
119 
121  for (int i = 0; i < 3; ++i)
122  fDriftVelocity[i] =
124 
131 
132  MF_LOG_DEBUG("LArVoxelReadout")
133  << " e lifetime: " << fElectronLifetime << "\n Temperature: " << fDetProp->Temperature()
134  << "\n Drift velocity: " << fDriftVelocity[0] << " " << fDriftVelocity[1] << " "
135  << fDriftVelocity[2];
136 
138 
139  fNSteps = 0;
140  }
141 
142  //---------------------------------------------------------------------------------------
143  // Called at the end of each event.
145  {
146  MF_LOG_DEBUG("LArVoxelReadout") << "Total number of steps was " << fNSteps << std::endl;
147  }
148 
149  //---------------------------------------------------------------------------------------
151 
152  //--------------------------------------------------------------------------------------
154  {
155  fChannelMaps.resize(fGeo->Ncryostats());
156  size_t cryo = 0;
157  for (auto& cryoData : fChannelMaps) { // each, a vector of maps
158  cryoData.resize(fGeo->NTPC(geo::CryostatID(cryo++)));
159  for (auto& channelsMap : cryoData)
160  channelsMap.clear(); // each, a map
161  } // for cryostats
162  }
163 
165  {
166  if (bSingleTPC) return GetSimChannelMap(fCstat, fTPC);
167  throw cet::exception("LArVoxelReadout") << "TPC not specified";
168  }
169 
171  {
172  if (bSingleTPC) return GetSimChannelMap(fCstat, fTPC);
173  throw cet::exception("LArVoxelReadout") << "TPC not specified";
174  }
175 
177  unsigned short tpc) const
178  {
179  return fChannelMaps.at(cryo).at(tpc);
180  }
181 
183  unsigned short tpc)
184  {
185  return fChannelMaps.at(cryo).at(tpc);
186  }
187 
188  std::vector<sim::SimChannel> LArVoxelReadout::GetSimChannels() const
189  {
190  if (bSingleTPC) return GetSimChannels(fCstat, fTPC);
191  throw cet::exception("LArVoxelReadout") << "TPC not specified";
192  }
193 
194  std::vector<sim::SimChannel> LArVoxelReadout::GetSimChannels(unsigned short cryo,
195  unsigned short tpc) const
196  {
197  std::vector<sim::SimChannel> channels;
198  const ChannelMap_t& chmap = fChannelMaps.at(cryo).at(tpc);
199  channels.reserve(chmap.size());
200  for (const auto& chpair : chmap)
201  channels.push_back(chpair.second);
202  return channels;
203  }
204 
205  //---------------------------------------------------------------------------------------
206  // Called for each step.
207  G4bool LArVoxelReadout::ProcessHits(G4Step* step, G4TouchableHistory* pHistory)
208  {
209  // All work done for the "parallel world" "box of voxels" in LArVoxelReadoutGeometry
210  // makes this a fairly simple routine. First, the usual check for non-zero energy:
211 
212  // Only process the hit if the step is inside the active volume and it has deposited
213  // energy. The hit being inside the active volume is virtually sure to happen because
214  // the LArVoxelReadoutGeometry that this class makes use of only has voxels for inside
215  // the TPC.
216 
217  // The step can be no bigger than the size of the voxel, because of the geometry set
218  // up in LArVoxelGeometry and the transportation set up in PhysicsList. Find the
219  // mid-point of the step.
220 
221  if (step->GetTotalEnergyDeposit() > 0) {
222 
223  // Make sure we have the IonizationAndScintillation singleton reset to this step
225  fNSteps++;
226  if (!fDontDriftThem) {
227 
228  G4ThreeVector midPoint =
229  0.5 * (step->GetPreStepPoint()->GetPosition() + step->GetPostStepPoint()->GetPosition());
230  double g4time = step->GetPreStepPoint()->GetGlobalTime();
231 
232  // Find the Geant4 track ID for the particle responsible for depositing the
233  // energy. If we are only storing primary EM shower particles, and this energy is
234  // from a secondary etc EM shower particle, the ID returned is the primary
235  const int trackID = ParticleListAction::GetCurrentTrackID();
236  // For all particles but shower daughters, origTrackID is the same as trackID.
237  // For shower daughters it contains their original trackID instead of the shower
238  // primary's trackID.
239  const int origTrackID = ParticleListAction::GetCurrentOrigTrackID();
240 
241  // Find out which TPC we are in. If this readout object covers just one, we
242  // already know it. Otherwise, we have to ask Geant where we are.
243  unsigned short int cryostat = 0, tpc = 0;
244  if (bSingleTPC) {
245  cryostat = fCstat;
246  tpc = fTPC;
247  }
248  else {
249  // detect the TPC we are in
250  const G4VTouchable* pTouchable = step->GetPreStepPoint()->GetTouchable();
251  if (!pTouchable) {
252  throw cet::exception("LArG4") << "Untouchable step in LArVoxelReadout::ProcessHits()";
253  }
254 
255  // one of the ancestors of the touched volume is supposed to be actually a
256  // G4PVPlacementInTPC that knows which TPC it covers; currently, it's the 4th in
257  // the ladder:
258  // [0] voxel [1] voxel tower [2] voxel plane [3] the full box;
259  G4int depth = 0;
260  while (depth < pTouchable->GetHistoryDepth()) {
261  const G4PVPlacementInTPC* pPVinTPC =
262  dynamic_cast<const G4PVPlacementInTPC*>(pTouchable->GetVolume(depth++));
263  if (!pPVinTPC) continue;
264  cryostat = pPVinTPC->ID.Cryostat;
265  tpc = pPVinTPC->ID.TPC;
266  if (Has(fSkipWireSignalInTPCs, tpc)) { return true; }
267  break;
268  } // while
269  if (depth < pTouchable->GetHistoryDepth()) {
270  // this is a fundamental error where the step does not happen in any TPC; this
271  // should not happen in the readout geometry!
272  throw cet::exception("LArG4") << "No TPC ID found in LArVoxelReadout::ProcessHits()";
273  } // if
274  MF_LOG_DEBUG("LArVoxelReadoutHit") << " hit in C=" << cryostat << " T=" << tpc;
275  } // if more than one TPC
276 
277  // Note that if there is no particle ID for this energy deposit, the trackID will
278  // be sim::NoParticleId.
279 
281  *fClockData, midPoint, g4time, trackID, cryostat, tpc, origTrackID);
282  } // end we are drifting
283  } // end there is non-zero energy deposition
284 
285  return true;
286  }
287 
288  //----------------------------------------------------------------------------
289  void LArVoxelReadout::SetRandomEngines(CLHEP::HepRandomEngine* pPropGen)
290  {
291  assert(pPropGen); // random engine must be present
292  fPropGen = pPropGen;
293  }
294 
295  //----------------------------------------------------------------------------
297  geo::PlaneGeo const& plane) const
298  {
299  // translate the landing position on the two frame coordinates ("width" and "depth")
300  auto const landingPos = plane.PointWidthDepthProjection(pos);
301 
302  // compute the distance of the landing position on the two frame coordinates ("width"
303  // and "depth"); keep the point within 10 micrometers (0.001 cm) from the border
304  auto const offPlane = plane.DeltaFromActivePlane(landingPos, 0.001);
305 
306  // if both the distances are below the margin, move the point to the border
307 
308  // nothing to recover: landing is inside
309  if ((offPlane.X() == 0.0) && (offPlane.Y() == 0.0)) return pos;
310 
311  // won't recover: too far
312  if ((std::abs(offPlane.X()) > fOffPlaneMargin) || (std::abs(offPlane.Y()) > fOffPlaneMargin))
313  return pos;
314 
315  // we didn't fully decompose because it might be unnecessary; now we need the full
316  // thing
317  auto const distance = plane.DistanceFromPlane(pos);
318 
319  return plane.ComposePoint(distance, landingPos + offPlane);
320  }
321 
322  //----------------------------------------------------------------------------
323  // energy is passed in with units of MeV, dx has units of cm
325  G4ThreeVector stepMidPoint,
326  const double simTime,
327  int trackID,
328  unsigned short int cryostat,
329  unsigned short int tpc,
330  int origTrackID)
331  {
332  auto const tpcClock = clockData.TPCClock();
333 
334  // this must be always true, unless caller has been sloppy
335  assert(fPropGen); // No propagation random generator provided?!
336 
337  CLHEP::RandGauss PropRand(*fPropGen);
338 
339  // This routine gets called frequently, once per every particle traveling through
340  // every voxel. Use whatever tricks we can to increase its execution speed.
341 
342  static double LifetimeCorr_const = -1000. * fElectronLifetime;
343  static double LDiff_const = std::sqrt(2. * fLongitudinalDiffusion);
344  static double TDiff_const = std::sqrt(2. * fTransverseDiffusion);
345  static double RecipDriftVel[3] = {
346  1. / fDriftVelocity[0], 1. / fDriftVelocity[1], 1. / fDriftVelocity[2]};
347 
348  struct Deposit_t {
349  double energy = 0.;
350  double electrons = 0.;
351 
352  void add(double more_energy, double more_electrons)
353  {
354  energy += more_energy;
355  electrons += more_electrons;
356  }
357  }; // Deposit_t
358 
359  // Map of electrons to store - catalogued by map[channel][tdc]
360  std::map<raw::ChannelID_t, std::map<unsigned int, Deposit_t>> DepositsToStore;
361 
362  geo::Point_t xyz1;
363 
364  double const xyz[3] = {
365  stepMidPoint.x() / CLHEP::cm, stepMidPoint.y() / CLHEP::cm, stepMidPoint.z() / CLHEP::cm};
366 
367  // Already know which TPC we're in because we have been told
368 
369  try {
370  geo::TPCID const tpcid{cryostat, tpc};
371  geo::TPCGeo const& tpcg = fGeo->TPC(tpcid);
372 
373  // X drift distance - the drift direction can be either in the positive or negative
374  // direction, so use std::abs
375 
377  auto const& plane_center = fWireReadoutGeom->FirstPlane(tpcid).GetCenter();
378  double XDrift = std::abs(stepMidPoint.x() / CLHEP::cm - plane_center.X());
379  auto const [_, driftSign] = tpcg.DriftAxisWithSign();
380  if (driftSign == geo::DriftSign::Negative)
381  XDrift = stepMidPoint.x() / CLHEP::cm - plane_center.X();
382  else if (driftSign == geo::DriftSign::Positive)
383  XDrift = plane_center.X() - stepMidPoint.x() / CLHEP::cm;
384 
385  if (XDrift < 0.) return;
386 
387  // Get SCE {x,y,z} offsets for particular location in TPC
388  geo::Vector_t posOffsets;
389  auto const* SCE = lar::providerFrom<spacecharge::SpaceChargeService>();
390  if (SCE->EnableSimSpatialSCE() == true) {
391  posOffsets = SCE->GetPosOffsets({xyz[0], xyz[1], xyz[2]});
392  if (larsim::Utils::SCE::out_of_bounds(posOffsets)) { return; }
393  }
394  posOffsets.SetX(-posOffsets.X());
395 
396  // Drift time (nano-sec)
397  double TDrift;
398  XDrift += posOffsets.X();
399 
400  // Space charge distortion could push the energy deposit beyond the wire plane (see
401  // issue #15131). Given that we don't have any subtlety in the simulation of this
402  // region, bringing the deposit exactly on the plane should be enough for the time
403  // being.
404  if (XDrift < 0.) XDrift = 0.;
405 
406  TDrift = XDrift * RecipDriftVel[0];
407  unsigned int const nplanes = fWireReadoutGeom->Nplanes(tpcid);
408  if (nplanes == 2) { // special case for ArgoNeuT (plane 0 is the second wire plane)
409  double const pitch = fWireReadoutGeom->PlanePitch(tpcid);
410  TDrift = ((XDrift - pitch) * RecipDriftVel[0] + pitch * RecipDriftVel[1]);
411  }
412 
413  const double lifetimecorrection = TMath::Exp(TDrift / LifetimeCorr_const);
414  const int nIonizedElectrons =
417 
418  // if we have no electrons (too small energy or too large recombination) we are done
419  // already here
420  if (nIonizedElectrons <= 0) {
421  MF_LOG_DEBUG("LArVoxelReadout")
422  << "No electrons drifted to readout, " << energy << " MeV lost.";
423  return;
424  }
425  // includes the effect of lifetime
426  const double nElectrons = nIonizedElectrons * lifetimecorrection;
427 
428  // Longitudinal & transverse diffusion sigma (cm)
429  double SqrtT = std::sqrt(TDrift);
430  double LDiffSig = SqrtT * LDiff_const;
431  double TDiffSig = SqrtT * TDiff_const;
432  double electronclsize = fElectronClusterSize;
433 
434  int nClus = (int)std::ceil(nElectrons / electronclsize);
435  if (nClus < fMinNumberOfElCluster) {
436  electronclsize = nElectrons / fMinNumberOfElCluster;
437  if (electronclsize < 1.0) { electronclsize = 1.0; }
438  nClus = (int)std::ceil(nElectrons / electronclsize);
439  }
440 
441  // Compute arrays of values as quickly as possible.
442  std::vector<double> XDiff(nClus);
443  std::vector<double> YDiff(nClus);
444  std::vector<double> ZDiff(nClus);
445  std::vector<double> nElDiff(nClus, electronclsize);
446  std::vector<double> nEnDiff(nClus);
447 
448  // fix the number of electrons in the last cluster, that has smaller size
449  nElDiff.back() = nElectrons - (nClus - 1) * electronclsize;
450 
451  for (size_t xx = 0; xx < nElDiff.size(); ++xx) {
452  if (nElectrons > 0)
453  nEnDiff[xx] = energy / nElectrons * nElDiff[xx];
454  else
455  nEnDiff[xx] = 0.;
456  }
457 
458  double const averageYtransversePos = (stepMidPoint.y() / CLHEP::cm) + posOffsets.Y();
459  double const averageZtransversePos = (stepMidPoint.z() / CLHEP::cm) + posOffsets.Z();
460 
461  // Smear drift times by x position and drift time
462  if (LDiffSig > 0.0)
463  PropRand.fireArray(nClus, &XDiff[0], 0., LDiffSig);
464  else
465  XDiff.assign(nClus, 0.0);
466 
467  if (TDiffSig > 0.0) {
468  // Smear the Y,Z position by the transverse diffusion
469  PropRand.fireArray(nClus, &YDiff[0], averageYtransversePos, TDiffSig);
470  PropRand.fireArray(nClus, &ZDiff[0], averageZtransversePos, TDiffSig);
471  }
472  else {
473  YDiff.assign(nClus, averageYtransversePos);
474  ZDiff.assign(nClus, averageZtransversePos);
475  }
476 
477  // make a collection of electrons for each plane
478  double const plane_0_center_x = fWireReadoutGeom->FirstPlane(tpcid).GetCenter().X();
479  for (auto const& plane : fWireReadoutGeom->Iterate<geo::PlaneGeo>(tpcid)) {
480 
481  double Plane0Pitch = fWireReadoutGeom->Plane0Pitch(tpcid, plane.ID().Plane);
482 
483  // "-" sign is because Plane0Pitch output is positive. Andrzej
484  xyz1.SetX(plane_0_center_x - Plane0Pitch);
485 
486  // Drift nClus electron clusters to the induction plane
487  for (int k = 0; k < nClus; ++k) {
488  // Correct drift time for longitudinal diffusion and plane
489  double TDiff = TDrift + XDiff[k] * RecipDriftVel[0];
490  // Take into account different Efields between planes. Also take into account
491  // special case for ArgoNeuT where Nplanes = 2.
492  for (unsigned int ip = 0; ip < plane.ID().Plane; ++ip) {
493  TDiff += fWireReadoutGeom->PlanePitch(tpcid, ip, ip + 1) *
494  RecipDriftVel[nplanes == 3 ? ip + 1 : ip + 2];
495  }
496  xyz1.SetY(YDiff[k]);
497  xyz1.SetZ(ZDiff[k]);
498 
500 
501  // grab the nearest channel to the xyz1 position
502  try {
503  if (fOffPlaneMargin != 0) {
504  // get the effective position where to consider the charge landed;
505  //
506  // Some optimisations are possible; in particular, this method could be
507  // extended to inform us if the point was too far. Currently, if that is
508  // the case the code will proceed, find the point is off plane, emit a
509  // warning and skip the deposition.
510  xyz1 = RecoverOffPlaneDeposit(xyz1, plane);
511  } // if charge lands off plane
512  uint32_t channel = fWireReadoutGeom->NearestChannel(xyz1, plane.ID());
513 
516  // Add potential decay/capture/etc delay effect, simTime.
517  unsigned int tdc = tpcClock.Ticks(clockData.G4ToElecTime(TDiff + simTime));
518 
519  // Add electrons produced by each cluster to the map
520  DepositsToStore[channel][tdc].add(nEnDiff[k], nElDiff[k]);
521  }
522  catch (cet::exception& e) {
523  MF_LOG_DEBUG("LArVoxelReadout")
524  << "unable to drift electrons from point (" << xyz[0] << "," << xyz[1] << ","
525  << xyz[2] << ") with exception " << e;
526  }
527  } // end loop over clusters
528  } // end loop over planes
529 
530  // Now store them in SimChannels
531  ChannelMap_t& ChannelDataMap = fChannelMaps[cryostat][tpc];
532 
533  // browse deposited data on each channel: (channel; deposit data in time)
534  for (auto const& deposit_per_channel : DepositsToStore) {
535 
536  raw::ChannelID_t channel = deposit_per_channel.first;
537 
538  // find whether we already have this channel
539  auto iChannelData = ChannelDataMap.find(channel);
540 
541  // channelData is the SimChannel these deposits are going to be added to. If
542  // there is such a channel already, use it (first beanch). If it's a new channel,
543  // the inner assignment creates a new SimChannel in the map, and we save its
544  // reference in channelData
545  sim::SimChannel& channelData = (iChannelData == ChannelDataMap.end()) ?
546  (ChannelDataMap[channel] = sim::SimChannel(channel)) :
547  iChannelData->second;
548 
549  // go through all deposits, one for each TDC: (TDC, deposit data)
550  for (auto const& deposit_per_tdc : deposit_per_channel.second) {
551  channelData.AddIonizationElectrons(trackID,
552  deposit_per_tdc.first,
553  deposit_per_tdc.second.electrons,
554  xyz,
555  deposit_per_tdc.second.energy,
556  origTrackID);
557 
558  } // for deposit on TDCs
559  } // for deposit on channels
560 
561  } // end try intended to catch points where TPC can't be found
562  catch (cet::exception& e) {
563  MF_LOG_DEBUG("LArVoxelReadout") << "step cannot be found in a TPC\n" << e;
564  }
565 
566  return;
567  }
568 
569  //---------------------------------------------------------------------------------------
570  // Never used but still have to be defined for G4
573 
574 } // namespace larg4
CLHEP::HepRandomEngine * propGen
Random engine for charge propagation.
unsigned int fTPC
which TPC this LArVoxelReadout corresponds to
Point_t ComposePoint(WireDecomposedVector_t const &decomp) const
Returns the 3D point from composition of projection and distance.
Definition: PlaneGeo.h:824
Double_t xx
Definition: macro.C:12
Utilities related to art service access.
Collection of what it takes to set a LArVoxelReadout up.
Energy deposited on a readout channel by simulated tracks.
Definition: SimChannel.h:136
A G4PVPlacement with an additional identificator.
double Plane0Pitch(TPCID const &tpcid, unsigned int p1) const
ROOT::Math::DisplacementVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Vector_t
Type for representation of momenta in 3D space.
Definition: geo_vectors.h:160
Point_t const & GetCenter() const
Returns the centre of the wire plane in world coordinates [cm].
Definition: PlaneGeo.h:366
bool out_of_bounds(geo::Vector_t const &offset)
unsigned int NTPC(CryostatID const &cryoid=details::cryostat_zero) const
Returns the total number of TPCs in the specified cryostat.
Definition: GeometryCore.h:416
Geometry information for a single TPC.
Definition: TPCGeo.h:33
double Temperature() const
In kelvin.
const ChannelMap_t & GetSimChannelMap() const
Returns the accumulated channel -> SimChannel map for the single TPC.
constexpr auto abs(T v)
Returns the absolute value of the argument.
Geant4 interface.
for(Int_t i=0;i< nentries;i++)
Definition: comparison.C:30
void Setup(Setup_t const &setupData)
Reads all the configuration elements from setupData
double PlanePitch(TPCID const &tpcid, unsigned int p1=0, unsigned int p2=1) const
void SetRandomEngines(CLHEP::HepRandomEngine *pPropGen)
Sets the random generators to be used.
double DistanceFromPlane(Point_t const &point) const
Returns the distance of the specified point from the wire plane.
Definition: PlaneGeo.h:484
detinfo::DetectorClocksData const * fClockData
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
Definition: GeometryCore.h:303
DriftAxis DriftAxisWithSign() const
Returns the expected drift direction based on geometry.
Definition: TPCGeo.h:78
pure virtual base interface for detector clocks
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.
Access the description of the physical detector geometry.
geo::Point_t RecoverOffPlaneDeposit(geo::Point_t const &pos, geo::PlaneGeo const &plane) const
Returns the point on the specified plane closest to position.
geo::GeometryCore const * fGeo
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.
Use Geant4&#39;s user "hooks" to maintain a list of particles generated by Geant4.
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, int origTrackID)
const std::vector< unsigned short int > & SkipWireSignalInTPCs() const
Utility function for testing if Space Charge offsets are out of bounds.
double ElectronClusterSize() const
Interface for a class providing readout channel mapping to geometry.
Drift towards negative values.
WidthDepthProjection_t PointWidthDepthProjection(Point_t const &point) const
Returns the projection of the specified point on the plane.
Definition: PlaneGeo.h:897
Geometry information for a single wire plane.The plane is represented in the geometry by a solid whic...
Definition: PlaneGeo.h:67
double energy
Definition: plottest35.C:25
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 DriftVelocity(double efield=0., double temperature=0.) const
cm/us
bool Has(std::vector< unsigned short int > v, unsigned short int tpc) const
The data type to uniquely identify a TPC.
Definition: geo_types.h:306
Description of the physical geometry of one entire detector.
Definition: GeometryCore.h:91
virtual G4bool ProcessHits(G4Step *, G4TouchableHistory *)
Definition of data types for geometry description.
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.
raw::ChannelID_t NearestChannel(Point_t const &worldLoc, PlaneID const &planeid) const
Returns the ID of the channel nearest to the specified position.
unsigned int Nplanes(TPCID const &tpcid=details::tpc_zero) const
Returns the total number of planes in the specified TPC.
Drift towards positive values.
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
Definition: geo_vectors.h:180
A Geant4 sensitive detector that accumulates voxel information.
PlaneGeo const & FirstPlane(TPCID const &tpcid) const
Returns the first plane of the specified TPC.
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 ...
Definition: PlaneGeo.cxx:440
double G4ToElecTime(double const g4_time) const
Contains all timing reference information for the detector.
int MinNumberOfElCluster() const
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.
Singleton to access a unified treatment of ionization and scintillation in LAr.
range_type< T > Iterate() const
Definition: Iterable.h:121
CLHEP::HepRandomEngine * fPropGen
random engine for charge propagation
#define MF_LOG_DEBUG(id)
virtual void Initialize(G4HCofThisEvent *)
sim::LArG4Parameters const * fLgp
Transports energy depositions from GEANT4 to TPC channels.
void AddIonizationElectrons(TrackID_t trackID, TDC_t tdc, double numberElectrons, double const *xyz, double energy, TrackID_t origTrackID=util::kBogusI)
Add ionization electrons and energy to this channel.
Definition: SimChannel.cxx:49
double LongitudinalDiffusion() const
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
TPCGeo const & TPC(TPCID const &tpcid=details::tpc_zero) const
Returns the specified TPC.
Definition: GeometryCore.h:448
geo::WireReadoutGeom const * fWireReadoutGeom
Float_t e
Definition: plot.C:35
Interface to geometry for wire readouts .
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
LArVoxelReadout(std::string const &name, geo::GeometryCore const *geom, geo::WireReadoutGeom const *wireReadoutGeom, sim::LArG4Parameters const *lgp)
Constructor. Can detect which TPC to cover by the name.
bool DisableWireplanes() const
The data type to uniquely identify a cryostat.
Definition: geo_types.h:187