All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SimDriftElectrons_module.cc
Go to the documentation of this file.
1 /**
2  * @file SimDriftElectrons_module.cxx
3  *
4  * @brief Transports energy depositions in the LAr TPC to the TPC
5  * channels.
6  *
7  * author:
8  * This module was prepared by William Seligman (me), based on code
9  * that had been in
10  * `LArG4::LArVoxelReadout::DriftIonizationElectrons`. However, though
11  * I wrote the original LArVoxelReadout code, I have no idea who added
12  * DriftIonizationElectrons. I probably will not be able to answer any
13  * questions about how this code works.
14  *
15  * This module acts on sim::SimEnergyDeposit, the single energy
16  * depositions from the detector simulation (LArG4), and simulates the
17  * transport of the ensuing ionization electrons to the readout
18  * channels:
19  *
20  * 1. the number of ionisation electrons is read from the current
21  * `larg4::IonizationAndScintillation` instance
22  * 2. space charge displacement is optionally applied
23  * 3. lifetime correction is applied
24  * 4. charge is split in small electron clusters
25  * 5. each cluster is subject to longitudinal and transverse diffusion
26  * 6. each cluster is assigned to one TPC channel for each wire plane
27  * 7. optionally, charge is forced to stay on the planes; otherwise charge
28  * drifting outside the plane is lost
29  *
30  * For each energy deposition, entries on the appropriate
31  * `sim::SimChannel` are added, with the information of the position
32  * where the energy deposit happened (in global coordinates,
33  * centimeters), the ID of the track in the detector simulation which
34  * produced the deposition, and the quantized time of arrival to the
35  * channel (in global TDC tick units). At most one entry is added for
36  * each electron cluster, but entries from the same energy deposit can
37  * be compacted if falling on the same TDC tick.
38  *
39  * Options
40  * --------
41  *
42  * A few optional behaviours are supported:
43  *
44  * * lead off-plane charge to the planes: regulated by
45  * `RecoverOffPlaneDeposit()`, if charge which reaches a wire plane
46  * is actually off it by less than the chosen margin, it's accounted for by
47  * that plane; by default the margin is 0 and all the charge off the plane
48  * is lost (with a warning)
49  *
50  * Update:
51  * Christoph Alt, September 2018 (christoph.alt@cern.ch)
52  * Break hardcoded charge drift in x to support charge drift in y and z.
53  */
54 
55 // LArSoft includes
63 
64 #include "larcoreobj/SimpleTypesAndConstants/RawTypes.h" // raw::ChannelID_t
69 
70 // Framework includes
71 #include "art/Framework/Core/EDProducer.h"
72 #include "art/Framework/Core/ModuleMacros.h"
73 #include "art/Framework/Principal/Event.h"
74 #include "art/Framework/Principal/Handle.h"
75 #include "art/Framework/Services/Registry/ServiceHandle.h"
76 #include "fhiclcpp/ParameterSet.h"
77 #include "messagefacility/MessageLogger/MessageLogger.h"
78 #include "nurandom/RandomUtils/NuRandomService.h"
79 
80 // External libraries
81 #include "CLHEP/Random/RandGauss.h"
82 #include "TMath.h"
83 
84 // C++ includes
85 #include <algorithm> // std::find
86 #include <cmath>
87 #include <unordered_map>
88 #include <memory>
89 #include <utility>
90 #include <vector>
91 
92 namespace detsim {
93 
94  // Base class for creation of raw signals on wires.
95  class SimDriftElectrons : public art::EDProducer {
96  public:
97  explicit SimDriftElectrons(fhicl::ParameterSet const& pset);
98 
99  // Methods that that are available for a module derived from
100  // art::EDProducer.
101  void produce(art::Event& evt) override;
102  void beginJob() override;
103 
104  private:
105  // The label of the module that created the sim::SimEnergyDeposit
106  // objects (as of Oct-2017, this is probably "largeant").
107  art::InputTag fSimModuleLabel;
108 
109  CLHEP::RandGauss fRandGauss;
110 
116 
118  double fLDiff_const;
119  double fTDiff_const;
120  double fRecipDriftVel[3];
121 
123 
124  // double fOffPlaneMargin;
125 
126  // In order to create the associations, for each channel we create
127  // we have to keep track of its index in the output vector, and the
128  // indexes of all the steps that contributed to it.
130  size_t channelIndex;
131  std::vector<size_t> stepList;
132  };
133 
134  // Define type: channel -> sim::SimChannel's bookkeeping.
135  typedef std::unordered_map<raw::ChannelID_t, ChannelBookKeeping> ChannelMap_t;
136 
137  // Array of maps of channel data indexed by [cryostat,tpc]
138  std::vector<ChannelMap_t> fChannelMaps;
139  // The above ensemble may be thought of as a 3D array of
140  // ChannelBookKeepings: e.g., SimChannel[cryostat,tpc,channel ID].
141 
142  // Save the number of cryostats, and the number of TPCs within
143  // each cryostat.
144  size_t fNCryostats;
145  std::vector<size_t> fNTPCs;
146 
147  // Per-cluster information.
148  std::vector<double> fLongDiff;
149  std::vector<double> fTransDiff1;
150  std::vector<double> fTransDiff2;
151  std::vector<double> fnElDiff;
152  std::vector<double> fnEnDiff;
153 
154  double fDriftClusterPos[3];
155 
156  art::ServiceHandle<geo::Geometry const> fGeometry; ///< Handle to the Geometry service
157 
158  }; // class SimDriftElectrons
159 
160  //-------------------------------------------------
161  SimDriftElectrons::SimDriftElectrons(fhicl::ParameterSet const& pset)
162  : art::EDProducer{pset}
163  , fSimModuleLabel{pset.get<art::InputTag>("SimulationLabel")}
164  // create a default random engine; obtain the random seed from
165  // NuRandomService, unless overridden in configuration with key
166  // "Seed"
167  , fRandGauss{art::ServiceHandle<rndm::NuRandomService>{}->createEngine(*this, pset, "Seed")}
168  , fStoreDriftedElectronClusters{pset.get<bool>("StoreDriftedElectronClusters", false)}
169  {
170  produces<std::vector<sim::SimChannel>>();
171  if (fStoreDriftedElectronClusters) { produces<std::vector<sim::SimDriftedElectronCluster>>(); }
172  }
173 
174  //-------------------------------------------------
175  void
177  {
178  // Define the physical constants we'll use.
179 
180  auto const detProp =
181  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataForJob();
182  fElectronLifetime = detProp.ElectronLifetime(); // Electron lifetime as returned by the
183  // DetectorProperties service assumed to be in us;
184  for (int i = 0; i < 3; ++i) {
185  double driftVelocity = detProp.DriftVelocity(detProp.Efield(i),
186  detProp.Temperature()) *
187  1.e-3; // Drift velocity as returned by the DetectorProperties service
188  // assumed to be in cm/us. Multiply by 1.e-3 to convert into
189  // LArSoft standard velocity units, cm/ns;
190 
191  fRecipDriftVel[i] = 1. / driftVelocity;
192  }
193 
194  // To-do: Move the parameters we fetch from "LArG4" to detector
195  // properties.
196  art::ServiceHandle<sim::LArG4Parameters const> paramHandle;
197  fElectronClusterSize = paramHandle->ElectronClusterSize();
198  fMinNumberOfElCluster = paramHandle->MinNumberOfElCluster();
199  fLongitudinalDiffusion = paramHandle->LongitudinalDiffusion(); // cm^2/ns units
200  fTransverseDiffusion = paramHandle->TransverseDiffusion(); // cm^2/ns units
201 
202  MF_LOG_DEBUG("SimDriftElectrons")
203  << " e lifetime (ns): " << fElectronLifetime
204  << "\n Temperature (K): " << detProp.Temperature()
205  << "\n Drift velocity (cm/ns): " << 1. / fRecipDriftVel[0] << " " << 1. / fRecipDriftVel[1]
206  << " " << 1. / fRecipDriftVel[2];
207 
208  // Opposite of lifetime. Convert from us to standard LArSoft time units, ns;
210  fLDiff_const = std::sqrt(2. * fLongitudinalDiffusion);
211  fTDiff_const = std::sqrt(2. * fTransverseDiffusion);
212 
213  // For this detector's geometry, save the number of cryostats and
214  // the number of TPCs within each cryostat.
215  fNCryostats = fGeometry->Ncryostats();
216  fNTPCs.resize(fNCryostats);
217  for (size_t n = 0; n < fNCryostats; ++n)
218  fNTPCs[n] = fGeometry->NTPC(n);
219  }
220 
221  //-------------------------------------------------
222  void
223  SimDriftElectrons::produce(art::Event& event)
224  {
225  // Fetch the SimEnergyDeposit objects for this event.
226  typedef art::Handle<std::vector<sim::SimEnergyDeposit>> energyDepositHandle_t;
227  energyDepositHandle_t energyDepositHandle;
228  // If there aren't any energy deposits for this event, don't
229  // panic. It's possible someone is doing a study with events
230  // outside the TPC, or where there are only non-ionizing
231  // particles, or something like that.
232  if (!event.getByLabel(fSimModuleLabel, energyDepositHandle)) return;
233 
234  // Define the container for the SimChannel objects that will be
235  // transferred to the art::Event after the put statement below.
236  std::unique_ptr<std::vector<sim::SimChannel>> channels(new std::vector<sim::SimChannel>);
237  // Container for the SimDriftedElectronCluster objects
238  std::unique_ptr<std::vector<sim::SimDriftedElectronCluster>>
239  SimDriftedElectronClusterCollection(new std::vector<sim::SimDriftedElectronCluster>);
240 
241  // Clear the channel maps from the last event. Remember,
242  // fChannelMaps is an array[cryo][tpc] of maps.
243  size_t cryo = 0;
244  fChannelMaps.resize(fNCryostats);
245  for (auto& cryoData : fChannelMaps) { // each, a vector of maps
246  cryoData.clear();
247  }
248 
249  auto const clockData =
250  art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(event);
251  auto const& tpcClock = clockData.TPCClock();
252 
253  auto const detProp =
254  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(event, clockData);
255  // We're going through the input vector by index, rather than by
256  // iterator, because we need the index number to compute the
257  // associations near the end of this method.
258  auto const& energyDeposits = *energyDepositHandle;
259  auto energyDepositsSize = energyDeposits.size();
260 
261  // For each energy deposit in this event
262  for (size_t edIndex = 0; edIndex < energyDepositsSize; ++edIndex) {
263  auto const& energyDeposit = energyDeposits[edIndex];
264 
265  // "xyz" is the position of the energy deposit in world
266  // coordinates. Note that the units of distance in
267  // sim::SimEnergyDeposit are supposed to be cm.
268  auto const mp = energyDeposit.MidPoint();
269  double const xyz[3] = {mp.X(), mp.Y(), mp.Z()};
270 
271  // From the position in world coordinates, determine the
272  // cryostat and tpc. If somehow the step is outside a tpc
273  // (e.g., cosmic rays in rock) just move on to the next one.
274  unsigned int cryostat = 0;
275  try {
276  fGeometry->PositionToCryostat(xyz, cryostat);
277  }
278  catch (cet::exception& e) {
279  mf::LogWarning("SimDriftElectrons") << "step " // << energyDeposit << "\n"
280  << "cannot be found in a cryostat\n"
281  << e;
282  continue;
283  }
284 
285  unsigned int tpc = 0;
286  try {
287  fGeometry->PositionToTPC(xyz, tpc, cryostat);
288  }
289  catch (cet::exception& e) {
290  mf::LogWarning("SimDriftElectrons") << "step " // << energyDeposit << "\n"
291  << "cannot be found in a TPC\n"
292  << e;
293  continue;
294  }
295 
296  const geo::TPCGeo& tpcGeo = fGeometry->TPC(tpc, cryostat);
297 
298  // The drift direction can be either in the positive
299  // or negative direction in any coordinate x, y or z.
300  // Charge drift in ...
301  // +x: tpcGeo.DetectDriftDirection()==1
302  // -x: tpcGeo.DetectDriftDirection()==-1
303  // +y: tpcGeo.DetectDriftDirection()==2
304  // -y tpcGeo.DetectDriftDirection()==-2
305  // +z: tpcGeo.DetectDriftDirection()==3
306  // -z: tpcGeo.DetectDriftDirection()==-3
307 
308  // Define charge drift direction: driftcoordinate (x, y or z) and
309  // driftsign (positive or negative). Also define coordinates perpendicular
310  // to drift direction.
311  int driftcoordinate = std::abs(tpcGeo.DetectDriftDirection()) - 1; // x:0, y:1, z:2
312 
313  int transversecoordinate1 = 0;
314  int transversecoordinate2 = 0;
315  if (driftcoordinate == 0) {
316  transversecoordinate1 = 1;
317  transversecoordinate2 = 2;
318  }
319  else if (driftcoordinate == 1) {
320  transversecoordinate1 = 0;
321  transversecoordinate2 = 2;
322  }
323  else if (driftcoordinate == 2) {
324  transversecoordinate1 = 0;
325  transversecoordinate2 = 1;
326  }
327 
328  if (transversecoordinate1 == transversecoordinate2)
329  continue; // this is the case when driftcoordinate != 0, 1 or 2
330 
331  int driftsign = 0; // 1: +x, +y or +z, -1: -x, -y or -z
332  if (tpcGeo.DetectDriftDirection() > 0)
333  driftsign = 1;
334  else
335  driftsign = -1;
336 
337  // Check for charge deposits behind charge readout planes
338  if (driftsign == 1 && tpcGeo.PlaneLocation(0)[driftcoordinate] < xyz[driftcoordinate])
339  continue;
340  if (driftsign == -1 && tpcGeo.PlaneLocation(0)[driftcoordinate] > xyz[driftcoordinate])
341  continue;
342 
343  /// \todo think about effects of drift between planes.
344  // Center of plane is also returned in cm units
345  double DriftDistance =
346  std::abs(xyz[driftcoordinate] - tpcGeo.PlaneLocation(0)[driftcoordinate]);
347 
348  // Space-charge effect (SCE): Get SCE {x,y,z} offsets for
349  // particular location in TPC
350  geo::Vector_t posOffsets{0.0, 0.0, 0.0};
351  double posOffsetxyz[3] = {0.0, 0.0, 0.0}; // need this array for the driftcoordinate and
352  // transversecoordinates
353  auto const* SCE = lar::providerFrom<spacecharge::SpaceChargeService>();
354  if (SCE->EnableSimSpatialSCE() == true) {
355  posOffsets = SCE->GetPosOffsets(mp);
356  if (larsim::Utils::SCE::out_of_bounds(posOffsets)) {
357  continue;
358  }
359  posOffsetxyz[0] = posOffsets.X();
360  posOffsetxyz[1] = posOffsets.Y();
361  posOffsetxyz[2] = posOffsets.Z();
362  }
363 
364  double avegagetransversePos1 = 0.;
365  double avegagetransversePos2 = 0.;
366 
367  DriftDistance += -1. * posOffsetxyz[driftcoordinate];
368  avegagetransversePos1 = xyz[transversecoordinate1] + posOffsetxyz[transversecoordinate1];
369  avegagetransversePos2 = xyz[transversecoordinate2] + posOffsetxyz[transversecoordinate2];
370 
371  // Space charge distortion could push the energy deposit beyond the wire
372  // plane (see issue #15131). Given that we don't have any subtlety in the
373  // simulation of this region, bringing the deposit exactly on the plane
374  // should be enough for the time being.
375  if (DriftDistance < 0.) DriftDistance = 0.;
376 
377  // Drift time in ns
378  double TDrift = DriftDistance * fRecipDriftVel[0];
379 
380  if (tpcGeo.Nplanes() == 2 &&
381  driftcoordinate == 0) { // special case for ArgoNeuT (Nplanes = 2 and drift direction =
382  // x): plane 0 is the second wire plane
383  TDrift = ((DriftDistance - tpcGeo.PlanePitch(0, 1)) * fRecipDriftVel[0] +
384  tpcGeo.PlanePitch(0, 1) * fRecipDriftVel[1]);
385  }
386 
387  const int nIonizedElectrons = energyDeposit.NumElectrons();
388  const double lifetimecorrection = TMath::Exp(TDrift / fLifetimeCorr_const);
389  const double energy = energyDeposit.Energy();
390 
391  // if we have no electrons (too small energy or too large recombination)
392  // we are done already here
393  if (nIonizedElectrons <= 0) {
394  MF_LOG_DEBUG("SimDriftElectrons")
395  << "step " // << energyDeposit << "\n"
396  << "No electrons drifted to readout, " << energy << " MeV lost.";
397  continue;
398  }
399 
400  // includes the effect of lifetime: lifetimecorrection = exp[-tdrift/tau]
401  const double nElectrons = nIonizedElectrons * lifetimecorrection;
402 
403  // Longitudinal & transverse diffusion sigma (cm)
404  double SqrtT = std::sqrt(TDrift);
405  double LDiffSig = SqrtT * fLDiff_const;
406  double TDiffSig = SqrtT * fTDiff_const;
407  double electronclsize = fElectronClusterSize;
408 
409  // Number of electron clusters.
410  int nClus = (int)std::ceil(nElectrons / electronclsize);
411  if (nClus < fMinNumberOfElCluster) {
412  electronclsize = nElectrons / fMinNumberOfElCluster;
413  if (electronclsize < 1.0) { electronclsize = 1.0; }
414  nClus = (int)std::ceil(nElectrons / electronclsize);
415  }
416 
417  // Empty and resize the electron-cluster vectors.
418  fLongDiff.clear();
419  fTransDiff1.clear();
420  fTransDiff2.clear();
421  fnElDiff.clear();
422  fnEnDiff.clear();
423  fLongDiff.resize(nClus);
424  fTransDiff1.resize(nClus);
425  fTransDiff2.resize(nClus);
426  fnElDiff.resize(nClus, electronclsize);
427  fnEnDiff.resize(nClus);
428 
429  // fix the number of electrons in the last cluster, that has a smaller size
430  fnElDiff.back() = nElectrons - (nClus - 1) * electronclsize;
431 
432  for (size_t xx = 0; xx < fnElDiff.size(); ++xx) {
433  if (nElectrons > 0)
434  fnEnDiff[xx] = energy / nElectrons * fnElDiff[xx];
435  else
436  fnEnDiff[xx] = 0.;
437  }
438 
439  // Smear drift times by longitudinal diffusion
440  if (LDiffSig > 0.0)
441  fRandGauss.fireArray(nClus, &fLongDiff[0], 0., LDiffSig);
442  else
443  fLongDiff.assign(nClus, 0.0);
444 
445  if (TDiffSig > 0.0) {
446  // Smear the coordinates in plane perpendicular to drift direction by the transverse diffusion
447  fRandGauss.fireArray(nClus, &fTransDiff1[0], avegagetransversePos1, TDiffSig);
448  fRandGauss.fireArray(nClus, &fTransDiff2[0], avegagetransversePos2, TDiffSig);
449  }
450  else {
451  fTransDiff1.assign(nClus, avegagetransversePos1);
452  fTransDiff2.assign(nClus, avegagetransversePos2);
453  }
454 
455  // make a collection of electrons for each plane
456  for (size_t p = 0; p < tpcGeo.Nplanes(); ++p) {
457 
458  fDriftClusterPos[driftcoordinate] = tpcGeo.PlaneLocation(p)[driftcoordinate];
459 
460  // Drift nClus electron clusters to the induction plane
461  for (int k = 0; k < nClus; ++k) {
462 
463  // Correct drift time for longitudinal diffusion and plane
464  double TDiff = TDrift + fLongDiff[k] * fRecipDriftVel[0];
465 
466  // Take into account different Efields between planes
467  // Also take into account special case for ArgoNeuT (Nplanes = 2 and
468  // drift direction = x): plane 0 is the second wire plane
469  for (size_t ip = 0; ip < p; ++ip) {
470  TDiff +=
471  tpcGeo.PlanePitch(ip+1, ip) *
472  fRecipDriftVel[(tpcGeo.Nplanes() == 2 && driftcoordinate == 0) ? ip + 2 : ip + 1];
473  }
474 
475  fDriftClusterPos[transversecoordinate1] = fTransDiff1[k];
476  fDriftClusterPos[transversecoordinate2] = fTransDiff2[k];
477 
478  /// \todo think about effects of drift between planes
479 
480  // grab the nearest channel to the fDriftClusterPos position
481  try {
482  raw::ChannelID_t channel =
483  fGeometry->NearestChannel(fDriftClusterPos, p, tpc, cryostat);
484 
485  /// \todo check on what happens if we allow the tdc value to be
486  /// \todo beyond the end of the expected number of ticks
487  // Add potential decay/capture/etc delay effect, simTime.
488  auto const simTime = energyDeposit.Time();
489  unsigned int tdc = tpcClock.Ticks(clockData.G4ToElecTime(TDiff + simTime));
490 
491  // Find whether we already have this channel in our map.
492  ChannelMap_t& channelDataMap = fChannelMaps[cryostat];
493  auto search = channelDataMap.find(channel);
494 
495  // We will find (or create) the pointer to a
496  // sim::SimChannel.
497  size_t channelIndex = 0;
498 
499  // Have we created the sim::SimChannel corresponding to
500  // channel ID?
501  if (search == channelDataMap.end()) {
502  // We haven't. Initialize the bookkeeping information
503  // for this channel.
504  ChannelBookKeeping bookKeeping;
505 
506  // Add a new channel to the end of the list we'll
507  // write out after we've processed this event.
508  bookKeeping.channelIndex = channels->size();
509  channels->emplace_back(channel);
510  channelIndex = bookKeeping.channelIndex;
511 
512  // Initialize a vector with the index of the step that
513  // created this channel.
514  bookKeeping.stepList.push_back(edIndex);
515 
516  // Save the bookkeeping information for this channel.
517  channelDataMap[channel] = bookKeeping;
518  }
519  else {
520  // We've created this SimChannel for a previous energy
521  // deposit. Get its address.
522 
523  auto& bookKeeping = search->second;
524  channelIndex = bookKeeping.channelIndex;
525 
526  // Has this step contributed to this channel before?
527  auto& stepList = bookKeeping.stepList;
528  if (!std::binary_search(stepList.begin(), stepList.end(), edIndex)) {
529  // No, so add this step's index to the list.
530  stepList.push_back(edIndex);
531  }
532  }
533 
534  sim::SimChannel* channelPtr = &(channels->at(channelIndex));
535 
536  // Add the electron clusters and energy to the
537  // sim::SimChannel
538  channelPtr->AddIonizationElectrons(
539  energyDeposit.TrackID(), tdc, fnElDiff[k], xyz, fnEnDiff[k]);
540 
542  SimDriftedElectronClusterCollection->emplace_back(
543  fnElDiff[k],
544  TDiff + simTime, // timing
545  geo::Point_t{mp.X(), mp.Y(), mp.Z()}, // mean position of the deposited energy
547  fDriftClusterPos[1],
548  fDriftClusterPos[2]}, // final position of the drifted cluster
549  geo::Point_t{
550  LDiffSig, TDiffSig, TDiffSig}, // Longitudinal (X) and transverse (Y,Z) diffusion
551  fnEnDiff[k], // deposited energy that originated this cluster
552  energyDeposit.TrackID());
553  }
554  catch (cet::exception& e) {
555  mf::LogDebug("SimDriftElectrons")
556  << "unable to drift electrons from point (" << xyz[0] << "," << xyz[1] << ","
557  << xyz[2] << ") with exception " << e;
558  } // end try to determine channel
559  } // end loop over clusters
560  } // end loop over planes
561  } // for each sim::SimEnergyDeposit
562 
563  // Write the sim::SimChannel collection.
564  event.put(std::move(channels));
565  if (fStoreDriftedElectronClusters) event.put(std::move(SimDriftedElectronClusterCollection));
566  }
567 
568 } // namespace detsim
569 
570 DEFINE_ART_MODULE(detsim::SimDriftElectrons)
Store parameters for running LArG4.
ROOT::Math::DisplacementVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Vector_t
Type for representation of momenta in 3D space.
Definition: geo_vectors.h:164
double PlanePitch(unsigned int p1=0, unsigned int p2=1) const
Definition: TPCGeo.cxx:388
Utilities related to art service access.
Energy deposited on a readout channel by simulated tracks.
Definition: SimChannel.h:145
contains objects relating to SimDriftedElectronCluster
bool out_of_bounds(geo::Vector_t const &offset)
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:165
pdgs p
Definition: selectors.fcl:22
Geometry information for a single TPC.
Definition: TPCGeo.h:38
art::ServiceHandle< geo::Geometry const > fGeometry
Handle to the Geometry service.
T abs(T value)
createEngine fStoreDriftedElectronClusters
Utility function for testing if Space Charge offsets are out of bounds.
void produce(art::Event &evt) override
std::vector< ChannelMap_t > fChannelMaps
short int DetectDriftDirection() const
Returns the expected drift direction based on geometry.
Definition: TPCGeo.cxx:157
contains information for a single step in the detector simulation
void beginJob() override
object containing MC truth information necessary for making RawDigits and doing back tracking ...
do i e
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:55
std::unordered_map< raw::ChannelID_t, ChannelBookKeeping > ChannelMap_t
TCEvent evt
Definition: DataStructs.cxx:8
pdgs k
Definition: selectors.fcl:22
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
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:184
const double * PlaneLocation(unsigned int p) const
Definition: TPCGeo.cxx:382
art framework interface to geometry description
auto const detProp
SimDriftElectrons(fhicl::ParameterSet const &pset)