ATLAS Offline Software
Loading...
Searching...
No Matches
CaloBlueprintNodeBuilder.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
8
9#include "Acts/Geometry/Blueprint.hpp"
10#include "Acts/Geometry/PortalShell.hpp"
11#include "Acts/Geometry/Volume.hpp"
12#include "Acts/Geometry/StaticBlueprintNode.hpp"
13#include "Acts/Geometry/GeometryIdentifierBlueprintNode.hpp"
14#include <Acts/Navigation/SurfaceArrayNavigationPolicy.hpp>
15#include <Acts/Navigation/TryAllNavigationPolicy.hpp>
16#include <Acts/Utilities/AxisDefinitions.hpp>
17#include <Acts/Geometry/ContainerBlueprintNode.hpp>
18#include <Acts/Geometry/Extent.hpp>
19#include <Acts/Definitions/Units.hpp>
20#include <Acts/Geometry/LayerBlueprintNode.hpp>
21#include <Acts/Geometry/VolumeResizeStrategy.hpp>
22#include <Acts/Geometry/VolumeAttachmentStrategy.hpp>
23#include <Acts/Geometry/TrackingVolume.hpp>
24#include <Acts/Geometry/CylinderVolumeBounds.hpp>
25
27
28#include <Acts/Surfaces/SurfaceArray.hpp>
30
31using namespace Acts;
32using namespace Acts::UnitLiterals;
33using AttachmentStrategy = Acts::VolumeAttachmentStrategy;
34using ResizeStrategy = Acts::VolumeResizeStrategy;
35
36using namespace ActsTrk::detail::GeoVolIds;
37
39 ATH_MSG_DEBUG("Initializing CaloBlueprintNodeBuilder");
40
42
43 return StatusCode::SUCCESS;
44}
45
46std::shared_ptr<BlueprintNode> ActsTrk::CaloBlueprintNodeBuilder::buildBlueprintNode(const GeometryContext& /*gctx*/,
47 std::shared_ptr<BlueprintNode>&& childNode) {
48
49
50 std::map<caloRegion, caloSampleSurfaceMap_t> caloRegionSampleSurfaceMap;
51 std::map<caloRegion, caloSampleDDEElementsMap_t> caloRegionSampleDDEElementsMap;
52 std::map<std::string, double> caloDimensions;
53
54 fillMaps(caloRegionSampleSurfaceMap, caloRegionSampleDDEElementsMap, caloDimensions);
55
56 ATH_MSG_DEBUG("Have filled first two maps");
57
58 generateCylinderSurfaces(caloRegionSampleSurfaceMap[caloRegion::CylinderSymmetricZZero], caloRegionSampleDDEElementsMap[caloRegion::CylinderSymmetricZZero],false);
59 generateCylinderSurfaces(caloRegionSampleSurfaceMap[caloRegion::CylinderNegativeZ], caloRegionSampleDDEElementsMap[caloRegion::CylinderNegativeZ],true);
60 generateCylinderSurfaces(caloRegionSampleSurfaceMap[caloRegion::CylinderPositiveZ], caloRegionSampleDDEElementsMap[caloRegion::CylinderPositiveZ],true);
61 generateDiscSurfaces(caloRegionSampleSurfaceMap[caloRegion::DiscNegativeZ], caloRegionSampleDDEElementsMap[caloRegion::DiscNegativeZ]);
62 generateDiscSurfaces(caloRegionSampleSurfaceMap[caloRegion::DiscPositiveZ], caloRegionSampleDDEElementsMap[caloRegion::DiscPositiveZ]);
63
64 ATH_MSG_DEBUG("Have generated calorimeter cylindrical surfaces");
65
66 // The calo node is a container node that will hold the itk and calo nodes as children.
67 // The calo cylinder is static in order to avoid merging issues with the itk portals
68 // that are supposed to carry material.
69 // The envelope of the calo node is set to the maximum radius and half length in z of the calorimeter,
70 // which is via a loop over all CaloDetDescrElements in the CaloDetDescrManager in the fillMaps function.
71
72 double caloEvelopeMaxR = caloDimensions.at("maxR");
73 double caloEnvelopeHalfLenghtZ = (caloDimensions.at("maxPosZ") - caloDimensions.at("minNegZ"))/2.0;
74
75 ATH_MSG_INFO("Calo envelope dimensions: maxR = " << caloEvelopeMaxR << ", halfLengthZ = " << caloEnvelopeHalfLenghtZ);
76 std::shared_ptr<StaticBlueprintNode> itkCaloNode{};
77 {
78 auto envelope = std::make_unique<TrackingVolume>(Amg::Transform3D::Identity(),
79 std::make_shared<CylinderVolumeBounds>(0., caloEvelopeMaxR, caloEnvelopeHalfLenghtZ),"ITkCalo");
80 envelope->assignGeometryId(Acts::GeometryIdentifier{}.withVolume(s_caloEnvelopeID));
81 itkCaloNode = std::make_shared<StaticBlueprintNode>(std::move(envelope));
82 }
83 if (childNode) {
84 itkCaloNode->addChild(std::move(childNode));
85 }
86
87 ATH_MSG_DEBUG("Top level calorimeter node created");
88
89 auto caloNode = std::make_shared<CylinderContainerBlueprintNode>("CaloNode", AxisDirection::AxisZ);
90 CylinderContainerBlueprintNode& caloBarrelCylinderNode = caloNode->addCylinderContainer("CaloBarrelSymmetricZZeroCylinders", AxisDirection::AxisR);
91 caloBarrelCylinderNode.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
92 caloBarrelCylinderNode.setResizeStrategy(ResizeStrategy::Gap);
93
94 ATH_MSG_DEBUG("EM Barrel container node created");
95
96 unsigned int volumeCounter = 0;
97
98 //Barrel cylinders symmetric about z = 0
99 for (unsigned int sampleIndex = 0; sampleIndex < m_caloCylinderSymmetricSampleList.size(); ++sampleIndex) {
100 auto& sampleName = m_caloCylinderSymmetricSampleList.at(sampleIndex).first;
101 addCylindricalTrackingVolumeToCaloNode(caloBarrelCylinderNode, sampleName, caloRegionSampleSurfaceMap[caloRegion::CylinderSymmetricZZero].at({sampleName, getSampleEnum(sampleName)}), volumeCounter, false);
102 volumeCounter++;
103 }
104
105 //now do asymmetric cylinders, first negative z
106
107 CylinderContainerBlueprintNode& caloBarrelCylinderNegativeZNode = caloNode->addCylinderContainer("CaloBarrelNegativeZAsymmetricCylinders", AxisDirection::AxisR);
108 caloBarrelCylinderNegativeZNode.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
109 caloBarrelCylinderNegativeZNode.setResizeStrategy(ResizeStrategy::Gap);
110
111 //then positive z
112 CylinderContainerBlueprintNode& caloBarrelCylinderPositiveZNode = caloNode->addCylinderContainer("CaloBarrelPositiveZAsymmetricCylinders", AxisDirection::AxisR);
113 caloBarrelCylinderPositiveZNode.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
114 caloBarrelCylinderPositiveZNode.setResizeStrategy(ResizeStrategy::Gap);
115
116 for (unsigned int sampleIndex = 0; sampleIndex < m_caloCylinderAsymmetricSampleList.size(); ++sampleIndex) {
117 auto& sampleName = m_caloCylinderAsymmetricSampleList.at(sampleIndex).first;
118 //We only add TileGap1 and 2 here, because the TileExt0,1,2
119 //will need a special treatment to avoid clashes in Z.
120 if (sampleIndex < 2){
121 addCylindricalTrackingVolumeToCaloNode(caloBarrelCylinderNegativeZNode, sampleName+"NegZ", caloRegionSampleSurfaceMap[caloRegion::CylinderNegativeZ].at({sampleName, getSampleEnum(sampleName)}), volumeCounter, false);
122 volumeCounter++;
123 addCylindricalTrackingVolumeToCaloNode(caloBarrelCylinderPositiveZNode, sampleName+"PosZ", caloRegionSampleSurfaceMap[caloRegion::CylinderPositiveZ].at({sampleName, getSampleEnum(sampleName)}), volumeCounter, false);
124 volumeCounter++;
125 }
126 else{
127 //Tile extended barrel surfaces must be added to top level node directly because they always overlap in R
128 //or Z with other calorimeter surfaces, volumes etc.
129 //Note there is a speed penalty to do it this way.
130 itkCaloNode->addLayer(sampleName+"NegZ" + "_Layer", [&](auto& layer) {
131 layer.setSurfaces(caloRegionSampleSurfaceMap[caloRegion::CylinderNegativeZ].at({sampleName, getSampleEnum(sampleName)}));
132 layer.setEnvelope(Acts::ExtentEnvelope{{
133 .z = {0.1_mm, 0.1_mm},
134 .r = {2_mm, 2_mm},
135 }});
136 });
137 itkCaloNode->addLayer(sampleName+"PosZ" + "_Layer", [&](auto& layer) {
138 layer.setSurfaces(caloRegionSampleSurfaceMap[caloRegion::CylinderPositiveZ].at({sampleName, getSampleEnum(sampleName)}));
139 layer.setEnvelope(Acts::ExtentEnvelope{{
140 .z = {0.1_mm, 0.1_mm},
141 .r = {2_mm, 2_mm},
142 }});
143 });
144 }
145 }
146
147
148 CylinderContainerBlueprintNode& caloEndCapDiscNegativeZNode = caloNode->addCylinderContainer("CaloEndCapDiscNegativeZ", AxisDirection::AxisZ);
149 caloEndCapDiscNegativeZNode.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
150 // The -z end of this container defines the calorimeter's global minZ, so the
151 // enclosing envelope's -z edge coincides with this container's own -z edge to
152 // within floating-point rounding. With ResizeStrategy::Gap on that side, the
153 // resize mints a degenerate (~1e-13 mm) end-gap volume whose two disc faces
154 // coincide, which CylinderNavigationPolicy cannot resolve. Expand the
155 // outermost wheel on the boundary (-z) side instead; keep Gap on the interior
156 // (+z) side. NOTE: (inner, outer) map to (minZ, maxZ) for an AxisZ stack.
157 caloEndCapDiscNegativeZNode.setResizeStrategies(ResizeStrategy::Expand, ResizeStrategy::Gap);
158
159 CylinderContainerBlueprintNode& caloEndCapDiscPositiveZNode = caloNode->addCylinderContainer("CaloEndCapDiscPositiveZ", AxisDirection::AxisZ);
160 caloEndCapDiscPositiveZNode.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
161 // Mirror of the negative endcap: the +z (maxZ, outer) end is the global maxZ
162 // boundary, so Expand there and keep Gap on the interior (-z) side.
163 caloEndCapDiscPositiveZNode.setResizeStrategies(ResizeStrategy::Gap, ResizeStrategy::Expand);
164
165 for (unsigned int sampleIndex = 0; sampleIndex < m_caloDiscSampleList.size(); ++sampleIndex) {
166 auto& sampleName = m_caloDiscSampleList.at(sampleIndex).first;
167 addCylindricalTrackingVolumeToCaloNode(caloEndCapDiscNegativeZNode, sampleName+"NegZ", caloRegionSampleSurfaceMap[caloRegion::DiscNegativeZ].at({sampleName, getSampleEnum(sampleName)}), volumeCounter, true);
168 volumeCounter++;
169 addCylindricalTrackingVolumeToCaloNode(caloEndCapDiscPositiveZNode, sampleName+"PosZ", caloRegionSampleSurfaceMap[caloRegion::DiscPositiveZ].at({sampleName, getSampleEnum(sampleName)}), volumeCounter, true);
170 volumeCounter++;
171 }
172
173 ATH_MSG_DEBUG("Have added all Barrel layers to caloBarrelCylinderNode");
174
175 // Add calo barrel node to the top level calo node.
176 itkCaloNode->addChild(caloNode);
177
178 //return the top level calo node
179 return itkCaloNode;
180}
181
183 ATH_MSG_DEBUG("Finalizing CaloBlueprintNodeBuilder");
184 return StatusCode::SUCCESS;
185}
186
187void ActsTrk::CaloBlueprintNodeBuilder::fillMaps(std::map<caloRegion, caloSampleSurfaceMap_t>& caloRegionSampleSurfaceMap,
188 std::map<caloRegion, caloSampleDDEElementsMap_t>& caloRegionSampleDDEElementsMap, std::map<std::string, double>& caloDimensions) const {
189
190
191 //loop over all possible calo sampling layers
192 //and create empty vectors of surfaces in the map
193
194 //Create map between each calorimeter sampling and a vector of
195 //cylinder surfaces. We can have N cylinders in a given sampling,
196 //and the value of N is determined by how often the average radius
197 //calculated for a given phi ring, at fixed Z, changes by more than
198 //a tolerance value
199
200 //Use the same loop to create map bwteeen sampling and vectors of DDE
201 for (const auto & currentSample : m_caloCylinderSymmetricSampleList) {
202 caloRegionSampleSurfaceMap[caloRegion::CylinderSymmetricZZero][currentSample] = std::vector<std::shared_ptr<Surface> >();
203 caloRegionSampleDDEElementsMap[caloRegion::CylinderSymmetricZZero][currentSample] = std::vector<const CaloDetDescrElement*>();
204 }
205
206 for (const auto & currentSample : m_caloCylinderAsymmetricSampleList) {
207 caloRegionSampleSurfaceMap[caloRegion::CylinderNegativeZ][currentSample] = std::vector<std::shared_ptr<Surface> >();
208 caloRegionSampleDDEElementsMap[caloRegion::CylinderNegativeZ][currentSample] = std::vector<const CaloDetDescrElement*>();
209 caloRegionSampleSurfaceMap[caloRegion::CylinderPositiveZ][currentSample] = std::vector<std::shared_ptr<Surface> >();
210 caloRegionSampleDDEElementsMap[caloRegion::CylinderPositiveZ][currentSample] = std::vector<const CaloDetDescrElement*>();
211 }
212
213 for (const auto & currentSample : m_caloDiscSampleList) {
214 caloRegionSampleSurfaceMap[caloRegion::DiscNegativeZ][currentSample] = std::vector<std::shared_ptr<Surface> >();
215 caloRegionSampleDDEElementsMap[caloRegion::DiscNegativeZ][currentSample] = std::vector<const CaloDetDescrElement*>();
216 caloRegionSampleSurfaceMap[caloRegion::DiscPositiveZ][currentSample] = std::vector<std::shared_ptr<Surface> >();
217 caloRegionSampleDDEElementsMap[caloRegion::DiscPositiveZ][currentSample] = std::vector<const CaloDetDescrElement*>();
218 }
219
220 double maxR = 0.0;
221 double maxPosZ = 0.0;
222 double minNegZ = 0.0;
223
224 //for each calo sampling collect all the DDE in a vector
225 for (const CaloDetDescrElement* theDDE : m_caloDetSecrMgr->element_range()){
226 if (!theDDE){
227 ATH_MSG_ERROR("Null pointer to CaloDetDescrElement");
228 continue;
229 }
230
231 if (theDDE->r() > maxR) maxR = theDDE->r();
232 if (theDDE->z() > maxPosZ) maxPosZ = theDDE->z();
233 if (theDDE->z() < minNegZ) minNegZ = theDDE->z();
234
235 CaloCell_ID::CaloSample currentSample=theDDE->getSampling();
236 std::pair <std::string, CaloCell_ID::CaloSample> samplePair = std::make_pair(getSampleName(currentSample), currentSample);
237
238 //check if have cylinder symmetric about z = 0
240 caloRegionSampleDDEElementsMap[caloRegion::CylinderSymmetricZZero][{samplePair.first,samplePair.second}].push_back(theDDE);
241 }
242
243 //check if have cylinder asymmetric about z = 0, if so check if in negative or positive z
245 if (theDDE->z() < 0.0) {
246 caloRegionSampleDDEElementsMap[caloRegion::CylinderNegativeZ][{samplePair.first,samplePair.second}].push_back(theDDE);
247 }
248 else {
249 caloRegionSampleDDEElementsMap[caloRegion::CylinderPositiveZ][{samplePair.first,samplePair.second}].push_back(theDDE);
250 }
251 }
252
253 //check if sampling is in disc list
254 if (std::find(m_caloDiscSampleList.begin(), m_caloDiscSampleList.end(), std::make_pair(samplePair.first,samplePair.second)) != m_caloDiscSampleList.end()) {
255 //check if in negative or positive z
256 if (theDDE->z() < 0.0) {
257 caloRegionSampleDDEElementsMap[caloRegion::DiscNegativeZ][{getSampleName(currentSample), currentSample}].push_back(theDDE);
258 }
259 else {
260 caloRegionSampleDDEElementsMap[caloRegion::DiscPositiveZ][{getSampleName(currentSample), currentSample}].push_back(theDDE);
261 }
262 }
263
264 }
265
266 caloDimensions["maxR"] = maxR;
267 caloDimensions["maxPosZ"] = maxPosZ;
268 caloDimensions["minNegZ"] = minNegZ;
269
270 auto sortAllLayersInZ = [&caloRegionSampleDDEElementsMap](const std::vector<std::pair<std::string, CaloCell_ID::CaloSample>>& caloSampleList, const caloRegion& region) {
271 for (const auto & currentSample : caloSampleList) {
272 std::vector<const CaloDetDescrElement*> currentElements = caloRegionSampleDDEElementsMap[region][currentSample];
273 std::sort(currentElements.begin(), currentElements.end(), [](const CaloDetDescrElement* a, const CaloDetDescrElement* b) {return a->z() < b->z();});
274 caloRegionSampleDDEElementsMap[region][currentSample] = std::move(currentElements);
275 }
276 };
277
278 //Sort the DDE, by Z, in all possible layers
282
283 auto sortAllLayersInR = [&caloRegionSampleDDEElementsMap](const std::vector<std::pair<std::string, CaloCell_ID::CaloSample>>& caloSampleList, const caloRegion& region) {
284 for (const auto& currentSample : caloSampleList) {
285 std::vector<const CaloDetDescrElement*> currentElements = caloRegionSampleDDEElementsMap[region][currentSample];
286 std::sort(currentElements.begin(), currentElements.end(), [](const CaloDetDescrElement* a, const CaloDetDescrElement* b) {return a->r() < b->r();});
287 caloRegionSampleDDEElementsMap[region][currentSample] = std::move(currentElements);
288 }
289 };
290
291 //Sort the DDE, by R, in all possible layers
294
295}
296
297void ActsTrk::CaloBlueprintNodeBuilder::generateCylinderSurfaces(caloSampleSurfaceMap_t& caloSampleSurfaceMap, caloSampleDDEElementsMap_t& caloSampleDDEElementsMap, bool asymmetricZ) const{
298
299 std::vector<std::pair<std::string, CaloCell_ID::CaloSample>> sampleList;
300 if (asymmetricZ) sampleList = m_caloCylinderAsymmetricSampleList;
301 else sampleList = m_caloCylinderSymmetricSampleList;
302
303 for (const auto & currentSample : sampleList) {
304
305 std::vector<const CaloDetDescrElement*> currentElements = caloSampleDDEElementsMap[currentSample];
306
307 double maxLArBRadius = 0.0, minLArBRadius = std::numeric_limits<double>::max();
308 double lowZLarB = 0.0, highZLarB = 0.0;
309
310 //loop over cells runs from -z to +z in a given sampling layer
311 //There are many cells with the same z value, but different phi values
312 //We will find the average radius of the cells in a given phi ring
313 double totalRadiusFixedPhi = 0.0;
314 bool firstCellInPhiRing = true;
315 unsigned int phiCounter = 0;
316
317 //Then we will also track changes in radius as we move in Z from
318 //each ring of cells in phi to the next ring of cells in phi
319 bool firstPhiRing = true;
320 double initialRadius = 0.0;
321 double initialZ = -std::numeric_limits<double>::max();
322
323 //check if we ever move along in Z value
324 bool movedInZ = false;
325
326 for (const CaloDetDescrElement* theDDE : currentElements){
327
328 double z = theDDE->z();
329 double radius = theDDE->r();
330
331 ATH_MSG_DEBUG(" Calo Sampling is " << currentSample.first);
332
333 if (firstCellInPhiRing) {
334 ATH_MSG_DEBUG("First Cell in phi ring " << currentSample.first << " has z = " << z << " and r = " << radius);
335 initialZ = z;
336 firstCellInPhiRing = false;
337 }
338
339 if (firstPhiRing) {
340 ATH_MSG_DEBUG("First Cell in layer " << currentSample.first << " has z = " << z << " and r = " << radius);
341 initialRadius = theDDE->r();
342 firstPhiRing = false;
343 lowZLarB = z;
344 }
345
346 ATH_MSG_DEBUG("Z and initialZ are " << z << " and " << initialZ);
347
348 //if z has not changed then we add the radius to the summed radius for this phi ring
349 //and increment the counter of cells in this phi ring
350 if (std::abs(z - initialZ) < 0.0001) {
351 ATH_MSG_DEBUG("phiCounter is " << phiCounter << " and radius is " << radius << " and totalRadiusFixedPhi is " << totalRadiusFixedPhi << " and hash is " << theDDE->calo_hash());
352 totalRadiusFixedPhi += radius;
353 phiCounter++;
354 continue;
355 }
356 else {
357 movedInZ = true;
358 firstCellInPhiRing = true;
359 if (phiCounter > 0) {
360 double cellRingRadius = totalRadiusFixedPhi / phiCounter;
361 totalRadiusFixedPhi = 0.0;
362 phiCounter = 0;
363
364 if (cellRingRadius > maxLArBRadius) maxLArBRadius = radius;
365 if (cellRingRadius < minLArBRadius) minLArBRadius = radius;
366
367 //if radius changes by more than tolerance, then we will create a cylinder
368 //with the average cell radius and length from neg to pos z
369 highZLarB = z;
370 ATH_MSG_DEBUG("Values of cellRingRadius, initialRadius, highZLarB and lowZLarB are " << cellRingRadius << ", " << initialRadius << ", " << highZLarB << " and " << lowZLarB);
371 if (std::abs(cellRingRadius - initialRadius) > m_radiusTolerance && highZLarB - lowZLarB > 0.0) {
372 ATH_MSG_DEBUG("CYLINDER: Create cylinder for layer " << currentSample.first);
373 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B radius are " << minLArBRadius << " " << maxLArBRadius);
374 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B z are " << lowZLarB << " " << highZLarB);
375
376 caloSampleSurfaceMap[currentSample].push_back(generateCylinderSurface(maxLArBRadius, minLArBRadius, lowZLarB, highZLarB, asymmetricZ));
377
378 //reset the dimensions of the cylinder to the initial conditions, in
379 //preparation for the next cylinder
380 firstPhiRing = true;
381 minLArBRadius = std::numeric_limits<double>::max();
382 maxLArBRadius = 0.0;
383 lowZLarB = 0.0;
384 highZLarB = 0.0;
385 }//if radius changes by more than tolerance
386 }//if at least one cell in phi (should always be the case!)
387 else ATH_MSG_ERROR("phiCounter is zero!");
388 }//if z has changed
389 }//loop over calorimeter DDE
390
391 if (0 == caloSampleSurfaceMap[currentSample].size()){
392 //If we never found any shift in Z whilst looping over the DDE
393 //then thee min/max Z and radius were not set
394 //so we set them here.
395 if (!movedInZ) {
396 lowZLarB = initialZ;
397 highZLarB = initialZ+0.001;
398 if (phiCounter > 0) {
399 maxLArBRadius = totalRadiusFixedPhi / phiCounter;
400 minLArBRadius = 0.0;
401 }
402 }
403 ATH_MSG_DEBUG("CYLINDER: Zero size Vector: Create cylinder for layer " << currentSample.first);
404 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B radius are " << minLArBRadius << " " << maxLArBRadius);
405 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B z are " << lowZLarB << " " << highZLarB);
406 caloSampleSurfaceMap[currentSample].push_back(generateCylinderSurface(maxLArBRadius, minLArBRadius, lowZLarB, highZLarB, asymmetricZ));
407 }
408 }
409}
410
411std::shared_ptr<CylinderSurface> ActsTrk::CaloBlueprintNodeBuilder::generateCylinderSurface(const double& maxLArBRadius, const double& minLArBRadius, const double& lowZLarB, const double& highZLarB, bool asymmetricZ) const{
412
413 //Characterise the dimensions of the cylinder
414 double LArBRadius = (maxLArBRadius + minLArBRadius) / 2.0;
415 double LArBLength = std::abs(highZLarB - lowZLarB);
416
417 ATH_MSG_DEBUG("Cylinder radius and length are " << LArBRadius << " and " << LArBLength);
418
419 if (asymmetricZ) {
420 double zShift = (highZLarB + lowZLarB) / 2.0;
421 ATH_MSG_DEBUG("Cylinder is asymmetric in Z, with shift of " << zShift);
422 return Surface::makeShared<CylinderSurface>(Transform3(Translation3(0.0, 0.0, zShift)), LArBRadius, LArBLength/2);
423 }
424 else return Surface::makeShared<CylinderSurface>(Transform3::Identity(), LArBRadius, LArBLength/2);
425
426}
427
429
430 for (const auto & currentSample : m_caloDiscSampleList) {
431
432 const std::vector<const CaloDetDescrElement*> & currentElements = caloSampleDDEElementsMap[currentSample];
433
434 //FCAL is treated differently because it is non-projective
435 //We create one surface for each of FCAL0, FCAL1 and FCAL2
436 if (CaloCell_ID::FCAL0 == currentSample.second || CaloCell_ID::FCAL1 == currentSample.second || CaloCell_ID::FCAL2 == currentSample.second) {
437 ATH_MSG_DEBUG("TOM3: FCAL sampling " << currentSample.second);
438
439 double fcalRMin = std::numeric_limits<double>::max();
440 double fcalRMax = std::numeric_limits<double>::min();
441 double fcalZSum = 0;
442 size_t cellCount = 0;
443
444 for (const CaloDetDescrElement* theDDE : currentElements) {
445 double r = theDDE->r();
446 if (r < fcalRMin) fcalRMin = r;
447 if (r > fcalRMax) fcalRMax = r;
448 fcalZSum += theDDE->z();
449 cellCount++;
450 }
451 if (cellCount == 0)[[unlikely]]{
452 ATH_MSG_WARNING("cellCount is zero in CaloBlueprintNodeBuilder::generateDiscSurfaces");
453 continue;
454 }
455 double fcalZ = fcalZSum / cellCount;
456 caloSampleSurfaceMap[currentSample].push_back(generateDiscSurface(fcalZ,fcalRMax, fcalRMin));
457 //now continue to the next sampling
458 continue;
459 }
460
461 //For other disc calorimeter layers we proceed to create many surfaces as needed.
462 double totalZFixedPhi = 0.0;
463 bool firstCellInPhiRing = true;
464 unsigned int phiCounter = 0;
465
466 bool firstPhiRing = true;
467 double initialRadius = 0.0;
468 double initialZ = -std::numeric_limits<double>::max();
469
470 //loop over cells runs from -z to +z in a given sampling layer
471 //There are many cells with the same z value, but different phi values
472 //We will find a min amd max radius in this phi ring.
473 double minDiscRadius = std::numeric_limits<double>::max(), maxDiscRadius = 0.0;
474
475 unsigned int currentElementsSize = currentElements.size();
476 unsigned int DDECounter = 0;
477
478 for (const CaloDetDescrElement* theDDE : currentElements){
479
480 bool isLastDDE = (DDECounter == (currentElementsSize-1));
481
482 ATH_MSG_DEBUG("Disc DDE with sampling, r and z of " << currentSample << ", " << theDDE->r() << ", " << theDDE->z());
483 ATH_MSG_DEBUG("isLastDDE is " << isLastDDE);
484
485 double z = theDDE->z();
486 double radius = theDDE->r();
487
488 if (firstCellInPhiRing) {
489 initialRadius = radius;
490 firstCellInPhiRing = false;
491 }
492
493 if (firstPhiRing) {
494 initialZ = z;
495 firstPhiRing = false;
496 minDiscRadius = radius;
497 }
498
499 //if radius is unchanged then we add the z to the summed z for this phi ring
500 //and increment the counter of cells in this phi ring
501 if (std::abs(radius - initialRadius) < 0.0001 && !isLastDDE) {
502 totalZFixedPhi += z;
503 phiCounter++;
504 DDECounter++;
505 continue;
506 }
507 else {
508 firstCellInPhiRing = true;
509 if (phiCounter > 0) {
510 double cellRingZ = totalZFixedPhi / phiCounter;
511 totalZFixedPhi = 0.0;
512 phiCounter = 0;
513
514 //if z changes by more than tolerance, then we will create a disc
515 //with the min and max radius found
516 //if we reach the last DDE and no new surface has been created, then we also create a disc surface
517 maxDiscRadius = radius;
518 ATH_MSG_DEBUG("cellRingZ, initialZ and z tolerance are " << cellRingZ << ", " << initialZ << " and " << m_zTolerance);
519 if (std::abs(cellRingZ - initialZ) > m_zTolerance || isLastDDE) {
520 ATH_MSG_DEBUG("DISC: About to create disc surface for sampling " << currentSample);
521 caloSampleSurfaceMap[currentSample].push_back(generateDiscSurface(cellRingZ,maxDiscRadius, minDiscRadius));
522 //reset the dimensions of the disc to the initial conditions, in
523 //preparation for the next disc
524 minDiscRadius = std::numeric_limits<double>::max();
525 maxDiscRadius = 0.0;
526 firstPhiRing = true;
527 }//if z changes by more than tolerance
528 }//if at least one cell in phi (should always be the case!)
529 else ATH_MSG_ERROR("phiCounter is zero!");
530 }//if radius has changed
531 DDECounter++;
532 }//loop over calorimeter DDE
533 }//loop over calo samplings
534}
535
536std::shared_ptr<Acts::DiscSurface> ActsTrk::CaloBlueprintNodeBuilder::generateDiscSurface(const double& z, const double& maxLArBRadius, const double& minLArBRadius) const{
537
538 ATH_MSG_DEBUG("DISC: Disc min and max radius are " << minLArBRadius << " and " << maxLArBRadius << " with z of " << z);
539 auto surface = Surface::makeShared<DiscSurface>(Amg::getTranslateZ3D(z), minLArBRadius, maxLArBRadius);
540
541 return surface;
542
543}
544
545void ActsTrk::CaloBlueprintNodeBuilder::addCylindricalTrackingVolumeToCaloNode(CylinderContainerBlueprintNode& containerNode, const std::string& volumeName,const std::vector<std::shared_ptr<Acts::Surface>>& surfaces, int layerIndex, const bool& isDisc) const{
546
547 // Construct the container node with geometry identifier and layer, and add the surfaces to the layer.
548 Acts::GeometryIdentifierBlueprintNode& geoIdNode = containerNode.withGeometryIdentifier();
549 geoIdNode.setAllVolumeIdsTo(s_caloBarrelId +
550 layerIndex);
551
552 AxisDirection axis = AxisDirection::AxisZ;
553 if (isDisc) axis = AxisDirection::AxisR;
554 CylinderContainerBlueprintNode& cylinder = geoIdNode.addCylinderContainer(volumeName,
555 axis);
556
557 cylinder.addLayer(volumeName + "_Layer", [&](auto& layer) {
558 layer.setSurfaces(surfaces);
559 layer.setEnvelope(Acts::ExtentEnvelope{{
560 .z = {0.1_mm, 0.1_mm},
561 .r = {2_mm, 2_mm},
562 }});
563 });
564
565}
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Acts::VolumeResizeStrategy ResizeStrategy
Acts::VolumeAttachmentStrategy AttachmentStrategy
std::map< std::pair< std::string, CaloCell_ID::CaloSample >, std::vector< std::shared_ptr< Acts::Surface > > > caloSampleSurfaceMap_t
std::map< std::pair< std::string, CaloCell_ID::CaloSample >, std::vector< const CaloDetDescrElement * > > caloSampleDDEElementsMap_t
std::unique_ptr< CaloDetDescrManager > buildCaloDetDescrNoAlign(ISvcLocator *svcLocator, IMessageSvc *msgSvc)
static Double_t a
size_t size() const
Number of registered mappings.
#define z
void addCylindricalTrackingVolumeToCaloNode(Acts::CylinderContainerBlueprintNode &containerNode, const std::string &volumeName, const std::vector< std::shared_ptr< Acts::Surface > > &surfaces, int layerIndex, const bool &isDisc) const
addCylindricalTrackingVolumeToCaloNode adds a cylindrical tracking volume to the calo node.
void fillMaps(std::map< caloRegion, caloSampleSurfaceMap_t > &caloRegionSampleSurfaceMap, std::map< caloRegion, caloSampleDDEElementsMap_t > &caloRegionSampleDDEElementsMap, std::map< std::string, double > &caloDimensions) const
fillMaps fills two maps.
std::vector< std::pair< std::string, CaloCell_ID::CaloSample > > m_caloDiscSampleList
std::string getSampleName(CaloCell_ID::CaloSample currentSample) const
std::shared_ptr< Acts::CylinderSurface > generateCylinderSurface(const double &maxLArBRadius, const double &minLArBRadius, const double &lowZLarB, const double &highZLarB, bool asymmetricZ) const
generateCylinderSurface generates a cylindrical surface for a given set of parameters.
std::shared_ptr< Acts::DiscSurface > generateDiscSurface(const double &z, const double &maxLArBRadius, const double &minLArBRadius) const
std::vector< std::pair< std::string, CaloCell_ID::CaloSample > > m_caloCylinderAsymmetricSampleList
void generateCylinderSurfaces(caloSampleSurfaceMap_t &caloSampleSurfaceMap, caloSampleDDEElementsMap_t &caloSampleDDEElementsMap, bool asymmetricZ) const
generateCylinderSurfaces generates cylindrical surfaces for each calo sampling.
std::shared_ptr< Acts::BlueprintNode > buildBlueprintNode(const Acts::GeometryContext &gctx, std::shared_ptr< Acts::BlueprintNode > &&childNode) override
Build the Itk Blueprint Node.
std::vector< std::pair< std::string, CaloCell_ID::CaloSample > > m_caloCylinderSymmetricSampleList
std::unique_ptr< CaloDetDescrManager > m_caloDetSecrMgr
CaloCell_ID::CaloSample getSampleEnum(const std::string &sampleName) const
void generateDiscSurfaces(caloSampleSurfaceMap_t &caloSampleSurfaceMap, caloSampleDDEElementsMap_t &caloSampleDDEElementsMap) const
CaloSampling::CaloSample CaloSample
Definition CaloCell_ID.h:53
This class groups all DetDescr information related to a CaloCell.
int r
Definition globals.cxx:22
Define the volume parts of the GeometryIdentifier for each ATLAS subsystem centrally.
constexpr std::size_t s_caloEnvelopeID
Volume Ids ofthe Calorimeter.
constexpr std::size_t s_caloBarrelId
Amg::Transform3D getTranslateZ3D(const double Z)
: Returns a shift transformation along the z-axis
IMessageSvc * getMessageSvc(bool quiet=false)
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
#define unlikely(x)