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#include <TGeoVolume.h>
14#include <TGeoMatrix.h>
15#include <TGeoTube.h>
16#include <TGeoBBox.h>
17#include <TMath.h>
18#include <TGeoCompositeShape.h>
19#include <TString.h>
21#include "TGeoManager.h"
22#include "Framework/Logger.h"
26#include <algorithm>
27#include <cmath>
28
29namespace o2::trk
30{
31
32static std::vector<VDSensorDesc> gVDSensors; // stays in this TU only
33std::vector<VDSensorDesc>& vdSensorRegistry() { return gVDSensors; }
34
35void clearVDSensorRegistry() { gVDSensors.clear(); }
36
37void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx)
38{
39 gVDSensors.push_back({volName, petal, region, type, idx});
40}
41
42static inline std::string makeSensorName(const std::string& layerName, int layerNumber)
43{
44 return Form("%s_%s%d", layerName.c_str(), o2::trk::GeometryTGeo::getTRKSensorPattern(), layerNumber);
45}
46
47namespace
48{
49
50// Config: which volumes count as SOLIDS to subtract from the vacuum volume
51inline bool isSolidToCut(const TGeoVolume* v)
52{
53 const char* nm = v->GetName();
54 const char* med = v->GetMedium() ? v->GetMedium()->GetName() : "";
55 // silicon sensors (barrel + disks)
56 if (med && strcmp(med, "TRK_SILICON$") == 0) {
57 return true;
58 }
59 // walls, sidewalls, cold-plate, service rings (names from your builders)
60 if (TString(nm).BeginsWith("VD_InnerWallArc")) {
61 return true;
62 }
63 if (TString(nm).BeginsWith("VD_OuterWallArc")) {
64 return true;
65 }
66 if (TString(nm).BeginsWith("VD_SideWall")) {
67 return true;
68 }
69 if (TString(nm).BeginsWith("VD_InnerWallCyl")) {
70 return true;
71 }
72 if (TString(nm).BeginsWith("VD_OuterWallCyl")) {
73 return true;
74 }
75 if (TString(nm).Contains("_Coldplate")) {
76 return true;
77 }
78 if (TString(nm).BeginsWith("IRIS_Service_Neg")) {
79 return true;
80 }
81 if (TString(nm).BeginsWith("IRIS_Service_Pos_InVac")) {
82 return true;
83 }
84 if (TString(nm).BeginsWith("VD_InclinedWall")) {
85 return true;
86 }
87 if (TString(nm).Contains("_ZCap")) {
88 return true;
89 }
90 return false;
91}
92
93// Ensure every leaf shape has a stable, informative name
94inline const char* ensureShapeName(TGeoVolume* v)
95{
96 auto* sh = v->GetShape();
97 TString nm = sh->GetName();
98 if (nm.IsNull() || nm.BeginsWith("TGeo")) {
99 TString wanted = TString(v->GetName()) + "_sh";
100 // avoid collisions
101 int k = 0;
102 TString cand = wanted;
103 auto* shapes = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr;
104 while (shapes && shapes->FindObject(cand)) {
105 cand = Form("%s_%d", wanted.Data(), ++k);
106 }
107 sh->SetName(cand);
108 if (shapes && !shapes->FindObject(cand)) {
109 shapes->Add(sh);
110 }
111 }
112 return sh->GetName();
113}
114
115// Recorder state for the petal-local composite
116static TString gPetalSolidsFormula;
117static int gLocalTrIdx = 0;
118
119// add "ShapeName:IRIS_LOC_TR_k" to the petal-local formula (no outer rotation)
120inline void appendLocalTerm(const char* shapeName, const TGeoHMatrix& H)
121{
122 auto* ct = new TGeoCombiTrans(H);
123 ct->SetName(Form("IRIS_LOC_TR_%d", gLocalTrIdx++));
124 ct->RegisterYourself();
125 if (!gPetalSolidsFormula.IsNull()) {
126 gPetalSolidsFormula += "+";
127 }
128 gPetalSolidsFormula += TString::Format("%s:%s", shapeName, ct->GetName());
129}
130
131// DFS: compose LOCAL transforms only (identity prefix), to capture the petal contents
132void traversePetalLocal(TGeoVolume* vol, const TGeoHMatrix& prefix)
133{
134 auto* nodes = vol->GetNodes();
135 if (!nodes) {
136 return;
137 }
138 for (int i = 0; i < nodes->GetEntriesFast(); ++i) {
139 auto* node = (TGeoNode*)nodes->At(i);
140 auto* childV = node->GetVolume();
141 TGeoHMatrix H(prefix);
142 if (auto* m = node->GetMatrix()) {
143 H.Multiply(m);
144 }
145
146 if (isSolidToCut(childV)) {
147 const char* shapeName = ensureShapeName(childV);
148 appendLocalTerm(shapeName, H);
149 }
150 traversePetalLocal(childV, H);
151 }
152}
153
154// Build (once) a petal-local composite containing ONLY solids (walls, silicon, coldplate, services, disks)
155inline void buildPetalSolidsComposite(TGeoVolume* petalAsm)
156{
157 // If it already exists, skip
158 if (gGeoManager && gGeoManager->GetListOfShapes() && gGeoManager->GetListOfShapes()->FindObject("IRIS_PETAL_SOLIDSsh")) {
159 return;
160 }
161
162 gPetalSolidsFormula.Clear();
163 gLocalTrIdx = 0;
164
165 TGeoHMatrix I; // identity
166 traversePetalLocal(petalAsm, I);
167
168 if (gPetalSolidsFormula.IsNull()) {
169 LOGP(error, "IRIS_PETAL_SOLIDSsh formula is empty; did not find solids in petal.");
170 return;
171 }
172
173 LOGP(info, "IRIS_PETAL_SOLIDSsh formula: {}", gPetalSolidsFormula.Data());
174 new TGeoCompositeShape("IRIS_PETAL_SOLIDSsh", gPetalSolidsFormula.Data());
175}
176
177// Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase
178inline void buildIrisCutoutFromPetalSolid(int nPetals)
179{
180 auto* shps = gGeoManager->GetListOfShapes();
181 auto* base = shps ? dynamic_cast<TGeoShape*>(shps->FindObject("IRIS_PETAL_SOLIDSsh")) : nullptr;
182 if (!base) {
183 LOGP(error, "IRIS cutout: shape 'IRIS_PETAL_SOLIDSsh' not found.");
184 return;
185 }
186
187 // IMPORTANT: for nPetals==1, a composite expression like "A:tr" is invalid.
188 // Just clone the petal solids shape as the global cutout.
189 if (nPetals == 1) {
190 // Remove any previous shape with same name if it exists (optional but keeps things clean)
191 if (shps->FindObject("IRIS_CUTOUTsh")) {
192 // ROOT shape lists are owned by gGeoManager; removing is not always necessary.
193 // Keeping it simple: just create a unique name if it already exists.
194 LOGP(warning, "IRIS cutout: 'IRIS_CUTOUTsh' already exists; overwriting by clone name reuse may be unsafe.");
195 }
196
197 auto* cut = dynamic_cast<TGeoShape*>(base->Clone("IRIS_CUTOUTsh"));
198 if (!cut) {
199 LOGP(error, "IRIS cutout: failed to clone 'IRIS_PETAL_SOLIDSsh' to 'IRIS_CUTOUTsh'.");
200 return;
201 }
202
203 LOGP(info, "IRIS_CUTOUTsh created as clone of IRIS_PETAL_SOLIDSsh (nPetals=1).");
204 return;
205 }
206
207 // nPetals > 1: build union of rotated copies
208 TString cutFormula;
209 for (int p = 0; p < nPetals; ++p) {
210 const double phi = (360.0 / nPetals) * (p + 0.5);
211 auto* R = new TGeoRotation();
212 R->RotateZ(phi);
213 auto* RT = new TGeoCombiTrans(0, 0, 0, R);
214 RT->SetName(Form("IRIS_PETAL_ROT_%d", p));
215 RT->RegisterYourself();
216
217 if (p) {
218 cutFormula += "+";
219 }
220 cutFormula += Form("IRIS_PETAL_SOLIDSsh:%s", RT->GetName());
221 }
222
223 LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data());
224 auto* cut = new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data());
225 (void)cut;
226
227 // Stronger sanity: ensure it parsed into a boolean node
228 auto* cutCheck = dynamic_cast<TGeoCompositeShape*>(shps->FindObject("IRIS_CUTOUTsh"));
229 if (!cutCheck || !cutCheck->GetBoolNode()) {
230 LOGP(error, "IRIS cutout sanity: IRIS_CUTOUTsh exists but parsing failed (no BoolNode).");
231 } else {
232 LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals);
233 }
234}
235
236} // namespace
237
238// =================== Specs & constants (ROOT units: cm) ===================
239static constexpr double kX2X0 = 0.001f; // 0.1% X0 per layer
240static constexpr double kLenZ_cm = 50.0f; // L0/L1/L2 Z length
241
242// Radii (cm)
243static constexpr double rL0_cm = 0.5f; // 5 mm
244static constexpr double rL1_cm = 1.2f; // 12 mm
245static constexpr double rL2_cm = 2.5f; // 25 mm
246
247// IRIS5 rectangular L0 width (cm)
248static constexpr double kL0RectHeight_cm = 0.5f; // 5.0 mm
249static constexpr double kL0RectWidth_cm = 0.83f; // 8.3 mm
250
251// Disks radii (cm)
252static constexpr double diskRin_cm = 0.5f; // 5 mm
253static constexpr double diskRout_cm = 2.5f; // 25 mm
254static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f};
255
256// Petal walls specifications (cm)
257static constexpr double kPetalZ_cm = 70.0f; // full wall height
258static constexpr double kWallThick_cm = 0.02f; // 0.2 mm
259static constexpr double kInnerWallRadius_cm = 0.48f; // 4.8 mm (ALWAYS cylindrical)
260static constexpr double kOuterWallRadius_cm = 4.8f; // 48 mm (can be changed)
261static constexpr double kEps_cm = 2.5e-4f;
262static constexpr double kEps_100um = 0.01f; // 100 microns in cm
263
264// 3 inclined walls ("walls") specs for the full-cylinder option
265// Thickness in-plane (cm). This is the short half-dimension of the TGeoBBox in XY.
266static constexpr double kInclinedWallThick_cm = 0.04f; // 0.4 mm
267// Layer-shell thickness used for the gap boundaries in the inclined-wall construction (cm)
268static constexpr double kSiLayerThick_cm = 0.01f; // 0.1 mm
269// Base tangency angle (deg) for the first wall; the other 2 are +120/+240.
270// This matches the angle used in the ROOT sketch from our chat.
271static constexpr double kInclinedWallPhi0_deg = 27.799f;
272static constexpr double kInclinedWallRmax_cm = 4.75f; // 47.5 mm outer extension
273
274// Coldplate specs (cm)
275static constexpr double kColdplateRadius_cm = 2.6f; // 26 mm (outer radius)
276static constexpr double kColdplateThickness_cm = 0.15f; // 1.5 mm
277static constexpr double kColdplateZ_cm = 50.0f; // full length
278
279// ========== φ-span helpers (gap/arc → degrees) ==========
280namespace
281{
282
283// Convert a linear gap at radius R into an angular gap (deg)
284inline double degFromArc(double arc, double radius)
285{
286 // arc and radius in the SAME units (cm or mm); result in degrees
287 return (radius > 0.f) ? (arc / radius) * TMath::RadToDeg() : 0.f;
288}
289
297inline double phiSpanFromGap(int nPetals, double gap, double radius)
298{
299 if (nPetals <= 0 || radius <= 0.f) {
300 return 0.f;
301 }
302 const double petalPhiDeg = 360.f / nPetals;
303 const double phi = petalPhiDeg - degFromArc(gap, radius);
304 return phi > 0.f ? phi : 0.f;
305}
306
311inline double phiSpanFromArc(double arcLen, double radius)
312{
313 return (arcLen > 0.f && radius > 0.f) ? degFromArc(arcLen, radius) : 0.f;
314}
315
316inline TGeoCombiTrans rotZ(double phiDeg)
317{
318 auto* r = new TGeoRotation();
319 r->RotateZ(static_cast<Double_t>(phiDeg));
320 return TGeoCombiTrans(0., 0., 0., r);
321}
322} // namespace
323
324// ============ Petal sub-builders (LOCAL coords only, no rotation) =========
325
326// Walls: inner cylindrical arc at r=4.8 mm (always), outer arc wall, and two side plates.
327static void addPetalWalls(TGeoVolume* petalAsm,
328 int nPetals,
329 double outerRadius_cm = kOuterWallRadius_cm,
330 bool withSideWalls = true,
331 bool fullCylindricalRadialWalls = false)
332{
333 if (!petalAsm) {
334 LOGP(error, "addPetalWalls: petalAsm is null");
335 return;
336 }
337
339 const TGeoMedium* med = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083");
340
341 if (!med) {
342 LOGP(warning, "Petal walls: ALICE3_TRKSERVICES_ALUMINIUM5083$ not found, walls not created.");
343 return;
344 }
345
346 const double halfZ = 0.5 * kPetalZ_cm;
347
348 // In full-cylinder radial-wall mode we ignore nPetals for the radial walls.
349 const double halfPhi = fullCylindricalRadialWalls ? 180.0 : 0.5 * (360.0 / static_cast<double>(nPetals));
350
351 // ---- Inner radial wall ----
352 if (fullCylindricalRadialWalls) {
353 auto* s = new TGeoTube(static_cast<Double_t>(kInnerWallRadius_cm),
354 static_cast<Double_t>(kInnerWallRadius_cm + kWallThick_cm),
355 static_cast<Double_t>(halfZ));
356 auto* v = new TGeoVolume("VD_InnerWallCyl", s, med);
357 v->SetLineColor(kGray + 2);
358 v->SetTransparency(70);
359 petalAsm->AddNode(v, 1);
360 } else {
361 auto* s = new TGeoTubeSeg(static_cast<Double_t>(kInnerWallRadius_cm),
362 static_cast<Double_t>(kInnerWallRadius_cm + kWallThick_cm),
363 static_cast<Double_t>(halfZ),
364 static_cast<Double_t>(-halfPhi),
365 static_cast<Double_t>(+halfPhi));
366 auto* v = new TGeoVolume("VD_InnerWallArc", s, med);
367 v->SetLineColor(kGray + 2);
368 v->SetTransparency(70);
369 petalAsm->AddNode(v, 1);
370 }
371
372 // ---- Outer radial wall ----
373 if (fullCylindricalRadialWalls) {
374 auto* s = new TGeoTube(static_cast<Double_t>(outerRadius_cm),
375 static_cast<Double_t>(outerRadius_cm + kWallThick_cm),
376 static_cast<Double_t>(halfZ));
377 auto* v = new TGeoVolume("VD_OuterWallCyl", s, med);
378 v->SetLineColor(kGray + 2);
379 v->SetTransparency(70);
380 petalAsm->AddNode(v, 1);
381 } else {
382 auto* s = new TGeoTubeSeg(static_cast<Double_t>(outerRadius_cm),
383 static_cast<Double_t>(outerRadius_cm + kWallThick_cm),
384 static_cast<Double_t>(halfZ),
385 static_cast<Double_t>(-halfPhi),
386 static_cast<Double_t>(+halfPhi));
387 auto* v = new TGeoVolume("VD_OuterWallArc", s, med);
388 v->SetLineColor(kGray + 2);
389 v->SetTransparency(70);
390 petalAsm->AddNode(v, 1);
391 }
392
393 // ---- Side plates (skip in "single petal full cylinders" mode) ----
394 if (!withSideWalls) {
395 return;
396 }
397
398 // ---- Side walls (boxes) at ±halfPhi ----
399 const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm));
400 auto* sideS = new TGeoBBox(static_cast<Double_t>(0.5f * radialLen),
401 static_cast<Double_t>(0.5f * kWallThick_cm),
402 static_cast<Double_t>(halfZ));
403 auto* sideV = new TGeoVolume("VD_SideWall", sideS, med);
404 sideV->SetLineColor(kGray + 2);
405 sideV->SetTransparency(70);
406
407 for (int sgn : {-1, +1}) {
408 const double phi = sgn * halfPhi;
409 const double rMid = kInnerWallRadius_cm + kWallThick_cm + 0.5f * radialLen;
410 const double rad = static_cast<double>(TMath::DegToRad());
411 const double x = rMid * std::cos(phi * rad);
412 const double y = rMid * std::sin(phi * rad);
413 auto* rot = new TGeoRotation();
414 rot->RotateZ(static_cast<Double_t>(phi));
415 auto* tr = new TGeoCombiTrans(static_cast<Double_t>(x),
416 static_cast<Double_t>(y),
417 0.0, rot);
418 petalAsm->AddNode(sideV, (sgn < 0 ? 1 : 2), tr);
419 }
420}
421
422// Build inner layers (L0..L2). L0 may be rectangular (IRIS5) or cylindrical.
423// φ-spans derive from spec gaps/arc; all local placement (no rotation).
424static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0, bool fullCylinders)
425{
426 if (!petalAsm) {
427 LOGP(error, "addBarrelLayers: petalAsm is null");
428 return;
429 }
430
431 // Per spec (mm → cm)
432 constexpr double gapL0_cm = 0.163f; // 1.63 mm
433 constexpr double gapL1L2_cm = 0.12f; // 1.2 mm
434 constexpr double arcL0_cm = 0.6247f; // 6.247 mm
435
436 // φ spans
437 const double phiL0_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL0_cm, rL0_cm);
438 const double phiL1_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm);
439 const double phiL2_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm);
440
441 const std::string nameL0 =
442 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
444
445 if (!fullCylinders && rectangularL0) {
446 VDRectangularLayer L0(0,
447 nameL0,
448 kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm);
449
450 // Correct translation: move to radius + half width along x
451 double x = kL0RectHeight_cm + L0.getChipThickness() / 2.;
452 LOGP(info, "Placing rectangular L0 at r={:.3f} cm (half-width={:.3f} cm)", x, 0.5f * kL0RectWidth_cm);
453 double y = 0.0;
454 double z = 0.0;
455
456 // Correct rotation: rotate 90 degrees around z so long side is horizontal
457 auto* rot = new TGeoRotation();
458 rot->RotateZ(90.0);
459
460 auto* tr = new TGeoCombiTrans(x, y, z, rot);
461 L0.createLayer(petalAsm, tr);
462 registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Plane, /*idx*/ 0);
463 } else {
464 VDCylindricalLayer L0(0,
465 nameL0,
466 kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm);
467 L0.createLayer(petalAsm, nullptr);
468 registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 0);
469 }
470
471 const std::string nameL1 =
472 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
474
475 VDCylindricalLayer L1(1,
476 nameL1,
477 kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm);
478 L1.createLayer(petalAsm, nullptr);
479 registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 1);
480
481 const std::string nameL2 =
482 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
484
485 VDCylindricalLayer L2(2,
486 nameL2,
487 kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm);
488 L2.createLayer(petalAsm, nullptr);
489 registerSensor(makeSensorName(nameL2, 2), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 2);
490}
491
492// Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly.
493static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId, bool fullCylinders = false)
494{
495 if (!petalAsm) {
496 LOGP(error, "addColdPlate: petalAsm is null");
497 return;
498 }
499
500 // Resolve medium: prefer provided medium, otherwise try to fetch from geo manager
501 const TGeoMedium* med = gGeoManager->GetMedium("ALICE3_TRKSERVICES_CERAMIC");
502 if (!med) {
503 LOGP(error, "addColdPlate: can't find the medium.");
504 }
505
506 // Angular span for one petal (deg)
507 constexpr double gapL1L2_cm = 0.12f; // 1.2 mm
508
509 // φ spans
510 const double phiSpanColdplate_deg =
511 fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined in normal mode
512 const double halfPhiDeg = 0.5 * phiSpanColdplate_deg;
513 const double startPhi = -halfPhiDeg;
514 const double endPhi = +halfPhiDeg;
515
516 // Build tube segment: inner radius, outer radius = inner + thickness, half-length Z
517 auto* shape = new TGeoTubeSeg(static_cast<Double_t>(kColdplateRadius_cm),
518 static_cast<Double_t>(kColdplateRadius_cm + kColdplateThickness_cm),
519 static_cast<Double_t>(0.5 * kColdplateZ_cm),
520 static_cast<Double_t>(startPhi),
521 static_cast<Double_t>(endPhi));
522
523 TString volName = TString::Format("Petal%d_Coldplate", petalId);
524 auto* coldVol = new TGeoVolume(volName, shape, med);
525 coldVol->SetLineColor(kAzure - 3);
526 coldVol->SetTransparency(10);
527
528 // Place in local petal coordinates (no extra transform); keep object alive by allocating shape/volume on heap.
529 petalAsm->AddNode(coldVol, 1);
530
531 LOGP(info, "Adding cold plate {} r={:.3f} cm t={:.3f} cm Lz={:.3f} cm φ=[{:.3f}, {:.3f}]",
532 volName.Data(), kColdplateRadius_cm, kColdplateThickness_cm, kColdplateZ_cm, startPhi, endPhi);
533}
534
535// Add IRIS service module(s) as aluminum annular cylinders placed outside the petals.
536// The two modules are placed at z = ±(36 + halfLength).
537static void addIRISServiceModules(TGeoVolume* petalAsm, int nPetals)
538{
539 if (!petalAsm) {
540 LOGP(error, "addIRISServiceModules: petalAsm is null");
541 return;
542 }
543
544 auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70);
545 const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl);
546
547 if (!med) {
548 LOGP(error, "addIRISServiceModules: ALUMINUM medium not found.");
549 return;
550 }
551
552 constexpr double radius = 3.2; // cm (inner radius)
553 constexpr double thickness = 0.133; // cm (radial thickness)
554 constexpr double halfLength = 19.5; // cm (half-length along Z)
555 const double rIn = radius;
556 const double rOut = radius + thickness;
557
558 // Petal angular span. If you have an exact half-φ from your walls, use it here.
559 const double halfPhi_deg = 0.5 * (360.0 / double(nPetals));
560
561 // Create shape once and reuse
562 auto* segSh = new TGeoTubeSeg(
563 "IRIS_SERVICE_SEGsh",
564 rIn, rOut,
565 halfLength,
566 -halfPhi_deg, halfPhi_deg);
567
568 // Positive Z module
569 TString namePos = "IRIS_Service_Pos";
570 auto* volPos = new TGeoVolume(namePos, segSh, med);
571 volPos->SetLineColor(kRed + 2);
572 volPos->SetTransparency(50);
573
574 // Negative Z module: reuse same shape object, give different name
575 TString nameNeg = "IRIS_Service_Neg";
576 auto* volNeg = new TGeoVolume(nameNeg, segSh, med);
577 volNeg->SetLineColor(kRed + 2);
578 volNeg->SetTransparency(50);
579
580 // Translations (heap-allocated so ROOT keeps them)
581 const double zpos = 36.0 + halfLength;
582 auto* transPos = new TGeoTranslation(0.0, 0.0, static_cast<Double_t>(zpos));
583 auto* transNeg = new TGeoTranslation(0.0, 0.0, static_cast<Double_t>(-zpos));
584
585 // Add to mother volume
586 petalAsm->AddNode(volPos, 1, transPos);
587 petalAsm->AddNode(volNeg, 2, transNeg);
588
589 LOGP(info, "Added IRIS service modules at z = ±{} cm, r=[{}, {}] cm", zpos, rIn, rOut);
590}
591
592// Only the A-side "inside vacuum" piece participates in the cutout.
593static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals)
594{
595 if (!petalAsm) {
596 LOGP(error, "addIRISServiceModulesSegmented: petalAsm is null");
597 return;
598 }
599
600 // --- Service geometry (same as your previous values)
601 constexpr double rIn = 3.2; // cm
602 constexpr double thickness = 0.133; // cm
603 constexpr double rOut = rIn + thickness;
604 constexpr double halfLen = 19.5; // cm
605 constexpr double z0 = 36.0 + halfLen; // 55.5 cm center of +Z service
606 const double zMinA = z0 - halfLen; // 36.0 cm
607 const double zMaxA = z0 + halfLen; // 75.0 cm
608
609 // --- Vacuum vessel window around z∈[-L/2, +L/2] with wall thickness on +Z side
610 // Keep these in sync with TRKServices::createVacuumCompositeShape()
611 constexpr double vacuumVesselLength = kPetalZ_cm; // cm
612 constexpr double vacuumVesselThickness = kWallThick_cm; // cm (0.2 mm)
613 const double halfVess = 0.5 * vacuumVesselLength; // 38.0 cm
614 const double gapStart = halfVess; // 38.00
615 const double gapEnd = halfVess + vacuumVesselThickness; // 38.08
616
617 // --- Petal φ-span (segment)
618 const double halfPhi = 0.5 * (360.0 / double(nPetals));
619
620 auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70);
621 const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl);
622
623 if (!med) {
624 LOGP(error, "addIRISServiceModules: ALUMINUM medium not found.");
625 return;
626 }
627
628 // =========================
629 // C-side (negative Z) whole
630 // =========================
631 {
632 auto* sh = new TGeoTubeSeg(rIn, rOut, halfLen, -halfPhi, +halfPhi);
633 auto* vN = new TGeoVolume("IRIS_Service_Neg", sh, med);
634 vN->SetLineColor(kRed + 2);
635 vN->SetTransparency(55);
636 petalAsm->AddNode(vN, 1, new TGeoTranslation(0., 0., -(z0)));
637 }
638
639 // =====================================
640 // A-side (positive Z): split with a gap
641 // =====================================
642 // Piece 1 (INSIDE vacuum): z ∈ [zMinA, min(zMaxA, gapStart)] → goes into cutout
643 const double L_inVac = std::max(0.0, std::min(zMaxA, gapStart) - zMinA); // expected ~2.0 cm
644 if (L_inVac > 0) {
645 const double dz = 0.5 * L_inVac;
646 const double zc = zMinA + dz; // center of lower slice, ≈ 37.0 cm
647 auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, halfPhi);
648 sh->SetName("IRIS_SERVICE_POS_INVACsh");
649 auto* vP = new TGeoVolume("IRIS_Service_Pos_InVac", sh, med);
650 vP->SetLineColor(kRed + 2);
651 vP->SetTransparency(55);
652 petalAsm->AddNode(vP, 1, new TGeoTranslation(0., 0., zc));
653 LOGP(info, "IRIS A-side (InVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm",
654 zc - dz, zc + dz, 2 * dz);
655 } else {
656 LOGP(warning, "IRIS A-side (InVac): no overlap with vacuum (L_inVac<=0)");
657 }
658
659 // Gap (no material): (gapStart, gapEnd) = (38.00, 38.08)
660
661 // Piece 2 (OUT of vacuum): z ∈ [max(zMinA, gapEnd), zMaxA] → NOT in cutout
662 const double L_outVac = std::max(0.0, zMaxA - std::max(zMinA, gapEnd)); // expected ~36.92 cm
663 if (L_outVac > 0) {
664 const double dz = 0.5 * L_outVac;
665 const double zc = std::max(zMinA, gapEnd) + dz; // center of upper slice
666 auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, +halfPhi);
667 sh->SetName("IRIS_SERVICE_POS_OUTVACsh");
668 auto* vP = new TGeoVolume("IRIS_Service_Pos_OutVac", sh, med);
669 vP->SetLineColor(kRed + 1);
670 vP->SetTransparency(70);
671 petalAsm->AddNode(vP, 2, new TGeoTranslation(0., 0., +zc));
672 LOGP(info, "IRIS A-side (OutVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm",
673 zc - dz, zc + dz, 2 * dz);
674 } else {
675 LOGP(warning, "IRIS A-side (OutVac): no upper piece (L_outVac<=0)");
676 }
677}
678
679// Build disks in local coords: each disk gets only a local Z translation.
680// φ span from gap at rOut.
681static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID, bool fullCylinders)
682{
683
684 if (!petalAsm) {
685 LOGP(error, "addDisks: petalAsm is null");
686 return;
687 }
688
689 const double phiDisk_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm);
690
691 for (int i = 0; i < 6; ++i) {
692 const std::string nameD =
693 std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" +
695
696 VDDiskLayer disk(i,
697 nameD,
698 kX2X0, diskRin_cm, diskRout_cm, phiDisk_deg, diskZ_cm[i]);
699
700 // Local Z placement only
701 auto* tr = new TGeoTranslation(0.0, 0.0, static_cast<Double_t>(disk.getZPosition()));
702 disk.createLayer(petalAsm, tr);
703 registerSensor(makeSensorName(nameD, i), petalID, VDSensorDesc::Region::Disk, VDSensorDesc::Type::Plane, /*idx*/ i);
704 }
705}
706
707// Add Z end-cap walls to "close" the petal/cylinder volume at zMin and zMax.
708// Implemented as thin rings (TGeoTube) with thickness 'capThick_cm' in Z,
709// spanning radii [rIn_cm, rOut_cm].
710static void addPetalEndCaps(TGeoVolume* petalAsm,
711 int petalId,
712 double rIn_cm,
713 double rOut_cm,
714 double zMin_cm,
715 double zMax_cm,
716 double capThick_cm)
717{
718 if (!petalAsm) {
719 LOGP(error, "addPetalEndCaps: petalAsm is null");
720 return;
721 }
722
724 const TGeoMedium* med =
725 matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083");
726
727 if (!med) {
728 LOGP(warning,
729 "addPetalEndCaps: ALICE3_TRKSERVICES_ALUMINIUM5083 not found, caps not created.");
730 return;
731 }
732
733 const double halfT = 0.5 * capThick_cm;
734
735 auto* sh = new TGeoTube(static_cast<Double_t>(rIn_cm),
736 static_cast<Double_t>(rOut_cm),
737 static_cast<Double_t>(halfT));
738
739 TString vname = Form("Petal%d_ZCap", petalId);
740 auto* v = new TGeoVolume(vname, sh, med);
741 v->SetLineColor(kGray + 2);
742 v->SetTransparency(70);
743
744 auto* trMin = new TGeoTranslation(0.0, 0.0,
745 static_cast<Double_t>(zMin_cm + halfT));
746 auto* trMax = new TGeoTranslation(0.0, 0.0,
747 static_cast<Double_t>(zMax_cm - halfT));
748
749 petalAsm->AddNode(v, 1, trMin);
750 petalAsm->AddNode(v, 2, trMax);
751}
752
753// Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords.
754static TGeoVolume* buildPetalAssembly(int nPetals,
755 int petalID,
756 bool rectangularL0,
757 bool fullCylinders,
758 bool withSideWalls)
759{
760 auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID));
761
762 // In the special mode: no side walls, but keep radial walls as FULL cylinders.
763 addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm,
764 /*withSideWalls=*/withSideWalls,
765 /*fullCylindricalRadialWalls=*/fullCylinders);
766
767 addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0, fullCylinders);
768 // addDisks(petalAsm, nPetals, petalID, fullCylinders); // disks removed according to the v3b layout
769
770 addColdPlate(petalAsm, nPetals, petalID, /*fullCylinders=*/false);
771 addIRISServiceModulesSegmented(petalAsm, nPetals);
772
773 return petalAsm;
774}
775
776static TGeoVolume* buildFullCylAssembly(int petalID, bool withDisks)
777{
778 // IMPORTANT: keep naming consistent with createIRIS4/5 (PETAL_%d)
779 auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID));
780
781 // Radial walls only: full 360° cylinders, no side plates
782 addPetalWalls(petalAsm,
783 /*nPetals=*/1,
784 /*outerRadius_cm=*/kOuterWallRadius_cm,
785 /*withSideWalls=*/false,
786 /*fullCylindricalRadialWalls=*/true);
787
788 // --- Z end-cap walls to close the petal in Z ---
789 {
790 const double zMin = -0.5 * kPetalZ_cm - 2 * kWallThick_cm;
791 const double zMax = +0.5 * kPetalZ_cm + 2 * kWallThick_cm;
792 const double rIn = kInnerWallRadius_cm;
793 const double rOut = kOuterWallRadius_cm + kWallThick_cm;
794
795 addPetalEndCaps(petalAsm,
796 petalID,
797 rIn,
798 rOut,
799 zMin,
800 zMax,
801 kWallThick_cm);
802 }
803
804 // Full 360° barrel cylinders
805 addBarrelLayers(petalAsm,
806 /*nPetals=*/1,
807 /*petalID=*/petalID,
808 /*rectangularL0=*/false,
809 /*fullCylinders=*/true);
810
811 addColdPlate(petalAsm, 1, petalID, /*fullCylinders=*/true);
812 addIRISServiceModulesSegmented(petalAsm, /*nPetals=*/1);
813
814 // Optionally add full 360° disks
815 if (withDisks) {
816 addDisks(petalAsm,
817 /*nPetals=*/1,
818 /*petalID=*/petalID,
819 /*fullCylinders=*/true);
820 }
821
822 return petalAsm;
823}
824
825// Add 3 inclined walls (straight walls) into a full-cylinder petal assembly.
826// The walls are implemented as TWO TGeoBBox segments per wall, living in the gaps:
827// - segment 01: from tangency at Rtan to inner surface of L1
828// - segment 12: from outer surface of L1 to inner surface of L2
829// The construction accounts for the finite wall thickness (kInclinedWallThick_cm).
830static void addInclinedWalls3FullCyl(TGeoVolume* petalAsm, double phi0_deg = kInclinedWallPhi0_deg)
831{
832 if (!petalAsm) {
833 LOGP(error, "addInclinedWalls3FullCyl: petalAsm is null");
834 return;
835 }
836
838 const TGeoMedium* med = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083");
839 if (!med) {
840 LOGP(warning, "addInclinedWalls3FullCyl: ALICE3_TRKSERVICES_ALUMINIUM5083 not found, walls not created.");
841 return;
842 }
843
844 // Clearance margin from layer/coldplate surfaces (cm)
845 constexpr double clearanceMargin = 0.010; // 100 microns
846
847 // Geometry inputs (cm)
848 constexpr double R0 = rL0_cm;
849 constexpr double R1 = rL1_cm;
850 constexpr double R2 = rL2_cm;
851 constexpr double Rmax = kInclinedWallRmax_cm;
852
853 const double wallDy = 0.5 * kInclinedWallThick_cm;
854 const double shellTh = kSiLayerThick_cm; // 0.1 mm shell thickness for bounds
855 const double h = 0.5 * shellTh;
856 const double dz = 0.5 * kPetalZ_cm; // match barrel/coldplate length in full-cyl option
857
858 constexpr int nWalls = 3;
859 constexpr double dPhi = 360.0 / double(nWalls);
860
861 // Gap boundaries (shell surfaces)
862 const double R0_out = R0 + h;
863 const double R1_in = R1 - h;
864 const double R1_out = R1 + h;
865 const double R2_in = R2 - h;
866 const double R2_out = R2 + h;
867
868 // Coldplate outer radius (tube segment is [kColdplateRadius_cm, kColdplateRadius_cm + kColdplateThickness_cm])
869 const double Rcold_out = kColdplateRadius_cm + kColdplateThickness_cm;
870
871 // Tangency radius choice (thickness-safe at s=0): need Rtan - wallDy >= R0_out
872 const double Rtan = R0_out + wallDy + clearanceMargin;
873
874 // For finite-thickness box:
875 // outermost edge uses Reff_plus, innermost edge uses Reff_minus
876 const double Reff_plus = Rtan + wallDy + clearanceMargin;
877 const double Reff_minus = std::max(0.0, Rtan - wallDy - clearanceMargin);
878
879 auto sAt = [](double R, double Reff) -> double {
880 const double v = R * R - Reff * Reff;
881 return (v > 0.0) ? std::sqrt(v) : 0.0;
882 };
883
884 // Segment bounds in 's' (thickness-safe):
885 // 01: from tangency to L1 inner surface (outer edge <= R1_in)
886 const double sa01 = 0.0;
887 const double sb01 = sAt(R1_in, Reff_plus);
888
889 // 12: from outside L1 to inside L2
890 const double sa12 = sAt(R1_out, Reff_minus); // inner edge >= R1_out
891 const double sb12 = sAt(R2_in, Reff_plus); // outer edge <= R2_in
892
893 // 23: from outside coldplate (and outside L2) to Rmax
894 const double R23_start = std::max(R2_out, Rcold_out) + clearanceMargin;
895 const double sa23 = sAt(R23_start, Reff_minus); // inner edge >= start radius
896 const double sb23 = sAt(Rmax, Reff_plus); // outer edge <= Rmax
897
898 if (!((sb01 > sa01) && (sb12 > sa12) && (sb23 > sa23))) {
899 LOGP(error,
900 "addInclinedWalls3FullCyl: invalid bounds. 01:[{},{}] 12:[{},{}] 23:[{},{}] "
901 "Rtan={} Reff-={} Reff+={} R23_start={}",
902 sa01, sb01, sa12, sb12, sa23, sb23,
903 Rtan, Reff_minus, Reff_plus, R23_start);
904 return;
905 }
906
907 // Half-lengths and center parameters (s-centers)
908 const double dx01 = 0.5 * (sb01 - sa01);
909 const double dx12 = 0.5 * (sb12 - sa12);
910 const double dx23 = 0.5 * (sb23 - sa23);
911
912 const double sc01 = 0.5 * (sa01 + sb01);
913 const double sc12 = 0.5 * (sa12 + sb12);
914 const double sc23 = 0.5 * (sa23 + sb23);
915
916 // Create shapes once, reuse for all walls
917 auto* sh01 = new TGeoBBox(dx01, wallDy, dz);
918 auto* sh12 = new TGeoBBox(dx12, wallDy, dz);
919 auto* sh23 = new TGeoBBox(dx23, wallDy, dz);
920 sh01->SetName("VD_InclinedWall01_sh");
921 sh12->SetName("VD_InclinedWall12_sh");
922 sh23->SetName("VD_InclinedWall23_sh");
923
924 const double phi0_rad = phi0_deg * TMath::DegToRad();
925
926 for (int i = 0; i < nWalls; ++i) {
927 const double phi = phi0_rad + i * (dPhi * TMath::DegToRad());
928 const double cosPhi = std::cos(phi);
929 const double sinPhi = std::sin(phi);
930
931 // Tangency point on Rtan
932 const double xT = Rtan * cosPhi;
933 const double yT = Rtan * sinPhi;
934
935 // Tangent direction u = (-sin, cos)
936 const double ux = -sinPhi;
937 const double uy = cosPhi;
938
939 // Centers (in XY)
940 const double cx01 = xT + sc01 * ux;
941 const double cy01 = yT + sc01 * uy;
942 const double cx12 = xT + sc12 * ux;
943 const double cy12 = yT + sc12 * uy;
944 const double cx23 = xT + sc23 * ux;
945 const double cy23 = yT + sc23 * uy;
946
947 // Rotation: local X along tangent => angle = phi + 90°
948 const double alpha_deg = phi0_deg + i * dPhi + 90.0;
949 auto* rot = new TGeoRotation();
950 rot->RotateZ(alpha_deg);
951
952 // Create volumes per wall (unique names)
953 auto* v01 = new TGeoVolume(Form("VD_InclinedWall01_%d", i), sh01, med);
954 auto* v12 = new TGeoVolume(Form("VD_InclinedWall12_%d", i), sh12, med);
955 auto* v23 = new TGeoVolume(Form("VD_InclinedWall23_%d", i), sh23, med);
956 v01->SetLineColor(kOrange + 7);
957 v12->SetLineColor(kOrange + 7);
958 v23->SetLineColor(kOrange + 7);
959 v01->SetTransparency(70);
960 v12->SetTransparency(70);
961 v23->SetTransparency(70);
962
963 auto* T01 = new TGeoCombiTrans(cx01, cy01, 0.0, rot);
964 auto* T12 = new TGeoCombiTrans(cx12, cy12, 0.0, new TGeoRotation(*rot));
965 auto* T23 = new TGeoCombiTrans(cx23, cy23, 0.0, new TGeoRotation(*rot));
966
967 petalAsm->AddNode(v01, 1, T01);
968 petalAsm->AddNode(v12, 1, T12);
969 petalAsm->AddNode(v23, 1, T23);
970
971 LOGP(debug,
972 "InclinedWall {}: 01({:.3f},{:.3f}) 12({:.3f},{:.3f}) 23({:.3f},{:.3f}) angle={:.2f}°",
973 i, cx01, cy01, cx12, cy12, cx23, cy23, alpha_deg);
974 }
975}
976
977// =================== Public entry points ===================
978
979void createIRIS4Geometry(TGeoVolume* motherVolume)
980{
981 if (!motherVolume) {
982 LOGP(error, "createIRIS4Geometry: motherVolume is null");
983 return;
984 }
985
987
988 constexpr int nPetals = 4;
989 for (int p = 0; p < nPetals; ++p) {
990 auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false,
991 /*fullCylinders=*/false,
992 /*withSideWalls=*/true);
993 // Build the petal-local solids composite once from the FIRST petal
994 if (p == 0) {
995 buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords
996 }
997 const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5);
998 auto* R = new TGeoRotation();
999 R->RotateZ(phiDeg);
1000 auto* T = new TGeoCombiTrans(0, 0, 0, R);
1001 motherVolume->AddNode(petal, p + 1, T);
1002 }
1003 buildIrisCutoutFromPetalSolid(nPetals);
1004}
1005
1006void createIRIS5Geometry(TGeoVolume* motherVolume)
1007{
1008 if (!motherVolume) {
1009 LOGP(error, "createIRIS5Geometry: motherVolume is null");
1010 return;
1011 }
1012
1014
1015 constexpr int nPetals = 4;
1016 for (int p = 0; p < nPetals; ++p) {
1017 auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true,
1018 /*fullCylinders=*/false,
1019 /*withSideWalls=*/true);
1020 // Build the petal-local solids composite once from the FIRST petal
1021 if (p == 0) {
1022 buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords
1023 }
1024 const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5);
1025 auto* R = new TGeoRotation();
1026 R->RotateZ(phiDeg);
1027 auto* T = new TGeoCombiTrans(0, 0, 0, R);
1028 motherVolume->AddNode(petal, p + 1, T);
1029 }
1030 buildIrisCutoutFromPetalSolid(nPetals);
1031}
1032
1033void createIRIS4aGeometry(TGeoVolume* motherVolume)
1034{
1035 if (!motherVolume) {
1036 LOGP(error, "createIRIS4aGeometry: motherVolume is null");
1037 return;
1038 }
1039
1041
1042 constexpr int nPetals = 3;
1043 for (int p = 0; p < nPetals; ++p) {
1044 auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false,
1045 /*fullCylinders=*/false,
1046 /*withSideWalls=*/true);
1047 // Build the petal-local solids composite once from the FIRST petal
1048 if (p == 0) {
1049 buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords
1050 }
1051 const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5);
1052 auto* R = new TGeoRotation();
1053 R->RotateZ(phiDeg);
1054 auto* T = new TGeoCombiTrans(0, 0, 0, R);
1055 motherVolume->AddNode(petal, p + 1, T);
1056 }
1057 buildIrisCutoutFromPetalSolid(nPetals);
1058}
1059
1060void createIRISGeometryFullCyl(TGeoVolume* motherVolume)
1061{
1062 if (!motherVolume) {
1063 LOGP(error, "createIRISGeometryFullCyl: motherVolume is null");
1064 return;
1065 }
1066
1068
1069 constexpr int nPetals = 1;
1070 constexpr int petalID = 0;
1071
1072 auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/false);
1073 motherVolume->AddNode(petal, 1, nullptr);
1074
1075 buildPetalSolidsComposite(petal);
1076 buildIrisCutoutFromPetalSolid(nPetals);
1077}
1078
1079void createIRISGeometry3InclinedWalls(TGeoVolume* motherVolume)
1080{
1081 if (!motherVolume) {
1082 LOGP(error, "createIRISGeometry3InclinedWalls: motherVolume is null");
1083 return;
1084 }
1085
1087
1088 constexpr int nPetals = 1;
1089 constexpr int petalID = 0;
1090
1091 // Start from the same content as createIRISGeometryFullCyl
1092 auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/false);
1093
1094 // Add the 3 inclined walls into the same assembly
1095 addInclinedWalls3FullCyl(petal, kInclinedWallPhi0_deg);
1096
1097 motherVolume->AddNode(petal, 1, nullptr);
1098
1099 // Same cutout pipeline as full-cyl
1100 buildPetalSolidsComposite(petal);
1101 buildIrisCutoutFromPetalSolid(nPetals);
1102}
1103
1104void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume)
1105{
1106 if (!motherVolume) {
1107 LOGP(error, "createIRISGeometryFullCylDisks: motherVolume is null");
1108 return;
1109 }
1110
1112
1113 constexpr int nPetals = 1;
1114 constexpr int petalID = 0;
1115
1116 auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/true);
1117 motherVolume->AddNode(petal, 1, nullptr);
1118
1119 // Same cutout pipeline as createIRIS4/5:
1120 buildPetalSolidsComposite(petal);
1121 buildIrisCutoutFromPetalSolid(nPetals);
1122}
1123
1124void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID, int nPetals, bool rectangularL0)
1125{
1126 auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0, false, true);
1127
1128 // Optionally rotate the petal for display
1129 const double phiDeg = (360.f / static_cast<double>(nPetals)) * (static_cast<double>(petalID) + 0.5f);
1130 auto* R = new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", phiDeg, 0, 0));
1131 motherVolume->AddNode(petal, 1, R);
1132
1133 LOGP(info, "Debug: Added Petal{} to {}", petalID, motherVolume->GetName());
1134}
1135
1136} // namespace o2::trk
std::ostringstream debug
std::unique_ptr< expressions::Node > node
int32_t i
float float float & zMax
float float & zMin
Class for time synchronization of RawReader instances.
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
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
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 createIRISGeometry3InclinedWalls(TGeoVolume *motherVolume)
void createSinglePetalDebug(TGeoVolume *motherVolume, int petalID=0, int nPetals=4, bool rectangularL0=false)
void createIRISGeometryFullCyl(TGeoVolume *motherVolume)
void createIRIS4aGeometry(TGeoVolume *motherVolume)
void createIRIS4Geometry(TGeoVolume *motherVolume)
void createIRISGeometryFullCylwithDisks(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