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;
135 std::string daughterName(
"volDetEnclosure");
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)
183 if ((
fGeo->Ncryostats() == 1) && (
fGeo->Cryostat(0).NTPC() == 1))
188 G4SDManager* sdManager = G4SDManager::GetSDMpointer();
192 using VoxelCache_t = std::map<VoxelSpecs_t, VoxelVolumes_t>;
193 VoxelCache_t VoxelCache;
195 for (
unsigned int c = 0; c <
fGeo->Ncryostats(); ++c) {
198 daughterName =
"volCryostat";
199 G4Transform3D cryostatTransform;
201 detEnclosureVolume, detEnclosureTransform, cryostatTransform, daughterName, c);
203 for (
unsigned int t = 0; t <
fGeo->Cryostat(c).NTPC(); ++t) {
206 daughterName =
"volTPC";
207 G4Transform3D tpcTransform;
208 G4VPhysicalVolume* tpcVolume =
209 this->
FindNestedVolume(cryostatVolume, cryostatTransform, tpcTransform, daughterName, t);
211 daughterName =
"volTPCActive";
213 G4VPhysicalVolume* larTPCPhysical =
214 this->
FindNestedVolume(tpcVolume, tpcTransform, transform, daughterName, t);
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();
250 throw cet::exception(
"LArVoxelReadoutGeometry")
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;
263 art::ServiceHandle<sim::LArVoxelCalculator const> lvc;
264 G4double voxelSizeX = lvc->VoxelSizeX() * CLHEP::cm;
265 G4double voxelSizeY = lvc->VoxelSizeY() * CLHEP::cm;
266 G4double voxelSizeZ = lvc->VoxelSizeZ() * CLHEP::cm;
267 G4double voxelOffsetX = lvc->VoxelOffsetX() * CLHEP::cm;
268 G4double voxelOffsetY = lvc->VoxelOffsetY() * CLHEP::cm;
269 G4double voxelOffsetZ = lvc->VoxelOffsetZ() * CLHEP::cm;
271 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry")
272 <<
": voxelSizeX=" << voxelSizeX <<
", voxelSizeY=" << voxelSizeY
273 <<
", voxelSizeZ=" << voxelSizeZ;
275 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry")
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.;
293 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry")
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};
306 VoxelCache_t::iterator iVoxelVol =
308 if (iVoxelVol == VoxelCache.end()) {
309 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry")
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.;
318 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry")
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;
402 MF_LOG_DEBUG(
"LArVoxelReadoutGeometry")
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}
424 if (mf::isDebugEnabled()) {
425 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"Dump of voxelized volume";
427 mf::LogDebug log(
"LArVoxelReadoutGeometryDump");
428 DumpPhysicalVolume(log, *parallelPhysical);
430 MF_LOG_DEBUG(
"LArVoxelReadoutGeometryDump") <<
"End of dump of voxelized volume";
larg4::LArVoxelReadout::Setup_t fReadoutSetupData
constexpr bool operator<(CryostatID const &a, CryostatID const &b)
Order cryostats with increasing ID.
void Setup(Setup_t const &setupData)
Reads all the configuration elements from setupData
larg4::LArVoxelReadout * flarVoxelReadout
Data for LArVoxelReadout setup.
void SetSingleTPC(unsigned int cryostat, unsigned int tpc)
Associates this readout to one specific TPC.
std::unique_ptr< G4UserLimits > fStepLimit
constexpr bool DisableVoxelCaching
G4PVPlacementWithID< TPCID_t > G4PVPlacementInTPC
A physical volume with a TPC ID.
art::ServiceHandle< geo::Geometry const > fGeo
Handle to the geometry.
G4VPhysicalVolume * FindNestedVolume(G4VPhysicalVolume *mother, G4Transform3D &motherTransform, G4Transform3D &daughterTransform, std::string &daughterName, unsigned int expectedNum)