19 #include "cetlib_except/demangle.h" 29 #include "Geant4/G4Box.hh" 30 #include "Geant4/G4LogicalVolume.hh" 31 #include "Geant4/G4PVReplica.hh" 32 #include "Geant4/G4Point3D.hh" 33 #include "Geant4/G4RotationMatrix.hh" 34 #include "Geant4/G4SDManager.hh" 35 #include "Geant4/G4ThreeVector.hh" 36 #include "Geant4/G4Tubs.hh" 37 #include "Geant4/G4VSolid.hh" 38 #include "Geant4/G4VisAttributes.hh" 44 std::string demangle_cxx_symbol(
const T& obj)
46 return cet::demangle_symbol(
typeid(obj).name());
49 template <
class STREAM>
50 void DumpPhysicalVolume(STREAM& out,
const G4VPhysicalVolume& PV, std::string indentstr =
"")
52 const G4ThreeVector& pos = PV.GetTranslation();
53 const G4LogicalVolume* LV = PV.GetLogicalVolume();
55 out << indentstr << PV.GetName() <<
" [" << demangle_cxx_symbol(PV) <<
"]" 56 <<
" at (" << pos.x() <<
", " << pos.y() <<
", " << pos.z() <<
")";
59 out <<
" with no logical volume (!?)\n";
63 out <<
", a " << LV->GetName();
65 const G4VSolid* Solid = LV->GetSolid();
67 out <<
" shaped as " << Solid->GetName();
68 if (
auto pBox = dynamic_cast<const G4Box*>(Solid)) {
69 out <<
", a (" << (2. * pBox->GetXHalfLength()) <<
" x " << (2. * pBox->GetYHalfLength())
70 <<
" x " << (2. * pBox->GetZHalfLength()) <<
") cm box";
73 out <<
", a " << demangle_cxx_symbol(*Solid);
77 out <<
" with no shape (!?!)";
80 G4int nDaughters = LV->GetNoDaughters();
82 out <<
" with " << nDaughters <<
" subvolumes:\n";
83 for (G4int i = 0; i < nDaughters; ++i) {
84 DumpPhysicalVolume(out, *LV->GetDaughter(i), indentstr +
" ");
96 LArVoxelReadoutGeometry::LArVoxelReadoutGeometry(
const G4String& name,
Setup_t const& setupData)
97 : G4VUserParallelWorld(name), fReadoutSetupData(setupData.readoutSetup)
111 G4VPhysicalVolume* parallelPhysical = GetWorld();
119 G4Transform3D worldTransform;
122 std::string daughterName(
"volDetEnclosure");
123 G4Transform3D detEnclosureTransform;
126 detEnclosureTransform,
131 struct VoxelSpecs_t {
133 unsigned int nw, nh, nd;
136 bool operator<(
const VoxelSpecs_t& vs)
const 138 if (w < vs.w)
return true;
139 if (w > vs.w)
return false;
140 if (h < vs.h)
return true;
141 if (h > vs.h)
return false;
142 if (d < vs.d)
return true;
143 if (d > vs.d)
return false;
144 if (nw < vs.nw)
return true;
145 if (nw > vs.nw)
return false;
146 if (nh < vs.nh)
return true;
147 if (nh > vs.nh)
return false;
148 if (nd < vs.nd)
return true;
154 struct VoxelVolumes_t {
155 G4LogicalVolume* pBox;
156 G4LogicalVolume* pCell;
158 VoxelVolumes_t(G4LogicalVolume* box =
nullptr, G4LogicalVolume* cell =
nullptr)
159 : pBox(box), pCell(cell)
173 G4SDManager* sdManager = G4SDManager::GetSDMpointer();
177 using VoxelCache_t = std::map<VoxelSpecs_t, VoxelVolumes_t>;
178 VoxelCache_t VoxelCache;
183 daughterName =
"volCryostat";
184 G4Transform3D cryostatTransform;
186 detEnclosureVolume, detEnclosureTransform, cryostatTransform, daughterName, c);
191 daughterName =
"volTPC";
192 G4Transform3D tpcTransform;
193 G4VPhysicalVolume* tpcVolume =
194 FindNestedVolume(cryostatVolume, cryostatTransform, tpcTransform, daughterName, t);
196 daughterName =
"volTPCActive";
197 G4Transform3D transform;
198 G4VPhysicalVolume* larTPCPhysical =
202 G4LogicalVolume* larTPCLogical = larTPCPhysical->GetLogicalVolume();
203 larTPCLogical->SetUserLimits(
fStepLimit.get());
205 G4VSolid* larTPCShape = larTPCLogical->GetSolid();
217 G4double larTPCHalfXLength = 0;
218 G4double larTPCHalfYLength = 0;
219 G4double larTPCHalfZLength = 0;
220 G4Box* tpcBox =
dynamic_cast<G4Box*
>(larTPCShape);
222 larTPCHalfXLength = tpcBox->GetXHalfLength();
223 larTPCHalfYLength = tpcBox->GetYHalfLength();
224 larTPCHalfZLength = tpcBox->GetZHalfLength();
228 G4Tubs* tube =
dynamic_cast<G4Tubs*
>(larTPCShape);
230 larTPCHalfXLength = tube->GetOuterRadius();
231 larTPCHalfYLength = tube->GetOuterRadius();
232 larTPCHalfZLength = tube->GetZHalfLength();
236 <<
"Unknown shape in readout geometry" 237 <<
"The LAr TPC volume is not a box or a tube. " 238 <<
"This routine can't convert any other shapes.\n";
242 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": larTPCHalfXLength=" << larTPCHalfXLength
243 <<
": larTPCHalfYLength=" << larTPCHalfYLength
244 <<
": larTPCHalfZLength=" << larTPCHalfZLength;
252 G4double voxelOffsetX = lvc->
VoxelOffsetX() * CLHEP::cm;
253 G4double voxelOffsetY = lvc->
VoxelOffsetY() * CLHEP::cm;
254 G4double voxelOffsetZ = lvc->
VoxelOffsetZ() * CLHEP::cm;
257 <<
": voxelSizeX=" << voxelSizeX <<
", voxelSizeY=" << voxelSizeY
261 <<
": voxelOffsetX=" << voxelOffsetX <<
", voxelOffsetY=" << voxelOffsetY
262 <<
", voxelOffsetZ=" << voxelOffsetZ;
271 G4double numberXvoxels = 2. * larTPCHalfXLength /
voxelSizeX;
272 G4double numberYvoxels = 2. * larTPCHalfYLength /
voxelSizeY;
273 G4double numberZvoxels = 2. * larTPCHalfZLength /
voxelSizeZ;
274 numberXvoxels = trunc(numberXvoxels) + 1.;
275 numberYvoxels = trunc(numberYvoxels) + 1.;
276 numberZvoxels = trunc(numberZvoxels) + 1.;
279 <<
"Active volume of cryo #" << c <<
" TPC #" << t <<
" will be split in " 280 << numberXvoxels <<
" x " << numberYvoxels <<
" x " << numberZvoxels <<
" = " 281 << (numberXvoxels * numberYvoxels * numberZvoxels) <<
" voxels of size " << voxelSizeX
282 <<
" x " << voxelSizeY <<
" x " << voxelSizeZ <<
" cm";
284 VoxelSpecs_t VoxelSpecs{ 2. * larTPCHalfXLength,
285 2. * larTPCHalfYLength,
286 2. * larTPCHalfZLength,
287 (
unsigned int)numberXvoxels,
288 (
unsigned int)numberYvoxels,
289 (
unsigned int)numberZvoxels};
293 if (iVoxelVol == VoxelCache.end()) {
295 <<
"Creating a new voxel volume " << VoxelSpecs.w <<
" x " << VoxelSpecs.h <<
" x " 296 << VoxelSpecs.d <<
" cm, " << VoxelSpecs.nw <<
" x " << VoxelSpecs.nh <<
" x " 297 << VoxelSpecs.nd <<
" voxels";
299 G4double voxelBoxHalfX = numberXvoxels * voxelSizeX / 2.;
300 G4double voxelBoxHalfY = numberYvoxels * voxelSizeY / 2.;
301 G4double voxelBoxHalfZ = numberZvoxels * voxelSizeZ / 2.;
304 <<
": voxelBoxHalfX=" << voxelBoxHalfX <<
", voxelBoxHalfY=" << voxelBoxHalfY
305 <<
", voxelBoxHalfZ=" << voxelBoxHalfZ;
310 auto voxelBox =
new G4Box(
"VoxelBox", voxelBoxHalfX, voxelBoxHalfY, voxelBoxHalfZ);
311 auto voxelBoxLogical =
new G4LogicalVolume(voxelBox, 0,
"VoxelizationLogicalVolume");
315 auto invisible =
new G4VisAttributes();
316 invisible->SetVisibility(
false);
317 voxelBoxLogical->SetVisAttributes(invisible);
323 G4Box* xSlice =
new G4Box(
"xSlice", voxelSizeX / 2., voxelBoxHalfY, voxelBoxHalfZ);
324 G4LogicalVolume* xSliceLogical =
new G4LogicalVolume(xSlice, 0,
"xLArVoxelSlice");
325 xSliceLogical->SetVisAttributes(invisible);
328 new G4PVReplica(
"VoxelSlicesInX",
332 G4int(numberXvoxels),
336 auto ySlice =
new G4Box(
"ySlice", voxelSizeX / 2., voxelSizeY / 2., voxelBoxHalfZ);
337 auto ySliceLogical =
new G4LogicalVolume(ySlice, 0,
"yLArVoxelSlice");
338 ySliceLogical->SetVisAttributes(invisible);
339 new G4PVReplica(
"VoxelSlicesInY",
343 G4int(numberYvoxels),
348 auto zSlice =
new G4Box(
"zSlice", voxelSizeX / 2., voxelSizeY / 2., voxelSizeZ / 2.);
349 auto voxelLogical =
new G4LogicalVolume(zSlice, 0,
"LArVoxel");
350 voxelLogical->SetVisAttributes(invisible);
352 "LArVoxel", voxelLogical, ySliceLogical, kZAxis, G4int(numberZvoxels), voxelSizeZ);
354 iVoxelVol = VoxelCache.emplace(VoxelSpecs, VoxelVolumes_t(voxelBoxLogical, voxelLogical))
362 G4LogicalVolume* voxelBoxLogical = iVoxelVol->second.pBox;
372 G4double offsetInVoxelsX = voxelOffsetX /
voxelSizeX;
373 G4double offsetInVoxelsY = voxelOffsetY /
voxelSizeY;
374 G4double offsetInVoxelsZ = voxelOffsetZ /
voxelSizeZ;
375 G4double fractionOffsetX = offsetInVoxelsX - trunc(offsetInVoxelsX);
376 G4double fractionOffsetY = offsetInVoxelsY - trunc(offsetInVoxelsY);
377 G4double fractionOffsetZ = offsetInVoxelsZ - trunc(offsetInVoxelsZ);
378 G4double offsetX = fractionOffsetX *
voxelSizeX;
379 G4double offsetY = fractionOffsetY *
voxelSizeY;
380 G4double offsetZ = fractionOffsetZ *
voxelSizeZ;
385 transform = G4Translate3D(offsetX, offsetY, offsetZ) * transform;
388 <<
": offsetX=" << offsetX <<
", offsetY=" << offsetY <<
", offsetZ=" << offsetZ;
391 std::ostringstream nums;
392 nums <<
"_Cryostat" << c <<
"_TPC" << t;
397 "VoxelizationPhysicalVolume" + nums.str(),
403 {(
unsigned short int)c, (
unsigned short int)t}
410 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"Dump of voxelized volume";
413 DumpPhysicalVolume(log, *parallelPhysical);
415 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"End of dump of voxelized volume";
427 G4Transform3D& motherTransform,
428 G4Transform3D& daughterTransform,
429 std::string
const& daughterName,
430 unsigned int expectedNum)
432 G4LogicalVolume* logicalVolume = mother->GetLogicalVolume();
433 G4int numberDaughters = logicalVolume->GetNoDaughters();
434 for (G4int i = 0; i != numberDaughters; ++i) {
435 G4VPhysicalVolume*
d = logicalVolume->GetDaughter(i);
437 if (d->GetName().contains(daughterName)) {
440 G4ThreeVector translation = d->GetObjectTranslation();
441 G4RotationMatrix rotation = d->GetObjectRotationValue();
442 G4Transform3D transform(rotation, translation);
443 daughterTransform = motherTransform * transform;
447 G4Point3D local(0., 0., 0.);
448 G4Point3D world = daughterTransform * local;
451 <<
"current " << daughterName <<
" origin is at (" << world.x() / CLHEP::cm <<
"," 452 << world.y() / CLHEP::cm <<
"," << world.z() / CLHEP::cm <<
")";
458 world.x() / CLHEP::cm, world.y() / CLHEP::cm, world.z() / CLHEP::cm};
459 unsigned int daughterNum = 0;
460 if (daughterName.compare(
"volCryostat") == 0)
462 else if (daughterName.compare(
"volTPC") == 0)
464 else if (daughterName.compare(
"volTPCActive") == 0 ||
465 daughterName.compare(
"volDetEnclosure") == 0) {
467 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
"found the desired " << daughterName;
472 if (daughterNum == expectedNum) {
473 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
"found the desired " << daughterName;
480 <<
"could not find the desired " << daughterName <<
" 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.
larg4::LArVoxelReadout::Setup_t fReadoutSetupData
Encapsulates calculation of LArVoxelID and LArVoxel parameters.
G4VPhysicalVolume * FindNestedVolume(G4VPhysicalVolume *mother, G4Transform3D &motherTransform, G4Transform3D &daughterTransform, std::string const &daughterName, unsigned int expectedNum)
constexpr bool operator<(CryostatID const &a, CryostatID const &b)
Order cryostats with increasing ID.
static G4VPhysicalVolume * GetWorld()
double VoxelSizeY() const
CryostatID_t Cryostat
Index of cryostat.
void Setup(Setup_t const &setupData)
Reads all the configuration elements from setupData
larg4::LArVoxelReadout * flarVoxelReadout
Data for LArVoxelReadout setup.
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
CryostatGeo const & Cryostat(CryostatID const &cryoid=cryostat_zero) const
Returns the specified cryostat.
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
static IonizationAndScintillation * Instance()
unsigned int NTPC() const
Number of TPCs in this cryostat.
G4PVPlacementWithID< TPCID_t > G4PVPlacementInTPC
A physical volume with a TPC ID.
double VoxelOffsetX() const
art::ServiceHandle< geo::Geometry const > fGeo
Handle to the geometry.
double VoxelOffsetZ() const
CryostatID PositionToCryostatID(Point_t const &point) const
Returns the ID of the cryostat at specified location.
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
A Geant4 sensitive detector that accumulates voxel information.
void Construct() override
Singleton to access a unified treatment of ionization and scintillation in LAr.
Transports energy depositions from GEANT4 to TPC channels.
TPCID_t TPC
Index of the TPC within its cryostat.
double StepSizeLimit() const
TPCID PositionToTPCID(Point_t const &point) const
Returns the ID of the TPC at specified location.
double VoxelOffsetY() const
cet::coded_exception< error, detail::translate > exception
The data type to uniquely identify a cryostat.