ATLAS Offline Software
Loading...
Searching...
No Matches
SCT_FwdRing.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "SCT_FwdRing.h"
6
9
12
13#include "SCT_FwdModule.h"
14#include "SCT_FwdCoolingBlock.h"
15
17
18#include "GeoModelRead/ReadGeoModel.h"
19#include "GeoModelKernel/GeoTube.h"
20#include "GeoModelKernel/GeoBox.h"
21#include "GeoModelKernel/GeoLogVol.h"
22#include "GeoModelKernel/GeoPhysVol.h"
23#include "GeoModelKernel/GeoNameTag.h"
24#include "GeoModelKernel/GeoIdentifierTag.h"
25#include "GeoModelKernel/GeoTransform.h"
26#include "GeoModelKernel/GeoAlignableTransform.h"
27#include "GeoModelKernel/GeoMaterial.h"
28#include "GeoModelKernel/GeoShapeShift.h"
29#include "GeoModelKernel/GeoDefinitions.h"
30#include "GaudiKernel/PhysicalConstants.h"
31
32#include <cmath>
33#include <sstream>
34#include <utility>
35
36inline double sqr(double x) {return x*x;}
37
38SCT_FwdRing::SCT_FwdRing(const std::string & name,
40 int iWheel,
41 int iRing,
42 int ec,
43 InDetDD::SCT_DetectorManager* detectorManager,
44 SCT_GeometryManager* geometryManager,
45 SCT_MaterialManager* materials,
46 GeoModelIO::ReadGeoModel* sqliteReader,
47 std::shared_ptr<std::map<std::string, GeoFullPhysVol*>> mapFPV,
48 std::shared_ptr<std::map<std::string, GeoAlignableTransform*>> mapAX)
49 : SCT_UniqueComponentFactory(name, detectorManager, geometryManager, materials, sqliteReader, std::move(mapFPV), std::move(mapAX)),
50 m_iWheel(iWheel),
51 m_iRing(iRing),
52 m_endcap(ec),
54{
58
59}
60
61void
63{
64
65 const SCT_ForwardParameters * parameters = m_geometryManager->forwardParameters();
66 m_numModules = parameters->fwdRingNumModules(m_iRing);
67
68 if(!m_sqliteReader){
69
70 const SCT_GeneralParameters * generalParameters = m_geometryManager->generalParameters();
71 m_safety = generalParameters->safety();
72
73 m_moduleStagger = parameters->fwdRingModuleStagger(m_iRing);
74 m_refStartAngle = parameters->fwdRingPhiOfRefModule(m_iRing);
75 m_refFirstStagger = parameters->fwdRingStaggerOfRefModule(m_iWheel, m_iRing, m_endcap);
76 m_ringSide = parameters->fwdWheelRingSide(m_iWheel, m_iRing, m_endcap);
77 m_stereoSign = parameters->fwdWheelStereoType(m_iWheel);
78 m_ringOffset = parameters->fwdRingDistToDiscCenter(m_iRing);
79 m_discSupportThickness = parameters->fwdDiscSupportThickness();
80 m_discRotated = (parameters->fwdRingUsualRingSide(m_iRing) != m_ringSide);
82 }
83 // Set numerology
84 m_detectorManager->numerology().setNumPhiModulesForDiskRing(m_iWheel,m_iRing,m_numModules);
85
86}
87
91
92const GeoLogVol *
94{
95
96 // Make a ring. This is made of two half rings. They are identical but as
97 // we need different identifiers they are made seperately.
98 // We will refer to the two halves as inner and outer.
99 // Inner will be the one closest to support when disk is on side of support
100 // furthest from IP. z of Inner < z of Outer.
101 // In later versions need to decide if I should rotate rings that are on IP side of
102 // support by 180 around x (or y) axis.
103
104 // Make sure we have even number of modules
105 if (m_numModules % 2) std::cout << "SCT_FwdRing: Number of modules in ring must be even." << std::endl;
106
107
108 // We define here the module with id = 0 as the nearest module to phi = 0.
109 // It is at most 1/2 a division from 0, ie Between -0.5*divisionAngle to +0.5*divisionAngle.
110 // For old number it was the first module with positive phi.
111
112 // The parameter refStartAngle is the angle of any module.
113 // refFirstStagger is whether this is an upper (+1) or lower (-1) module.
114 // The stagger and angle of the module with id = 0, is calculated from this.
115
116 double angle = m_refStartAngle;
117 // If disc is rotated then recalculate the angle.
118 // It assumed the disc is rotated around the Y axis.
119 // TODO: Check this assumption.
120 if (m_discRotated) angle = Gaudi::Units::pi - angle;
121 double divisionAngle = 2*Gaudi::Units::pi / m_numModules;
122
123 // Now we choose module 0 as the first module with -0.5 * divAngle < phi <= 0.5 * divAngle
124 double moduleCount = angle / divisionAngle;
125 int moduleCountInt = static_cast<int>(floor(moduleCount +0.5 -0.0001)); // The -0.0001 allows slightly positive
126 // in case of rounding errors.
127 m_startAngle = divisionAngle * (moduleCount - moduleCountInt);
128
129 // Determine numbering for -ve endcap.
130 // This is for a rotation around Y axis.
131 // After rotation we want the first module closest to phi = 0.
132 double angleNegEC = Gaudi::Units::pi - m_startAngle;
133 double moduleCountNegEC = angleNegEC / divisionAngle;
134 m_moduleZero = static_cast<int>(floor(moduleCountNegEC + 0.5 - 0.0001));
135
136 if(m_sqliteReader) return nullptr;
137
138 // Determine if it is an upper or lower.
140 if (moduleCountInt % 2) m_firstStagger = -m_refFirstStagger;
141
143
144 // Make envelope for ring
145 double moduleClearanceZ = 0.6 * Gaudi::Units::mm; // Arbitrary choice
146 double moduleClearanceR = 0.5 * Gaudi::Units::mm;
147
148 m_innerRadius = m_module->innerRadius() - 0.5*m_module->stereoAngle()*(0.5*m_module->innerWidth()) - moduleClearanceR;
149 m_outerRadius = sqrt(sqr(m_module->outerRadius()) + sqr(0.5*m_module->outerWidth()))
150 + 0.5*m_module->stereoAngle()*(0.5*m_module->outerWidth()) + moduleClearanceR;
151
152 // Calculate clearance we have. NB. This is an approximate.
153 m_thicknessOuter = 0.5 * m_module->thickness() + m_moduleStagger + moduleClearanceZ;
155 // We have to at least include 1*m_safety as the moduleservices envelope is increased by this amount.
156 // m_maxModuleServicesBaseToRingCenter is calculated in makeModuleServices()
157
159
160 // We want the center in z to be at the module mid plane. So we shift the volume.
161 double envelopeShift = -m_ringSide * (0.5 * m_thickness - m_thicknessOuter);
162
163 const GeoTube * tmpShape = new GeoTube(m_innerRadius, m_outerRadius, 0.5 * m_thickness);
164 const GeoShape & ringEnvelopeShape = (*tmpShape << GeoTrf::Translate3D(0, 0, envelopeShift));
165 GeoLogVol * ringLog = new GeoLogVol(getName(), &ringEnvelopeShape, m_materials->gasMaterial());
166
167 return ringLog;
168}
169
170
171
172GeoVPhysVol *
174{
175
176 // Physical volume for the half ring
177 GeoPhysVol * ring=nullptr;
178 bool negativeEndCap = (id.getBarrelEC() < 0);
179
180 if(!m_sqliteReader){
181
182 ring=new GeoPhysVol(m_logVolume);
183
184 double deltaPhi = 360*Gaudi::Units::degree / m_numModules;
185
186 for (int i = 0; i < m_numModules; i++){
187
188 // As used by the identifier
189 int idNumber = i;
190
191 // Alternate upper/lower
192 int staggerUpperLower = m_firstStagger;
193 if (i%2) staggerUpperLower = -m_firstStagger;
194
195 // The negative endcap is rotated and so we have to play some tricks to get the
196 // identifier numbering right.
197
198 // In order to get the identifiers going in the direction of
199 // increasing phi we have to invert them in the negative endcap.
200
201 // Although the endcaps differ slightly (some upper/lower swaps) we build them in much the same
202 // way and change just the numbering.
203
204 // The id number for the detector element
205 int idModule = idNumber;
206
207 if (negativeEndCap) {
208 // identifiers go in the opposite direction for the negative endcap.
209 // We renumber so that module number "moduleZero" becomes zero.
210 idModule = (m_numModules + m_moduleZero - idNumber) % m_numModules;
211 }
212
213 // The module is a TRD with length along z-axis.
214 // We need to rotate this so length is along the y-axis
215 // This can be achieved with a 90 deg rotation around Y.
216 // This leaves the depth axis point in the -z direction which
217 // is correct for modules mounted on the -ve side (side closest to the IP, ringSide = -1).
218 // For modules mounted on the opposite side we
219 // rotate 180 around X so that the depth axis is pointing in the same direction as z.
220 // Finally we rotate about z by phi and the 0.5*stereo (ie the u-phi or v-phi orientation)
221
222 // It is assumed that the module is centered on the physics center (center of sensor)
223
224 double phi = i * deltaPhi + m_startAngle;
225
226 GeoTrf::Transform3D rot = GeoTrf::RotateZ3D(phi + 0.5 * m_module->stereoAngle() * m_stereoSign);
227 if (m_ringSide > 0) {
228 rot = rot*GeoTrf::RotateX3D(180*Gaudi::Units::degree);
229 }
230 rot = rot*GeoTrf::RotateY3D(90*Gaudi::Units::degree);
231
232 double zPos = staggerUpperLower * m_moduleStagger * m_ringSide;
233 GeoTrf::Vector3D xyz(m_module->sensorCenterRadius(), 0, zPos);
234 xyz = GeoTrf::RotateZ3D(phi)*xyz;
235 GeoTrf::Transform3D modulePos = GeoTrf::Translate3D(xyz.x(),xyz.y(),xyz.z())*rot;
236
237
238 // Add the module
239 std::string moduleName = "FwdModuleR" + intToString(m_iRing) + "#" + intToString(idModule);
240 ring->add(new GeoNameTag(moduleName));
241 ring->add(new GeoIdentifierTag(idModule));
242 GeoAlignableTransform * moduleTransform = new GeoAlignableTransform(modulePos);
243 ring->add(moduleTransform);
244 id.setPhiModule(idModule);
245 GeoVPhysVol * modulePV = m_module->build(id);
246 ring->add(modulePV);
247
248 // Store alignable transform
249 m_detectorManager->addAlignableTransform(1, id.getWaferId(), moduleTransform, modulePV);
250
251 // Add the moduleServices (contains the cooling block)
252 // In principle this should also be rotated by the stereo angle (although one
253 // would need to be care were the center of rotation is. However this is not
254 // really necessary for the services so we do not bother.
255
256 double zModuleServices = 0;
257 double rModuleServices = 0;
258 GeoVPhysVol * moduleServices = nullptr;
259 if (staggerUpperLower > 0){ // Upper
260 zModuleServices = m_moduleServicesHiZPos * m_ringSide;
261 rModuleServices = m_moduleServicesHiRPos;
262 moduleServices = m_moduleServicesHi;
263 } else { // Lower
264 zModuleServices = m_moduleServicesLoZPos * m_ringSide;
265 rModuleServices = m_moduleServicesLoRPos;
266 moduleServices = m_moduleServicesLo;
267 }
268
269
270 ring->add(new GeoTransform(GeoTrf::RotateZ3D(phi)*GeoTrf::Translate3D(rModuleServices, 0, zModuleServices)));
271 ring->add(moduleServices);
272
273 }
274 }
275 else{
276
277
278 for (int i = 0; i < m_numModules; i++){
279
280 // As used by the identifier
281 int idNumber = i;
282
283 // Alternate upper/lower
284 //int staggerUpperLower = m_firstStagger;
285 //if (i%2) staggerUpperLower = -m_firstStagger;
286
287 // The negative endcap is rotated and so we have to play some tricks to get the
288 // identifier numbering right.
289
290 // In order to get the identifiers going in the direction of
291 // increasing phi we have to invert them in the negative endcap.
292
293 // Although the endcaps differ slightly (some upper/lower swaps) we build them in much the same
294 // way and change just the numbering.
295
296 // The id number for the detector element
297 int idModule = idNumber;
298
299 if (negativeEndCap) {
300 // identifiers go in the opposite direction for the negative endcap.
301 // We renumber so that module number "moduleZero" becomes zero.
302 idModule = (m_numModules + m_moduleZero - idNumber) % m_numModules;
303 }
304
305 id.setPhiModule(idModule);
306 m_module->build(id);
307 std::string key="FwdModuleR" + intToString(m_iRing)+"_"+std::to_string(id.getBarrelEC())+"_"+std::to_string(id.getLayerDisk())+"_"+std::to_string(id.getEtaModule())+"_"+std::to_string(id.getPhiModule());
308
309 // Store alignable transform
310 m_detectorManager->addAlignableTransform(1, id.getWaferId(), (*m_mapAX)[key], (*m_mapFPV)[key]);
311
312 }
313
314 }
315
316 return ring;
317}
318
319
320
321
322// These are offset in z by m_moduleStagger and one is rotated relative to the other by the
323// 360/m_numModules.
324
325
326
327void
329{
330 // Make an envelope to contain the two cooling blocks. Not sure if there much to gain by this
331 // rather than just adding the cooling blocks directly to the ring but it may help if we decide
332 // to add more things to it later. We call it module services.
333
334 // Cooling blocks for the upper Modules
335 m_coolingBlockHiMain = std::make_unique<SCT_FwdCoolingBlock>("CoolingBlkHiMain",SCT_FwdCoolingBlock::UPPER, SCT_FwdCoolingBlock::MAIN,
337 m_coolingBlockHiSec = std::make_unique<SCT_FwdCoolingBlock>("CoolingBlkHiSec", SCT_FwdCoolingBlock::UPPER, SCT_FwdCoolingBlock::SECONDARY,
339 // Cooling blocks for the lower Modules
340 m_coolingBlockLoMain = std::make_unique<SCT_FwdCoolingBlock>("CoolingBlkLoMain",SCT_FwdCoolingBlock::LOWER, SCT_FwdCoolingBlock::MAIN,
342 m_coolingBlockLoSec = std::make_unique<SCT_FwdCoolingBlock>("CoolingBlkLoSec", SCT_FwdCoolingBlock::LOWER, SCT_FwdCoolingBlock::SECONDARY,
344
345 double coolingBlkMainR = m_module->mainMountPointRadius();
346 double coolingBlkSecR = m_module->endModuleRadius(); // This is the end of the module. Align block with the end.
347 double moduleServicesHiWidth = std::max(m_coolingBlockHiMain->rphi(), m_coolingBlockHiSec->rphi());
348 double moduleServicesLoWidth = std::max(m_coolingBlockLoMain->rphi(), m_coolingBlockLoSec->rphi());
349 double moduleServicesHiLength = std::abs(coolingBlkMainR - coolingBlkSecR) + 0.5 * m_coolingBlockHiMain->deltaR();
350 double moduleServicesLoLength = std::abs(coolingBlkMainR - coolingBlkSecR) + 0.5 * m_coolingBlockLoMain->deltaR();
351 double moduleServicesHiThickness = std::max(m_coolingBlockHiMain->thickness(), m_coolingBlockHiSec->thickness());
352 double moduleServicesLoThickness = std::max(m_coolingBlockLoMain->thickness(), m_coolingBlockLoSec->thickness());
353
354 // Radial position of this services volume. The calculation depends on whether the main cooling block is at the outer or inner radius.
355 double moduleOrientation = (coolingBlkMainR > coolingBlkSecR) ? +1 : -1;
356 m_moduleServicesHiRPos = coolingBlkMainR + moduleOrientation * (0.5 * m_coolingBlockHiMain->deltaR() - 0.5 * moduleServicesHiLength);
357 m_moduleServicesLoRPos = coolingBlkMainR + moduleOrientation * (0.5 * m_coolingBlockLoMain->deltaR() - 0.5 * moduleServicesLoLength);
358
359 // Radial position of the mid point of the secondary cooling block
360 double coolingBlkHiSecRMid = coolingBlkSecR + moduleOrientation * 0.5 * m_coolingBlockHiSec->deltaR();
361 double coolingBlkLoSecRMid = coolingBlkSecR + moduleOrientation * 0.5 * m_coolingBlockLoSec->deltaR();
362
363 // z position. Set so the surface closest to the disc support is at a fixed distance relative to the disc support or ring center.
364 // The distance between the disc surface and cooling block surface is obtained from the cooling block.
365 // We average the number even though they are all the same.
366 double coolingBlockOffsetHi = 0.5 * (m_coolingBlockHiMain->offsetFromDisc() + m_coolingBlockHiSec->offsetFromDisc());
367 double coolingBlockOffsetLo = 0.5 * (m_coolingBlockLoMain->offsetFromDisc() + m_coolingBlockLoSec->offsetFromDisc());
368 double moduleServicesBaseToRingCenterHi = m_ringOffset - 0.5*m_discSupportThickness - coolingBlockOffsetHi;
369 double moduleServicesBaseToRingCenterLo = m_ringOffset - 0.5*m_discSupportThickness - coolingBlockOffsetLo;
370 m_maxModuleServicesBaseToRingCenter = std::max(moduleServicesBaseToRingCenterHi, moduleServicesBaseToRingCenterLo);
371 m_moduleServicesHiZPos = -(moduleServicesBaseToRingCenterHi - 0.5 * moduleServicesHiThickness);
372 m_moduleServicesLoZPos = -(moduleServicesBaseToRingCenterLo - 0.5 * moduleServicesLoThickness);
373
374 // For checking clearance. Position of outer surface of module service with respect to the ring center.
375 //
376 m_moduleServicesHiOuterZPos = -(moduleServicesBaseToRingCenterHi - moduleServicesHiThickness);
377 m_moduleServicesLoOuterZPos = -(moduleServicesBaseToRingCenterLo - moduleServicesLoThickness);
378
379 const GeoBox * moduleServicesHiShape = new GeoBox(0.5*moduleServicesHiLength + m_safety,
380 0.5*moduleServicesHiWidth + m_safety,
381 0.5*moduleServicesHiThickness + m_safety);
382 const GeoBox * moduleServicesLoShape = new GeoBox(0.5*moduleServicesLoLength + m_safety,
383 0.5*moduleServicesLoWidth + m_safety,
384 0.5*moduleServicesLoThickness + m_safety);
385 const GeoLogVol * moduleServicesHiLog = new GeoLogVol("ModuleServicesHi", moduleServicesHiShape, m_materials->gasMaterial());
386 const GeoLogVol * moduleServicesLoLog = new GeoLogVol("ModuleServicesLo", moduleServicesLoShape, m_materials->gasMaterial());
387
388 m_moduleServicesHi = new GeoPhysVol(moduleServicesHiLog);
389 m_moduleServicesLo = new GeoPhysVol(moduleServicesLoLog);
390
391 // Add the cooling blocks
392 // Main Upper
393 m_moduleServicesHi->add(new GeoTransform(GeoTrf::Translate3D(coolingBlkMainR - m_moduleServicesHiRPos, 0, 0)));
394 m_moduleServicesHi->add(m_coolingBlockHiMain->getVolume());
395 // Secondary Upper
396 m_moduleServicesHi->add(new GeoTransform(GeoTrf::Translate3D(coolingBlkHiSecRMid - m_moduleServicesHiRPos, 0, 0)));
397 m_moduleServicesHi->add(m_coolingBlockHiSec->getVolume());
398 // Main Lower
399 m_moduleServicesLo->add(new GeoTransform(GeoTrf::Translate3D(coolingBlkMainR - m_moduleServicesLoRPos, 0, 0)));
400 m_moduleServicesLo->add(m_coolingBlockLoMain->getVolume());
401 // Secondary Lower
402 m_moduleServicesLo->add(new GeoTransform(GeoTrf::Translate3D(coolingBlkLoSecRMid - m_moduleServicesLoRPos, 0, 0)));
403 m_moduleServicesLo->add(m_coolingBlockLoSec->getVolume());
404}
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
#define sqr(t)
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
#define xyz
#define x
Dedicated detector manager extending the functionality of the SiDetectorManager with dedicated SCT in...
const std::string & getName() const
std::string intToString(int i) const
InDetDD::SCT_DetectorManager * m_detectorManager
SCT_GeometryManager * m_geometryManager
SCT_MaterialManager * m_materials
double m_moduleServicesLoRPos
GeoPhysVol * m_moduleServicesLo
std::unique_ptr< SCT_FwdCoolingBlock > m_coolingBlockLoSec
double m_moduleServicesHiZPos
SCT_FwdRing(const std::string &name, SCT_FwdModule *module, int iWheel, int iRing, int ec, InDetDD::SCT_DetectorManager *detectorManager, SCT_GeometryManager *geometryManager, SCT_MaterialManager *materials, GeoModelIO::ReadGeoModel *sqliteReader, std::shared_ptr< std::map< std::string, GeoFullPhysVol * > > mapFPV, std::shared_ptr< std::map< std::string, GeoAlignableTransform * > > mapAX)
std::unique_ptr< SCT_FwdCoolingBlock > m_coolingBlockLoMain
double m_innerRadius
Definition SCT_FwdRing.h:91
std::unique_ptr< SCT_FwdCoolingBlock > m_coolingBlockHiMain
GeoPhysVol * m_moduleServicesHi
double m_refStartAngle
Definition SCT_FwdRing.h:85
const SCT_FwdModule * module() const
Definition SCT_FwdRing.h:65
double m_ringOffset
Definition SCT_FwdRing.h:87
double m_moduleServicesLoOuterZPos
double m_moduleServicesHiOuterZPos
virtual GeoVPhysVol * build(SCT_Identifier id)
double m_moduleServicesHiRPos
virtual const GeoLogVol * preBuild()
void getParameters()
double m_thickness
Definition SCT_FwdRing.h:93
double m_moduleStagger
Definition SCT_FwdRing.h:80
double m_maxModuleServicesBaseToRingCenter
bool m_discRotated
Definition SCT_FwdRing.h:98
double m_outerRadius
Definition SCT_FwdRing.h:92
SCT_FwdModule * m_module
double m_discSupportThickness
Definition SCT_FwdRing.h:88
int m_firstStagger
Definition SCT_FwdRing.h:97
void makeModuleServices()
double m_safety
Definition SCT_FwdRing.h:81
int m_refFirstStagger
Definition SCT_FwdRing.h:86
double m_thicknessOuter
Definition SCT_FwdRing.h:95
double m_thicknessInner
Definition SCT_FwdRing.h:94
std::unique_ptr< SCT_FwdCoolingBlock > m_coolingBlockHiSec
double m_moduleServicesLoZPos
double m_startAngle
Definition SCT_FwdRing.h:96
SCT_UniqueComponentFactory(const std::string &name, InDetDD::SCT_DetectorManager *detectorManager, SCT_GeometryManager *geometryManager, SCT_MaterialManager *materials=nullptr, GeoModelIO::ReadGeoModel *sqliteReader=nullptr, std::shared_ptr< std::map< std::string, GeoFullPhysVol * > > mapFPV=nullptr, std::shared_ptr< std::map< std::string, GeoAlignableTransform * > > mapAX=nullptr)
std::shared_ptr< std::map< std::string, GeoFullPhysVol * > > m_mapFPV
std::shared_ptr< std::map< std::string, GeoAlignableTransform * > > m_mapAX
GeoModelIO::ReadGeoModel * m_sqliteReader
STL namespace.