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)
100 ,
fLgp{setupData.larG4Params}
115 G4VPhysicalVolume* parallelPhysical = GetWorld();
121 G4Transform3D worldTransform;
124 std::string daughterName(
"volDetEnclosure");
125 G4Transform3D detEnclosureTransform;
128 detEnclosureTransform,
133 struct VoxelSpecs_t {
135 unsigned int nw, nh, nd;
138 bool operator<(
const VoxelSpecs_t& vs)
const 140 if (w < vs.w)
return true;
141 if (w > vs.w)
return false;
142 if (h < vs.h)
return true;
143 if (h > vs.h)
return false;
144 if (d < vs.d)
return true;
145 if (d > vs.d)
return false;
146 if (nw < vs.nw)
return true;
147 if (nw > vs.nw)
return false;
148 if (nh < vs.nh)
return true;
149 if (nh > vs.nh)
return false;
150 if (nd < vs.nd)
return true;
156 struct VoxelVolumes_t {
157 G4LogicalVolume* pBox;
158 G4LogicalVolume* pCell;
160 VoxelVolumes_t(G4LogicalVolume* box =
nullptr, G4LogicalVolume* cell =
nullptr)
161 : 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();
216 G4double larTPCHalfXLength = 0;
217 G4double larTPCHalfYLength = 0;
218 G4double larTPCHalfZLength = 0;
219 G4Box* tpcBox =
dynamic_cast<G4Box*
>(larTPCShape);
221 larTPCHalfXLength = tpcBox->GetXHalfLength();
222 larTPCHalfYLength = tpcBox->GetYHalfLength();
223 larTPCHalfZLength = tpcBox->GetZHalfLength();
227 G4Tubs* tube =
dynamic_cast<G4Tubs*
>(larTPCShape);
229 larTPCHalfXLength = tube->GetOuterRadius();
230 larTPCHalfYLength = tube->GetOuterRadius();
231 larTPCHalfZLength = tube->GetZHalfLength();
235 <<
"Unknown shape in readout geometry" 236 <<
"The LAr TPC volume is not a box or a tube. " 237 <<
"This routine can't convert any other shapes.\n";
241 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": larTPCHalfXLength=" << larTPCHalfXLength
242 <<
": larTPCHalfYLength=" << larTPCHalfYLength
243 <<
": larTPCHalfZLength=" << larTPCHalfZLength;
250 G4double voxelOffsetX = lvc->
VoxelOffsetX() * CLHEP::cm;
251 G4double voxelOffsetY = lvc->
VoxelOffsetY() * CLHEP::cm;
252 G4double voxelOffsetZ = lvc->
VoxelOffsetZ() * CLHEP::cm;
255 <<
": voxelSizeX=" << voxelSizeX <<
", voxelSizeY=" << voxelSizeY
259 <<
": voxelOffsetX=" << voxelOffsetX <<
", voxelOffsetY=" << voxelOffsetY
260 <<
", voxelOffsetZ=" << voxelOffsetZ;
268 G4double numberXvoxels = 2. * larTPCHalfXLength /
voxelSizeX;
269 G4double numberYvoxels = 2. * larTPCHalfYLength /
voxelSizeY;
270 G4double numberZvoxels = 2. * larTPCHalfZLength /
voxelSizeZ;
271 numberXvoxels = trunc(numberXvoxels) + 1.;
272 numberYvoxels = trunc(numberYvoxels) + 1.;
273 numberZvoxels = trunc(numberZvoxels) + 1.;
276 <<
"Active volume of cryo #" << c <<
" TPC #" << t <<
" will be split in " 277 << numberXvoxels <<
" x " << numberYvoxels <<
" x " << numberZvoxels <<
" = " 278 << (numberXvoxels * numberYvoxels * numberZvoxels) <<
" voxels of size " << voxelSizeX
279 <<
" x " << voxelSizeY <<
" x " << voxelSizeZ <<
" cm";
281 VoxelSpecs_t VoxelSpecs{ 2. * larTPCHalfXLength,
282 2. * larTPCHalfYLength,
283 2. * larTPCHalfZLength,
284 (
unsigned int)numberXvoxels,
285 (
unsigned int)numberYvoxels,
286 (
unsigned int)numberZvoxels};
290 if (iVoxelVol == VoxelCache.end()) {
292 <<
"Creating a new voxel volume " << VoxelSpecs.w <<
" x " << VoxelSpecs.h <<
" x " 293 << VoxelSpecs.d <<
" cm, " << VoxelSpecs.nw <<
" x " << VoxelSpecs.nh <<
" x " 294 << VoxelSpecs.nd <<
" voxels";
296 G4double voxelBoxHalfX = numberXvoxels * voxelSizeX / 2.;
297 G4double voxelBoxHalfY = numberYvoxels * voxelSizeY / 2.;
298 G4double voxelBoxHalfZ = numberZvoxels * voxelSizeZ / 2.;
301 <<
": voxelBoxHalfX=" << voxelBoxHalfX <<
", voxelBoxHalfY=" << voxelBoxHalfY
302 <<
", voxelBoxHalfZ=" << voxelBoxHalfZ;
306 auto voxelBox =
new G4Box(
"VoxelBox", voxelBoxHalfX, voxelBoxHalfY, voxelBoxHalfZ);
307 auto voxelBoxLogical =
new G4LogicalVolume(voxelBox, 0,
"VoxelizationLogicalVolume");
310 auto invisible =
new G4VisAttributes();
311 invisible->SetVisibility(
false);
312 voxelBoxLogical->SetVisAttributes(invisible);
318 G4Box* xSlice =
new G4Box(
"xSlice", voxelSizeX / 2., voxelBoxHalfY, voxelBoxHalfZ);
319 G4LogicalVolume* xSliceLogical =
new G4LogicalVolume(xSlice, 0,
"xLArVoxelSlice");
320 xSliceLogical->SetVisAttributes(invisible);
323 new G4PVReplica(
"VoxelSlicesInX",
327 G4int(numberXvoxels),
331 auto ySlice =
new G4Box(
"ySlice", voxelSizeX / 2., voxelSizeY / 2., voxelBoxHalfZ);
332 auto ySliceLogical =
new G4LogicalVolume(ySlice, 0,
"yLArVoxelSlice");
333 ySliceLogical->SetVisAttributes(invisible);
334 new G4PVReplica(
"VoxelSlicesInY",
338 G4int(numberYvoxels),
342 auto zSlice =
new G4Box(
"zSlice", voxelSizeX / 2., voxelSizeY / 2., voxelSizeZ / 2.);
343 auto voxelLogical =
new G4LogicalVolume(zSlice, 0,
"LArVoxel");
344 voxelLogical->SetVisAttributes(invisible);
346 "LArVoxel", voxelLogical, ySliceLogical, kZAxis, G4int(numberZvoxels), voxelSizeZ);
348 iVoxelVol = VoxelCache.emplace(VoxelSpecs, VoxelVolumes_t(voxelBoxLogical, voxelLogical))
356 G4LogicalVolume* voxelBoxLogical = iVoxelVol->second.pBox;
364 G4double offsetInVoxelsX = voxelOffsetX /
voxelSizeX;
365 G4double offsetInVoxelsY = voxelOffsetY /
voxelSizeY;
366 G4double offsetInVoxelsZ = voxelOffsetZ /
voxelSizeZ;
367 G4double fractionOffsetX = offsetInVoxelsX - trunc(offsetInVoxelsX);
368 G4double fractionOffsetY = offsetInVoxelsY - trunc(offsetInVoxelsY);
369 G4double fractionOffsetZ = offsetInVoxelsZ - trunc(offsetInVoxelsZ);
370 G4double offsetX = fractionOffsetX *
voxelSizeX;
371 G4double offsetY = fractionOffsetY *
voxelSizeY;
372 G4double offsetZ = fractionOffsetZ *
voxelSizeZ;
376 transform = G4Translate3D(offsetX, offsetY, offsetZ) * transform;
379 <<
": offsetX=" << offsetX <<
", offsetY=" << offsetY <<
", offsetZ=" << offsetZ;
382 std::ostringstream nums;
383 nums <<
"_Cryostat" << c <<
"_TPC" << t;
387 "VoxelizationPhysicalVolume" + nums.str(),
393 {(
unsigned short int)c, (
unsigned short int)t}
400 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"Dump of voxelized volume";
403 DumpPhysicalVolume(log, *parallelPhysical);
405 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"End of dump of voxelized volume";
415 G4Transform3D& motherTransform,
416 G4Transform3D& daughterTransform,
417 std::string
const& daughterName,
418 unsigned int expectedNum)
420 G4LogicalVolume* logicalVolume = mother->GetLogicalVolume();
421 G4int numberDaughters = logicalVolume->GetNoDaughters();
422 for (G4int i = 0; i != numberDaughters; ++i) {
423 G4VPhysicalVolume*
d = logicalVolume->GetDaughter(i);
425 if (d->GetName().contains(daughterName)) {
428 G4ThreeVector translation = d->GetObjectTranslation();
429 G4RotationMatrix rotation = d->GetObjectRotationValue();
430 G4Transform3D transform(rotation, translation);
431 daughterTransform = motherTransform * transform;
434 G4Point3D local(0., 0., 0.);
435 G4Point3D world = daughterTransform * local;
438 <<
"current " << daughterName <<
" origin is at (" << world.x() / CLHEP::cm <<
"," 439 << world.y() / CLHEP::cm <<
"," << world.z() / CLHEP::cm <<
")";
445 world.x() / CLHEP::cm, world.y() / CLHEP::cm, world.z() / CLHEP::cm};
446 unsigned int daughterNum = 0;
447 if (daughterName.compare(
"volCryostat") == 0)
449 else if (daughterName.compare(
"volTPC") == 0)
451 else if (daughterName.compare(
"volTPCActive") == 0 ||
452 daughterName.compare(
"volDetEnclosure") == 0) {
454 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
"found the desired " << daughterName;
459 if (daughterNum == expectedNum) {
460 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
"found the desired " << daughterName;
467 <<
"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.
geo::GeometryCore const * fGeom
Define the "parallel" geometry that's seen by the LAr Voxels.
double VoxelSizeZ() const
geo::WireReadoutGeom const * fWireReadoutGeom
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()
sim::LArG4Parameters const * fLgp
unsigned int NTPC() const
Number of TPCs in this cryostat.
G4PVPlacementWithID< TPCID_t > G4PVPlacementInTPC
A physical volume with a TPC ID.
geo::GeometryCore const * geometry
Set up data for LArVoxelReadout.
double VoxelOffsetX() const
CryostatGeo const & Cryostat(CryostatID const &cryoid=details::cryostat_zero) const
Returns the specified cryostat.
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.