LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
LArVoxelReadoutGeometry.cxx
Go to the documentation of this file.
1 
9 
10 // C/C++ libraries
11 #include <vector>
12 #include <cmath>
13 #include <map>
14 #include <memory> // std::unique_ptr()
15 #include <sstream> // std::ostringstream
16 #include <iostream> // std::endl
17 
18 // Framework includes
21 
22 // LArSoft includes
26 
27 // G4 includes
28 #include "Geant4/G4PVPlacement.hh"
29 #include "Geant4/G4PVReplica.hh"
30 #include "Geant4/G4LogicalVolume.hh"
31 #include "Geant4/G4VisAttributes.hh"
32 #include "Geant4/G4VSolid.hh"
33 #include "Geant4/G4Box.hh"
34 #include "Geant4/G4Tubs.hh"
35 #include "Geant4/G4ThreeVector.hh"
36 #include "Geant4/G4RotationMatrix.hh"
37 #include "Geant4/G4VSensitiveDetector.hh"
38 #include "Geant4/G4SDManager.hh"
39 #include "Geant4/G4Material.hh"
40 #include "Geant4/G4Point3D.hh"
41 #include "Geant4/globals.hh"
42 
43 
44 constexpr bool DisableVoxelCaching = false;
45 
46 #include <typeinfo>
47 
48 #ifdef __GNUG__
49 #include <cxxabi.h>
50 
51 template <typename T>
52 std::string demangle_cxx_symbol(const T& obj) {
53  int status = -4; // some arbitrary value to eliminate the compiler warning
54 
55  std::string name = typeid(obj).name();
56 
57  // __cxa_demangle() allocates with malloc(), so we ask unique_ptr to
58  // deallocate by free()
59  std::unique_ptr<char, void(*)(void*)> res
60  { abi::__cxa_demangle(name.c_str(), NULL, NULL, &status), std::free };
61 
62  return (status == 0)? res.get(): name;
63 } // demangle_cxx_symbol()
64 
65 #else
66 
67 template <typename T>
68 std::string demangle_cxx_symbol(const T& obj) { return typeid(obj).name(); }
69 
70 #endif
71 
72 
73 template <class STREAM>
75  (STREAM& out, const G4VPhysicalVolume& PV, std::string indentstr = "")
76 {
77 
78  const G4ThreeVector& pos = PV.GetTranslation();
79  const G4LogicalVolume* LV = PV.GetLogicalVolume();
80 
81  int count = 1;
82  out << indentstr << PV.GetName() << " [" << demangle_cxx_symbol(PV) << "]"
83  << " at (" << pos.x() << ", " << pos.y() << ", " << pos.z() << ")";
84  if (LV) {
85  out << ", a " << LV->GetName();
86 
87  const G4VSolid* Solid = LV->GetSolid();
88  if (Solid) {
89  out << " shaped as " << Solid->GetName();
90  const G4Box* pBox;
91 
92  try { pBox = dynamic_cast<const G4Box*>(Solid); }
93  catch (std::bad_cast&) { pBox = nullptr; }
94 
95  if (pBox) {
96  out << ", a (" << (2.*pBox->GetXHalfLength())
97  << " x " << (2.*pBox->GetYHalfLength())
98  << " x " << (2.*pBox->GetZHalfLength()) << ") cm box";
99  } // if box
100  else {
101  out << ", a " << demangle_cxx_symbol(*Solid);
102  }
103  }
104  else {
105  out << " with no shape (!?!)";
106  }
107 
108  G4int nDaughters = LV->GetNoDaughters();
109  if (nDaughters > 0) {
110  out << " with " << nDaughters << " subvolumes:\n";
111  for (G4int i = 0; i < nDaughters; ++i) {
112  count += DumpPhysicalVolume(out, *LV->GetDaughter(i), indentstr + " ");
113  }
114  }
115  else out << '\n';
116  }
117  else out << " with no logical volume (!?)\n";
118 
119  return count;
120 } // DumpPhysicalVolume()
121 
122 
123 namespace larg4 {
124 
125  // Constructor and destructor.
127  (const G4String name, Setup_t const& setupData)
128  : G4VUserParallelWorld(name)
129  , fReadoutSetupData(setupData.readoutSetup)
130  {
132  std::unique_ptr<G4UserLimits> fStepLimit(new G4UserLimits(ios->StepSizeLimit()));
133  }
134 
137 
138  {
139  // With a "parallel geometry", Geant4 has already created a clone
140  // of the world physical and logical volumes. We want to place
141  // the LAr TPC, and only the LAr TPC, within this cloned world.
142 
143  // Get the parallel world physical volume.
144  G4VPhysicalVolume* parallelPhysical = GetWorld();
145 
146  // Now we want to place a parallel LAr TPC volume within this
147  // parallel world volume. We only want to duplicate the LAr TPC
148  // volume; any other volumes in the "official" geometry are going
149  // to be ignored. Our parallel world will consist only of the
150  // world volume and the LAr TPC volume.
151 
152  G4Transform3D worldTransform;
153 
154  // first get the volDetEnclosure
155  std::string daughterName("volDetEnclosure");
156  G4Transform3D detEnclosureTransform;
157  G4VPhysicalVolume* detEnclosureVolume = this->FindNestedVolume(g4b::DetectorConstruction::GetWorld(),
158  worldTransform,
159  detEnclosureTransform,
160  daughterName,
161  0);
162 
163 
164 
165  // we keep track of all the voxel boxes with different geometries we create
166  struct VoxelSpecs_t {
167  double w, h, d;
168  unsigned int nw, nh, nd;
169 
171  bool operator< (const VoxelSpecs_t& vs) const
172  {
173  if (w < vs.w) return true;
174  if (w > vs.w) return false;
175  if (h < vs.h) return true;
176  if (h > vs.h) return false;
177  if (d < vs.d) return true;
178  if (d > vs.d) return false;
179  if (nw < vs.nw) return true;
180  if (nw > vs.nw) return false;
181  if (nh < vs.nh) return true;
182  if (nh > vs.nh) return false;
183  if (nd < vs.nd) return true;
184  return false;
185  } // operator<
186  }; // VoxelSpecs_t
187 
188  // TODO we don't need to cache the cell any more: a simple map will suffice
189  struct VoxelVolumes_t {
190  G4LogicalVolume* pBox;
191  G4LogicalVolume* pCell;
192 
193  VoxelVolumes_t
194  (G4LogicalVolume* box = nullptr, G4LogicalVolume* cell = nullptr):
195  pBox(box), pCell(cell) {}
196  }; // VoxelVolumes_t
197 
198 
199  // Define the sensitive detector for the voxel readout. This class
200  // routines will be called every time a particle deposits energy in
201  // a voxel that overlaps the LAr TPC.
202  LArVoxelReadout* larVoxelReadout = new LArVoxelReadout("LArVoxelSD");
203  larVoxelReadout->Setup(fReadoutSetupData);
204  if ((fGeo->Ncryostats() == 1) && (fGeo->Cryostat(0).NTPC() == 1))
205  larVoxelReadout->SetSingleTPC(0, 0); // just one TPC in the detector...
206 
207  // Tell Geant4's sensitive-detector manager that the voxel SD
208  // class exists.
209  G4SDManager* sdManager = G4SDManager::GetSDMpointer();
210  sdManager->AddNewDetector(larVoxelReadout);
211 
212  // hope hashing doubles is reliable...
213  typedef std::map<VoxelSpecs_t, VoxelVolumes_t> VoxelCache_t;
214  VoxelCache_t VoxelCache;
215 
216  for(unsigned int c = 0; c < fGeo->Ncryostats(); ++c){
217 
218  // next get the cryostat
219  daughterName = "volCryostat";
220  G4Transform3D cryostatTransform;
221  G4VPhysicalVolume* cryostatVolume = this->FindNestedVolume(detEnclosureVolume,
222  detEnclosureTransform,
223  cryostatTransform,
224  daughterName,
225  c);
226 
227  for(unsigned int t = 0; t < fGeo->Cryostat(c).NTPC(); ++t){
228 
229  // now for the TPC
230  daughterName = "volTPC";
231  G4Transform3D tpcTransform;
232  G4VPhysicalVolume* tpcVolume = this->FindNestedVolume(cryostatVolume,
233  cryostatTransform,
234  tpcTransform,
235  daughterName,
236  t);
237 
238  daughterName = "volTPCActive";
239  G4Transform3D transform;
240  G4VPhysicalVolume* larTPCPhysical = this->FindNestedVolume(tpcVolume,
241  tpcTransform,
242  transform,
243  daughterName,
244  t);
245 
246  // Get the LAr TPC volume, and its shape.
247  G4LogicalVolume* larTPCLogical = larTPCPhysical->GetLogicalVolume();
248  larTPCLogical->SetUserLimits(fStepLimit.get());
249 
250  G4VSolid* larTPCShape = larTPCLogical->GetSolid();
251 
252  // We're not going to exactly duplicate the LAr TPC in our
253  // parallel world. We're going to construct a box of voxels.
254  // What should the size and position of that box be?
255 
256  // To get our first hints, we need the overall dimensions of a
257  // "bounding box" that contains the shape. For now, we'll allow
258  // two possible shapes: a box (by the far the most likely) and a
259  // cylinder (for bizarre future detectors that I know nothing
260  // about).
261 
262  G4double larTPCHalfXLength = 0;
263  G4double larTPCHalfYLength = 0;
264  G4double larTPCHalfZLength = 0;
265  G4Box* tpcBox = dynamic_cast< G4Box* >( larTPCShape );
266  if ( tpcBox != 0 ){
267  larTPCHalfXLength = tpcBox->GetXHalfLength();
268  larTPCHalfYLength = tpcBox->GetYHalfLength();
269  larTPCHalfZLength = tpcBox->GetZHalfLength();
270  }
271  else{
272  // It's not a box. Try a cylinder.
273  G4Tubs* tube = dynamic_cast< G4Tubs* >( larTPCShape );
274  if ( tube != 0 ){
275  larTPCHalfXLength = tube->GetOuterRadius();
276  larTPCHalfYLength = tube->GetOuterRadius();
277  larTPCHalfZLength = tube->GetZHalfLength();
278  }
279  else{
280  throw cet::exception("LArVoxelReadoutGeometry") << "Unknown shape in readout geometry"
281  << "The LAr TPC volume is not a box or a tube. "
282  << "This routine can't convert any other shapes.\n";
283  }
284  }
285 
286  LOG_DEBUG("LArVoxelReadoutGeometry") << ": larTPCHalfXLength=" << larTPCHalfXLength
287  << ": larTPCHalfYLength=" << larTPCHalfYLength
288  << ": larTPCHalfZLength=" << larTPCHalfZLength;
289 
290  // Get some constants from the LAr voxel information object.
291  // Remember, ROOT uses cm.
293  G4double voxelSizeX = lvc->VoxelSizeX() * CLHEP::cm;
294  G4double voxelSizeY = lvc->VoxelSizeY() * CLHEP::cm;
295  G4double voxelSizeZ = lvc->VoxelSizeZ() * CLHEP::cm;
296  G4double voxelOffsetX = lvc->VoxelOffsetX() * CLHEP::cm;
297  G4double voxelOffsetY = lvc->VoxelOffsetY() * CLHEP::cm;
298  G4double voxelOffsetZ = lvc->VoxelOffsetZ() * CLHEP::cm;
299 
300  LOG_DEBUG("LArVoxelReadoutGeometry") << ": voxelSizeX=" << voxelSizeX
301  << ", voxelSizeY=" << voxelSizeY
302  << ", voxelSizeZ=" << voxelSizeZ;
303 
304  LOG_DEBUG("LArVoxelReadoutGeometry") << ": voxelOffsetX=" << voxelOffsetX
305  << ", voxelOffsetY=" << voxelOffsetY
306  << ", voxelOffsetZ=" << voxelOffsetZ;
307 
308  // We want our voxelization region to be an integer multiple of
309  // the voxel sizes in all directions; if we didn't do this, we
310  // might get into trouble when we start playing with replicas.
311  // Compute the the dimensions of our voxelization to be about the
312  // size of the LAr TPC region, adjusted to be an integer number of
313  // voxels in all directions.
314 
315  G4double numberXvoxels = 2.*larTPCHalfXLength / voxelSizeX;
316  G4double numberYvoxels = 2.*larTPCHalfYLength / voxelSizeY;
317  G4double numberZvoxels = 2.*larTPCHalfZLength / voxelSizeZ;
318  numberXvoxels = trunc(numberXvoxels) + 1.;
319  numberYvoxels = trunc(numberYvoxels) + 1.;
320  numberZvoxels = trunc(numberZvoxels) + 1.;
321 
322  LOG_DEBUG("LArVoxelReadoutGeometry") << "Active volume of cryo #" << c << " TPC #" << t
323  << " will be split in " << numberXvoxels << " x " << numberYvoxels << " x " << numberZvoxels
324  << " = " << (numberXvoxels * numberYvoxels * numberZvoxels)
325  << " voxels of size " << voxelSizeX << " x " << voxelSizeY << " x " << voxelSizeZ << " cm"
326  ;
327 
328  VoxelSpecs_t VoxelSpecs{
329  /* w */ 2.*larTPCHalfXLength,
330  /* h */ 2.*larTPCHalfYLength,
331  /* d */ 2.*larTPCHalfZLength,
332  /* nw */ (unsigned int) numberXvoxels,
333  /* nh */ (unsigned int) numberYvoxels,
334  /* nd */ (unsigned int) numberZvoxels
335  };
336 
338  VoxelCache.end(): VoxelCache.find(VoxelSpecs);
339  if (iVoxelVol == VoxelCache.end()) {
340  LOG_DEBUG("LArVoxelReadoutGeometry") << "Creating a new voxel volume "
341  << VoxelSpecs.w << " x " << VoxelSpecs.h << " x " << VoxelSpecs.d
342  << " cm, "
343  << VoxelSpecs.nw << " x " << VoxelSpecs.nh << " x " << VoxelSpecs.nd
344  << " voxels";
345 
346  G4double voxelBoxHalfX = numberXvoxels * voxelSizeX / 2.;
347  G4double voxelBoxHalfY = numberYvoxels * voxelSizeY / 2.;
348  G4double voxelBoxHalfZ = numberZvoxels * voxelSizeZ / 2.;
349 
350  LOG_DEBUG("LArVoxelReadoutGeometry") << ": voxelBoxHalfX=" << voxelBoxHalfX
351  << ", voxelBoxHalfY=" << voxelBoxHalfY
352  << ", voxelBoxHalfZ=" << voxelBoxHalfZ;
353 
354  // Now we have a box that will include an integer number of voxels
355  // in each direction. Note that the material is irrelevant for a
356  // "parallel world."
357  G4Box* voxelBox = new G4Box("VoxelBox",voxelBoxHalfX,voxelBoxHalfY,voxelBoxHalfZ);
358  G4LogicalVolume* voxelBoxLogical = new G4LogicalVolume(voxelBox,
359  0,
360  "VoxelizationLogicalVolume" );
361 
362  // If we generate an event display within Geant4, we won't want to
363  // see this box.
364  G4VisAttributes* invisible = new G4VisAttributes();
365  invisible->SetVisibility(false);
366  voxelBoxLogical->SetVisAttributes(invisible);
367 
368  //LOG_DEBUG("LArVoxelReadoutGeometry") << ": transform = \n";
369  //for ( G4int i = 0; i < 3; ++i ){
370  // for ( G4int j = 0; j < 4; ++j ){
371  // LOG_DEBUG("LArVoxelReadoutGeometry") << transform[i][j] << " ";
372  // }
373  // LOG_DEBUG("LArVoxelReadoutGeometry") << "\n";
374  //}
375 
376  // Now we've fill our "box of voxels" with the voxels themselves.
377  // We'll do this by sub-dividing the volume in x, then y, then z.
378 
379  // Create an "x-slice".
380  G4Box* xSlice = new G4Box("xSlice",voxelSizeX/2.,voxelBoxHalfY,voxelBoxHalfZ);
381  G4LogicalVolume* xSliceLogical = new G4LogicalVolume( xSlice, 0, "xLArVoxelSlice" );
382  xSliceLogical->SetVisAttributes(invisible);
383 
384  // Use replication to slice up the "box of voxels" along the x-axis.
385  new G4PVReplica( "VoxelSlicesInX",
386  xSliceLogical,
387  voxelBoxLogical,
388  kXAxis,
389  G4int( numberXvoxels ),
390  voxelSizeX );
391 
392  // Now do the same thing, dividing that x-slice along the y-axis.
393  G4Box* ySlice = new G4Box("ySlice",voxelSizeX/2.,voxelSizeY/2., voxelBoxHalfZ);
394  G4LogicalVolume* ySliceLogical = new G4LogicalVolume( ySlice, 0, "yLArVoxelSlice" );
395  ySliceLogical->SetVisAttributes(invisible);
396  new G4PVReplica( "VoxelSlicesInY",
397  ySliceLogical,
398  xSliceLogical,
399  kYAxis,
400  G4int( numberYvoxels ),
401  voxelSizeY );
402 
403  // Now divide the y-slice along the z-axis, giving us our actual voxels.
404  G4Box* zSlice = new G4Box("zSlice",voxelSizeX/2.,voxelSizeY/2., voxelSizeZ/2.);
405  G4LogicalVolume* voxelLogical = new G4LogicalVolume( zSlice, 0, "LArVoxel" );
406  voxelLogical->SetVisAttributes(invisible);
407  new G4PVReplica( "LArVoxel",
408  voxelLogical,
409  ySliceLogical,
410  kZAxis,
411  G4int( numberZvoxels ),
412  voxelSizeZ );
413 
414  iVoxelVol = VoxelCache.emplace(VoxelSpecs,
415  VoxelVolumes_t(voxelBoxLogical, voxelLogical)
416  ).first; // iterator to the inserted element
417 
418  // Set the sensitive detector of this LAr TPC (logical) volume
419  // to be the voxel readout.
420  voxelLogical->SetSensitiveDetector(larVoxelReadout);
421 
422  } // if not cached yet
423  G4LogicalVolume* voxelBoxLogical = iVoxelVol->second.pBox;
424 
425  // We have a "box of voxels" that's the right dimensions, but we
426  // have to know exactly where to put it. The user has the option
427  // to offset the voxel co-ordinate system. We want to place our
428  // box so the edges of our voxels align with that co-ordinate
429  // system. In effect, we want to offset our "box of voxels" by
430  // the user's offsets, modulo the size of the voxel in each
431  // direction.
432 
433  G4double offsetInVoxelsX = voxelOffsetX / voxelSizeX;
434  G4double offsetInVoxelsY = voxelOffsetY / voxelSizeY;
435  G4double offsetInVoxelsZ = voxelOffsetZ / voxelSizeZ;
436  G4double fractionOffsetX = offsetInVoxelsX - trunc(offsetInVoxelsX);
437  G4double fractionOffsetY = offsetInVoxelsY - trunc(offsetInVoxelsY);
438  G4double fractionOffsetZ = offsetInVoxelsZ - trunc(offsetInVoxelsZ);
439  G4double offsetX = fractionOffsetX * voxelSizeX;
440  G4double offsetY = fractionOffsetY * voxelSizeY;
441  G4double offsetZ = fractionOffsetZ * voxelSizeZ;
442 
443  // Now we know how much to offset the "box of voxels". Include
444  // that in the transformation of the co-ordinates from world
445  // volume to LAr TPC volume.
446  transform = G4Translate3D( offsetX, offsetY, offsetZ ) * transform;
447 
448  LOG_DEBUG("LArVoxelReadoutGeometry") << ": offsetX=" << offsetX
449  << ", offsetY=" << offsetY
450  << ", offsetZ=" << offsetZ;
451 
452  // suffix representing this TPC
453  std::ostringstream nums;
454  nums << "_Cryostat" << c << "_TPC" << t;
455 
456  // Place the box of voxels, with the accumulated transformations
457  // computed above.
458  new G4PVPlacementInTPC( transform,
459  "VoxelizationPhysicalVolume" + nums.str(),
460  voxelBoxLogical,
461  parallelPhysical,
462  false, // Only one volume
463  0, // Copy number
464  false, // No surface check
465  { (unsigned short int) c, (unsigned short int) t } // TPC ID
466  );
467 
468  } // end loop over tpcs
469  } // end loop over cryostats
470 
471  if (mf::isDebugEnabled()) {
472  LOG_DEBUG("LArVoxelReadoutGeometryDump") << "Dump of voxelized volume";
473  {
474  mf::LogDebug log("LArVoxelReadoutGeometryDump");
475  DumpPhysicalVolume(log, *parallelPhysical);
476  }
477  LOG_DEBUG("LArVoxelReadoutGeometryDump")
478  << "End of dump of voxelized volume";
479  }
480  return;
481  }
482 
483  //---------------------------------------------------------------
485  // We know the ordering of the volumes in the Geometry,
486  // see https://cdcvs.fnal.gov/redmine/projects/larsoftsvn/wiki/Geometry
487  // Make use of that knowledge to efficiently get the desired volumes and
488  // their total transforms.
489  // the daughterTransform is the total transform to the world coordinate system
490  G4VPhysicalVolume* LArVoxelReadoutGeometry::FindNestedVolume(G4VPhysicalVolume* mother,
491  G4Transform3D& motherTransform,
492  G4Transform3D& daughterTransform,
493  std::string& daughterName,
494  unsigned int expectedNum)
495  {
496  G4LogicalVolume* logicalVolume = mother->GetLogicalVolume();
497  G4int numberDaughters = logicalVolume->GetNoDaughters();
498  for ( G4int i = 0; i != numberDaughters; ++i ){
499  G4VPhysicalVolume* d = logicalVolume->GetDaughter(i);
500 
501  // LOG_DEBUG("LArVoxelReadoutGeometry") << d->GetName() << ":" << mother->GetName();
502 
503  if(d->GetName().contains(daughterName)){
504 
505  // check that this cryostat is the requested one using fCryostat
506  G4ThreeVector translation = d->GetObjectTranslation();
507  G4RotationMatrix rotation = d->GetObjectRotationValue();
508  G4Transform3D transform(rotation, translation);
509  daughterTransform = motherTransform * transform;
510 
511 
512  // take the origin of the volume and transform it to
513  // world coordinated
514  G4Point3D local(0., 0., 0.);
515  G4Point3D world = daughterTransform * local;
516 
517 
518  LOG_DEBUG("LArVoxelReadoutGeometry") << "current " << daughterName
519  << " origin is at ("
520  << world.x() / CLHEP::cm << ","
521  << world.y() / CLHEP::cm << ","
522  << world.z() / CLHEP::cm << ")";
523 
524  // we don't bother with the cryostat number when calling Geometry::PositionToTPC
525  // because we know we have already started off with the correct cryostat volume
526  // G4 uses mm, we want cm
527  double worldPos[3] = { world.x() / CLHEP::cm, world.y() / CLHEP::cm, world.z() / CLHEP::cm };
528  unsigned int daughterNum = 0;
529  unsigned int extra = 0;
530  if(daughterName.compare("volCryostat") == 0)
531  fGeo->PositionToCryostat(worldPos, daughterNum);
532  else if(daughterName.compare("volTPC") == 0)
533  fGeo->PositionToTPC(worldPos, daughterNum, extra);
534  else if(daughterName.compare("volTPCActive") == 0 ||
535  daughterName.compare("volDetEnclosure") == 0){
536  // for either of these volumes, we know there is only 1 in the mother volume
537  LOG_DEBUG("LArVoxelReadoutGeometry") << "found the desired " << daughterName;
538  return d;
539  }
540 
541  // if we found the desired volume, stop looking
542  if(daughterNum == expectedNum){
543  LOG_DEBUG("LArVoxelReadoutGeometry") << "found the desired " << daughterName;
544  return d;
545  }
546  }// end if the volume has the right name
547  }// end loop over volumes
548 
549  throw cet::exception("LArVoxelReadoutGeometry") << "could not find the desired "
550  << daughterName
551  << " to make LArVoxelReadoutGeometry\n";
552 
553  return 0;
554  }
555 
556 
557 } // namespace larg4
Build Geant4 geometry from GDML.
Collection of all it takes to set up this object.
double VoxelSizeX() const
Access to voxel dimensions and offsets.
int DumpPhysicalVolume(STREAM &out, const G4VPhysicalVolume &PV, std::string indentstr="")
CryostatGeo const & PositionToCryostat(geo::Point_t const &point) const
Returns the cryostat at specified location.
larg4::LArVoxelReadout::Setup_t fReadoutSetupData
Data for LArVoxelReadout setup.
bool operator<(CryostatID const &a, CryostatID const &b)
Order cryostats with increasing ID.
Definition: geo_types.h:413
intermediate_table::iterator iterator
Encapsulates calculation of LArVoxelID and LArVoxel parameters.
static G4VPhysicalVolume * GetWorld()
art::ServiceHandle< geo::Geometry > fGeo
Handle to the geometry.
Geant4 interface.
void Setup(Setup_t const &setupData)
Reads all the configuration elements from setupData
larg4::LArVoxelReadout::Setup_t readoutSetup
Set up data for LArVoxelReadout.
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
std::string demangle_cxx_symbol(const T &obj)
geo::TPCGeo const & PositionToTPC(geo::Point_t const &point) const
Returns the TPC at specified location.
Define the "parallel" geometry that&#39;s seen by the LAr Voxels.
Float_t voxelSizeY
Definition: plot.C:41
void SetSingleTPC(unsigned int cryostat, unsigned int tpc)
Associates this readout to one specific TPC.
std::unique_ptr< G4UserLimits > fStepLimit
constexpr bool DisableVoxelCaching
Float_t voxelSizeZ
Definition: plot.C:41
LArVoxelReadoutGeometry(const G4String name, Setup_t const &setupData)
Constructor: sets up all its LArVoxelReadout instances.
static IonizationAndScintillation * Instance()
Float_t d
Definition: plot.C:237
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:155
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
G4PVPlacementWithID< TPCID_t > G4PVPlacementInTPC
A physical volume with a TPC ID.
bool isDebugEnabled()
double VoxelOffsetX() const
G4VPhysicalVolume * FindNestedVolume(G4VPhysicalVolume *mother, G4Transform3D &motherTransform, G4Transform3D &daughterTransform, std::string &daughterName, unsigned int expectedNum)
Float_t voxelSizeX
Definition: plot.C:41
double VoxelOffsetZ() const
A Geant4 sensitive detector that accumulates voxel information.
#define LOG_DEBUG(id)
Transports energy depositions from GEANT4 to TPC channels.
Float_t w
Definition: plot.C:23
double VoxelOffsetY() const
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33