Project
Loading...
Searching...
No Matches
FT3ModuleConstants.h
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
17#ifndef FT3MODULECONSTANTS_H
18#define FT3MODULECONSTANTS_H
19
20#include <vector>
21#include <map>
22#include <TMath.h>
23
25{
26/* CURRENT STATUS:
27 * 25x29mm sensors, 2mm inactive on one side
28 * Most granular layout is 2x1 sensors, where the one on the right has the inactive region
29 * on the right, and the one on the left has the inactive region on the left.
30 * When stacking 2x1 modules, there is a 0.2mm gap between them. By default, we assume this
31 * gap to be ABOVE the most recently placed module.
32 *
33 * |<- 25mm ->||<- 25mm ->|
34 * _______________________
35 * ------------------------ 0.15mm gap above
36 * | | || | |
37 * | | || | |
38 * | | || | |
39 * | | || | | 29mm sensor height
40 * | | || | |
41 * | | || | |
42 * ------------------------ 0.15mm gap below
43 * ^
44 * |
45 * 0.15mm gap in the middle
46 */
47// First set all layout constants for the rest of the function
48const double single_sensor_width = 2.5;
49const double single_sensor_height = 2.9;
50const double inactive_width = 0.15;
51const double sensor2x1_gap = 0.015; // gap between L&R sensors in 2x1, and between sensors in a stack
52const double stackGap = 0.035; // gap between 2xN module stacks
53
56
59const std::vector<unsigned> kSensorsPerStack = {4, 2, 1};
60inline const double getStackHeight(unsigned nSensorsPerStack)
61{
62 return nSensorsPerStack * sensor2x1_height +
63 (nSensorsPerStack - 1) * sensor2x1_gap;
64}
65
66// small helper function to get 1-indexed stave ID, counting from the middle outwards,
67// with negative IDs on the left and positive IDs on the right
68inline const int staveIdxToID(int staveIdx, unsigned nStavesPerDisc)
69{
70 unsigned nStavesOneSide = nStavesPerDisc / 2;
71 bool isRight = staveIdx >= nStavesOneSide;
72 return staveIdx - nStavesOneSide + isRight;
73}
74
75/*
76 * Stave x midpoints follow from their number and spacing: they are spread
77 * symmetrically about x=0, so an even count leaves a gap on the axis rather
78 * than putting a stave on it. Deriving them keeps the spacing and the
79 * positions from drifting apart.
80 */
81inline std::vector<double> makeStaveXMidpoints(unsigned nStaves, double spacing)
82{
83 std::vector<double> midpoints(nStaves);
84 for (unsigned i = 0; i < nStaves; i++) {
85 midpoints[i] = (i - (nStaves - 1) / 2.0) * spacing;
86 }
87 return midpoints;
88}
89
90/*
91 * Staves alternate between the front and the back of the disc so that
92 * neighbours can overlap in x without touching, staggered in z by
93 * z_offsetStave. Starting at 0 puts the leftmost stave at the back.
94 */
95inline std::vector<bool> makeStaveOnFront(unsigned nStaves)
96{
97 std::vector<bool> staveOnFront(nStaves);
98 for (unsigned i = 0; i < nStaves; i++) {
99 staveOnFront[i] = i % 2;
100 }
101 return staveOnFront;
102}
103
104// material properties
105const double siliconThickness = 0.01;
106const double copperThickness = 0.006;
107const double kaptonThickness = 0.03;
108const double epoxyThickness = 0.0012;
109
110const double effectiveCarbonThickness_Stave = 0.02; // foam + shell
111const double staveOpeningAngle = 60 * TMath::DegToRad();
112const double sinTheta = TMath::Sin(staveOpeningAngle / 2);
113const double alpha = TMath::Pi() / 2 - staveOpeningAngle / 2; // bottom angles
114const double staveSensorGap = 0.1025; // 1025µm padding on each side: 52.2mm stave width
116const double staveTriangleHeight = staveWidth / 2.0 / tan(staveOpeningAngle / 2.0);
117/*
118 * Now describe the offset of every other stave in z to avoid overlaps
119 * ______ ______
120 * \ /______\ / | <-- z_offsetStave
121 * \ / \ / \ /
122 * \/ \ / \/
123 * \/
124 */
125// If midpoint spacing becomes non constant, this becomes a function
126// TODO: add some tolerance to avoid overlaps?
127inline const double z_offsetStave(double x_midpoint_spacing)
128{
129 return staveTriangleHeight *
130 (2 - x_midpoint_spacing / (sensor2x1_width / 2 + staveSensorGap));
131}
132
133/*
134 * One uninterrupted fill of 2xN modules along a stave.
135 *
136 * yStart is the y of the BOTTOM edge of the first module; modules follow
137 * upwards, each separated from the previous one by stackGap. Nothing is
138 * mirrored: the layout is symmetric about the y-axis (stave +-ID) but NOT
139 * about the x-axis, so every fill states its own y explicitly.
140 *
141 * A stave that the beam pipe cuts in two therefore carries two fills, one
142 * below the hole and one above it, each with its own yStart.
143 */
144struct StaveFill {
145 const double yStart;
146 const std::vector<unsigned> stackHeights;
147};
148
149// Struct for stave position configuration (varies between ML/OT)
151 const unsigned isML; // whether this config is for ML or OT
152 /*
153 * Constants for staves are written for both positive
154 * and negative x even though they are just mirrored now,
155 * because there might be design changes in the future
156 * that require a non-mirrored layout, making it easier to
157 * change here if so required, even though it looks uglier now.
158 *
159 * The second element in the mapping pair is whether the stave
160 * with a certain ID should be mirrored around the x-axis.
161 */
162 // map from Stave ID (1-indexed from other documents) to midpoint
163 // Do NOT add any zero midpoints, this is taken off separately
164 const std::map<int, std::pair<double, bool>>& staveID_to_y_midpoint;
165 // lengths of staves, their midpoint, and their face
166 const std::vector<double>& y_lengths;
167 const std::vector<double>& x_midpoints;
168 const double x_midpoint_spacing;
169 // whether staves can be placed outside of nominal radii
170 const double maxToleranceInner;
171 const double maxToleranceOuter;
172 // which side of the disc do we place the stave?
173 // kSegmentedStave: staggering staves in z (see z_offsetStave)
174 // accessed via stave index, NOT stave ID
175 const std::vector<bool>& staveOnFront;
176 /*
177 * Tabulated module layout, used when FT3Base.useExactStavePlacement is set.
178 * One entry per stave, indexed like x_midpoints (NOT by stave ID), holding
179 * that stave's fills: one for a stave reaching across y=0, two for a stave
180 * split by the beam pipe.
181 */
182 const std::vector<std::vector<StaveFill>>& exactStaveFills;
183};
184
185namespace OT_StavePositions
186{
187/*
188 * Staves that the beam pipe cuts in two, built as two pieces on +-y_midpoint.
189 * Do NOT add any zero midpoints, this is taken off separately.
190 */
191const std::map<int, std::pair<double, bool>> staveID_to_y_midpoint = {
192 {-4, {35.659, true}},
193 {-3, {41.735, true}},
194 {-2, {42.882, true}},
195 {-1, {43.761, true}},
196 {1, {43.761, true}},
197 {2, {42.882, true}},
198 {3, {41.735, true}},
199 {4, {35.659, true}}};
200/*
201 * Length of one stave piece: for a stave in staveID_to_y_midpoint that is one
202 * of its two pieces, otherwise the whole stave centred on y=0. Trimmed to the
203 * modules in exactStaveFills, rounded up to the nearest 10 um.
204 */
205const std::vector<double> y_lengths = {
206 32.225, 58.401, 73.016, 87.611,
207 99.29, 108.055, 113.886, 119.736,
208 125.566, 128.481, 61.336, 49.655,
209 49.656, 46.818, 46.818, 49.656,
210 49.655, 61.336, 128.481, 125.566,
211 119.736, 113.886, 108.055, 99.29,
212 87.611, 73.016, 58.401, 32.225};
213const unsigned nStaves = 28; // y_lengths, staveOnFront and exactStaveFills follow this
214const double x_midpoint_spacing = 4.92;
216const double maxToleranceInner = 9.; // close but not directly at 10cm yet
217const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation
218const std::vector<bool> staveOnFront = makeStaveOnFront(nStaves);
219/*
220 * From the disk optimiser: Rin 20, Rout 68, stave width 5.22, overlap 0.30,
221 * intrusion and extrusion <= 2 with 6 exception staves at 9 and 3, giving
222 * 99.37% filling with staves and 93.48% with active silicon. One entry per
223 * stave in x_midpoints order; a stave cut in two by the beam pipe has one
224 * fill either side of the hole.
225 *
226 * yStart is the bottom of the first SENSOR, which is half a module gap above
227 * the bottom of the module the optimiser reports.
228 */
229const std::vector<std::vector<StaveFill>> exactStaveFills = {
230 {{-15.9953, {4, 4, 3}}}, // ID -14
231 {{-29.1825, {4, 4, 4, 4, 4}}}, // ID -13
232 {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID -12
233 {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -11
234 {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID -10
235 {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -9
236 {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -8
237 {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID -7
238 {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID -6
239 {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID -5
240 {{-66.3087, {4, 4, 4, 3, 3, 3}}, {5.0087, {4, 4, 4, 3, 3, 3}}}, // ID -4
241 {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID -3
242 {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID -2
243 {{-67.0542, {4, 4, 4, 4}}, {20.4669, {4, 4, 4, 4}}}, // ID -1
244 {{-67.1519, {4, 4, 4, 4}}, {20.3692, {4, 4, 4, 4}}}, // ID +1
245 {{-67.6917, {4, 4, 3, 3, 3}}, {18.0717, {4, 4, 3, 3, 3}}}, // ID +2
246 {{-66.545, {4, 4, 3, 3, 3}}, {16.925, {4, 4, 3, 3, 3}}}, // ID +3
247 {{-66.3087, {4, 4, 4, 3, 3, 3}}, {5.0087, {4, 4, 4, 3, 3, 3}}}, // ID +4
248 {{-64.2225, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}}}, // ID +5
249 {{-62.765, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +6
250 {{-59.85, {4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +7
251 {{-56.925, {4, 4, 4, 4, 4, 4, 4, 4, 4, 3}}}, // ID +8
252 {{-54.01, {4, 4, 4, 4, 4, 4, 4, 3, 3, 3}}}, // ID +9
253 {{-49.6275, {4, 4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +10
254 {{-43.7875, {4, 4, 4, 4, 4, 4, 3, 3}}}, // ID +11
255 {{-36.49, {4, 4, 4, 4, 3, 3, 3}}}, // ID +12
256 {{-29.1825, {4, 4, 4, 4, 4}}}, // ID +13
257 {{-16.0947, {4, 4, 3}}}, // ID +14
258};
259} // namespace OT_StavePositions
260
261namespace ML_StavePositions
262{
263// Use prelim numbers for now, these will change! TODO
264const std::map<int, std::pair<double, bool>> staveID_to_y_midpoint = {
265 {-3, {19.1, true}},
266 {-2, {21.8, true}},
267 {-1, {22.5, true}},
268 {1, {22.5, true}},
269 {2, {21.8, true}},
270 {3, {19.1, true}}};
271const std::vector<double> y_lengths = {
272 30.5, 44.5, 53.6, 60.0, 64.6, 29.5, 25.8, 25.0,
273 25.0, 25.8, 29.5, 64.6, 60.0, 53.6, 44.5, 30.5};
274const unsigned nStaves = 16; // y_lengths, staveOnFront and exactStaveFills follow this
275const double x_midpoint_spacing = 4.5;
277const double maxToleranceInner = 0.; // default not allowed inwards
278const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation
279const std::vector<bool> staveOnFront = makeStaveOnFront(nStaves);
280// TODO: fill from the disk optimiser output, see OT_StavePositions above.
281const std::vector<std::vector<StaveFill>> exactStaveFills = {};
282} // namespace ML_StavePositions
283
284// Get stave configuration based on tracker type
311
312// ---------------------------------------------------------------------------
313// End-of-stave (EoS) readout cards at the outer-radius tips of the disk staves.
314// Same construction as the TRK barrel cards: an FR4 board carrying evenly spaced
315// copper planes. Separate parameter sets for the Small (ML/inner) and Large (OT)
316// disks. All lengths in cm. Local card axes at the outer stave tip:
317// length -> along beam z, width -> along stave width, thickness -> stave axis.
319 double length; // along beam z
320 double width; // along stave width
321 double thickness; // along stave axis (radial at the tip): FR4 + Cu planes
322 int nCopperLayers; // copper planes, spread over the thickness
323 double copperThickness; // per copper plane (default of the FT3Base Cu knob)
324 double zGap; // clearance from the stave's downstream face
325};
326// NOTE on width: a card spanning the full z-range must be narrower than the
327// clearance to the staggered neighbour stave, whose carbon triangle is
328// staveWidth/2 (= 2.61 cm) half-wide: half-width < x_midpoint_spacing - staveWidth/2.
329// That is tightest on the ML disks (4.5 - 2.61 = 1.89 cm), so width = 3.4 cm
330// (half 1.7 cm) clears both ML and OT (OT spacing 4.92 cm) neighbours.
331// Large (OT) disks
332constexpr EosCardParams eosCardOT{12.0, 3.4, 0.15, 4, 0.0122, 0.2};
333// Small (ML) disks
334constexpr EosCardParams eosCardML{8.0, 3.4, 0.15, 4, 0.0122, 0.2};
335inline const EosCardParams& getEosCardParams(bool isML) { return isML ? eosCardML : eosCardOT; }
336
337// Downstream half-length (beam z) that the disk layer envelope must gain to
338// contain the EoS cards (card body + clearance + a small margin).
339inline double eosCardEnvelopeExtension(bool isML)
340{
341 const EosCardParams& c = getEosCardParams(isML);
342 return c.zGap + c.length + 0.2;
343}
344
345} // namespace o2::ft3::ModuleConstants
346
347#endif // FT3MODULECONSTANTS_H
int32_t i
uint32_t c
Definition RawData.h:2
GLfloat GLfloat GLfloat alpha
Definition glcorearb.h:279
const std::vector< std::vector< StaveFill > > exactStaveFills
const std::map< int, std::pair< double, bool > > staveID_to_y_midpoint
const std::map< int, std::pair< double, bool > > staveID_to_y_midpoint
const std::vector< std::vector< StaveFill > > exactStaveFills
const double effectiveCarbonThickness_Stave
double eosCardEnvelopeExtension(bool isML)
const double z_offsetStave(double x_midpoint_spacing)
const EosCardParams & getEosCardParams(bool isML)
constexpr EosCardParams eosCardML
const int staveIdxToID(int staveIdx, unsigned nStavesPerDisc)
std::vector< double > makeStaveXMidpoints(unsigned nStaves, double spacing)
std::vector< bool > makeStaveOnFront(unsigned nStaves)
StaveConfig getStaveConfig(bool isInnerDisk)
const double getStackHeight(unsigned nSensorsPerStack)
constexpr EosCardParams eosCardOT
const std::vector< unsigned > kSensorsPerStack
const std::vector< double > & x_midpoints
const std::vector< double > & y_lengths
const std::vector< std::vector< StaveFill > > & exactStaveFills
const std::map< int, std::pair< double, bool > > & staveID_to_y_midpoint
const std::vector< bool > & staveOnFront
const std::vector< unsigned > stackHeights