diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index 6077d9e5f9839..ef4d5657a1b4f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -12,11 +12,14 @@ #ifndef ALICEO2_TRK_LAYER_H #define ALICEO2_TRK_LAYER_H +#include "TRKBase/Specs.h" +#include "TRKBase/TRKBaseParam.h" #include + #include -#include "TRKBase/TRKBaseParam.h" -#include "TRKBase/Specs.h" +#include +#include namespace o2 { @@ -68,7 +71,7 @@ class TRKSegmentedLayer : public TRKCylindricalLayer { public: TRKSegmentedLayer() = default; - TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKSegmentedLayer() override = default; TGeoVolume* createSensor() override; @@ -80,7 +83,10 @@ class TRKSegmentedLayer : public TRKCylindricalLayer void createLayer(TGeoVolume* motherVolume) override = 0; protected: + float mTiltAngle; int mNumberOfModules; + int mNumberOfStaves; + bool mIsFlipped = false; // Fixed parameters for the layer, to be set based on the specifications of the chip and module static constexpr double sChipWidth = constants::moduleMLOT::chip::width; @@ -93,6 +99,12 @@ class TRKSegmentedLayer : public TRKCylindricalLayer // TGeo objects outside logical volumes can cause errors static constexpr float sLogicalVolumeThickness = 1.3; + // For the segmented layers, because of tilting and staggering the bounding radii can be different + // from the inner radius and inner radius + thickness. + // This function calculates the bounding radii based on the geometry of the stave and the tilt angle, + // to ensure that the layer volume is large enough to contain all the staves without overlaps. + virtual std::pair getBoundingRadii(double staveWidth) const; + ClassDefOverride(TRKSegmentedLayer, 0); }; @@ -100,14 +112,20 @@ class TRKMLLayer : public TRKSegmentedLayer { public: TRKMLLayer() = default; - TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKMLLayer(int layerNumber, std::string layerName, float rInn, float staggerOffset, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKMLLayer() override = default; TGeoVolume* createStave() override; void createLayer(TGeoVolume* motherVolume) override; private: + float mStaggerOffset; + static constexpr double sStaveWidth = constants::ML::width; + static constexpr int sFlippedLayerNumber = 3; + + // Override to account for the staggering offset present in specific ML layers + std::pair getBoundingRadii(double staveWidth) const override; ClassDefOverride(TRKMLLayer, 0); }; @@ -116,7 +134,7 @@ class TRKOTLayer : public TRKSegmentedLayer { public: TRKOTLayer() = default; - TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKOTLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKOTLayer() override = default; TGeoVolume* createStave() override; @@ -128,9 +146,12 @@ class TRKOTLayer : public TRKSegmentedLayer static constexpr double sInStaveOverlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true) overlap static constexpr double sStaveWidth = constants::OT::width - sInStaveOverlap; - ClassDefOverride(TRKOTLayer, 0) + // Override to account for the staggering offset present in OT layers + std::pair getBoundingRadii(double staveWidth) const override; + + ClassDefOverride(TRKOTLayer, 0); }; } // namespace trk } // namespace o2 -#endif // ALICEO2_TRK_LAYER_H \ No newline at end of file +#endif // ALICEO2_TRK_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 66c02a080e0b6..66ace4746d399 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -9,18 +9,19 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include - -#include -#include -#include +#include "TRKSimulation/Detector.h" #include "DetectorsBase/Stack.h" -#include "TRKSimulation/Hit.h" -#include "TRKSimulation/Detector.h" + #include "TRKBase/TRKBaseParam.h" +#include "TRKSimulation/Hit.h" #include "TRKSimulation/VDGeometryBuilder.h" #include "TRKSimulation/VDSensorRegistry.h" +#include +#include +#include + +#include #include #include @@ -105,14 +106,21 @@ void Detector::configMLOT() break; } case kSegmented: { + const std::vector tiltAngles{11.2f, 11.9f, 11.4f, 0.f, 0.f, 0.f, 0.f, 0.f}; + // const std::vector tiltAngles{10.f, 16.1f, 19.2f, 0.f, 0.f, 0.f, 0.f, 0.f}; + const std::vector nStaves{10, 14, 18, 26, 38, 32, 42, 56}; + // const std::vector nStaves{10, 16, 22, 26, 38, 32, 42, 56}; const std::vector nMods{10, 10, 10, 10, 10, 20, 20, 20}; + + const std::vector stagOffsets{0.f, 0.f, 0.f, 1.17f, 0.89f}; + LOGP(warning, "Loading segmented configuration for ALICE3 TRK"); for (int i{0}; i < 8; ++i) { std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); - if (i < 4) { - mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + if (i < 5) { + mLayers.push_back(std::make_unique(i, name, rInn[i], stagOffsets[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness)); } else { - mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + mLayers.push_back(std::make_unique(i, name, rInn[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness)); } } break; @@ -153,16 +161,66 @@ void Detector::configFromFile(std::string fileName) } std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount); + switch (trkPars.layoutMLOT) { - case kCylindrical: - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], MatBudgetParamMode::Thickness)); + case kCylindrical: { + // Cylindrical requires at least 3 parameters + if (tmpBuff.size() < 3) { + LOGP(fatal, "Invalid configuration for cylindrical layer {}: insufficient parameters.", layerCount); + } + + // Default mode is Thickness + MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + if (tmpBuff.size() >= 4) { + mode = static_cast(static_cast(tmpBuff[3])); + } + + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], mode)); break; + } case kSegmented: { - int nMods = static_cast(tmpBuff[1]); - if (layerCount < 4) { - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + // Expected column mapping in the text file (separated by \t): + // tmpBuff[0] = rInn + // tmpBuff[1] = thick + // tmpBuff[2] = tiltAngle + // tmpBuff[3] = nStaves + // tmpBuff[4] = nMods + // tmpBuff[5] = stagOffset (required ONLY for ML) + // tmpBuff[6] = matBudgetMode (optional, default = Thickness) + + // Base parameters for all segmented layers (at least 5 needed) + if (tmpBuff.size() < 5) { + LOGP(fatal, "Invalid configuration for segmented layer {}: missing base parameters.", layerCount); + } + + float rInn = tmpBuff[0]; + float thick = tmpBuff[1]; + float tiltAngle = tmpBuff[2]; + int nStaves = static_cast(tmpBuff[3]); + int nMods = static_cast(tmpBuff[4]); + + // Default mode is Thickness + MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + + if (layerCount < 5) { + // ML layers (0 to 4) require stagOffset (index 5) + if (tmpBuff.size() < 6) { + LOGP(fatal, "Invalid configuration for ML layer {}: stagOffset is missing.", layerCount); + } + float stagOffset = tmpBuff[5]; + + if (tmpBuff.size() >= 7) { + mode = static_cast(static_cast(tmpBuff[6])); + } + + mLayers.push_back(std::make_unique(layerCount, name, rInn, stagOffset, tiltAngle, nStaves, nMods, thick, mode)); } else { - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + // OT layers (5+) do NOT have stagOffset. The optional mode is at index 5. + if (tmpBuff.size() >= 6) { + mode = static_cast(static_cast(tmpBuff[5])); + } + + mLayers.push_back(std::make_unique(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, mode)); } break; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 39c7b3598d19b..7a4b7bef34e03 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -10,17 +10,21 @@ // or submit itself to any jurisdiction. #include "TRKSimulation/TRKLayer.h" -#include "TRKBase/GeometryTGeo.h" -#include "TRKBase/Specs.h" #include "Framework/Logger.h" -#include +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" #include +#include #include - #include +#include +#include +#include +#include + namespace o2 { namespace trk @@ -84,9 +88,10 @@ void TRKCylindricalLayer::createLayer(TGeoVolume* motherVolume) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mNumberOfModules(numberOfModules) +TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mTiltAngle(tiltAngle), mNumberOfStaves(numberOfStaves), mNumberOfModules(numberOfModules) { + assert(numberOfStaves % 2 == 0 && "Error: numberOfStaves must be even!"); } TGeoVolume* TRKSegmentedLayer::createSensor() @@ -132,22 +137,29 @@ TGeoVolume* TRKSegmentedLayer::createChip() TGeoVolume* sensVol = createSensor(); TGeoCombiTrans* transSens = new TGeoCombiTrans(); - // transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); - transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); - LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, transSens); TGeoVolume* deadVol = createDeadzone(); TGeoCombiTrans* transDead = new TGeoCombiTrans(); - // transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); - transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); - LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); - chipVol->AddNode(deadVol, 1, transDead); TGeoVolume* metalVol = createMetalStack(); TGeoCombiTrans* transMetal = new TGeoCombiTrans(); - // transMetal->SetTranslation(0, sSensorThickness / 2, 0); - transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + + if (!mIsFlipped) { + transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); + transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + } else { + transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transMetal->SetTranslation(0, sSensorThickness / 2, 0); + } + + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + + LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); + chipVol->AddNode(deadVol, 1, transDead); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); chipVol->AddNode(metalVol, 1, transMetal); @@ -186,11 +198,59 @@ TGeoVolume* TRKSegmentedLayer::createModule() return moduleVol; } +std::pair TRKSegmentedLayer::getBoundingRadii(double staveWidth) const +{ + const float avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + + /*const float deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); + + float radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); + float radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt);*/ + + const double alpha = TMath::DegToRad() * std::abs(mTiltAngle); + + // The maximum distance from the center is always the outer top corner + double u_max = avgRadius * std::sin(alpha) + staveSizeX / 2.0; + double v_max = avgRadius * std::cos(alpha) + staveSizeY / 2.0; + double radiusMax = std::sqrt(u_max * u_max + v_max * v_max); + + // The perpendicular distance from the center to the line where the inner face lies + double perpDistance = avgRadius * std::cos(alpha) - staveSizeY / 2.0; + + // The projection of the center along the width of the stave + double projDistance = avgRadius * std::sin(alpha); + + double radiusMin; + if (projDistance <= staveSizeX / 2.0) { + // The center projects directly inside the flat face. + // The closest point is on the face itself, not on the corner + radiusMin = perpDistance; + } else { + // The center projects outside the face. The closest point is the inner corner + double u_min = projDistance - staveSizeX / 2.0; + radiusMin = std::sqrt(u_min * u_min + perpDistance * perpDistance); + } + + // Add a 0.5 mm safety margin to prevent false-positive overlaps in ROOT's geometry checker caused by floating-point inaccuracies + const float precisionMargin = 0.05f; + + return {radiusMin - precisionMargin, radiusMax + precisionMargin}; +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, float staggerOffset, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, tiltAngle, numberOfStaves, numberOfModules, thickOrX2X0, mode), mStaggerOffset(staggerOffset) { + if (mLayerNumber == sFlippedLayerNumber) { + mOuterRadius = rInn; + mInnerRadius = rInn - mChipThickness; + mIsFlipped = true; + mStaggerOffset = -staggerOffset; + LOGP(info, "Layer {} is flipped: sensor and metal stack positions are switched", mLayerNumber); + } } TGeoVolume* TRKMLLayer::createStave() @@ -215,32 +275,43 @@ TGeoVolume* TRKMLLayer::createStave() void TRKMLLayer::createLayer(TGeoVolume* motherVolume) { + // Retrieve exact bounding boundaries and create the logical container volume + auto [rMin, rMax] = getBoundingRadii(sStaveWidth); + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + // TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoTube* layer = new TGeoTube(rMin, rMax, mLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow); // Compute the number of staves - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); - nStaves += nStaves % 2; // Require an even number of staves + // int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + // nStaves += nStaves % 2; // Require an even number of staves + + // Nominal average radii used as placement barycenters for the staves + const double avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const double avgRadiusOuter = avgRadiusInner + mStaggerOffset; // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; + double theta = 2. * TMath::Pi() / mNumberOfStaves; double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 2 * st)); double overlap = (theta1 - theta2) * mInnerRadius; - LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); + LOGP(info, "Creating a layer with {} staves and {} mm overlap", mNumberOfStaves, overlap * 10); - for (int iStave = 0; iStave < nStaves; iStave++) { + for (int iStave = 0; iStave < mNumberOfStaves; iStave++) { TGeoVolume* staveVol = createStave(); TGeoCombiTrans* trans = new TGeoCombiTrans(); - double theta = 360. * iStave / nStaves; - // TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 4, 0, 0); - TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 4, 0, 0); + // If the number of staves is a multiple of 4, rotate by half a stave to avoid having the first one exactly on the x + double phi = (mNumberOfStaves % 4 == 0) ? theta * (iStave + 0.5) : theta * iStave; + double phiDeg = phi * TMath::RadToDeg(); + TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); + // float trueRadius = (mLayerNumber == 3 || mLayerNumber == 4) ? (iStave % 2 == 0 ? mInnerRadius : mInnerRadius + mStaggerOffset) : mInnerRadius; + float trueRadius = (mLayerNumber == 3 || mLayerNumber == 4) ? (iStave % 2 == 0 ? avgRadiusInner : avgRadiusOuter) : avgRadiusInner; + trans->SetTranslation(trueRadius * std::cos(phi), trueRadius * std::sin(phi), 0); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } @@ -249,10 +320,67 @@ void TRKMLLayer::createLayer(TGeoVolume* motherVolume) motherVolume->AddNode(layerVol, 1, nullptr); } +std::pair TRKMLLayer::getBoundingRadii(double staveWidth) const +{ + // Get the baseline RMin from the base class + auto [defaultRadiusMin, defaultRadiusMax] = TRKSegmentedLayer::getBoundingRadii(staveWidth); + + // If we are not in the staggered layers, return the baseline values + if (mLayerNumber != 3 && mLayerNumber != 4) { + return {defaultRadiusMin, defaultRadiusMax}; + } + + /*// For staggered layers, we must recalculate RMax based on the outer shifted row + const float avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const float avgRadiusOuter = avgRadiusInner + mStaggerOffset; + + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + + const float deltaForTiltOuter = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); + + const float radiusMax = std::sqrt(avgRadiusOuter * avgRadiusOuter + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadiusOuter * 2. * deltaForTiltOuter);*/ + + const float avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const float avgRadiusStaggered = avgRadiusInner + mStaggerOffset; + + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + const float alpha = TMath::DegToRad() * std::abs(mTiltAngle); + + const float precisionMargin = 0.05f; + + // If the layer is NOT flipped (e.g., Layer 4), the stagger goes outwards + // Therefore, we must recalculate only the maximum radius based on the outer shifted row + if (!mIsFlipped) { + float u_max = avgRadiusStaggered * std::sin(alpha) + staveSizeX / 2.0; + float v_max = avgRadiusStaggered * std::cos(alpha) + staveSizeY / 2.0; + float radiusMax = std::sqrt(u_max * u_max + v_max * v_max); + + return {defaultRadiusMin, radiusMax + precisionMargin}; + } + // If the layer IS flipped (e.g., Layer 3), the stagger goes inwards + // Therefore, we must recalculate only the minimum radius based on the inner shifted row + else { + double perpDistance = avgRadiusStaggered * std::cos(alpha) - staveSizeY / 2.0; + double projDistance = avgRadiusStaggered * std::sin(alpha); + double newRadiusMin; + + if (projDistance <= staveSizeX / 2.0) { + newRadiusMin = perpDistance; + } else { + double u_min = projDistance - staveSizeX / 2.0; + newRadiusMin = std::sqrt(u_min * u_min + perpDistance * perpDistance); + } + + return {newRadiusMin - precisionMargin, defaultRadiusMax}; + } +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, tiltAngle, numberOfStaves, numberOfModules, thickOrX2X0, mode) { } @@ -298,8 +426,12 @@ TGeoVolume* TRKOTLayer::createStave() void TRKOTLayer::createLayer(TGeoVolume* motherVolume) { + // Retrieve exact bounding boundaries automatically inherited from TRKSegmentedLayer + auto [rMin, rMax] = getBoundingRadii(sStaveWidth); + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + // TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoTube* layer = new TGeoTube(rMin, rMax, mLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow); @@ -307,8 +439,11 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); nStaves += nStaves % 2; // Require an even number of staves + // Nominal average radius used as the placement barycenter for all staves + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; + double theta = 2. * TMath::Pi() / nStaves; double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); @@ -319,11 +454,12 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) for (int iStave = 0; iStave < nStaves; iStave++) { TGeoVolume* staveVol = createStave(); TGeoCombiTrans* trans = new TGeoCombiTrans(); - double theta = 360. * iStave / nStaves; - // TGeoRotation* rot = new TGeoRotation("rot", theta - 90, 0, 0); - TGeoRotation* rot = new TGeoRotation("rot", theta + 90, 0, 0); + double phi = theta * iStave; + double phiDeg = phi * TMath::RadToDeg(); + TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); + // trans->SetTranslation(mInnerRadius * std::cos(phi), mInnerRadius * std::sin(phi), 0); + trans->SetTranslation(avgRadius * std::cos(phi), avgRadius * std::sin(phi), 0); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } @@ -331,7 +467,14 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); motherVolume->AddNode(layerVol, 1, nullptr); } + +std::pair TRKOTLayer::getBoundingRadii(double staveWidth) const +{ + auto [radiusMin, radiusMax] = TRKSegmentedLayer::getBoundingRadii(staveWidth); + + return {radiusMin - 0.201f, radiusMax}; +} // ClassImp(TRKLayer); } // namespace trk -} // namespace o2 \ No newline at end of file +} // namespace o2