Project
Loading...
Searching...
No Matches
FT3Layer.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
16
22
23#include <TGeoManager.h> // for TGeoManager, gGeoManager
24#include <TGeoMatrix.h> // for TGeoCombiTrans, TGeoRotation, etc
25#include <TGeoTube.h> // for TGeoTube, TGeoTubeSeg
26#include <TGeoArb8.h> // for TGeoTrap
27#include <TGeoVolume.h> // for TGeoVolume, TGeoVolumeAssembly
28#include <TGeoCompositeShape.h> // for TGeoCompositeShape
29#include "TMathBase.h" // for Abs
30#include <TMath.h> // for Sin, RadToDeg, DegToRad, Cos, Tan, etc
31
32class TGeoMedium;
33
34using namespace TMath;
35using namespace o2::ft3;
36
38
39FT3Layer::~FT3Layer() = default;
40
41FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0, bool partOfMiddleLayers)
42{
43 // Creates a simple parametrized EndCap layer covering the given
44 // pseudorapidity range at the z layer position
45 mDirection = layerDirection;
46 mLayerNumber = layerNumber;
47 mIsMiddleLayer = partOfMiddleLayers;
48 mLayerName = layerName;
49 mZ = layerDirection ? std::abs(z) : -std::abs(z);
50 mx2X0 = Layerx2X0;
51 mInnerRadius = rIn;
52 mOuterRadius = rOut;
53 const double Si_X0 = Materials::materials.at(Materials::MaterialID::Silicon).radl;
54 mChipThickness = Layerx2X0 * Si_X0;
55 mSensorThickness = 0.005; // assume 50 microns of active thickness (for sensor volumes for trapezoidal disks)
56
57 // Sanity checks
58 if (std::isnan(mZ)) {
59 LOG(fatal) << "FT3 Layer " << mLayerNumber << " has z = NaN, which is not a valid number.";
60 }
61 if (mZ < 0.001 && mZ > -0.001) {
62 LOG(fatal) << "FT3 Layer " << mLayerNumber << " has z = " << mZ << " cm, which is very close to 0.";
63 }
64
65 LOG(info) << "Creating FT3 Layer " << mLayerNumber << " ; direction " << mDirection;
66 LOG(info) << " Using silicon X0 = " << Si_X0 << " to emulate layer radiation length.";
67 LOG(info) << " Layer z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness;
68}
69
70static double y_circle(double x, double radius)
71{
72 return (x * x < radius * radius) ? std::sqrt(radius * radius - x * x) : 0;
73}
74
75void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, const std::string& separationLayerName)
76{
77
78 const double carbonFiberThickness = 0.01; // cm
79 const double foamSpacingThickness = 0.5; // cm
80
81 TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2);
82
83 // volumes
84 TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber));
85 TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber));
86
87 carbonFiberLayerVol1->SetLineColor(kGray + 2);
88 carbonFiberLayerVol2->SetLineColor(kGray + 2);
89
90 const double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0;
91
92 motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, mZ - zSeparation));
93 motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, mZ + zSeparation));
94
95 const double pipeOuterRadius = 0.20;
96 const double kaptonThickness = 0.0025;
97 const double pipeInnerRadius = pipeOuterRadius - kaptonThickness;
98 const double pipeMaxLength = mOuterRadius * 2.0;
99
100 int name_it = 0;
101
102 // positions of the pipes depending on the overlap of the sensors inactive regions: (ALICE 3 dimensions)
103 // partial:
104 // std::vector<double> X_pos = {-63.2, -58.4, -53.6, -48.8, -44.0, -39.199999999999996, -34.4, -29.599999999999994, -24.799999999999997, -19.999999999999993, -15.199999999999998, -10.399999999999993, -5.599999999999998, -0.7999999999999936, 4.000000000000002, 8.800000000000006, 13.600000000000001, 18.400000000000006, 23.200000000000003, 28.000000000000007, 32.800000000000004, 37.60000000000001, 42.400000000000006, 47.20000000000001, 52.00000000000001, 56.80000000000001, 61.60000000000001, 66.4};
105 // complete:
106 // std::vector<double> X_pos = {-63.4, -58.8, -54.199999999999996, -49.599999999999994, -44.99999999999999, -40.39999999999999, -35.79999999999999, -31.199999999999992, -26.59999999999999, -21.999999999999993, -17.39999999999999, -12.799999999999994, -8.199999999999992, -3.5999999999999934, 1.000000000000008, 5.600000000000007, 10.200000000000008, 14.800000000000008, 19.40000000000001, 24.000000000000007, 28.60000000000001, 33.20000000000001, 37.80000000000001, 42.40000000000001, 47.000000000000014, 51.600000000000016, 56.20000000000002, 60.80000000000002, 65.40000000000002};
107 std::vector<double> X_pos = {-62.3168, -57.9836, -53.650400000000005, -49.317200000000014, -44.984000000000016, -40.65080000000002, -36.31760000000002, -31.984400000000026, -27.65120000000003, -23.318000000000037, -18.98480000000004, -14.651600000000043, -10.318400000000047, -5.98520000000005, -1.6520000000000519, 2.6811999999999445, 7.014399999999941, 11.347599999999936, 15.680799999999934, 20.01399999999993, 24.347199999999926, 28.68039999999992, 33.013599999999926, 37.34679999999992, 41.980000000000004, 46.613200000000006, 51.246399999999994, 55.87960000000001, 60.5128};
108
109 for (double xPos : X_pos) {
110
111 double pipeLength = pipeMaxLength;
112 double yMax = 0.0;
113
114 TGeoRotation* rotation = new TGeoRotation();
115 rotation->RotateX(90);
116
117 if (std::abs(xPos) < mInnerRadius) {
118 double yInner = std::abs(y_circle(xPos, mInnerRadius));
119 double yOuter = std::abs(y_circle(xPos, mOuterRadius));
120
121 yMax = 2 * yOuter;
122 pipeLength = yMax;
123
124 double positiveYLength = yOuter - yInner;
125
126 TGeoVolume* kaptonPipePos = new TGeoVolume((separationLayerName + "_KaptonPipePos_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Kapton));
127 kaptonPipePos->SetLineColor(kGray);
128 TGeoVolume* waterVolumePos = new TGeoVolume((separationLayerName + "_WaterVolumePos_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Water));
129 waterVolumePos->SetLineColor(kBlue);
130
131 motherVolume->AddNode(waterVolumePos, 1, new TGeoCombiTrans(xPos, (yInner + yOuter) / 2.0, mZ, rotation));
132
133 TGeoVolume* kaptonPipeNeg = new TGeoVolume((separationLayerName + "_KaptonPipeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Kapton));
134 kaptonPipeNeg->SetLineColor(kGray);
135 TGeoVolume* waterVolumeNeg = new TGeoVolume((separationLayerName + "_WaterVolumeNeg_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, positiveYLength / 2), getMedium(Materials::MaterialID::Water));
136 waterVolumeNeg->SetLineColor(kBlue);
137
138 motherVolume->AddNode(waterVolumeNeg, 1, new TGeoCombiTrans(xPos, -(yInner + yOuter) / 2.0, mZ, rotation));
139
140 motherVolume->AddNode(kaptonPipePos, 1, new TGeoCombiTrans(xPos, (yInner + yOuter) / 2.0, mZ, rotation));
141 motherVolume->AddNode(kaptonPipeNeg, 1, new TGeoCombiTrans(xPos, -(yInner + yOuter) / 2.0, mZ, rotation));
142
143 } else {
144
145 double yOuter = std::abs(y_circle(xPos, mOuterRadius));
146 yMax = 2 * yOuter;
147 pipeLength = yMax;
148
149 TGeoVolume* kaptonPipe = new TGeoVolume((separationLayerName + "_KaptonPipe_" + std::to_string(name_it)).c_str(), new TGeoTube(pipeInnerRadius, pipeOuterRadius, pipeLength / 2), getMedium(Materials::MaterialID::Kapton));
150 kaptonPipe->SetLineColor(kGray);
151 TGeoVolume* waterVolume = new TGeoVolume((separationLayerName + "_WaterVolume_" + std::to_string(name_it)).c_str(), new TGeoTube(0.0, pipeInnerRadius, pipeLength / 2), getMedium(Materials::MaterialID::Water));
152 waterVolume->SetLineColor(kBlue);
153
154 motherVolume->AddNode(waterVolume, 1, new TGeoCombiTrans(xPos, 0, mZ, rotation));
155 motherVolume->AddNode(kaptonPipe, 1, new TGeoCombiTrans(xPos, 0, mZ, rotation));
156 }
157
158 name_it++;
159 }
160}
161
162void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string& separationLayerName)
163{
164
165 constexpr double carbonFiberThickness = 0.01; // cm
166 constexpr double foamSpacingThickness = 1.0; // cm
167
168 TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2);
169 TGeoTube* foamLayer = new TGeoTube(mInnerRadius, mOuterRadius, foamSpacingThickness / 2);
170
171 // volumes
172 TGeoVolume* carbonFiberLayerVol1 = new TGeoVolume((separationLayerName + "_CarbonFiber1").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber));
173 TGeoVolume* foamLayerVol = new TGeoVolume((separationLayerName + "_Foam").c_str(), foamLayer, getMedium(Materials::MaterialID::Foam));
174 TGeoVolume* carbonFiberLayerVol2 = new TGeoVolume((separationLayerName + "_CarbonFiber2").c_str(), carbonFiberLayer, getMedium(Materials::MaterialID::CarbonFiber));
175
176 carbonFiberLayerVol1->SetLineColor(kGray + 2);
177 foamLayerVol->SetLineColor(kBlack);
178 foamLayerVol->SetFillColorAlpha(kBlack, 1.0);
179 carbonFiberLayerVol2->SetLineColor(kGray + 2);
180
181 const double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0;
182
183 motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, 0 - zSeparation));
184 motherVolume->AddNode(foamLayerVol, 1, new TGeoTranslation(0, 0, 0));
185 motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, 0 + zSeparation));
186}
187
188void FT3Layer::createReferenceCircles(TGeoVolume* motherVolume, const std::string& name)
189{
190
191 // create reference circles at the inner and outer radius of the layer, for visualization purposes
192 TGeoTube* innerCircle = new TGeoTube(mInnerRadius - 0.1, mInnerRadius + 0.1, 0.01);
193 TGeoTube* outerCircle = new TGeoTube(mOuterRadius - 0.1, mOuterRadius + 0.1, 0.01);
194 TGeoTube* outerCircleEdge = new TGeoTube(mOuterRadius + 3.3, mOuterRadius + 3.5, 0.01);
195
196 TGeoVolume* innerCircleVol = new TGeoVolume((mLayerName + "_InnerCircle").c_str(), innerCircle, getMedium(Materials::MaterialID::Air));
197 TGeoVolume* outerCircleVol = new TGeoVolume((mLayerName + "_OuterCircle").c_str(), outerCircle, getMedium(Materials::MaterialID::Air));
198 TGeoVolume* outerCircleEdgeVol = new TGeoVolume((mLayerName + "_OuterCircleEdge").c_str(), outerCircleEdge, getMedium(Materials::MaterialID::Air));
199
200 innerCircleVol->SetLineColor(kRed);
201 outerCircleVol->SetLineColor(kBlue);
202 outerCircleEdgeVol->SetLineColor(kBlack);
203
204 double z_position = mDirection ? 0.5 : -0.5;
205
206 motherVolume->AddNode(innerCircleVol, 1, new TGeoTranslation(0, 0, z_position));
207 motherVolume->AddNode(outerCircleVol, 1, new TGeoTranslation(0, 0, z_position));
208 motherVolume->AddNode(outerCircleEdgeVol, 1, new TGeoTranslation(0, 0, z_position));
209}
210
211void FT3Layer::createLayer(TGeoVolume* motherVolume)
212{
213 auto& ft3Params = FT3BaseParam::Instance();
214
215 if (mLayerNumber < 0) {
216 LOG(fatal) << "Invalid layer number " << mLayerNumber << " for FT3 layer.";
217 }
218
219 LOG(info) << "FT3: ft3Params.layoutFT3 = " << ft3Params.layoutFT3
220 << " Creating Layer " << mLayerNumber << " at z=" << mZ
221 << " with direction " << mDirection;
222
223 // ### options for ML and OT disk layout
224 if (ft3Params.layoutFT3 == kTrapezoidal /*|| (mIsMiddleLayer && ft3Params.layoutFT3 == kSegmented)*/) {
225 // trapezoidal ML+OT disks
226 // (disks with TGeoTubes doesn'n work properly in ACTS, due to polar coordinates on TGeoTube sides)
227
228 // (!) Currently (March 12, 2026), only OT disks are segmented --> use Trapezoidal option for ML disks as a simplified segmentation
229 // To be changed to "true" paving with modules, as for the OT disks
230
231 std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber);
232 std::string sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber);
233 std::string passiveName = o2::ft3::GeometryTGeo::getFT3PassivePattern() + std::to_string(mLayerNumber);
234
235 TGeoMedium* medSi = getMedium(Materials::MaterialID::Silicon);
236 TGeoMedium* medAir = getMedium(Materials::MaterialID::Air);
237
238 TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2);
239 TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
240 layerVol->SetLineColor(kGray);
241
242 const int NtrapezoidalSegments = ft3Params.nTrapezoidalSegments;
243
244 const double dz = mChipThickness / 2;
245 const double dzSensor = mSensorThickness / 2;
246
247 const double dphi = 2.0 * TMath::Pi() / NtrapezoidalSegments;
248 double innerRadiusTrapezoidCorner = mInnerRadius / sin((TMath::Pi() - dphi) / 2); // to ensure that the trapezoid segments do not extend beyond the volume
249
250 const double rc = 0.5 * (innerRadiusTrapezoidCorner + mOuterRadius) * TMath::Cos(0.5 * dphi); // radius of tile center
251 const double h = 0.5 * (mOuterRadius - innerRadiusTrapezoidCorner) * TMath::Cos(0.5 * dphi); // half radial length
252
253 // chord lengths at inner/outer radii
254 const double bl = innerRadiusTrapezoidCorner * TMath::Sin(0.5 * dphi); // half lower base
255 const double tl = mOuterRadius * TMath::Sin(0.5 * dphi); // half upper base
256
257 // create trapezoids
258 for (int iTr = 0; iTr < NtrapezoidalSegments; ++iTr) {
259 // chip volume
260 auto trdShapeChip = new TGeoTrap(dz,
261 0.0, 0.0, // theta, phi
262 h, // h1
263 bl, // bl1
264 tl, // tl1
265 0.0, // alpha1
266 h, // h2
267 bl, // bl2
268 tl, // tl2
269 0.0); // alpha2
270 TGeoVolume* trapezoidChipVolume = new TGeoVolume(chipName.c_str(), trdShapeChip, medSi);
271 trapezoidChipVolume->SetLineColor(kCyan);
272 trapezoidChipVolume->SetTransparency(50);
273
274 // sensor volume
275 auto trdShapeSensor = new TGeoTrap(dzSensor,
276 0.0, 0.0, // theta, phi
277 h, // h1
278 bl, // bl1
279 tl, // tl1
280 0.0, // alpha1
281 h, // h2
282 bl, // bl2
283 tl, // tl2
284 0.0); // alpha2
285 TGeoVolume* trapezoidSensorVolume = new TGeoVolume(sensName.c_str(), trdShapeSensor, medSi);
286 trapezoidSensorVolume->SetLineColor(kYellow);
287
288 // placing sensor in chip:
289 const double zSensorInChip = (dz - dzSensor) * (mZ < 0 ? 1 : -1); // place sensor at the outer face of the chip, towards the incoming particles
290 TGeoCombiTrans* transSens = new TGeoCombiTrans();
291 transSens->SetTranslation(0, 0, zSensorInChip);
292 trapezoidChipVolume->AddNode(trapezoidSensorVolume, iTr, transSens);
293
294 // passive volume
295 auto trdShapePassive = new TGeoTrap(dz - dzSensor,
296 0.0, 0.0, // theta, phi
297 h, // h1
298 bl, // bl1
299 tl, // tl1
300 0.0, // alpha1
301 h, // h2
302 bl, // bl2
303 tl, // tl2
304 0.0); // alpha2
305 TGeoVolume* trapezoidPassiveVolume = new TGeoVolume(passiveName.c_str(), trdShapePassive, medSi);
306 trapezoidPassiveVolume->SetLineColor(kGray);
307
308 // placing passive volume in chip:
309 const double zPassiveInChip = (-dzSensor) * (mZ < 0 ? 1 : -1); // place passive volume at the outer face of the chip, towards the incoming particles
310 TGeoCombiTrans* transPassive = new TGeoCombiTrans();
311 transPassive->SetTranslation(0, 0, zPassiveInChip);
312 trapezoidChipVolume->AddNode(trapezoidPassiveVolume, iTr, transPassive);
313
314 // prepare placing of chip in layer:
315 const double phi_c = (iTr + 0.5) * dphi; // sector center
316 const double phi_deg = phi_c * 180.0 / TMath::Pi();
317
318 // center of tile
319 const double x = rc * TMath::Cos(phi_c);
320 const double y = rc * TMath::Sin(phi_c);
321 const double z = 0.0;
322
323 // local +Y should point radially outward
324 auto rot = new TGeoRotation();
325 rot->RotateZ(phi_deg - 90.0);
326 auto transf = new TGeoCombiTrans(x, y, z, rot);
327
328 layerVol->AddNode(trapezoidChipVolume, iTr, transf);
329 }
330
331 LOG(info) << "Inserting " << NtrapezoidalSegments << " trapezoidal segments (Rmin="
332 << mInnerRadius << ", Rmax=" << mOuterRadius << ", z = " << mZ << "cm) inside " << layerVol->GetName();
333
334 auto* diskRotation = new TGeoRotation("TrapezoidalDiskRotation", 0, 0, 0);
335 auto* diskCombiTrans = new TGeoCombiTrans(0, 0, mZ, diskRotation);
336 motherVolume->AddNode(layerVol, 1, diskCombiTrans);
337 } else if (ft3Params.layoutFT3 == kCylindrical) {
338 // cylindrical ML+OT disks
339
340 std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber),
341 sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber);
342 TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2);
343 TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2);
344 TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2);
345
346 TGeoMedium* medSi = getMedium(Materials::MaterialID::Silicon);
347 TGeoMedium* medAir = getMedium(Materials::MaterialID::Air);
348
349 TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi);
350 sensVol->SetLineColor(kYellow);
351 TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi);
352 chipVol->SetLineColor(kYellow);
353 TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
354 layerVol->SetLineColor(kYellow);
355
356 LOG(info) << "Inserting " << sensVol->GetName() << " inside " << chipVol->GetName();
357 chipVol->AddNode(sensVol, 1, nullptr);
358
359 LOG(info) << "Inserting " << chipVol->GetName() << " inside " << layerVol->GetName();
360 layerVol->AddNode(chipVol, 1, nullptr);
361
362 // Finally put everything in the mother volume
363 auto* FwdDiskRotation = new TGeoRotation("FwdDiskRotation", 0, 0, 180);
364 auto* FwdDiskCombiTrans = new TGeoCombiTrans(0, 0, mZ, FwdDiskRotation);
365
366 LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName();
367 motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans);
368 } else if (ft3Params.layoutFT3 == kSegmented ||
369 (ft3Params.layoutFT3 == kSegmentedStaveOTOnly && mIsMiddleLayer)) {
370 FT3Module module;
371
372 // layer structure
373 std::string frontLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Front";
374 std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back";
375 std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber);
376
377 TGeoMedium* medAir = getMedium(Materials::MaterialID::Air);
378 TGeoVolume* layerVol = nullptr;
379 // Add a little additional room in radius
380 TGeoTube* layer = new TGeoTube(mInnerRadius - 0.1, mOuterRadius + 0.1, 1.5);
381 layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
382 layerVol->SetLineColor(kYellow + 2);
383 // createSeparationLayer_waterCooling(motherVolume, separationLayerName);
384 createSeparationLayer(layerVol, separationLayerName);
385 module.createModule(0, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "front", "rectangular", layerVol);
386 module.createModule(0, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "back", "rectangular", layerVol);
387
388 // Finally put everything in the mother volume
389 auto* FwdDiskRotation = new TGeoRotation("FwdDiskRotation", 0, 0, 180);
390 // need to shift outwards always, so + forwards and - backwards
391 auto* FwdDiskCombiTrans = new TGeoCombiTrans(0, 0, mZ + 0, FwdDiskRotation);
392
393 LOG(info) << "Inserting " << layerVol->GetName() << " (Rmin=" << mInnerRadius << ", Rmax=" << mOuterRadius << ", z=" << mZ << "cm) inside " << motherVolume->GetName();
394 motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans);
395 } else if (ft3Params.layoutFT3 == kSegmentedStave ||
396 ft3Params.layoutFT3 == kSegmentedStaveOTOnly) {
397 FT3Module module;
398
399 // layer structure
400 std::string frontLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Front";
401 std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back";
402 std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber);
403
404 TGeoMedium* medAir = getMedium(Materials::MaterialID::Air);
405 TGeoVolume* layerVol = nullptr;
406
407 // set up stave config, differs between ML and OT disks
408 const Constants::StaveConfig& staveConfig = Constants::getStaveConfig(mIsMiddleLayer);
409
410 // need a thicker air layer to encompass the staves (4.5cm high, 1.2cm offsets)
411 // stave face is at z=0 (or +-z_offset_stave), meaning that volumes are at
412 // ~-+1cm < z < ~+-6cm, the +- referring forward/backward discs
413 double z_layer_thickness = // need to shift internally with this
420 0.5; // add some extra room to ensure all volumes are encapsulated
421
422 // shift stave volumes into layer volume, since nominal z_{stave face} = 0
423 double z_local_offset = z_layer_thickness / 2.0;
424 // ensure staves fully encapsulated in the layer volume,
425 // but don't cross out of max nominal radii of 38.5cm & 71.5cm respectively (3.5cm tolerance)
426 // Allow 3, 9cm outward, inward for OT:
427 double innerRadiusForAirTube = mIsMiddleLayer ? mInnerRadius : mInnerRadius - 9.0;
428 double outerRadiusForAirTube = mIsMiddleLayer ? mOuterRadius + 2.5 : mOuterRadius + 3.0;
429 // MvL: try 70.5 // 2.5 cm tolerance instead
430 const double rInEnv = innerRadiusForAirTube - 0.2;
431 const double rOutEnv = outerRadiusForAirTube;
432 const double H0 = z_layer_thickness / 2;
433 if (ft3Params.addDiskEosCards) {
434 // Extend the envelope DOWNSTREAM only (away from the IP) with a union, so the
435 // end-of-stave cards fit without growing toward the IP / the neighbouring disk.
436 const double ext = o2::ft3::ModuleConstants::eosCardEnvelopeExtension(mIsMiddleLayer);
437 const double s = (mDirection == 1) ? 1.0 : -1.0; // downstream sign
438 TGeoTube* baseTube = new TGeoTube((mLayerName + "_base").c_str(), rInEnv, rOutEnv, H0);
439 TGeoTube* extTube = new TGeoTube((mLayerName + "_eosext").c_str(), rInEnv, rOutEnv, ext / 2);
440 TGeoTranslation* extTr = new TGeoTranslation((mLayerName + "_eosext_tr").c_str(), 0, 0, s * (H0 + ext / 2));
441 extTr->RegisterYourself();
442 TGeoCompositeShape* layerShape = new TGeoCompositeShape(
443 (mLayerName + "_env").c_str(),
444 Form("%s + %s:%s", baseTube->GetName(), extTube->GetName(), extTr->GetName()));
445 layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir);
446 } else {
447 TGeoTube* layer = new TGeoTube(rInEnv, rOutEnv, H0);
448 layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
449 }
450
451 if (ft3Params.drawReferenceCircles) {
452 std::string referenceCirclesName = "ReferenceCircles_Dir" + std::to_string(mDirection) + "_Layer" + std::to_string(mLayerNumber);
453 createReferenceCircles(layerVol, referenceCirclesName); // for visualization purposes
454 }
455
456 // need the -0.5 added to local offset to ensure all sensor modules are inside the layer
457 module.createModule_staveGeo(0., mLayerNumber, mDirection, mInnerRadius,
458 mOuterRadius, z_local_offset, staveConfig, layerVol);
459 // Finally put everything in the mother volume
460 auto* FwdDiskRotation = new TGeoRotation("FwdDiskRotation", 0, 0, 180);
461 // need to shift outwards always, so + forwards and - backwards
462 double z_offset_directional = mDirection ? z_local_offset : -z_local_offset;
463 auto* FwdDiskCombiTrans = new TGeoCombiTrans(0, 0, mZ + z_offset_directional, FwdDiskRotation);
464
465 LOG(info) << "Inserting " << layerVol->GetName() << " (Rmin=" << mInnerRadius << ", Rmax=" << mOuterRadius << ", z=" << mZ << "cm, segmented disk with staves) inside " << motherVolume->GetName();
466
467 motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans);
468 } else {
469 LOG(fatal) << "Unknown FT3 layout option: " << static_cast<int>(ft3Params.layoutFT3);
470 }
471}
ClassImp(FT3Layer)
Definition of the FT3Layer class.
Materials of the FT3 detector, and access to the media made from them.
Definition of various constants for tiling the modules of sensors.
float & yMax
Definition of the GeometryTGeo class.
Class for time synchronization of RawReader instances.
virtual void createLayer(TGeoVolume *motherVolume)
Definition FT3Layer.cxx:211
void createSeparationLayer_waterCooling(TGeoVolume *motherVolume, const std::string &separationLayerName)
Definition FT3Layer.cxx:75
void createReferenceCircles(TGeoVolume *motherVolume, const std::string &name)
Definition FT3Layer.cxx:188
~FT3Layer() override
Default destructor.
FT3Layer()=default
void createSeparationLayer(TGeoVolume *motherVolume, const std::string &separationLayerName)
Definition FT3Layer.cxx:162
static const char * getFT3PassivePattern()
static const char * getFT3SensorPattern()
static const char * getFT3ChipPattern()
static const char * getFT3LayerPattern()
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint const GLchar * name
Definition glcorearb.h:781
GLint y
Definition glcorearb.h:270
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
const std::unordered_map< MaterialID, MaterialProperties > materials
double eosCardEnvelopeExtension(bool isML)
const double z_offsetStave(double x_midpoint_spacing)
StaveConfig getStaveConfig(bool isInnerDisk)
TGeoMedium * getMedium(Materials::MaterialID id)
@ kSegmentedStaveOTOnly
@ kSegmentedStave
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"