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" 55 std::string name =
typeid(obj).name();
59 std::unique_ptr<char, void(*)(void*)> res
60 { abi::__cxa_demangle(name.c_str(), NULL, NULL, &status), std::free };
62 return (status == 0)? res.get(): name;
73 template <
class STREAM>
75 (STREAM& out,
const G4VPhysicalVolume& PV, std::string indentstr =
"")
78 const G4ThreeVector& pos = PV.GetTranslation();
79 const G4LogicalVolume* LV = PV.GetLogicalVolume();
83 <<
" at (" << pos.x() <<
", " << pos.y() <<
", " << pos.z() <<
")";
85 out <<
", a " << LV->GetName();
87 const G4VSolid* Solid = LV->GetSolid();
89 out <<
" shaped as " << Solid->GetName();
92 try { pBox =
dynamic_cast<const G4Box*
>(Solid); }
93 catch (std::bad_cast&) { pBox =
nullptr; }
96 out <<
", a (" << (2.*pBox->GetXHalfLength())
97 <<
" x " << (2.*pBox->GetYHalfLength())
98 <<
" x " << (2.*pBox->GetZHalfLength()) <<
") cm box";
105 out <<
" with no shape (!?!)";
108 G4int nDaughters = LV->GetNoDaughters();
109 if (nDaughters > 0) {
110 out <<
" with " << nDaughters <<
" subvolumes:\n";
111 for (G4int i = 0; i < nDaughters; ++i) {
117 else out <<
" with no logical volume (!?)\n";
128 : G4VUserParallelWorld(name)
132 std::unique_ptr<G4UserLimits> fStepLimit(
new G4UserLimits(ios->
StepSizeLimit()));
144 G4VPhysicalVolume* parallelPhysical = GetWorld();
152 G4Transform3D worldTransform;
155 std::string daughterName(
"volDetEnclosure");
156 G4Transform3D detEnclosureTransform;
159 detEnclosureTransform,
166 struct VoxelSpecs_t {
168 unsigned int nw, nh, nd;
171 bool operator< (
const VoxelSpecs_t& vs)
const 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;
189 struct VoxelVolumes_t {
190 G4LogicalVolume* pBox;
191 G4LogicalVolume* pCell;
194 (G4LogicalVolume* box =
nullptr, G4LogicalVolume* cell =
nullptr):
195 pBox(box), pCell(cell) {}
209 G4SDManager* sdManager = G4SDManager::GetSDMpointer();
210 sdManager->AddNewDetector(larVoxelReadout);
213 typedef std::map<VoxelSpecs_t, VoxelVolumes_t> VoxelCache_t;
214 VoxelCache_t VoxelCache;
219 daughterName =
"volCryostat";
220 G4Transform3D cryostatTransform;
221 G4VPhysicalVolume* cryostatVolume = this->
FindNestedVolume(detEnclosureVolume,
222 detEnclosureTransform,
230 daughterName =
"volTPC";
231 G4Transform3D tpcTransform;
238 daughterName =
"volTPCActive";
239 G4Transform3D transform;
247 G4LogicalVolume* larTPCLogical = larTPCPhysical->GetLogicalVolume();
248 larTPCLogical->SetUserLimits(
fStepLimit.get());
250 G4VSolid* larTPCShape = larTPCLogical->GetSolid();
262 G4double larTPCHalfXLength = 0;
263 G4double larTPCHalfYLength = 0;
264 G4double larTPCHalfZLength = 0;
265 G4Box* tpcBox =
dynamic_cast< G4Box*
>( larTPCShape );
267 larTPCHalfXLength = tpcBox->GetXHalfLength();
268 larTPCHalfYLength = tpcBox->GetYHalfLength();
269 larTPCHalfZLength = tpcBox->GetZHalfLength();
273 G4Tubs* tube =
dynamic_cast< G4Tubs*
>( larTPCShape );
275 larTPCHalfXLength = tube->GetOuterRadius();
276 larTPCHalfYLength = tube->GetOuterRadius();
277 larTPCHalfZLength = tube->GetZHalfLength();
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";
286 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": larTPCHalfXLength=" << larTPCHalfXLength
287 <<
": larTPCHalfYLength=" << larTPCHalfYLength
288 <<
": larTPCHalfZLength=" << larTPCHalfZLength;
296 G4double voxelOffsetX = lvc->
VoxelOffsetX() * CLHEP::cm;
297 G4double voxelOffsetY = lvc->
VoxelOffsetY() * CLHEP::cm;
298 G4double voxelOffsetZ = lvc->
VoxelOffsetZ() * CLHEP::cm;
300 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": voxelSizeX=" << voxelSizeX
301 <<
", voxelSizeY=" << voxelSizeY
304 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": voxelOffsetX=" << voxelOffsetX
305 <<
", voxelOffsetY=" << voxelOffsetY
306 <<
", voxelOffsetZ=" << voxelOffsetZ;
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.;
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" 328 VoxelSpecs_t VoxelSpecs{
329 2.*larTPCHalfXLength,
330 2.*larTPCHalfYLength,
331 2.*larTPCHalfZLength,
332 (
unsigned int) numberXvoxels,
333 (
unsigned int) numberYvoxels,
334 (
unsigned int) numberZvoxels
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
343 << VoxelSpecs.nw <<
" x " << VoxelSpecs.nh <<
" x " << VoxelSpecs.nd
346 G4double voxelBoxHalfX = numberXvoxels * voxelSizeX / 2.;
347 G4double voxelBoxHalfY = numberYvoxels * voxelSizeY / 2.;
348 G4double voxelBoxHalfZ = numberZvoxels * voxelSizeZ / 2.;
350 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": voxelBoxHalfX=" << voxelBoxHalfX
351 <<
", voxelBoxHalfY=" << voxelBoxHalfY
352 <<
", voxelBoxHalfZ=" << voxelBoxHalfZ;
357 G4Box* voxelBox =
new G4Box(
"VoxelBox",voxelBoxHalfX,voxelBoxHalfY,voxelBoxHalfZ);
358 G4LogicalVolume* voxelBoxLogical =
new G4LogicalVolume(voxelBox,
360 "VoxelizationLogicalVolume" );
364 G4VisAttributes* invisible =
new G4VisAttributes();
365 invisible->SetVisibility(
false);
366 voxelBoxLogical->SetVisAttributes(invisible);
380 G4Box* xSlice =
new G4Box(
"xSlice",voxelSizeX/2.,voxelBoxHalfY,voxelBoxHalfZ);
381 G4LogicalVolume* xSliceLogical =
new G4LogicalVolume( xSlice, 0,
"xLArVoxelSlice" );
382 xSliceLogical->SetVisAttributes(invisible);
385 new G4PVReplica(
"VoxelSlicesInX",
389 G4int( numberXvoxels ),
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",
400 G4int( numberYvoxels ),
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",
411 G4int( numberZvoxels ),
414 iVoxelVol = VoxelCache.emplace(VoxelSpecs,
415 VoxelVolumes_t(voxelBoxLogical, voxelLogical)
420 voxelLogical->SetSensitiveDetector(larVoxelReadout);
423 G4LogicalVolume* voxelBoxLogical = iVoxelVol->second.pBox;
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;
446 transform = G4Translate3D( offsetX, offsetY, offsetZ ) * transform;
448 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": offsetX=" << offsetX
449 <<
", offsetY=" << offsetY
450 <<
", offsetZ=" << offsetZ;
453 std::ostringstream nums;
454 nums <<
"_Cryostat" << c <<
"_TPC" << t;
459 "VoxelizationPhysicalVolume" + nums.str(),
465 { (
unsigned short int) c, (
unsigned short int) t }
472 LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"Dump of voxelized volume";
478 <<
"End of dump of voxelized volume";
491 G4Transform3D& motherTransform,
492 G4Transform3D& daughterTransform,
493 std::string& daughterName,
494 unsigned int expectedNum)
496 G4LogicalVolume* logicalVolume = mother->GetLogicalVolume();
497 G4int numberDaughters = logicalVolume->GetNoDaughters();
498 for ( G4int i = 0; i != numberDaughters; ++i ){
499 G4VPhysicalVolume*
d = logicalVolume->GetDaughter(i);
503 if(d->GetName().contains(daughterName)){
506 G4ThreeVector translation = d->GetObjectTranslation();
507 G4RotationMatrix rotation = d->GetObjectRotationValue();
508 G4Transform3D transform(rotation, translation);
509 daughterTransform = motherTransform * transform;
514 G4Point3D local(0., 0., 0.);
515 G4Point3D world = daughterTransform * local;
518 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
"current " << daughterName
520 << world.x() / CLHEP::cm <<
"," 521 << world.y() / CLHEP::cm <<
"," 522 << world.z() / CLHEP::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)
532 else if(daughterName.compare(
"volTPC") == 0)
534 else if(daughterName.compare(
"volTPCActive") == 0 ||
535 daughterName.compare(
"volDetEnclosure") == 0){
537 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
"found the desired " << daughterName;
542 if(daughterNum == expectedNum){
543 LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
"found the desired " << daughterName;
549 throw cet::exception(
"LArVoxelReadoutGeometry") <<
"could not find the desired " 551 <<
" to make LArVoxelReadoutGeometry\n";
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.
Encapsulates calculation of LArVoxelID and LArVoxel parameters.
static G4VPhysicalVolume * GetWorld()
art::ServiceHandle< geo::Geometry > fGeo
Handle to the geometry.
double VoxelSizeY() const
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's seen by the LAr Voxels.
double VoxelSizeZ() const
void SetSingleTPC(unsigned int cryostat, unsigned int tpc)
Associates this readout to one specific TPC.
std::unique_ptr< G4UserLimits > fStepLimit
constexpr bool DisableVoxelCaching
LArVoxelReadoutGeometry(const G4String name, Setup_t const &setupData)
Constructor: sets up all its LArVoxelReadout instances.
static IonizationAndScintillation * Instance()
unsigned int NTPC() const
Number of TPCs in this cryostat.
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
G4PVPlacementWithID< TPCID_t > G4PVPlacementInTPC
A physical volume with a TPC ID.
double VoxelOffsetX() const
G4VPhysicalVolume * FindNestedVolume(G4VPhysicalVolume *mother, G4Transform3D &motherTransform, G4Transform3D &daughterTransform, std::string &daughterName, unsigned int expectedNum)
double VoxelOffsetZ() const
A Geant4 sensitive detector that accumulates voxel information.
Transports energy depositions from GEANT4 to TPC channels.
double StepSizeLimit() const
double VoxelOffsetY() const
cet::coded_exception< error, detail::translate > exception