Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,16 @@ namespace o2
{
namespace ft3
{

// **
// ** Parameters for FT3 base configuration
// **

enum FT3Geometry {
Default = 0,
Telescope = 1
// Parameters for FT3 (ML and OT disks)
enum eFT3Layout {
kCylindrical = 0,
kTrapezoidal,
kSegmented,
};

struct FT3BaseParam : public o2::conf::ConfigurableParamHelper<FT3BaseParam> {
// Geometry Builder parameters

Int_t geoModel = FT3Geometry::Default;
eFT3Layout layoutFT3 = kSegmented;
int nTrapezoidalSegments = 32; // for the simple trapezoidal disks

// FT3Geometry::Telescope parameters
Int_t nLayers = 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo
static const char* getFT3LayerPattern() { return sLayerName.c_str(); }
static const char* getFT3ChipPattern() { return sChipName.c_str(); }
static const char* getFT3SensorPattern() { return sSensorName.c_str(); }
static const char* getFT3PassivePattern() { return sPassiveName.c_str(); }

static const char* composeSymNameFT3(Int_t d) { return Form("%s_%d", o2::detectors::DetID(o2::detectors::DetID::FT3).getName(), d); }
static const char* composeSymNameLayer(Int_t d, Int_t lr);
Expand All @@ -105,8 +106,8 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo
static std::string sVolumeName; ///< Mother volume name
static std::string sLayerName; ///< Layer name
static std::string sChipName; ///< Chip name

static std::string sSensorName; ///< Sensor name
static std::string sPassiveName; ///< Passive material name

private:
static std::unique_ptr<o2::ft3::GeometryTGeo> sInstance; ///< singletone instance
Expand Down
3 changes: 2 additions & 1 deletion Detectors/Upgrades/ALICE3/FT3/base/src/GeometryTGeo.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ std::unique_ptr<o2::ft3::GeometryTGeo> GeometryTGeo::sInstance;
std::string GeometryTGeo::sVolumeName = "FT3V"; ///< Mother volume name
std::string GeometryTGeo::sInnerVolumeName = "FT3Inner"; ///< Mother inner volume name
std::string GeometryTGeo::sLayerName = "FT3Layer"; ///< Layer name
std::string GeometryTGeo::sChipName = "FT3Chip"; ///< Sensor name
std::string GeometryTGeo::sChipName = "FT3Chip"; ///< Chip name
std::string GeometryTGeo::sSensorName = "FT3Sensor"; ///< Sensor name
std::string GeometryTGeo::sPassiveName = "FT3Passive"; ///< Passive material name

//__________________________________________________________________________
GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : o2::itsmft::GeometryTGeo(DetID::FT3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class FT3Layer : public TObject
Double_t mOuterRadius; ///< Outer radius of this layer
Double_t mZ; ///< Z position of the layer
Double_t mChipThickness; ///< Chip thickness
Double_t mSensorThickness; ///< Sensor thickness
Double_t mx2X0; ///< Layer material budget x/X0

ClassDefOverride(FT3Layer, 0); // ALICE 3 EndCaps geometry
Expand Down
16 changes: 1 addition & 15 deletions Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -356,21 +356,7 @@ Detector::Detector(bool active)
mTrackData(),
mHits(o2::utils::createSimVector<o2::itsmft::Hit>())
{

// FT3 Base configuration parameters
auto& ft3BaseParam = FT3BaseParam::Instance();

switch (ft3BaseParam.geoModel) {
case Default:
buildFT3ScopingV3(); // v3 Dec 25
break;
case Telescope:
buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers)
break;
default:
LOG(fatal) << "Invalid Geometry.\n";
break;
}
buildFT3ScopingV3(); // v3 Dec 25
}

//_________________________________________________________________________________________________
Expand Down
140 changes: 128 additions & 12 deletions Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,17 @@

#include "FT3Simulation/FT3Layer.h"
#include "FT3Base/GeometryTGeo.h"

#include <fairlogger/Logger.h> // for LOG
#include "FT3Base/FT3BaseParam.h"

#include <TGeoManager.h> // for TGeoManager, gGeoManager
#include <TGeoMatrix.h> // for TGeoCombiTrans, TGeoRotation, etc
#include <TGeoTube.h> // for TGeoTube, TGeoTubeSeg
#include <TGeoArb8.h> // for TGeoTrap
#include <TGeoVolume.h> // for TGeoVolume, TGeoVolumeAssembly
#include <TGeoCompositeShape.h> // for TGeoCompositeShape
#include "TMathBase.h" // for Abs
#include <TMath.h> // for Sin, RadToDeg, DegToRad, Cos, Tan, etc

#include <TGeoBBox.h>
#include <string>
#include <cstdio> // for snprintf
#include <cmath>

class TGeoMedium;

using namespace TMath;
Expand Down Expand Up @@ -68,6 +63,7 @@ FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerNam
mOuterRadius = rOut;
const double Si_X0 = 9.5;
mChipThickness = Layerx2X0 * Si_X0;
mSensorThickness = 0.005; // assume 50 microns of active thickness (for sensor volumes for trapezoidal disks)

// Sanity checks
if (std::isnan(mZ)) {
Expand Down Expand Up @@ -232,10 +228,130 @@ void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string

void FT3Layer::createLayer(TGeoVolume* motherVolume)
{
auto& ft3Params = FT3BaseParam::Instance();

if (mLayerNumber < 0) {
LOG(fatal) << "Invalid layer number " << mLayerNumber << " for FT3 layer.";
}
if (mIsMiddleLayer) { // ML disks

LOG(info) << "FT3: ft3Params.layoutFT3 = " << ft3Params.layoutFT3;

// ### options for ML and OT disk layout
if (ft3Params.layoutFT3 == kTrapezoidal || (mIsMiddleLayer && ft3Params.layoutFT3 == kSegmented)) {
// trapezoidal ML+OT disks
// (disks with TGeoTubes doesn'n work properly in ACTS, due to polar coordinates on TGeoTube sides)

// (!) Currently (March 12, 2026), only OT disks are segmented --> use Trapezoidal option for ML disks as a simplified segmentation
// To be changed to "true" paving with modules, as for the OT disks

std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber);
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$");

TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2);
TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
layerVol->SetLineColor(kGray);

const int NtrapezoidalSegments = ft3Params.nTrapezoidalSegments;

const double dz = mChipThickness / 2;
const double dzSensor = mSensorThickness / 2;

const double dphi = 2.0 * TMath::Pi() / NtrapezoidalSegments;
double innerRadiusTrapezoidCorner = mInnerRadius / sin((TMath::Pi() - dphi) / 2); // to ensure that the trapezoid segments do not extend beyond the volume

const double rc = 0.5 * (innerRadiusTrapezoidCorner + mOuterRadius) * TMath::Cos(0.5 * dphi); // radius of tile center
const double h = 0.5 * (mOuterRadius - innerRadiusTrapezoidCorner) * TMath::Cos(0.5 * dphi); // half radial length

// chord lengths at inner/outer radii
const double bl = innerRadiusTrapezoidCorner * TMath::Sin(0.5 * dphi); // half lower base
const double tl = mOuterRadius * TMath::Sin(0.5 * dphi); // half upper base

// create trapezoids
for (int iTr = 0; iTr < NtrapezoidalSegments; ++iTr) {
// chip volume
auto trdShapeChip = new TGeoTrap(dz,
0.0, 0.0, // theta, phi
h, // h1
bl, // bl1
tl, // tl1
0.0, // alpha1
h, // h2
bl, // bl2
tl, // tl2
0.0); // alpha2
TGeoVolume* trapezoidChipVolume = new TGeoVolume(chipName.c_str(), trdShapeChip, medSi);
trapezoidChipVolume->SetLineColor(kCyan);
trapezoidChipVolume->SetTransparency(50);

// sensor volume
auto trdShapeSensor = new TGeoTrap(dzSensor,
0.0, 0.0, // theta, phi
h, // h1
bl, // bl1
tl, // tl1
0.0, // alpha1
h, // h2
bl, // bl2
tl, // tl2
0.0); // alpha2
TGeoVolume* trapezoidSensorVolume = new TGeoVolume(sensName.c_str(), trdShapeSensor, medSi);
trapezoidSensorVolume->SetLineColor(kYellow);

// placing sensor in chip:
const double zSensorInChip = (dz - dzSensor) * (mZ < 0 ? 1 : -1); // place sensor at the outer face of the chip, towards the incoming particles
TGeoCombiTrans* transSens = new TGeoCombiTrans();
transSens->SetTranslation(0, 0, zSensorInChip);
trapezoidChipVolume->AddNode(trapezoidSensorVolume, iTr, transSens);

// passive volume
auto trdShapePassive = new TGeoTrap(dz - dzSensor,
0.0, 0.0, // theta, phi
h, // h1
bl, // bl1
tl, // tl1
0.0, // alpha1
h, // h2
bl, // bl2
tl, // tl2
0.0); // alpha2
TGeoVolume* trapezoidPassiveVolume = new TGeoVolume(passiveName.c_str(), trdShapePassive, medSi);
trapezoidPassiveVolume->SetLineColor(kGray);

// placing passive volume in chip:
const double zPassiveInChip = (-dzSensor) * (mZ < 0 ? 1 : -1); // place passive volume at the outer face of the chip, towards the incoming particles
TGeoCombiTrans* transPassive = new TGeoCombiTrans();
transPassive->SetTranslation(0, 0, zPassiveInChip);
trapezoidChipVolume->AddNode(trapezoidPassiveVolume, iTr, transPassive);

// prepare placing of chip in layer:
const double phi_c = (iTr + 0.5) * dphi; // sector center
const double phi_deg = phi_c * 180.0 / TMath::Pi();

// center of tile
const double x = rc * TMath::Cos(phi_c);
const double y = rc * TMath::Sin(phi_c);
const double z = 0.0;

// local +Y should point radially outward
auto rot = new TGeoRotation();
rot->RotateZ(phi_deg - 90.0);
auto transf = new TGeoCombiTrans(x, y, z, rot);

layerVol->AddNode(trapezoidChipVolume, iTr, transf);
}

LOG(info) << "Inserting " << NtrapezoidalSegments << " trapezoidal segments (Rmin="
<< mInnerRadius << ", Rmax=" << mOuterRadius << ", z = " << mZ << "cm) inside " << layerVol->GetName();

auto* diskRotation = new TGeoRotation("TrapezoidalDiskRotation", 0, 0, 0);
auto* diskCombiTrans = new TGeoCombiTrans(0, 0, mZ, diskRotation);
motherVolume->AddNode(layerVol, 1, diskCombiTrans);
} else if (ft3Params.layoutFT3 == kCylindrical) {
// cylindrical ML+OT disks

std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber),
sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber);
Expand Down Expand Up @@ -265,9 +381,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume)

LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName();
motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans);

} else { // OT disks

} else if (ft3Params.layoutFT3 == kSegmented) {
FT3Module module;

// layer structure
Expand All @@ -276,7 +390,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume)
std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber);

TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$");
TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 10 * mChipThickness / 2);
TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 12 * mChipThickness / 2); // additional "thickness factor" is to avoid sub-volumes crossing the mother layer
TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
layerVol->SetLineColor(kYellow + 2);

Expand All @@ -293,5 +407,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume)

LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName();
motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans);
} else {
LOG(fatal) << "Unknown FT3 layout option: " << static_cast<int>(ft3Params.layoutFT3);
}
}
10 changes: 5 additions & 5 deletions Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str
}
if (itof) { // iTOF
const std::string name = GeometryTGeo::getITOFLayerPattern();
const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case
const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm
const double staveTiltAngle = itofSegmented ? 10.0 : 0.0; // degrees
const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case
const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case
const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm
const double staveTiltAngle = itofSegmented ? 3.0 : 0.0; // degrees
const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case
mITOFLayer = ITOFLayer(name,
dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, ITOFLayer::kBarrelSegmented,
nStaves, staveWidth, staveTiltAngle, modulesPerStave);
Expand All @@ -108,7 +108,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str
const std::string name = GeometryTGeo::getOTOFLayerPattern();
const int nStaves = otofSegmented ? 62 : 0; // number of staves in segmented case
const double staveWidth = otofSegmented ? 9.74 : 0.0; // cm
const double staveTiltAngle = otofSegmented ? 5.0 : 0.0; // degrees
const double staveTiltAngle = otofSegmented ? 3.0 : 0.0; // degrees
const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case
mOTOFLayer = OTOFLayer(name,
dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, OTOFLayer::kBarrelSegmented,
Expand Down
16 changes: 11 additions & 5 deletions Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,17 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume)
case kBarrelSegmented: {
// First we create the volume for the whole layer, which will be used as mother volume for the segments
const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius);
const double staveSizeX = mStaves.second; // cm
const double staveSizeY = mOuterRadius - mInnerRadius; // cm
const double staveSizeZ = mZLength; // cm
const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves
const double staveSizeX = mStaves.second; // cm
const double staveSizeY = mOuterRadius - mInnerRadius; // cm
const double staveSizeZ = mZLength; // cm
const double rMargin = 0.2; // cm, a small margin to avoid layer extrusion by sub-volumes
const double deltaForTilt = rMargin + 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves
TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2);
TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
setLayerStyle(layerVol);

LOGP(info, "iTOF kBarrelSegmented layout: stave tilt angle {}, layer tube rMin {}, rMax {}", mTiltAngle, mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt);

// Now we create the volume for a single stave
TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5);
TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir);
Expand Down Expand Up @@ -287,10 +290,13 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume)
case kBarrelSegmented: {
// First we create the volume for the whole layer, which will be used as mother volume for the segments
const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius);
TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2);
const double rMargin = 0.8; // cm, a small margin to avoid layer extrusion by sub-volumes
TGeoTube* layer = new TGeoTube(mInnerRadius - rMargin, mOuterRadius + rMargin, mZLength / 2);
TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
setLayerStyle(layerVol);

LOGP(info, "oTOF kBarrelSegmented layout: stave tilt angle {}, layer tube rMin {}, rMax {}", mTiltAngle, mInnerRadius - rMargin, mOuterRadius + rMargin);

// Now we create the volume for a single stave
const double staveSizeX = mStaves.second; // cm
const double staveSizeY = mOuterRadius - mInnerRadius; // cm
Expand Down
Loading