Skip to content

Commit 492489f

Browse files
joseesquivel-hubnjacazio
authored andcommitted
[MID] Add superconducting magnet/cryostat geometry to simulation
Apply clang-format [MI3] Add superconducting magnet/cryostat geometry to ALICE3 MID simulation [ALICE3] Magnet: add WindingPack (NbTi+Cu+Al) coil material option [ALICE3] Magnet: improve WindingPack material documentation ICN-UNAM: IanMagnet, IanAbsorber, ICNStepped MID layout — 0 overlaps verified ICN-UNAM: IanAbsorber — Geometria 6 (Antonio): cara externa plana Rmax=290, escalon hacia adentro ICN-UNAM: cleanup diagnostic instrumentation and translate comments to English ICN-UNAM: fix hit production — simple sensor names for VMC resolution and reset mHitStarted on Reset() ICN-UNAM: fix physLay in ProcessHits — extract layer index from sensor name; remove ITS TrackReference boilerplate; guard against invalid physLay ICN-UNAM: document CurrentVolOffID limitation; full sensor location available via sensor name pattern ICN-UNAM: IanAbsorber — correct external Rmin to 245 cm (45 cm thickness per Ian reference code) ICN-UNAM: rename enum values — IanMagnet->SuperconductingMagnet, IanAbsorber->SteppedAbsorber, ICNStepped->SteppedLayout ICN-UNAM: fix layer 1 half-length to 525 cm (Ian/SD Table 16); revert absorber to Geometry 6 (Rext=290 const, step in Rmin) ICN-UNAM: fix ALICE3Field.C — B1 in numerator, Rc=170 cm (R_out_coil per Ian; confirmed by A. Ortiz Aug 2026)
1 parent bcabde7 commit 492489f

8 files changed

Lines changed: 156 additions & 45 deletions

File tree

Detectors/Upgrades/ALICE3/MID/base/include/MI3Base/MI3BaseParam.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ namespace mi3
2626

2727
enum MIDLayout : int {
2828
StandardRadius = 0,
29-
ReducedRadius = 1
29+
ReducedRadius = 1,
30+
SteppedLayout = 2
3031
};
3132

3233
struct MIDBaseParam : public o2::conf::ConfigurableParamHelper<MIDBaseParam> {

Detectors/Upgrades/ALICE3/MID/simulation/include/MI3Simulation/MIDLayer.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ class MIDLayer
9292
float staveLength = 500.f,
9393
float staveWidth = 50.f,
9494
float staveThickness = 0.5f,
95-
int nModulesZ = 10);
95+
int nModulesZ = 10,
96+
int nBars = -1);
9697
void createStave(TGeoVolume* motherVolume);
9798

9899
private:
@@ -110,16 +111,20 @@ class MIDLayer
110111

111112
public:
112113
MIDLayer() = default;
113-
MIDLayer(int layerNumber, std::string layerName, float rInn, float length, int nstaves = 16);
114+
MIDLayer(int layerNumber, std::string layerName, float rInn, float length, int nstaves = 16, float zOffset = 0.f, int nModulesZ = 10, float staveWidth = -1.f, int nBars = -1);
114115
void createLayer(TGeoVolume* motherVolume);
115116

116117
private:
117118
std::string mName;
118119
std::vector<Stave> mStaves;
119120
float mRadius;
120121
float mLength;
122+
float mZOffset;
123+
float mStaveWidth;
121124
int mNumber;
122125
int mNStaves;
126+
int mNModulesZ;
127+
int mNBars;
123128
};
124129
} // namespace o2::mi3
125130

Detectors/Upgrades/ALICE3/MID/simulation/src/Detector.cxx

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "DetectorsBase/Stack.h"
2121
#include "ITSMFTSimulation/Hit.h"
2222
#include "MI3Simulation/Detector.h"
23+
#include <set>
2324
#include "MI3Base/MI3BaseParam.h"
2425

2526
using o2::itsmft::Hit;
@@ -92,7 +93,20 @@ void Detector::InitializeO2Detector()
9293
{
9394
LOG(info) << "Initialize MID O2Detector";
9495
mGeometryTGeo = GeometryTGeo::Instance();
95-
// defineSensitiveVolumes();
96+
// Register sensitive volumes
97+
TObjArray* allVols = gGeoManager->GetListOfVolumes();
98+
TString sensorPattern = GeometryTGeo::getMIDSensorPattern();
99+
std::set<TGeoVolume*> registered;
100+
for (int i = 0; i < allVols->GetEntries(); i++) {
101+
TGeoVolume* v = (TGeoVolume*)allVols->At(i);
102+
TString vname = v->GetName();
103+
if (vname.Contains(sensorPattern) && registered.find(v) == registered.end()) {
104+
AddSensitiveVolume(v);
105+
registered.insert(v);
106+
}
107+
}
108+
LOGP(info, "Total MI3 sensitive volumes registered: {}", registered.size());
109+
96110
}
97111

98112
void Detector::EndOfEvent() { Reset(); }
@@ -125,28 +139,52 @@ void Detector::createGeometry()
125139
vMID->SetTitle(vstrng);
126140

127141
// Build the MID
128-
mLayers.resize(2);
129142
auto& midParam = MIDBaseParam::Instance();
130143
const bool standardRadius = (midParam.mLayout == o2::mi3::MIDLayout::StandardRadius);
131144

132145
if (standardRadius) {
146+
mLayers.resize(2);
133147
mLayers[0] = MIDLayer(0, GeometryTGeo::composeSymNameLayer(0), 301.f, 500.f);
134-
mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 311.f, 520.f); // arbitrarily reduced to get multiple of 5.2f
148+
mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 311.f, 525.f); // 10 modules x 52.5 cm = 525 cm — matches Ian ref. code and SD Table 16 (10.5 m)
149+
} else if (midParam.mLayout == o2::mi3::MIDLayout::SteppedLayout) {
150+
// Ian Perez Garcia design (ICN-UNAM) — tesis §3.4.7 Geometria 8
151+
// 11 cm gap from absorber outer face to MID layer
152+
// mLayer index is flat 0-5: even = physical layer 0, odd = physical layer 1
153+
// Module step: layer0=99.8cm (2x49.9), layer1=104cm (2x52=2xsumWidth)
154+
// Central segment: Rmax_abso=290 -> Layer0=301, Layer1=311, nMod=6, semi-dz=299.4/312 at Z=0
155+
// External segments: Rmax_abso=265 -> Layer0=276, Layer1=286, nMod=2, semi-dz=99.8/104 at Z=+-400
156+
constexpr float kAbsGap = 11.f;
157+
constexpr float kPitch = 10.f;
158+
constexpr float kRCen0 = 290.f + kAbsGap; // 301 cm
159+
constexpr float kRCen1 = kRCen0 + kPitch; // 311 cm
160+
constexpr float kRExt0 = 265.f + kAbsGap; // 276 cm
161+
constexpr float kRExt1 = kRExt0 + kPitch; // 286 cm
162+
mLayers.resize(6);
163+
// length = semi-length = nModulesZ x step (layer0: step=49.9cm, layer1: step=52cm)
164+
mLayers[0] = MIDLayer(0, "MIDLayer0_central", kRCen0, 299.4f, 16, 0.f, 6); // 6 modules x 49.9 cm step
165+
mLayers[1] = MIDLayer(1, "MIDLayer1_central", kRCen1, 312.f, 16, 0.f, 6); // 6 modules x 52 cm step
166+
mLayers[2] = MIDLayer(2, "MIDLayer0_forward", kRExt0, 99.8f, 16, +400.f, 2, -1.f, 21); // 2 modules x 49.9 cm step, nBars=21 for R=276 cm
167+
mLayers[3] = MIDLayer(3, "MIDLayer1_forward", kRExt1, 104.f, 16, +405.f, 2); // 2 modules x 52 cm step, +5 cm offset to clear absorber transition
168+
mLayers[4] = MIDLayer(4, "MIDLayer0_backward", kRExt0, 99.8f, 16, -400.f, 2, -1.f, 21); // 2 modules x 49.9 cm step, nBars=21 for R=276 cm
169+
mLayers[5] = MIDLayer(5, "MIDLayer1_backward", kRExt1, 104.f, 16, -405.f, 2); // 2 modules x 52 cm step, -5 cm offset to clear absorber transition
135170
} else {
171+
mLayers.resize(2);
136172
mLayers[0] = MIDLayer(0, GeometryTGeo::composeSymNameLayer(0), 266.f, 500.f);
137173
mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 276.f, 520.f);
138174
}
139175

140176
for (auto& layer : mLayers) {
141177
layer.createLayer(vMID);
142178
}
179+
143180
}
144181

145182
void Detector::Reset()
146183
{
147184
if (!o2::utils::ShmManager::Instance().isOperational()) {
148185
mHits->clear();
149186
}
187+
mTrackData.mHitStarted = false;
150188
}
151189

152190
bool Detector::ProcessHits(FairVolume* vol)
@@ -159,14 +197,15 @@ bool Detector::ProcessHits(FairVolume* vol)
159197
int lay = vol->getVolumeId();
160198
int volID = vol->getMCid();
161199

162-
// Is it needed to keep a track reference when the outer ITS volume is encountered?
200+
// TrackReference block removed: ITS boilerplate whose condition (lay == 0
201+
// against a TGeo volume ID) never fired. No MID reconstruction consumes
202+
// MID track references at present.
163203
auto stack = (o2::data::Stack*)fMC->GetStack();
164-
if (fMC->IsTrackExiting() && (lay == 0)) {
165-
o2::TrackReference tr(*fMC, GetDetId());
166-
tr.setTrackID(stack->GetCurrentTrackNumber());
167-
tr.setUserId(lay);
168-
stack->addTrackReference(tr);
169-
}
204+
// Extract physical layer index (0 or 1) from sensor name: MIDSensor_L<lay>_S...
205+
int physLay = -1;
206+
const char* volName = fMC->CurrentVolName();
207+
sscanf(volName, "MIDSensor_L%d", &physLay);
208+
if (physLay >= 0) physLay = physLay % 2;
170209
bool startHit = false, stopHit = false;
171210
unsigned char status = 0;
172211
if (fMC->IsTrackEntering()) {
@@ -213,14 +252,14 @@ bool Detector::ProcessHits(FairVolume* vol)
213252
if (stopHit) {
214253
TLorentzVector positionStop;
215254
fMC->TrackPosition(positionStop);
216-
// Retrieve the indices with the volume path
217-
int stave(0), halfstave(0), chipinmodule(0), module;
218-
fMC->CurrentVolOffID(1, chipinmodule);
219-
fMC->CurrentVolOffID(2, module);
220-
fMC->CurrentVolOffID(3, halfstave);
221-
fMC->CurrentVolOffID(4, stave);
222-
223-
Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(),
255+
// CurrentVolOffID(1..4) yields copy numbers of module/halfstave/stave ancestors.
256+
// With TGeoVolumeAssembly nodes these are always 0 except the stave level.
257+
// Full sensor location (layer, stave, module, bar) is encoded in the sensor
258+
// name (MIDSensor_L<l>_S<s>_M<m>_B<b>) and can be decoded with sscanf if needed.
259+
// Left as future work for hit digitization.
260+
261+
if (physLay < 0) { return false; } // guard: sensor name did not match expected pattern
262+
Hit* p = addHit(stack->GetCurrentTrackNumber(), physLay, mTrackData.mPositionStart.Vect(), positionStop.Vect(),
224263
mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(),
225264
mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status);
226265
// p->SetTotalEnergy(vmc->Etot());

Detectors/Upgrades/ALICE3/MID/simulation/src/MIDLayer.cxx

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,33 @@ MIDLayer::MIDLayer(int layerNumber,
2626
std::string layerName,
2727
float rInn,
2828
float length,
29-
int nstaves) : mName(layerName),
29+
int nstaves,
30+
float zOffset,
31+
int nModulesZ,
32+
float staveWidth,
33+
int nBars) : mName(layerName),
3034
mRadius(rInn),
3135
mLength(length),
36+
mZOffset(zOffset),
37+
mStaveWidth(staveWidth),
3238
mNumber(layerNumber),
33-
mNStaves(nstaves)
39+
mNStaves(nstaves),
40+
mNModulesZ(nModulesZ),
41+
mNBars(nBars)
3442
{
3543
mStaves.reserve(nstaves);
36-
LOGP(debug, "Constructing MIDLayer: {} with inner radius: {}, length: {} cm and {} staves", mName, mRadius, mLength, mNStaves);
44+
LOGP(debug, "Constructing MIDLayer: {} with inner radius: {}, length: {} cm, {} staves and {} modules/stave", mName, mRadius, mLength, mNStaves, mNModulesZ);
3745
for (int iStave = 0; iStave < mNStaves; ++iStave) {
3846
mStaves.emplace_back(GeometryTGeo::composeSymNameStave(layerNumber, iStave),
3947
mRadius,
4048
TMath::TwoPi() / (float)nstaves * iStave,
4149
mNumber,
4250
iStave,
4351
mLength,
44-
!layerNumber ? 59.8f : 61.75f,
45-
0.5f);
52+
!(layerNumber % 2) ? 59.8f : 61.75f,
53+
0.5f,
54+
mNModulesZ,
55+
mNBars);
4656
}
4757
}
4858

@@ -54,7 +64,8 @@ MIDLayer::Stave::Stave(std::string staveName,
5464
float staveLength,
5565
float staveWidth,
5666
float staveThickness,
57-
int nModulesZ) : mName(staveName),
67+
int nModulesZ,
68+
int nBars) : mName(staveName),
5869
mRadDistance(radDistance),
5970
mRotAngle(rotAngle),
6071
mLength(staveLength),
@@ -64,17 +75,20 @@ MIDLayer::Stave::Stave(std::string staveName,
6475
mNumber(number),
6576
mNModulesZ(nModulesZ)
6677
{
78+
// nBars=-1 uses default calibrated for standard radii
79+
int effNBars = (nBars < 0) ? (!(mLayer % 2) ? 23 : 20) : nBars;
80+
float moduleOffset = -effNBars * 5.2f / 2.f; // 5.2 = 2*barWidth + barSpacing
6781
// Staves are ideal shapes made of air including the modules, for now.
68-
LOGP(debug, "\t\tConstructing MIDStave: {} layer: {} at angle {}", mName, mLayer, mRotAngle * TMath::RadToDeg());
82+
LOGP(debug, "\t\tConstructing MIDStave: {} layer: {} at angle {} nBars={}", mName, mLayer, mRotAngle * TMath::RadToDeg(), effNBars);
6983
mModules.reserve(nModulesZ);
7084
for (int iModule = 0; iModule < mNModulesZ; ++iModule) {
7185
mModules.emplace_back(GeometryTGeo::composeSymNameModule(mLayer, mNumber, iModule),
7286
mLayer,
7387
mNumber,
7488
iModule,
75-
!mLayer ? 23 : 20,
89+
effNBars,
7690
-staveLength,
77-
!mLayer ? 49.9f : 61.75f);
91+
!(mLayer % 2) ? 49.9f : 61.75f);
7892
}
7993
}
8094

@@ -106,8 +120,8 @@ MIDLayer::Stave::Module::Module(std::string moduleName,
106120
mStave,
107121
mNumber,
108122
iBar,
109-
!mLayer ? -59.8f : -52.f, // offset
110-
!mLayer ? 49.9f : 61.75f); // sensor length
123+
-mNBars * 5.2f / 2.f, // moduleOffset derived from nBars
124+
!(mLayer % 2) ? 49.9f : 61.75f); // sensor length
111125
}
112126
}
113127

@@ -136,9 +150,13 @@ MIDLayer::Stave::Module::Sensor::Sensor(std::string sensorName,
136150

137151
void MIDLayer::createLayer(TGeoVolume* motherVolume)
138152
{
139-
LOGP(debug, "Creating MIDLayer: {}", mName);
153+
LOGP(debug, "Creating MIDLayer: {} at zOffset={} cm", mName, mZOffset);
140154
TGeoVolumeAssembly* layerVolume = new TGeoVolumeAssembly(mName.c_str());
141-
motherVolume->AddNode(layerVolume, 0);
155+
if (mZOffset != 0.f) {
156+
motherVolume->AddNode(layerVolume, 0, new TGeoTranslation(0, 0, mZOffset));
157+
} else {
158+
motherVolume->AddNode(layerVolume, 0);
159+
}
142160
for (auto& stave : mStaves) {
143161
stave.createStave(layerVolume);
144162
}
@@ -172,7 +190,7 @@ void MIDLayer::Stave::Module::createModule(TGeoVolume* motherVolume)
172190
sensor.createSensor(moduleVolume);
173191
}
174192
TGeoCombiTrans* modTrans = nullptr;
175-
if (!mLayer) {
193+
if (!(mLayer % 2)) {
176194
modTrans = new TGeoCombiTrans(0, 0, mZOffset + mNumber * 2 * mBarLength + mBarLength, nullptr);
177195
} else {
178196
modTrans = new TGeoCombiTrans(0, 0, mZOffset + mNumber * 2 * sumWidth + sumWidth, nullptr);
@@ -184,17 +202,19 @@ void MIDLayer::Stave::Module::Sensor::createSensor(TGeoVolume* motherVolume)
184202
{
185203
LOGP(debug, "\t\t\t\tCreating MIDSensor: {}", mName);
186204
TGeoBBox* sensor = nullptr;
187-
if (!mLayer) {
205+
if (!(mLayer % 2)) {
188206
sensor = new TGeoBBox(mName.c_str(), mWidth, mThickness, mLength);
189207
} else {
190208
sensor = new TGeoBBox(mName.c_str(), mLength, mThickness, mWidth);
191209
}
192210
auto* polyMed = gGeoManager->GetMedium("MI3_POLYSTYRENE");
193-
TGeoVolume* sensorVolume = new TGeoVolume(mName.c_str(), sensor, polyMed);
211+
// Simple unique name without slashes so gMC->VolId() resolves correctly during stepping
212+
auto volName = Form("MIDSensor_L%d_S%d_M%d_B%d", mLayer, mStave, mNumber, mNumber);
213+
TGeoVolume* sensorVolume = new TGeoVolume(volName, sensor, polyMed);
194214
sensorVolume->SetVisibility(true);
195215
auto totWidth = mWidth + mSpacing / 2;
196216
TGeoTranslation* sensorTrans = nullptr;
197-
if (!mLayer) {
217+
if (!(mLayer % 2)) {
198218
sensorTrans = new TGeoTranslation(mModuleOffset + 2 * totWidth * mNumber + totWidth, 0, 0);
199219
sensorVolume->SetLineColor(kAzure + 4);
200220
sensorVolume->SetTransparency(50);

Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/PassiveBaseParam.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,23 @@ namespace passive
2626

2727
enum MagnetLayout : int {
2828
AluminiumStabilizer = 0,
29-
CopperStabilizer = 1
29+
CopperStabilizer = 1,
30+
WindingPack = 2,
31+
SuperconductingMagnet = 3
3032
};
3133

3234
enum DetLayout : int {
3335
StandardRadius = 0,
3436
ReducedRadius = 1,
35-
Version2 = 2 // Specs from engineering note 2026-05-06
37+
SteppedAbsorber = 2
3638
};
3739

3840
struct Alice3PassiveBaseParam : public o2::conf::ConfigurableParamHelper<Alice3PassiveBaseParam> {
3941
// Geometry Builder parameters
4042

4143
MagnetLayout mLayout = MagnetLayout::AluminiumStabilizer; // Magnet layout: Aluminium or Copper stabilizer
4244
// DetLayout mDetLayout = DetLayout::StandardRadius; // Detector layout: Standard or Reduced radius
43-
DetLayout mDetLayout = o2::passive::DetLayout::Version2; // Detector layout: Standard or Reduced radius
45+
DetLayout mDetLayout = o2::passive::DetLayout::SteppedAbsorber; // Detector layout: Standard or Reduced radius
4446

4547
O2ParamDef(Alice3PassiveBaseParam, "Alice3PassiveBase");
4648
};

Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ void Alice3Absorber::ConstructGeometry()
130130
LOG(fatal) << "Could not find the barrel volume while constructing absorber geometry";
131131
}
132132

133-
TGeoPcon* absorings = new TGeoPcon(0., 360., 18);
134133
auto& passiveBaseParam = Alice3PassiveBaseParam::Instance();
134+
int nSections = (passiveBaseParam.mDetLayout == o2::passive::DetLayout::SteppedAbsorber) ? 6 : 18;
135+
TGeoPcon* absorings = new TGeoPcon(0., 360., nSections);
135136
switch (passiveBaseParam.mDetLayout) {
136137
case o2::passive::DetLayout::StandardRadius:
137138
absorings->DefineSection(0, 500, 236, 274);
@@ -174,6 +175,16 @@ void Alice3Absorber::ConstructGeometry()
174175
absorings->DefineSection(16, -400, 201, 239);
175176
absorings->DefineSection(17, -500, 201, 239);
176177
break;
178+
case o2::passive::DetLayout::SteppedAbsorber:
179+
// Geometria 6 (Ian/tesis): Rext=290 constante, escalon en Rmin.
180+
// Externas 45 cm (Rmin=245), central 70 cm (Rmin=220). Ref: Ian DetectorConstruction.cc abs_thickness={45,70,45}
181+
absorings->DefineSection(0, -500, 245, 290);
182+
absorings->DefineSection(1, -300, 245, 290);
183+
absorings->DefineSection(2, -300, 220, 290);
184+
absorings->DefineSection(3, 300, 220, 290);
185+
absorings->DefineSection(4, 300, 245, 290);
186+
absorings->DefineSection(5, 500, 245, 290);
187+
break;
177188
default:
178189
LOG(fatal) << "Unknown detector layout " << passiveBaseParam.mDetLayout;
179190
break;

0 commit comments

Comments
 (0)