From 440983a5738cf0cea1989be8114a042160fa60c0 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Thu, 24 Sep 2026 17:17:31 +0200 Subject: [PATCH 01/12] Make medium creation in FT3 defined and executed more centrally --- .../include/FT3Simulation/FT3Layer.h | 14 -- .../include/FT3Simulation/FT3Module.h | 20 +-- .../FT3Simulation/FT3ModuleConstants.h | 108 +++++++++++++-- .../TRKFT3/FT3/simulation/src/Detector.cxx | 45 ++++--- .../TRKFT3/FT3/simulation/src/FT3Layer.cxx | 89 +++---------- .../TRKFT3/FT3/simulation/src/FT3Module.cxx | 124 ++++-------------- 6 files changed, 173 insertions(+), 227 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Layer.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Layer.h index 512326949128b..404bde7895b07 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Layer.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Layer.h @@ -59,25 +59,11 @@ class FT3Layer : public TObject /// \param motherVolume the TGeoVolume owing the volume structure virtual void createLayer(TGeoVolume* motherVolume); - static void initialize_mat(); - // create layer for disk support void createSeparationLayer(TGeoVolume* motherVolume, const std::string& separationLayerName); void createSeparationLayer_waterCooling(TGeoVolume* motherVolume, const std::string& separationLayerName); void createReferenceCircles(TGeoVolume* motherVolume, const std::string& name); - static TGeoMaterial* carbonFiberMat; - static TGeoMedium* medCarbonFiber; - - static TGeoMixture* kaptonMat; - static TGeoMedium* kaptonMed; - - static TGeoMaterial* waterMat; - static TGeoMedium* waterMed; - - static TGeoMaterial* foamMat; - static TGeoMedium* medFoam; - private: Int_t mLayerNumber = -1; ///< Current layer number Int_t mDirection; ///< Layer direction 0=Forward 1 = Backward diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h index 71449b008cd80..ba391f24a9fd0 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h @@ -15,6 +15,7 @@ #ifndef FT3MODULE_H #define FT3MODULE_H +#include #include #include #include @@ -33,19 +34,12 @@ class FT3Module { public: - static void initialize_materials(); - static TGeoMaterial* siliconMat; - static TGeoMedium* siliconMed; - static TGeoMaterial* copperMat; - static TGeoMedium* copperMed; - static TGeoMixture* kaptonMat; - static TGeoMedium* kaptonMed; - static TGeoMaterial* epoxyMat; - static TGeoMedium* epoxyMed; - static TGeoMaterial* AluminumMat; - static TGeoMedium* AluminumMed; - static TGeoMaterial* carbonFiberMat; - static TGeoMedium* carbonFiberMed; + /* + * The media themselves are created by Detector::createMaterials() from the + * table in FT3ModuleConstants.h, before the geometry is built. This only + * looks them up again in the MaterialManager by their MaterialID. + */ + static TGeoMedium* getMedium(Constants::MaterialID id); const char* mDetName; diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index dd3aa412ad525..52068ca766883 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -15,6 +15,8 @@ #ifndef FT3MODULECONSTANTS_H #define FT3MODULECONSTANTS_H +#include +#include #include #include #include @@ -29,17 +31,19 @@ namespace o2::ft3::ModuleConstants * When stacking 2x1 modules, there is a 0.2mm gap between them. By default, we assume this * gap to be ABOVE the most recently placed module. * - * |<- 25mm ->|<- 25mm ->| + * |<- 25mm ->||<- 25mm ->| * _______________________ - * ----------------------- 0.2mm gap above - * | | | | | - * | | | | | - * | | | | | - * | | | | | 29mm sensor height - * | | | | | - * | | | | | + * ------------------------ 0.2mm gap above + * | | || | | + * | | || | | + * | | || | | + * | | || | | 29mm sensor height + * | | || | | + * | | || | | * ------------------------ - * + * ^ + * | + * 0.2mm gap in the middle */ // First set all layout constants for the rest of the function const double single_sensor_width = 2.5; @@ -98,12 +102,88 @@ inline const double z_offsetStave(double x_midpoint_spacing) (2 - x_midpoint_spacing / (sensor2x1_width / 2 + staveSensorGap)); } -const int SiColor = kGreen; +/* + * Materials of the FT3 module. + * + * Everything the simulation needs to know about a material lives in the + * materials map below, keyed by its FT3-local ID: composition, density, + * transport parameters and the colour its volumes are drawn in. + * Detector::createMaterials() registers the whole map with the MaterialManager, + * and FT3Module/FT3Layer look the media up again by MaterialID, so no ID, + * density or colour is ever written out by hand. + */ +enum class MaterialID : unsigned { + Air = 1, + Silicon, + Copper, + Kapton, + CarbonFiber, + Epoxy, + Aluminum, + Foam, + Water +}; + +// Transport parameters of a medium, in the order expected by Detector::Medium() +struct TrackingParams { + float tmaxfd; // maximum field-induced angular deviation per step, degrees + float stemax; // maximum step length, cm + float deemax; // maximum fractional energy loss per step + float epsil; // tracking precision, cm + float stmin; // minimum step length, cm +}; + +constexpr TrackingParams sensitiveTracking = {0.1f, 0.0075f, 0.1f, 1.0e-4f, 0.0f}; +constexpr TrackingParams passiveTracking = {0.1f, 1.0f, 0.1f, 1.0e-4f, 0.0f}; + +// Maximum number of elements any of the mixtures below is built from +constexpr unsigned maxMaterialComponents = 4; +using ComponentArray = std::array; + +struct MaterialProperties { + const char* name; + int colour; // ROOT colour every volume made of this material is drawn in + float density; // g/cm3 + // Radiation and nuclear interaction length, cm. Only single elements carry + // them: Mixture() derives both from the composition and takes no such + // arguments. A non-positive value lets the transport engine compute it. + float radl; + float absl; + int nComponents; // 0: single element; > 0: mixture by weight; < 0: mixture by atom count + ComponentArray a; // mass numbers; only a[0] is used for a single element + ComponentArray z; // atomic numbers; only z[0] is used for a single element + ComponentArray w; // weight fractions or atom counts; unused for a single element + TrackingParams tracking; +}; + +/* + * Silicon, copper and carbon fibre are shared with TRK and are kept numerically + * identical to its SILICON$, COPPER$ and CARBONFIBER$ (TRK Detector::createMaterials()), + * down to the radiation lengths. Kapton, epoxy and aluminium have no TRK + * counterpart: TRK models the flex as the effective FPC$ mixture instead. + */ +inline const std::unordered_map materials = { + // Air volumes get their colour set individually where they are built + {MaterialID::Air, {"AIR$", kWhite, 1.20479e-3f, 0.0f, 0.0f, 4, {12.0107f, 14.0067f, 15.9994f, 39.948f}, {6.0f, 7.0f, 8.0f, 18.0f}, {0.000124f, 0.755267f, 0.231781f, 0.012827f}, passiveTracking}}, + {MaterialID::Silicon, {"SILICON$", kGreen, 2.33f, 9.36f, 999.0f, 0, {28.086f}, {14.0f}, {}, sensitiveTracking}}, + // Copper planes of the end-of-stave cards: X0 = 1.436 cm + {MaterialID::Copper, {"COPPER$", kOrange, 8.96f, 1.436f, 999.0f, 0, {63.546f}, {29.0f}, {}, passiveTracking}}, + // Kapton: C22 H10 N2 O5, by weight fraction. Also the cooling pipe material. + {MaterialID::Kapton, {"KAPTON$", kYellow, 1.346f, 0.0f, 0.0f, 4, {12.0107f, 1.00794f, 14.0067f, 15.999f}, {6.0f, 1.0f, 7.0f, 8.0f}, {0.5641f, 0.2564f, 0.0513f, 0.1282f}, passiveTracking}}, + // Carbon fibre: density tuned so X0 ~ 27 cm, as in TRK + // TODO: Check with Rene the exact type of carbon fiber + {MaterialID::CarbonFiber, {"CARBONFIBER$", kGray + 1, 1.45f, 27.0f, 999.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}}, + // Epoxy: C18 H19 O3, by atom count (negative nComponents) + {MaterialID::Epoxy, {"EPOXY$", kBlue, 2.186f, 0.0f, 0.0f, -3, {12.0107f, 1.00794f, 15.999f}, {6.0f, 1.0f, 8.0f}, {18.0f, 19.0f, 3.0f}, passiveTracking}}, + // No TRK counterpart; X0 and lambda are left to the transport engine + {MaterialID::Aluminum, {"ALUMINUM$", kBlack, 2.7f, 0.0f, 0.0f, 0, {26.98f}, {13.0f}, {}, passiveTracking}}, + // Carbon foam core of the disk separation layer + {MaterialID::Foam, {"FOAM$", kBlack, 0.17f, 0.0f, 0.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}}, + // Coolant inside the kapton pipes + {MaterialID::Water, {"WATER$", kBlue, 1.064f, 0.0f, 0.0f, 0, {18.01528f}, {8.0f}, {}, passiveTracking}}}; +// The inactive rim of a sensor is made of silicon as well, but is drawn +// separately so that it can be told apart from the active area. const int SiInactiveColor = kRed; -const int glueColor = kBlue; -const int CuColor = kOrange; -const int kaptonColor = kYellow; -const int carbonFiberColor = kGray + 1; // Struct for stave position configuration (varies between ML/OT) struct StaveConfig { diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx index 7fe43975934f4..124f952b5557a 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx @@ -20,6 +20,7 @@ #include "FT3Base/FT3BaseParam.h" #include "FT3Base/GeometryTGeo.h" #include "FT3Simulation/FT3Layer.h" +#include "FT3Simulation/FT3ModuleConstants.h" // FairRoot includes #include "FairDetector.h" // for FairDetector @@ -39,6 +40,7 @@ #include // for LOG, LOG_IF +#include #include // for NULL, snprintf #define MAX_SENSORS 2000 @@ -495,29 +497,26 @@ void Detector::createMaterials() float fieldm = 10.0; o2::base::Detector::initFieldTrackingParams(ifield, fieldm); - float tmaxfdSi = 0.1; // .10000E+01; // Degree - float stemaxSi = 0.0075; // .10000E+01; // cm - float deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0(materialID); + if (material.nComponents == 0) { + o2::base::Detector::Material(id, material.name, material.a[0], material.z[0], material.density, + material.radl, material.absl); + } else { + // Mixture() takes non-const pointers, so hand it copies of the table rows + ModuleConstants::ComponentArray a = material.a; + ModuleConstants::ComponentArray z = material.z; + ModuleConstants::ComponentArray w = material.w; + o2::base::Detector::Mixture(id, material.name, a.data(), z.data(), material.density, material.nComponents, w.data()); + } + const auto& tracking = material.tracking; + o2::base::Detector::Medium(id, material.name, id, 0, ifield, fieldm, tracking.tmaxfd, tracking.stemax, + tracking.deemax, tracking.epsil, tracking.stmin); + } } //_________________________________________________________________________________________________ diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx index b85e89f87f099..a1bcfdf6a9c9b 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx @@ -37,18 +37,6 @@ ClassImp(FT3Layer); FT3Layer::~FT3Layer() = default; -TGeoMaterial* FT3Layer::carbonFiberMat = nullptr; -TGeoMedium* FT3Layer::medCarbonFiber = nullptr; - -TGeoMixture* FT3Layer::kaptonMat = nullptr; -TGeoMedium* FT3Layer::kaptonMed = nullptr; - -TGeoMaterial* FT3Layer::waterMat = nullptr; -TGeoMedium* FT3Layer::waterMed = nullptr; - -TGeoMaterial* FT3Layer::foamMat = nullptr; -TGeoMedium* FT3Layer::medFoam = nullptr; - FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0, bool partOfMiddleLayers) { // Creates a simple parametrized EndCap layer covering the given @@ -61,7 +49,7 @@ FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerNam mx2X0 = Layerx2X0; mInnerRadius = rIn; mOuterRadius = rOut; - const double Si_X0 = 9.5; + const double Si_X0 = Constants::materials.at(Constants::MaterialID::Silicon).radl; mChipThickness = Layerx2X0 * Si_X0; mSensorThickness = 0.005; // assume 50 microns of active thickness (for sensor volumes for trapezoidal disks) @@ -78,37 +66,6 @@ FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerNam LOG(info) << " Layer z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness; } -void FT3Layer::initialize_mat() -{ - - if (carbonFiberMat) { - return; - } - - carbonFiberMat = new TGeoMaterial("CarbonFiber", 12.0, 6.0, 1.6); - medCarbonFiber = new TGeoMedium("CarbonFiber", 1, carbonFiberMat); - - auto* itsC = new TGeoElement("FT3_C", "Carbon", 6, 12.0107); - - auto* itsFoam = new TGeoMixture("FT3_Foam", 1); - itsFoam->AddElement(itsC, 1); - itsFoam->SetDensity(0.17); - - medFoam = new TGeoMedium("FT3_Foam", 1, itsFoam); - foamMat = medFoam->GetMaterial(); - - kaptonMat = new TGeoMixture("Kapton (cooling pipe)", 4, 1.346); // C22 H10 N2 O5 - - kaptonMat->DefineElement(0, 12.0107, 6, 0.5641); // Carbon - kaptonMat->DefineElement(1, 1.00794, 1, 0.2564); // Hydrogen - kaptonMat->DefineElement(2, 14.0067, 7, 0.0513); // Nitrogen - kaptonMat->DefineElement(3, 15.999, 8, 0.1282); // Oxygen - kaptonMed = new TGeoMedium("Kapton (cooling pipe)", 1, kaptonMat); - - waterMat = new TGeoMaterial("Water", 18.01528, 8.0, 1.064); - waterMed = new TGeoMedium("Water", 2, waterMat); -} - static double y_circle(double x, double radius) { return (x * x < radius * radius) ? std::sqrt(radius * radius - x * x) : 0; @@ -117,16 +74,14 @@ static double y_circle(double x, double radius) void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, const std::string& separationLayerName) { - FT3Layer::initialize_mat(); - const double carbonFiberThickness = 0.01; // cm const double foamSpacingThickness = 0.5; // cm TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); // volumes - TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, medCarbonFiber); - TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, medCarbonFiber); + TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); + TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); carbonFiberLayerVol1->SetLineColor(kGray + 2); carbonFiberLayerVol2->SetLineColor(kGray + 2); @@ -167,16 +122,16 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons double positiveYLength = yOuter - yInner; - TGeoVolume* kaptonPipePos = new TGeoVolume((separationLayerName + "_KaptonPipePos_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), kaptonMed); + TGeoVolume* kaptonPipePos = new TGeoVolume((separationLayerName + "_KaptonPipePos_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Kapton)); kaptonPipePos->SetLineColor(kGray); - TGeoVolume* waterVolumePos = new TGeoVolume((separationLayerName + "_WaterVolumePos_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), waterMed); + TGeoVolume* waterVolumePos = new TGeoVolume((separationLayerName + "_WaterVolumePos_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Water)); waterVolumePos->SetLineColor(kBlue); motherVolume->AddNode(waterVolumePos, 1, new TGeoCombiTrans(xPos, (yInner + yOuter) / 2.0, mZ, rotation)); - TGeoVolume* kaptonPipeNeg = new TGeoVolume((separationLayerName + "_KaptonPipeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), kaptonMed); + TGeoVolume* kaptonPipeNeg = new TGeoVolume((separationLayerName + "_KaptonPipeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Kapton)); kaptonPipeNeg->SetLineColor(kGray); - TGeoVolume* waterVolumeNeg = new TGeoVolume((separationLayerName + "_WaterVolumeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), waterMed); + TGeoVolume* waterVolumeNeg = new TGeoVolume((separationLayerName + "_WaterVolumeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Water)); waterVolumeNeg->SetLineColor(kBlue); motherVolume->AddNode(waterVolumeNeg, 1, new TGeoCombiTrans(xPos, -(yInner + yOuter) / 2.0, mZ, rotation)); @@ -190,9 +145,9 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons yMax = 2 * yOuter; pipeLength = yMax; - TGeoVolume* kaptonPipe = new TGeoVolume((separationLayerName + "_KaptonPipe_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, pipeLength / 2), kaptonMed); + TGeoVolume* kaptonPipe = new TGeoVolume((separationLayerName + "_KaptonPipe_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, pipeLength / 2), FT3Module::getMedium(Constants::MaterialID::Kapton)); kaptonPipe->SetLineColor(kGray); - TGeoVolume* waterVolume = new TGeoVolume((separationLayerName + "_WaterVolume_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, pipeLength / 2), waterMed); + TGeoVolume* waterVolume = new TGeoVolume((separationLayerName + "_WaterVolume_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, pipeLength / 2), FT3Module::getMedium(Constants::MaterialID::Water)); waterVolume->SetLineColor(kBlue); motherVolume->AddNode(waterVolume, 1, new TGeoCombiTrans(xPos, 0, mZ, rotation)); @@ -206,8 +161,6 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string& separationLayerName) { - FT3Layer::initialize_mat(); - constexpr double carbonFiberThickness = 0.01; // cm constexpr double foamSpacingThickness = 1.0; // cm @@ -215,9 +168,9 @@ void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string TGeoTube* foamLayer = new TGeoTube(mInnerRadius, mOuterRadius, foamSpacingThickness / 2); // volumes - TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, medCarbonFiber); - TGeoVolume* foamLayerVol = new TGeoVolume((separationLayerName + "_Foam").c_str(), foamLayer, medFoam); - TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, medCarbonFiber); + TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); + TGeoVolume* foamLayerVol = new TGeoVolume((separationLayerName + "_Foam").c_str(), foamLayer, FT3Module::getMedium(Constants::MaterialID::Foam)); + TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); carbonFiberLayerVol1->SetLineColor(kGray + 2); foamLayerVol->SetLineColor(kBlack); @@ -239,9 +192,9 @@ void FT3Layer::createReferenceCircles(TGeoVolume* motherVolume, const std::strin TGeoTube* outerCircle = new TGeoTube(mOuterRadius - 0.1, mOuterRadius + 0.1, 0.01); TGeoTube* outerCircleEdge = new TGeoTube(mOuterRadius + 3.3, mOuterRadius + 3.5, 0.01); - TGeoVolume* innerCircleVol = new TGeoVolume((mLayerName + "_InnerCircle").c_str(), innerCircle, gGeoManager->GetMedium("FT3_AIR$")); - TGeoVolume* outerCircleVol = new TGeoVolume((mLayerName + "_OuterCircle").c_str(), outerCircle, gGeoManager->GetMedium("FT3_AIR$")); - TGeoVolume* outerCircleEdgeVol = new TGeoVolume((mLayerName + "_OuterCircleEdge").c_str(), outerCircleEdge, gGeoManager->GetMedium("FT3_AIR$")); + TGeoVolume* innerCircleVol = new TGeoVolume((mLayerName + "_InnerCircle").c_str(), innerCircle, FT3Module::getMedium(Constants::MaterialID::Air)); + TGeoVolume* outerCircleVol = new TGeoVolume((mLayerName + "_OuterCircle").c_str(), outerCircle, FT3Module::getMedium(Constants::MaterialID::Air)); + TGeoVolume* outerCircleEdgeVol = new TGeoVolume((mLayerName + "_OuterCircleEdge").c_str(), outerCircleEdge, FT3Module::getMedium(Constants::MaterialID::Air)); innerCircleVol->SetLineColor(kRed); outerCircleVol->SetLineColor(kBlue); @@ -278,8 +231,8 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber); std::string passiveName = o2::ft3::GeometryTGeo::getFT3PassivePattern() + std::to_string(mLayerNumber); - TGeoMedium* medSi = gGeoManager->GetMedium("FT3_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); + TGeoMedium* medSi = FT3Module::getMedium(Constants::MaterialID::Silicon); + TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); @@ -389,8 +342,8 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2); TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2); - TGeoMedium* medSi = gGeoManager->GetMedium("FT3_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); + TGeoMedium* medSi = FT3Module::getMedium(Constants::MaterialID::Silicon); + TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); sensVol->SetLineColor(kYellow); @@ -420,7 +373,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back"; std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); - TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); + TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); TGeoVolume* layerVol = nullptr; // Add a little additional room in radius TGeoTube* layer = new TGeoTube(mInnerRadius - 0.1, mOuterRadius + 0.1, 1.5); @@ -447,7 +400,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back"; std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); - TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); + TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); TGeoVolume* layerVol = nullptr; // set up stave config, differs between ML and OT disks diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx index 00f53c1f693ad..c9e000b13d5bf 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx @@ -15,9 +15,7 @@ #include "FT3Simulation/FT3Module.h" #include "FT3Base/FT3BaseParam.h" #include "DetectorsBase/MaterialManager.h" -#include "DetectorsBase/Detector.h" #include -#include #include #include #include @@ -31,92 +29,20 @@ #include #include -TGeoMaterial* FT3Module::siliconMat = nullptr; -TGeoMedium* FT3Module::siliconMed = nullptr; - -TGeoMaterial* FT3Module::copperMat = nullptr; -TGeoMedium* FT3Module::copperMed = nullptr; - -TGeoMixture* FT3Module::kaptonMat = nullptr; -TGeoMedium* FT3Module::kaptonMed = nullptr; - -TGeoMaterial* FT3Module::epoxyMat = nullptr; -TGeoMedium* FT3Module::epoxyMed = nullptr; - -TGeoMaterial* FT3Module::AluminumMat = nullptr; -TGeoMedium* FT3Module::AluminumMed = nullptr; - -TGeoMaterial* FT3Module::carbonFiberMat = nullptr; -TGeoMedium* FT3Module::carbonFiberMed = nullptr; - -void FT3Module::initialize_materials() +/* + * The FT3 module media are registered with the MaterialManager by + * Detector::createMaterials(), which walks the material table in + * FT3ModuleConstants.h. Here they are only looked up again by their + * MaterialID, so that the IDs never have to be repeated by hand. + */ +TGeoMedium* FT3Module::getMedium(Constants::MaterialID id) { - LOG(debug) << "FT3Module: initialize_materials"; - if (siliconMat) { - return; + auto* medium = o2::base::MaterialManager::Instance().getTGeoMedium("FT3", static_cast(id)); + if (!medium) { + LOG(fatal) << "FT3Module: no medium registered for " << Constants::materials.at(id).name + << "; Detector::createMaterials() has to run before the geometry is built"; } - - auto& matmgr = o2::base::MaterialManager::Instance(); - - int ifield; // Initialized below - float fieldm; // Initialized below - o2::base::Detector::initFieldTrackingParams(ifield, fieldm); - - float tmaxfdSi = 0.1; - float stemaxSi = 0.0075; - float deemaxSi = 0.1; - float epsilSi = 1.0E-4; - float stminSi = 0.0; - - float tmaxfdPas = 0.1; - float stemaxPas = 1.0; - float deemaxPas = 0.1; - float epsilPas = 1.0E-4; - float stminPas = 0.0; - - // FT3-local material/medium IDs: 10-15. Keep them distinct from IDs 1 and 3, - // which are already used by FT3 Detector::createMaterials() for AIR and SILICON. - // MaterialManager maps these local IDs to globally unique VMC medium IDs. - matmgr.Material("FT3", 10, "Silicon", 28.0855, 14, 2.33, 0, 0); - matmgr.Medium("FT3", 10, "Silicon", 10, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); - siliconMed = matmgr.getTGeoMedium("FT3", 10); - siliconMat = siliconMed->GetMaterial(); - - matmgr.Material("FT3", 11, "Copper", 63.546, 29, 8.96, 0, 0); - matmgr.Medium("FT3", 11, "Copper", 11, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas); - copperMed = matmgr.getTGeoMedium("FT3", 11); - copperMat = copperMed->GetMaterial(); - - // Kapton: C22 H10 N2 O5, by weight fraction - float aKapton[4] = {12.0107, 1.00794, 14.0067, 15.999}; - float zKapton[4] = {6., 1., 7., 8.}; - float wKapton[4] = {0.5641, 0.2564, 0.0513, 0.1282}; - matmgr.Mixture("FT3", 12, "Kapton", aKapton, zKapton, 1.346, 4, wKapton); - matmgr.Medium("FT3", 12, "Kapton", 12, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas); - kaptonMed = matmgr.getTGeoMedium("FT3", 12); - kaptonMat = dynamic_cast(kaptonMed->GetMaterial()); - - // TODO: Check with Rene the exact type of carbon fiber - matmgr.Material("FT3", 13, "Carbon", 12.0107, 6, 1.8, 0, 0); - matmgr.Medium("FT3", 13, "Carbon", 13, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas); - carbonFiberMed = matmgr.getTGeoMedium("FT3", 13); - carbonFiberMat = carbonFiberMed->GetMaterial(); - - // Epoxy: C18 H19 O3, by atom count (negative nlmat) - float aEpoxy[3] = {12.0107, 1.00794, 15.999}; - float zEpoxy[3] = {6., 1., 8.}; - float wEpoxy[3] = {18., 19., 3.}; - matmgr.Mixture("FT3", 14, "Epoxy", aEpoxy, zEpoxy, 2.186, -3, wEpoxy); - matmgr.Medium("FT3", 14, "Epoxy", 14, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas); - epoxyMed = matmgr.getTGeoMedium("FT3", 14); - epoxyMat = epoxyMed->GetMaterial(); - - matmgr.Material("FT3", 15, "Aluminum", 26.98, 13, 2.7, 0, 0); - matmgr.Medium("FT3", 15, "Aluminum", 15, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas); - AluminumMed = matmgr.getTGeoMedium("FT3", 15); - AluminumMat = AluminumMed->GetMaterial(); - - LOG(debug) << "FT3Module: done initialize_materials"; + return medium; } double calculate_y_circle(double x, double radius) @@ -355,9 +281,10 @@ void FT3Module::addStaveVolume( TGeoVolume* staveVolume = new TGeoVolume( (volumeName).c_str(), staveShape, - carbonFiberMed); - staveVolume->SetLineColor(Constants::carbonFiberColor); - staveVolume->SetFillColorAlpha(Constants::carbonFiberColor, 0.4); + getMedium(Constants::MaterialID::CarbonFiber)); + const int carbonFiberColor = Constants::materials.at(Constants::MaterialID::CarbonFiber).colour; + staveVolume->SetLineColor(carbonFiberColor); + staveVolume->SetFillColorAlpha(carbonFiberColor, 0.4); TGeoRotation* rot = new TGeoRotation(); rot->RotateX(-90); // lift from xy plane into xz plane @@ -440,7 +367,8 @@ void FT3Module::add2x1GlueVolume( { std::string glue_name = "FT3glue_" + element_glued_to + "_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, glue_name, Constants::glueColor, epoxyMed, volume_count, + motherVolume, glue_name, Constants::materials.at(Constants::MaterialID::Epoxy).colour, + getMedium(Constants::MaterialID::Epoxy), volume_count, x_mid, y_mid, z_mid, Constants::sensor2x1_width / 2, Constants::sensor2x1_height / 2, Constants::epoxyThickness / 2); } @@ -455,7 +383,8 @@ void FT3Module::add2x1CopperVolume( { std::string copper_name = "FT3Copper_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, copper_name, Constants::CuColor, copperMed, volume_count, + motherVolume, copper_name, Constants::materials.at(Constants::MaterialID::Copper).colour, + getMedium(Constants::MaterialID::Copper), volume_count, x_mid, y_mid, z_mid, Constants::sensor2x1_width / 2, Constants::sensor2x1_height / 2, Constants::copperThickness / 2); } @@ -470,7 +399,8 @@ void FT3Module::add2x1KaptonVolume( { std::string kapton_name = "FT3Kapton_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, kapton_name, Constants::kaptonColor, kaptonMed, volume_count, + motherVolume, kapton_name, Constants::materials.at(Constants::MaterialID::Kapton).colour, + getMedium(Constants::MaterialID::Kapton), volume_count, x_mid, y_mid, z_mid, Constants::sensor2x1_width / 2, Constants::sensor2x1_height / 2, Constants::kaptonThickness / 2); } @@ -500,11 +430,12 @@ void FT3Module::addSingleSensorVolume( { TGeoVolume* sensor; TGeoManager* geoManager = gGeoManager; + TGeoMedium* siliconMed = getMedium(Constants::MaterialID::Silicon); // ACTIVE AREA // Sensor thickness is along the Y axis; this convention is used in digitisation by barrels and disks std::string sensor_name = "FT3Sensor_Active_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, sensor_name, Constants::SiColor, siliconMed, + motherVolume, sensor_name, Constants::materials.at(Constants::MaterialID::Silicon).colour, siliconMed, volume_count, active_x_mid, y_mid, z_mid, Constants::active_width / 2, Constants::siliconThickness / 2, Constants::single_sensor_height / 2, 90.); @@ -528,7 +459,6 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction LOG(debug) << "FT3Module: create_layout_staveGeo - Direction " << direction << ", Layer " << layerNumber; - FT3Module::initialize_materials(); auto& ft3Params = o2::ft3::FT3BaseParam::Instance(); // First let's define some constants used throughout @@ -803,7 +733,11 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double LOG(debug) << "FT3Module: create_layout - Layer " << layerNumber << ", Direction " << direction << ", Face " << face; TGeoManager* geoManager = gGeoManager; - FT3Module::initialize_materials(); + TGeoMedium* siliconMed = getMedium(Constants::MaterialID::Silicon); + TGeoMedium* copperMed = getMedium(Constants::MaterialID::Copper); + TGeoMedium* kaptonMed = getMedium(Constants::MaterialID::Kapton); + TGeoMedium* epoxyMed = getMedium(Constants::MaterialID::Epoxy); + TGeoMedium* AluminumMed = getMedium(Constants::MaterialID::Aluminum); // double sensor_width = 2.5; // double sensor_height = 9.6; From 40ae21c329796ade57bd4c0c40ae62d32472ecd0 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Thu, 24 Sep 2026 17:49:56 +0200 Subject: [PATCH 02/12] move materials into separate header with constants, and define the getMedium file there instead for contained structure --- .../TRKFT3/FT3/simulation/CMakeLists.txt | 1 + .../include/FT3Simulation/FT3Materials.h | 126 ++++++++++++++++++ .../include/FT3Simulation/FT3Module.h | 9 +- .../FT3Simulation/FT3ModuleConstants.h | 88 +----------- .../TRKFT3/FT3/simulation/src/Detector.cxx | 16 +-- .../TRKFT3/FT3/simulation/src/FT3Layer.cxx | 43 +++--- .../FT3/simulation/src/FT3Materials.cxx | 30 +++++ .../TRKFT3/FT3/simulation/src/FT3Module.cxx | 49 +++---- 8 files changed, 208 insertions(+), 154 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h create mode 100644 Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/CMakeLists.txt index 98adea7c6124a..bb5c04de2611b 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/CMakeLists.txt @@ -11,6 +11,7 @@ o2_add_library(FT3Simulation SOURCES + src/FT3Materials.cxx src/FT3Module.cxx src/FT3Layer.cxx src/Detector.cxx diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h new file mode 100644 index 0000000000000..1d323dd6dfd04 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h @@ -0,0 +1,126 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + + +/// \file FT3Materials.h +/// \brief Materials of the FT3 detector, and access to the media made from them + +#ifndef FT3MATERIALS_H +#define FT3MATERIALS_H + +#include +#include +#include + +class TGeoMedium; + +namespace o2::ft3 +{ +namespace Materials +{ +// The name FT3 registers itself under with the MaterialManager. getMedium() +// looks media up under this key, so it has to be the same name the Detector +// hands to DetImpl. +constexpr const char* moduleName = "FT3"; + +/* + * Materials of the FT3 detector. + * + * Everything the simulation needs to know about a material lives in the + * materials map below, keyed by its FT3-local ID: composition, density, + * transport parameters and the colour its volumes are drawn in. + * Detector::createMaterials() registers the whole map with the MaterialManager, + * and FT3Module/FT3Layer reach the media through getMedium() below, so no ID, + * density or colour is ever written out by hand. + */ +enum class MaterialID : unsigned { + Air = 1, + Silicon, + Copper, + Kapton, + CarbonFiber, + Epoxy, + Aluminum, + Foam, + Water +}; + +// Transport parameters of a medium, in the order expected by Detector::Medium() +struct TrackingParams { + float tmaxfd; // maximum field-induced angular deviation per step, degrees + float stemax; // maximum step length, cm + float deemax; // maximum fractional energy loss per step + float epsil; // tracking precision, cm + float stmin; // minimum step length, cm +}; + +constexpr TrackingParams sensitiveTracking = {0.1f, 0.0075f, 0.1f, 1.0e-4f, 0.0f}; +constexpr TrackingParams passiveTracking = {0.1f, 1.0f, 0.1f, 1.0e-4f, 0.0f}; + +// Maximum number of elements any of the mixtures below is built from +constexpr unsigned maxMaterialComponents = 4; +using ComponentArray = std::array; + +struct MaterialProperties { + const char* name; + int colour; // ROOT colour every volume made of this material is drawn in + float density; // g/cm3 + // Radiation and nuclear interaction length, cm. Only single elements carry + // them: Mixture() derives both from the composition and takes no such + // arguments. A non-positive value lets the transport engine compute it. + float radl; + float absl; + int nComponents; // 0: single element; > 0: mixture by weight; < 0: mixture by atom count + ComponentArray a; // mass numbers; only a[0] is used for a single element + ComponentArray z; // atomic numbers; only z[0] is used for a single element + ComponentArray w; // weight fractions or atom counts; unused for a single element + TrackingParams tracking; +}; + +/* + * Silicon, copper and carbon fibre are shared with TRK and are kept numerically + * identical to its SILICON$, COPPER$ and CARBONFIBER$ (TRK Detector::createMaterials()), + * down to the radiation lengths. Kapton, epoxy and aluminium have no TRK + * counterpart: TRK models the flex as the effective FPC$ mixture instead. + */ +inline const std::unordered_map materials = { + // Air volumes get their colour set individually where they are built + {MaterialID::Air, {"Air", kWhite, 1.20479e-3f, 0.0f, 0.0f, 4, {12.0107f, 14.0067f, 15.9994f, 39.948f}, {6.0f, 7.0f, 8.0f, 18.0f}, {0.000124f, 0.755267f, 0.231781f, 0.012827f}, passiveTracking}}, + {MaterialID::Silicon, {"Silicon", kGreen, 2.33f, 9.36f, 999.0f, 0, {28.086f}, {14.0f}, {}, sensitiveTracking}}, + // Copper planes of the end-of-stave cards: X0 = 1.436 cm + {MaterialID::Copper, {"Copper", kOrange, 8.96f, 1.436f, 999.0f, 0, {63.546f}, {29.0f}, {}, passiveTracking}}, + // Kapton: C22 H10 N2 O5, by weight fraction. Also the cooling pipe material. + {MaterialID::Kapton, {"Kapton", kYellow, 1.346f, 0.0f, 0.0f, 4, {12.0107f, 1.00794f, 14.0067f, 15.999f}, {6.0f, 1.0f, 7.0f, 8.0f}, {0.5641f, 0.2564f, 0.0513f, 0.1282f}, passiveTracking}}, + // Carbon fibre: density tuned so X0 ~ 27 cm, as in TRK + // TODO: Check with Rene the exact type of carbon fiber + {MaterialID::CarbonFiber, {"CarbonFiber", kGray + 1, 1.45f, 27.0f, 999.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}}, + // Epoxy: C18 H19 O3, by atom count (negative nComponents) + {MaterialID::Epoxy, {"Epoxy", kBlue, 2.186f, 0.0f, 0.0f, -3, {12.0107f, 1.00794f, 15.999f}, {6.0f, 1.0f, 8.0f}, {18.0f, 19.0f, 3.0f}, passiveTracking}}, + // No TRK counterpart; X0 and lambda are left to the transport engine + {MaterialID::Aluminum, {"Aluminum", kBlack, 2.7f, 0.0f, 0.0f, 0, {26.98f}, {13.0f}, {}, passiveTracking}}, + // Carbon foam core of the disk separation layer + {MaterialID::Foam, {"Foam", kBlack, 0.17f, 0.0f, 0.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}}, + // Coolant inside the kapton pipes + {MaterialID::Water, {"Water", kBlue, 1.064f, 0.0f, 0.0f, 0, {18.01528f}, {8.0f}, {}, passiveTracking}}}; +// The inactive rim of a sensor is made of silicon as well, but is drawn +// separately so that it can be told apart from the active area. +const int SiInactiveColor = kRed; +} // namespace Materials + +/// Retrieve one of the media registered by Detector::createMaterials(). +/// +/// A free function rather than a member of Detector: the media live in the +/// MaterialManager singleton keyed by Materials::moduleName, not in the +/// detector object, so the lookup needs no Detector instance. +TGeoMedium* getMedium(Materials::MaterialID id); +} // namespace o2::ft3 + +#endif // FT3MATERIALS_H diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h index ba391f24a9fd0..0072d575d6c4b 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h @@ -20,6 +20,7 @@ #include #include +#include "FT3Simulation/FT3Materials.h" #include "FT3Simulation/FT3ModuleConstants.h" // define types for y positions, second element is the stack height @@ -29,18 +30,12 @@ using PosNegPositionTypes = std::pair; // define type of the y position range: First pair is (min, max) for positive y using PositionRangeType = std::pair, std::pair>; namespace Constants = o2::ft3::ModuleConstants; +namespace Materials = o2::ft3::Materials; class FT3Module { public: - /* - * The media themselves are created by Detector::createMaterials() from the - * table in FT3ModuleConstants.h, before the geometry is built. This only - * looks them up again in the MaterialManager by their MaterialID. - */ - static TGeoMedium* getMedium(Constants::MaterialID id); - const char* mDetName; static void createModule( diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index 52068ca766883..8b1cded3bfec3 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -11,15 +11,14 @@ /// \file FT3ModuleConstants.h /// \brief Definition of various constants for tiling the modules of sensors +/// +/// The materials themselves live in FT3Materials.h. #ifndef FT3MODULECONSTANTS_H #define FT3MODULECONSTANTS_H -#include -#include #include #include -#include #include namespace o2::ft3::ModuleConstants @@ -102,89 +101,6 @@ inline const double z_offsetStave(double x_midpoint_spacing) (2 - x_midpoint_spacing / (sensor2x1_width / 2 + staveSensorGap)); } -/* - * Materials of the FT3 module. - * - * Everything the simulation needs to know about a material lives in the - * materials map below, keyed by its FT3-local ID: composition, density, - * transport parameters and the colour its volumes are drawn in. - * Detector::createMaterials() registers the whole map with the MaterialManager, - * and FT3Module/FT3Layer look the media up again by MaterialID, so no ID, - * density or colour is ever written out by hand. - */ -enum class MaterialID : unsigned { - Air = 1, - Silicon, - Copper, - Kapton, - CarbonFiber, - Epoxy, - Aluminum, - Foam, - Water -}; - -// Transport parameters of a medium, in the order expected by Detector::Medium() -struct TrackingParams { - float tmaxfd; // maximum field-induced angular deviation per step, degrees - float stemax; // maximum step length, cm - float deemax; // maximum fractional energy loss per step - float epsil; // tracking precision, cm - float stmin; // minimum step length, cm -}; - -constexpr TrackingParams sensitiveTracking = {0.1f, 0.0075f, 0.1f, 1.0e-4f, 0.0f}; -constexpr TrackingParams passiveTracking = {0.1f, 1.0f, 0.1f, 1.0e-4f, 0.0f}; - -// Maximum number of elements any of the mixtures below is built from -constexpr unsigned maxMaterialComponents = 4; -using ComponentArray = std::array; - -struct MaterialProperties { - const char* name; - int colour; // ROOT colour every volume made of this material is drawn in - float density; // g/cm3 - // Radiation and nuclear interaction length, cm. Only single elements carry - // them: Mixture() derives both from the composition and takes no such - // arguments. A non-positive value lets the transport engine compute it. - float radl; - float absl; - int nComponents; // 0: single element; > 0: mixture by weight; < 0: mixture by atom count - ComponentArray a; // mass numbers; only a[0] is used for a single element - ComponentArray z; // atomic numbers; only z[0] is used for a single element - ComponentArray w; // weight fractions or atom counts; unused for a single element - TrackingParams tracking; -}; - -/* - * Silicon, copper and carbon fibre are shared with TRK and are kept numerically - * identical to its SILICON$, COPPER$ and CARBONFIBER$ (TRK Detector::createMaterials()), - * down to the radiation lengths. Kapton, epoxy and aluminium have no TRK - * counterpart: TRK models the flex as the effective FPC$ mixture instead. - */ -inline const std::unordered_map materials = { - // Air volumes get their colour set individually where they are built - {MaterialID::Air, {"AIR$", kWhite, 1.20479e-3f, 0.0f, 0.0f, 4, {12.0107f, 14.0067f, 15.9994f, 39.948f}, {6.0f, 7.0f, 8.0f, 18.0f}, {0.000124f, 0.755267f, 0.231781f, 0.012827f}, passiveTracking}}, - {MaterialID::Silicon, {"SILICON$", kGreen, 2.33f, 9.36f, 999.0f, 0, {28.086f}, {14.0f}, {}, sensitiveTracking}}, - // Copper planes of the end-of-stave cards: X0 = 1.436 cm - {MaterialID::Copper, {"COPPER$", kOrange, 8.96f, 1.436f, 999.0f, 0, {63.546f}, {29.0f}, {}, passiveTracking}}, - // Kapton: C22 H10 N2 O5, by weight fraction. Also the cooling pipe material. - {MaterialID::Kapton, {"KAPTON$", kYellow, 1.346f, 0.0f, 0.0f, 4, {12.0107f, 1.00794f, 14.0067f, 15.999f}, {6.0f, 1.0f, 7.0f, 8.0f}, {0.5641f, 0.2564f, 0.0513f, 0.1282f}, passiveTracking}}, - // Carbon fibre: density tuned so X0 ~ 27 cm, as in TRK - // TODO: Check with Rene the exact type of carbon fiber - {MaterialID::CarbonFiber, {"CARBONFIBER$", kGray + 1, 1.45f, 27.0f, 999.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}}, - // Epoxy: C18 H19 O3, by atom count (negative nComponents) - {MaterialID::Epoxy, {"EPOXY$", kBlue, 2.186f, 0.0f, 0.0f, -3, {12.0107f, 1.00794f, 15.999f}, {6.0f, 1.0f, 8.0f}, {18.0f, 19.0f, 3.0f}, passiveTracking}}, - // No TRK counterpart; X0 and lambda are left to the transport engine - {MaterialID::Aluminum, {"ALUMINUM$", kBlack, 2.7f, 0.0f, 0.0f, 0, {26.98f}, {13.0f}, {}, passiveTracking}}, - // Carbon foam core of the disk separation layer - {MaterialID::Foam, {"FOAM$", kBlack, 0.17f, 0.0f, 0.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}}, - // Coolant inside the kapton pipes - {MaterialID::Water, {"WATER$", kBlue, 1.064f, 0.0f, 0.0f, 0, {18.01528f}, {8.0f}, {}, passiveTracking}}}; -// The inactive rim of a sensor is made of silicon as well, but is drawn -// separately so that it can be told apart from the active area. -const int SiInactiveColor = kRed; - // Struct for stave position configuration (varies between ML/OT) struct StaveConfig { const unsigned isML; // whether this config is for ML or OT diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx index 124f952b5557a..6b07ae3ecd842 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/Detector.cxx @@ -20,7 +20,7 @@ #include "FT3Base/FT3BaseParam.h" #include "FT3Base/GeometryTGeo.h" #include "FT3Simulation/FT3Layer.h" -#include "FT3Simulation/FT3ModuleConstants.h" +#include "FT3Simulation/FT3Materials.h" // FairRoot includes #include "FairDetector.h" // for FairDetector @@ -56,7 +56,7 @@ using o2::trkft3::Hit; //_________________________________________________________________________________________________ Detector::Detector() - : o2::base::DetImpl("FT3", kTRUE), + : o2::base::DetImpl(Materials::moduleName, kTRUE), mTrackData(), mHits(o2::utils::createSimVector()) { @@ -348,7 +348,7 @@ void Detector::buildFT3Scoping() //_________________________________________________________________________________________________ Detector::Detector(bool active) - : o2::base::DetImpl("FT3", active), + : o2::base::DetImpl(Materials::moduleName, active), mTrackData(), mHits(o2::utils::createSimVector()) { @@ -497,20 +497,20 @@ void Detector::createMaterials() float fieldm = 10.0; o2::base::Detector::initFieldTrackingParams(ifield, fieldm); - // Every FT3 material is described by the map in FT3ModuleConstants.h: name, + // Every FT3 material is described by the map in FT3Materials.h: name, // composition, density, radiation length, transport parameters and display // colour, keyed by its MaterialID. FT3Module and FT3Layer retrieve the media // from the MaterialManager by the same ID, so nothing is written out twice. - for (const auto& [materialID, material] : ModuleConstants::materials) { + for (const auto& [materialID, material] : Materials::materials) { const int id = static_cast(materialID); if (material.nComponents == 0) { o2::base::Detector::Material(id, material.name, material.a[0], material.z[0], material.density, material.radl, material.absl); } else { // Mixture() takes non-const pointers, so hand it copies of the table rows - ModuleConstants::ComponentArray a = material.a; - ModuleConstants::ComponentArray z = material.z; - ModuleConstants::ComponentArray w = material.w; + Materials::ComponentArray a = material.a; + Materials::ComponentArray z = material.z; + Materials::ComponentArray w = material.w; o2::base::Detector::Mixture(id, material.name, a.data(), z.data(), material.density, material.nComponents, w.data()); } const auto& tracking = material.tracking; diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx index a1bcfdf6a9c9b..90962a5e6b0fc 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx @@ -17,6 +17,7 @@ #include "FT3Simulation/FT3Layer.h" #include "FT3Base/GeometryTGeo.h" #include "FT3Base/FT3BaseParam.h" +#include "FT3Simulation/FT3Materials.h" #include "FT3Simulation/FT3ModuleConstants.h" #include // for TGeoManager, gGeoManager @@ -49,7 +50,7 @@ FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerNam mx2X0 = Layerx2X0; mInnerRadius = rIn; mOuterRadius = rOut; - const double Si_X0 = Constants::materials.at(Constants::MaterialID::Silicon).radl; + const double Si_X0 = Materials::materials.at(Materials::MaterialID::Silicon).radl; mChipThickness = Layerx2X0 * Si_X0; mSensorThickness = 0.005; // assume 50 microns of active thickness (for sensor volumes for trapezoidal disks) @@ -80,8 +81,8 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); // volumes - TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); - TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); + TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber)); + TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber)); carbonFiberLayerVol1->SetLineColor(kGray + 2); carbonFiberLayerVol2->SetLineColor(kGray + 2); @@ -122,16 +123,16 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons double positiveYLength = yOuter - yInner; - TGeoVolume* kaptonPipePos = new TGeoVolume((separationLayerName + "_KaptonPipePos_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Kapton)); + TGeoVolume* kaptonPipePos = new TGeoVolume((separationLayerName + "_KaptonPipePos_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Kapton)); kaptonPipePos->SetLineColor(kGray); - TGeoVolume* waterVolumePos = new TGeoVolume((separationLayerName + "_WaterVolumePos_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Water)); + TGeoVolume* waterVolumePos = new TGeoVolume((separationLayerName + "_WaterVolumePos_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Water)); waterVolumePos->SetLineColor(kBlue); motherVolume->AddNode(waterVolumePos, 1, new TGeoCombiTrans(xPos, (yInner + yOuter) / 2.0, mZ, rotation)); - TGeoVolume* kaptonPipeNeg = new TGeoVolume((separationLayerName + "_KaptonPipeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Kapton)); + TGeoVolume* kaptonPipeNeg = new TGeoVolume((separationLayerName + "_KaptonPipeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Kapton)); kaptonPipeNeg->SetLineColor(kGray); - TGeoVolume* waterVolumeNeg = new TGeoVolume((separationLayerName + "_WaterVolumeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), FT3Module::getMedium(Constants::MaterialID::Water)); + TGeoVolume* waterVolumeNeg = new TGeoVolume((separationLayerName + "_WaterVolumeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Water)); waterVolumeNeg->SetLineColor(kBlue); motherVolume->AddNode(waterVolumeNeg, 1, new TGeoCombiTrans(xPos, -(yInner + yOuter) / 2.0, mZ, rotation)); @@ -145,9 +146,9 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons yMax = 2 * yOuter; pipeLength = yMax; - TGeoVolume* kaptonPipe = new TGeoVolume((separationLayerName + "_KaptonPipe_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, pipeLength / 2), FT3Module::getMedium(Constants::MaterialID::Kapton)); + TGeoVolume* kaptonPipe = new TGeoVolume((separationLayerName + "_KaptonPipe_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, pipeLength / 2), getMedium(Materials::MaterialID::Kapton)); kaptonPipe->SetLineColor(kGray); - TGeoVolume* waterVolume = new TGeoVolume((separationLayerName + "_WaterVolume_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, pipeLength / 2), FT3Module::getMedium(Constants::MaterialID::Water)); + TGeoVolume* waterVolume = new TGeoVolume((separationLayerName + "_WaterVolume_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, pipeLength / 2), getMedium(Materials::MaterialID::Water)); waterVolume->SetLineColor(kBlue); motherVolume->AddNode(waterVolume, 1, new TGeoCombiTrans(xPos, 0, mZ, rotation)); @@ -168,9 +169,9 @@ void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string TGeoTube* foamLayer = new TGeoTube(mInnerRadius, mOuterRadius, foamSpacingThickness / 2); // volumes - TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); - TGeoVolume* foamLayerVol = new TGeoVolume((separationLayerName + "_Foam").c_str(), foamLayer, FT3Module::getMedium(Constants::MaterialID::Foam)); - TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, FT3Module::getMedium(Constants::MaterialID::CarbonFiber)); + TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber)); + TGeoVolume* foamLayerVol = new TGeoVolume((separationLayerName + "_Foam").c_str(), foamLayer, getMedium(Materials::MaterialID::Foam)); + TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber)); carbonFiberLayerVol1->SetLineColor(kGray + 2); foamLayerVol->SetLineColor(kBlack); @@ -192,9 +193,9 @@ void FT3Layer::createReferenceCircles(TGeoVolume* motherVolume, const std::strin TGeoTube* outerCircle = new TGeoTube(mOuterRadius - 0.1, mOuterRadius + 0.1, 0.01); TGeoTube* outerCircleEdge = new TGeoTube(mOuterRadius + 3.3, mOuterRadius + 3.5, 0.01); - TGeoVolume* innerCircleVol = new TGeoVolume((mLayerName + "_InnerCircle").c_str(), innerCircle, FT3Module::getMedium(Constants::MaterialID::Air)); - TGeoVolume* outerCircleVol = new TGeoVolume((mLayerName + "_OuterCircle").c_str(), outerCircle, FT3Module::getMedium(Constants::MaterialID::Air)); - TGeoVolume* outerCircleEdgeVol = new TGeoVolume((mLayerName + "_OuterCircleEdge").c_str(), outerCircleEdge, FT3Module::getMedium(Constants::MaterialID::Air)); + TGeoVolume* innerCircleVol = new TGeoVolume((mLayerName + "_InnerCircle").c_str(), innerCircle, getMedium(Materials::MaterialID::Air)); + TGeoVolume* outerCircleVol = new TGeoVolume((mLayerName + "_OuterCircle").c_str(), outerCircle, getMedium(Materials::MaterialID::Air)); + TGeoVolume* outerCircleEdgeVol = new TGeoVolume((mLayerName + "_OuterCircleEdge").c_str(), outerCircleEdge, getMedium(Materials::MaterialID::Air)); innerCircleVol->SetLineColor(kRed); outerCircleVol->SetLineColor(kBlue); @@ -231,8 +232,8 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber); std::string passiveName = o2::ft3::GeometryTGeo::getFT3PassivePattern() + std::to_string(mLayerNumber); - TGeoMedium* medSi = FT3Module::getMedium(Constants::MaterialID::Silicon); - TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); + TGeoMedium* medSi = getMedium(Materials::MaterialID::Silicon); + TGeoMedium* medAir = getMedium(Materials::MaterialID::Air); TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); @@ -342,8 +343,8 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2); TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2); - TGeoMedium* medSi = FT3Module::getMedium(Constants::MaterialID::Silicon); - TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); + TGeoMedium* medSi = getMedium(Materials::MaterialID::Silicon); + TGeoMedium* medAir = getMedium(Materials::MaterialID::Air); TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); sensVol->SetLineColor(kYellow); @@ -373,7 +374,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back"; std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); - TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); + TGeoMedium* medAir = getMedium(Materials::MaterialID::Air); TGeoVolume* layerVol = nullptr; // Add a little additional room in radius TGeoTube* layer = new TGeoTube(mInnerRadius - 0.1, mOuterRadius + 0.1, 1.5); @@ -400,7 +401,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back"; std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); - TGeoMedium* medAir = FT3Module::getMedium(Constants::MaterialID::Air); + TGeoMedium* medAir = getMedium(Materials::MaterialID::Air); TGeoVolume* layerVol = nullptr; // set up stave config, differs between ML and OT disks diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx new file mode 100644 index 0000000000000..7d35a2d39beb5 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx @@ -0,0 +1,30 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + + +/// \file FT3Materials.cxx +/// \brief Access to the media created from the FT3 material map + +#include "FT3Simulation/FT3Materials.h" + +#include "DetectorsBase/MaterialManager.h" + +#include + +TGeoMedium* o2::ft3::getMedium(Materials::MaterialID id) +{ + auto* medium = o2::base::MaterialManager::Instance().getTGeoMedium(Materials::moduleName, static_cast(id)); + if (!medium) { + LOG(fatal) << "FT3: no medium registered for " << Materials::materials.at(id).name + << "; createMaterials() has to run before the geometry is built"; + } + return medium; +} diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx index c9e000b13d5bf..f94c3d149427d 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx @@ -14,7 +14,6 @@ #include "FT3Simulation/FT3Module.h" #include "FT3Base/FT3BaseParam.h" -#include "DetectorsBase/MaterialManager.h" #include #include #include @@ -29,21 +28,7 @@ #include #include -/* - * The FT3 module media are registered with the MaterialManager by - * Detector::createMaterials(), which walks the material table in - * FT3ModuleConstants.h. Here they are only looked up again by their - * MaterialID, so that the IDs never have to be repeated by hand. - */ -TGeoMedium* FT3Module::getMedium(Constants::MaterialID id) -{ - auto* medium = o2::base::MaterialManager::Instance().getTGeoMedium("FT3", static_cast(id)); - if (!medium) { - LOG(fatal) << "FT3Module: no medium registered for " << Constants::materials.at(id).name - << "; Detector::createMaterials() has to run before the geometry is built"; - } - return medium; -} +using o2::ft3::getMedium; double calculate_y_circle(double x, double radius) { @@ -281,8 +266,8 @@ void FT3Module::addStaveVolume( TGeoVolume* staveVolume = new TGeoVolume( (volumeName).c_str(), staveShape, - getMedium(Constants::MaterialID::CarbonFiber)); - const int carbonFiberColor = Constants::materials.at(Constants::MaterialID::CarbonFiber).colour; + getMedium(Materials::MaterialID::CarbonFiber)); + const int carbonFiberColor = Materials::materials.at(Materials::MaterialID::CarbonFiber).colour; staveVolume->SetLineColor(carbonFiberColor); staveVolume->SetFillColorAlpha(carbonFiberColor, 0.4); @@ -367,8 +352,8 @@ void FT3Module::add2x1GlueVolume( { std::string glue_name = "FT3glue_" + element_glued_to + "_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, glue_name, Constants::materials.at(Constants::MaterialID::Epoxy).colour, - getMedium(Constants::MaterialID::Epoxy), volume_count, + motherVolume, glue_name, Materials::materials.at(Materials::MaterialID::Epoxy).colour, + getMedium(Materials::MaterialID::Epoxy), volume_count, x_mid, y_mid, z_mid, Constants::sensor2x1_width / 2, Constants::sensor2x1_height / 2, Constants::epoxyThickness / 2); } @@ -383,8 +368,8 @@ void FT3Module::add2x1CopperVolume( { std::string copper_name = "FT3Copper_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, copper_name, Constants::materials.at(Constants::MaterialID::Copper).colour, - getMedium(Constants::MaterialID::Copper), volume_count, + motherVolume, copper_name, Materials::materials.at(Materials::MaterialID::Copper).colour, + getMedium(Materials::MaterialID::Copper), volume_count, x_mid, y_mid, z_mid, Constants::sensor2x1_width / 2, Constants::sensor2x1_height / 2, Constants::copperThickness / 2); } @@ -399,8 +384,8 @@ void FT3Module::add2x1KaptonVolume( { std::string kapton_name = "FT3Kapton_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, kapton_name, Constants::materials.at(Constants::MaterialID::Kapton).colour, - getMedium(Constants::MaterialID::Kapton), volume_count, + motherVolume, kapton_name, Materials::materials.at(Materials::MaterialID::Kapton).colour, + getMedium(Materials::MaterialID::Kapton), volume_count, x_mid, y_mid, z_mid, Constants::sensor2x1_width / 2, Constants::sensor2x1_height / 2, Constants::kaptonThickness / 2); } @@ -430,12 +415,12 @@ void FT3Module::addSingleSensorVolume( { TGeoVolume* sensor; TGeoManager* geoManager = gGeoManager; - TGeoMedium* siliconMed = getMedium(Constants::MaterialID::Silicon); + TGeoMedium* siliconMed = getMedium(Materials::MaterialID::Silicon); // ACTIVE AREA // Sensor thickness is along the Y axis; this convention is used in digitisation by barrels and disks std::string sensor_name = "FT3Sensor_Active_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count); addDetectorVolume( - motherVolume, sensor_name, Constants::materials.at(Constants::MaterialID::Silicon).colour, siliconMed, + motherVolume, sensor_name, Materials::materials.at(Materials::MaterialID::Silicon).colour, siliconMed, volume_count, active_x_mid, y_mid, z_mid, Constants::active_width / 2, Constants::siliconThickness / 2, Constants::single_sensor_height / 2, 90.); @@ -446,7 +431,7 @@ void FT3Module::addSingleSensorVolume( sensor = geoManager->MakeBox(sensor_inactive_name.c_str(), siliconMed, Constants::inactive_width / 2, Constants::single_sensor_height / 2, Constants::siliconThickness / 2); addDetectorVolume( - motherVolume, sensor_inactive_name, Constants::SiInactiveColor, siliconMed, + motherVolume, sensor_inactive_name, Materials::SiInactiveColor, siliconMed, volume_count, inactive_x_mid, y_mid, z_mid, Constants::inactive_width / 2, Constants::single_sensor_height / 2, Constants::siliconThickness / 2); } @@ -733,11 +718,11 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double LOG(debug) << "FT3Module: create_layout - Layer " << layerNumber << ", Direction " << direction << ", Face " << face; TGeoManager* geoManager = gGeoManager; - TGeoMedium* siliconMed = getMedium(Constants::MaterialID::Silicon); - TGeoMedium* copperMed = getMedium(Constants::MaterialID::Copper); - TGeoMedium* kaptonMed = getMedium(Constants::MaterialID::Kapton); - TGeoMedium* epoxyMed = getMedium(Constants::MaterialID::Epoxy); - TGeoMedium* AluminumMed = getMedium(Constants::MaterialID::Aluminum); + TGeoMedium* siliconMed = getMedium(Materials::MaterialID::Silicon); + TGeoMedium* copperMed = getMedium(Materials::MaterialID::Copper); + TGeoMedium* kaptonMed = getMedium(Materials::MaterialID::Kapton); + TGeoMedium* epoxyMed = getMedium(Materials::MaterialID::Epoxy); + TGeoMedium* AluminumMed = getMedium(Materials::MaterialID::Aluminum); // double sensor_width = 2.5; // double sensor_height = 9.6; From c616f617aa1d1b610b77af9951424310bd27f013 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Thu, 24 Sep 2026 21:19:22 +0200 Subject: [PATCH 03/12] Add basic functionality for placing exact staves and sensors: achieve by bundling existing functionalities into functions more --- .../FT3/base/include/FT3Base/FT3BaseParam.h | 9 +- .../include/FT3Simulation/FT3Module.h | 35 +- .../FT3Simulation/FT3ModuleConstants.h | 99 +++-- .../TRKFT3/FT3/simulation/src/FT3Module.cxx | 354 ++++++++++++------ 4 files changed, 363 insertions(+), 134 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h index f4619c608c099..0fbea7d1a5e76 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h @@ -46,8 +46,13 @@ struct FT3BaseParam : public o2::conf::ConfigurableParamHelper { double staveTolOTInner = 0.; double staveTolOTOuter = 0.; - // What to place over x=0 line in case of full outer-outer stave: Gap or Module - bool placeSensorStackInMiddleOfStave = false; + /* + * Place the sensor stacks from the tabulated layout in FT3ModuleConstants.h + * (StaveConfig::exactStaveFills) instead of filling every stave greedily + * with the stack sizes in kSensorsPerStack. The tabulated layout is taken as + * given: none of the radial tolerances above are applied to it. + */ + bool useExactStavePlacement = false; // Draw reference circles at inner and outer radius of stave layer, for visualisation bool drawReferenceCircles = false; diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h index 0072d575d6c4b..52e2922368f72 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h @@ -59,10 +59,39 @@ class FT3Module double Rout, double z_offset_local, const Constants::StaveConfig& staveConfig, TGeoVolume* motherVolume); + // Walk every stave of a layer, create its volumes and work out where its + // modules go, leaving the positions in y_positionsPosNeg + void build_staves_exact( + TGeoVolume* motherVolume, int layerNumber, int direction, + const Constants::StaveConfig& staveConfig, + const std::array, 4>& staveTriangles, + double z_offset_to_carbon_face, + std::vector& y_positionsPosNeg, + unsigned& staveVolumeCount); + + void build_staves_greedy( + TGeoVolume* motherVolume, int layerNumber, int direction, double Rin, double Rout, + const Constants::StaveConfig& staveConfig, + const std::array, 4>& staveTriangles, + double z_offset_to_carbon_face, + std::vector& y_positionsPosNeg, unsigned& staveVolumeCount); + + // Shared by both: one stave's carbon shell, plus its mirror where needed + void add_stave_volumes( + TGeoVolume* motherVolume, int layerNumber, int direction, + const Constants::StaveConfig& staveConfig, unsigned i_stave, + const std::array, 4>& staveTriangles, + double z_offset_to_carbon_face, std::pair& absAllowedYRange, + double y_midpoint, bool mirrorStaveAroundX, unsigned* staveVolumeCount); + // Helper functions - void fill_stave(PosNegPositionTypes& y_positions, double Rin, double Rout, - double x_left, unsigned kSensorStack, PositionRangeType y_range, - std::pair& absAllowedYRange); + void fill_stave_greedy( + PosNegPositionTypes& y_positions, double Rin, double Rout, + double x_left, unsigned kSensorStack, PositionRangeType y_range, + std::pair& absAllowedYRange); + + PositionTypes fill_stave_exact(const std::vector& fills); + void addStaveVolume( TGeoVolume* motherVolume, std::string volumeName, int direction, unsigned* volume_count, double staveLength, diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index 8b1cded3bfec3..52800b1e9efd4 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -73,6 +73,35 @@ inline const int staveIdxToID(int staveIdx, unsigned nStavesPerDisc) return staveIdx - nStavesOneSide + isRight; } +/* + * Stave x midpoints follow from their number and spacing: they are spread + * symmetrically about x=0, so an even count leaves a gap on the axis rather + * than putting a stave on it. Deriving them keeps the spacing and the + * positions from drifting apart. + */ +inline std::vector makeStaveXMidpoints(unsigned nStaves, double spacing) +{ + std::vector midpoints(nStaves); + for (unsigned i = 0; i < nStaves; i++) { + midpoints[i] = (i - (nStaves - 1) / 2.0) * spacing; + } + return midpoints; +} + +/* + * Staves alternate between the front and the back of the disc so that + * neighbours can overlap in x without touching, staggered in z by + * z_offsetStave. Starting at 0 puts the leftmost stave at the back. + */ +inline std::vector makeStaveOnFront(unsigned nStaves) +{ + std::vector staveOnFront(nStaves); + for (unsigned i = 0; i < nStaves; i++) { + staveOnFront[i] = i % 2; + } + return staveOnFront; +} + // material properties const double siliconThickness = 0.01; const double copperThickness = 0.006; @@ -101,6 +130,22 @@ inline const double z_offsetStave(double x_midpoint_spacing) (2 - x_midpoint_spacing / (sensor2x1_width / 2 + staveSensorGap)); } +/* + * One uninterrupted fill of 2xN modules along a stave. + * + * yStart is the y of the BOTTOM edge of the first module; modules follow + * upwards, each separated from the previous one by stackGap. Nothing is + * mirrored: the layout is symmetric about the y-axis (stave +-ID) but NOT + * about the x-axis, so every fill states its own y explicitly. + * + * A stave that the beam pipe cuts in two therefore carries two fills, one + * below the hole and one above it, each with its own yStart. + */ +struct StaveFill { + const double yStart; + const std::vector stackHeights; +}; + // Struct for stave position configuration (varies between ML/OT) struct StaveConfig { const unsigned isML; // whether this config is for ML or OT @@ -128,6 +173,13 @@ struct StaveConfig { // kSegmentedStave: staggering staves in z (see z_offsetStave) // accessed via stave index, NOT stave ID const std::vector& staveOnFront; + /* + * Tabulated module layout, used when FT3Base.useExactStavePlacement is set. + * One entry per stave, indexed like x_midpoints (NOT by stave ID), holding + * that stave's fills: one for a stave reaching across y=0, two for a stave + * split by the beam pipe. + */ + const std::vector>& exactStaveFills; }; namespace OT_StavePositions @@ -142,20 +194,21 @@ const std::vector y_lengths = { 128.7, 132.0, 132.0, 138.6, 138.6, 56.1, 52.8, 52.8, 56.1, 138.6, 138.6, 132.0, 132.0, 128.7, 118.8, 118.8, 105.6, 99.0, 92.4, 79.2, 66.0, 52.8}; -const std::vector x_midpoints = { - -65.25, -60.75, -56.25, -51.75, -47.25, -42.75, -38.25, // L - -33.75, -29.25, -24.75, -20.25, -15.75, -11.25, -6.75, -2.25, // L - 2.25, 6.75, 11.25, 15.75, 20.25, 24.75, 29.25, 33.75, // R - 38.25, 42.75, 47.25, 51.75, 56.25, 60.75, 65.25 // R -}; -const double x_midpoint_spacing = 4.5; // assume constant for now -const double maxToleranceInner = 0.; // default not allowed inwards +const unsigned nStaves = 30; // y_lengths, staveOnFront and exactStaveFills follow this +const double x_midpoint_spacing = 4.5; +const std::vector x_midpoints = makeStaveXMidpoints(nStaves, x_midpoint_spacing); +const double maxToleranceInner = 9.; // close but not directly at 10cm yet const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation -const std::vector staveOnFront = - { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // L - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 // R -}; +const std::vector staveOnFront = makeStaveOnFront(nStaves); +/* + * TODO: fill from the disk optimiser output. One entry per stave, in the same + * order as x_midpoints, and nStaves entries in total before + * useExactStavePlacement works. Each entry lists that stave's fills: + * + * {{{-52.80, {4, 4, 4, 3}}}}, // reaches across y=0 + * {{-63.84, {4, 4, 3}}, {8.34, {4, 4, 3}}}, // split by the beam pipe + */ +const std::vector> exactStaveFills = {}; } // namespace OT_StavePositions namespace ML_StavePositions @@ -171,18 +224,14 @@ const std::map> staveID_to_y_midpoint = { const std::vector y_lengths = { 30.5, 44.5, 53.6, 60.0, 64.6, 29.5, 25.8, 25.0, 25.0, 25.8, 29.5, 64.6, 60.0, 53.6, 44.5, 30.5}; -const std::vector x_midpoints = { - -33.75, -29.25, -24.75, -20.25, -15.75, -11.25, -6.75, -2.25, // L - 2.25, 6.75, 11.25, 15.75, 20.25, 24.75, 29.25, 33.75 // R -}; +const unsigned nStaves = 16; // y_lengths, staveOnFront and exactStaveFills follow this const double x_midpoint_spacing = 4.5; +const std::vector x_midpoints = makeStaveXMidpoints(nStaves, x_midpoint_spacing); const double maxToleranceInner = 0.; // default not allowed inwards const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation -const std::vector staveOnFront = - { - 1, 0, 1, 0, 1, 0, 1, 0, // L - 1, 0, 1, 0, 1, 0, 1, 0 // R -}; +const std::vector staveOnFront = makeStaveOnFront(nStaves); +// TODO: fill from the disk optimiser output, see OT_StavePositions above. +const std::vector> exactStaveFills = {}; } // namespace ML_StavePositions // Get stave configuration based on tracker type @@ -197,7 +246,8 @@ inline StaveConfig getStaveConfig(bool isInnerDisk) ML_StavePositions::x_midpoint_spacing, ML_StavePositions::maxToleranceInner, ML_StavePositions::maxToleranceOuter, - ML_StavePositions::staveOnFront}; + ML_StavePositions::staveOnFront, + ML_StavePositions::exactStaveFills}; } else { return StaveConfig{ false, // isML @@ -207,7 +257,8 @@ inline StaveConfig getStaveConfig(bool isInnerDisk) OT_StavePositions::x_midpoint_spacing, OT_StavePositions::maxToleranceInner, OT_StavePositions::maxToleranceOuter, - OT_StavePositions::staveOnFront}; + OT_StavePositions::staveOnFront, + OT_StavePositions::exactStaveFills}; } } diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx index f94c3d149427d..630a607375019 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -74,7 +76,7 @@ std::pair calculate_y_range( } /* - * This function is a helper function to determine the positions of sensors on the stave + * Greedy fill: determine the positions of sensors on the stave * by adding sensors until there is no more space available. * * Arguments: @@ -90,9 +92,9 @@ std::pair calculate_y_range( * absAllowedYRange: the absolute y range allowed for placing sensors, * used to cut placement if they go past allowed tolerances */ -void FT3Module::fill_stave(PosNegPositionTypes& y_positions, double Rin, double Rout, - double x_left, unsigned kSensorStack, PositionRangeType y_ranges, - std::pair& absAllowedYRange) +void FT3Module::fill_stave_greedy(PosNegPositionTypes& y_positions, double Rin, double Rout, + double x_left, unsigned kSensorStack, PositionRangeType y_ranges, + std::pair& absAllowedYRange) { // start with upper half of the stave, then mirror to the bottom half // add the height of kSensorStack sensors + the gaps in between them @@ -141,6 +143,33 @@ void FT3Module::fill_stave(PosNegPositionTypes& y_positions, double Rin, double } } +/* + * Exact fill: read the module positions of one stave straight out of the + * tabulated layout. The counterpart to fill_stave_greedy, without any of its + * checks -- the table is validated before it reaches the geometry, so there is + * nothing here to cut, clip or derive. + * + * fills: the stave's fills, each an uninterrupted stretch of modules. A stave + * split by the beam pipe has one fill below the hole and one above it; + * nothing is mirrored, since the layout is symmetric about the y-axis + * but not about the x-axis. + * + * Returns one list holding every module of the stave, at negative y as well, + * each stored by its BOTTOM edge. + */ +PositionTypes FT3Module::fill_stave_exact(const std::vector& fills) +{ + PositionTypes y_positions; + for (const auto& fill : fills) { + double y_bottom = fill.yStart; + for (unsigned kSensorStack : fill.stackHeights) { + y_positions.emplace_back(y_bottom, kSensorStack); + y_bottom += Constants::getStackHeight(kSensorStack) + Constants::stackGap; + } + } + return y_positions; +} + /* * Create the vertices of the triangles that make up the stave cross section * @@ -436,67 +465,143 @@ void FT3Module::addSingleSensorVolume( Constants::inactive_width / 2, Constants::single_sensor_height / 2, Constants::siliconThickness / 2); } -void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction, - double Rin, double Rout, double z_offset_local, - const Constants::StaveConfig& staveConfig, - TGeoVolume* motherVolume) +/* + * Look up a stave's y midpoint and whether it is built as two pieces mirrored + * about the x-axis. Returns false for a stave absent from the map, which sits + * on y=0 in one piece and leaves both outputs untouched. + */ +bool staveMidpointAndMirror(const Constants::StaveConfig& staveConfig, int staveID, + double& y_midpoint, bool& mirrorStaveAroundX) { - LOG(debug) << "FT3Module: create_layout_staveGeo - Direction " - << direction << ", Layer " << layerNumber; + auto y_midpoint_it = staveConfig.staveID_to_y_midpoint.find(staveID); + if (y_midpoint_it == staveConfig.staveID_to_y_midpoint.end()) { + return false; + } + y_midpoint = y_midpoint_it->second.first; // avoid double map lookup + mirrorStaveAroundX = y_midpoint_it->second.second; + return true; +} - auto& ft3Params = o2::ft3::FT3BaseParam::Instance(); +/* + * Create the carbon shell of one stave, staggered in z, plus the mirrored one + * when the stave is built as two pieces. + */ +void FT3Module::add_stave_volumes( + TGeoVolume* motherVolume, int layerNumber, int direction, + const Constants::StaveConfig& staveConfig, unsigned i_stave, + const std::array, 4>& staveTriangles, + double z_offset_to_carbon_face, std::pair& absAllowedYRange, + double y_midpoint, bool mirrorStaveAroundX, unsigned* staveVolumeCount) +{ + // Get whether the stave is shifted backward or not before creating + double z_stave_shift_abs = staveConfig.staveOnFront[i_stave] ? 0 : Constants::z_offsetStave(staveConfig.x_midpoint_spacing); + double z_stave_shift_forward = // move staves more inward to fit in layer volume + -z_offset_to_carbon_face + z_stave_shift_abs; + std::string stave_volume_name = + "FT3_Stave_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + + "_" + std::to_string(i_stave); + + addStaveVolume( + motherVolume, stave_volume_name, direction, staveVolumeCount, + staveConfig.y_lengths[i_stave], staveTriangles, absAllowedYRange, + staveConfig.x_midpoints[i_stave], y_midpoint, z_stave_shift_forward); + // Now create the mirrored stave + if (mirrorStaveAroundX) { + addStaveVolume( + motherVolume, stave_volume_name + "_mirrored", direction, staveVolumeCount, + staveConfig.y_lengths[i_stave], staveTriangles, absAllowedYRange, + staveConfig.x_midpoints[i_stave], -y_midpoint, z_stave_shift_forward); + } +} - // First let's define some constants used throughout - /* - * we build the volume from the outside in, starting with the silicon, - * then glue & materials towards the stave. Depending on direction, - * the distance from the center will be mirrored. - * - * | SILICON SENSOR | GLUE | COPPER | KAPTON | GLUE | CARBON STAVE | - * ----------------------------------------------------------------> z - * - * Naturally, this will be mirrored for layers in the backwards direction, - * such that the face of the sensors always face the interaction region. - * - * Currently, we stipulate that the default stave face is at local z=0, - * that is then shifted by the half air thickness encapsulating the layer - * to avoid overlaps with the air and services. All offsets are - * calculated for backward direction (since that is a positive shift), - * and then flipped for forward. At that point, the innermost/frontmost - * stave face is at the edge of the air volume, so we shift it back a little - * to make space for the sensor materials and a slight margin. - */ - double totalSensorMaterialThickness = - Constants::epoxyThickness + Constants::kaptonThickness + Constants::copperThickness + - Constants::epoxyThickness + Constants::siliconThickness; - double z_offset_to_carbon_face = z_offset_local - totalSensorMaterialThickness - 0.1; - double z_offset_to_glue_Ka = - z_offset_to_carbon_face + Constants::epoxyThickness / 2; - double z_offset_to_kapton = - z_offset_to_carbon_face + Constants::epoxyThickness + - Constants::kaptonThickness / 2; - double z_offset_to_copper = - z_offset_to_carbon_face + Constants::epoxyThickness + - Constants::kaptonThickness + Constants::copperThickness / 2; - double z_offset_to_glue_Si = - z_offset_to_carbon_face + Constants::epoxyThickness + Constants::kaptonThickness + - Constants::copperThickness + Constants::epoxyThickness / 2; - double z_offset_to_silicon = - z_offset_to_carbon_face + Constants::epoxyThickness + - Constants::kaptonThickness + Constants::copperThickness + - Constants::epoxyThickness + Constants::siliconThickness / 2; +/* + * Exact layout: build every stave of the layer from the tabulated layout in + * exactStaveFills, the counterpart to build_staves_greedy. + * + * Deliberately stupid: the stave is built to the length y_lengths gives it and + * the modules sit exactly where the table says. No tolerance is applied, the + * stave is not cut on the layer radii, no stave is skipped and nothing is + * mirrored, so none of Rin, Rout or the staveTol parameters are needed here. + * Whatever produced the table is responsible for it fitting the layer. + */ +void FT3Module::build_staves_exact( + TGeoVolume* motherVolume, int layerNumber, int direction, + const Constants::StaveConfig& staveConfig, + const std::array, 4>& staveTriangles, + double z_offset_to_carbon_face, + std::vector& y_positionsPosNeg, + unsigned& staveVolumeCount) +{ + // number of modules per stack height, only used for logging. Keyed by height + // rather than indexed by kSensorsPerStack, since the table is free to use + // heights that are not in that list. + std::map nSensorStackTotal; + for (unsigned i_stave = 0; i_stave < staveConfig.x_midpoints.size(); i_stave++) { + const int staveID = Constants::staveIdxToID(i_stave, staveConfig.x_midpoints.size()); - // initialise all y_positions, vector over all staves/columns - std::vector y_positionsPosNeg; - // stave triangle cross sections are the same for every stave (direction based) - std::array, 4> staveTriangles = buildStaveTriangle(direction); + // a stave in the map is built as two pieces, one either side of the beam pipe + double y_midpoint = 0.; + bool mirrorStaveAroundX = false; + staveMidpointAndMirror(staveConfig, staveID, y_midpoint, mirrorStaveAroundX); + + // no radial limit, so addStaveVolume cuts nothing off the stave + std::pair absAllowedYRange = {0., std::numeric_limits::max()}; + + add_stave_volumes(motherVolume, layerNumber, direction, staveConfig, i_stave, + staveTriangles, z_offset_to_carbon_face, absAllowedYRange, + y_midpoint, mirrorStaveAroundX, &staveVolumeCount); + + /* + * Every module goes in the positive-y list, whatever the sign of its y, and + * the negative-y list stays empty. The placement loop reads that list with + * y_sign = +1 and only ever adds to the stored bottom edge, so it does not + * care that some of those edges are negative. + */ + y_positionsPosNeg.emplace_back(fill_stave_exact(staveConfig.exactStaveFills[i_stave]), + PositionTypes{}); + + std::map nSensorStackCount; + for (const auto& [y_bottom, kSensorStack] : y_positionsPosNeg.back().first) { + nSensorStackCount[kSensorStack]++; + nSensorStackTotal[kSensorStack]++; + } + std::string moduleDebugStr = "Module size counts for layer " + std::to_string(layerNumber) + " in direction " + std::to_string(direction) + ":\n"; + for (const auto& [kSensorStack, nModules] : nSensorStackCount) { + moduleDebugStr += "\t" + std::to_string(nModules) + " modules with " + std::to_string(kSensorStack) + " sensors stacked\n"; + } + LOG(debug) << moduleDebugStr; + } + std::string totalModuleInfoStr = + "Total module size counts for layer " + std::to_string(layerNumber) + + " in direction " + std::to_string(direction) + ":\n"; + for (const auto& [kSensorStack, nModules] : nSensorStackTotal) { + totalModuleInfoStr += "\t" + std::to_string(nModules) + " modules with " + std::to_string(kSensorStack) + " sensors stacked\n"; + } + LOG(info) << totalModuleInfoStr; +} + +/* + * Greedy layout: work out the module positions from the layer radii and the + * stave length, filling each stave from the middle outwards with the stack + * sizes in kSensorsPerStack. Since it chooses the positions itself, this is + * the path that applies the radial tolerances. + * + * Extracted verbatim from create_layout_staveGeo; the body is unchanged. + */ +void FT3Module::build_staves_greedy( + TGeoVolume* motherVolume, int layerNumber, int direction, double Rin, double Rout, + const Constants::StaveConfig& staveConfig, + const std::array, 4>& staveTriangles, + double z_offset_to_carbon_face, + std::vector& y_positionsPosNeg, unsigned& staveVolumeCount) +{ + auto& ft3Params = o2::ft3::FT3BaseParam::Instance(); // declare vector with number of 2xn sensor stacks (modules) -- only used for logging // each entry is a vector, where each entry is the number of modules of that stack height std::vector> nSensorStackCountPerStave( staveConfig.x_midpoints.size(), std::vector(Constants::kSensorsPerStack.size(), 0)); std::vector nSensorStackTotal(Constants::kSensorsPerStack.size(), 0); - unsigned staveVolumeCount = 0; for (unsigned i_stave = 0; i_stave < staveConfig.x_midpoints.size(); i_stave++) { y_positionsPosNeg.emplace_back(PosNegPositionTypes{PositionTypes{}, PositionTypes{}}); const int staveID = Constants::staveIdxToID(i_stave, staveConfig.x_midpoints.size()); @@ -505,35 +610,14 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction bool mirrorStaveAroundX = false; // default positive and negative starting points has a gap around x-axis for symmetry double stave_half_length = staveConfig.y_lengths[i_stave] / 2; - PositionRangeType y_ranges; - if (ft3Params.placeSensorStackInMiddleOfStave) { - /* - * We want a sensor stack to cross over the x-axis for coverage at y=0 - * N.B. not necessarily exactly mirrored, only if stack gap is the same - * as the gap between sensors in a stack. Since we start filling with the - * first value in the kSensorsPerStack vector, we offset the first position - * by half of that. - * - * NOTE: TODO: in case the stave is too short to fit one full stack over the middle, - * then we will not be able to place anything since the bottom right/left point of - * the module will already be outside of acceptable bounds -- killing further placement. - */ - double stackHeight = Constants::getStackHeight(Constants::kSensorsPerStack[0]); - y_ranges = {{-stackHeight / 2, stave_half_length}, - {-stackHeight / 2 - Constants::stackGap, -stave_half_length}}; - } else { - /* - * Otherwise have a gap around y=0, so sensors are not placed there. - * This means the stave is perfectly mirrored around the x-axis. - */ - y_ranges = {{Constants::stackGap / 2, stave_half_length}, - {-Constants::stackGap / 2, -stave_half_length}}; - } - auto y_midpoint_it = staveConfig.staveID_to_y_midpoint.find(staveID); - if (y_midpoint_it != staveConfig.staveID_to_y_midpoint.end()) { + /* + * Have a gap around y=0, so sensors are not placed there. + * This means the stave is perfectly mirrored around the x-axis. + */ + PositionRangeType y_ranges = {{Constants::stackGap / 2, stave_half_length}, + {-Constants::stackGap / 2, -stave_half_length}}; + if (staveMidpointAndMirror(staveConfig, staveID, y_midpoint, mirrorStaveAroundX)) { // there is a defined midpoint for this stave, use this for starting points - y_midpoint = y_midpoint_it->second.first; // avoid double map lookup - mirrorStaveAroundX = y_midpoint_it->second.second; y_ranges.first = {y_midpoint - stave_half_length, y_midpoint + stave_half_length}; y_ranges.second = {-y_midpoint + stave_half_length, -y_midpoint - stave_half_length}; } @@ -587,33 +671,17 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction continue; } - // Get whether the stave is shifted backward or not before creating - double z_stave_shift_abs = staveConfig.staveOnFront[i_stave] ? 0 : Constants::z_offsetStave(staveConfig.x_midpoint_spacing); - double z_stave_shift_forward = // move staves more inward to fit in layer volume - -z_offset_to_carbon_face + z_stave_shift_abs; - std::string stave_volume_name = - "FT3_Stave_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + - "_" + std::to_string(i_stave); - // Create the stave volumes and fill the y positions where to put sensors on the stave - addStaveVolume( - motherVolume, stave_volume_name, direction, &staveVolumeCount, - staveConfig.y_lengths[i_stave], staveTriangles, absAllowedYRange, - staveConfig.x_midpoints[i_stave], y_midpoint, z_stave_shift_forward); - // Now create the mirrored stave - if (mirrorStaveAroundX) { - addStaveVolume( - motherVolume, stave_volume_name + "_mirrored", direction, &staveVolumeCount, - staveConfig.y_lengths[i_stave], staveTriangles, absAllowedYRange, - staveConfig.x_midpoints[i_stave], -y_midpoint, z_stave_shift_forward); - } + add_stave_volumes(motherVolume, layerNumber, direction, staveConfig, i_stave, + staveTriangles, z_offset_to_carbon_face, absAllowedYRange, + y_midpoint, mirrorStaveAroundX, &staveVolumeCount); // now add the sensor positions on the stave for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) { unsigned nModulesCurr = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size(); - fill_stave(y_positionsPosNeg.back(), Rin, Rout, x_left, - Constants::kSensorsPerStack[i_kSens], y_ranges, - absAllowedYRange); + fill_stave_greedy(y_positionsPosNeg.back(), Rin, Rout, x_left, + Constants::kSensorsPerStack[i_kSens], y_ranges, + absAllowedYRange); unsigned nModulesAdded = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size() - nModulesCurr; nSensorStackCountPerStave[i_stave][i_kSens] = nModulesAdded; nSensorStackTotal[i_kSens] += nModulesAdded; @@ -631,6 +699,80 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction totalModuleInfoStr += "\t" + std::to_string(nSensorStackTotal[i_kSens]) + " modules with " + std::to_string(Constants::kSensorsPerStack[i_kSens]) + " sensors stacked\n"; } LOG(info) << totalModuleInfoStr; +} + +void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction, + double Rin, double Rout, double z_offset_local, + const Constants::StaveConfig& staveConfig, + TGeoVolume* motherVolume) +{ + LOG(debug) << "FT3Module: create_layout_staveGeo - Direction " + << direction << ", Layer " << layerNumber; + + auto& ft3Params = o2::ft3::FT3BaseParam::Instance(); + + // First let's define some constants used throughout + /* + * we build the volume from the outside in, starting with the silicon, + * then glue & materials towards the stave. Depending on direction, + * the distance from the center will be mirrored. + * + * | SILICON SENSOR | GLUE | COPPER | KAPTON | GLUE | CARBON STAVE | + * ----------------------------------------------------------------> z + * + * Naturally, this will be mirrored for layers in the backwards direction, + * such that the face of the sensors always face the interaction region. + * + * Currently, we stipulate that the default stave face is at local z=0, + * that is then shifted by the half air thickness encapsulating the layer + * to avoid overlaps with the air and services. All offsets are + * calculated for backward direction (since that is a positive shift), + * and then flipped for forward. At that point, the innermost/frontmost + * stave face is at the edge of the air volume, so we shift it back a little + * to make space for the sensor materials and a slight margin. + */ + double totalSensorMaterialThickness = + Constants::epoxyThickness + Constants::kaptonThickness + Constants::copperThickness + + Constants::epoxyThickness + Constants::siliconThickness; + double z_offset_to_carbon_face = z_offset_local - totalSensorMaterialThickness - 0.1; + double z_offset_to_glue_Ka = + z_offset_to_carbon_face + Constants::epoxyThickness / 2; + double z_offset_to_kapton = + z_offset_to_carbon_face + Constants::epoxyThickness + + Constants::kaptonThickness / 2; + double z_offset_to_copper = + z_offset_to_carbon_face + Constants::epoxyThickness + + Constants::kaptonThickness + Constants::copperThickness / 2; + double z_offset_to_glue_Si = + z_offset_to_carbon_face + Constants::epoxyThickness + Constants::kaptonThickness + + Constants::copperThickness + Constants::epoxyThickness / 2; + double z_offset_to_silicon = + z_offset_to_carbon_face + Constants::epoxyThickness + + Constants::kaptonThickness + Constants::copperThickness + + Constants::epoxyThickness + Constants::siliconThickness / 2; + + // initialise all y_positions, vector over all staves/columns + std::vector y_positionsPosNeg; + // stave triangle cross sections are the same for every stave (direction based) + std::array, 4> staveTriangles = buildStaveTriangle(direction); + unsigned staveVolumeCount = 0; + /* + * Either read the module positions out of the tabulated layout, or work them + * out here from the layer radii and the stave length. Both create the stave + * volumes and leave the module positions in y_positionsPosNeg, so the sensor + * placement below does not care which of the two ran. + * + * A disc without a tabulated layout falls back to the greedy fill even when + * the parameter is set, so that the discs which do have one can use it. + */ + if (ft3Params.useExactStavePlacement && !staveConfig.exactStaveFills.empty()) { + build_staves_exact(motherVolume, layerNumber, direction, staveConfig, staveTriangles, + z_offset_to_carbon_face, y_positionsPosNeg, staveVolumeCount); + } else { + build_staves_greedy(motherVolume, layerNumber, direction, Rin, Rout, staveConfig, + staveTriangles, z_offset_to_carbon_face, y_positionsPosNeg, + staveVolumeCount); + } // Create volumes for the sensors and the support materials on top of the stave for (unsigned i_stave = 0; i_stave < staveConfig.x_midpoints.size(); i_stave++) { @@ -664,6 +806,8 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction unsigned sensor_count = 0; // reset for each stave for (int y_sign = -1; y_sign < 2; y_sign += 2) { // place sensors at positive and negative y + // recall for exact placement the first entry is filled, second is empty + // (this has no effect on the placement as it loops over all positions) const auto& positions = (y_sign == 1) ? y_positionsPosNeg[i_stave].first : y_positionsPosNeg[i_stave].second; // define starting midpoint: y = y_start +- distance to middle of sensor From eb3f6ae80fef8d6ae4871ec51c93a57e5844dff7 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Thu, 24 Sep 2026 21:33:34 +0200 Subject: [PATCH 04/12] add the relevant constansts --- .../FT3Simulation/FT3ModuleConstants.h | 77 +++++++++++++++---- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index 52800b1e9efd4..b0b964e286d94 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -184,16 +184,33 @@ struct StaveConfig { namespace OT_StavePositions { +/* + * Staves that the beam pipe cuts in two, built as two pieces on +-y_midpoint. + * Do NOT add any zero midpoints, this is taken off separately. + */ const std::map> staveID_to_y_midpoint = { - {-2, {39.0, true}}, - {-1, {41.4, true}}, - {1, {41.4, true}}, - {2, {39.0, true}}}; + {-5, {32.73, true}}, + {-4, {38.609, true}}, + {-3, {42.094, true}}, + {-2, {43.023, true}}, + {-1, {44.054, true}}, + {1, {44.053, true}}, + {2, {43.023, true}}, + {3, {42.094, true}}, + {4, {38.609, true}}, + {5, {32.73, true}}}; +/* + * Length of one stave piece: for a stave in staveID_to_y_midpoint that is one + * of its two pieces, otherwise the whole stave centred on y=0. Sized to cover + * the modules in exactStaveFills, with 1 mm of margin. + */ const std::vector y_lengths = { - 52.8, 66.0, 79.2, 92.4, 99.0, 105.6, 118.8, 118.8, - 128.7, 132.0, 132.0, 138.6, 138.6, 56.1, 52.8, - 52.8, 56.1, 138.6, 138.6, 132.0, 132.0, 128.7, - 118.8, 118.8, 105.6, 99.0, 92.4, 79.2, 66.0, 52.8}; + 40.87, 61.31, 78.83, 90.51, 99.27, + 108.03, 113.87, 119.71, 122.63, 128.47, + 64.23, 55.47, 49.63, 49.63, 47.23, + 47.23, 49.63, 49.63, 55.47, 64.23, + 128.47, 122.63, 119.71, 113.87, 108.03, + 99.27, 90.51, 78.83, 61.31, 40.87}; const unsigned nStaves = 30; // y_lengths, staveOnFront and exactStaveFills follow this const double x_midpoint_spacing = 4.5; const std::vector x_midpoints = makeStaveXMidpoints(nStaves, x_midpoint_spacing); @@ -201,14 +218,46 @@ const double maxToleranceInner = 9.; // close but not directly at 10cm yet const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation const std::vector staveOnFront = makeStaveOnFront(nStaves); /* - * TODO: fill from the disk optimiser output. One entry per stave, in the same - * order as x_midpoints, and nStaves entries in total before - * useExactStavePlacement works. Each entry lists that stave's fills: + * From the disk optimiser: Rin 20, Rout 68, stave width 5.22, overlap 0.72, + * intrusion <= 9, extrusion <= 3, giving 99.84% filling with staves and + * 99.29% with modules. One entry per stave in x_midpoints order; a stave cut + * in two by the beam pipe has one fill either side of the hole. * - * {{{-52.80, {4, 4, 4, 3}}}}, // reaches across y=0 - * {{-63.84, {4, 4, 3}}, {8.34, {4, 4, 3}}}, // split by the beam pipe + * Each fill is anchored on its centre rather than the optimiser's own yStart, + * because FT3 puts a stackGap between modules and the optimiser does not. */ -const std::vector> exactStaveFills = {}; +const std::vector> exactStaveFills = { + {{-20.43, {4, 4, 3, 3}}}, // ID -15 + {{-30.65, {4, 4, 4, 3, 3, 3}}}, // ID -14 + {{-39.41, {4, 4, 4, 4, 4, 4, 3}}}, // ID -13 + {{-45.25, {4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -12 + {{-49.63, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -11 + {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -10 + {{-56.93, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -9 + {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -8 + {{-61.31, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -7 + {{-64.23, {4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}}, // ID -6 + {{-64.84, {4, 4, 4, 4, 3, 3}}, {0.62, {4, 4, 4, 4, 3, 3}}}, // ID -5 + {{-66.339, {4, 4, 4, 4, 3}}, {10.879, {4, 4, 4, 4, 3}}}, // ID -4 + {{-66.904, {4, 4, 3, 3, 3}}, {17.284, {4, 4, 3, 3, 3}}}, // ID -3 + {{-67.833, {4, 4, 3, 3, 3}}, {18.213, {4, 4, 3, 3, 3}}}, // ID -2 + {{-67.147, {4, 3, 3, 3, 3}}, {20.96, {4, 3, 3, 3, 3}}}, // ID -1 + {{-67.66, {4, 3, 3, 3, 3}}, {20.447, {4, 3, 3, 3, 3}}}, // ID +1 + {{-67.833, {4, 4, 3, 3, 3}}, {18.213, {4, 4, 3, 3, 3}}}, // ID +2 + {{-66.904, {4, 4, 3, 3, 3}}, {17.284, {4, 4, 3, 3, 3}}}, // ID +3 + {{-66.339, {4, 4, 4, 4, 3}}, {10.879, {4, 4, 4, 4, 3}}}, // ID +4 + {{-64.84, {4, 4, 4, 4, 3, 3}}, {0.62, {4, 4, 4, 4, 3, 3}}}, // ID +5 + {{-64.23, {4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}}, // ID +6 + {{-61.31, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +7 + {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +8 + {{-56.93, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +9 + {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +10 + {{-49.63, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +11 + {{-45.25, {4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +12 + {{-39.41, {4, 4, 4, 4, 4, 4, 3}}}, // ID +13 + {{-30.65, {4, 4, 4, 3, 3, 3}}}, // ID +14 + {{-20.43, {4, 4, 3, 3}}}, // ID +15 +}; } // namespace OT_StavePositions namespace ML_StavePositions From 0982d6cccd693fe10f810b212b47e65599866438 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Mon, 28 Sep 2026 17:06:47 +0200 Subject: [PATCH 05/12] add new optimised tiling to FT3ModuleConstants. Now has active coverage area of 97.25% in the nominal region --- .../FT3Simulation/FT3ModuleConstants.h | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index b0b964e286d94..8057a3892dc2c 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -48,8 +48,8 @@ namespace o2::ft3::ModuleConstants const double single_sensor_width = 2.5; const double single_sensor_height = 2.9; const double inactive_width = 0.2; -const double sensor2x1_gap = 0.02; // both between L&R sensors in 2x1, and between two 2x1s -const double stackGap = sensor2x1_gap; // gap between 2xN module stacks +const double sensor2x1_gap = 0.015; // between L&R sensors in 2x1, and between sensors in a stack +const double stackGap = 0.035; // gap between 2xN module stacks const double active_width = single_sensor_width - inactive_width; const double active_height = single_sensor_height; @@ -189,28 +189,26 @@ namespace OT_StavePositions * Do NOT add any zero midpoints, this is taken off separately. */ const std::map> staveID_to_y_midpoint = { - {-5, {32.73, true}}, - {-4, {38.609, true}}, - {-3, {42.094, true}}, - {-2, {43.023, true}}, - {-1, {44.054, true}}, - {1, {44.053, true}}, - {2, {43.023, true}}, - {3, {42.094, true}}, - {4, {38.609, true}}, - {5, {32.73, true}}}; + {-4, {39.679, true}}, + {-3, {42.065, true}}, + {-2, {42.991, true}}, + {-1, {43.772, true}}, + {1, {43.772, true}}, + {2, {42.991, true}}, + {3, {42.065, true}}, + {4, {39.679, true}}}; /* * Length of one stave piece: for a stave in staveID_to_y_midpoint that is one * of its two pieces, otherwise the whole stave centred on y=0. Sized to cover * the modules in exactStaveFills, with 1 mm of margin. */ const std::vector y_lengths = { - 40.87, 61.31, 78.83, 90.51, 99.27, - 108.03, 113.87, 119.71, 122.63, 128.47, - 64.23, 55.47, 49.63, 49.63, 47.23, - 47.23, 49.63, 49.63, 55.47, 64.23, - 128.47, 122.63, 119.71, 113.87, 108.03, - 99.27, 90.51, 78.83, 61.31, 40.87}; + 41, 61.35, 73.03, 87.63, 96.39, + 105.14, 110.99, 116.82, 122.67, 125.58, + 128.5, 52.59, 49.67, 49.67, 46.78, + 46.78, 49.67, 49.67, 52.59, 128.5, + 125.58, 122.67, 116.82, 110.99, 105.14, + 96.39, 87.63, 73.03, 61.35, 41}; const unsigned nStaves = 30; // y_lengths, staveOnFront and exactStaveFills follow this const double x_midpoint_spacing = 4.5; const std::vector x_midpoints = makeStaveXMidpoints(nStaves, x_midpoint_spacing); @@ -227,36 +225,36 @@ const std::vector staveOnFront = makeStaveOnFront(nStaves); * because FT3 puts a stackGap between modules and the optimiser does not. */ const std::vector> exactStaveFills = { - {{-20.43, {4, 4, 3, 3}}}, // ID -15 + {{-20.476, {4, 4, 3, 3}}}, // ID -15 {{-30.65, {4, 4, 4, 3, 3, 3}}}, // ID -14 - {{-39.41, {4, 4, 4, 4, 4, 4, 3}}}, // ID -13 - {{-45.25, {4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -12 - {{-49.63, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -11 - {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -10 - {{-56.93, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -9 - {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -8 - {{-61.31, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -7 - {{-64.23, {4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}}, // ID -6 - {{-64.84, {4, 4, 4, 4, 3, 3}}, {0.62, {4, 4, 4, 4, 3, 3}}}, // ID -5 - {{-66.339, {4, 4, 4, 4, 3}}, {10.879, {4, 4, 4, 4, 3}}}, // ID -4 - {{-66.904, {4, 4, 3, 3, 3}}, {17.284, {4, 4, 3, 3, 3}}}, // ID -3 - {{-67.833, {4, 4, 3, 3, 3}}, {18.213, {4, 4, 3, 3, 3}}}, // ID -2 - {{-67.147, {4, 3, 3, 3, 3}}, {20.96, {4, 3, 3, 3, 3}}}, // ID -1 - {{-67.66, {4, 3, 3, 3, 3}}, {20.447, {4, 3, 3, 3, 3}}}, // ID +1 - {{-67.833, {4, 4, 3, 3, 3}}, {18.213, {4, 4, 3, 3, 3}}}, // ID +2 - {{-66.904, {4, 4, 3, 3, 3}}, {17.284, {4, 4, 3, 3, 3}}}, // ID +3 - {{-66.339, {4, 4, 4, 4, 3}}, {10.879, {4, 4, 4, 4, 3}}}, // ID +4 - {{-64.84, {4, 4, 4, 4, 3, 3}}, {0.62, {4, 4, 4, 4, 3, 3}}}, // ID +5 - {{-64.23, {4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}}, // ID +6 - {{-61.31, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +7 - {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +8 - {{-56.93, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +9 - {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +10 - {{-49.63, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +11 - {{-45.25, {4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +12 - {{-39.41, {4, 4, 4, 4, 4, 4, 3}}}, // ID +13 + {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID -13 + {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -12 + {{-48.17, {4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -11 + {{-52.5425, {4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -10 + {{-55.4675, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -9 + {{-58.3825, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -8 + {{-61.3075, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -7 + {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -6 + {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -5 + {{-65.946, {4, 4, 4, 3, 3}}, {13.411, {4, 4, 4, 3, 3}}}, // ID -4 + {{-66.8749, {4, 4, 3, 3, 3}}, {17.2549, {4, 4, 3, 3, 3}}}, // ID -3 + {{-67.8015, {4, 4, 3, 3, 3}}, {18.1815, {4, 4, 3, 3, 3}}}, // ID -2 + {{-67.1397, {4, 4, 4, 4}}, {20.4051, {4, 4, 4, 4}}}, // ID -1 + {{-67.0901, {4, 4, 4, 4}}, {20.4547, {4, 4, 4, 4}}}, // ID +1 + {{-67.8015, {4, 4, 3, 3, 3}}, {18.1815, {4, 4, 3, 3, 3}}}, // ID +2 + {{-66.8749, {4, 4, 3, 3, 3}}, {17.2549, {4, 4, 3, 3, 3}}}, // ID +3 + {{-65.946, {4, 4, 4, 3, 3}}, {13.411, {4, 4, 4, 3, 3}}}, // ID +4 + {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +5 + {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +6 + {{-61.3075, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +7 + {{-58.3825, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +8 + {{-55.4675, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +9 + {{-52.5425, {4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +10 + {{-48.17, {4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +11 + {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +12 + {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID +13 {{-30.65, {4, 4, 4, 3, 3, 3}}}, // ID +14 - {{-20.43, {4, 4, 3, 3}}}, // ID +15 + {{-20.379, {4, 4, 3, 3}}}, // ID +15 }; } // namespace OT_StavePositions From 91a4fcaa985d13a7e49b1cab0f4f58c6f1847652 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Mon, 28 Sep 2026 18:28:57 +0200 Subject: [PATCH 06/12] make exact stave placement default --- .../ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h index 0fbea7d1a5e76..a4357bf8b8995 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/base/include/FT3Base/FT3BaseParam.h @@ -52,7 +52,7 @@ struct FT3BaseParam : public o2::conf::ConfigurableParamHelper { * with the stack sizes in kSensorsPerStack. The tabulated layout is taken as * given: none of the radial tolerances above are applied to it. */ - bool useExactStavePlacement = false; + bool useExactStavePlacement = true; // Draw reference circles at inner and outer radius of stave layer, for visualisation bool drawReferenceCircles = false; From 24fe222b7ae524112f4017230ed48c0fc1716cf5 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Tue, 29 Sep 2026 15:24:46 +0200 Subject: [PATCH 07/12] update FT3ModuleConstants.h with original tiling again -- with 150um sensor gaps, 350um module gap. Change 2x1 sensor size with the gap as well and fix placement accordingly --- .../FT3Simulation/FT3ModuleConstants.h | 111 +++++++++--------- .../TRKFT3/FT3/simulation/src/FT3Module.cxx | 4 +- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index 8057a3892dc2c..8a7f41467d943 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -32,30 +32,29 @@ namespace o2::ft3::ModuleConstants * * |<- 25mm ->||<- 25mm ->| * _______________________ - * ------------------------ 0.2mm gap above + * ------------------------ 0.15mm gap above * | | || | | * | | || | | * | | || | | * | | || | | 29mm sensor height * | | || | | * | | || | | - * ------------------------ + * ------------------------ 0.15mm gap below * ^ * | - * 0.2mm gap in the middle + * 0.15mm gap in the middle */ // First set all layout constants for the rest of the function const double single_sensor_width = 2.5; const double single_sensor_height = 2.9; const double inactive_width = 0.2; -const double sensor2x1_gap = 0.015; // between L&R sensors in 2x1, and between sensors in a stack +const double sensor2x1_gap = 0.015; // gap between L&R sensors in 2x1, and between sensors in a stack const double stackGap = 0.035; // gap between 2xN module stacks const double active_width = single_sensor_width - inactive_width; const double active_height = single_sensor_height; -const double sensor2x1_width = 2 * single_sensor_width; -const double sensor2x1_active_width = 2 * active_width; +const double sensor2x1_width = 2 * single_sensor_width + sensor2x1_gap; const double sensor2x1_height = single_sensor_height; const std::vector kSensorsPerStack = {4, 2, 1}; inline const double getStackHeight(unsigned nSensorsPerStack) @@ -189,72 +188,72 @@ namespace OT_StavePositions * Do NOT add any zero midpoints, this is taken off separately. */ const std::map> staveID_to_y_midpoint = { - {-4, {39.679, true}}, - {-3, {42.065, true}}, - {-2, {42.991, true}}, - {-1, {43.772, true}}, - {1, {43.772, true}}, - {2, {42.991, true}}, - {3, {42.065, true}}, - {4, {39.679, true}}}; + {-4, {35.659, true}}, + {-3, {41.735, true}}, + {-2, {42.882, true}}, + {-1, {43.761, true}}, + {1, {43.761, true}}, + {2, {42.882, true}}, + {3, {41.735, true}}, + {4, {35.659, true}}}; /* * Length of one stave piece: for a stave in staveID_to_y_midpoint that is one - * of its two pieces, otherwise the whole stave centred on y=0. Sized to cover - * the modules in exactStaveFills, with 1 mm of margin. + * of its two pieces, otherwise the whole stave centred on y=0. Trimmed to the + * modules in exactStaveFills, rounded up to the nearest 10 um. */ const std::vector y_lengths = { - 41, 61.35, 73.03, 87.63, 96.39, - 105.14, 110.99, 116.82, 122.67, 125.58, - 128.5, 52.59, 49.67, 49.67, 46.78, - 46.78, 49.67, 49.67, 52.59, 128.5, - 125.58, 122.67, 116.82, 110.99, 105.14, - 96.39, 87.63, 73.03, 61.35, 41}; -const unsigned nStaves = 30; // y_lengths, staveOnFront and exactStaveFills follow this -const double x_midpoint_spacing = 4.5; + 32.225, 58.401, 73.016, 87.611, + 99.29, 108.055, 113.886, 119.736, + 125.566, 128.481, 61.336, 49.655, + 49.656, 46.818, 46.818, 49.656, + 49.655, 61.336, 128.481, 125.566, + 119.736, 113.886, 108.055, 99.29, + 87.611, 73.016, 58.401, 32.225}; +const unsigned nStaves = 28; // y_lengths, staveOnFront and exactStaveFills follow this +const double x_midpoint_spacing = 4.92; const std::vector x_midpoints = makeStaveXMidpoints(nStaves, x_midpoint_spacing); const double maxToleranceInner = 9.; // close but not directly at 10cm yet const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation const std::vector staveOnFront = makeStaveOnFront(nStaves); /* - * From the disk optimiser: Rin 20, Rout 68, stave width 5.22, overlap 0.72, - * intrusion <= 9, extrusion <= 3, giving 99.84% filling with staves and - * 99.29% with modules. One entry per stave in x_midpoints order; a stave cut - * in two by the beam pipe has one fill either side of the hole. + * From the disk optimiser: Rin 20, Rout 68, stave width 5.22, overlap 0.30, + * intrusion and extrusion <= 2 with 6 exception staves at 9 and 3, giving + * 99.37% filling with staves and 93.48% with active silicon. One entry per + * stave in x_midpoints order; a stave cut in two by the beam pipe has one + * fill either side of the hole. * - * Each fill is anchored on its centre rather than the optimiser's own yStart, - * because FT3 puts a stackGap between modules and the optimiser does not. + * yStart is the bottom of the first SENSOR, which is half a module gap above + * the bottom of the module the optimiser reports. */ const std::vector> exactStaveFills = { - {{-20.476, {4, 4, 3, 3}}}, // ID -15 - {{-30.65, {4, 4, 4, 3, 3, 3}}}, // ID -14 - {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID -13 - {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -12 - {{-48.17, {4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -11 - {{-52.5425, {4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -10 - {{-55.4675, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -9 - {{-58.3825, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -8 - {{-61.3075, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -7 + {{-15.9953, {4, 4, 3}}}, // ID -14 + {{-29.1825, {4, 4, 4, 4, 4}}}, // ID -13 + {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID -12 + {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -11 + {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -10 + {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -9 + {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -8 + {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -7 {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -6 {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -5 - {{-65.946, {4, 4, 4, 3, 3}}, {13.411, {4, 4, 4, 3, 3}}}, // ID -4 - {{-66.8749, {4, 4, 3, 3, 3}}, {17.2549, {4, 4, 3, 3, 3}}}, // ID -3 - {{-67.8015, {4, 4, 3, 3, 3}}, {18.1815, {4, 4, 3, 3, 3}}}, // ID -2 - {{-67.1397, {4, 4, 4, 4}}, {20.4051, {4, 4, 4, 4}}}, // ID -1 - {{-67.0901, {4, 4, 4, 4}}, {20.4547, {4, 4, 4, 4}}}, // ID +1 - {{-67.8015, {4, 4, 3, 3, 3}}, {18.1815, {4, 4, 3, 3, 3}}}, // ID +2 - {{-66.8749, {4, 4, 3, 3, 3}}, {17.2549, {4, 4, 3, 3, 3}}}, // ID +3 - {{-65.946, {4, 4, 4, 3, 3}}, {13.411, {4, 4, 4, 3, 3}}}, // ID +4 + {{-66.3087, {4, 4, 4, 3, 3, 3}}, {5.0087, {4, 4, 4, 3, 3, 3}}}, // ID -4 + {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID -3 + {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID -2 + {{-67.0542, {4, 4, 4, 4}}, {20.4669, {4, 4, 4, 4}}}, // ID -1 + {{-67.1519, {4, 4, 4, 4}}, {20.3692, {4, 4, 4, 4}}}, // ID +1 + {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID +2 + {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID +3 + {{-66.3087, {4, 4, 4, 3, 3, 3}}, {5.0087, {4, 4, 4, 3, 3, 3}}}, // ID +4 {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +5 {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +6 - {{-61.3075, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +7 - {{-58.3825, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +8 - {{-55.4675, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +9 - {{-52.5425, {4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +10 - {{-48.17, {4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +11 - {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +12 - {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID +13 - {{-30.65, {4, 4, 4, 3, 3, 3}}}, // ID +14 - {{-20.379, {4, 4, 3, 3}}}, // ID +15 + {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +7 + {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +8 + {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +9 + {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +10 + {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +11 + {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID +12 + {{-29.1825, {4, 4, 4, 4, 4}}}, // ID +13 + {{-16.0947, {4, 4, 3}}}, // ID +14 }; } // namespace OT_StavePositions diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx index 630a607375019..62464188ae914 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx @@ -820,12 +820,12 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction double z_mid = z_offset_to_silicon * z_offset_multiplier + z_stave_shift; addSingleSensorVolume( motherVolume, layerNumber, direction, i_stave, sensor_count, - x_mid - Constants::active_width / 2 - Constants::sensor2x1_gap / 2, + x_mid - Constants::sensor2x1_width / 2, y_mid, z_mid, true); // right single sensor of the 2x1: place left edge half of sensor gap from center addSingleSensorVolume( motherVolume, layerNumber, direction, i_stave, sensor_count + 1, - x_mid + Constants::active_width / 2 + Constants::sensor2x1_gap / 2, + x_mid + Constants::sensor2x1_width / 2, y_mid, z_mid, false); // ------------ (2) Epoxy glue layer between silicon and copper (FPC) ------------ z_mid = z_offset_to_glue_Si * z_offset_multiplier + z_stave_shift; From 424a0ce56c5a87cc4658b5651d4180ec7bb6ee61 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Tue, 29 Sep 2026 17:02:40 +0200 Subject: [PATCH 08/12] make stave width explicit variable and use it to constrain its placement, rather than sensor width: this allowed for potential small ex/intrusions. --- .../include/FT3Simulation/FT3Module.h | 4 ++-- .../FT3Simulation/FT3ModuleConstants.h | 7 +++--- .../TRKFT3/FT3/simulation/src/FT3Layer.cxx | 4 +++- .../TRKFT3/FT3/simulation/src/FT3Module.cxx | 22 ++++++++----------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h index 52e2922368f72..e071e5cbd521b 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Module.h @@ -86,8 +86,8 @@ class FT3Module // Helper functions void fill_stave_greedy( - PosNegPositionTypes& y_positions, double Rin, double Rout, - double x_left, unsigned kSensorStack, PositionRangeType y_range, + PosNegPositionTypes& y_positions, unsigned kSensorStack, + PositionRangeType y_range, std::pair& absAllowedYRange); PositionTypes fill_stave_exact(const std::vector& fills); diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index 8a7f41467d943..e4c63deeeee72 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -47,7 +47,7 @@ namespace o2::ft3::ModuleConstants // First set all layout constants for the rest of the function const double single_sensor_width = 2.5; const double single_sensor_height = 2.9; -const double inactive_width = 0.2; +const double inactive_width = 0.15; const double sensor2x1_gap = 0.015; // gap between L&R sensors in 2x1, and between sensors in a stack const double stackGap = 0.035; // gap between 2xN module stacks @@ -111,8 +111,9 @@ const double effectiveCarbonThickness_Stave = 0.02; // foam + shell const double staveOpeningAngle = 60 * TMath::DegToRad(); const double sinTheta = TMath::Sin(staveOpeningAngle / 2); const double alpha = TMath::Pi() / 2 - staveOpeningAngle / 2; // bottom angles -const double staveSensorGap = 0.1; // 2mm padding on each side when sensor is glued -const double staveTriangleHeight = (sensor2x1_width + 2 * staveSensorGap) / 2.0 / tan(staveOpeningAngle / 2.0); +const double staveSensorGap = 0.1025; // 1025µm padding on each side: 52.2mm stave width +const double staveWidth = sensor2x1_width + 2 * staveSensorGap; +const double staveTriangleHeight = staveWidth / 2.0 / tan(staveOpeningAngle / 2.0); /* * Now describe the offset of every other stave in z to avoid overlaps * ______ ______ diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx index 90962a5e6b0fc..fa8f278d875df 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx @@ -423,8 +423,10 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) double z_local_offset = z_layer_thickness / 2.0; // ensure staves fully encapsulated in the layer volume, // but don't cross out of max nominal radii of 38.5cm & 71.5cm respectively (3.5cm tolerance) + // Allow 9cm inward for OT: + double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; // MvL: try 70.5 // 2.5 cm tolerance instead - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.2, mOuterRadius + 2.49, z_layer_thickness / 2); + TGeoTube* layer = new TGeoTube(innerRadiusForAirTube - 0.2, mOuterRadius + 2.49, z_layer_thickness / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); if (ft3Params.drawReferenceCircles) { diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx index 62464188ae914..aebab49ba9dcf 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Module.cxx @@ -83,17 +83,14 @@ std::pair calculate_y_range( * y_positions: a pair of vectors, where each vector contains pairs of * y position and stack height for the positive and negative y positions respectively. * This argument will be appended with the new sensor positions and stack heights. - * Rout: the outer radius of the layer - * Rin: the inner radius of the layer - * x_left: the x position of the left edge of the sensor to be placed * kSensorStack: the number of sensors to be stacked on top of each other * y_ranges: the y positions to start and end placing sensors, * for positive and negative y respectively * absAllowedYRange: the absolute y range allowed for placing sensors, * used to cut placement if they go past allowed tolerances */ -void FT3Module::fill_stave_greedy(PosNegPositionTypes& y_positions, double Rin, double Rout, - double x_left, unsigned kSensorStack, PositionRangeType y_ranges, +void FT3Module::fill_stave_greedy(PosNegPositionTypes& y_positions, unsigned kSensorStack, + PositionRangeType y_ranges, std::pair& absAllowedYRange) { // start with upper half of the stave, then mirror to the bottom half @@ -648,10 +645,10 @@ void FT3Module::build_staves_greedy( * (2) The inner tolerance is large enough to allow stave placement as wished * a) AND the given stave midpoint is above the inner radius */ - double x_left = staveConfig.x_midpoints[i_stave] - Constants::sensor2x1_width / 2; - double x_right = x_left + Constants::sensor2x1_width; + double x_left_stave = staveConfig.x_midpoints[i_stave] - Constants::staveWidth / 2; + double x_right_stave = x_left_stave + Constants::staveWidth; std::pair absAllowedYRange = - calculate_y_range(x_left, x_right, Rin, Rout); + calculate_y_range(x_left_stave, x_right_stave, Rin, Rout); /* * Shift allowed range by tolerance. Note that both values in the range must @@ -679,9 +676,8 @@ void FT3Module::build_staves_greedy( // now add the sensor positions on the stave for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) { unsigned nModulesCurr = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size(); - fill_stave_greedy(y_positionsPosNeg.back(), Rin, Rout, x_left, - Constants::kSensorsPerStack[i_kSens], y_ranges, - absAllowedYRange); + fill_stave_greedy(y_positionsPosNeg.back(), Constants::kSensorsPerStack[i_kSens], + y_ranges, absAllowedYRange); unsigned nModulesAdded = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size() - nModulesCurr; nSensorStackCountPerStave[i_stave][i_kSens] = nModulesAdded; nSensorStackTotal[i_kSens] += nModulesAdded; @@ -820,12 +816,12 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction double z_mid = z_offset_to_silicon * z_offset_multiplier + z_stave_shift; addSingleSensorVolume( motherVolume, layerNumber, direction, i_stave, sensor_count, - x_mid - Constants::sensor2x1_width / 2, + x_mid - Constants::sensor2x1_gap / 2 - Constants::active_width / 2, y_mid, z_mid, true); // right single sensor of the 2x1: place left edge half of sensor gap from center addSingleSensorVolume( motherVolume, layerNumber, direction, i_stave, sensor_count + 1, - x_mid + Constants::sensor2x1_width / 2, + x_mid + Constants::sensor2x1_gap / 2 + Constants::active_width / 2, y_mid, z_mid, false); // ------------ (2) Epoxy glue layer between silicon and copper (FPC) ------------ z_mid = z_offset_to_glue_Si * z_offset_multiplier + z_stave_shift; From a6f28f8f51a2a3d9e040aaf872a6f9c39c171cc3 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Tue, 29 Sep 2026 15:03:28 +0000 Subject: [PATCH 09/12] Please consider the following formatting changes --- .../include/FT3Simulation/FT3Materials.h | 13 ++--- .../FT3Simulation/FT3ModuleConstants.h | 56 +++++++++---------- .../TRKFT3/FT3/simulation/src/FT3Layer.cxx | 2 +- .../FT3/simulation/src/FT3Materials.cxx | 1 - 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h index 1d323dd6dfd04..06752bf267db1 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3Materials.h @@ -9,7 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. - /// \file FT3Materials.h /// \brief Materials of the FT3 detector, and access to the media made from them @@ -71,17 +70,17 @@ using ComponentArray = std::array; struct MaterialProperties { const char* name; - int colour; // ROOT colour every volume made of this material is drawn in - float density; // g/cm3 + int colour; // ROOT colour every volume made of this material is drawn in + float density; // g/cm3 // Radiation and nuclear interaction length, cm. Only single elements carry // them: Mixture() derives both from the composition and takes no such // arguments. A non-positive value lets the transport engine compute it. float radl; float absl; - int nComponents; // 0: single element; > 0: mixture by weight; < 0: mixture by atom count - ComponentArray a; // mass numbers; only a[0] is used for a single element - ComponentArray z; // atomic numbers; only z[0] is used for a single element - ComponentArray w; // weight fractions or atom counts; unused for a single element + int nComponents; // 0: single element; > 0: mixture by weight; < 0: mixture by atom count + ComponentArray a; // mass numbers; only a[0] is used for a single element + ComponentArray z; // atomic numbers; only z[0] is used for a single element + ComponentArray w; // weight fractions or atom counts; unused for a single element TrackingParams tracking; }; diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index e4c63deeeee72..c14d5fd42d719 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -48,8 +48,8 @@ namespace o2::ft3::ModuleConstants const double single_sensor_width = 2.5; const double single_sensor_height = 2.9; const double inactive_width = 0.15; -const double sensor2x1_gap = 0.015; // gap between L&R sensors in 2x1, and between sensors in a stack -const double stackGap = 0.035; // gap between 2xN module stacks +const double sensor2x1_gap = 0.015; // gap between L&R sensors in 2x1, and between sensors in a stack +const double stackGap = 0.035; // gap between 2xN module stacks const double active_width = single_sensor_width - inactive_width; const double active_height = single_sensor_height; @@ -227,34 +227,34 @@ const std::vector staveOnFront = makeStaveOnFront(nStaves); * the bottom of the module the optimiser reports. */ const std::vector> exactStaveFills = { - {{-15.9953, {4, 4, 3}}}, // ID -14 - {{-29.1825, {4, 4, 4, 4, 4}}}, // ID -13 - {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID -12 - {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -11 - {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -10 - {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -9 - {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -8 - {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -7 - {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -6 - {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -5 + {{-15.9953, {4, 4, 3}}}, // ID -14 + {{-29.1825, {4, 4, 4, 4, 4}}}, // ID -13 + {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID -12 + {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -11 + {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -10 + {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -9 + {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -8 + {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -7 + {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -6 + {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -5 {{-66.3087, {4, 4, 4, 3, 3, 3}}, {5.0087, {4, 4, 4, 3, 3, 3}}}, // ID -4 - {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID -3 - {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID -2 - {{-67.0542, {4, 4, 4, 4}}, {20.4669, {4, 4, 4, 4}}}, // ID -1 - {{-67.1519, {4, 4, 4, 4}}, {20.3692, {4, 4, 4, 4}}}, // ID +1 - {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID +2 - {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID +3 + {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID -3 + {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID -2 + {{-67.0542, {4, 4, 4, 4}}, {20.4669, {4, 4, 4, 4}}}, // ID -1 + {{-67.1519, {4, 4, 4, 4}}, {20.3692, {4, 4, 4, 4}}}, // ID +1 + {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID +2 + {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID +3 {{-66.3087, {4, 4, 4, 3, 3, 3}}, {5.0087, {4, 4, 4, 3, 3, 3}}}, // ID +4 - {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +5 - {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +6 - {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +7 - {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +8 - {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +9 - {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +10 - {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +11 - {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID +12 - {{-29.1825, {4, 4, 4, 4, 4}}}, // ID +13 - {{-16.0947, {4, 4, 3}}}, // ID +14 + {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +5 + {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +6 + {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +7 + {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +8 + {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +9 + {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +10 + {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +11 + {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID +12 + {{-29.1825, {4, 4, 4, 4, 4}}}, // ID +13 + {{-16.0947, {4, 4, 3}}}, // ID +14 }; } // namespace OT_StavePositions diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx index fa8f278d875df..7205a0a15a4cb 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx @@ -424,7 +424,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) // ensure staves fully encapsulated in the layer volume, // but don't cross out of max nominal radii of 38.5cm & 71.5cm respectively (3.5cm tolerance) // Allow 9cm inward for OT: - double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; + double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; // MvL: try 70.5 // 2.5 cm tolerance instead TGeoTube* layer = new TGeoTube(innerRadiusForAirTube - 0.2, mOuterRadius + 2.49, z_layer_thickness / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx index 7d35a2d39beb5..4097af9d838bb 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Materials.cxx @@ -9,7 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. - /// \file FT3Materials.cxx /// \brief Access to the media created from the FT3 material map From c29e5e802b869fba65f6c932c31ef1aab25912b0 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Tue, 29 Sep 2026 17:13:54 +0200 Subject: [PATCH 10/12] allow a bit more radius for OT than ML, so that we fit all the exceptions from the tiling that had -9, +3 as the maximum placements past the nominal radius --- .../ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx index 7205a0a15a4cb..54a0de457c836 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx @@ -423,10 +423,12 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) double z_local_offset = z_layer_thickness / 2.0; // ensure staves fully encapsulated in the layer volume, // but don't cross out of max nominal radii of 38.5cm & 71.5cm respectively (3.5cm tolerance) - // Allow 9cm inward for OT: - double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; + // Allow 3, 9cm outward, inward for OT: + double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; + double outerRadiusForAirTube = mIsMiddleLayer ? mOuterRadius + 2.0 : mOuterRadius + 3.0; // MvL: try 70.5 // 2.5 cm tolerance instead - TGeoTube* layer = new TGeoTube(innerRadiusForAirTube - 0.2, mOuterRadius + 2.49, z_layer_thickness / 2); + TGeoTube* layer = new TGeoTube(innerRadiusForAirTube - 0.2, outerRadiusForAirTube, + z_layer_thickness / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); if (ft3Params.drawReferenceCircles) { From 4f26ff855a5de8b6a384c49668c5b5caa5a21b85 Mon Sep 17 00:00:00 2001 From: Justus Rudolph Date: Wed, 30 Sep 2026 10:45:53 +0200 Subject: [PATCH 11/12] (maybe) temporarily move the OT services to 71cm, to make space for the air tube at 3cm extrusion, since the outermost staves extrude that far. Also make max outer radius back to 2.5cm for the ML --- .../Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx | 2 +- .../Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx index 54a0de457c836..afaed56db681a 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx @@ -425,7 +425,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) // but don't cross out of max nominal radii of 38.5cm & 71.5cm respectively (3.5cm tolerance) // Allow 3, 9cm outward, inward for OT: double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; - double outerRadiusForAirTube = mIsMiddleLayer ? mOuterRadius + 2.0 : mOuterRadius + 3.0; + double outerRadiusForAirTube = mIsMiddleLayer ? mOuterRadius + 2.5 : mOuterRadius + 3.0; // MvL: try 70.5 // 2.5 cm tolerance instead TGeoTube* layer = new TGeoTube(innerRadiusForAirTube - 0.2, outerRadiusForAirTube, z_layer_thickness / 2); diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx index c51277665ba72..69f44998fc106 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx @@ -995,7 +995,7 @@ void TRKServices::createOTServicesPeacock(TGeoVolume* motherVolume) float zLengthOuterBarrelTubeServices = 215.f; // cm, IA, May 11, 2026: temporary length (?) // geometry of service "tubes" for OT disks - float rMinOuterDiskServices = 70.5f; // cm + float rMinOuterDiskServices = 71.f; // cm float zStartOuterDiskServices = 149.f; // cm float zLengthOuterDiskServices = 201.f; // cm From de9fb027876cd4f755b50e4d87323d6ded73fe15 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Wed, 30 Sep 2026 08:46:38 +0000 Subject: [PATCH 12/12] Please consider the following formatting changes --- .../Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx | 2 +- .../Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx index afaed56db681a..b70afc9337ab9 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/FT3/simulation/src/FT3Layer.cxx @@ -424,7 +424,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) // ensure staves fully encapsulated in the layer volume, // but don't cross out of max nominal radii of 38.5cm & 71.5cm respectively (3.5cm tolerance) // Allow 3, 9cm outward, inward for OT: - double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; + double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0; double outerRadiusForAirTube = mIsMiddleLayer ? mOuterRadius + 2.5 : mOuterRadius + 3.0; // MvL: try 70.5 // 2.5 cm tolerance instead TGeoTube* layer = new TGeoTube(innerRadiusForAirTube - 0.2, outerRadiusForAirTube, diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx index 69f44998fc106..a5f897ec9ef4f 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKServices.cxx @@ -995,7 +995,7 @@ void TRKServices::createOTServicesPeacock(TGeoVolume* motherVolume) float zLengthOuterBarrelTubeServices = 215.f; // cm, IA, May 11, 2026: temporary length (?) // geometry of service "tubes" for OT disks - float rMinOuterDiskServices = 71.f; // cm + float rMinOuterDiskServices = 71.f; // cm float zStartOuterDiskServices = 149.f; // cm float zLengthOuterDiskServices = 201.f; // cm