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