LArSoft  v10_04_05
Liquid Argon Software toolkit - https://larsoft.org/
DriftElectronstoPlane_module.cc
Go to the documentation of this file.
1 
55 // LArSoft includes
65 
66 // Framework includes
72 #include "fhiclcpp/ParameterSet.h"
75 
76 // External libraries
77 #include "CLHEP/Random/RandGauss.h"
78 #include "TMath.h"
79 
80 #include <cmath>
81 #include <memory>
82 #include <utility>
83 #include <vector>
84 
85 namespace detsim {
86 
87  // Base class for creation of raw signals on wires.
89  public:
90  explicit DriftElectronstoPlane(fhicl::ParameterSet const& pset);
91 
92  // Methods that that are available for a module derived from
93  // art::EDProducer.
94  void produce(art::Event& evt) override;
95  void beginJob() override;
96 
97  private:
98  // The label of the module that created the sim::SimEnergyDeposit
99  // objects (as of Oct-2017, this is probably "largeant").
101 
102  CLHEP::RandGauss fRandGauss;
103 
110  double fRecombA;
111  double fRecombk;
112  double fModBoxA;
113  double fModBoxB;
115 
118  double fLDiff_const;
119  double fTDiff_const;
120  double fRecipDriftVel[3];
121 
122  // Save the number of cryostats, and the number of TPCs within
123  // each cryostat.
124  size_t fNCryostats;
125  std::vector<size_t> fNTPCs;
126 
127  // Per-cluster information.
128  std::vector<double> fLongDiff;
129  std::vector<double> fTransDiff1;
130  std::vector<double> fTransDiff2;
131  std::vector<double> fnElDiff;
132  std::vector<double> fnEnDiff;
133 
134  double fDriftClusterPos[3];
135 
137 
138  //IS calculationg
140 
141  }; // class DriftElectronstoPlane
142 
143  //-------------------------------------------------
145  : art::EDProducer{pset}
146  , fSimModuleLabel{pset.get<art::InputTag>("SimulationLabel")}
147  // create a default random engine; obtain the random seed from
148  // NuRandomService, unless overridden in configuration with key
149  // "Seed"
151  -> registerAndSeedEngine(createEngine(0), pset, "Seed")}
152  , fStoreDriftedElectronClusters{pset.get<bool>("StoreDriftedElectronClusters", true)}
153  , fLongitudinalDiffusion{pset.get<double>("LongitudinalDiffusion", 6.2e-9)}
154  , fTransverseDiffusion{pset.get<double>("TransverseDiffusion", 16.3e-9)}
155  , fElectronClusterSize{pset.get<double>("ElectronClusterSize", 600.0)}
156  , fMinNumberOfElCluster{pset.get<int>("MinNumberOfElCluster", 0)}
157  , fGeVToElectrons{pset.get<double>("GeVToElectrons", 4.237e+07)}
158  , fRecombA{pset.get<double>("RecombA", 0.800)}
159  , fRecombk{pset.get<double>("Recombk", 0.0486)}
160  , fModBoxA{pset.get<double>("ModBoxA", 0.930)}
161  , fModBoxB{pset.get<double>("ModBoxB", 0.212)}
162  , fUseModBoxRecomb{pset.get<bool>("UseModBoxRecomb", true)}
163  , fISAlg{pset}
164  {
165  if (fStoreDriftedElectronClusters) { produces<std::vector<sim::SimDriftedElectronCluster>>(); }
166  }
167 
168  //-------------------------------------------------
170  {
171  // Define the physical constants we'll use.
172 
173  auto const detProp =
176  detProp
177  .ElectronLifetime(); // Electron lifetime as returned by the DetectorProperties service assumed to be in us;
178  for (int i = 0; i < 3; ++i) {
179  double driftVelocity =
180  detProp.DriftVelocity(detProp.Efield(i),
181  detProp.Temperature()) *
182  1.e-3; // Drift velocity as returned by the DetectorProperties service assumed to be in cm/us. Multiply by 1.e-3 to convert into LArSoft standard velocity units, cm/ns;
183 
184  fRecipDriftVel[i] = 1. / driftVelocity;
185  }
186  MF_LOG_DEBUG("DriftElectronstoPlane")
187  << " e lifetime (ns): " << fElectronLifetime
188  << "\n Temperature (K): " << detProp.Temperature()
189  << "\n Drift velocity (cm/ns): " << 1. / fRecipDriftVel[0] << " " << 1. / fRecipDriftVel[1]
190  << " " << 1. / fRecipDriftVel[2];
191 
192  // Opposite of lifetime. Convert from us to standard LArSoft time units, ns;
194  fLDiff_const = std::sqrt(2. * fLongitudinalDiffusion);
195  fTDiff_const = std::sqrt(2. * fTransverseDiffusion);
196 
197  // For this detector's geometry, save the number of cryostats and the number of TPCs
198  // within each cryostat.
200  fNTPCs.resize(fNCryostats);
201  for (size_t n = 0; n < fNCryostats; ++n)
203  }
204 
205  //-------------------------------------------------
207  {
208  // Fetch the SimEnergyDeposit objects for this event.
209  typedef art::Handle<std::vector<sim::SimEnergyDeposit>> energyDepositHandle_t;
210  energyDepositHandle_t energyDepositHandle;
211  // If there aren't any energy deposits for this event, don't panic. It's possible
212  // someone is doing a study with events outside the TPC, or where there are only
213  // non-ionizing particles, or something like that.
214  if (!event.getByLabel(fSimModuleLabel, energyDepositHandle)) return;
215  // Container for the SimDriftedElectronCluster objects
216  std::unique_ptr<std::vector<sim::SimDriftedElectronCluster>>
217  SimDriftedElectronClusterCollection(new std::vector<sim::SimDriftedElectronCluster>);
218 
219  // We're going through the input vector by index, rather than by iterator, because we
220  // need the index number to compute the associations near the end of this method.
221  auto const& energyDeposits = *energyDepositHandle;
222  auto energyDepositsSize = energyDeposits.size();
223 
224  auto const detProp =
226 
227  auto const& wireReadoutGeom = art::ServiceHandle<geo::WireReadout>()->Get();
228  // For each energy deposit in this event
229  for (size_t edIndex = 0; edIndex < energyDepositsSize; ++edIndex) {
230  auto const& energyDeposit = energyDeposits[edIndex];
231 
232  // "xyz" is the position of the energy deposit in world coordinates. Note that the
233  // units of distance in sim::SimEnergyDeposit are supposed to be cm.
234  auto const mp = energyDeposit.MidPoint();
235  double const xyz[3] = {mp.X(), mp.Y(), mp.Z()};
236 
237  // From the position in world coordinates, determine the cryostat and tpc. If
238  // somehow the step is outside a tpc (e.g., cosmic rays in rock) just move on to the
239  // next one.
240  geo::TPCGeo const& tpcGeo = fGeometry->TPC();
241  geo::TPCID const& tpcid = tpcGeo.ID();
242  unsigned const nplanes = wireReadoutGeom.Nplanes(tpcid);
243 
244  // The drift direction can be either in the positive or negative direction in any
245  // coordinate x, y or z.
246 
247  // Define charge drift direction: driftcoordinate (x, y or z) and driftsign
248  // (positive or negative). Also define coordinates perpendicular to drift direction.
249  auto const [axis, sign] = tpcGeo.DriftAxisWithSign();
250 
251  // For axis == geo::Coordinate::X
252  int driftcoordinate = 0;
253  int transversecoordinate1 = 1;
254  int transversecoordinate2 = 2;
255  if (axis == geo::Coordinate::Y) {
256  transversecoordinate1 = 0;
257  driftcoordinate = 1;
258  transversecoordinate2 = 2;
259  }
260  else if (axis == geo::Coordinate::Z) {
261  transversecoordinate1 = 0;
262  transversecoordinate2 = 1;
263  driftcoordinate = 2;
264  }
265 
266  int const driftsign = to_int(sign); //1: +x, +y or +z, -1: -x, -y or -z
267 
268  //Check for charge deposits behind charge readout planes
269  auto const plane_center = wireReadoutGeom.FirstPlane(tpcid).GetCenter();
270  auto const plane_center_coord = geo::vect::coord(plane_center, driftcoordinate);
271  if (driftsign == 1 && plane_center_coord < xyz[driftcoordinate]) continue;
272  if (driftsign == -1 && plane_center_coord > xyz[driftcoordinate]) continue;
273 
275  // Center of plane is also returned in cm units
276  double DriftDistance = std::abs(xyz[driftcoordinate] - plane_center_coord);
277 
278  // Space-charge effect (SCE): Get SCE {x,y,z} offsets for particular location in TPC
279  geo::Vector_t posOffsets{0.0, 0.0, 0.0};
280  double posOffsetxyz[3] = {
281  0.0, 0.0, 0.0}; //need this array for the driftcoordinate and transversecoordinates
282  auto const* SCE = lar::providerFrom<spacecharge::SpaceChargeService>();
283  if (SCE->EnableSimSpatialSCE() == true) {
284  posOffsets = SCE->GetPosOffsets(mp);
285  if (larsim::Utils::SCE::out_of_bounds(posOffsets)) continue;
286  posOffsetxyz[0] = posOffsets.X();
287  posOffsetxyz[1] = posOffsets.Y();
288  posOffsetxyz[2] = posOffsets.Z();
289  }
290 
291  double avegagetransversePos1 = 0.;
292  double avegagetransversePos2 = 0.;
293 
294  DriftDistance += -1. * posOffsetxyz[driftcoordinate];
295  avegagetransversePos1 = xyz[transversecoordinate1] + posOffsetxyz[transversecoordinate1];
296  avegagetransversePos2 = xyz[transversecoordinate2] + posOffsetxyz[transversecoordinate2];
297 
298  // Space charge distortion could push the energy deposit beyond the wire plane (see
299  // issue #15131). Given that we don't have any subtlety in the simulation of this
300  // region, bringing the deposit exactly on the plane should be enough for the time
301  // being.
302  if (DriftDistance < 0.) DriftDistance = 0.;
303 
304  // Drift time in ns
305  double TDrift = DriftDistance * fRecipDriftVel[0];
306 
307  if (nplanes == 2 &&
308  driftcoordinate ==
309  0) { // special case for ArgoNeuT (Nplanes = 2 and drift direction = x): plane 0
310  // is the second wire plane
311  auto const pitch = wireReadoutGeom.PlanePitch(tpcGeo.ID(), 0, 1);
312  TDrift = ((DriftDistance - pitch) * fRecipDriftVel[0] + pitch * fRecipDriftVel[1]);
313  }
314  const int nIonizedElectrons =
316 
317  const double lifetimecorrection = TMath::Exp(TDrift / fLifetimeCorr_const);
318  const double energy = energyDeposit.Energy();
319 
320  // if we have no electrons (too small energy or too large recombination) we are done
321  // already here
322  if (nIonizedElectrons <= 0) {
323  MF_LOG_DEBUG("DriftElectronstoPlane")
324  << "step " // << energyDeposit << "\n"
325  << "No electrons drifted to readout, " << energy << " MeV lost.";
326  continue;
327  }
328 
329  // includes the effect of lifetime: lifetimecorrection = exp[-tdrift/tau]
330  const double nElectrons = nIonizedElectrons * lifetimecorrection;
331 
332  // Longitudinal & transverse diffusion sigma (cm)
333  double SqrtT = std::sqrt(TDrift);
334  double LDiffSig = SqrtT * fLDiff_const;
335  double TDiffSig = SqrtT * fTDiff_const;
336  double electronclsize = fElectronClusterSize;
337 
338  // Number of electron clusters.
339  int nClus = (int)std::ceil(nElectrons / electronclsize);
340  if (nClus < fMinNumberOfElCluster) {
341  electronclsize = nElectrons / fMinNumberOfElCluster;
342  if (electronclsize < 1.0) { electronclsize = 1.0; }
343  nClus = (int)std::ceil(nElectrons / electronclsize);
344  }
345 
346  // Empty and resize the electron-cluster vectors.
347  fLongDiff.clear();
348  fTransDiff1.clear();
349  fTransDiff2.clear();
350  fnElDiff.clear();
351  fnEnDiff.clear();
352  fLongDiff.resize(nClus);
353  fTransDiff1.resize(nClus);
354  fTransDiff2.resize(nClus);
355  fnElDiff.resize(nClus, electronclsize);
356  fnEnDiff.resize(nClus);
357 
358  // fix the number of electrons in the last cluster, that has a smaller size
359  fnElDiff.back() = nElectrons - (nClus - 1) * electronclsize;
360 
361  for (size_t xx = 0; xx < fnElDiff.size(); ++xx) {
362  if (nElectrons > 0)
363  fnEnDiff[xx] = energy / nElectrons * fnElDiff[xx];
364  else
365  fnEnDiff[xx] = 0.;
366  }
367 
368  // Smear drift times by longitudinal diffusion
369  if (LDiffSig > 0.0)
370  fRandGauss.fireArray(nClus, &fLongDiff[0], 0., LDiffSig);
371  else
372  fLongDiff.assign(nClus, 0.0);
373 
374  if (TDiffSig > 0.0) {
375  // Smear the coordinates in plane perpendicular to drift direction by the transverse diffusion
376  fRandGauss.fireArray(nClus, &fTransDiff1[0], avegagetransversePos1, TDiffSig);
377  fRandGauss.fireArray(nClus, &fTransDiff2[0], avegagetransversePos2, TDiffSig);
378  }
379  else {
380  fTransDiff1.assign(nClus, avegagetransversePos1);
381  fTransDiff2.assign(nClus, avegagetransversePos2);
382  }
383 
384  // make a collection of electrons for each plane
385  for (auto const& plane : wireReadoutGeom.Iterate<geo::PlaneGeo>(tpcGeo.ID())) {
386  auto const plane_center = plane.GetCenter();
387  fDriftClusterPos[driftcoordinate] = geo::vect::coord(plane_center, driftcoordinate);
388 
389  // Drift nClus electron clusters to the induction plane
390  for (int k = 0; k < nClus; ++k) {
391 
392  // Correct drift time for longitudinal diffusion and plane
393  double TDiff = TDrift + fLongDiff[k] * fRecipDriftVel[0];
394 
395  // Take into account different Efields between planes. Also take into account
396  // special case for ArgoNeuT (Nplanes = 2 and drift direction = x): plane 0 is
397  // the second wire plane
398  for (unsigned int ip = 0; ip < plane.ID().Plane; ++ip) {
399  auto const plane_center = wireReadoutGeom.Plane({tpcGeo.ID(), ip}).GetCenter();
400  auto const next_plane_center = wireReadoutGeom.Plane({tpcGeo.ID(), ip + 1}).GetCenter();
401  TDiff += (geo::vect::coord(next_plane_center, driftcoordinate) -
402  geo::vect::coord(plane_center, driftcoordinate)) *
403  fRecipDriftVel[(nplanes == 2 && driftcoordinate == 0) ? ip + 2 : ip + 1];
404  }
405 
406  fDriftClusterPos[transversecoordinate1] = fTransDiff1[k];
407  fDriftClusterPos[transversecoordinate2] = fTransDiff2[k];
408  auto const simTime = energyDeposit.Time();
410  SimDriftedElectronClusterCollection->emplace_back(
411  fnElDiff[k],
412  TDiff + simTime, // timing
413  geo::Point_t{mp.X(), mp.Y(), mp.Z()}, // mean position of the deposited energy
415  fDriftClusterPos[1],
416  fDriftClusterPos[2]}, // final position of the drifted cluster
417  geo::Point_t{
418  LDiffSig, TDiffSig, TDiffSig}, // Longitudinal (X) and transverse (Y,Z) diffusion
419  fnEnDiff[k], //deposited energy that originated this cluster
420  energyDeposit.TrackID());
421 
422  } // end loop over clusters
423  } // end loop over planes
424  } // for each sim::SimEnergyDeposit
425 
426  if (fStoreDriftedElectronClusters) event.put(std::move(SimDriftedElectronClusterCollection));
427  }
428 } // namespace detsim
429 
base_engine_t & createEngine(seed_t seed)
Double_t xx
Definition: macro.C:12
Utilities related to art service access.
contains objects relating to SimDriftedElectronCluster
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
bool out_of_bounds(geo::Vector_t const &offset)
auto coord(Vector &v, unsigned int n) noexcept
Returns an object to manage the coordinate n of a vector.
unsigned int NTPC(CryostatID const &cryoid=details::cryostat_zero) const
Returns the total number of TPCs in the specified cryostat.
Definition: GeometryCore.h:416
art::ServiceHandle< geo::Geometry const > fGeometry
Handle to the Geometry service.
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.cc:6
Geometry information for a single TPC.
Definition: TPCGeo.h:33
Detector simulation of raw signals on wires.
constexpr auto abs(T v)
Returns the absolute value of the argument.
cout<< "Opened file "<< fin<< " ixs= "<< ixs<< endl;if(ixs==0) hhh=(TH1F *) fff-> Get("h1")
Definition: AddMC.C:8
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
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:65
Utility function for testing if Space Charge offsets are out of bounds.
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
constexpr int to_int(Coordinate const coord) noexcept
Enumerate the possible plane projections.
Definition: geo_types.h:124
Data CalculateIonizationAndScintillation(detinfo::DetectorPropertiesData const &detProp, sim::SimEnergyDeposit const &edep) const
The data type to uniquely identify a TPC.
Definition: geo_types.h:306
An art service to assist in the distribution of guaranteed unique seeds to all engines within an art ...
void produce(art::Event &evt) override
int sign(double val)
Definition: UtilFunc.cxx:104
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
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
contains information for a single step in the detector simulation
DriftElectronstoPlane(fhicl::ParameterSet const &pset)
#define MF_LOG_DEBUG(id)
Definition: MVAAlg.h:12
Char_t n[5]
TCEvent evt
Definition: DataStructs.cxx:8
TPCGeo const & TPC(TPCID const &tpcid=details::tpc_zero) const
Returns the specified TPC.
Definition: GeometryCore.h:448
Float_t e
Definition: plot.C:35
TPCID const & ID() const
Returns the identifier of this TPC.
Definition: TPCGeo.h:147
art framework interface to geometry description
Event finding and building.
The data type to uniquely identify a cryostat.
Definition: geo_types.h:187