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) {
61 if (TString(nm).BeginsWith(
"VD_InnerWallArc")) {
64 if (TString(nm).BeginsWith(
"VD_OuterWallArc")) {
67 if (TString(nm).BeginsWith(
"VD_SideWall")) {
70 if (TString(nm).Contains(
"_Coldplate")) {
73 if (TString(nm).BeginsWith(
"IRIS_Service_Neg")) {
76 if (TString(nm).BeginsWith(
"IRIS_Service_Pos_InVac")) {
83inline const char* ensureShapeName(TGeoVolume*
v)
85 auto* sh =
v->GetShape();
86 TString nm = sh->GetName();
87 if (nm.IsNull() || nm.BeginsWith(
"TGeo")) {
88 TString wanted = TString(
v->GetName()) +
"_sh";
91 TString cand = wanted;
92 auto* shapes = gGeoManager ? gGeoManager->GetListOfShapes() :
nullptr;
93 while (shapes && shapes->FindObject(cand)) {
94 cand = Form(
"%s_%d", wanted.Data(), ++k);
97 if (shapes && !shapes->FindObject(cand)) {
101 return sh->GetName();
105static TString gPetalSolidsFormula;
106static int gLocalTrIdx = 0;
109inline void appendLocalTerm(
const char* shapeName,
const TGeoHMatrix& H)
111 auto* ct =
new TGeoCombiTrans(H);
112 ct->SetName(Form(
"IRIS_LOC_TR_%d", gLocalTrIdx++));
113 ct->RegisterYourself();
114 if (!gPetalSolidsFormula.IsNull()) {
115 gPetalSolidsFormula +=
"+";
117 gPetalSolidsFormula += TString::Format(
"%s:%s", shapeName, ct->GetName());
121void traversePetalLocal(TGeoVolume* vol,
const TGeoHMatrix& prefix)
123 auto*
nodes = vol->GetNodes();
127 for (
int i = 0;
i <
nodes->GetEntriesFast(); ++
i) {
129 auto* childV =
node->GetVolume();
130 TGeoHMatrix H(prefix);
131 if (
auto*
m =
node->GetMatrix()) {
135 if (isSolidToCut(childV)) {
136 const char* shapeName = ensureShapeName(childV);
137 appendLocalTerm(shapeName, H);
139 traversePetalLocal(childV, H);
144inline void buildPetalSolidsComposite(TGeoVolume* petalAsm)
147 if (gGeoManager && gGeoManager->GetListOfShapes() && gGeoManager->GetListOfShapes()->FindObject(
"IRIS_PETAL_SOLIDSsh")) {
151 gPetalSolidsFormula.Clear();
155 traversePetalLocal(petalAsm, I);
157 if (gPetalSolidsFormula.IsNull()) {
158 LOGP(error,
"IRIS_PETAL_SOLIDSsh formula is empty; did not find solids in petal.");
162 LOGP(info,
"IRIS_PETAL_SOLIDSsh formula: {}", gPetalSolidsFormula.Data());
163 new TGeoCompositeShape(
"IRIS_PETAL_SOLIDSsh", gPetalSolidsFormula.Data());
167inline void buildIrisCutoutFromPetalSolid(
int nPetals)
171 for (
int p = 0;
p < nPetals; ++
p) {
172 const double phi = (360.0 / nPetals) * (p + 0.5);
173 auto*
R =
new TGeoRotation();
175 auto*
RT =
new TGeoCombiTrans(0, 0, 0,
R);
176 RT->SetName(Form(
"IRIS_PETAL_ROT_%d", p));
177 RT->RegisterYourself();
181 cutFormula += Form(
"IRIS_PETAL_SOLIDSsh:%s",
RT->GetName());
183 LOGP(info,
"IRIS_CUTOUTsh formula: {}", cutFormula.Data());
184 new TGeoCompositeShape(
"IRIS_CUTOUTsh", cutFormula.Data());
187 auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() :
nullptr;
188 auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() :
nullptr;
190 if (!mats || !shps) {
191 LOGP(error,
"IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing).");
196 for (
int p = 0;
p < nPetals; ++
p) {
197 const TString
name = Form(
"IRIS_PETAL_ROT_%d", p);
198 if (!mats->FindObject(
name)) {
199 LOGP(error,
"IRIS cutout sanity: missing matrix {}",
name.Data());
205 if (!shps->FindObject(
"IRIS_PETAL_SOLIDSsh")) {
206 LOGP(error,
"IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found.");
211 if (!shps->FindObject(
"IRIS_CUTOUTsh")) {
212 LOGP(error,
"IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found.");
217 LOGP(info,
"IRIS cutout sanity: OK ({} petals).", nPetals);
225static constexpr double kX2X0 = 0.001f;
226static constexpr double kLenZ_cm = 50.0f;
229static constexpr double rL0_cm = 0.5f;
230static constexpr double rL1_cm = 1.2f;
231static constexpr double rL2_cm = 2.5f;
234static constexpr double kL0RectHeight_cm = 0.5f;
235static constexpr double kL0RectWidth_cm = 0.83f;
238static constexpr double diskRin_cm = 0.5f;
239static constexpr double diskRout_cm = 2.5f;
240static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f};
243static constexpr double kPetalZ_cm = 70.0f;
244static constexpr double kWallThick_cm = 0.015f;
245static constexpr double kInnerWallRadius_cm = 0.48f;
246static constexpr double kOuterWallRadius_cm = 3.0f;
247static constexpr double kEps_cm = 1.e-4f;
250static constexpr double kColdplateRadius_cm = 2.6f;
251static constexpr double kColdplateThickness_cm = 0.15f;
252static constexpr double kColdplateZ_cm = 50.0f;
259inline double degFromArc(
double arc,
double radius)
262 return (radius > 0.f) ? (arc / radius) * TMath::RadToDeg() : 0.f;
272inline double phiSpanFromGap(
int nPetals,
double gap,
double radius)
274 if (nPetals <= 0 || radius <= 0.f) {
277 const double petalPhiDeg = 360.f / nPetals;
278 const double phi = petalPhiDeg - degFromArc(gap, radius);
279 return phi > 0.f ?
phi : 0.f;
286inline double phiSpanFromArc(
double arcLen,
double radius)
288 return (arcLen > 0.f && radius > 0.f) ? degFromArc(arcLen, radius) : 0.f;
291inline TGeoCombiTrans rotZ(
double phiDeg)
293 auto*
r =
new TGeoRotation();
294 r->RotateZ(
static_cast<Double_t
>(phiDeg));
295 return TGeoCombiTrans(0., 0., 0.,
r);
302static void addPetalWalls(TGeoVolume* petalAsm,
int nPetals,
double outerRadius_cm = kOuterWallRadius_cm)
305 LOGP(error,
"addPetalWalls: petalAsm is null");
310 const TGeoMedium* med = matmgr.getTGeoMedium(
"ALICE3_TRKSERVICES_ALUMINIUM5083");
313 LOGP(warning,
"Petal walls: ALICE3_TRKSERVICES_ALUMINIUM5083$ not found, walls not created.");
317 const double halfPhi = 0.5f * (360.f /
static_cast<double>(nPetals));
318 const double halfZ = 0.5f * kPetalZ_cm;
322 auto*
s =
new TGeoTubeSeg(
static_cast<Double_t
>(kInnerWallRadius_cm),
323 static_cast<Double_t
>(kInnerWallRadius_cm + kWallThick_cm),
324 static_cast<Double_t
>(halfZ),
325 static_cast<Double_t
>(-halfPhi),
326 static_cast<Double_t
>(+halfPhi));
327 auto*
v =
new TGeoVolume(
"VD_InnerWallArc", s, med);
328 v->SetLineColor(kGray + 2);
329 v->SetTransparency(70);
330 petalAsm->AddNode(
v, 1);
335 auto*
s =
new TGeoTubeSeg(
static_cast<Double_t
>(outerRadius_cm),
336 static_cast<Double_t
>(outerRadius_cm + kWallThick_cm),
337 static_cast<Double_t
>(halfZ),
338 static_cast<Double_t
>(-halfPhi),
339 static_cast<Double_t
>(+halfPhi));
340 auto*
v =
new TGeoVolume(
"VD_OuterWallArc", s, med);
341 v->SetLineColor(kGray + 2);
342 v->SetTransparency(70);
343 petalAsm->AddNode(
v, 1);
347 const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm));
348 auto* sideS =
new TGeoBBox(
static_cast<Double_t
>(0.5f * radialLen),
349 static_cast<Double_t
>(0.5f * kWallThick_cm),
350 static_cast<Double_t
>(halfZ));
351 auto* sideV =
new TGeoVolume(
"VD_SideWall", sideS, med);
352 sideV->SetLineColor(kGray + 2);
353 sideV->SetTransparency(70);
355 for (
int sgn : {-1, +1}) {
356 const double phi = sgn * halfPhi;
357 const double rMid = kInnerWallRadius_cm + kWallThick_cm + 0.5f * radialLen;
358 const double rad =
static_cast<double>(TMath::DegToRad());
359 const double x = rMid * std::cos(phi * rad);
360 const double y = rMid * std::sin(phi * rad);
361 auto* rot =
new TGeoRotation();
362 rot->RotateZ(
static_cast<Double_t
>(phi));
363 auto* tr =
new TGeoCombiTrans(
static_cast<Double_t
>(
x),
364 static_cast<Double_t
>(
y),
366 petalAsm->AddNode(sideV, (sgn < 0 ? 1 : 2), tr);
372static void addBarrelLayers(TGeoVolume* petalAsm,
int nPetals,
int petalID,
bool rectangularL0)
375 LOGP(error,
"addBarrelLayers: petalAsm is null");
380 constexpr double gapL0_cm = 0.163f;
381 constexpr double gapL1L2_cm = 0.12f;
382 constexpr double arcL0_cm = 0.6247f;
385 const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm);
386 const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm);
387 const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm);
389 const std::string nameL0 =
394 VDRectangularLayer L0(0,
396 kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm);
399 double x = kL0RectHeight_cm + L0.getChipThickness() / 2.;
400 LOGP(info,
"Placing rectangular L0 at r={:.3f} cm (half-width={:.3f} cm)",
x, 0.5f * kL0RectWidth_cm);
405 auto* rot =
new TGeoRotation();
408 auto* tr =
new TGeoCombiTrans(
x,
y,
z, rot);
409 L0.createLayer(petalAsm, tr);
412 VDCylindricalLayer L0(0,
414 kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm);
415 L0.createLayer(petalAsm,
nullptr);
419 const std::string nameL1 =
423 VDCylindricalLayer L1(1,
425 kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm);
426 L1.createLayer(petalAsm,
nullptr);
429 const std::string nameL2 =
433 VDCylindricalLayer L2(2,
435 kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm);
436 L2.createLayer(petalAsm,
nullptr);
441static void addColdPlate(TGeoVolume* petalAsm,
int nPetals,
int petalId)
444 LOGP(error,
"addColdPlate: petalAsm is null");
449 const TGeoMedium* med = gGeoManager->GetMedium(
"ALICE3_TRKSERVICES_CERAMIC");
451 LOGP(error,
"addColdPlate: can't find the medium.");
455 constexpr double gapL1L2_cm = 0.12f;
458 const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm);
459 const double halfPhiDeg = 0.5f * phiSpanColdplate_deg;
460 const double startPhi = -halfPhiDeg;
461 const double endPhi = +halfPhiDeg;
464 auto* shape =
new TGeoTubeSeg(
static_cast<Double_t
>(kColdplateRadius_cm),
465 static_cast<Double_t
>(kColdplateRadius_cm + kColdplateThickness_cm),
466 static_cast<Double_t
>(0.5 * kColdplateZ_cm),
467 static_cast<Double_t
>(startPhi),
468 static_cast<Double_t
>(endPhi));
470 TString volName = TString::Format(
"Petal%d_Coldplate", petalId);
471 auto* coldVol =
new TGeoVolume(volName, shape, med);
472 coldVol->SetLineColor(kAzure - 3);
473 coldVol->SetTransparency(10);
476 petalAsm->AddNode(coldVol, 1);
478 LOGP(info,
"Adding cold plate {} r={:.3f} cm t={:.3f} cm Lz={:.3f} cm φ=[{:.3f}, {:.3f}]",
479 volName.Data(), kColdplateRadius_cm, kColdplateThickness_cm, kColdplateZ_cm, startPhi, endPhi);
484static void addIRISServiceModules(TGeoVolume* petalAsm,
int nPetals)
487 LOGP(error,
"addIRISServiceModules: petalAsm is null");
491 auto* matAl =
new TGeoMaterial(
"ALUMINUM", 26.9815, 13, 2.70);
492 const TGeoMedium* med =
new TGeoMedium(
"ALUMINUM", 4, matAl);
495 LOGP(error,
"addIRISServiceModules: ALUMINUM medium not found.");
499 constexpr double radius = 3.2;
501 constexpr double halfLength = 19.5;
502 const double rIn = radius;
506 const double halfPhi_deg = 0.5 * (360.0 / double(nPetals));
509 auto* segSh =
new TGeoTubeSeg(
510 "IRIS_SERVICE_SEGsh",
513 -halfPhi_deg, halfPhi_deg);
516 TString namePos =
"IRIS_Service_Pos";
517 auto* volPos =
new TGeoVolume(namePos, segSh, med);
518 volPos->SetLineColor(kRed + 2);
519 volPos->SetTransparency(50);
522 TString nameNeg =
"IRIS_Service_Neg";
523 auto* volNeg =
new TGeoVolume(nameNeg, segSh, med);
524 volNeg->SetLineColor(kRed + 2);
525 volNeg->SetTransparency(50);
528 const double zpos = 36.0 + halfLength;
529 auto* transPos =
new TGeoTranslation(0.0, 0.0,
static_cast<Double_t
>(zpos));
530 auto* transNeg =
new TGeoTranslation(0.0, 0.0,
static_cast<Double_t
>(-zpos));
533 petalAsm->AddNode(volPos, 1, transPos);
534 petalAsm->AddNode(volNeg, 2, transNeg);
536 LOGP(info,
"Added IRIS service modules at z = ±{} cm, r=[{}, {}] cm", zpos, rIn, rOut);
540static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm,
int nPetals)
543 LOGP(error,
"addIRISServiceModulesSegmented: petalAsm is null");
548 constexpr double rIn = 3.2;
551 constexpr double halfLen = 19.5;
552 constexpr double z0 = 36.0 + halfLen;
553 const double zMinA = z0 - halfLen;
554 const double zMaxA = z0 + halfLen;
558 constexpr double vacuumVesselLength = 76.0;
559 constexpr double vacuumVesselThickness = 0.08;
560 const double halfVess = 0.5 * vacuumVesselLength;
561 const double gapStart = halfVess;
562 const double gapEnd = halfVess + vacuumVesselThickness;
565 const double halfPhi = 0.5 * (360.0 / double(nPetals));
567 auto* matAl =
new TGeoMaterial(
"ALUMINUM", 26.9815, 13, 2.70);
568 const TGeoMedium* med =
new TGeoMedium(
"ALUMINUM", 4, matAl);
571 LOGP(error,
"addIRISServiceModules: ALUMINUM medium not found.");
579 auto* sh =
new TGeoTubeSeg(rIn, rOut, halfLen, -halfPhi, +halfPhi);
580 auto* vN =
new TGeoVolume(
"IRIS_Service_Neg", sh, med);
581 vN->SetLineColor(kRed + 2);
582 vN->SetTransparency(55);
583 petalAsm->AddNode(vN, 1,
new TGeoTranslation(0., 0., -(z0)));
590 const double L_inVac = std::max(0.0, std::min(zMaxA, gapStart) - zMinA);
592 const double dz = 0.5 * L_inVac;
593 const double zc = zMinA + dz;
594 auto* sh =
new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, halfPhi);
595 sh->SetName(
"IRIS_SERVICE_POS_INVACsh");
596 auto* vP =
new TGeoVolume(
"IRIS_Service_Pos_InVac", sh, med);
597 vP->SetLineColor(kRed + 2);
598 vP->SetTransparency(55);
599 petalAsm->AddNode(vP, 1,
new TGeoTranslation(0., 0., zc));
600 LOGP(info,
"IRIS A-side (InVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm",
601 zc - dz, zc + dz, 2 * dz);
603 LOGP(warning,
"IRIS A-side (InVac): no overlap with vacuum (L_inVac<=0)");
609 const double L_outVac = std::max(0.0, zMaxA - std::max(zMinA, gapEnd));
611 const double dz = 0.5 * L_outVac;
612 const double zc = std::max(zMinA, gapEnd) + dz;
613 auto* sh =
new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, +halfPhi);
614 sh->SetName(
"IRIS_SERVICE_POS_OUTVACsh");
615 auto* vP =
new TGeoVolume(
"IRIS_Service_Pos_OutVac", sh, med);
616 vP->SetLineColor(kRed + 1);
617 vP->SetTransparency(70);
618 petalAsm->AddNode(vP, 2,
new TGeoTranslation(0., 0., +zc));
619 LOGP(info,
"IRIS A-side (OutVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm",
620 zc - dz, zc + dz, 2 * dz);
622 LOGP(warning,
"IRIS A-side (OutVac): no upper piece (L_outVac<=0)");
628static void addDisks(TGeoVolume* petalAsm,
int nPetals,
int petalID)
632 LOGP(error,
"addDisks: petalAsm is null");
636 const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm);
638 for (
int i = 0;
i < 6; ++
i) {
639 const std::string nameD =
645 kX2X0, diskRin_cm, diskRout_cm, phiDisk_deg, diskZ_cm[
i]);
648 auto* tr =
new TGeoTranslation(0.0, 0.0,
static_cast<Double_t
>(disk.getZPosition()));
649 disk.createLayer(petalAsm, tr);
655static TGeoVolume* buildPetalAssembly(
int nPetals,
int petalID,
bool rectangularL0)
657 auto* petalAsm =
new TGeoVolumeAssembly(Form(
"PETAL_%d", petalID));
658 addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm);
661 addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0);
662 addColdPlate(petalAsm, nPetals, petalID);
663 addDisks(petalAsm, nPetals, petalID);
664 addIRISServiceModulesSegmented(petalAsm, nPetals);
674 LOGP(error,
"createIRIS4Geometry: motherVolume is null");
680 constexpr int nPetals = 4;
681 for (
int p = 0; p < nPetals; ++p) {
682 auto* petal = buildPetalAssembly(nPetals, p,
false);
685 buildPetalSolidsComposite(petal);
687 const double phiDeg = (360.0 / double(nPetals)) * (
double(p) + 0.5);
688 auto*
R =
new TGeoRotation();
690 auto* T =
new TGeoCombiTrans(0, 0, 0,
R);
691 motherVolume->AddNode(petal, p + 1, T);
693 buildIrisCutoutFromPetalSolid(nPetals);
699 LOGP(error,
"createIRIS5Geometry: motherVolume is null");
705 constexpr int nPetals = 4;
706 for (
int p = 0; p < nPetals; ++p) {
707 auto* petal = buildPetalAssembly(nPetals, p,
true);
710 buildPetalSolidsComposite(petal);
712 const double phiDeg = (360.0 / double(nPetals)) * (
double(p) + 0.5);
713 auto*
R =
new TGeoRotation();
715 auto* T =
new TGeoCombiTrans(0, 0, 0,
R);
716 motherVolume->AddNode(petal, p + 1, T);
718 buildIrisCutoutFromPetalSolid(nPetals);
724 LOGP(error,
"createIRIS4aGeometry: motherVolume is null");
730 constexpr int nPetals = 3;
731 for (
int p = 0; p < nPetals; ++p) {
732 auto* petal = buildPetalAssembly(nPetals, p,
false);
735 buildPetalSolidsComposite(petal);
737 const double phiDeg = (360.0 / double(nPetals)) * (
double(p) + 0.5);
738 auto*
R =
new TGeoRotation();
740 auto* T =
new TGeoCombiTrans(0, 0, 0,
R);
741 motherVolume->AddNode(petal, p + 1, T);
743 buildIrisCutoutFromPetalSolid(nPetals);
748 auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0);
751 const double phiDeg = (360.f /
static_cast<double>(nPetals)) * (
static_cast<double>(petalID) + 0.5f);
752 auto*
R =
new TGeoCombiTrans(0, 0, 0,
new TGeoRotation(
"", phiDeg, 0, 0));
753 motherVolume->AddNode(petal, 1,
R);
755 LOGP(info,
"Debug: Added Petal{} to {}", petalID, motherVolume->GetName());
std::unique_ptr< expressions::Node > node
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)