The key method in this class; creates a parallel world view of those volumes relevant to the LAr voxel readout. Required of any class that inherits from G4VUserParallelWorld
have some sorting...
124 G4VPhysicalVolume* parallelPhysical = GetWorld();
132 G4Transform3D worldTransform;
136 G4Transform3D detEnclosureTransform;
137 G4VPhysicalVolume* detEnclosureVolume =
140 detEnclosureTransform,
145 struct VoxelSpecs_t {
147 unsigned int nw, nh, nd;
153 if (w < vs.w)
return true;
154 if (w > vs.w)
return false;
155 if (h < vs.h)
return true;
156 if (h > vs.h)
return false;
157 if (d < vs.d)
return true;
158 if (d > vs.d)
return false;
159 if (nw < vs.nw)
return true;
160 if (nw > vs.nw)
return false;
161 if (nh < vs.nh)
return true;
162 if (nh > vs.nh)
return false;
163 if (nd < vs.nd)
return true;
169 struct VoxelVolumes_t {
170 G4LogicalVolume* pBox;
171 G4LogicalVolume* pCell;
173 VoxelVolumes_t(G4LogicalVolume* box =
nullptr, G4LogicalVolume* cell =
nullptr)
174 : pBox(box), pCell(cell)
188 G4SDManager* sdManager = G4SDManager::GetSDMpointer();
192 using VoxelCache_t = std::map<VoxelSpecs_t, VoxelVolumes_t>;
193 VoxelCache_t VoxelCache;
198 daughterName =
"volCryostat";
199 G4Transform3D cryostatTransform;
201 detEnclosureVolume, detEnclosureTransform, cryostatTransform, daughterName,
c);
206 daughterName =
"volTPC";
207 G4Transform3D tpcTransform;
208 G4VPhysicalVolume* tpcVolume =
209 this->
FindNestedVolume(cryostatVolume, cryostatTransform, tpcTransform, daughterName,
t);
211 daughterName =
"volTPCActive";
212 G4Transform3D transform;
213 G4VPhysicalVolume* larTPCPhysical =
217 G4LogicalVolume* larTPCLogical = larTPCPhysical->GetLogicalVolume();
218 larTPCLogical->SetUserLimits(
fStepLimit.get());
220 G4VSolid* larTPCShape = larTPCLogical->GetSolid();
232 G4double larTPCHalfXLength = 0;
233 G4double larTPCHalfYLength = 0;
234 G4double larTPCHalfZLength = 0;
235 G4Box* tpcBox =
dynamic_cast<G4Box*
>(larTPCShape);
237 larTPCHalfXLength = tpcBox->GetXHalfLength();
238 larTPCHalfYLength = tpcBox->GetYHalfLength();
239 larTPCHalfZLength = tpcBox->GetZHalfLength();
243 G4Tubs* tube =
dynamic_cast<G4Tubs*
>(larTPCShape);
245 larTPCHalfXLength = tube->GetOuterRadius();
246 larTPCHalfYLength = tube->GetOuterRadius();
247 larTPCHalfZLength = tube->GetZHalfLength();
251 <<
"Unknown shape in readout geometry" 252 <<
"The LAr TPC volume is not a box or a tube. " 253 <<
"This routine can't convert any other shapes.\n";
257 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry") <<
": larTPCHalfXLength=" << larTPCHalfXLength
258 <<
": larTPCHalfYLength=" << larTPCHalfYLength
259 <<
": larTPCHalfZLength=" << larTPCHalfZLength;
272 <<
": voxelSizeX=" << voxelSizeX <<
", voxelSizeY=" << voxelSizeY
273 <<
", voxelSizeZ=" << voxelSizeZ;
276 <<
": voxelOffsetX=" << voxelOffsetX <<
", voxelOffsetY=" << voxelOffsetY
277 <<
", voxelOffsetZ=" << voxelOffsetZ;
286 G4double numberXvoxels = 2. * larTPCHalfXLength / voxelSizeX;
287 G4double numberYvoxels = 2. * larTPCHalfYLength / voxelSizeY;
288 G4double numberZvoxels = 2. * larTPCHalfZLength / voxelSizeZ;
289 numberXvoxels = trunc(numberXvoxels) + 1.;
290 numberYvoxels = trunc(numberYvoxels) + 1.;
291 numberZvoxels = trunc(numberZvoxels) + 1.;
294 <<
"Active volume of cryo #" <<
c <<
" TPC #" <<
t <<
" will be split in " 295 << numberXvoxels <<
" x " << numberYvoxels <<
" x " << numberZvoxels <<
" = " 296 << (numberXvoxels * numberYvoxels * numberZvoxels) <<
" voxels of size " << voxelSizeX
297 <<
" x " << voxelSizeY <<
" x " << voxelSizeZ <<
" cm";
299 VoxelSpecs_t VoxelSpecs{ 2. * larTPCHalfXLength,
300 2. * larTPCHalfYLength,
301 2. * larTPCHalfZLength,
302 (
unsigned int)numberXvoxels,
303 (
unsigned int)numberYvoxels,
304 (
unsigned int)numberZvoxels};
308 if (iVoxelVol == VoxelCache.end()) {
310 <<
"Creating a new voxel volume " << VoxelSpecs.w <<
" x " << VoxelSpecs.h <<
" x " 311 << VoxelSpecs.d <<
" cm, " << VoxelSpecs.nw <<
" x " << VoxelSpecs.nh <<
" x " 312 << VoxelSpecs.nd <<
" voxels";
314 G4double voxelBoxHalfX = numberXvoxels * voxelSizeX / 2.;
315 G4double voxelBoxHalfY = numberYvoxels * voxelSizeY / 2.;
316 G4double voxelBoxHalfZ = numberZvoxels * voxelSizeZ / 2.;
319 <<
": voxelBoxHalfX=" << voxelBoxHalfX <<
", voxelBoxHalfY=" << voxelBoxHalfY
320 <<
", voxelBoxHalfZ=" << voxelBoxHalfZ;
325 auto voxelBox =
new G4Box(
"VoxelBox", voxelBoxHalfX, voxelBoxHalfY, voxelBoxHalfZ);
326 auto voxelBoxLogical =
new G4LogicalVolume(voxelBox, 0,
"VoxelizationLogicalVolume");
330 auto invisible =
new G4VisAttributes();
331 invisible->SetVisibility(
false);
332 voxelBoxLogical->SetVisAttributes(invisible);
338 G4Box* xSlice =
new G4Box(
"xSlice", voxelSizeX / 2., voxelBoxHalfY, voxelBoxHalfZ);
339 G4LogicalVolume* xSliceLogical =
new G4LogicalVolume(xSlice, 0,
"xLArVoxelSlice");
340 xSliceLogical->SetVisAttributes(invisible);
343 new G4PVReplica(
"VoxelSlicesInX",
347 G4int(numberXvoxels),
351 auto ySlice =
new G4Box(
"ySlice", voxelSizeX / 2., voxelSizeY / 2., voxelBoxHalfZ);
352 auto ySliceLogical =
new G4LogicalVolume(ySlice, 0,
"yLArVoxelSlice");
353 ySliceLogical->SetVisAttributes(invisible);
354 new G4PVReplica(
"VoxelSlicesInY",
358 G4int(numberYvoxels),
363 auto zSlice =
new G4Box(
"zSlice", voxelSizeX / 2., voxelSizeY / 2., voxelSizeZ / 2.);
364 auto voxelLogical =
new G4LogicalVolume(zSlice, 0,
"LArVoxel");
365 voxelLogical->SetVisAttributes(invisible);
367 "LArVoxel", voxelLogical, ySliceLogical, kZAxis, G4int(numberZvoxels), voxelSizeZ);
369 iVoxelVol = VoxelCache.emplace(VoxelSpecs, VoxelVolumes_t(voxelBoxLogical, voxelLogical))
377 G4LogicalVolume* voxelBoxLogical = iVoxelVol->second.pBox;
387 G4double offsetInVoxelsX = voxelOffsetX / voxelSizeX;
388 G4double offsetInVoxelsY = voxelOffsetY / voxelSizeY;
389 G4double offsetInVoxelsZ = voxelOffsetZ / voxelSizeZ;
390 G4double fractionOffsetX = offsetInVoxelsX - trunc(offsetInVoxelsX);
391 G4double fractionOffsetY = offsetInVoxelsY - trunc(offsetInVoxelsY);
392 G4double fractionOffsetZ = offsetInVoxelsZ - trunc(offsetInVoxelsZ);
393 G4double offsetX = fractionOffsetX * voxelSizeX;
394 G4double offsetY = fractionOffsetY * voxelSizeY;
395 G4double offsetZ = fractionOffsetZ * voxelSizeZ;
400 transform = G4Translate3D(offsetX, offsetY, offsetZ) * transform;
403 <<
": offsetX=" << offsetX <<
", offsetY=" << offsetY <<
", offsetZ=" << offsetZ;
406 std::ostringstream nums;
407 nums <<
"_Cryostat" <<
c <<
"_TPC" <<
t;
412 "VoxelizationPhysicalVolume" + nums.str(),
418 {(
unsigned short int)
c, (
unsigned short int)t}
425 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"Dump of voxelized volume";
428 DumpPhysicalVolume(log, *parallelPhysical);
430 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"End of dump of voxelized volume";
static constexpr double cm
double VoxelSizeX() const
Access to voxel dimensions and offsets.
larg4::LArVoxelReadout::Setup_t fReadoutSetupData
double VoxelSizeY() const
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.
double VoxelSizeZ() const
bool operator<(ProductInfo const &a, ProductInfo const &b)
void SetSingleTPC(unsigned int cryostat, unsigned int tpc)
Associates this readout to one specific TPC.
std::unique_ptr< G4UserLimits > fStepLimit
constexpr bool DisableVoxelCaching
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
art::ServiceHandle< geo::Geometry const > fGeo
Handle to the geometry.
G4VPhysicalVolume * FindNestedVolume(G4VPhysicalVolume *mother, G4Transform3D &motherTransform, G4Transform3D &daughterTransform, std::string &daughterName, unsigned int expectedNum)
double VoxelOffsetZ() const
double VoxelOffsetY() const
cet::coded_exception< error, detail::translate > exception