From e62ddfe065c763d2bee88038736c15b715feb738 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Wed, 21 Jan 2026 12:01:42 +0100 Subject: [PATCH 1/4] VD layer sensitive silicon 20 um, 80 um non-sens --- Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index a5a60422f77eb..59a211c25fe91 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -34,11 +34,11 @@ namespace VD // TODO: add a primitive segmentation with more granularity wrt 1/4 { namespace silicon { -constexpr double thickness{30 * mu}; // thickness of the silicon (should be 10 um epitaxial layer + 20 um substrate)? +constexpr double thickness{20 * mu}; // thickness of the silicon (should be 10 um epitaxial layer + 20 um substrate)? } // namespace silicon namespace metalstack { -constexpr double thickness{0 * mu}; // thickness of the copper metal stack - for the moment it is not implemented +constexpr double thickness{80 * mu}; // thickness of the copper metal stack - for the moment it is not implemented. PL: set to 80 um considering silicon as material } // namespace metalstack namespace petal { From 3e6bee869bedffea97d1eb5c22fd90e194552a7c Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Wed, 21 Jan 2026 12:04:09 +0100 Subject: [PATCH 2/4] Add an option for pure cylindrical IRIS --- .../include/TRKSimulation/VDGeometryBuilder.h | 8 +- .../include/TRKSimulation/VDLayer.h | 13 +- .../TRK/simulation/src/VDGeometryBuilder.cxx | 307 ++++++++++++++---- .../ALICE3/TRK/simulation/src/VDLayer.cxx | 280 ++++++++++++++-- 4 files changed, 521 insertions(+), 87 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h index 0a2cb68f2233a..c337ddb102147 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h @@ -24,9 +24,11 @@ namespace o2::trk // Each function builds one local petal assembly (walls + layers + disks) // and then places/rotates the petal once into the mother volume. -void createIRIS4Geometry(TGeoVolume* motherVolume); // 4 petals, cylindrical L0 -void createIRIS4aGeometry(TGeoVolume* motherVolume); // 3 petals, cylindrical L0 -void createIRIS5Geometry(TGeoVolume* motherVolume); // 4 petals, rectangular L0 +void createIRISGeometryFullCyl(TGeoVolume* motherVolume); // Full-cylinder IRIS geometry (no petals, no gaps, no side walls) +void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume); // Full-cylinder IRIS geometry (no petals, no gaps, no side walls) incl. disks +void createIRIS4Geometry(TGeoVolume* motherVolume); // 4 petals, cylindrical L0 +void createIRIS4aGeometry(TGeoVolume* motherVolume); // 3 petals, cylindrical L0 +void createIRIS5Geometry(TGeoVolume* motherVolume); // 4 petals, rectangular L0 void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID = 0, int nPetals = 4, bool rectangularL0 = false); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h index 9e9ca2971bc3b..acf9b19342e4b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h @@ -39,9 +39,10 @@ class VDLayer protected: int mLayerNumber{0}; std::string mLayerName; - double mX2X0{0.f}; // Radiation length in units of X0 - double mChipThickness{0.f}; // thickness derived from X/X0 - double mModuleWidth{4.54f}; // cm + double mX2X0{0.f}; // Radiation length in units of X0 + double mChipThickness{0.f}; // thickness derived from X/X0 + double mSensorThickness{0.f}; // + double mModuleWidth{4.54f}; // cm // ClassDef(VDLayer, 1) }; @@ -54,6 +55,8 @@ class VDCylindricalLayer : public VDLayer double radius, double phiSpanDeg, double lengthZ, double lengthSensZ); TGeoVolume* createSensor() const; // builds the sensor volume + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; private: @@ -73,6 +76,8 @@ class VDRectangularLayer : public VDLayer double width, double lengthZ, double lengthSensZ); TGeoVolume* createSensor() const; + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; private: @@ -91,6 +96,8 @@ class VDDiskLayer : public VDLayer double rMin, double rMax, double phiSpanDeg, double zPos); TGeoVolume* createSensor() const; + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; double getZPosition() const { return mZPos; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 5df875713262c..6ce04bb8443ef 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -67,6 +67,12 @@ inline bool isSolidToCut(const TGeoVolume* v) if (TString(nm).BeginsWith("VD_SideWall")) { return true; } + if (TString(nm).BeginsWith("VD_InnerWallCyl")) { + return true; + } + if (TString(nm).BeginsWith("VD_OuterWallCyl")) { + return true; + } if (TString(nm).Contains("_Coldplate")) { return true; } @@ -166,7 +172,34 @@ inline void buildPetalSolidsComposite(TGeoVolume* petalAsm) // Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase inline void buildIrisCutoutFromPetalSolid(int nPetals) { - // Create n rotation transforms + auto* shps = gGeoManager->GetListOfShapes(); + auto* base = shps ? dynamic_cast(shps->FindObject("IRIS_PETAL_SOLIDSsh")) : nullptr; + if (!base) { + LOGP(error, "IRIS cutout: shape 'IRIS_PETAL_SOLIDSsh' not found."); + return; + } + + // IMPORTANT: for nPetals==1, a composite expression like "A:tr" is invalid. + // Just clone the petal solids shape as the global cutout. + if (nPetals == 1) { + // Remove any previous shape with same name if it exists (optional but keeps things clean) + if (shps->FindObject("IRIS_CUTOUTsh")) { + // ROOT shape lists are owned by gGeoManager; removing is not always necessary. + // Keeping it simple: just create a unique name if it already exists. + LOGP(warning, "IRIS cutout: 'IRIS_CUTOUTsh' already exists; overwriting by clone name reuse may be unsafe."); + } + + auto* cut = dynamic_cast(base->Clone("IRIS_CUTOUTsh")); + if (!cut) { + LOGP(error, "IRIS cutout: failed to clone 'IRIS_PETAL_SOLIDSsh' to 'IRIS_CUTOUTsh'."); + return; + } + + LOGP(info, "IRIS_CUTOUTsh created as clone of IRIS_PETAL_SOLIDSsh (nPetals=1)."); + return; + } + + // nPetals > 1: build union of rotated copies TString cutFormula; for (int p = 0; p < nPetals; ++p) { const double phi = (360.0 / nPetals) * (p + 0.5); @@ -175,47 +208,23 @@ inline void buildIrisCutoutFromPetalSolid(int nPetals) auto* RT = new TGeoCombiTrans(0, 0, 0, R); RT->SetName(Form("IRIS_PETAL_ROT_%d", p)); RT->RegisterYourself(); + if (p) { cutFormula += "+"; } cutFormula += Form("IRIS_PETAL_SOLIDSsh:%s", RT->GetName()); } - LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); - new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); - // --- Sanity check: required matrices & shapes exist - auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() : nullptr; - auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); + auto* cut = new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); + (void)cut; - if (!mats || !shps) { - LOGP(error, "IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing)."); + // Stronger sanity: ensure it parsed into a boolean node + auto* cutCheck = dynamic_cast(shps->FindObject("IRIS_CUTOUTsh")); + if (!cutCheck || !cutCheck->GetBoolNode()) { + LOGP(error, "IRIS cutout sanity: IRIS_CUTOUTsh exists but parsing failed (no BoolNode)."); } else { - bool ok = true; - - // Check the petal rotations were registered and referenced - for (int p = 0; p < nPetals; ++p) { - const TString name = Form("IRIS_PETAL_ROT_%d", p); - if (!mats->FindObject(name)) { - LOGP(error, "IRIS cutout sanity: missing matrix {}", name.Data()); - ok = false; - } - } - - // Check that the local petal composite exists - if (!shps->FindObject("IRIS_PETAL_SOLIDSsh")) { - LOGP(error, "IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found."); - ok = false; - } - - // Check that the global cutout shape was created - if (!shps->FindObject("IRIS_CUTOUTsh")) { - LOGP(error, "IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found."); - ok = false; - } - - if (ok) { - LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); - } + LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); } } @@ -299,7 +308,11 @@ inline TGeoCombiTrans rotZ(double phiDeg) // ============ Petal sub-builders (LOCAL coords only, no rotation) ========= // Walls: inner cylindrical arc at r=4.8 mm (always), outer arc wall, and two side plates. -static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_cm = kOuterWallRadius_cm) +static void addPetalWalls(TGeoVolume* petalAsm, + int nPetals, + double outerRadius_cm = kOuterWallRadius_cm, + bool withSideWalls = true, + bool fullCylindricalRadialWalls = false) { if (!petalAsm) { LOGP(error, "addPetalWalls: petalAsm is null"); @@ -314,11 +327,21 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ return; } - const double halfPhi = 0.5f * (360.f / static_cast(nPetals)); - const double halfZ = 0.5f * kPetalZ_cm; + const double halfZ = 0.5 * kPetalZ_cm; - // ---- Inner cylindrical wall (always at r=4.8 mm) ---- - { + // In full-cylinder radial-wall mode we ignore nPetals for the radial walls. + const double halfPhi = fullCylindricalRadialWalls ? 180.0 : 0.5 * (360.0 / static_cast(nPetals)); + + // ---- Inner radial wall ---- + if (fullCylindricalRadialWalls) { + auto* s = new TGeoTube(static_cast(kInnerWallRadius_cm), + static_cast(kInnerWallRadius_cm + kWallThick_cm), + static_cast(halfZ)); + auto* v = new TGeoVolume("VD_InnerWallCyl", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } else { auto* s = new TGeoTubeSeg(static_cast(kInnerWallRadius_cm), static_cast(kInnerWallRadius_cm + kWallThick_cm), static_cast(halfZ), @@ -330,8 +353,16 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ petalAsm->AddNode(v, 1); } - // ---- Outer arc wall ---- - { + // ---- Outer radial wall ---- + if (fullCylindricalRadialWalls) { + auto* s = new TGeoTube(static_cast(outerRadius_cm), + static_cast(outerRadius_cm + kWallThick_cm), + static_cast(halfZ)); + auto* v = new TGeoVolume("VD_OuterWallCyl", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } else { auto* s = new TGeoTubeSeg(static_cast(outerRadius_cm), static_cast(outerRadius_cm + kWallThick_cm), static_cast(halfZ), @@ -343,6 +374,11 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ petalAsm->AddNode(v, 1); } + // ---- Side plates (skip in "single petal full cylinders" mode) ---- + if (!withSideWalls) { + return; + } + // ---- Side walls (boxes) at ±halfPhi ---- const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm)); auto* sideS = new TGeoBBox(static_cast(0.5f * radialLen), @@ -369,7 +405,7 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ // Build inner layers (L0..L2). L0 may be rectangular (IRIS5) or cylindrical. // φ-spans derive from spec gaps/arc; all local placement (no rotation). -static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0) +static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0, bool fullCylinders) { if (!petalAsm) { LOGP(error, "addBarrelLayers: petalAsm is null"); @@ -382,15 +418,15 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool constexpr double arcL0_cm = 0.6247f; // 6.247 mm // φ spans - const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); // L0 gap-defined - const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); // L1 gap-defined - const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined + const double phiL0_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); + const double phiL1_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); + const double phiL2_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); const std::string nameL0 = std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "0"; - if (rectangularL0) { + if (!fullCylinders && rectangularL0) { VDRectangularLayer L0(0, nameL0, kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm); @@ -438,7 +474,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool } // Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly. -static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) +static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId, bool fullCylinders = false) { if (!petalAsm) { LOGP(error, "addColdPlate: petalAsm is null"); @@ -455,8 +491,9 @@ static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) constexpr double gapL1L2_cm = 0.12f; // 1.2 mm // φ spans - const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined - const double halfPhiDeg = 0.5f * phiSpanColdplate_deg; + const double phiSpanColdplate_deg = + fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined in normal mode + const double halfPhiDeg = 0.5 * phiSpanColdplate_deg; const double startPhi = -halfPhiDeg; const double endPhi = +halfPhiDeg; @@ -625,7 +662,7 @@ static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) // Build disks in local coords: each disk gets only a local Z translation. // φ span from gap at rOut. -static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) +static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID, bool fullCylinders) { if (!petalAsm) { @@ -633,7 +670,7 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) return; } - const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); + const double phiDisk_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); for (int i = 0; i < 6; ++i) { const std::string nameD = @@ -651,21 +688,124 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) } } +// Add Z end-cap walls to "close" the petal/cylinder volume at zMin and zMax. +// Implemented as thin rings (TGeoTube) with thickness 'capThick_cm' in Z, +// spanning radii [rIn_cm, rOut_cm]. +static void addPetalEndCaps(TGeoVolume* petalAsm, + int petalId, + double rIn_cm, + double rOut_cm, + double zMin_cm, + double zMax_cm, + double capThick_cm) +{ + if (!petalAsm) { + LOGP(error, "addPetalEndCaps: petalAsm is null"); + return; + } + + auto& matmgr = o2::base::MaterialManager::Instance(); + const TGeoMedium* med = + matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083"); + + if (!med) { + LOGP(warning, + "addPetalEndCaps: ALICE3_TRKSERVICES_ALUMINIUM5083 not found, caps not created."); + return; + } + + const double halfT = 0.5 * capThick_cm; + + auto* sh = new TGeoTube(static_cast(rIn_cm), + static_cast(rOut_cm), + static_cast(halfT)); + + TString vname = Form("Petal%d_ZCap", petalId); + auto* v = new TGeoVolume(vname, sh, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + + auto* trMin = new TGeoTranslation(0.0, 0.0, + static_cast(zMin_cm + halfT)); + auto* trMax = new TGeoTranslation(0.0, 0.0, + static_cast(zMax_cm - halfT)); + + petalAsm->AddNode(v, 1, trMin); + petalAsm->AddNode(v, 2, trMax); +} + // Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords. -static TGeoVolume* buildPetalAssembly(int nPetals, int petalID, bool rectangularL0) +static TGeoVolume* buildPetalAssembly(int nPetals, + int petalID, + bool rectangularL0, + bool fullCylinders, + bool withSideWalls) { auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); - addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm); - // Pass petalID to layers/disks for naming - addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0); - addColdPlate(petalAsm, nPetals, petalID); - addDisks(petalAsm, nPetals, petalID); + // In the special mode: no side walls, but keep radial walls as FULL cylinders. + addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm, + /*withSideWalls=*/withSideWalls, + /*fullCylindricalRadialWalls=*/fullCylinders); + + addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0, fullCylinders); + addDisks(petalAsm, nPetals, petalID, fullCylinders); + + addColdPlate(petalAsm, nPetals, petalID, /*fullCylinders=*/false); addIRISServiceModulesSegmented(petalAsm, nPetals); return petalAsm; } +static TGeoVolume* buildFullCylAssembly(int petalID, bool withDisks) +{ + // IMPORTANT: keep naming consistent with createIRIS4/5 (PETAL_%d) + auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); + + // Radial walls only: full 360° cylinders, no side plates + addPetalWalls(petalAsm, + /*nPetals=*/1, + /*outerRadius_cm=*/kOuterWallRadius_cm, + /*withSideWalls=*/false, + /*fullCylindricalRadialWalls=*/true); + + // --- Z end-cap walls to close the petal in Z --- + { + const double zMin = -0.5 * kLenZ_cm; + const double zMax = +0.5 * kLenZ_cm; + const double rIn = kInnerWallRadius_cm; + const double rOut = kOuterWallRadius_cm + kWallThick_cm; + + addPetalEndCaps(petalAsm, + petalID, + rIn, + rOut, + zMin, + zMax, + kWallThick_cm); + } + + // Full 360° barrel cylinders + addBarrelLayers(petalAsm, + /*nPetals=*/1, + /*petalID=*/petalID, + /*rectangularL0=*/false, + /*fullCylinders=*/true); + + addColdPlate(petalAsm, 1, petalID, /*fullCylinders=*/true); + addIRISServiceModulesSegmented(petalAsm, /*nPetals=*/1); + + // Optionally add full 360° disks + if (withDisks) { + addDisks(petalAsm, + /*nPetals=*/1, + /*petalID=*/petalID, + /*fullCylinders=*/true); + } + + return petalAsm; +} + // =================== Public entry points =================== void createIRIS4Geometry(TGeoVolume* motherVolume) @@ -679,7 +819,9 @@ void createIRIS4Geometry(TGeoVolume* motherVolume) constexpr int nPetals = 4; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false, + /*fullCylinders=*/false, + /*withSideWalls=*/true); // Build the petal-local solids composite once from the FIRST petal if (p == 0) { buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords @@ -704,7 +846,9 @@ void createIRIS5Geometry(TGeoVolume* motherVolume) constexpr int nPetals = 4; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true); + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true, + /*fullCylinders=*/false, + /*withSideWalls=*/true); // Build the petal-local solids composite once from the FIRST petal if (p == 0) { buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords @@ -729,7 +873,9 @@ void createIRIS4aGeometry(TGeoVolume* motherVolume) constexpr int nPetals = 3; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false, + /*fullCylinders=*/false, + /*withSideWalls=*/true); // Build the petal-local solids composite once from the FIRST petal if (p == 0) { buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords @@ -743,9 +889,48 @@ void createIRIS4aGeometry(TGeoVolume* motherVolume) buildIrisCutoutFromPetalSolid(nPetals); } +void createIRISGeometryFullCyl(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRISGeometryFullCyl: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 1; + constexpr int petalID = 0; + + auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/false); + motherVolume->AddNode(petal, 1, nullptr); + + buildPetalSolidsComposite(petal); + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRISGeometryFullCylDisks: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 1; + constexpr int petalID = 0; + + auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/true); + motherVolume->AddNode(petal, 1, nullptr); + + // Same cutout pipeline as createIRIS4/5: + buildPetalSolidsComposite(petal); + buildIrisCutoutFromPetalSolid(nPetals); +} + void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID, int nPetals, bool rectangularL0) { - auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0); + auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0, false, true); // Optionally rotate the petal for display const double phiDeg = (360.f / static_cast(nPetals)) * (static_cast(petalID) + 0.5f); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx index 20f36f1f6f4e7..411dd485684b9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -11,6 +11,7 @@ #include "TRKSimulation/VDLayer.h" #include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" #include "Framework/Logger.h" @@ -32,6 +33,8 @@ VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0 { constexpr double kSiX0_cm = 9.5; // Radiation length of Silicon in cm mChipThickness = mX2X0 * kSiX0_cm; + + mSensorThickness = o2::trk::constants::VD::silicon::thickness; // cm } // VDCylindricalLayer constructor @@ -83,7 +86,7 @@ TGeoVolume* VDCylindricalLayer::createSensor() const } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); const double rIn = mRadius; - const double rOut = mRadius + mChipThickness; + const double rOut = mRadius + mSensorThickness; const double halfZ = 0.5 * mLengthSensZ; const double halfPhi = 0.5 * mPhiSpanDeg; // degrees auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); @@ -106,8 +109,8 @@ TGeoVolume* VDRectangularLayer::createSensor() const } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); const double hx = 0.5 * mWidth; - const double hy = 0.5 * mChipThickness; // thickness in Y - const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer + const double hy = 0.5 * mSensorThickness; + const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer auto* shape = new TGeoBBox(hx, hy, hz); auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); @@ -134,8 +137,8 @@ TGeoVolume* VDDiskLayer::createSensor() const return nullptr; } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); - const double halfThickness = 0.5 * mChipThickness; // disk thickness is along Z - const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + const double halfThickness = 0.5 * mSensorThickness; // active sensor thickness along Z + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees // Same geometry as the layer (identical radii + phi span + thickness) auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); @@ -147,6 +150,243 @@ TGeoVolume* VDDiskLayer::createSensor() const return sensVol; } +/* +** Create metal stack +*/ + +TGeoVolume* VDCylindricalLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; // nothing to add + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double rIn = mRadius + mSensorThickness; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDRectangularLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double hx = 0.5 * mWidth; + const double hy = 0.5 * metalT; + const double hz = 0.5 * mLengthSensZ; + + auto* shape = new TGeoBBox(hx, hy, hz); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDDiskLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; + } + + if (mRMin < 0 || mRMax <= mRMin || mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk metal dims: rMin={}, rMax={}, metalT={}, phiSpanDeg={}", + mRMin, mRMax, metalT, mPhiSpanDeg); + return nullptr; + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double halfThickness = 0.5 * metalT; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +/* +** Create chip +*/ + +TGeoVolume* VDCylindricalLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* chipShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + + // sensor + if (auto* sensVol = createSensor()) { + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + } + + // metal stack + if (auto* metalVol = createMetalStack()) { + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, nullptr); // concentric, no translation needed + } + + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + return chipVol; +} + +TGeoVolume* VDRectangularLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; + const double hz = 0.5 * mLengthSensZ; + + auto* chipShape = new TGeoBBox(hx, hy, hz); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + + // sensor (place it on the "bottom" side, like TRK) + if (auto* sensVol = createSensor()) { + auto* transSens = new TGeoTranslation(0.0, -(mChipThickness - mSensorThickness) / 2, 0.0); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + } + + // metal stack (remaining thickness on top) + if (auto* metalVol = createMetalStack()) { + auto* transMetal = new TGeoTranslation(0.0, +mSensorThickness / 2, 0.0); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, transMetal); + } + + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + return chipVol; +} + +TGeoVolume* VDDiskLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk chip dims: rMin={}, rMax={}, t={}, phi={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double halfThickness = 0.5 * mChipThickness; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* chipShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + + // Sensor slab (sensitive) placed on one side in Z (TRK-like stacking convention) + if (auto* sensVol = createSensor()) { + const double zSens = -(mChipThickness - mSensorThickness) / 2.0; + auto* tSens = new TGeoTranslation(0.0, 0.0, zSens); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, tSens); + } + + // Metal stack slab (non-sensitive), remaining thickness, also silicon + if (auto* metalVol = createMetalStack()) { + const double zMetal = +mSensorThickness / 2.0; + auto* tMetal = new TGeoTranslation(0.0, 0.0, zMetal); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, tMetal); + } + + return chipVol; +} + /* ** Create layer */ @@ -184,14 +424,14 @@ void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi layerVol->SetLineColor(kYellow); layerVol->SetTransparency(30); - // Sensor volume (must use mLengthSensZ internally) - TGeoVolume* sensorVol = VDCylindricalLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDCylindricalLayer::createSensor() returned null"); + // Chip volume (must use mLengthSensZ internally) + TGeoVolume* chipVol = VDCylindricalLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDCylindricalLayer::createChip() returned null"); return; } - LOGP(debug, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); - layerVol->AddNode(sensorVol, 1, nullptr); + LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); // Tiling: edge-to-edge if sensor shorter than layer; else single centered // const auto zCenters = (mLengthSensZ < mLengthZ) @@ -238,14 +478,14 @@ void VDRectangularLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi layerVol->SetTransparency(30); // Sensor volume (uses mLengthSensZ internally) - TGeoVolume* sensorVol = VDRectangularLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDRectangularLayer::createSensor() returned null"); + TGeoVolume* chipVol = VDRectangularLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDRectangularLayer::chipVol() returned null"); return; } - LOGP(debug, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); - layerVol->AddNode(sensorVol, 1, nullptr); + LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); // Tiling along Z, edge - to - edge if needed // const auto zCenters = (mLengthSensZ < mLengthZ) @@ -292,14 +532,14 @@ void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) layerVol->SetTransparency(30); // Sensor (same size & shape as the layer for disks) - TGeoVolume* sensorVol = VDDiskLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDDiskLayer::createSensor() returned null"); + TGeoVolume* chipVol = VDDiskLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDDiskLayer::createChip() returned null"); return; } // Insert single sensor (no Z-segmentation for disks) - layerVol->AddNode(sensorVol, 1, nullptr); + layerVol->AddNode(chipVol, 1, nullptr); TGeoTranslation tz(0.0, 0.0, mZPos); motherVolume->AddNode(layerVol, 1, combiTrans ? combiTrans : &tz); From 0c30aa6866a54508293baacd0f6c416a50577638 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Wed, 21 Jan 2026 12:11:15 +0100 Subject: [PATCH 3/4] add v3 building option for FT3 --- .../include/FT3Simulation/Detector.h | 1 + .../ALICE3/FT3/simulation/src/Detector.cxx | 61 ++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h index a88ea5a351ad2..a68f8cf7788b6 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h @@ -116,6 +116,7 @@ class Detector : public o2::base::DetImpl void buildFT3V3b(); void buildFT3Scoping(); void buildFT3NewVacuumVessel(); + void buildFT3ScopingV3(); void buildFT3FromFile(std::string); GeometryTGeo* mGeometryTGeo; //! access to geometry details diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index aab8ae070d936..9303979ada930 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -346,6 +346,65 @@ void Detector::buildFT3NewVacuumVessel() } } +void Detector::buildFT3ScopingV3() +{ + // Build the FT3 detector according to v3 layout + // https://indico.cern.ch/event/1596309/contributions/6728167/attachments/3190117/5677220/2025-12-10-AW-ALICE3planning.pdf + // Middle disks inner radius 10 cm + // Outer disks inner radius 20 cm + + LOG(info) << "Building FT3 Detector: v3 scoping version"; + + mNumberOfLayers = 6; + float sensorThickness = 30.e-4; + float layersx2X0 = 1.e-2; + std::vector> layersConfigCSide{ + {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} + {100., 10.0, 35., layersx2X0}, + {122., 10.0, 35., layersx2X0}, + {150., 20.0, 68.f, layersx2X0}, + {180., 20.0, 68.f, layersx2X0}, + {220., 20.0, 68.f, layersx2X0}}; + + std::vector> layersConfigASide{ + {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} + {100., 10.0, 35., layersx2X0}, + {122., 10.0, 35., layersx2X0}, + {150., 20.0, 68.f, layersx2X0}, + {180., 20.0, 68.f, layersx2X0}, + {220., 20.0, 68.f, layersx2X0}}; + + mLayerName.resize(2); + mLayerName[0].resize(mNumberOfLayers); + mLayerName[1].resize(mNumberOfLayers); + mLayerID.clear(); + mLayers.resize(2); + + for (auto direction : {0, 1}) { + for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + std::string directionName = std::to_string(direction); + std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); + mLayerName[direction][layerNumber] = layerName; + float z, rIn, rOut, x0; + if (direction == 0) { // C-Side + z = layersConfigCSide[layerNumber][0]; + rIn = layersConfigCSide[layerNumber][1]; + rOut = layersConfigCSide[layerNumber][2]; + x0 = layersConfigCSide[layerNumber][3]; + } else if (direction == 1) { // A-Side + z = layersConfigASide[layerNumber][0]; + rIn = layersConfigASide[layerNumber][1]; + rOut = layersConfigASide[layerNumber][2]; + x0 = layersConfigASide[layerNumber][3]; + } + + LOG(info) << "Adding Layer " << layerName << " at z = " << z; + // Add layers + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + } + } +} + //_________________________________________________________________________________________________ void Detector::buildFT3Scoping() { @@ -411,7 +470,7 @@ Detector::Detector(bool active) } else { switch (ft3BaseParam.geoModel) { case Default: - buildFT3NewVacuumVessel(); // FT3 after Upgrade days March 2024 + buildFT3ScopingV3(); // v3 Dec 25 break; case Telescope: buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers) From b8f88da9cfe5dc193105d7e0beeb25968be5ee58 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Wed, 21 Jan 2026 12:12:35 +0100 Subject: [PATCH 4/4] build pure cyl IRIS v3 by default --- Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 0924be5fb6764..e0fc6ef1ed35b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -266,7 +266,7 @@ void Detector::createGeometry() // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); o2::trk::clearVDSensorRegistry(); - o2::trk::createIRIS4Geometry(vTRK); + o2::trk::createIRISGeometryFullCyl(vTRK); // Fill sensor names from registry right after geometry creation const auto& regs = o2::trk::vdSensorRegistry();