ATLAS Offline Software
Loading...
Searching...
No Matches
HGTD_DetectorFactory.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4
5/* Three-ring detector layout, created by Christian and David
6 * Sep 2020 Lianyou SHAN merged
7 * hgtdlayout-master/place_modules_option2.py by Christina Agapopoulou
8 * into LArGeoModel/LArGeoEndcap/EndcapCryostatConstruction in R20.20.14.6
9 * Dec 2020 Lianyou SHAN integrate 2-ring layout
10 */
11
14
17#include "StoreGate/StoreGateSvc.h" // For alignment getAlignableFolderType()
20
21#include "GeoModelKernel/GeoNameTag.h"
22#include "GeoModelKernel/GeoIdentifierTag.h"
23#include "GeoModelKernel/GeoMaterial.h"
24#include "GeoModelKernel/GeoTransform.h"
25#include "GeoModelKernel/GeoAlignableTransform.h"
26#include "GeoModelKernel/GeoTorus.h"
27#include "GeoModelKernel/GeoTube.h"
28#include "GeoModelKernel/GeoBox.h"
29#include "GeoModelKernel/GeoPhysVol.h"
30#include "GeoModelKernel/GeoFullPhysVol.h"
36#include "GaudiKernel/SystemOfUnits.h"
37
39
43
46
50
51#include <sstream>
52#include <cmath>
53
54using namespace std;
55using namespace InDetDD;
56
58 InDetDD::DetectorFactoryBase( athComps ),
59 m_athComps( athComps ),
60 m_materialMgr( nullptr ),
61 m_geomVersion( -1 ),
62 m_outputIdfr( false ) {
63 // create the detector manager
65
66 ATH_MSG_INFO( "HGTD geometry from hard-coded definition - No Information being taken from Geometry Tag!" );
67
68
69 // Create SiCommonItems. These are items that are shared by all elements
70 m_commonItems = std::make_unique<const InDetDD::SiCommonItems>(m_athComps->getIdHelper());
71
72 // temporarily hardcode the HGTD version to build until the geo db has been updated with tables for 3-ring layout
73 // m_geomVersion = 0; // two-ring layout
74 m_geomVersion = 1; // three-ring layout
75}
76
78 // NB the detector manager (m_detectorManager) is stored in the detector store by the Tool and so we don't delete it.
79}
80
85
86void HGTD_DetectorFactory::create(GeoPhysVol* world) {
87
88 ATH_MSG_INFO( "Building HGTD detector");
89
90 // initialize the geometry parameters
91 // Originally taken from geometry db, but now fully hard-coded until move to GMX implementation
93
94 // for now the position of the HGTD mother volumes is hardcoded - TODO: take from db!
95 constexpr float zMother = 3482.5;
96
97 // build logical volumes for the two endcaps
98 const GeoLogVol* positiveEndcapLogicalVolume = buildEndcapLogicalVolume(true);
99 const GeoLogVol* negativeEndcapLogicalVolume = buildEndcapLogicalVolume(false);
100
101 // create and place positive endcap
102 world->add(new GeoNameTag("HGTD_Pos"));
103 world->add(new GeoIdentifierTag(9));
104 ATH_MSG_INFO( "HGTD_Pos mother volume will be placed at z = " << zMother << " mm" );
105 world->add(new GeoTransform(GeoTrf::TranslateZ3D(zMother)));
106 GeoVPhysVol* endcapPos = build( positiveEndcapLogicalVolume, true);
107 world->add( endcapPos );
108 m_detectorManager->addTreeTop( endcapPos);
109
110 // create and place negative endcap
111 world->add(new GeoNameTag("HGTD_Neg"));
112 world->add(new GeoIdentifierTag(-9));
113 ATH_MSG_INFO( "HGTD_Neg mother volume will be placed at z = " << -zMother << " mm" );
114 world->add(new GeoTransform(GeoTrf::TranslateZ3D(-zMother)));
115 world->add(new GeoTransform(GeoTrf::RotateY3D(180.0*Gaudi::Units::deg)));
116 GeoVPhysVol* endcapNeg = build( negativeEndcapLogicalVolume, false);
117 world->add( endcapNeg );
118 m_detectorManager->addTreeTop( endcapNeg );
119
120 // Add SiCommonItems to HGTD_DetectorManager to hold and delete it.
121 m_detectorManager->setCommonItems(std::move(m_commonItems));
122
123 return;
124}
125
126// initialize the geometry parameters (now fully hard-coded until move to GMX implementation)
128
129 // retrieve the material manager (can't use ATH_CHECK macros within create(), it seems..)
130 StatusCode sc = detStore()->retrieve(m_materialMgr, std::string("MATERIALS"));
131 if (sc != StatusCode::SUCCESS) {
132 ATH_MSG_ERROR("Cannot retrieve material manager from DetStore");
133 }
134
135 // temporarily hard-code custom materials - eventually will be defined in xml once HGTD migrates to GeoModelXML detector description
136
137 GeoMaterial* CFRP = new GeoMaterial("hgtd::CFRP", 1.78*(CLHEP::gram / CLHEP::cm3)); // copy of sct::CFRP used in 21.9
138 CFRP->add(m_materialMgr->getElement("Carbon"), 0.92);
139 CFRP->add(m_materialMgr->getElement("Hydrogen"), 0.02);
140 CFRP->add(m_materialMgr->getElement("Oxygen"), 0.05);
141 m_materialMgr->addMaterial("hgtd", CFRP);
142
143 GeoMaterial* Honeycomb = new GeoMaterial("hgtd::Honeycomb", 0.042*(CLHEP::gram / CLHEP::cm3)); // copy of muo::Honeycomb used in 21.9
144 Honeycomb->add(m_materialMgr->getElement("Carbon"), 0.88);
145 Honeycomb->add(m_materialMgr->getElement("Hydrogen"), 0.11);
146 m_materialMgr->addMaterial("hgtd", Honeycomb);
147
148 GeoMaterial* Peek = new GeoMaterial("hgtd::Peek", 1.3*(CLHEP::gram / CLHEP::cm3)); // copy of pix::Peek used in 21.9
149 Peek->add(m_materialMgr->getElement("Hydrogen"), 0.04);
150 Peek->add(m_materialMgr->getElement("Carbon"), 0.79);
151 Peek->add(m_materialMgr->getElement("Oxygen"), 0.16);
152 m_materialMgr->addMaterial("hgtd", Peek);
153
154 GeoMaterial* CO2_Liquid = new GeoMaterial("hgtd::CO2_Liquid", 1.032*(CLHEP::gram / CLHEP::cm3)); // copy of pix::CO2_Liquid used in 21.9
155 CO2_Liquid->add(m_materialMgr->getElement("Carbon"), 0.27);
156 CO2_Liquid->add(m_materialMgr->getElement("Oxygen"), 0.72);
157 m_materialMgr->addMaterial("hgtd", CO2_Liquid);
158
159 GeoMaterial* CO2 = new GeoMaterial("hgtd::CO2", 0.001842*(CLHEP::gram / CLHEP::cm3)); // copy of trt::CO2 used in 21.9
160 CO2->add(m_materialMgr->getElement("Carbon"), 0.27);
161 CO2->add(m_materialMgr->getElement("Oxygen"), 0.72);
162 m_materialMgr->addMaterial("hgtd", CO2);
163
164 GeoMaterial* CFiberSupport = new GeoMaterial("hgtd::CFiberSupport", 0.189*(CLHEP::gram / CLHEP::cm3)); // copy of sct::CFiberSupport used in 21.9
165 CFiberSupport->add(m_materialMgr->getElement("Carbon"), 1.0);
166 m_materialMgr->addMaterial("hgtd", CFiberSupport);
167
168 GeoMaterial* CuKapton = new GeoMaterial("hgtd::CuKapton", 2.94*(CLHEP::gram / CLHEP::cm3)); // copy of sct::CuKapton used in 21.9
169 CuKapton->add(m_materialMgr->getElement("Copper"), 0.61);
170 CuKapton->add(m_materialMgr->getElement("Carbon"), 0.26);
171 CuKapton->add(m_materialMgr->getElement("Hydrogen"), 0.01);
172 CuKapton->add(m_materialMgr->getElement("Oxygen"), 0.08);
173 CuKapton->add(m_materialMgr->getElement("Nitrogen"), 0.02);
174 m_materialMgr->addMaterial("hgtd", CuKapton);
175
176 GeoMaterial* BoratedPolyethelyne = new GeoMaterial("hgtd::BoratedPolyethelyne", 0.99*(CLHEP::gram / CLHEP::cm3)); // copy of LAr::BoratedPolyethelyne used in 21.9
177 BoratedPolyethelyne->add(m_materialMgr->getElement("Hydrogen"), 0.13);
178 BoratedPolyethelyne->add(m_materialMgr->getElement("Carbon"), 0.81);
179 BoratedPolyethelyne->add(m_materialMgr->getElement("Boron"), 0.05);
180 m_materialMgr->addMaterial("hgtd", BoratedPolyethelyne);
181
182 GeoMaterial* FEBoards = new GeoMaterial("hgtd::FEBoards", 0.99*(CLHEP::gram / CLHEP::cm3)); // copy of LAr::FEBoards used in 21.9
183 FEBoards->add(m_materialMgr->getElement("Silicon"), 0.27);
184 FEBoards->add(m_materialMgr->getElement("Oxygen"), 0.34);
185 FEBoards->add(m_materialMgr->getElement("Copper"), 0.28);
186 FEBoards->add(m_materialMgr->getElement("Hydrogen"), 0.01);
187 FEBoards->add(m_materialMgr->getElement("Carbon"), 0.09);
188 m_materialMgr->addMaterial("hgtd", FEBoards);
189
190 GeoMaterial* Epoxy = new GeoMaterial("hgtd::Epoxy", 1*(CLHEP::gram / CLHEP::cm3)); // copy of sct::Epoxy used in 21.9
191 Epoxy->add(m_materialMgr->getElement("Carbon"), 0.76);
192 Epoxy->add(m_materialMgr->getElement("Hydrogen"), 0.07);
193 Epoxy->add(m_materialMgr->getElement("Oxygen"), 0.16);
194 m_materialMgr->addMaterial("hgtd", Epoxy);
195
196 // Hardcoded box parameters taken from geometry db:
197
198 // Node: HGTDBox BOX DX DY DZ ZPOS MATERIAL
199 m_boxVolPars["HGTDModule0"] = {"HGTDModule0", 11, 20, 1.75, 0, "std::Air"};
200 m_boxVolPars["HGTDModule1"] = {"HGTDModule1", 11, 20, 1.75, 0, "std::Air"};
201 m_boxVolPars["HGTDModule2"] = {"HGTDModule2", 11, 20, 1.75, 0, "std::Air"};
202 m_boxVolPars["HGTDModule3"] = {"HGTDModule3", 11, 20, 1.75, 0, "std::Air"};
203 m_boxVolPars["HGTD::Hybrid"] = {"HGTD::Hybrid", 10.25, 20, .175, 0, "hgtd::CuKapton"};
204 m_boxVolPars["HGTD::GlueSensor"] = {"HGTD::GlueSensor", 10.25, 20, .04, 0, "hgtd::Epoxy"};
205 m_boxVolPars["HGTD::GlueAsic"] = {"HGTD::GlueAsic", 11, 20, .04, 0, "hgtd::Epoxy"};
206 m_boxVolPars["HGTDSiSensor0"] = {"HGTDSiSensor0", 10.25, 20, .025, 0, "std::Silicon"};
207 m_boxVolPars["HGTDSiSensor1"] = {"HGTDSiSensor1", 10.25, 20, .025, 0, "std::Silicon"};
208 m_boxVolPars["HGTDSiSensor2"] = {"HGTDSiSensor2", 10.25, 20, .025, 0, "std::Silicon"};
209 m_boxVolPars["HGTDSiSensor3"] = {"HGTDSiSensor3", 10.25, 20, .025, 0, "std::Silicon"};
210 m_boxVolPars["HGTD::LGADInactive"] = {"HGTD::LGADInactive", 10.25, 20, .1, 0, "std::Silicon"};
211 m_boxVolPars["HGTD::ASIC"] = {"HGTD::ASIC", 11, 20, .15, 0, "std::Silicon"};
212
213 // Add a dummy entry that will be used to leave some space - no volume will actually be created for this
214 // needed after fix of ASIC thickness (and material) in HGTD-TDR-01 tag (ATLAS-P2-ITK-17-04-02 and later), compared to HGTD-TDR-00
215 double moduleSpaceHalfZ = 0.225;
216 m_boxVolPars["HGTD::ModuleSpace"] = {"HGTD::ModuleSpace", 11, 20, moduleSpaceHalfZ, 0, "std::Air"};
217
218
219
220 // Node: HGTDTubs TUBE RMIN RMAX DZ ZPOS MATERIAL
221 m_cylVolPars["HGTD_mother"] = {"HGTD_mother", 100, 1100, 62.5, -3252, "std::Air"};
222 m_cylVolPars["HGTD::FrontCover"] = {"HGTD::FrontCover", 120, 1000, 7.5, 0, "hgtd::CFiberSupport"};
223 m_cylVolPars["HGTD::FlexPackage"] = {"HGTD::FlexPackage", 120, 660, 2, 0, "std::Air"};
224 m_cylVolPars["HGTD::FlexTube"] = {"HGTD::FlexTube", 120, 660, .175, 0, "hgtd::CuKapton"};
225 m_cylVolPars["HGTD::ModuleLayer0"] = {"HGTD::ModuleLayer0", 120, 660, 3.75, 0, "std::Air"};
226 m_cylVolPars["HGTD::ModuleLayer1"] = {"HGTD::ModuleLayer1", 120, 660, 3.75, 0, "std::Air"};
227 m_cylVolPars["HGTD::ModuleLayer2"] = {"HGTD::ModuleLayer2", 120, 660, 3.75, 0, "std::Air"};
228 m_cylVolPars["HGTD::ModuleLayer3"] = {"HGTD::ModuleLayer3", 120, 660, 3.75, 0, "std::Air"};
229 m_cylVolPars["HGTD::CoolingPlate"] = {"HGTD::CoolingPlate", 120, 920, 3, 0, "std::Aluminium"};
230 m_cylVolPars["HGTD::SupportPlate"] = {"HGTD::SupportPlate", 120, 660, .5, 0, "std::Aluminium"};
231 m_cylVolPars["HGTD::ToleranceFront"] = {"HGTD::ToleranceFront", 120, 660, 1, 0, "std::Air"};
232 m_cylVolPars["HGTD::ToleranceBack"] = {"HGTD::ToleranceBack", 120, 660, 1, 0, "std::Air"};
233 m_cylVolPars["HGTD::ToleranceMid"] = {"HGTD::ToleranceMid", 120, 660, 1, 0, "std::Air"};
234 m_cylVolPars["HGTD::ModeratorIn"] = {"HGTD::ModeratorIn", 120, 900, 15, 0, "hgtd::BoratedPolyethelyne"};
235 m_cylVolPars["HGTD::ModeratorOut"] = {"HGTD::ModeratorOut", 120, 1100, 10, 0, "hgtd::BoratedPolyethelyne"};
236 m_cylVolPars["HGTD::BackCover"] = {"HGTD::BackCover", 120, 1100, 4, 0, "hgtd::CFiberSupport"};
237 m_cylVolPars["HGTD::PeriphElec"] = {"HGTD::PeriphElec", 674, 900, 1, 2, "hgtd::FEBoards"};
238
239 m_cylVolPars["HGTD::InnerRCover1"] = {"HGTD::InnerRCover1", 110., 111., 105./2, -10., "hgtd::CFRP"};
240 // the InnerRCover bulk should be 70% aerogel and 30% honeycomb made from "aradime" (not defined - using "hgtd::Honeycomb" for now)
241 // proportions should be 50/50 by weight, which is used for GeoMaterial fractions
242 // TODO: these should be double-checked, or at least that the density/weight matches engineering drawings
243 GeoMaterial* innerRCoverBulkMaterial = new GeoMaterial("hgtd::AerogelAndHoneycomb", 0.17*(CLHEP::gram / CLHEP::cm3));
244 innerRCoverBulkMaterial->add(m_materialMgr->getMaterial("std::Aerogel"), 0.5);
245 innerRCoverBulkMaterial->add(m_materialMgr->getMaterial("hgtd::Honeycomb"), 0.5);
246 m_materialMgr->addMaterial("hgtd", innerRCoverBulkMaterial);
247 m_cylVolPars["HGTD::InnerRCover2"] = {"HGTD::InnerRCover2", 111., 119., 105./2, -10., "hgtd::AerogelAndHoneycomb"};
248 m_cylVolPars["HGTD::InnerRCover3"] = {"HGTD::InnerRCover3", 119., 120., 105./2, -10., "hgtd::CFRP"};
249 m_cylVolPars["HGTD::OuterRCover"] = {"HGTD::OuterRCover", 980., 1000., 82./2, -6.5, "hgtd::Peek"};
250 m_cylVolPars["HGTD::PeripheralCoolingLines"] = {"HGTD::PeripheralCoolingLines", 920., 980., 3./2, 31., "std::SSteel"};
251 // TODO: outer cover should be 40% "hgtd::Peek" and 60% electrical connectors (unclear material)
252
253 m_cylVolPars["HGTD::CoolingTube"] = {"HGTD::CoolingTubes", 0, 0, 2.0, 0, "std::Titanium"};
254 // Coolant should be 50% liquid and 50% gas CO2 ("hgtd::CO2")
255 GeoMaterial* coolantMaterial = new GeoMaterial("hgtd::CO2CoolantMix", 0.55*(CLHEP::gram / CLHEP::cm3));
256 coolantMaterial->add(m_materialMgr->getMaterial("hgtd::CO2_Liquid"), 0.5);
257 coolantMaterial->add(m_materialMgr->getMaterial("hgtd::CO2"), 0.5);
258 m_materialMgr->addMaterial("hgtd", coolantMaterial);
259 m_cylVolPars["HGTD::CoolingTubeFluid"] = {"HGTD::CoolingTubeFluid", 0, 0, 1.5, 0, "hgtd::CO2CoolantMix"};
260
261
262
263 // These parameters were not in the db (they don't fit into the cylinder or box structures used above)
264 m_hgtdPars = { 320., // rMid
265 640., // rOuter - only used in one place, and there 20 mm is added to it...
266 0., // disk1Rotation (in degrees)
267 15., // disk2Rotation (in degrees)
268 1., // rowSpaceSide
269 4., // rowBacksideInnerShift
270 17., // rowBacksideOuterShift
271 1.5, // moduleSpaceInner
272 12.5, // moduleSpaceOuter
273 0.456 // flexSheetSpacing
274 };
275
276 return;
277}
278
279// prepare an envelope for one HGTD side
280GeoLogVol* HGTD_DetectorFactory::buildEndcapLogicalVolume(bool isPositiveSide) {
281
282 // build the solid volume
283 GeoTube* world_solid_hgtd = new GeoTube(m_cylVolPars["HGTD_mother"].rMin, m_cylVolPars["HGTD_mother"].rMax,
284 m_cylVolPars["HGTD_mother"].zHalf);
285
286 // build the logical volume
287 std::string name = isPositiveSide ? "HGTD_PositiveEndcap" : "HGTD_NegativeEndcap";
288 GeoLogVol* world_logical_hgtd = new GeoLogVol( name.c_str(), world_solid_hgtd,
289 m_materialMgr->getMaterial( m_cylVolPars[ "HGTD_mother"].material) );
290
291 return world_logical_hgtd;
292}
293
294
295GeoVPhysVol* HGTD_DetectorFactory::build( const GeoLogVol* logicalEnvelope, bool bPos) {
296
297 ATH_MSG_INFO( "**************************************************");
298 ATH_MSG_INFO( " Building HGTD geometry , side = " << bPos << " ");
299 ATH_MSG_INFO( "**************************************************" );
300
301 GeoFullPhysVol* HGTDparent = new GeoFullPhysVol( logicalEnvelope );
302
303 // to be calculated from parameters in db using map
304 double motherHalfZ = ((GeoTube*) HGTDparent->getLogVol()->getShape())->getZHalfLength();
305 double modulePackageHalfZtot = 3.5/2 + 4./2; // including flex - can we not get this from the db numbers? /CO
306
307 double modulePackageHalfZ = 2*m_boxVolPars["HGTD::GlueSensor"].zHalf + m_boxVolPars["HGTDSiSensor0"].zHalf
308 + m_boxVolPars["HGTD::LGADInactive"].zHalf + m_boxVolPars["HGTD::ASIC"].zHalf
309 + m_boxVolPars["HGTD::Hybrid"].zHalf + m_boxVolPars["HGTD::ModuleSpace"].zHalf;
310
311 // add volumes by key name to ordered vector, outside in (from larger z to smaller)
312 std::vector<std::string> hgtdVolumes;
313 hgtdVolumes.push_back("HGTD::ModeratorOut"); // Out as in outside the vessel
314 hgtdVolumes.push_back("HGTD::BackCover");
315 hgtdVolumes.push_back("HGTD::ToleranceBack");
316 hgtdVolumes.push_back("HGTD::ModeratorIn"); // In as in inside the vessel
317
318 hgtdVolumes.push_back("HGTD::ModuleLayer3");
319 hgtdVolumes.push_back("HGTD::SupportPlate");
320 hgtdVolumes.push_back("HGTD::CoolingPlate");
321 hgtdVolumes.push_back("HGTD::SupportPlate");
322 hgtdVolumes.push_back("HGTD::ModuleLayer2");
323
324 hgtdVolumes.push_back("HGTD::ToleranceMid");
325
326 hgtdVolumes.push_back("HGTD::ModuleLayer1");
327 hgtdVolumes.push_back("HGTD::SupportPlate");
328 hgtdVolumes.push_back("HGTD::CoolingPlate");
329 hgtdVolumes.push_back("HGTD::SupportPlate");
330 hgtdVolumes.push_back("HGTD::ModuleLayer0");
331
332 hgtdVolumes.push_back("HGTD::ToleranceFront");
333 hgtdVolumes.push_back("HGTD::FrontCover");
334 // Important - these must come last since they will otherwise shift positions of the previous volumes!
335 hgtdVolumes.push_back("HGTD::InnerRCover1"); // don't reorder!
336 hgtdVolumes.push_back("HGTD::InnerRCover2"); // don't reorder!
337 hgtdVolumes.push_back("HGTD::InnerRCover3"); // don't reorder!
338 hgtdVolumes.push_back("HGTD::OuterRCover"); // don't reorder!
339 hgtdVolumes.push_back("HGTD::PeripheralCoolingLines"); // don't reorder!
340
341 // Now build up the solid, logical and physical volumes as appropriate (starting from the outermost volume)
342 // We first start with the volumes we'll reuse several times
343
345 // FLEX PACKAGE VOLUMES //
347
348 // Flex package volume modeled as 8 concentric flex sheets with progressively larger inner radius
349 // Order of sheets depend on whether package is for front or back of a cooling plate
350 // First calculate the inner radii for the flex sheets
351 GeoCylVolParams packagePars = m_cylVolPars["HGTD::FlexPackage"];
352 GeoCylVolParams flexPars = m_cylVolPars["HGTD::FlexTube"];
353 std::vector<double> flexSheetInnerR;
354 double currentInnerR = 144.; // adding flex sheets from the second sensor (all have the hybrid already)
355 for (int flexSheet = 0; flexSheet < 8; flexSheet++) {
356 flexSheetInnerR.push_back(currentInnerR);
357 // set the inner radius for the next flex sheet, increased by two module heights and two radius-dependent spaces per sheet
358 currentInnerR += m_boxVolPars["HGTDModule0"].xHalf*2 * (2 + 2 * (flexSheet < 4 ? 0.2 : 0.8) );
359 }
360
361 // build up the two flex volumes for front (0) and back (1) sides
362 GeoPhysVol* flexPackagePhysical[2] = {};
363 for (int flexVolume = 0; flexVolume < 2; flexVolume++) {
364 std::vector<double> rInner = flexSheetInnerR;
365 if (flexVolume) reverse(rInner.begin(), rInner.end()); // reverse order for backside flex package
366
367 GeoTube* flexPackageSolid = new GeoTube(packagePars.rMin, packagePars.rMax, packagePars.zHalf);
368 GeoLogVol* flexPackageLogical = new GeoLogVol(packagePars.name, flexPackageSolid, m_materialMgr->getMaterial(packagePars.material));
369 flexPackagePhysical[flexVolume] = new GeoPhysVol(flexPackageLogical);
370 // build up a volume of flex cables, starting in z at half a flex layer from the edge of the flex package volume
371 double flexZoffset = packagePars.zHalf - flexPars.zHalf;
372 for (int flexSheet = 0; flexSheet < 8; flexSheet++) {
373 GeoTube* hgtdFlexSolid = new GeoTube(rInner[flexSheet], flexPars.rMax, flexPars.zHalf);
374 GeoLogVol* hgtdFlexLogical = new GeoLogVol("HGTD::FlexTube"+std::to_string(flexSheet),
375 hgtdFlexSolid, m_materialMgr->getMaterial(flexPars.material));
376 GeoPhysVol* hgtdFlexPhysical = new GeoPhysVol(hgtdFlexLogical);
377 flexPackagePhysical[flexVolume]->add(new GeoTransform(GeoTrf::TranslateZ3D(flexZoffset)));
378 flexPackagePhysical[flexVolume]->add(hgtdFlexPhysical);
379 // print out a line for each flex layer
380 ATH_MSG_DEBUG( "Flex layer (" << (flexSheet ? "front" : "back") << ")" << flexSheet << ", Rmin = " << std::setw(5)
381 << rInner[flexSheet] << " mm, flexZoffset = " << flexZoffset << " mm" );
382 flexZoffset = flexZoffset - m_hgtdPars.flexSheetSpacing;
383 }
384 }
385
387 // COOLING TUBES //
389
390 // make list of radii of cooling tubes
391 std::vector<double> coolingTubeRadii;
392 double coolingTubeRadius = 130.;
393 coolingTubeRadii.push_back(coolingTubeRadius);
394
395 // two-ring layout
396 if (m_geomVersion == 0) {
397 ATH_MSG_INFO("Will now calculate cooling-loop positions for the two-ring layout");
398 for (int i = 0; i < 18; i++) {
399 coolingTubeRadius += (418-130.)/18;
400 coolingTubeRadii.push_back(coolingTubeRadius);
401 }
402 for (int i = 0; i < 12; i++) {
403 coolingTubeRadius += (658-418.)/14;
404 coolingTubeRadii.push_back(coolingTubeRadius);
405 }
406 coolingTubeRadius = 710.;
407 coolingTubeRadii.push_back(coolingTubeRadius);
408 for (int i = 0; i < 7; i++) {
409 coolingTubeRadius += (890-710.)/6;
410 coolingTubeRadii.push_back(coolingTubeRadius);
411 }
412 }
413 else if (m_geomVersion == 1) {
414 ATH_MSG_INFO("Will now calculate cooling-loop positions for the three-ring layout");
415 // inner part, even spacing from 130 mm to 674 mm, 35 rings with 16 mm spacing (first one already placed above)
416 int numberOfLoops = 34;
417 float loopDistance = (674.-130.)/numberOfLoops; // in mm
418 for (int i = 0; i < numberOfLoops; i++) {
419 coolingTubeRadius += loopDistance;
420 coolingTubeRadii.push_back(coolingTubeRadius);
421 }
422 // outer part, even spacing from 720 mm to 900 mm, 7 rings with 30 mm spacing
423 coolingTubeRadius = 720;
424 coolingTubeRadii.push_back(coolingTubeRadius);
425 numberOfLoops = 6;
426 loopDistance = (900.-720.)/numberOfLoops;
427 for (int i = 0; i < numberOfLoops; i++) {
428 coolingTubeRadius += loopDistance;
429 coolingTubeRadii.push_back(coolingTubeRadius);
430 }
431 }
432 ATH_MSG_DEBUG( "Cooling tubes will be created at the following radii (" << coolingTubeRadii.size() << " in total):");
433 for (size_t i = 0; i < coolingTubeRadii.size(); i++) {
434 ATH_MSG_DEBUG( " R = " << coolingTubeRadii[i] << " mm" );
435 }
436
438 // PERIPHERAL ELECTRONICS VOLUME //
440
441 //build peripheral electronics
442 GeoCylVolParams periphElPars = m_cylVolPars["HGTD::PeriphElec"];
443 GeoTube* periphElec_solid = new GeoTube(periphElPars.rMin, periphElPars.rMax, periphElPars.zHalf);
444 GeoLogVol* periphElec_log = new GeoLogVol(periphElPars.name, periphElec_solid, m_materialMgr->getMaterial(periphElPars.material));
445 GeoPhysVol* periphElec_phys = new GeoPhysVol(periphElec_log);
446
447 std::array< GeoPhysVol*, 4 > moduleLayerPhysical = {}; // array of pointers to the physical volumes for the module layers which need special care
448
450 // BUILD UP ALL MAIN VOLUMES IN SEQUENCE //
452
453 // now build up the volumes in the order specified in the vector
454 double zModuleLayerF = 0.;
455 double zModuleLayerB = 0.;
456 for (size_t vol = 0; vol < hgtdVolumes.size(); vol++) {
457
458 std::string v = hgtdVolumes[vol];
459
460 // calculate local z offsets for each main volume sequentially
461 if (vol == 0) // special treatment for the first one
462 m_cylVolPars[v].zOffsetLocal = motherHalfZ - m_cylVolPars[v].zHalf;
463
464 // All but the InnerRCover, OuterRCover and peripheral cooling lines are placed relative to other components,
465 // but the zOffsetLocal parameter of these volumes is left as read from the db
466 else {
467 if (v.substr(9,8) != "erRCover" && v != "HGTD::PeripheralCoolingLines") {
468 std::string vPrev = hgtdVolumes[vol-1];
469 m_cylVolPars[v].zOffsetLocal = m_cylVolPars[vPrev].zOffsetLocal - m_cylVolPars[vPrev].zHalf - m_cylVolPars[v].zHalf;
470 }
471 }
472
473 // skip the tolerances - we don't actually want to create volumes for the space
474 if (v.substr(0,15) == "HGTD::Tolerance") continue;
475
476 float safety = 0.;
477 if (v.substr(0,17) == "HGTD::ModuleLayer")
478 safety = 10.;
479
480 // a disk volume to hold 4 quadrants
481 GeoTube* hgtdSubVolumeSolid = new GeoTube(m_cylVolPars[v].rMin, m_cylVolPars[v].rMax+safety, m_cylVolPars[v].zHalf);
482 GeoLogVol* hgtdSubVolumeLogical = new GeoLogVol(m_cylVolPars[v].name, hgtdSubVolumeSolid, m_materialMgr->getMaterial(m_cylVolPars[v].material));
483 GeoPhysVol* hgtdSubVolumePhysical = new GeoPhysVol(hgtdSubVolumeLogical);
484
485 // if building the cooling plate, also add peripheral electronics since position of those are relative to that of cooling plate
486 if (v == "HGTD::CoolingPlate") {
487 double zOffsetPeriphElec = m_cylVolPars[v].zHalf + periphElPars.zOffsetLocal + periphElPars.zHalf;
488 // place two, one on each side of cooling plate
489 static constexpr std::array<int,2> signArr{1,-1};
490 for (int side = 0; side < 2; side++) {
491 //0, 1 index -> 1, -1 sign
492 HGTDparent->add(new GeoTransform(GeoTrf::TranslateZ3D(m_cylVolPars[v].zOffsetLocal + signArr[side]*zOffsetPeriphElec)));
493 HGTDparent->add(periphElec_phys);
494 }
495
496 // and the CO2 cooling tubes inside the cooling plate
497 for (size_t i = 0; i < coolingTubeRadii.size(); i++) {
498 // the tube itself
499 GeoTorus* coolingTubeSolid = new GeoTorus(m_cylVolPars["HGTD::CoolingTubeFluid"].zHalf, m_cylVolPars["HGTD::CoolingTube"].zHalf,
500 coolingTubeRadii[i], 0, 2*M_PI);
501 GeoLogVol* coolingTubeLogical = new GeoLogVol("HGTD::CoolingTube", coolingTubeSolid,
502 m_materialMgr->getMaterial(m_cylVolPars["HGTD::CoolingTube"].material));
503 GeoPhysVol* coolingTubePhysical = new GeoPhysVol(coolingTubeLogical);
504 hgtdSubVolumePhysical->add(coolingTubePhysical); // no transformations needed, concentric with cooling plate and centered in z
505 // and the contents, i.e. the cooling fluid
506 GeoTorus* coolingFluidSolid = new GeoTorus(0, m_cylVolPars["HGTD::CoolingTubeFluid"].zHalf,
507 coolingTubeRadii[i], 0, 2*M_PI);
508 GeoLogVol* coolingFluidLogical = new GeoLogVol("HGTD::CoolingFluid", coolingFluidSolid,
509 m_materialMgr->getMaterial(m_cylVolPars["HGTD::CoolingTubeFluid"].material));
510 GeoPhysVol* coolingFluidPhysical = new GeoPhysVol(coolingFluidLogical);
511 hgtdSubVolumePhysical->add(coolingFluidPhysical); // no transformations needed, concentric with cooling plate and centered in z
512 }
513 }
514
515 // module layer
516 if (v.substr(0,17) == "HGTD::ModuleLayer") {
517
518 int layer = atoi(v.substr(17,1).c_str());
519
520 // front and back side layers are treated differently: z position of flex and module layers, and rotation
521 double zFlex = 0.;
522 bool Lside = layer % 2;
523 if (Lside == 0) { // layers 0 and 2
524 zFlex = -modulePackageHalfZtot + m_cylVolPars["HGTD::FlexPackage"].zHalf;
525 zModuleLayerF = modulePackageHalfZtot - modulePackageHalfZ;
526 }
527 else { // layers 1 and 3
528 zFlex = modulePackageHalfZtot - m_cylVolPars["HGTD::FlexPackage"].zHalf;
529 zModuleLayerB = -modulePackageHalfZtot + modulePackageHalfZ;
530 }
531
532 // place flex within module packages, at different positions depending on front or back or cooling plate
533 hgtdSubVolumePhysical->add(new GeoTransform(GeoTrf::TranslateZ3D(zFlex)));
534 hgtdSubVolumePhysical->add(flexPackagePhysical[(Lside ? 0 : 1)]);
535
536 float diskRotation = layer <= 1 ? m_hgtdPars.disk1Rotation : m_hgtdPars.disk2Rotation;
537
538 HGTDparent->add(new GeoTransform( GeoTrf::TranslateZ3D(m_cylVolPars[v].zOffsetLocal) *
539 GeoTrf::RotateZ3D(diskRotation*Gaudi::Units::deg)) );
540 // one needs to check this rotation against the "quadrot" will be used in the following
541
542 HGTDparent->add( hgtdSubVolumePhysical );
543 moduleLayerPhysical[layer] = hgtdSubVolumePhysical;
544
545 } // end of module package
546 else {
547 HGTDparent->add(new GeoTransform(GeoTrf::TranslateZ3D(m_cylVolPars[v].zOffsetLocal)));
548 HGTDparent->add(hgtdSubVolumePhysical);
549 }
550
551 // print out info about each main volume
552 ATH_MSG_INFO( std::setw(20) << m_cylVolPars[v].name << " ( " << std::setw(20) << m_cylVolPars[v].material
553 << " ), local z = " << std::setw(6) << m_cylVolPars[v].zOffsetLocal
554 << " mm, Rmin = " << std::setw(4) << m_cylVolPars[v].rMin
555 << " mm, Rmax = " << std::setw(4) << m_cylVolPars[v].rMax
556 << " mm, DZ = " << std::setw(5) << m_cylVolPars[v].zHalf << " mm" );
557
558 } // end loop over hgtdVolumes
559
561 // MODULE VOLUMES //
563
564 // components for the module
565 std::vector<std::string> moduleVolumes;
566 moduleVolumes.push_back("HGTD::GlueAsic");
567 moduleVolumes.push_back("HGTD::ASIC");
568 moduleVolumes.push_back("HGTD::LGADInactive");
569 moduleVolumes.push_back("SensorPlaceHolder"); // replaced below to get the numbered name right
570 moduleVolumes.push_back("HGTD::GlueSensor");
571 moduleVolumes.push_back("HGTD::Hybrid");
572 moduleVolumes.push_back("HGTD::ModuleSpace");
573
574 int endcap = bPos ? +2 : -2;
575 double thickness = 2.*m_boxVolPars["HGTDSiSensor0"].zHalf;
576 InDetDD::HGTD_ModuleDesign* moduleDesign = createHgtdDesign( thickness );
577
578 // create the module --> each for cell and with different names
579 // calculate the positions where modules should be placed in one quadrant
580
581 int totMod = 0;
582 // this should be taken from DB or XML
583 unsigned int maxRows = 21;
584 if ( m_geomVersion == 0 ) maxRows = 18;
585
586 std::array< PositionsInQuadrant, 4 > positions = prepareLayersFromQuadrants( maxRows ) ;
587 // inside m_geomVersion implicitly control 3-ring layout vs 2-ring
588
590
591 for (int layer = 0; layer < 4; layer++) {
592 if (m_outputIdfr) cout << "Layer #" << layer << std::endl;
593 // select from front vs back side of a disk
594 int Lside = layer % 2;
595
596 std::vector<std::string> volumes = moduleVolumes;
597 if ( Lside != 0 ) reverse( volumes.begin(), volumes.end() ); // reverse order of components for backside modules
598
599 std::string sensorName = std::string("HGTDSiSensor") + std::to_string(layer);
600 std::string moduleName = std::string("HGTDModule") + std::to_string(layer);
601
602 // here we assumed all 4 layers share the same dimensions.
603 // As described at HGTD_DetectorFactory::reorderRows,
604 // the short edge of 4*2 cm module in the leading row is defined as local X/Width
605 // in ReadoutGeometry, this short edge is also defined as Eta, since the row is roughly along radius.
606 double moduleHalfWidth = m_boxVolPars[moduleName].xHalf; // 11m than 10 to hold wire bond
607 double moduleHalfHeight = m_boxVolPars[moduleName].yHalf;
608
609 // loop over quadrants in the current layer
610 // take a prepared quadrant as protype
611 PositionsInQuadrant tmpQuadrant = positions[ layer ];
612 // The relative rotation between two disks is supposed to be defined/accounted within tmpQuadrant
613 for (int q = 0; q < 4; q++) {
614 float quadrot = q*90.;
615
616 for ( unsigned int row = 0; row < maxRows; row ++ ) {
617 std::vector< ModulePosition > ModsPerRow = tmpQuadrant[ row ];
618
619 // print #modules per row to fill HGTD_Identifier dictionary etc.
620 if ( m_outputIdfr && q == 0 ) std::cout << " Row #"<< row + 1 <<" :: " << ModsPerRow.size() << std::endl;
621
622 for ( unsigned int mod = 0; mod < ModsPerRow.size(); mod ++ ) {
623 ModulePosition module = ModsPerRow[ mod ];
624
625 double myx = -9999999.9 , myy = -9999999.9 , myrot = -9999999.9;
626 int myphi = -1 , myeta = - 1;
627 std::string module_string = formModuleName( layer, q, maxRows, row, mod, module, myx, myy, myrot, myphi, myeta );
628
629 if ( module_string == "" || myrot == -9999999.9 || myeta == -1 )
630 ATH_MSG_WARNING ( " Please check the module at layer "<< layer <<" quadrant " << q <<" row "<< row <<" mod " << mod <<" not well retrieved ! " );
631
632 // an hgtd module defined in the form of ( X, Y, Z )
633 GeoBox* moduleSolid = new GeoBox( moduleHalfWidth, moduleHalfHeight, modulePackageHalfZ);
634 GeoLogVol* moduleLogical = new GeoLogVol( moduleName + module_string, moduleSolid, m_materialMgr->getMaterial("std::Air"));
635 GeoFullPhysVol* modulePhysical = new GeoFullPhysVol( moduleLogical );
636
637 // print out one module per layer
638 if ( q == 0 && row == 0 && mod == 0 )
639 ATH_MSG_DEBUG( "Will now build up an individual HGTD module of layer " << layer << " and quadrant " << q << " (" << module_string << ")" );
640
641 // loop over components in module
642 for (size_t comp = 0; comp < volumes.size(); comp++) {
643 if (volumes[comp] == "SensorPlaceHolder") volumes[comp] = sensorName; // replace placeholder
644
645 std::string c = volumes[comp];
646 // calculate local z offsets for each sensor component sequentially
647 if (comp == 0) // special treatment for the first one
648 m_boxVolPars[c].zOffsetLocal = modulePackageHalfZ - m_boxVolPars[c].zHalf;
649 else {
650 std::string cPrev = volumes[comp-1];
651 m_boxVolPars[c].zOffsetLocal = m_boxVolPars[cPrev].zOffsetLocal - m_boxVolPars[cPrev].zHalf - m_boxVolPars[c].zHalf;
652 }
653
654 // skip the module space - we don't actually want to create volumes for the space
655 if (volumes[comp] == "HGTD::ModuleSpace") continue;
656
657 double comp_halfx = m_boxVolPars[c].xHalf;
658 double comp_halfy = m_boxVolPars[c].yHalf;
659
660 // and x offsets for those components that are smaller, to make room for wire bond of flex to ASIC which is larger than the sensor
661 double xOffsetLocal = moduleHalfWidth - comp_halfx;
662 // need tuning then dataBase : to make room for wire bond of flex to ASIC which is larger than the sensor
663
664 GeoBox* sensorCompSolidVol = new GeoBox(comp_halfx, comp_halfy, m_boxVolPars[c].zHalf);
665 // No attachment?
666 std::string attach = (volumes[comp] == sensorName) ? "" : "_L" + std::to_string( layer ) + module_string;
667
668 GeoLogVol* sensorCompLogicalVol = new GeoLogVol( m_boxVolPars[c].name+attach, sensorCompSolidVol,
669 m_materialMgr->getMaterial(m_boxVolPars[c].material));
670 GeoFullPhysVol* sensorCompPhysicalVol = new GeoFullPhysVol(sensorCompLogicalVol);
671
672 if (volumes[comp] == sensorName) {
673 const HGTD_ID* hgtdId = dynamic_cast<const HGTD_ID*>( m_athComps->getIdHelper() );
674 Identifier idwafer = hgtdId->wafer_id( endcap, layer, myphi, myeta );
675
676 // print only the first and last module of each row in the first quadrant
677 if ( q == 0 && ( mod == 0 || mod == ( ModsPerRow.size() - 1 ) ) && !m_outputIdfr ) {
678 ATH_MSG_DEBUG( " waferHash : " << hgtdId->wafer_hash( idwafer )
679 << " upon HGTD_ID => ec: " << endcap << ", layer: " << layer << ", quadrant: " << q
680 << ", row: " << myphi <<", module: "<< myeta );
681 ATH_MSG_DEBUG( " HGTD Module: " << m_boxVolPars[c].name+module_string << ", posX: " << myx << ", posY: " << myy << ", rot: " << quadrot + myrot );
682 }
683
684 InDetDD::HGTD_DetectorElement* detElement = new InDetDD::HGTD_DetectorElement(idwafer, moduleDesign, sensorCompPhysicalVol, m_commonItems.get());
685 m_detectorManager->addDetectorElement( detElement );
686
687 GeoTrf::Transform3D sensorTransform = GeoTrf::TranslateZ3D(m_boxVolPars[c].zOffsetLocal)*GeoTrf::TranslateX3D(xOffsetLocal);
688 GeoAlignableTransform* xform = new GeoAlignableTransform(sensorTransform);
689
690 modulePhysical->add( xform );
691 modulePhysical->add( sensorCompPhysicalVol );
692
693 totMod ++;
694 }
695 else {
696 modulePhysical->add(new GeoTransform(GeoTrf::TranslateZ3D(m_boxVolPars[c].zOffsetLocal)*GeoTrf::TranslateX3D(xOffsetLocal)));
697 modulePhysical->add(sensorCompPhysicalVol);
698 }
699
700 // print out each module component
701 if ( mod == 0 && q == 0 && volumes[comp] != sensorName )
702 ATH_MSG_DEBUG( std::setw(20) << m_boxVolPars[c].name << " ( " << std::setw(15) << m_boxVolPars[c].material
703 << " ), in-sensor-layer local z = " << std::setw(7) << m_boxVolPars[c].zOffsetLocal << " mm"
704 << ", DX = " << std::setw(5) << m_boxVolPars[c].xHalf << " mm"
705 << ", DY = " << std::setw(5) << m_boxVolPars[c].yHalf << " mm"
706 << ", DZ = " << std::setw(5) << m_boxVolPars[c].zHalf << " mm" );
707 } // end of components loop
708
709 double zModule = ( Lside == 0 ? zModuleLayerF : zModuleLayerB );
710
711 GeoTransform* moduleTransform = new GeoTransform( GeoTrf::TranslateZ3D(zModule) *
712 GeoTrf::TranslateX3D(myx) *
713 GeoTrf::TranslateY3D(myy) *
714 GeoTrf::RotateZ3D( ( quadrot + myrot )*Gaudi::Units::deg) );
715 moduleLayerPhysical[layer]->add( moduleTransform );
716 moduleLayerPhysical[layer]->add( modulePhysical );
717 } //end of modules loop
718 ATH_MSG_DEBUG( "Done placing modules for row " << row );
719 } // end of row loop
720 ATH_MSG_DEBUG( "Done placing modules for quadrant " << q );
721 } // end of quadrants loop
722 ATH_MSG_DEBUG( "Done placing modules for layer " << layer );
723 }
724
725 ATH_MSG_INFO( "**************************************************" );
726 ATH_MSG_INFO( " Done building HGTD with " << totMod <<" modules " );
727 ATH_MSG_INFO( "**************************************************" );
728
729 return HGTDparent;
730}
731
732// backwards compatibility two-ring layout used for TDR studies
733// careful implicit dependence on m_geomVersion to make 3-ring layout different from 2-ring
734std::array< PositionsInQuadrant, 4 > HGTD_DetectorFactory::prepareLayersFromQuadrants( unsigned int nRows ) {
735
736 std::array< PositionsInQuadrant, 4 > positions;
737
738 // three-ring layout
739 if ( m_geomVersion == 1 ) {
740 // the 1st disk
741 PositionsInQuadrant d0q0front = prepareQuadrantsFromRows( 0, nRows );
742 reorderRows( &d0q0front );
743 PositionsInQuadrant d0q0back = prepareQuadrantsFromRows( 1, nRows );
744 reorderRows( &d0q0back );
745
746 // the 2nd disk is the same as the 1st, but mirrored
747 PositionsInQuadrant d1q0front = mirrorModulesInQuadrant( d0q0back );
748 PositionsInQuadrant d1q0back = mirrorModulesInQuadrant( d0q0front );
749
750 positions[ 0 ] = std::move(d0q0front); // front-side module positions
751 positions[ 1 ] = std::move(d0q0back); // back-side module positions
752 positions[ 2 ] = std::move(d1q0front); // front-side module positions
753 positions[ 3 ] = std::move(d1q0back); // back-side module positions
754
755 } else {
756 nRows = 18; // note 21-18 = 3 elements with positions of modules in rows are left empty
757 positions[0] = positions[2] = prepareQuadrantsFromRows(0, nRows ); // front-side module positions
758 positions[1] = positions[3] = prepareQuadrantsFromRows(1, nRows ); // back-side module positions
759 }
760 return positions;
761}
762
763// careful m_geomVersion control layout implicitly
764// backward compatibility to pre-TDR two-ring layouts
765// 3-ring layout differ from 2-ring here.
766std::string HGTD_DetectorFactory::formModuleName( int layer, int quadrant, unsigned int maxrows, int row, int mod,
767 const ModulePosition& module,
768 double& myx, double& myy, double& myrot, int& phi, int& eta ) {
769
770 std::string module_string = "";
771
772 double x = module.x;
773 double y = module.y;
774 double myphi = atan(y/x);
775 double radius = std::sqrt(x*x+y*y);
776
777 myx = radius*cos( quadrant*M_PI*0.5 + myphi );
778 myy = radius*sin( quadrant*M_PI*0.5 + myphi );
779
780 // three-ring layout
781 if ( m_geomVersion == 1 ) {
782 myrot = module.phiRotation;
783 phi = quadrant*21 + row + 1; // quadrant is absent ( hidden into row ) in HGTD-Identifier
784 eta = mod + 1;
785 //module_string = "_R" + std::to_string(phi) + "_M" + std::to_string(eta); //This was the previous string, but doesn't match expectations of HGTDSensorSD
786 module_string = "_layer_" + std::to_string(layer) + "_" + std::to_string(phi) + "_" + std::to_string(eta);
787 }
788 // two-ring layout
789 else {
790 double rot = module.flipped ? 90. : 0.;
791 int myrow = module.row;
792 double moduleRotation = 0;
793 if ( layer > 1 ) {
794 myx = -myx;
795 // need to rotate 180 degrees some modules in q0 and q2
796 if ( quadrant%2 == 0 && myrow <= 15) moduleRotation = 180.;
797 else if (quadrant%2 == 1 && myrow > 15) moduleRotation = 180.;
798 }
799 eta = ( quadrant*maxrows ) + myrow;
800 phi = module.el_in_row;
801 myrot = moduleRotation + rot;
802 module_string = "_layer_" + std::to_string(layer) + "_" + std::to_string(phi) + "_" + std::to_string(eta);
803 }
804
805 return module_string;
806}
807
808// calculate the positions of modules in a quadrant, taking care of separate schemes for two- and three-ring layouts
810
811 PositionsInQuadrant rowsInQuad;
812 bool isBackside = (layer % 2);
813 // three-ring layout
814 if ( m_geomVersion == 1 ) {
815 for (size_t row = 0; row <= maxRow; row++) {
816 if ( row == 13 ) continue; // element #21 is tried since one row is skipped
817 std::vector<ModulePosition> rowModulePositions = prepareModulePositionsInRowThreeRing( row, isBackside );
818 rowsInQuad[ row > 13 ? row - 1 : row ] = std::move(rowModulePositions);
819 }
820 }
821 // two-ring layout
822 else {
823 for (size_t row = 0; row < maxRow; row++) {
824 std::vector<ModulePosition> rowModulePositions = prepareModulePositionsInRowTwoRing(row, isBackside);
825 rowsInQuad[ row ] = std::move(rowModulePositions);
826 }
827 }
828 return rowsInQuad;
829}
830
832 PositionsInQuadrant rowsInQuad;
833 for (size_t row = 0; row < inquad.size(); row ++ ) {
834 std::vector<ModulePosition> modulePositions = inquad[ row ];
835 for ( size_t mod = 0; mod < modulePositions.size(); mod++ ) {
836 ModulePosition old = modulePositions[mod];
837 ModulePosition mirror = old;
838 mirror.x = old.y;
839 mirror.y = old.x;
840 mirror.phiRotation = ( old.phiRotation == 0. ? 90. : 0. );
841 modulePositions[mod] = mirror;
842 }
843 // keeping the order defined in HGTD_DetectorFactory::reorderRows
844 rowsInQuad[ inquad.size() - row - 1 ] = std::move(modulePositions);
845 }
846 return rowsInQuad;
847}
848
849// calculate module positions for the three-ring layout, updated to agree with post-TDR developments
850std::vector< ModulePosition > HGTD_DetectorFactory::prepareModulePositionsInRowThreeRing( int row, int back ) {
851
852 // below parameters should be collected into xml or RDB
853 int index_XYcoord_change = 14;
854
855 // height is the short edge of module, width is the longer edge
856 float halfWidth = .5*40., halfHeight = .5*21.8; // bare module
857 float midR = 230., midR2 = 470.5, maxRcut = 660., maxOuterR = 670.;
858 // special tweak needed to avoid overlapping holes on front and back
859 if (row == 21 and back==1) {
860 midR2 = 510.;
861 }
862 float readoutRowSpace = 1.0;
863 bool extrude = ( ( row == 6 || row == 18 ) && !back ) || // front side
864 ( ( row == 2 || row == 11 || row == 12 || row == 17 ) && back ); // back side
865
866 // instead of attempting to re-calculate the leading module per row, just pick up from dataBase,
867 // numbers here taken from spreadsheet at https://cernbox.cern.ch/index.php/s/PPXEWSBnBjwI7UU
868 std::array< float, 22 > ModStarting = { 122., 122.7, 89.85, 123.5, 175.4, 257.4, 287.5, 298.4, 287.5, 304.5, 287.5, 304.5, 287.5, 0.0, 299.7,
869 130., 114.7, 131.45, 164.45, 216.35, 205.45, 257.35 };
870
871 std::array< float, 22 > ModStartBack = { 130., 114.7, 97.85, 131.5, 164.5, 246.5, 298.4, 287.5, 298.4, 287.5, 304.5, 287.5, 304.5, 0.0, 287.5,
872 122., 122.7, 123.45, 172.45, 205.45, 216.35, 246.45 };
873 /* row == 13 will be skipped from outside, and, since row == 15 XY flip take place. */
874
875 // this is a bit hacky for this layout, a db-based solution will help in the future
876 int useCorner = 0;
877 if ( ( ( row == 1 || row == 5 || row == 15 || row == 19 ) && ! back ) || // front side
878 ( ( row == 0 || row == 8 || row == 16 || row == 18 || row == 21 ) && back ) // back side
879 ) useCorner = 1;
880 if ( row == 17 ) useCorner = 2;
881 // in some exceptional cases the spacing will be smaller even though the module crossed the ring boundary
882 float backshift = 6.;
883
884 // the new layout tune makes small adjustments (usually 2~3 mm) for the last modules of some rows.
885 // even though most of element is zero for most of modules, we store these adjustments in a 2D array for now
886 float tailModCorrection[ 22 ][ 19 ];
887 for ( int r = 0; r < 22; r ++ )
888 for ( int m = 0; m < 19; m ++ ) tailModCorrection[r][m] = 0.;
889 tailModCorrection[11][4] = tailModCorrection[12][2] = 10.;
890
891 // TDR layout: spaceSmallR = 5.5 , spaceMediumR = 8.4 , spaceLargeR = 14.5
892 float spaceSmallR = 3.7 , spaceMediumR = 6.6, spaceLargeR = 12.7; // updated spacings from post-TDR developments
893
894 float backsideSmallR = spaceSmallR;
895 float backsideMediumR = spaceMediumR;
896 float backsideLargeR = spaceLargeR;
897
898 float extendedWidth = readoutRowSpace + 2.*halfWidth;
899
900 float posRadius = 0.;
901 float posOfLastPlacedModule = 0.; // start with dummy position
902 int moduleCounter = 0;
903 std::vector< ModulePosition > rowModulePositions;
904
905 float effectiveRow = row;
906 // note the flipping of effectiveRow, it is the cause of XY flipping in later occurrance
907 if ( row == index_XYcoord_change ) effectiveRow = 13;
908 if ( row > index_XYcoord_change ) effectiveRow -= ( index_XYcoord_change + 1 );
909
910 // x coordinate for vertical rows
911 float rowCentPos = 0.5*extendedWidth*( 2*effectiveRow + 1 );
912
913 if ( extrude ) maxRcut = maxOuterR;
914 while ( posRadius < maxRcut ) {
915 // horizontal rows need care (restart from other edge of quadrant), this variable helps get the inner radius right
916 // y coordinate for vertical rows, then x coordinate for modules
917 float modPos_row = -999.;
918
919 // for the first module in each row, take the starting position from the arrays created earlier from the spreadsheet
920 if ( moduleCounter == 0 ) { // leading module per row
921 modPos_row = ( back ? ModStartBack[row] : ModStarting[row] );
922 modPos_row += halfHeight;
923 }
924 // the rest of the modules follow sequential, radius-dependent placement rules
925 else {
926 float prevX = rowModulePositions[ moduleCounter - 1 ].x;
927 float prevY = rowModulePositions[ moduleCounter - 1 ].y;
928 float spacing = back ? backsideSmallR : spaceSmallR;
929
930 // increase the spacing by the ring it will fallin
931 float ringCrossRcorner = std::sqrt( ( prevY + halfHeight)*( prevY + halfHeight) +
932 ( prevX + halfWidth )*( prevX + halfWidth ) );
933 float ringCrossRcenter = std::sqrt( prevY*prevY + prevX*prevX );
934
935 bool tuned_center = ( row == 3 && ( moduleCounter == 3 && !back ) ) || // front, row 3
936 ( row == 20 && moduleCounter == 8 && !back ) || // front, row 20
937 ( row == 21 && moduleCounter == 6 && back ); // back, row 21
938 if ( useCorner == 2 ) {
939 if ( ( moduleCounter == 3 && ! back ) || ( ( moduleCounter == 3 || moduleCounter == 4 ) && back ) ) {
940 ringCrossRcenter -= backshift;
941 if ( ringCrossRcenter > midR && ringCrossRcenter <= midR2 ) spacing = back ? backsideMediumR : spaceMediumR;
942 if ( ringCrossRcenter > midR2 ) spacing = back ? backsideLargeR : spaceLargeR;
943 }
944 else {
945 if ( ringCrossRcorner > midR && ringCrossRcorner <= midR2 ) spacing = back ? backsideMediumR : spaceMediumR;
946 if ( ringCrossRcorner > midR2 ) spacing = back ? backsideLargeR : spaceLargeR;
947 }
948 }
949 else if ( useCorner == 1 ) {
950 if ( ringCrossRcorner > midR && ringCrossRcorner <= midR2 ) spacing = back ? backsideMediumR : spaceMediumR;
951 if ( ringCrossRcorner > midR2 ) spacing = back ? backsideLargeR : spaceLargeR;
952 }
953 else {
954 if ( tuned_center ) ringCrossRcenter -= backshift;
955 if ( ringCrossRcenter > midR && ringCrossRcenter <= midR2 ) spacing = back ? backsideMediumR : spaceMediumR;
956 if ( ringCrossRcenter > midR2 ) spacing = back ? backsideLargeR : spaceLargeR;
957 }
958
959 modPos_row = posOfLastPlacedModule + 2.*halfHeight + spacing;
960
961 if ( back && moduleCounter < 19) modPos_row -= tailModCorrection[ row ][ moduleCounter ];
962
963 } // endif non-leading module
964
965 // check and limit the length of the row
966 posRadius = std::sqrt( ( rowCentPos + halfWidth )*( rowCentPos + halfWidth ) +
967 ( modPos_row + halfHeight)*( modPos_row + halfHeight ) );
968 if ( posRadius > maxRcut ) {
969 ATH_MSG_DEBUG(" row " << row <<" finished with " << moduleCounter <<" modules ");
970 break;
971 }
972
973 // the X and Y coordinates need to be flipped if this row is horizontal,
974 // needed only for backwards compatibility for two-ring layout
975 ModulePosition modu = { modPos_row, rowCentPos, 0., false, row, moduleCounter };
976 ModulePosition moduFlipped = { rowCentPos, modPos_row, 90., true, row, moduleCounter };
977
978 // eventually arrived at a module to append into this row
979 if ( row > index_XYcoord_change ) rowModulePositions.push_back( modu );
980 else rowModulePositions.push_back( moduFlipped );
981
982 // the spreadsheet gave the center of bottom edge of a module, so an adjustment by halfHeight is needed
983 if ( m_outputIdfr ) ATH_MSG_DEBUG( " Row " << ( row <= index_XYcoord_change ? effectiveRow + 1 : 36 - row )
984 << " Module " << moduleCounter + 1 <<" at (x,y) : "
985 << ( row > index_XYcoord_change ? rowModulePositions.back().x - halfHeight : rowModulePositions.back().x ) << ", "
986 << ( row > index_XYcoord_change ? rowModulePositions.back().y : rowModulePositions.back().y - halfHeight ) );
987
988 posOfLastPlacedModule = modPos_row;
989 moduleCounter ++;
990 } // endof while loop
991
992 if ( m_outputIdfr ) std::cout << "Total #Module " << rowModulePositions.size() <<" at original row " << row << std::endl;
993
994 return rowModulePositions;
995}
996
997// adjust row ordering to adhere to a convention in upper right quadrant of front :
998// 0 is first/bottom of many horizontal laid module along X-coord ( Y is almost zero )
999// increasing as they're stacked upwards ( anticlockwise ), the upmost horizontal is 6
1000// then the outermost ( right-most ) vertical is 7, increase as they are laid to left
1001// 20 is the last ( left ) adhereing to Y-coord ( X is almost zero )
1002//
1004
1005 PositionsInQuadrant tmpQuadrant;
1006 int xchng = 0;
1007 unsigned int numrow = quadrant->size();
1008 for ( unsigned int r = 0; r < numrow; r ++ ) {
1009 unsigned int idx = r > 13 ? 13 + numrow - r : r;
1010 ATH_MSG_DEBUG( " original row " << ( r <= 12 ? r : r + 1 ) <<" new row " << idx + 1
1011 << " : "<< numrow );
1012 tmpQuadrant[ idx ] = quadrant->at( r );
1013 if ( idx != r ) xchng++;
1014 }
1015 // refill the quadrant
1016 for ( unsigned int r = 0; r < numrow; r++ )
1017 quadrant->at( r ) = tmpQuadrant[ numrow - 1 - r ];
1018
1019 return xchng;
1020}
1021
1022void HGTD_DetectorFactory::mirrorPositionsAroundYaxis(std::array< PositionsInQuadrant, 4 >& arr) {
1023 for (auto& layer : arr) {
1024 for (auto& row : layer) {
1025 for (auto& module : row) {
1026 module.x = -module.x;
1027 }
1028 }
1029 }
1030}
1031
1032
1034
1035 double phiPitch = 1.3; // mm
1036 double etaPitch = 1.3; // mm
1037
1038 // for each module there are two ASICs connected, need further confirmation for below specifications
1039 // the "collumns" along module short edge align to a direction just following the EndOfCollumn(EoC) of each ASIC.
1040 int circuitsPerColumn = 1;
1041 int circuitsPerRow = 2; // along the long edge of module, tentatively following IBL descriptin
1042
1043 // each ASIC just cover half ( 15*15 pixels ) of a module
1044 int cellColumnsPerCircuit = 15;
1045 int cellRowsPerCircuit = 15; // along module FULL length there are 2*15 rows
1046
1047 int diodeColumnsPerCircuit = cellColumnsPerCircuit;
1048 int diodeRowsPerCircuit = cellRowsPerCircuit;
1049
1050 std::shared_ptr<const PixelDiodeMatrix> normalCell = InDetDD::PixelDiodeMatrix::construct(phiPitch, etaPitch);
1051 std::shared_ptr<const PixelDiodeMatrix> singleRow = InDetDD::PixelDiodeMatrix::construct(InDetDD::PixelDiodeMatrix::phiDir, 0,
1052 std::move(normalCell), diodeColumnsPerCircuit, 0);
1053 std::shared_ptr<const PixelDiodeMatrix> fullMatrix = InDetDD::PixelDiodeMatrix::construct(InDetDD::PixelDiodeMatrix::etaDir, 0,
1054 std::move(singleRow), 2*diodeRowsPerCircuit, 0); // note 30 = 2*15 rows adopted
1055
1057
1059 circuitsPerColumn, circuitsPerRow,
1060 cellColumnsPerCircuit, cellRowsPerCircuit,
1061 diodeColumnsPerCircuit, diodeRowsPerCircuit,
1062 std::move(fullMatrix),
1063 InDetDD::CarrierType::electrons, 1, yDirection );
1064
1065 return design;
1066}
1067
1071
1072// backwards compatibility two-ring layout used for TDR studies
1073std::vector<ModulePosition> HGTD_DetectorFactory::prepareModulePositionsInRowTwoRing(int row, bool back) {
1074
1075 unsigned int module = 0;
1076 std::vector<ModulePosition> modulePositions;
1077 double posOfLastPlacedModule = 0.;
1078
1079 while (true) {
1080 //ATH_MSG_INFO) << "Will now place module " << module );
1081 // horizontal rows need care (restart from other edge of quadrant), this variable helps get the inner radius right
1082 // in quadrant 0 - ie top right quadrant
1083 // row 0 = bottom horizontal row. numbering grows upwards and counterclockwise; row 17=leftmost vertical row
1084 // rowForInnerRadius = 0-1 for vertical rows too
1085 int rowForInnerRadius = row; // because row 16-17 are equivalent to 0-1 regarding module placement
1086 if (row == 17) rowForInnerRadius = 0;
1087 if (row == 16) rowForInnerRadius = 1;
1088
1089 // params needed frequently below
1090 double moduleWidth = m_boxVolPars["HGTDModule0"].yHalf*2;
1091 double moduleHeight = m_boxVolPars["HGTDModule0"].xHalf*2;
1092 double rInner = m_cylVolPars["HGTD::ModuleLayer0"].rMin;
1093 double rMid = m_hgtdPars.rMid;
1094 double rOuter = m_hgtdPars.rOuter;
1095 double rowSpaceSide = m_hgtdPars.rowSpaceSide;
1096
1097 // x coordinate for vertical rows
1098 double rowCenterPos = (moduleWidth + rowSpaceSide)*(rowForInnerRadius + 0.5);
1099 // y coordinate for vertical rows
1100 double modulePosAlongRow = -99.; // mock value for now
1101 // for the first module, pick the right starting point
1102 if (modulePositions.empty()) {
1103 // start at inner radius and include any offset for backside.
1104 if (rowForInnerRadius < 3) {
1105 modulePosAlongRow = std::sqrt( pow(rInner, 2) - pow((moduleWidth + rowSpaceSide)*rowForInnerRadius, 2) )
1106 + back*m_hgtdPars.rowBacksideInnerShift + moduleHeight/2;
1107 }
1108 else { // later modules start at 2*moduleWidth, with offset for the backside
1109 double backSpacing = m_hgtdPars.rowBacksideInnerShift;
1110 if (back && (rowCenterPos - moduleWidth/2 > rMid)) {
1111 backSpacing = m_hgtdPars.rowBacksideOuterShift;
1112 }
1113 modulePosAlongRow = 2*(moduleWidth + rowSpaceSide) + moduleHeight/2 + back*backSpacing;
1114 }
1115 }
1116 // the rest of the modules follow sequential, radius-dependent placement rules
1117 else {
1118 ModulePosition prev = modulePositions.back();
1119 double spacing = m_hgtdPars.moduleSpaceInner;
1120 // if the previous module was completely outside rMid, increase the spacing
1121 // (+1 mm is a needed shift for full coverage - could need tweaking if layout parameters change!)
1122 float innermostCornerR = std::sqrt( pow(prev.y - moduleHeight/2, 2) + pow(prev.x - moduleWidth/2, 2) ) + 1.;
1123 if (innermostCornerR > rMid) {
1124 spacing = m_hgtdPars.moduleSpaceOuter;
1125 }
1126 // for the back the large spacing starts as soon as the space would entirely be outside R = 320 mm
1127 if (back) {
1128 double startOfSpaceAlongRow = std::sqrt( pow(prev.y + moduleHeight/2, 2) + pow(prev.x - moduleWidth/2, 2) ) - 2;
1129 if (startOfSpaceAlongRow > rMid) {
1130 spacing = m_hgtdPars.moduleSpaceOuter;
1131 }
1132 }
1133 // correction to the first two spaces on the front side, compensating for the 2 mm introduced at beginning of a row
1134 if (!back && rowForInnerRadius < 8 && module < 3) {
1135 spacing -= 1.; // should result in 3 mm instead of 4 mm for innermost spaces on rows starting at R = 120
1136 }
1137 // squeeze in a few more modules at the end of some rows
1138 double maxRcut = rOuter+20;
1139 if(row == 8 || row == 9 || row == 10) {
1140 maxRcut = 661;
1141 if(row == 8 && module > 12) spacing -= 4;
1142 }
1143 else if ( row == 11 && module > 9) {
1144 maxRcut = 662;
1145 spacing -= 6;
1146 }
1147 else if (row == 12 && back) {
1148 maxRcut = 665;
1149 }
1150 else if(row == 13 && module > 5) {
1151 maxRcut = 666;
1152 if (!back && module > 6 ) spacing -= 8.5;
1153 else if (back && module > 5) spacing -= 2;
1154 }
1155 else if (row == 14 && module > 3) {
1156 maxRcut = 665;
1157 spacing -= 5;
1158 }
1159 else if (row == 15) {
1160 maxRcut = 669;
1161 spacing -= 5.5;
1162 }
1163 modulePosAlongRow = posOfLastPlacedModule + moduleHeight + spacing;
1164 // stop if the next module will extend outside the max allowed radius
1165 // HC max radius is 665
1166 if ( std::sqrt( pow(rowCenterPos + moduleWidth/2, 2) + pow(modulePosAlongRow + moduleHeight/2, 2) ) > maxRcut) {
1167 break;
1168 }
1169 }
1170 ModulePosition m = {rowCenterPos, modulePosAlongRow, 0, true, row, (int)module};
1171 modulePositions.push_back(m);
1172 posOfLastPlacedModule = modulePosAlongRow;
1173 module += 1;
1174 } // end of loop over modules
1175
1176 // finally, flip x and y for all modules if this row is horizontal
1177 if (row < 16) {
1178 // ATH_MSG_INFO) << "Flipping x and y for modules in row " << row );
1179 for (size_t i=0; i < modulePositions.size(); i++) {
1180 ModulePosition old = modulePositions[i];
1181 ModulePosition rotated = old;
1182 rotated.x = old.y;
1183 rotated.y = old.x;
1184 rotated.flipped = !old.flipped;
1185 rotated.row = old.row;
1186 rotated.el_in_row = old.el_in_row;
1187 modulePositions[i] = rotated;
1188 }
1189 }
1190
1191 ATH_MSG_DEBUG( "row = " << row );
1192 for(size_t i=0; i < modulePositions.size(); i++) {
1193 ATH_MSG_DEBUG( "Module " << i << " at (x,y) = (" << modulePositions[i].x << "," << modulePositions[i].y << ")" );
1194 }
1195
1196 return modulePositions;
1197}
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
This file defines the class for a collection of AttributeLists where each one is associated with a ch...
std::array< std::vector< ModulePosition >, 21 > PositionsInQuadrant
Definition of the abstract IRDBAccessSvc interface.
Definition of the abstract IRDBRecord interface.
Definition of the abstract IRDBRecordset interface.
static Double_t sc
void print(char *figname, TCanvas *c1)
#define y
#define x
constexpr int pow(int base, int exp) noexcept
GeoLogVol * buildEndcapLogicalVolume(bool isPositiveSide)
virtual void create(GeoPhysVol *world) override
InDetDD::HGTD_ModuleDesign * createHgtdDesign(double thickness)
PositionsInQuadrant prepareQuadrantsFromRows(int layer, unsigned int maxRow)
int reorderRows(PositionsInQuadrant *quadrant)
HGTD_GeoModelAthenaComps * m_athComps
PositionsInQuadrant mirrorModulesInQuadrant(const PositionsInQuadrant &)
std::vector< ModulePosition > prepareModulePositionsInRowTwoRing(int row, bool back=false)
std::vector< ModulePosition > prepareModulePositionsInRowThreeRing(int row, int back=0)
std::map< std::string, GeoBoxVolParams > m_boxVolPars
HGTD_DetectorManager * m_detectorManager
GeoVPhysVol * build(const GeoLogVol *logicalEnvelope, bool bPos)
std::unique_ptr< const InDetDD::SiCommonItems > m_commonItems
StoredMaterialManager * m_materialMgr
std::string formModuleName(int layer, int quadrant, unsigned int maxrows, int row, int mod, const ModulePosition &module, double &myx, double &myy, double &myrot, int &phi, int &eta)
virtual const HGTD_DetectorManager * getDetectorManager() const override
std::map< std::string, GeoCylVolParams > m_cylVolPars
HGTD_DetectorFactory(HGTD_GeoModelAthenaComps *athenaComps)
void mirrorPositionsAroundYaxis(std::array< PositionsInQuadrant, 4 > &arr)
std::array< PositionsInQuadrant, 4 > prepareLayersFromQuadrants(unsigned int)
The Detector manager has methods to retrieve the Identifier helper and methods to retrieve the detect...
Class to hold various Athena components.
This is an Identifier helper class for the HGTD subdetector.
Definition HGTD_ID.h:47
Identifier wafer_id(int endcap, int layer, int phi_module, int eta_module) const
For a single crystal.
Definition HGTD_ID.h:287
IdentifierHash wafer_hash(Identifier wafer_id) const
wafer hash from id
Definition HGTD_ID.h:404
DetectorFactoryBase(InDetDD::AthenaComps *athenaComps)
Class to hold geometrical description of an HGTD detector element.
Class used to describe the design of a module (diode segmentation and readout scheme)
static std::shared_ptr< const PixelDiodeMatrix > construct(double phiWidth, double etaWidth)
Construct method for just a single cell.
StatusCode retrieve(const T *&ptr) const
Retrieve the default object into a const T*.
int r
Definition globals.cxx:22
Message Stream Member.
STL namespace.
void reverse(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of reverse for DataVector/List.