14#include <TGeoVolume.h>
15#include <TGeoMatrix.h>
19#include <TGeoCompositeShape.h>
23#include "TGeoManager.h"
33static std::vector<VDSensorDesc> gVDSensors;
40 gVDSensors.push_back({volName, petal, region,
type, idx});
43static inline std::string makeSensorName(
const std::string& layerName,
int layerNumber)
52inline bool isSolidToCut(
const TGeoVolume*
v)
54 const char* nm =
v->GetName();
55 const char* med =
v->GetMedium() ?
v->GetMedium()->GetName() :
"";
57 if (med && strcmp(med,
"TRK_SILICON$") == 0)
60 if (TString(nm).BeginsWith(
"VD_InnerWallArc"))
62 if (TString(nm).BeginsWith(
"VD_OuterWallArc"))
64 if (TString(nm).BeginsWith(
"VD_SideWall"))
66 if (TString(nm).Contains(
"_Coldplate"))
68 if (TString(nm).BeginsWith(
"IRIS_Service_Neg"))
70 if (TString(nm).BeginsWith(
"IRIS_Service_Pos_InVac"))
76inline const char* ensureShapeName(TGeoVolume*
v)
78 auto* sh =
v->GetShape();
79 TString nm = sh->GetName();
80 if (nm.IsNull() || nm.BeginsWith(
"TGeo")) {
81 TString wanted = TString(
v->GetName()) +
"_sh";
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);
89 if (shapes && !shapes->FindObject(cand))
96static TString gPetalSolidsFormula;
97static int gLocalTrIdx = 0;
100inline void appendLocalTerm(
const char* shapeName,
const TGeoHMatrix& H)
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());
111void traversePetalLocal(TGeoVolume* vol,
const TGeoHMatrix& prefix)
113 auto*
nodes = vol->GetNodes();
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())
123 if (isSolidToCut(childV)) {
124 const char* shapeName = ensureShapeName(childV);
125 appendLocalTerm(shapeName, H);
127 traversePetalLocal(childV, H);
132inline void buildPetalSolidsComposite(TGeoVolume* petalAsm)
135 if (gGeoManager && gGeoManager->GetListOfShapes() && gGeoManager->GetListOfShapes()->FindObject(
"IRIS_PETAL_SOLIDSsh"))
138 gPetalSolidsFormula.Clear();
142 traversePetalLocal(petalAsm, I);
144 if (gPetalSolidsFormula.IsNull()) {
145 LOGP(error,
"IRIS_PETAL_SOLIDSsh formula is empty; did not find solids in petal.");
149 LOGP(info,
"IRIS_PETAL_SOLIDSsh formula: {}", gPetalSolidsFormula.Data());
150 new TGeoCompositeShape(
"IRIS_PETAL_SOLIDSsh", gPetalSolidsFormula.Data());
154inline void buildIrisCutoutFromPetalSolid(
int nPetals)
158 for (
int p = 0;
p < nPetals; ++
p) {
159 const double phi = (360.0 / nPetals) * (p + 0.5);
160 auto*
R =
new TGeoRotation();
162 auto*
RT =
new TGeoCombiTrans(0, 0, 0,
R);
163 RT->SetName(Form(
"IRIS_PETAL_ROT_%d", p));
164 RT->RegisterYourself();
167 cutFormula += Form(
"IRIS_PETAL_SOLIDSsh:%s",
RT->GetName());
169 LOGP(info,
"IRIS_CUTOUTsh formula: {}", cutFormula.Data());
170 new TGeoCompositeShape(
"IRIS_CUTOUTsh", cutFormula.Data());
173 auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() :
nullptr;
174 auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() :
nullptr;
176 if (!mats || !shps) {
177 LOGP(error,
"IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing).");
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());
191 if (!shps->FindObject(
"IRIS_PETAL_SOLIDSsh")) {
192 LOGP(error,
"IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found.");
197 if (!shps->FindObject(
"IRIS_CUTOUTsh")) {
198 LOGP(error,
"IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found.");
203 LOGP(info,
"IRIS cutout sanity: OK ({} petals).", nPetals);
211static constexpr double kX2X0 = 0.001f;
212static constexpr double kLenZ_cm = 50.0f;
215static constexpr double rL0_cm = 0.5f;
216static constexpr double rL1_cm = 1.2f;
217static constexpr double rL2_cm = 2.5f;
220static constexpr double kL0RectHeight_cm = 0.5f;
221static constexpr double kL0RectWidth_cm = 0.83f;
224static constexpr double diskRin_cm = 0.5f;
225static constexpr double diskRout_cm = 2.5f;
226static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f};
229static constexpr double kPetalZ_cm = 70.0f;
230static constexpr double kWallThick_cm = 0.015f;
231static constexpr double kInnerWallRadius_cm = 0.48f;
232static constexpr double kOuterWallRadius_cm = 3.0f;
233static constexpr double kEps_cm = 1.e-4f;
236static constexpr double kColdplateRadius_cm = 2.6f;
237static constexpr double kColdplateThickness_cm = 0.15f;
238static constexpr double kColdplateZ_cm = 50.0f;
245inline double degFromArc(
double arc,
double radius)
248 return (radius > 0.f) ? (arc / radius) * TMath::RadToDeg() : 0.f;
258inline double phiSpanFromGap(
int nPetals,
double gap,
double radius)
260 if (nPetals <= 0 || radius <= 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;
271inline double phiSpanFromArc(
double arcLen,
double radius)
273 return (arcLen > 0.f && radius > 0.f) ? degFromArc(arcLen, radius) : 0.f;
276inline TGeoCombiTrans rotZ(
double phiDeg)
278 auto*
r =
new TGeoRotation();
279 r->RotateZ(
static_cast<Double_t
>(phiDeg));
280 return TGeoCombiTrans(0., 0., 0.,
r);
287static void addPetalWalls(TGeoVolume* petalAsm,
int nPetals,
double outerRadius_cm = kOuterWallRadius_cm)
290 LOGP(error,
"addPetalWalls: petalAsm is null");
295 const TGeoMedium* med = matmgr.getTGeoMedium(
"ALICE3_TRKSERVICES_ALUMINIUM5083");
298 LOGP(warning,
"Petal walls: ALICE3_TRKSERVICES_ALUMINIUM5083$ not found, walls not created.");
302 const double halfPhi = 0.5f * (360.f /
static_cast<double>(nPetals));
303 const double halfZ = 0.5f * kPetalZ_cm;
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);
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);
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);
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),
351 petalAsm->AddNode(sideV, (sgn < 0 ? 1 : 2), tr);
357static void addBarrelLayers(TGeoVolume* petalAsm,
int nPetals,
int petalID,
bool rectangularL0)
360 LOGP(error,
"addBarrelLayers: petalAsm is null");
365 constexpr double gapL0_cm = 0.163f;
366 constexpr double gapL1L2_cm = 0.12f;
367 constexpr double arcL0_cm = 0.6247f;
370 const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm);
371 const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm);
372 const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm);
374 const std::string nameL0 =
379 VDRectangularLayer L0(0,
381 kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm);
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);
390 auto* rot =
new TGeoRotation();
393 auto* tr =
new TGeoCombiTrans(
x,
y,
z, rot);
394 L0.createLayer(petalAsm, tr);
397 VDCylindricalLayer L0(0,
399 kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm);
400 L0.createLayer(petalAsm,
nullptr);
404 const std::string nameL1 =
408 VDCylindricalLayer L1(1,
410 kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm);
411 L1.createLayer(petalAsm,
nullptr);
414 const std::string nameL2 =
418 VDCylindricalLayer L2(2,
420 kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm);
421 L2.createLayer(petalAsm,
nullptr);
426static void addColdPlate(TGeoVolume* petalAsm,
int nPetals,
int petalId)
429 LOGP(error,
"addColdPlate: petalAsm is null");
434 const TGeoMedium* med = gGeoManager->GetMedium(
"ALICE3_TRKSERVICES_CERAMIC");
436 LOGP(error,
"addColdPlate: can't find the medium.");
440 constexpr double gapL1L2_cm = 0.12f;
443 const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm);
444 const double halfPhiDeg = 0.5f * phiSpanColdplate_deg;
445 const double startPhi = -halfPhiDeg;
446 const double endPhi = +halfPhiDeg;
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));
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);
461 petalAsm->AddNode(coldVol, 1);
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);
469static void addIRISServiceModules(TGeoVolume* petalAsm,
int nPetals)
472 LOGP(error,
"addIRISServiceModules: petalAsm is null");
476 auto* matAl =
new TGeoMaterial(
"ALUMINUM", 26.9815, 13, 2.70);
477 const TGeoMedium* med =
new TGeoMedium(
"ALUMINUM", 4, matAl);
480 LOGP(error,
"addIRISServiceModules: ALUMINUM medium not found.");
484 constexpr double radius = 3.2;
486 constexpr double halfLength = 19.5;
487 const double rIn = radius;
491 const double halfPhi_deg = 0.5 * (360.0 / double(nPetals));
494 auto* segSh =
new TGeoTubeSeg(
495 "IRIS_SERVICE_SEGsh",
498 -halfPhi_deg, halfPhi_deg);
501 TString namePos =
"IRIS_Service_Pos";
502 auto* volPos =
new TGeoVolume(namePos, segSh, med);
503 volPos->SetLineColor(kRed + 2);
504 volPos->SetTransparency(50);
507 TString nameNeg =
"IRIS_Service_Neg";
508 auto* volNeg =
new TGeoVolume(nameNeg, segSh, med);
509 volNeg->SetLineColor(kRed + 2);
510 volNeg->SetTransparency(50);
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));
518 petalAsm->AddNode(volPos, 1, transPos);
519 petalAsm->AddNode(volNeg, 2, transNeg);
521 LOGP(info,
"Added IRIS service modules at z = ±{} cm, r=[{}, {}] cm", zpos, rIn, rOut);
525static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm,
int nPetals)
528 LOGP(error,
"addIRISServiceModulesSegmented: petalAsm is null");
533 constexpr double rIn = 3.2;
536 constexpr double halfLen = 19.5;
537 constexpr double z0 = 36.0 + halfLen;
538 const double zMinA = z0 - halfLen;
539 const double zMaxA = z0 + halfLen;
543 constexpr double vacuumVesselLength = 76.0;
544 constexpr double vacuumVesselThickness = 0.08;
545 const double halfVess = 0.5 * vacuumVesselLength;
546 const double gapStart = halfVess;
547 const double gapEnd = halfVess + vacuumVesselThickness;
550 const double halfPhi = 0.5 * (360.0 / double(nPetals));
552 auto* matAl =
new TGeoMaterial(
"ALUMINUM", 26.9815, 13, 2.70);
553 const TGeoMedium* med =
new TGeoMedium(
"ALUMINUM", 4, matAl);
556 LOGP(error,
"addIRISServiceModules: ALUMINUM medium not found.");
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)));
575 const double L_inVac = std::max(0.0, std::min(zMaxA, gapStart) - zMinA);
577 const double dz = 0.5 * L_inVac;
578 const double zc = zMinA + dz;
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);
588 LOGP(warning,
"IRIS A-side (InVac): no overlap with vacuum (L_inVac<=0)");
594 const double L_outVac = std::max(0.0, zMaxA - std::max(zMinA, gapEnd));
596 const double dz = 0.5 * L_outVac;
597 const double zc = std::max(zMinA, gapEnd) + dz;
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);
607 LOGP(warning,
"IRIS A-side (OutVac): no upper piece (L_outVac<=0)");
613static void addDisks(TGeoVolume* petalAsm,
int nPetals,
int petalID)
617 LOGP(error,
"addDisks: petalAsm is null");
621 const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm);
623 for (
int i = 0;
i < 6; ++
i) {
624 const std::string nameD =
630 kX2X0, diskRin_cm, diskRout_cm, phiDisk_deg, diskZ_cm[
i]);
633 auto* tr =
new TGeoTranslation(0.0, 0.0,
static_cast<Double_t
>(disk.getZPosition()));
634 disk.createLayer(petalAsm, tr);
640static TGeoVolume* buildPetalAssembly(
int nPetals,
int petalID,
bool rectangularL0)
642 auto* petalAsm =
new TGeoVolumeAssembly(Form(
"PETAL_%d", petalID));
643 addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm);
646 addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0);
647 addColdPlate(petalAsm, nPetals, petalID);
648 addDisks(petalAsm, nPetals, petalID);
649 addIRISServiceModulesSegmented(petalAsm, nPetals);
659 LOGP(error,
"createIRIS4Geometry: motherVolume is null");
665 constexpr int nPetals = 4;
666 for (
int p = 0; p < nPetals; ++p) {
667 auto* petal = buildPetalAssembly(nPetals, p,
false);
670 buildPetalSolidsComposite(petal);
672 const double phiDeg = (360.0 / double(nPetals)) * (
double(p) + 0.5);
673 auto*
R =
new TGeoRotation();
675 auto* T =
new TGeoCombiTrans(0, 0, 0,
R);
676 motherVolume->AddNode(petal, p + 1, T);
678 buildIrisCutoutFromPetalSolid(nPetals);
684 LOGP(error,
"createIRIS5Geometry: motherVolume is null");
690 constexpr int nPetals = 4;
691 for (
int p = 0; p < nPetals; ++p) {
692 auto* petal = buildPetalAssembly(nPetals, p,
true);
695 buildPetalSolidsComposite(petal);
697 const double phiDeg = (360.0 / double(nPetals)) * (
double(p) + 0.5);
698 auto*
R =
new TGeoRotation();
700 auto* T =
new TGeoCombiTrans(0, 0, 0,
R);
701 motherVolume->AddNode(petal, p + 1, T);
703 buildIrisCutoutFromPetalSolid(nPetals);
709 LOGP(error,
"createIRIS4aGeometry: motherVolume is null");
715 constexpr int nPetals = 3;
716 for (
int p = 0; p < nPetals; ++p) {
717 auto* petal = buildPetalAssembly(nPetals, p,
false);
720 buildPetalSolidsComposite(petal);
722 const double phiDeg = (360.0 / double(nPetals)) * (
double(p) + 0.5);
723 auto*
R =
new TGeoRotation();
725 auto* T =
new TGeoCombiTrans(0, 0, 0,
R);
726 motherVolume->AddNode(petal, p + 1, T);
728 buildIrisCutoutFromPetalSolid(nPetals);
733 auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0);
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);
740 LOGP(info,
"Debug: Added Petal{} to {}", petalID, motherVolume->GetName());
static MaterialManager & Instance()
static const char * getTRKPetalDiskPattern()
static const char * getTRKSensorPattern()
static const char * getTRKPetalLayerPattern()
static const char * getTRKPetalPattern()
GLuint const GLchar * name
GLint GLint GLsizei GLint GLenum GLenum type
GLdouble GLdouble GLdouble z
constexpr double thickness
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)