Project
Loading...
Searching...
No Matches
VDGeometryBuilder.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
14#include <TGeoVolume.h>
15#include <TGeoMatrix.h>
16#include <TGeoTube.h>
17#include <TGeoBBox.h>
18#include <TMath.h>
19#include <TGeoCompositeShape.h>
20#include <TString.h>
22
23#include "TGeoManager.h"
24
25#include "Framework/Logger.h"
29
30namespace o2::trk
31{
32
33static std::vector<VDSensorDesc> gVDSensors; // stays in this TU only
34std::vector<VDSensorDesc>& vdSensorRegistry() { return gVDSensors; }
35
36void clearVDSensorRegistry() { gVDSensors.clear(); }
37
38void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx)
39{
40 gVDSensors.push_back({volName, petal, region, type, idx});
41}
42
43static inline std::string makeSensorName(const std::string& layerName, int layerNumber)
44{
45 return Form("%s_%s%d", layerName.c_str(), o2::trk::GeometryTGeo::getTRKSensorPattern(), layerNumber);
46}
47
48namespace
49{
50
51// Config: which volumes count as SOLIDS to subtract from the vacuum volume
52inline bool isSolidToCut(const TGeoVolume* v)
53{
54 const char* nm = v->GetName();
55 const char* med = v->GetMedium() ? v->GetMedium()->GetName() : "";
56 // silicon sensors (barrel + disks)
57 if (med && strcmp(med, "TRK_SILICON$") == 0)
58 return true;
59 // walls, sidewalls, cold-plate, service rings (names from your builders)
60 if (TString(nm).BeginsWith("VD_InnerWallArc"))
61 return true;
62 if (TString(nm).BeginsWith("VD_OuterWallArc"))
63 return true;
64 if (TString(nm).BeginsWith("VD_SideWall"))
65 return true;
66 if (TString(nm).Contains("_Coldplate"))
67 return true;
68 if (TString(nm).BeginsWith("IRIS_Service_Neg"))
69 return true;
70 if (TString(nm).BeginsWith("IRIS_Service_Pos_InVac"))
71 return true;
72 return false;
73}
74
75// Ensure every leaf shape has a stable, informative name
76inline const char* ensureShapeName(TGeoVolume* v)
77{
78 auto* sh = v->GetShape();
79 TString nm = sh->GetName();
80 if (nm.IsNull() || nm.BeginsWith("TGeo")) {
81 TString wanted = TString(v->GetName()) + "_sh";
82 // avoid collisions
83 int k = 0;
84 TString cand = wanted;
85 auto* shapes = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr;
86 while (shapes && shapes->FindObject(cand))
87 cand = Form("%s_%d", wanted.Data(), ++k);
88 sh->SetName(cand);
89 if (shapes && !shapes->FindObject(cand))
90 shapes->Add(sh);
91 }
92 return sh->GetName();
93}
94
95// Recorder state for the petal-local composite
96static TString gPetalSolidsFormula;
97static int gLocalTrIdx = 0;
98
99// add "ShapeName:IRIS_LOC_TR_k" to the petal-local formula (no outer rotation)
100inline void appendLocalTerm(const char* shapeName, const TGeoHMatrix& H)
101{
102 auto* ct = new TGeoCombiTrans(H);
103 ct->SetName(Form("IRIS_LOC_TR_%d", gLocalTrIdx++));
104 ct->RegisterYourself();
105 if (!gPetalSolidsFormula.IsNull())
106 gPetalSolidsFormula += "+";
107 gPetalSolidsFormula += TString::Format("%s:%s", shapeName, ct->GetName());
108}
109
110// DFS: compose LOCAL transforms only (identity prefix), to capture the petal contents
111void traversePetalLocal(TGeoVolume* vol, const TGeoHMatrix& prefix)
112{
113 auto* nodes = vol->GetNodes();
114 if (!nodes)
115 return;
116 for (int i = 0; i < nodes->GetEntriesFast(); ++i) {
117 auto* node = (TGeoNode*)nodes->At(i);
118 auto* childV = node->GetVolume();
119 TGeoHMatrix H(prefix);
120 if (auto* m = node->GetMatrix())
121 H.Multiply(m);
122
123 if (isSolidToCut(childV)) {
124 const char* shapeName = ensureShapeName(childV);
125 appendLocalTerm(shapeName, H);
126 }
127 traversePetalLocal(childV, H);
128 }
129}
130
131// Build (once) a petal-local composite containing ONLY solids (walls, silicon, coldplate, services, disks)
132inline void buildPetalSolidsComposite(TGeoVolume* petalAsm)
133{
134 // If it already exists, skip
135 if (gGeoManager && gGeoManager->GetListOfShapes() && gGeoManager->GetListOfShapes()->FindObject("IRIS_PETAL_SOLIDSsh"))
136 return;
137
138 gPetalSolidsFormula.Clear();
139 gLocalTrIdx = 0;
140
141 TGeoHMatrix I; // identity
142 traversePetalLocal(petalAsm, I);
143
144 if (gPetalSolidsFormula.IsNull()) {
145 LOGP(error, "IRIS_PETAL_SOLIDSsh formula is empty; did not find solids in petal.");
146 return;
147 }
148
149 LOGP(info, "IRIS_PETAL_SOLIDSsh formula: {}", gPetalSolidsFormula.Data());
150 new TGeoCompositeShape("IRIS_PETAL_SOLIDSsh", gPetalSolidsFormula.Data());
151}
152
153// Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase
154inline void buildIrisCutoutFromPetalSolid(int nPetals)
155{
156 // Create n rotation transforms
157 TString cutFormula;
158 for (int p = 0; p < nPetals; ++p) {
159 const double phi = (360.0 / nPetals) * (p + 0.5);
160 auto* R = new TGeoRotation();
161 R->RotateZ(phi);
162 auto* RT = new TGeoCombiTrans(0, 0, 0, R);
163 RT->SetName(Form("IRIS_PETAL_ROT_%d", p));
164 RT->RegisterYourself();
165 if (p)
166 cutFormula += "+";
167 cutFormula += Form("IRIS_PETAL_SOLIDSsh:%s", RT->GetName());
168 }
169 LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data());
170 new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data());
171
172 // --- Sanity check: required matrices & shapes exist
173 auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() : nullptr;
174 auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr;
175
176 if (!mats || !shps) {
177 LOGP(error, "IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing).");
178 } else {
179 bool ok = true;
180
181 // Check the petal rotations were registered and referenced
182 for (int p = 0; p < nPetals; ++p) {
183 const TString name = Form("IRIS_PETAL_ROT_%d", p);
184 if (!mats->FindObject(name)) {
185 LOGP(error, "IRIS cutout sanity: missing matrix {}", name.Data());
186 ok = false;
187 }
188 }
189
190 // Check that the local petal composite exists
191 if (!shps->FindObject("IRIS_PETAL_SOLIDSsh")) {
192 LOGP(error, "IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found.");
193 ok = false;
194 }
195
196 // Check that the global cutout shape was created
197 if (!shps->FindObject("IRIS_CUTOUTsh")) {
198 LOGP(error, "IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found.");
199 ok = false;
200 }
201
202 if (ok) {
203 LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals);
204 }
205 }
206}
207
208} // namespace
209
210// =================== Specs & constants (ROOT units: cm) ===================
211static constexpr double kX2X0 = 0.001f; // 0.1% X0 per layer
212static constexpr double kLenZ_cm = 50.0f; // L0/L1/L2 Z length
213
214// Radii (cm)
215static constexpr double rL0_cm = 0.5f; // 5 mm
216static constexpr double rL1_cm = 1.2f; // 12 mm
217static constexpr double rL2_cm = 2.5f; // 25 mm
218
219// IRIS5 rectangular L0 width (cm)
220static constexpr double kL0RectHeight_cm = 0.5f; // 5.0 mm
221static constexpr double kL0RectWidth_cm = 0.83f; // 8.3 mm
222
223// Disks radii (cm)
224static constexpr double diskRin_cm = 0.5f; // 5 mm
225static constexpr double diskRout_cm = 2.5f; // 25 mm
226static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f};
227
228// Petal walls specifications (cm)
229static constexpr double kPetalZ_cm = 70.0f; // full wall height
230static constexpr double kWallThick_cm = 0.015f; // 0.15 mm
231static constexpr double kInnerWallRadius_cm = 0.48f; // 4.8 mm (ALWAYS cylindrical)
232static constexpr double kOuterWallRadius_cm = 3.0f; // 30 mm (can be changed)
233static constexpr double kEps_cm = 1.e-4f;
234
235// Coldplate specs (cm)
236static constexpr double kColdplateRadius_cm = 2.6f; // 26 mm (outer radius)
237static constexpr double kColdplateThickness_cm = 0.15f; // 1.5 mm
238static constexpr double kColdplateZ_cm = 50.0f; // full length
239
240// ========== φ-span helpers (gap/arc → degrees) ==========
241namespace
242{
243
244// Convert a linear gap at radius R into an angular gap (deg)
245inline double degFromArc(double arc, double radius)
246{
247 // arc and radius in the SAME units (cm or mm); result in degrees
248 return (radius > 0.f) ? (arc / radius) * TMath::RadToDeg() : 0.f;
249}
250
258inline double phiSpanFromGap(int nPetals, double gap, double radius)
259{
260 if (nPetals <= 0 || radius <= 0.f)
261 return 0.f;
262 const double petalPhiDeg = 360.f / nPetals;
263 const double phi = petalPhiDeg - degFromArc(gap, radius);
264 return phi > 0.f ? phi : 0.f;
265}
266
271inline double phiSpanFromArc(double arcLen, double radius)
272{
273 return (arcLen > 0.f && radius > 0.f) ? degFromArc(arcLen, radius) : 0.f;
274}
275
276inline TGeoCombiTrans rotZ(double phiDeg)
277{
278 auto* r = new TGeoRotation();
279 r->RotateZ(static_cast<Double_t>(phiDeg));
280 return TGeoCombiTrans(0., 0., 0., r);
281}
282} // namespace
283
284// ============ Petal sub-builders (LOCAL coords only, no rotation) =========
285
286// Walls: inner cylindrical arc at r=4.8 mm (always), outer arc wall, and two side plates.
287static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_cm = kOuterWallRadius_cm)
288{
289 if (!petalAsm) {
290 LOGP(error, "addPetalWalls: petalAsm is null");
291 return;
292 }
293
295 const TGeoMedium* med = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083");
296
297 if (!med) {
298 LOGP(warning, "Petal walls: ALICE3_TRKSERVICES_ALUMINIUM5083$ not found, walls not created.");
299 return;
300 }
301
302 const double halfPhi = 0.5f * (360.f / static_cast<double>(nPetals));
303 const double halfZ = 0.5f * kPetalZ_cm;
304
305 // ---- Inner cylindrical wall (always at r=4.8 mm) ----
306 {
307 auto* s = new TGeoTubeSeg(static_cast<Double_t>(kInnerWallRadius_cm),
308 static_cast<Double_t>(kInnerWallRadius_cm + kWallThick_cm),
309 static_cast<Double_t>(halfZ),
310 static_cast<Double_t>(-halfPhi),
311 static_cast<Double_t>(+halfPhi));
312 auto* v = new TGeoVolume("VD_InnerWallArc", s, med);
313 v->SetLineColor(kGray + 2);
314 v->SetTransparency(70);
315 petalAsm->AddNode(v, 1);
316 }
317
318 // ---- Outer arc wall ----
319 {
320 auto* s = new TGeoTubeSeg(static_cast<Double_t>(outerRadius_cm),
321 static_cast<Double_t>(outerRadius_cm + kWallThick_cm),
322 static_cast<Double_t>(halfZ),
323 static_cast<Double_t>(-halfPhi),
324 static_cast<Double_t>(+halfPhi));
325 auto* v = new TGeoVolume("VD_OuterWallArc", s, med);
326 v->SetLineColor(kGray + 2);
327 v->SetTransparency(70);
328 petalAsm->AddNode(v, 1);
329 }
330
331 // ---- Side walls (boxes) at ±halfPhi ----
332 const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm));
333 auto* sideS = new TGeoBBox(static_cast<Double_t>(0.5f * radialLen),
334 static_cast<Double_t>(0.5f * kWallThick_cm),
335 static_cast<Double_t>(halfZ));
336 auto* sideV = new TGeoVolume("VD_SideWall", sideS, med);
337 sideV->SetLineColor(kGray + 2);
338 sideV->SetTransparency(70);
339
340 for (int sgn : {-1, +1}) {
341 const double phi = sgn * halfPhi;
342 const double rMid = kInnerWallRadius_cm + kWallThick_cm + 0.5f * radialLen;
343 const double rad = static_cast<double>(TMath::DegToRad());
344 const double x = rMid * std::cos(phi * rad);
345 const double y = rMid * std::sin(phi * rad);
346 auto* rot = new TGeoRotation();
347 rot->RotateZ(static_cast<Double_t>(phi));
348 auto* tr = new TGeoCombiTrans(static_cast<Double_t>(x),
349 static_cast<Double_t>(y),
350 0.0, rot);
351 petalAsm->AddNode(sideV, (sgn < 0 ? 1 : 2), tr);
352 }
353}
354
355// Build inner layers (L0..L2). L0 may be rectangular (IRIS5) or cylindrical.
356// φ-spans derive from spec gaps/arc; all local placement (no rotation).
357static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0)
358{
359 if (!petalAsm) {
360 LOGP(error, "addBarrelLayers: petalAsm is null");
361 return;
362 }
363
364 // Per spec (mm → cm)
365 constexpr double gapL0_cm = 0.163f; // 1.63 mm
366 constexpr double gapL1L2_cm = 0.12f; // 1.2 mm
367 constexpr double arcL0_cm = 0.6247f; // 6.247 mm
368
369 // φ spans
370 const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); // L0 gap-defined
371 const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); // L1 gap-defined
372 const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined
373
374 const std::string nameL0 =
375 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
377
378 if (rectangularL0) {
379 VDRectangularLayer L0(0,
380 nameL0,
381 kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm);
382
383 // Correct translation: move to radius + half width along x
384 double x = kL0RectHeight_cm + L0.getChipThickness() / 2.;
385 LOGP(info, "Placing rectangular L0 at r={:.3f} cm (half-width={:.3f} cm)", x, 0.5f * kL0RectWidth_cm);
386 double y = 0.0;
387 double z = 0.0;
388
389 // Correct rotation: rotate 90 degrees around z so long side is horizontal
390 auto* rot = new TGeoRotation();
391 rot->RotateZ(90.0);
392
393 auto* tr = new TGeoCombiTrans(x, y, z, rot);
394 L0.createLayer(petalAsm, tr);
395 registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Plane, /*idx*/ 0);
396 } else {
397 VDCylindricalLayer L0(0,
398 nameL0,
399 kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm);
400 L0.createLayer(petalAsm, nullptr);
401 registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 0);
402 }
403
404 const std::string nameL1 =
405 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
407
408 VDCylindricalLayer L1(1,
409 nameL1,
410 kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm);
411 L1.createLayer(petalAsm, nullptr);
412 registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 1);
413
414 const std::string nameL2 =
415 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
417
418 VDCylindricalLayer L2(2,
419 nameL2,
420 kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm);
421 L2.createLayer(petalAsm, nullptr);
422 registerSensor(makeSensorName(nameL2, 2), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 2);
423}
424
425// Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly.
426static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId)
427{
428 if (!petalAsm) {
429 LOGP(error, "addColdPlate: petalAsm is null");
430 return;
431 }
432
433 // Resolve medium: prefer provided medium, otherwise try to fetch from geo manager
434 const TGeoMedium* med = gGeoManager->GetMedium("ALICE3_TRKSERVICES_CERAMIC");
435 if (!med) {
436 LOGP(error, "addColdPlate: can't find the medium.");
437 }
438
439 // Angular span for one petal (deg)
440 constexpr double gapL1L2_cm = 0.12f; // 1.2 mm
441
442 // φ spans
443 const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined
444 const double halfPhiDeg = 0.5f * phiSpanColdplate_deg;
445 const double startPhi = -halfPhiDeg;
446 const double endPhi = +halfPhiDeg;
447
448 // Build tube segment: inner radius, outer radius = inner + thickness, half-length Z
449 auto* shape = new TGeoTubeSeg(static_cast<Double_t>(kColdplateRadius_cm),
450 static_cast<Double_t>(kColdplateRadius_cm + kColdplateThickness_cm),
451 static_cast<Double_t>(0.5 * kColdplateZ_cm),
452 static_cast<Double_t>(startPhi),
453 static_cast<Double_t>(endPhi));
454
455 TString volName = TString::Format("Petal%d_Coldplate", petalId);
456 auto* coldVol = new TGeoVolume(volName, shape, med);
457 coldVol->SetLineColor(kAzure - 3);
458 coldVol->SetTransparency(10);
459
460 // Place in local petal coordinates (no extra transform); keep object alive by allocating shape/volume on heap.
461 petalAsm->AddNode(coldVol, 1);
462
463 LOGP(info, "Adding cold plate {} r={:.3f} cm t={:.3f} cm Lz={:.3f} cm φ=[{:.3f}, {:.3f}]",
464 volName.Data(), kColdplateRadius_cm, kColdplateThickness_cm, kColdplateZ_cm, startPhi, endPhi);
465}
466
467// Add IRIS service module(s) as aluminum annular cylinders placed outside the petals.
468// The two modules are placed at z = ±(36 + halfLength).
469static void addIRISServiceModules(TGeoVolume* petalAsm, int nPetals)
470{
471 if (!petalAsm) {
472 LOGP(error, "addIRISServiceModules: petalAsm is null");
473 return;
474 }
475
476 auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70);
477 const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl);
478
479 if (!med) {
480 LOGP(error, "addIRISServiceModules: ALUMINUM medium not found.");
481 return;
482 }
483
484 constexpr double radius = 3.2; // cm (inner radius)
485 constexpr double thickness = 0.133; // cm (radial thickness)
486 constexpr double halfLength = 19.5; // cm (half-length along Z)
487 const double rIn = radius;
488 const double rOut = radius + thickness;
489
490 // Petal angular span. If you have an exact half-φ from your walls, use it here.
491 const double halfPhi_deg = 0.5 * (360.0 / double(nPetals));
492
493 // Create shape once and reuse
494 auto* segSh = new TGeoTubeSeg(
495 "IRIS_SERVICE_SEGsh",
496 rIn, rOut,
497 halfLength,
498 -halfPhi_deg, halfPhi_deg);
499
500 // Positive Z module
501 TString namePos = "IRIS_Service_Pos";
502 auto* volPos = new TGeoVolume(namePos, segSh, med);
503 volPos->SetLineColor(kRed + 2);
504 volPos->SetTransparency(50);
505
506 // Negative Z module: reuse same shape object, give different name
507 TString nameNeg = "IRIS_Service_Neg";
508 auto* volNeg = new TGeoVolume(nameNeg, segSh, med);
509 volNeg->SetLineColor(kRed + 2);
510 volNeg->SetTransparency(50);
511
512 // Translations (heap-allocated so ROOT keeps them)
513 const double zpos = 36.0 + halfLength;
514 auto* transPos = new TGeoTranslation(0.0, 0.0, static_cast<Double_t>(zpos));
515 auto* transNeg = new TGeoTranslation(0.0, 0.0, static_cast<Double_t>(-zpos));
516
517 // Add to mother volume
518 petalAsm->AddNode(volPos, 1, transPos);
519 petalAsm->AddNode(volNeg, 2, transNeg);
520
521 LOGP(info, "Added IRIS service modules at z = ±{} cm, r=[{}, {}] cm", zpos, rIn, rOut);
522}
523
524// Only the A-side "inside vacuum" piece participates in the cutout.
525static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals)
526{
527 if (!petalAsm) {
528 LOGP(error, "addIRISServiceModulesSegmented: petalAsm is null");
529 return;
530 }
531
532 // --- Service geometry (same as your previous values)
533 constexpr double rIn = 3.2; // cm
534 constexpr double thickness = 0.133; // cm
535 constexpr double rOut = rIn + thickness;
536 constexpr double halfLen = 19.5; // cm
537 constexpr double z0 = 36.0 + halfLen; // 55.5 cm center of +Z service
538 const double zMinA = z0 - halfLen; // 36.0 cm
539 const double zMaxA = z0 + halfLen; // 75.0 cm
540
541 // --- Vacuum vessel window around z∈[-L/2, +L/2] with wall thickness on +Z side
542 // Keep these in sync with TRKServices::createVacuumCompositeShape()
543 constexpr double vacuumVesselLength = 76.0; // cm
544 constexpr double vacuumVesselThickness = 0.08; // cm (0.8 mm)
545 const double halfVess = 0.5 * vacuumVesselLength; // 38.0 cm
546 const double gapStart = halfVess; // 38.00
547 const double gapEnd = halfVess + vacuumVesselThickness; // 38.08
548
549 // --- Petal φ-span (segment)
550 const double halfPhi = 0.5 * (360.0 / double(nPetals));
551
552 auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70);
553 const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl);
554
555 if (!med) {
556 LOGP(error, "addIRISServiceModules: ALUMINUM medium not found.");
557 return;
558 }
559
560 // =========================
561 // C-side (negative Z) whole
562 // =========================
563 {
564 auto* sh = new TGeoTubeSeg(rIn, rOut, halfLen, -halfPhi, +halfPhi);
565 auto* vN = new TGeoVolume("IRIS_Service_Neg", sh, med);
566 vN->SetLineColor(kRed + 2);
567 vN->SetTransparency(55);
568 petalAsm->AddNode(vN, 1, new TGeoTranslation(0., 0., -(z0)));
569 }
570
571 // =====================================
572 // A-side (positive Z): split with a gap
573 // =====================================
574 // Piece 1 (INSIDE vacuum): z ∈ [zMinA, min(zMaxA, gapStart)] → goes into cutout
575 const double L_inVac = std::max(0.0, std::min(zMaxA, gapStart) - zMinA); // expected ~2.0 cm
576 if (L_inVac > 0) {
577 const double dz = 0.5 * L_inVac;
578 const double zc = zMinA + dz; // center of lower slice, ≈ 37.0 cm
579 auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, halfPhi);
580 sh->SetName("IRIS_SERVICE_POS_INVACsh");
581 auto* vP = new TGeoVolume("IRIS_Service_Pos_InVac", sh, med);
582 vP->SetLineColor(kRed + 2);
583 vP->SetTransparency(55);
584 petalAsm->AddNode(vP, 1, new TGeoTranslation(0., 0., zc));
585 LOGP(info, "IRIS A-side (InVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm",
586 zc - dz, zc + dz, 2 * dz);
587 } else {
588 LOGP(warning, "IRIS A-side (InVac): no overlap with vacuum (L_inVac<=0)");
589 }
590
591 // Gap (no material): (gapStart, gapEnd) = (38.00, 38.08)
592
593 // Piece 2 (OUT of vacuum): z ∈ [max(zMinA, gapEnd), zMaxA] → NOT in cutout
594 const double L_outVac = std::max(0.0, zMaxA - std::max(zMinA, gapEnd)); // expected ~36.92 cm
595 if (L_outVac > 0) {
596 const double dz = 0.5 * L_outVac;
597 const double zc = std::max(zMinA, gapEnd) + dz; // center of upper slice
598 auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, +halfPhi);
599 sh->SetName("IRIS_SERVICE_POS_OUTVACsh");
600 auto* vP = new TGeoVolume("IRIS_Service_Pos_OutVac", sh, med);
601 vP->SetLineColor(kRed + 1);
602 vP->SetTransparency(70);
603 petalAsm->AddNode(vP, 2, new TGeoTranslation(0., 0., +zc));
604 LOGP(info, "IRIS A-side (OutVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm",
605 zc - dz, zc + dz, 2 * dz);
606 } else {
607 LOGP(warning, "IRIS A-side (OutVac): no upper piece (L_outVac<=0)");
608 }
609}
610
611// Build disks in local coords: each disk gets only a local Z translation.
612// φ span from gap at rOut.
613static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID)
614{
615
616 if (!petalAsm) {
617 LOGP(error, "addDisks: petalAsm is null");
618 return;
619 }
620
621 const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm);
622
623 for (int i = 0; i < 6; ++i) {
624 const std::string nameD =
625 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
627
628 VDDiskLayer disk(i,
629 nameD,
630 kX2X0, diskRin_cm, diskRout_cm, phiDisk_deg, diskZ_cm[i]);
631
632 // Local Z placement only
633 auto* tr = new TGeoTranslation(0.0, 0.0, static_cast<Double_t>(disk.getZPosition()));
634 disk.createLayer(petalAsm, tr);
635 registerSensor(makeSensorName(nameD, i), petalID, VDSensorDesc::Region::Disk, VDSensorDesc::Type::Plane, /*idx*/ i);
636 }
637}
638
639// Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords.
640static TGeoVolume* buildPetalAssembly(int nPetals, int petalID, bool rectangularL0)
641{
642 auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID));
643 addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm);
644
645 // Pass petalID to layers/disks for naming
646 addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0);
647 addColdPlate(petalAsm, nPetals, petalID);
648 addDisks(petalAsm, nPetals, petalID);
649 addIRISServiceModulesSegmented(petalAsm, nPetals);
650
651 return petalAsm;
652}
653
654// =================== Public entry points ===================
655
656void createIRIS4Geometry(TGeoVolume* motherVolume)
657{
658 if (!motherVolume) {
659 LOGP(error, "createIRIS4Geometry: motherVolume is null");
660 return;
661 }
662
664
665 constexpr int nPetals = 4;
666 for (int p = 0; p < nPetals; ++p) {
667 auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false);
668 // Build the petal-local solids composite once from the FIRST petal
669 if (p == 0) {
670 buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords
671 }
672 const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5);
673 auto* R = new TGeoRotation();
674 R->RotateZ(phiDeg);
675 auto* T = new TGeoCombiTrans(0, 0, 0, R);
676 motherVolume->AddNode(petal, p + 1, T);
677 }
678 buildIrisCutoutFromPetalSolid(nPetals);
679}
680
681void createIRIS5Geometry(TGeoVolume* motherVolume)
682{
683 if (!motherVolume) {
684 LOGP(error, "createIRIS5Geometry: motherVolume is null");
685 return;
686 }
687
689
690 constexpr int nPetals = 4;
691 for (int p = 0; p < nPetals; ++p) {
692 auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true);
693 // Build the petal-local solids composite once from the FIRST petal
694 if (p == 0) {
695 buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords
696 }
697 const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5);
698 auto* R = new TGeoRotation();
699 R->RotateZ(phiDeg);
700 auto* T = new TGeoCombiTrans(0, 0, 0, R);
701 motherVolume->AddNode(petal, p + 1, T);
702 }
703 buildIrisCutoutFromPetalSolid(nPetals);
704}
705
706void createIRIS4aGeometry(TGeoVolume* motherVolume)
707{
708 if (!motherVolume) {
709 LOGP(error, "createIRIS4aGeometry: motherVolume is null");
710 return;
711 }
712
714
715 constexpr int nPetals = 3;
716 for (int p = 0; p < nPetals; ++p) {
717 auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false);
718 // Build the petal-local solids composite once from the FIRST petal
719 if (p == 0) {
720 buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords
721 }
722 const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5);
723 auto* R = new TGeoRotation();
724 R->RotateZ(phiDeg);
725 auto* T = new TGeoCombiTrans(0, 0, 0, R);
726 motherVolume->AddNode(petal, p + 1, T);
727 }
728 buildIrisCutoutFromPetalSolid(nPetals);
729}
730
731void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID, int nPetals, bool rectangularL0)
732{
733 auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0);
734
735 // Optionally rotate the petal for display
736 const double phiDeg = (360.f / static_cast<double>(nPetals)) * (static_cast<double>(petalID) + 0.5f);
737 auto* R = new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", phiDeg, 0, 0));
738 motherVolume->AddNode(petal, 1, R);
739
740 LOGP(info, "Debug: Added Petal{} to {}", petalID, motherVolume->GetName());
741}
742
743} // namespace o2::trk
int32_t i
static MaterialManager & Instance()
static const char * getTRKPetalDiskPattern()
static const char * getTRKSensorPattern()
static const char * getTRKPetalLayerPattern()
static const char * getTRKPetalPattern()
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLboolean r
Definition glcorearb.h:1233
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr double thickness
Definition Specs.h:37
std::vector< VDSensorDesc > & vdSensorRegistry()
void createSinglePetalDebug(TGeoVolume *motherVolume, int petalID=0, int nPetals=4, bool rectangularL0=false)
void createIRIS4aGeometry(TGeoVolume *motherVolume)
void createIRIS4Geometry(TGeoVolume *motherVolume)
void registerSensor(const std::string &volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx)
void createIRIS5Geometry(TGeoVolume *motherVolume)
void clearVDSensorRegistry()
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52