Skip to content

Commit cc6e4ae

Browse files
[ALICE3] FT3: Tiling Stave Geometry (#15339)
* Add separate file for constants in FT3 module creation * Create modular design in similar fashion as before, but with slightly different module placement. Rewrite structure to be more granular and extendable. The old code remains for backward compatibility for now * Give new geometry creation an FT3Layout enum and change Layer&Module accordingly. Also simplify create_layout_scopingV3 function since the layout_type isn't needed. * Add functionality for the user to enforce a strict cut on module placement to remain within nominal radii. Also remove initial placement of sensor stack before calling fill_stave, moving to using y_start instead. * Make forward and backward directions mirrored, front and back faces swap based on direction * Add loop over all sensors in stack so all sensors are added to volume, not just the first in the stack * Add constants for hollow stave based geometry * Add carbon fiber material, functionality to toggle between stave and old geometry. Also rename material counts to 'volume' instead of 'sensor'. EDIT: Change TGeo layer thickness back to original. EDIT2: Change Middle layer disc usage to newly added segmentation. * Add full hollow stave geometry functionality * Make Stave layout the standard and remove old kSegmentedMarch26: EDIT 20/04/26: Revert to newly added middle disc layer segmentation. * Remove all instances of old slab geometry and work only with staves. * Remove all log(info) statements, except one which is changed to debug. * Don't create the separation layer for stave layout, since we already get the structural support (in carbon) from the staves * add bools for cutting staves and sensors on staves on nominal radii * merge two if statements with the same if * Add implementations for cutting staves and sensors on nominal radii. Also change kSensorsPerStack to a vector in which order of sensor stack height we will pad the staves. * Fix bug in default y range for staves & sensor placements * Bugfix: Stack correctly by using previous stack height in fill_stave, and move in correct direction when placing sensors later * Remove now stale info statements. TODO: let staves be cut as well on nominal radii * Add splitting of stave in case of strict inner cutoff * Add support to place either a stack gap or single sensor around y=0 in staves that have full +Rout to -Rout coverage * Bugfix: When starting sensor placement around the x-axis you can have a different number of sensor stacks on the positive/negative side of the stave leading to garbage memory access. Fixed now. * Add option to draw reference circles onto the layer -- strictly for visualisation purposes * remove stale info statements * Remove stale overlap argument, and add local offset in z. In contrast to previous layout, to encapsulate the staves with face at local z=0 in air, we need to shift the staves and sensors locally since the mother volume is always around local z=0. Shift the layer volume by the same amount when adding it to get the right global position. * Fix placement issue to get sensor materials inside the volume. This now means that the stave faces are not at local z=0 but instead at z=+-totalSensorMaterialThickness+0.1. Also fix global position bug since movement outwards is directional. * Add OT only segmentation and change defaults * Make stave geometry available with middle layer disks as well. Currently use simple calculated values for stave placements, these are subject to change. Hence the existence of kSegmentedOTOnly * Change default to stave segmentation for outer disks only * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot <alibuild@cern.ch>
1 parent 34bd367 commit cc6e4ae

6 files changed

Lines changed: 1022 additions & 11 deletions

File tree

Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ enum eFT3Layout {
2424
kCylindrical = 0,
2525
kTrapezoidal,
2626
kSegmented,
27+
kSegmentedStave,
28+
kSegmentedStaveOTOnly
2729
};
2830
struct FT3BaseParam : public o2::conf::ConfigurableParamHelper<FT3BaseParam> {
2931
// Geometry Builder parameters
30-
eFT3Layout layoutFT3 = kSegmented;
32+
eFT3Layout layoutFT3 = kSegmentedStaveOTOnly;
3133
int nTrapezoidalSegments = 32; // for the simple trapezoidal disks
3234

3335
// FT3Geometry::Telescope parameters
@@ -38,6 +40,16 @@ struct FT3BaseParam : public o2::conf::ConfigurableParamHelper<FT3BaseParam> {
3840
Float_t etaOut = 1.5;
3941
Float_t Layerx2X0 = 0.01;
4042

43+
// override values from FT3ModuleConstants, inner and outer
44+
bool cutStavesOnNominalRadius_inner = true;
45+
bool cutStavesOnNominalRadius_outer = false;
46+
47+
// What to place over x=0 line in case of full outer-outer stave: Gap or Sensor
48+
bool placeSensorInMiddleOfStave = false;
49+
50+
// Draw reference circles at inner and outer radius of stave layer, for visualisation
51+
bool drawReferenceCircles = false;
52+
4153
O2ParamDef(FT3BaseParam, "FT3Base");
4254
};
4355

Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class FT3Layer : public TObject
6464
// create layer for disk support
6565
void createSeparationLayer(TGeoVolume* motherVolume, const std::string& separationLayerName);
6666
void createSeparationLayer_waterCooling(TGeoVolume* motherVolume, const std::string& separationLayerName);
67+
void createReferenceCircles(TGeoVolume* motherVolume, const std::string& name);
6768

6869
static TGeoMaterial* carbonFiberMat;
6970
static TGeoMedium* medCarbonFiber;

Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Module.h

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@
1717

1818
#include <TGeoVolume.h>
1919
#include <string>
20+
#include <vector>
21+
22+
#include "FT3Simulation/FT3ModuleConstants.h"
23+
24+
// define types for y positions, second element is the stack height
25+
using PositionType = std::pair<double, unsigned>;
26+
using PositionTypes = std::vector<PositionType>;
27+
using PosNegPositionTypes = std::pair<PositionTypes, PositionTypes>;
28+
// define type of the y position range: First pair is (min, max) for positive y
29+
using PositionRangeType = std::pair<std::pair<double, double>, std::pair<double, double>>;
30+
namespace Constants = o2::ft3::ModuleConstants;
2031

2132
class FT3Module
2233
{
@@ -33,13 +44,63 @@ class FT3Module
3344
static TGeoMedium* epoxyMed;
3445
static TGeoMaterial* AluminumMat;
3546
static TGeoMedium* AluminumMed;
47+
static TGeoMaterial* carbonFiberMat;
48+
static TGeoMedium* carbonFiberMed;
3649

3750
const char* mDetName;
3851

39-
static void createModule(double mZ, int layerNumber, int direction, double Rin, double Rout, double overlap, const std::string& face, const std::string& layout_type, TGeoVolume* motherVolume);
52+
static void createModule(
53+
double mZ, int layerNumber, int direction, double Rin,
54+
double Rout, double overlap, const std::string& face,
55+
const std::string& layout_type, TGeoVolume* motherVolume);
56+
57+
void createModule_staveGeo(
58+
double mZ, int layerNumber, int direction, double Rin,
59+
double Rout, double z_offset_local, const Constants::StaveConfig& staveConfig,
60+
TGeoVolume* motherVolume);
4061

4162
private:
42-
static void create_layout(double mZ, int layerNumber, int direction, double Rin, double Rout, double overlap, const std::string& face, const std::string& layout_type, TGeoVolume* motherVolume);
63+
static void create_layout(
64+
double mZ, int layerNumber, int direction, double Rin,
65+
double Rout, double overlap, const std::string& face,
66+
const std::string& layout_type, TGeoVolume* motherVolume);
67+
68+
void create_layout_staveGeo(
69+
double mZ, int layerNumber, int direction, double Rin,
70+
double Rout, double z_offset_local, const Constants::StaveConfig& staveConfig,
71+
TGeoVolume* motherVolume);
72+
73+
// Helper functions
74+
void fill_stave(PosNegPositionTypes& y_positions, double Rin, double Rout,
75+
double x_left, unsigned kSensorStack, PositionRangeType y_range,
76+
std::pair<double, double>& absAllowedYRange);
77+
void addStaveVolume(
78+
TGeoVolume* motherVolume, std::string volumeName, int direction,
79+
unsigned* volume_count, double staveLength,
80+
std::array<std::array<double, 3>, 4> staveTriangles,
81+
std::pair<double, double>& absAllowedYRange,
82+
double x_mid, double y_mid, double z_stave_shift_forward);
83+
void addDetectorVolume(
84+
TGeoVolume* motherVolume, std::string volumeName, int color, unsigned* volume_count,
85+
double x_mid, double y_mid, double z_mid,
86+
double x_half_length, double y_half_length, double z_half_length);
87+
88+
void add2x1GlueVolume(
89+
TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
90+
unsigned* volume_count, double x_mid, double y_mid, double z_mid,
91+
std::string element_glued_to);
92+
93+
void add2x1CopperVolume(
94+
TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
95+
unsigned* volume_count, double x_mid, double y_mid, double z_mid);
96+
97+
void add2x1KaptonVolume(
98+
TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
99+
unsigned* volume_count, double x_mid, double y_mid, double z_mid);
100+
101+
void addSingleSensorVolume(
102+
TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
103+
unsigned* volume_count, double active_x_mid, double y_mid, double z_mid, bool isLeft);
43104
};
44105

45106
#endif // FT3MODULE_H
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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+
12+
/// \file FT3ModuleConstants.h
13+
/// \brief Definition of various constants for tiling the modules of sensors
14+
15+
#ifndef FT3MODULECONSTANTS_H
16+
#define FT3MODULECONSTANTS_H
17+
18+
#include <vector>
19+
#include <map>
20+
#include <TColor.h>
21+
#include <TMath.h>
22+
23+
namespace o2::ft3::ModuleConstants
24+
{
25+
/* CURRENT STATUS:
26+
* 25x32mm sensors, 2mm inactive on one side
27+
* Most granular layout is 2x1 sensors, where the one on the right has the inactive region
28+
* on the right, and the one on the left has the inactive region on the left.
29+
* When stacking 2x1 modules, there is a 0.2mm gap between them. By default, we assume this
30+
* gap to be ABOVE the most recently placed module.
31+
*
32+
* |<- 25mm ->|<- 25mm ->|
33+
* _______________________
34+
* ----------------------- 0.2mm gap above
35+
* | | | | |
36+
* | | | | |
37+
* | | | | |
38+
* | | | | | 32mm sensor height
39+
* | | | | |
40+
* | | | | |
41+
* ------------------------
42+
*
43+
*/
44+
// First set all layout constants for the rest of the function
45+
const double single_sensor_width = 2.5;
46+
const double single_sensor_height = 3.2;
47+
const double inactive_width = 0.2;
48+
const double sensor2x1_gap = 0.02;
49+
const double stackGap = sensor2x1_gap; // gap between 2xN module stacks
50+
51+
const double active_width = single_sensor_width - inactive_width;
52+
const double active_height = single_sensor_height;
53+
54+
const double sensor2x1_width = 2 * single_sensor_width;
55+
const double sensor2x1_active_width = 2 * active_width;
56+
const double sensor2x1_height = single_sensor_height;
57+
const std::vector<unsigned> kSensorsPerStack = {4, 2, 1};
58+
inline const double getStackHeight(unsigned nSensorsPerStack)
59+
{
60+
return nSensorsPerStack * sensor2x1_height +
61+
(nSensorsPerStack - 1) * sensor2x1_gap;
62+
}
63+
64+
// small helper function to get 1-indexed stave ID, counting from the middle outwards,
65+
// with negative IDs on the left and positive IDs on the right
66+
inline const int staveIdxToID(int staveIdx, unsigned nStavesPerDisc)
67+
{
68+
unsigned nStavesOneSide = nStavesPerDisc / 2;
69+
bool isRight = staveIdx >= nStavesOneSide;
70+
return staveIdx - nStavesOneSide + isRight;
71+
}
72+
73+
// material properties
74+
const double siliconThickness = 0.01;
75+
const double copperThickness = 0.006;
76+
const double kaptonThickness = 0.03;
77+
const double epoxyThickness = 0.0012;
78+
79+
const double effectiveCarbonThickness_Stave = 0.02; // foam + shell
80+
const double staveOpeningAngle = 60 * TMath::DegToRad();
81+
const double sinTheta = TMath::Sin(staveOpeningAngle / 2);
82+
const double alpha = TMath::Pi() / 2 - staveOpeningAngle / 2; // bottom angles
83+
const double staveSensorGap = 0.1; // 2mm padding on each side when sensor is glued
84+
const double staveTriangleHeight = (sensor2x1_width + 2 * staveSensorGap) / 2.0 / tan(staveOpeningAngle / 2.0);
85+
/*
86+
* Now describe the offset of every other stave in z to avoid overlaps
87+
* ______ ______
88+
* \ /______\ / | <-- z_offsetStave
89+
* \ / \ / \ /
90+
* \/ \ / \/
91+
* \/
92+
*/
93+
// If midpoint spacing becomes non constant, this becomes a function
94+
// TODO: add some tolerance to avoid overlaps?
95+
inline const double z_offsetStave(double x_midpoint_spacing)
96+
{
97+
return staveTriangleHeight *
98+
(2 - x_midpoint_spacing / (sensor2x1_width / 2 + staveSensorGap));
99+
}
100+
101+
const int SiColor = kGreen;
102+
const int SiInactiveColor = kRed;
103+
const int glueColor = kBlue;
104+
const int CuColor = kOrange;
105+
const int kaptonColor = kYellow;
106+
const int carbonColor = kBlack;
107+
108+
// Struct for stave position configuration (varies between IT/OT)
109+
struct StaveConfig {
110+
/*
111+
* Constants for staves are written for both positive
112+
* and negative x even though they are just mirrored now,
113+
* because there might be design changes in the future
114+
* that require a non-mirrored layout, making it easier to
115+
* change here if so required, even though it looks uglier now.
116+
*
117+
* The second element in the mapping pair is whether the stave
118+
* with a certain ID should be mirrored around the x-axis.
119+
*/
120+
// map from Stave ID (1-indexed from other documents) to midpoint
121+
// Do NOT add any zero midpoints, this is taken off separately
122+
const std::map<int, std::pair<double, bool>>& staveID_to_y_midpoint;
123+
// lengths of staves, their midpoint, and their face
124+
const std::vector<double>& y_lengths;
125+
const std::vector<double>& x_midpoints;
126+
double x_midpoint_spacing;
127+
// which side of the disc do we place the stave?
128+
// kSegmentedStave: staggering staves in z (see z_offsetStave)
129+
// accessed via stave index, NOT stave ID
130+
const std::vector<bool>& staveOnFront;
131+
};
132+
133+
namespace OT_StavePositions
134+
{
135+
const std::map<int, std::pair<double, bool>> staveID_to_y_midpoint = {
136+
{-2, {39.0, true}},
137+
{-1, {41.4, true}},
138+
{1, {41.4, true}},
139+
{2, {39.0, true}}};
140+
const std::vector<double> y_lengths = {
141+
52.8, 66.0, 79.2, 92.4, 99.0, 105.6, 118.8, 118.8,
142+
128.7, 132.0, 132.0, 138.6, 138.6, 56.1, 52.8,
143+
52.8, 56.1, 138.6, 138.6, 132.0, 132.0, 128.7,
144+
118.8, 118.8, 105.6, 99.0, 92.4, 79.2, 66.0, 52.8};
145+
const std::vector<double> x_midpoints = {
146+
-65.25, -60.75, -56.25, -51.75, -47.25, -42.75, -38.25, // L
147+
-33.75, -29.25, -24.75, -20.25, -15.75, -11.25, -6.75, -2.25, // L
148+
2.25, 6.75, 11.25, 15.75, 20.25, 24.75, 29.25, 33.75, // R
149+
38.25, 42.75, 47.25, 51.75, 56.25, 60.75, 65.25 // R
150+
};
151+
const double x_midpoint_spacing = 4.5; // assume constant for now
152+
const std::vector<bool> staveOnFront =
153+
{
154+
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // L
155+
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 // R
156+
};
157+
} // namespace OT_StavePositions
158+
159+
namespace ML_StavePositions
160+
{
161+
// Use prelim numbers for now, these will change! TODO
162+
const std::map<int, std::pair<double, bool>> staveID_to_y_midpoint = {
163+
{-3, {19.1, true}},
164+
{-2, {21.8, true}},
165+
{-1, {22.5, true}},
166+
{1, {22.5, true}},
167+
{2, {21.8, true}},
168+
{3, {19.1, true}}};
169+
const std::vector<double> y_lengths = {
170+
30.5, 44.5, 53.6, 60.0, 64.6, 29.5, 25.8, 25.0,
171+
25.0, 25.8, 29.5, 64.6, 60.0, 53.6, 44.5, 30.5};
172+
const std::vector<double> x_midpoints = {
173+
-33.75, -29.25, -24.75, -20.25, -15.75, -11.25, -6.75, -2.25, // L
174+
2.25, 6.75, 11.25, 15.75, 20.25, 24.75, 29.25, 33.75 // R
175+
};
176+
const double x_midpoint_spacing = 4.5;
177+
const std::vector<bool> staveOnFront =
178+
{
179+
1, 0, 1, 0, 1, 0, 1, 0, // L
180+
0, 1, 0, 1, 0, 1, 0, 1 // R
181+
};
182+
} // namespace ML_StavePositions
183+
184+
// Get stave configuration based on tracker type
185+
inline StaveConfig getStaveConfig(bool isInnerDisk)
186+
{
187+
if (isInnerDisk) {
188+
return StaveConfig{
189+
ML_StavePositions::staveID_to_y_midpoint,
190+
ML_StavePositions::y_lengths,
191+
ML_StavePositions::x_midpoints,
192+
ML_StavePositions::x_midpoint_spacing,
193+
ML_StavePositions::staveOnFront};
194+
} else {
195+
return StaveConfig{
196+
OT_StavePositions::staveID_to_y_midpoint,
197+
OT_StavePositions::y_lengths,
198+
OT_StavePositions::x_midpoints,
199+
OT_StavePositions::x_midpoint_spacing,
200+
OT_StavePositions::staveOnFront};
201+
}
202+
}
203+
204+
} // namespace o2::ft3::ModuleConstants
205+
206+
#endif // FT3MODULECONSTANTS_H

0 commit comments

Comments
 (0)