ATLAS Offline Software
Loading...
Searching...
No Matches
CaloBlueprintNodeBuilder.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#include "Acts/Geometry/Blueprint.hpp"
8#include "Acts/Geometry/PortalShell.hpp"
9#include "Acts/Geometry/Volume.hpp"
10#include "Acts/Geometry/StaticBlueprintNode.hpp"
11
12#include <Acts/Utilities/AxisDefinitions.hpp>
13#include <Acts/Geometry/ContainerBlueprintNode.hpp>
14
16
17#include "Acts/Surfaces/Surface.hpp"
19
20using namespace Acts;
21using namespace Acts::Experimental;
22
24 ATH_MSG_DEBUG("Initializing CaloBlueprintNodeBuilder");
25
28
29 return StatusCode::SUCCESS;
30}
31
32std::shared_ptr<BlueprintNode> ActsTrk::CaloBlueprintNodeBuilder::buildBlueprintNode(const GeometryContext& /*gctx*/,
33 std::shared_ptr<BlueprintNode>&& childNode) {
34
35 caloSampleSurfaceMap_t caloSampleSurfaceMap;
36 caloSampleDDEElementsMap_t caloSampleDDEElementsMap;
37 fillMaps(caloSampleSurfaceMap, caloSampleDDEElementsMap);
38
39 ATH_MSG_DEBUG("Have filled first two maps");
40
41 caloDimensionMap_t caloDimensionMap;
42 fillCaloDimensionsMap(caloDimensionMap, caloSampleDDEElementsMap);
43
44 ATH_MSG_DEBUG("Have filled calo dimensions map");
45
46 generateCylinderSurfaces(caloSampleSurfaceMap, caloSampleDDEElementsMap);
47
48 ATH_MSG_DEBUG("Have generated calorimeter cylindrical surfaces");
49
50 //The calo node is a container node that will hold the calo layers
51 auto caloNode = std::make_shared<CylinderContainerBlueprintNode>("CaloNode", AxisDirection::AxisR);
52 if (childNode) caloNode->addChild(std::move(childNode));
53
54 ATH_MSG_DEBUG("Top level calorimeter node created");
55
56 CylinderContainerBlueprintNode& EMBarrelCylinder = caloNode->addCylinderContainer("EMBarrel", AxisDirection::AxisR);
57 // Use Gap attachment strategy to fill gaps between layers
58 EMBarrelCylinder.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
59
60 ATH_MSG_DEBUG("EM Barrel container node created");
61
62 //Add all cylindrical layers that are symmetric about zero in Z.
63 addCylindricalTrackingVolumeToCaloNode(EMBarrelCylinder, caloDimensionMap, "PreSamplerB", caloSampleSurfaceMap[CaloCell_ID::PreSamplerB]);
64 addCylindricalTrackingVolumeToCaloNode(EMBarrelCylinder, caloDimensionMap, "EMB1", caloSampleSurfaceMap[CaloCell_ID::EMB1]);
65 addCylindricalTrackingVolumeToCaloNode(EMBarrelCylinder, caloDimensionMap, "EMB2", caloSampleSurfaceMap[CaloCell_ID::EMB2]);
66 addCylindricalTrackingVolumeToCaloNode(EMBarrelCylinder, caloDimensionMap, "EMB3", caloSampleSurfaceMap[CaloCell_ID::EMB3]);
67 addCylindricalTrackingVolumeToCaloNode(EMBarrelCylinder, caloDimensionMap, "TileBar0", caloSampleSurfaceMap[CaloCell_ID::TileBar0]);
68 addCylindricalTrackingVolumeToCaloNode(EMBarrelCylinder, caloDimensionMap, "TileBar1", caloSampleSurfaceMap[CaloCell_ID::TileBar1]);
69 addCylindricalTrackingVolumeToCaloNode(EMBarrelCylinder, caloDimensionMap, "TileBar2", caloSampleSurfaceMap[CaloCell_ID::TileBar2]);
70
71 ATH_MSG_DEBUG("Have added all Barrel layers to EMBarrelCylinder node");
72
73 return caloNode;
74}
75
77 ATH_MSG_DEBUG("Finalizing CaloBlueprintNodeBuilder");
78 return StatusCode::SUCCESS;
79}
80
81void ActsTrk::CaloBlueprintNodeBuilder::fillMaps(caloSampleSurfaceMap_t& caloSampleSurfaceMap, caloSampleDDEElementsMap_t& caloSampleDDEElementsMap) const {
82
83
84 //loop over all possible calo sampling layers
85 //and create empty vectors of surfaces in the map
86
87 //Create map between each calorimeter sampling and a vector of
88 //cylinder surfaces. We can have N cylinders in a given sampling,
89 //and the value of N is determined by how often the average radius
90 //calculated for a given phi ring, at fixed Z, changes by more than
91 //a tolerance value
92
93 //Use the same loop to create map bwteeen sampling and vectors of DDE
94 for (auto currentSample : m_caloCylinderSampleList) {
95 caloSampleSurfaceMap[currentSample] = std::vector<std::shared_ptr<Surface> >();
96 caloSampleDDEElementsMap[currentSample] = std::vector<const CaloDetDescrElement*>();
97 }
98
99 for (auto currentSample : m_caloDiscSampleList) {
100 caloSampleSurfaceMap[currentSample] = std::vector<std::shared_ptr<Surface> >();
101 caloSampleDDEElementsMap[currentSample] = std::vector<const CaloDetDescrElement*>();
102 }
103
104 //for each calo sampling collect all the DDE in a vector
105 for (const CaloDetDescrElement* theDDE : m_caloDetSecrMgr->element_range()){
106 if (!theDDE){
107 ATH_MSG_ERROR("Null pointer to CaloDetDescrElement");
108 continue;
109 }
110 CaloCell_ID::CaloSample currentSample=theDDE->getSampling();
111 caloSampleDDEElementsMap[currentSample].push_back(theDDE);
112 }
113
114 auto sortAllLayersInZ = [&caloSampleDDEElementsMap](const std::vector<CaloCell_ID::CaloSample>& caloSampleList) {
115 for (auto currentSample : caloSampleList) {
116 std::vector<const CaloDetDescrElement*> currentElements = caloSampleDDEElementsMap[currentSample];
117 std::sort(currentElements.begin(), currentElements.end(), [](const CaloDetDescrElement* a, const CaloDetDescrElement* b) {return a->z() < b->z();});
118 caloSampleDDEElementsMap[currentSample] = std::move(currentElements);
119 }
120 };
121
122 //Sort the DDE, by Z, in all possible layers
123 sortAllLayersInZ(m_caloCylinderSampleList);
124 sortAllLayersInZ(m_caloDiscSampleList);
125
126}
127
129
130 for (auto currentSample : m_caloCylinderSampleList) {
131
132 std::vector<const CaloDetDescrElement*> currentElements = caloSampleDDEElementsMap[currentSample];
133
134 double maxLArBRadius = 0.0, minLArBRadius = 999999.0;
135 double lowZLarB = 0.0, highZLarB = 0.0;
136
137 //loop over cells runs from -z to +z in a given sampling layer
138 //There are many cells with the same z value, but different phi values
139 //We will find the average radius of the cells in a given phi ring
140 double totalRadiusFixedPhi = 0.0;
141 bool firstCellInPhiRing = true;
142 unsigned int phiCounter = 0;
143
144 //Then we will also track changes in radius as we move in Z from
145 //each ring of cells in phi to the next ring of cells in phi
146 bool firstPhiRing = true;
147 double initialRadius = 0.0;
148 double initialZ = -999999.0;
149
150 for (const CaloDetDescrElement* theDDE : currentElements){
151
152 double z = theDDE->z();
153 double radius = theDDE->r();
154
155 ATH_MSG_DEBUG(" Calo Sampling is " << currentSample);
156
157 if (firstCellInPhiRing) {
158 ATH_MSG_DEBUG("First Cell in phi ring " << currentSample << " has z = " << z << " and r = " << radius);
159 initialZ = z;
160 firstCellInPhiRing = false;
161 }
162
163 if (firstPhiRing) {
164 ATH_MSG_DEBUG("First Cell in layer " << currentSample << " has z = " << z << " and r = " << radius);
165 initialRadius = theDDE->r();
166 firstPhiRing = false;
167 lowZLarB = z;
168 }
169
170 ATH_MSG_DEBUG("Z and initialZ are " << z << " and " << initialZ);
171
172 //if z has not changed then we add the radius to the summed radius for this phi ring
173 //and increment the counter of cells in this phi ring
174 if (std::abs(z - initialZ) < 0.0001) {
175 ATH_MSG_DEBUG("phiCounter is " << phiCounter << " and radius is " << radius << " and totalRadiusFixedPhi is " << totalRadiusFixedPhi << " and hash is " << theDDE->calo_hash());
176 totalRadiusFixedPhi += radius;
177 phiCounter++;
178 continue;
179 }
180 else {
181 firstCellInPhiRing = true;
182 if (phiCounter > 0) {
183 double cellRingRadius = totalRadiusFixedPhi / phiCounter;
184 totalRadiusFixedPhi = 0.0;
185 phiCounter = 0;
186
187 if (cellRingRadius > maxLArBRadius) maxLArBRadius = radius;
188 if (cellRingRadius < minLArBRadius) minLArBRadius = radius;
189
190 //if radius changes by more than tolerance, then we will create a cylinder
191 //with the average cell radius and length from neg to pos z
192 highZLarB = z;
193 ATH_MSG_DEBUG("Values of cellRingRadius, initialRadius, highZLarB and lowZLarB are " << cellRingRadius << ", " << initialRadius << ", " << highZLarB << " and " << lowZLarB);
194 if (std::abs(cellRingRadius - initialRadius) > m_radiusTolerance && highZLarB - lowZLarB > 0.0) {
195 ATH_MSG_DEBUG("CYLINDER: Create cylinder for layer " << currentSample);
196 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B radius are " << minLArBRadius << " " << maxLArBRadius);
197 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B z are " << lowZLarB << " " << highZLarB);
198
199 caloSampleSurfaceMap[currentSample].push_back(generateCylinderSurface(maxLArBRadius, minLArBRadius, lowZLarB, highZLarB));
200
201 //reset the dimensions of the cylinder to the initial conditions, in
202 //preparation for the next cylinder
203 firstPhiRing = true;
204 minLArBRadius = 999999.0;
205 maxLArBRadius = 0.0;
206 lowZLarB = 0.0;
207 highZLarB = 0.0;
208 }//if radius changes by more than tolerance
209 }//if at least one cell in phi (should always be the case!)
210 else ATH_MSG_ERROR("phiCounter is zero!");
211 }//if z has changed
212 }//loop over calorimeter DDE
213
214 if (0 == caloSampleSurfaceMap[currentSample].size()){
215 ATH_MSG_DEBUG("CYLINDER: Zero size Vector: Create cylinder for layer " << currentSample);
216 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B radius are " << minLArBRadius << " " << maxLArBRadius);
217 ATH_MSG_DEBUG("CYLINDER: Create Cylinder: Min and Max LAr B z are " << lowZLarB << " " << highZLarB);
218 caloSampleSurfaceMap[currentSample].push_back(generateCylinderSurface(maxLArBRadius, minLArBRadius, lowZLarB, highZLarB));
219 }
220 }
221}
222
223std::shared_ptr<CylinderSurface> ActsTrk::CaloBlueprintNodeBuilder::generateCylinderSurface(const double& maxLArBRadius, const double& minLArBRadius, const double& lowZLarB, const double& highZLarB) const{
224
225 //Characterise the dimensions of the cylinder
226 double LArBRadius = (maxLArBRadius + minLArBRadius) / 2.0;
227 double LArBLength = std::abs(highZLarB - lowZLarB);
228
229 ATH_MSG_DEBUG("Cylinder radius and length are " << LArBRadius << " and " << LArBLength);
230
231 //Now we need to transform the local cylinder centred on 0,0,0 into the global
232 //atlas coordinate system. To do this we shift the cylinder in Z away from zero
233 //to the midpoint in the Z coordinates used to build the cylinder.
234 auto transform = Transform3(Translation3(0.0,0.0,lowZLarB + (lowZLarB + highZLarB) / 2.0));
235 auto surface = Surface::makeShared<CylinderSurface>(transform,LArBRadius, LArBLength);
236
237 return surface;
238
239}
240
242
243 //initialise parameters we will use to find the dimensions of the calorimeter
244 double minR = 99999999999999;
245 double maxR = 0.0;
246 double minZ = 99999999999999;
247 double maxZ = -99999999999999;
248
249 //define functions we will use
250 auto checkMinR = [&minR](const CaloDetDescrElement* theDDE){double r = theDDE->r();if (r < minR) minR = r;};
251 auto checkMaxR = [&maxR](const CaloDetDescrElement* theDDE){double r = theDDE->r();if (r > maxR) maxR = r;};
252 auto checkMinMaxZ = [&minZ,&maxZ](const CaloDetDescrElement* theDDE){double z = theDDE->z();if (z > maxZ) maxZ = z;if (z < minZ) minZ = z;};
253 auto calcHalfLengthZ = [&minZ, &maxZ](){return (maxZ - minZ) / 2.0;};
254
255 //function to use the above to get minR, maxR and halfLengthZ
256 auto getMinRMaxRHalfLengthZ = [&minR, &maxR, &minZ, &maxZ, &caloSampleDDEElementsMap, &checkMinR, &checkMaxR, &checkMinMaxZ, &calcHalfLengthZ](CaloCell_ID::CaloSample currentSample) {
257
258 minR = 99999999999999;
259 maxR = 0.0;
260 minZ = 99999999999999;
261 maxZ = -99999999999999;
262
263 for (const CaloDetDescrElement* theDDE : caloSampleDDEElementsMap[currentSample]) {
264 checkMinR(theDDE);
265 checkMaxR(theDDE);
266 checkMinMaxZ(theDDE);
267 }
268
269 return calcHalfLengthZ();
270 };
271
272 //find minR from PreSamplerB
273 for (const CaloDetDescrElement* theDDE : caloSampleDDEElementsMap[CaloCell_ID::PreSamplerB]) checkMinR(theDDE);
274
275 //find maxR from TileBar2, TileGap2 and Tile Ext2
276 std::vector<const CaloDetDescrElement*> DDE_TileBar2 = caloSampleDDEElementsMap[CaloCell_ID::TileBar2];
277 std::vector<const CaloDetDescrElement*> DDE_TileGap2 = caloSampleDDEElementsMap[CaloCell_ID::TileGap2];
278 std::vector<const CaloDetDescrElement*> DDE_TileExt2 = caloSampleDDEElementsMap[CaloCell_ID::TileExt2];
279
280 for (auto currentSample : {CaloCell_ID::TileBar2, CaloCell_ID::TileGap2, CaloCell_ID::TileExt2}) {
281 for (const CaloDetDescrElement* theDDE : caloSampleDDEElementsMap[currentSample]) checkMaxR(theDDE);
282 }
283
284 ATH_MSG_DEBUG("Min R in PreSamplerB is " << minR);
285 ATH_MSG_DEBUG("Max R in TileBar2, TileGap2 and TileExt2 is " << maxR);
286
287 caloDimensionsMap["CaloMinR"] = minR;
288 caloDimensionsMap["CaloMaxR"] = maxR;
289
290 //find minZ and maxZ from all barrel samplings
291 for (auto currentSample : {CaloCell_ID::PreSamplerB, CaloCell_ID::EMB1, CaloCell_ID::EMB2, CaloCell_ID::EMB3, CaloCell_ID::TileBar0, CaloCell_ID::TileBar1, CaloCell_ID::TileBar2, CaloCell_ID::TileGap1, CaloCell_ID::TileGap2, CaloCell_ID::TileExt0, CaloCell_ID::TileExt1, CaloCell_ID::TileExt2}) {
292 for (const CaloDetDescrElement* theDDE : caloSampleDDEElementsMap[currentSample]) checkMinMaxZ(theDDE);
293 }
294
295 caloDimensionsMap["CaloMinZ"] = minZ;
296 caloDimensionsMap["CaloMaxZ"] = maxZ;
297
298 double halfLengthZ = calcHalfLengthZ();
299 ATH_MSG_DEBUG("Half length in Z for entire barrel is " << halfLengthZ);
300 caloDimensionsMap["CaloHalfLengthZ"] = halfLengthZ;
301
302 auto putMinRMaxRHalfLengthZInMap = [&caloDimensionsMap,&minR,&maxR,&halfLengthZ](const std::string& sampleString){
303 //PresamplerB, TileBar0/2, TileGap1/2, TileExt0/1/2 return the same radial value for all DDE, so need to make a small offset because a zero thickness
304 //cylinder is not allowed by Acts volumes etc.
305 float smallOffset = 0.1;
306 if (std::fabs((maxR - minR)) < smallOffset) maxR = minR + smallOffset;
307 caloDimensionsMap[sampleString + "MinR"] = minR;
308 caloDimensionsMap[sampleString + "MaxR"] = maxR;
309 caloDimensionsMap[sampleString + "HalfLengthZ"] = halfLengthZ;
310 };
311
312 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::PreSamplerB);
313 putMinRMaxRHalfLengthZInMap("PreSamplerB");
314 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::EMB1);
315 putMinRMaxRHalfLengthZInMap("EMB1");
316 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::EMB2);
317 putMinRMaxRHalfLengthZInMap("EMB2");
318 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::EMB3);
319 putMinRMaxRHalfLengthZInMap("EMB3");
320 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::TileBar0);
321 putMinRMaxRHalfLengthZInMap("TileBar0");
322 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::TileBar1);
323 putMinRMaxRHalfLengthZInMap("TileBar1");
324 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::TileBar2);
325 putMinRMaxRHalfLengthZInMap("TileBar2");
326 halfLengthZ = getMinRMaxRHalfLengthZ(CaloCell_ID::TileGap1);
327
328}
329
330void ActsTrk::CaloBlueprintNodeBuilder::addCylindricalTrackingVolumeToCaloNode(CylinderContainerBlueprintNode& containerNode, caloDimensionMap_t& caloDimensionMap, const std::string& volumeName,const std::vector<std::shared_ptr<Surface>>& surfaces) const{
331
332 CylinderContainerBlueprintNode& cylinder = containerNode.addCylinderContainer(volumeName, AxisDirection::AxisZ);
333
334 ATH_MSG_DEBUG("Creating TrackingVolume for " << volumeName << " with minR, maxR and halfLengthZ of " << caloDimensionMap[volumeName+"MinR"] << ", " << caloDimensionMap[volumeName+"MaxR"] << " and " << caloDimensionMap[volumeName+"HalfLengthZ"]);
335
336 auto trackingVolume = std::make_unique<TrackingVolume>(
337 Transform3::Identity(),
338 std::make_shared<CylinderVolumeBounds>(caloDimensionMap[volumeName+"MinR"], caloDimensionMap[volumeName+"MaxR"], caloDimensionMap[volumeName+"HalfLengthZ"]),
339 volumeName);
340
341 for (auto surface : surfaces) trackingVolume->addSurface(std::move(surface));
342
343
344 cylinder.addStaticVolume(std::move(trackingVolume));
345
346}
#define ATH_MSG_ERROR(x)
#define ATH_MSG_DEBUG(x)
std::map< CaloCell_ID::CaloSample, std::vector< std::shared_ptr< Acts::Surface > > > caloSampleSurfaceMap_t
std::map< CaloCell_ID::CaloSample, std::vector< const CaloDetDescrElement * > > caloSampleDDEElementsMap_t
std::map< std::string, double > caloDimensionMap_t
std::unique_ptr< CaloDetDescrManager > buildCaloDetDescrNoAlign(ISvcLocator *svcLocator, IMessageSvc *msgSvc)
static Double_t a
#define z
void fillCaloDimensionsMap(caloDimensionMap_t &caloDimensionsMap, caloSampleDDEElementsMap_t &caloSampleDDEElementsMap) const
fillCaloDimensionsMap fills a map of calorimeter dimensions for each sampling layer.
Gaudi::Property< double > m_radiusTolerance
void generateCylinderSurfaces(caloSampleSurfaceMap_t &caloSampleSurfaceMap, caloSampleDDEElementsMap_t &caloSampleDDEElementsMap) const
generateCylinderSurfaces generates cylindrical surfaces for each calo sampling.
std::shared_ptr< Acts::CylinderSurface > generateCylinderSurface(const double &maxLArBRadius, const double &minLArBRadius, const double &lowZLarB, const double &highZLarB) const
generateCylinderSurface generates a cylindrical surface for a given set of parameters.
std::vector< CaloCell_ID::CaloSample > m_caloCylinderSampleList
void addCylindricalTrackingVolumeToCaloNode(Acts::Experimental::CylinderContainerBlueprintNode &containerNode, caloDimensionMap_t &caloDimensionMap, const std::string &volumeName, const std::vector< std::shared_ptr< Acts::Surface > > &surfaces) const
addCylindricalTrackingVolumeToCaloNode adds a cylindrical tracking volume to the calo node.
std::vector< CaloCell_ID::CaloSample > m_caloDiscSampleList
void fillMaps(caloSampleSurfaceMap_t &caloSampleSurfaceMap, caloSampleDDEElementsMap_t &caloSampleDDEElementsMap) const
fillMaps fills two maps.
std::shared_ptr< Acts::Experimental::BlueprintNode > buildBlueprintNode(const Acts::GeometryContext &gctx, std::shared_ptr< Acts::Experimental::BlueprintNode > &&childNode) override
Build the Itk Blueprint Node.
std::unique_ptr< CaloDetDescrManager > m_caloDetSecrMgr
CaloSampling::CaloSample CaloSample
Definition CaloCell_ID.h:53
This class groups all DetDescr information related to a CaloCell.
int r
Definition globals.cxx:22
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.