Project
Loading...
Searching...
No Matches
FT3Module.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
14
17#include <TGeoManager.h>
18#include <TGeoMaterial.h>
19#include <TGeoMedium.h>
20#include <TGeoBBox.h>
21#include <TGeoXtru.h>
22#include <TGeoMatrix.h>
23#include <TGeoCompositeShape.h>
24#include <Framework/Logger.h>
25#include <cmath>
26#include <iostream>
27#include <vector>
28#include <set>
29#include <algorithm>
30#include <utility>
31
32TGeoMaterial* FT3Module::siliconMat = nullptr;
33TGeoMedium* FT3Module::siliconMed = nullptr;
34
35TGeoMaterial* FT3Module::copperMat = nullptr;
36TGeoMedium* FT3Module::copperMed = nullptr;
37
38TGeoMixture* FT3Module::kaptonMat = nullptr;
39TGeoMedium* FT3Module::kaptonMed = nullptr;
40
41TGeoMaterial* FT3Module::epoxyMat = nullptr;
42TGeoMedium* FT3Module::epoxyMed = nullptr;
43
44TGeoMaterial* FT3Module::AluminumMat = nullptr;
45TGeoMedium* FT3Module::AluminumMed = nullptr;
46
47TGeoMaterial* FT3Module::carbonFiberMat = nullptr;
48TGeoMedium* FT3Module::carbonFiberMed = nullptr;
49
51{
52 LOG(debug) << "FT3Module: initialize_materials";
53 if (siliconMat) {
54 return;
55 }
56
57 TGeoManager* geoManager = gGeoManager;
58
59 auto* itsH = new TGeoElement("FT3_H", "Hydrogen", 1, 1.00794);
60 auto* itsC = new TGeoElement("FT3_C", "Carbon", 6, 12.0107);
61 auto* itsO = new TGeoElement("FT3_O", "Oxygen", 8, 15.994);
62
63 siliconMat = new TGeoMaterial("FT3_Silicon", 28.0855, 14, 2.33);
64 siliconMed = new TGeoMedium("FT3_Silicon", 1, siliconMat);
65
66 copperMat = new TGeoMaterial("FT3_Copper", 63.546, 29, 8.96);
67 copperMed = new TGeoMedium("FT3_Copper", 2, copperMat);
68
69 TGeoMixture* kaptonMat = new TGeoMixture("FT3_Kapton", 4, 1.346); // C22 H10 N2 O5
70
71 kaptonMat->DefineElement(0, 12.0107, 6, 0.5641); // Carbon
72 kaptonMat->DefineElement(1, 1.00794, 1, 0.2564); // Hydrogen
73 kaptonMat->DefineElement(2, 14.0067, 7, 0.0513); // Nitrogen
74 kaptonMat->DefineElement(3, 15.999, 8, 0.1282); // Oxygen
75 kaptonMed = new TGeoMedium("FT3_Kapton", 3, kaptonMat);
76
77 // TODO: Check with Rene the exact type of carbon fiber
78 carbonFiberMat = new TGeoMaterial("FT3_Carbon", 12.0107, 6, 1.8);
79 carbonFiberMed = new TGeoMedium("FT3_Carbon", 6, carbonFiberMat);
80
81 // Epoxy: C18 H19 O3
82 auto* itsEpoxy = new TGeoMixture("FT3_Epoxy", 3);
83 itsEpoxy->AddElement(itsC, 18);
84 itsEpoxy->AddElement(itsH, 19);
85 itsEpoxy->AddElement(itsO, 3);
86 itsEpoxy->SetDensity(2.186);
87
88 epoxyMed = new TGeoMedium("FT3_Epoxy", 4, itsEpoxy);
89 epoxyMat = epoxyMed->GetMaterial();
90
91 AluminumMat = new TGeoMaterial("Aluminum", 26.98, 13, 2.7);
92 AluminumMed = new TGeoMedium("Aluminum", 5, AluminumMat);
93 LOG(debug) << "FT3Module: done initialize_materials";
94}
95
96double calculate_y_circle(double x, double radius)
97{
98 return (x * x < radius * radius) ? std::sqrt(radius * radius - x * x) : 0;
99}
100
101std::pair<double, double> calculate_y_range(
102 double x_left, double x_right, double Rin, double Rout)
103{
104 double max_y_abs;
105 double min_y_abs;
106 /*
107 * Have 5 cases:
108 * (1) Stave wholly on the left of inner radius
109 * (2) Stave wholly on the left, but within inner radius
110 * (3) Stave crosses the middle x=0
111 * (4) Stave wholly on the right, but within inner radius
112 * (5) Stave wholly on the right of inner radius
113 */
114 if (x_right < -Rin) {
115 // Stave is completely on the left of inner radius
116 min_y_abs = 0;
117 max_y_abs = calculate_y_circle(x_left, Rout);
118 } else if (x_left < -Constants::sensor2x1_width) {
119 // Stave is completely on the left, but within inner radius
120 min_y_abs = calculate_y_circle(x_right, Rin);
121 max_y_abs = calculate_y_circle(x_left, Rout);
122 } else if (x_left < 0) {
123 // Stave crosses the middle x=0
124 min_y_abs = Rin;
125 // x_right should be > 0, but might have FLP issues, so do abs nonetheless
126 max_y_abs = calculate_y_circle(std::max(std::abs(x_left), std::abs(x_right)), Rout);
127 } else if (x_left < Rin) {
128 // Stave is completely on the right, but within inner radius
129 min_y_abs = calculate_y_circle(x_left, Rin);
130 max_y_abs = calculate_y_circle(x_right, Rout);
131 } else {
132 // Stave is completely on the right of inner radius
133 min_y_abs = 0.;
134 max_y_abs = calculate_y_circle(x_right, Rout);
135 }
136 return {min_y_abs, max_y_abs};
137}
138
139/*
140 * This function is a helper function to determine the positions of sensors on the stave
141 * by adding sensors until there is no more space available.
142 *
143 * Arguments:
144 * y_positions: a pair of vectors, where each vector contains pairs of
145 * y position and stack height for the positive and negative y positions respectively.
146 * This argument will be appended with the new sensor positions and stack heights.
147 * Rout: the outer radius of the layer
148 * Rin: the inner radius of the layer
149 * x_left: the x position of the left edge of the sensor to be placed
150 * kSensorStack: the number of sensors to be stacked on top of each other
151 * y_ranges: the y positions to start and end placing sensors,
152 * for positive and negative y respectively
153 * absAllowedYRange: the absolute y range allowed for placing sensors,
154 * used to cut placement if they go past allowed tolerances
155 */
156void FT3Module::fill_stave(PosNegPositionTypes& y_positions, double Rin, double Rout,
157 double x_left, unsigned kSensorStack, PositionRangeType y_ranges,
158 std::pair<double, double>& absAllowedYRange)
159{
160 // start with upper half of the stave, then mirror to the bottom half
161 // add the height of kSensorStack sensors + the gaps in between them
162 double sensorStackHeight = Constants::getStackHeight(kSensorStack);
163 double sensorAbsStackYShift = sensorStackHeight + Constants::stackGap;
164
165 // in case a big tolerance is given, cut on the given range instead
166 double max_sensor_y_abs = std::min(absAllowedYRange.second, y_ranges.first.second);
167
168 double y_top; // top half of the xy grid, y>0
169 // either start at given value (adjusted for tolerance), or at last placed sensors
170 if (!y_positions.first.empty()) { // sensors already placed
171 double previousStackHeight = Constants::getStackHeight(y_positions.first.back().second);
172 y_top = y_positions.first.back().first + previousStackHeight + Constants::stackGap;
173 } else if (absAllowedYRange.first > 0) {
174 // there is a minimum inner value --> start at the max of the two
175 y_top = std::max(absAllowedYRange.first, y_ranges.first.first);
176 } else {
177 // No inner minimum value, start at given value
178 y_top = y_ranges.first.first;
179 }
180 // fill positive y sensor positions
181 while ((y_top + sensorStackHeight) <= max_sensor_y_abs) {
182 y_positions.first.emplace_back(y_top, kSensorStack);
183 y_top += sensorAbsStackYShift;
184 }
185
186 // now we do the same for the negative y positions
187 // they do not have to be exactly mirrored, hence done separately
188 double y_bottom;
189 if (!y_positions.second.empty()) {
190 // subtract instead to move further down
191 double previousStackHeight = Constants::getStackHeight(y_positions.second.back().second);
192 y_bottom = y_positions.second.back().first - previousStackHeight - Constants::stackGap;
193 } else if (absAllowedYRange.first > 0) {
194 // there is a minimum inner value --> start at the min of the two
195 y_bottom = std::min(-absAllowedYRange.first, y_ranges.second.first);
196 } else {
197 // No inner minimum value, start at given value
198 y_bottom = y_ranges.second.first;
199 }
200 // fill in the sensors on negative y
201 while ((y_bottom - sensorStackHeight) >= -max_sensor_y_abs) {
202 y_positions.second.emplace_back(y_bottom, kSensorStack);
203 y_bottom -= sensorAbsStackYShift;
204 }
205}
206
207/*
208 * Create the vertices of the triangles that make up the stave cross section
209 *
210 * Each array of 3 corresponds to x or z values of the 3 triangle vertices,
211 * and the outer array corresponds to which triangle:
212 *
213 * [x_outer, z_outer, x_inner, z_inner], each of which has three values
214 */
215std::array<std::array<double, 3>, 4> buildStaveTriangle(int direction)
216{
217 // Set some constants for readability
220 /*
221 * Inner and outer vertices of the stave cross section triangle
222 * all vertices are at y_mid, we simply extend the triangle into y dir.
223 * We work in the local coordinate system of the stave, but still
224 * call the coordinates x and z for readability.
225 *
226 * 1. Get all local coordinates of the two triangle vertices
227 * 2. Extrude a volume from the subtracted triangle cross section area
228 * 3. Rotate the volume around the x-axis since it is by default in xy,
229 * and extruded in z. Rotate by -90 for xz -> xy, otherwise xz -> x(-y)
230 * 4. Translate the volume to the given position (arguments)
231 *
232 */
233 std::array<double, 3> xv_inner, xv_outer, zv_inner, zv_outer;
234 // calculate the coordinates of the triangle vertices
235 // Top/bottom vertex (apex)
236 xv_outer[0] = 0;
237 zv_outer[0] = (direction == 1) ? -H
238 : H;
239 ;
240 // right
242 zv_outer[1] = 0;
243 // left
244 xv_outer[2] = -xv_outer[1];
245 zv_outer[2] = 0;
246
247 // now get inner vertices, shifted inwards by effective carbon thickness
248 xv_inner[0] = xv_outer[0];
249 double z_shift_inner = d / Constants::sinTheta;
250 zv_inner[0] = (direction == 1) ? zv_outer[0] + z_shift_inner
251 : zv_outer[0] - z_shift_inner;
252 // face vertices, first right
253 zv_inner[1] = (direction == 1) ? zv_outer[1] - d
254 : zv_outer[1] + d;
255 double x_shift_abs = d / TMath::Tan(Constants::alpha / 2);
256 xv_inner[1] = xv_outer[1] - x_shift_abs;
257 // left
258 zv_inner[2] = zv_inner[1];
259 xv_inner[2] = -xv_inner[1];
260
261 return {xv_outer, zv_outer, xv_inner, zv_inner};
262}
263
264/*
265 * This function creates a carbon fibre volume for the stave,
266 * onto which the sensor and its support will be glued.
267 */
268void FT3Module::addStaveVolume(
269 TGeoVolume* motherVolume, std::string volumeName, int direction,
270 unsigned* volume_count, double staveLength,
271 std::array<std::array<double, 3>, 4> staveTriangles,
272 std::pair<double, double>& absAllowedYRange,
273 double x_mid, double y_mid, double z_stave_shift_forward)
274{
275 // The allowed y range is assumed to be non-negative.
276 if (absAllowedYRange.first < 0 || absAllowedYRange.second < 0 ||
277 absAllowedYRange.first >= absAllowedYRange.second) {
278 LOG(error) << "Invalid allowed y range in addStaveVolume(): ("
279 << absAllowedYRange.first << ", " << absAllowedYRange.second
280 << "). Both values must be non-negative and the first "
281 << "value must be less than the second value.";
282 return;
283 }
284 // Set the lower and upper y values of the stave:
285 double y_lower = y_mid - staveLength / 2;
286 double y_upper = y_mid + staveLength / 2;
287 bool splitStave = false;
288 if (y_lower > 0) { // This stave is fully above x-axis
289 y_lower = std::max(y_lower, absAllowedYRange.first);
290 y_upper = std::min(y_upper, absAllowedYRange.second);
291 } else if (y_upper < 0) { // stave entirely below x-axis
292 y_lower = std::max(y_lower, -absAllowedYRange.second);
293 y_upper = std::min(y_upper, -absAllowedYRange.first);
294 } else { // Full range stave that goes across x-axis
295 // Here we might have to cut the stave up into two pieces
296 if (absAllowedYRange.first > 0) {
297 // There is a minimum inner value --> Split stave
298 splitStave = true;
299 y_lower = absAllowedYRange.first;
300 } else {
301 // regular stave, use full length, but don't forget outer cut
302 y_lower = std::max(y_lower, -absAllowedYRange.second);
303 }
304 y_upper = std::min(y_upper, absAllowedYRange.second);
305 }
306 double staveLengthToUse = y_upper - y_lower;
307 /*
308 * create the extruded volumes from z=0 (later y=0 after rotation) to stave length
309 * and not from midpoint - staveLength/2 to midpoint + staveLength/2, translate later
310 *
311 * Note also that we first need to check if the length is allowed given the inner
312 * and outer radius of the layer.
313 */
314 TGeoXtru* staveFull = new TGeoXtru(2);
315 staveFull->SetName((volumeName + "_Xtru_outer").c_str());
316 staveFull->DefinePolygon(3, staveTriangles[0].data(), staveTriangles[1].data());
317 staveFull->DefineSection(0, 0);
318 staveFull->DefineSection(1, staveLengthToUse);
319
320 TGeoXtru* staveInner = new TGeoXtru(2);
321 staveInner->SetName((volumeName + "_Xtru_inner").c_str());
322 staveInner->DefinePolygon(3, staveTriangles[2].data(), staveTriangles[3].data());
323 staveInner->DefineSection(0, 0);
324 staveInner->DefineSection(1, staveLengthToUse);
325
326 TGeoCompositeShape* staveShape = new TGeoCompositeShape(
327 (volumeName + "_shape").c_str(),
328 Form("%s - %s", staveFull->GetName(), staveInner->GetName()));
329 TGeoVolume* staveVolume = new TGeoVolume(
330 (volumeName).c_str(),
331 staveShape,
333 staveVolume->SetLineColor(Constants::carbonFiberColor);
334 staveVolume->SetFillColorAlpha(Constants::carbonFiberColor, 0.4);
335
336 TGeoRotation* rot = new TGeoRotation();
337 rot->RotateX(-90); // lift from xy plane into xz plane
338 /*
339 * After rotations the face of the stave lies in the xy-plane,
340 * facing downwards for direction == 1 and upwards for direction == 0.
341 * We still need to shift it in z to get the right staggered layout.
342 * This means moving the staves that must be shifted in the opposite
343 * direction they are facing: up for direction 1, and down for direction 0.
344 *
345 * Unlike a regular node placement, we have to put the stave at its
346 * starting point in y, not the midpoint. Hence, if we have the mirror,
347 * the starting point is the upper y value, since that is the bottom
348 * of the mirrored stave -- by the outer radius
349 */
350 double z_shift = (direction == 1) ? z_stave_shift_forward : -z_stave_shift_forward;
351 TGeoCombiTrans* combiTrans =
352 new TGeoCombiTrans(x_mid, y_lower, z_shift, rot);
353 motherVolume->AddNode(staveVolume,
354 *volume_count,
355 combiTrans);
356 (*volume_count)++;
357
358 // if the stave needs to be split, reuse the same volume on opposite side
359 if (splitStave) {
360 TGeoCombiTrans* combiTransSplit =
361 new TGeoCombiTrans(x_mid, -y_upper, z_shift, rot);
362 motherVolume->AddNode(staveVolume,
363 *volume_count,
364 combiTransSplit);
365 (*volume_count)++;
366 }
367}
368
369/*
370 * Generic helper function that adds a box at the given position with
371 * the given dimensions to the given mother volume, with the given color and name.
372 */
373
374void FT3Module::addDetectorVolume(
375 TGeoVolume* motherVolume, std::string volumeName, int color, TGeoMedium* med,
376 unsigned volume_count, double x_mid, double y_mid, double z_mid,
377 double x_half_length, double y_half_length, double z_half_length)
378{
379 TGeoManager* geoManager = gGeoManager;
380 TGeoVolume* volume = geoManager->MakeBox(volumeName.c_str(), med, x_half_length,
381 y_half_length, z_half_length);
382 volume->SetLineColor(color);
383 volume->SetFillColorAlpha(color, 0.4);
384 motherVolume->AddNode(
385 volume,
386 volume_count,
387 new TGeoTranslation( // midpoint of box to add
388 x_mid,
389 y_mid,
390 z_mid) // TGeoTranslation
391 ); // addNode
392}
393
394/*
395 * This function adds a glue volume between two element layers,
396 * immediately for a whole 2x1 layout, under both the active and inactive region.
397 */
398void FT3Module::add2x1GlueVolume(
399 TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
400 unsigned volume_count, double x_mid, double y_mid, double z_mid,
401 std::string element_glued_to)
402{
403 std::string glue_name = "FT3glue_" + element_glued_to + "_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count);
404 addDetectorVolume(
405 motherVolume, glue_name, Constants::glueColor, epoxyMed, volume_count,
406 x_mid, y_mid, z_mid,
408}
409
410/*
411 * This function adds a copper volume onto which the silicon sensor is glued.
412 * As with the glue, this is a whole 2x1 layout volume.
413 */
414void FT3Module::add2x1CopperVolume(
415 TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
416 unsigned volume_count, double x_mid, double y_mid, double z_mid)
417{
418 std::string copper_name = "FT3Copper_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count);
419 addDetectorVolume(
420 motherVolume, copper_name, Constants::CuColor, copperMed, volume_count,
421 x_mid, y_mid, z_mid,
423}
424
425/*
426 * This function adds a kapton volume behind the copper, which represents the ???
427 * As with copper and glue, this is a whole 2x1 layout volume.
428 */
429void FT3Module::add2x1KaptonVolume(
430 TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
431 unsigned volume_count, double x_mid, double y_mid, double z_mid)
432{
433 std::string kapton_name = "FT3Kapton_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count);
434 addDetectorVolume(
435 motherVolume, kapton_name, Constants::kaptonColor, kaptonMed, volume_count,
436 x_mid, y_mid, z_mid,
438}
439
440/*
441 * This function adds a single sensor (currently 2.5x3.2cm) to the given mother volume
442 * at the given (x,y,z) position of the module.
443 *
444 * Because the sensor has an inactive region of 2mm on one side, we also add a
445 * separate volume for the inactive region, which will be either on the left or
446 * or right dependent on the if the sensor is on the left or right in a 2x1 layout.
447 * See FT3Module.h for more details on the layout.
448 *
449 * Arguments:
450 * motherVolume: the volume to which the sensor volume will be added
451 * layerNumber: the layer number of the sensor, used for naming
452 * direction: the direction of the sensor (forward or backward eta), used for naming
453 * x_mid: the x position of the center of the sensor volume
454 * y_mid: the y position of the center of the sensor volume
455 * z_mid: the z position of the center of the sensor volume
456 * isLeft: whether the sensor is on the left or right in the 2x1 layout
457 */
458void FT3Module::addSingleSensorVolume(
459 TGeoVolume* motherVolume, int layerNumber, int direction, unsigned stave_idx,
460 unsigned volume_count, double active_x_mid, double y_mid, double z_mid,
461 bool isLeft)
462{
463 TGeoVolume* sensor;
464 TGeoManager* geoManager = gGeoManager;
465 // ACTIVE AREA
466 std::string sensor_name = "FT3Sensor_Active_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count);
467 addDetectorVolume(
468 motherVolume, sensor_name, Constants::SiColor, siliconMed,
469 volume_count, active_x_mid, y_mid, z_mid,
471
472 // INACTIVE STRIP ON LEFT OR RIGHT
473 double inactive_x_mid = isLeft ? (active_x_mid - Constants::active_width / 2 - Constants::inactive_width / 2)
474 : (active_x_mid + Constants::active_width / 2 + Constants::inactive_width / 2);
475 std::string sensor_inactive_name = "FT3Sensor_Inactive_" + std::to_string(direction) + "_" + std::to_string(layerNumber) + "_" + std::to_string(stave_idx) + "_" + std::to_string(volume_count);
476 sensor = geoManager->MakeBox(sensor_inactive_name.c_str(), siliconMed, Constants::inactive_width / 2,
478 addDetectorVolume(
479 motherVolume, sensor_inactive_name, Constants::SiInactiveColor, siliconMed,
480 volume_count, inactive_x_mid, y_mid, z_mid,
482}
483
484void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction,
485 double Rin, double Rout, double z_offset_local,
486 const Constants::StaveConfig& staveConfig,
487 TGeoVolume* motherVolume)
488{
489 LOG(debug) << "FT3Module: create_layout_staveGeo - Direction "
490 << direction << ", Layer " << layerNumber;
491
493 auto& ft3Params = o2::ft3::FT3BaseParam::Instance();
494
495 // First let's define some constants used throughout
496 /*
497 * we build the volume from the outside in, starting with the silicon,
498 * then glue & materials towards the stave. Depending on direction,
499 * the distance from the center will be mirrored.
500 *
501 * | SILICON SENSOR | GLUE | COPPER | KAPTON | GLUE | CARBON STAVE |
502 * ----------------------------------------------------------------> z
503 *
504 * Naturally, this will be mirrored for layers in the backwards direction,
505 * such that the face of the sensors always face the interaction region.
506 *
507 * Currently, we stipulate that the default stave face is at local z=0,
508 * that is then shifted by the half air thickness encapsulating the layer
509 * to avoid overlaps with the air and services. All offsets are
510 * calculated for backward direction (since that is a positive shift),
511 * and then flipped for forward. At that point, the innermost/frontmost
512 * stave face is at the edge of the air volume, so we shift it back a little
513 * to make space for the sensor materials and a slight margin.
514 */
515 double totalSensorMaterialThickness =
518 double z_offset_to_carbon_face = z_offset_local - totalSensorMaterialThickness - 0.1;
519 double z_offset_to_glue_Ka =
520 z_offset_to_carbon_face + Constants::epoxyThickness / 2;
521 double z_offset_to_kapton =
522 z_offset_to_carbon_face + Constants::epoxyThickness +
524 double z_offset_to_copper =
525 z_offset_to_carbon_face + Constants::epoxyThickness +
527 double z_offset_to_glue_Si =
528 z_offset_to_carbon_face + Constants::epoxyThickness + Constants::kaptonThickness +
530 double z_offset_to_silicon =
531 z_offset_to_carbon_face + Constants::epoxyThickness +
534
535 // initialise all y_positions, vector over all staves/columns
536 std::vector<PosNegPositionTypes> y_positionsPosNeg;
537 // stave triangle cross sections are the same for every stave (direction based)
538 std::array<std::array<double, 3>, 4> staveTriangles = buildStaveTriangle(direction);
539 // declare vector with number of 2xn sensor stacks (modules) -- only used for logging
540 // each entry is a vector, where each entry is the number of modules of that stack height
541 std::vector<std::vector<unsigned>> nSensorStackCountPerStave(
542 staveConfig.x_midpoints.size(),
543 std::vector<unsigned>(Constants::kSensorsPerStack.size(), 0));
544 std::vector<unsigned> nSensorStackTotal(Constants::kSensorsPerStack.size(), 0);
545 unsigned staveVolumeCount = 0;
546 for (unsigned i_stave = 0; i_stave < staveConfig.x_midpoints.size(); i_stave++) {
547 y_positionsPosNeg.emplace_back(PosNegPositionTypes{PositionTypes{}, PositionTypes{}});
548 const int staveID = Constants::staveIdxToID(i_stave, staveConfig.x_midpoints.size());
549
550 double y_midpoint = 0.;
551 bool mirrorStaveAroundX = false;
552 // default positive and negative starting points has a gap around x-axis for symmetry
553 double stave_half_length = staveConfig.y_lengths[i_stave] / 2;
554 PositionRangeType y_ranges;
555 if (ft3Params.placeSensorStackInMiddleOfStave) {
556 /*
557 * We want a sensor stack to cross over the x-axis for coverage at y=0
558 * N.B. not necessarily exactly mirrored, only if stack gap is the same
559 * as the gap between sensors in a stack. Since we start filling with the
560 * first value in the kSensorsPerStack vector, we offset the first position
561 * by half of that.
562 *
563 * NOTE: TODO: in case the stave is too short to fit one full stack over the middle,
564 * then we will not be able to place anything since the bottom right/left point of
565 * the module will already be outside of acceptable bounds -- killing further placement.
566 */
568 y_ranges = {{-stackHeight / 2, stave_half_length},
569 {-stackHeight / 2 - Constants::stackGap, -stave_half_length}};
570 } else {
571 /*
572 * Otherwise have a gap around y=0, so sensors are not placed there.
573 * This means the stave is perfectly mirrored around the x-axis.
574 */
575 y_ranges = {{Constants::stackGap / 2, stave_half_length},
576 {-Constants::stackGap / 2, -stave_half_length}};
577 }
578 auto y_midpoint_it = staveConfig.staveID_to_y_midpoint.find(staveID);
579 if (y_midpoint_it != staveConfig.staveID_to_y_midpoint.end()) {
580 // there is a defined midpoint for this stave, use this for starting points
581 y_midpoint = y_midpoint_it->second.first; // avoid double map lookup
582 mirrorStaveAroundX = y_midpoint_it->second.second;
583 y_ranges.first = {y_midpoint - stave_half_length, y_midpoint + stave_half_length};
584 y_ranges.second = {-y_midpoint + stave_half_length, -y_midpoint - stave_half_length};
585 }
586
587 // Define tolerances for cutting staves and placing sensors
588 double tolerance_inner, tolerance_outer;
589 if (staveConfig.isML) {
590 tolerance_inner = ft3Params.staveTolMLInner;
591 tolerance_outer = ft3Params.staveTolMLOuter;
592 } else {
593 tolerance_inner = ft3Params.staveTolOTInner;
594 tolerance_outer = ft3Params.staveTolOTOuter;
595 }
596 // cut staves on nominal inner radius if specified
597 if (tolerance_inner > staveConfig.maxToleranceInner) {
598 tolerance_inner = staveConfig.maxToleranceInner;
599 }
600 if (tolerance_outer > staveConfig.maxToleranceOuter) {
601 tolerance_outer = staveConfig.maxToleranceOuter;
602 }
603
604 /*
605 * There are two cases in which we want to mirror the stave around the x-axis,
606 * which correspond to the stave not going fully from + to - Rout in y.
607 *
608 * (1) The inner tolerance is 0 (or negative)
609 * a) AND either x_left or x_right lies within the inner radius
610 * (2) The inner tolerance is large enough to allow stave placement as wished
611 * a) AND the given stave midpoint is above the inner radius
612 */
613 double x_left = staveConfig.x_midpoints[i_stave] - Constants::sensor2x1_width / 2;
614 double x_right = x_left + Constants::sensor2x1_width;
615 std::pair<double, double> absAllowedYRange =
616 calculate_y_range(x_left, x_right, Rin, Rout);
617
618 /*
619 * Shift allowed range by tolerance. Note that both values in the range must
620 * be non-negative, and if the inner is not, then set it to 0. This just means
621 * that there is no lower limit. The upper limit must however be larger than 0,
622 * if it is not, then skip this stave and give a warning.
623 */
624 absAllowedYRange.first -= tolerance_inner;
625 absAllowedYRange.second += tolerance_outer;
626
627 if (absAllowedYRange.first < 0) {
628 absAllowedYRange.first = 0;
629 }
630 if (absAllowedYRange.second <= 0) {
631 LOG(warning) << "For stave " << i_stave << " in layer " << layerNumber
632 << " with direction " << direction << ": no space to place sensors after applying tolerances, skipping stave.";
633 continue;
634 }
635
636 // Get whether the stave is shifted backward or not before creating
637 double z_stave_shift_abs = staveConfig.staveOnFront[i_stave] ? 0 : Constants::z_offsetStave(staveConfig.x_midpoint_spacing);
638 double z_stave_shift_forward = // move staves more inward to fit in layer volume
639 -z_offset_to_carbon_face + z_stave_shift_abs;
640 std::string stave_volume_name =
641 "FT3_Stave_" + std::to_string(direction) + "_" + std::to_string(layerNumber) +
642 "_" + std::to_string(i_stave);
643
644 // Create the stave volumes and fill the y positions where to put sensors on the stave
645 addStaveVolume(
646 motherVolume, stave_volume_name, direction, &staveVolumeCount,
647 staveConfig.y_lengths[i_stave], staveTriangles, absAllowedYRange,
648 staveConfig.x_midpoints[i_stave], y_midpoint, z_stave_shift_forward);
649 // Now create the mirrored stave
650 if (mirrorStaveAroundX) {
651 addStaveVolume(
652 motherVolume, stave_volume_name + "_mirrored", direction, &staveVolumeCount,
653 staveConfig.y_lengths[i_stave], staveTriangles, absAllowedYRange,
654 staveConfig.x_midpoints[i_stave], -y_midpoint, z_stave_shift_forward);
655 }
656
657 // now add the sensor positions on the stave
658 for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) {
659 unsigned nModulesCurr = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size();
660 fill_stave(y_positionsPosNeg.back(), Rin, Rout, x_left,
661 Constants::kSensorsPerStack[i_kSens], y_ranges,
662 absAllowedYRange);
663 unsigned nModulesAdded = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size() - nModulesCurr;
664 nSensorStackCountPerStave[i_stave][i_kSens] = nModulesAdded;
665 nSensorStackTotal[i_kSens] += nModulesAdded;
666 }
667 std::string moduleDebugStr = "Module size counts for layer " + std::to_string(layerNumber) + " in direction " + std::to_string(direction) + ":\n";
668 for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) {
669 moduleDebugStr += "\t" + std::to_string(nSensorStackCountPerStave[i_stave][i_kSens]) + " modules with " + std::to_string(Constants::kSensorsPerStack[i_kSens]) + " sensors stacked\n";
670 }
671 LOG(debug) << moduleDebugStr;
672 }
673 std::string totalModuleInfoStr =
674 "Total module size counts for layer " + std::to_string(layerNumber) +
675 " in direction " + std::to_string(direction) + ":\n";
676 for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) {
677 totalModuleInfoStr += "\t" + std::to_string(nSensorStackTotal[i_kSens]) + " modules with " + std::to_string(Constants::kSensorsPerStack[i_kSens]) + " sensors stacked\n";
678 }
679 LOG(info) << totalModuleInfoStr;
680
681 // Create volumes for the sensors and the support materials on top of the stave
682 for (unsigned i_stave = 0; i_stave < staveConfig.x_midpoints.size(); i_stave++) {
683 double x_mid = staveConfig.x_midpoints[i_stave];
684 int staveID = Constants::staveIdxToID(i_stave, staveConfig.x_midpoints.size());
685 /*
686 * Declare an offset multiplier for the z offsets, used for distinguishing
687 * sensors facing either forward or backward.
688 *
689 * In the stave layout, all sensors face inward, and isFront
690 * refers to whether a stave is shifted backwards or not. Thus,
691 * we decide the offset multiplier only with direction, to
692 * keep the face facing inwards.
693 */
694 bool isFront;
695 if (direction == 1) { // direction = 1 is forward
696 isFront = staveConfig.staveOnFront[i_stave];
697 } else {
698 isFront = !(staveConfig.staveOnFront[i_stave]);
699 }
700 int z_offset_multiplier = (direction == 1) ? -1 : 1;
701
702 // Get whether the stave is shifted for staggering or not
703 double z_stave_shift = 0;
704 if (!staveConfig.staveOnFront[i_stave]) {
705 // in forward direction, shifting backwards means +z shift
706 z_stave_shift = (direction == 1) ? Constants::z_offsetStave(staveConfig.x_midpoint_spacing)
708 }
709
710 unsigned sensor_count = 0; // reset for each stave
711 for (int y_sign = -1; y_sign < 2; y_sign += 2) {
712 // place sensors at positive and negative y
713 const auto& positions = (y_sign == 1) ? y_positionsPosNeg[i_stave].first
714 : y_positionsPosNeg[i_stave].second;
715 // define starting midpoint: y = y_start +- distance to middle of sensor
716 for (unsigned i_y_pos = 0; i_y_pos < positions.size(); i_y_pos++) {
717 double y_mid = positions[i_y_pos].first + y_sign * Constants::sensor2x1_height / 2;
718 for (unsigned i_sens = 0; i_sens < positions[i_y_pos].second; i_sens++) {
719 TGeoVolume* sensor;
720 // ------------ (1) Silicon sensor ------------
721 // left single sensor of the 2x1: place right edge half of sensor gap from center
722 double z_mid = z_offset_to_silicon * z_offset_multiplier + z_stave_shift;
723 addSingleSensorVolume(
724 motherVolume, layerNumber, direction, i_stave, sensor_count,
726 y_mid, z_mid, true);
727 // right single sensor of the 2x1: place left edge half of sensor gap from center
728 addSingleSensorVolume(
729 motherVolume, layerNumber, direction, i_stave, sensor_count + 1,
731 y_mid, z_mid, false);
732 // ------------ (2) Epoxy glue layer between silicon and copper (FPC) ------------
733 z_mid = z_offset_to_glue_Si * z_offset_multiplier + z_stave_shift;
734 add2x1GlueVolume(
735 motherVolume, layerNumber, direction, i_stave, sensor_count,
736 x_mid, y_mid, z_mid, "SiCu");
737 // ------------ (3) Copper layer (FPC) ------------
738 z_mid = z_offset_to_copper * z_offset_multiplier + z_stave_shift;
739 add2x1CopperVolume(
740 motherVolume, layerNumber, direction, i_stave, sensor_count,
741 x_mid, y_mid, z_mid);
742 // ------------ (4) Kapton layer (FPC) ------------
743 z_mid = z_offset_to_kapton * z_offset_multiplier + z_stave_shift;
744 add2x1KaptonVolume(
745 motherVolume, layerNumber, direction, i_stave, sensor_count,
746 x_mid, y_mid, z_mid);
747 // ------------ (5) Epoxy glue layer between stave and Kapton ------------
748 z_mid = z_offset_to_glue_Ka * z_offset_multiplier + z_stave_shift;
749 add2x1GlueVolume(
750 motherVolume, layerNumber, direction, i_stave, sensor_count,
751 x_mid, y_mid, z_mid, "CarbonKapton");
752 // increment to next sensor: (height + gap of one sensor)
754 sensor_count += 2; // same count for each material in the glued stack of materials
755 } // sensors in stack
756 } // for y_sign (writing of positive or negative y positions)
757 } // i_y_pos
758 } // i_stave
759}
760
761void FT3Module::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)
762{
763
764 LOG(debug) << "FT3Module: create_layout - Layer " << layerNumber << ", Direction " << direction << ", Face " << face;
765 TGeoManager* geoManager = gGeoManager;
766
768
769 // double sensor_width = 2.5;
770 // double sensor_height = 9.6;
771 // double active_width = 2.3;
772 // double active_height = 9.6;
773
774 double sensor_width = 5.0;
775 double sensor_height = 9.6;
776 double inactive_width = 0.2; // per side
777 double active_width = 4.6;
778 double active_height = 9.6;
779
780 double silicon_thickness = 0.01;
781 double copper_thickness = 0.006;
782 double kapton_thickness = 0.03;
783 double epoxy_thickness = 0.0012;
784
785 double carbonFiberThickness = 0.01;
786
787 double foamSpacingThickness = 1.0;
788
789 int dist_offset = 0;
790
791 double x_offset;
792 double y_offset;
793
794 double z_offset = (face == "front") ? -foamSpacingThickness / 2.0 - carbonFiberThickness : foamSpacingThickness / 2.0 + carbonFiberThickness;
795
796 // offset correction
797 if (sensor_height == 3.2 && sensor_width == 2.5) {
798 x_offset = 0.8;
799 y_offset = 1.5;
800 } else if (sensor_height == 19.2 && sensor_width == 5) {
801 x_offset = 0.7;
802 y_offset = 9;
803 } else {
804 x_offset = sensor_width / 2;
805 y_offset = sensor_height / 2;
806 }
807
808 double x_condition_min = 0;
809 double x_condition_max = 0;
810 double offset_Rin_lower = 0;
811 double offset_Rin_upper = 0;
812 bool adjust_bottom_y_pos = false;
813 bool adjust_bottom_y_neg = false;
814 double x_adjust_bottom_y_pos = 0;
815 double bottom_y_pos_value = 0;
816 double bottom_y_neg_value = 0;
817
818 double Rin_offset = (sensor_height == 19.2) ? 1 : 0;
819 double Rout_offset = (sensor_height == 19.2) ? 1 : 0;
820
821 if (Rin == 7 && sensor_height == 9.6 && sensor_width == 5) {
822 x_condition_min = -Rin - 2;
823 x_condition_max = Rin;
824 dist_offset = 2;
825 adjust_bottom_y_pos = true;
826 adjust_bottom_y_neg = true;
827 x_adjust_bottom_y_pos = 3.5;
828 bottom_y_pos_value = 3.5;
829 bottom_y_neg_value = -3.5;
830 } else if (Rin == 5 && sensor_height == 9.6 && sensor_width == 5) {
831 x_condition_min = -Rin - 6;
832 x_condition_max = Rin;
833 adjust_bottom_y_pos = true;
834 adjust_bottom_y_neg = true;
835 x_adjust_bottom_y_pos = 3.5;
836 bottom_y_pos_value = 3.5;
837 bottom_y_neg_value = -3.5;
838 } else if ((Rin == 5 || Rin == 7) && sensor_height == 19.2) {
839 x_condition_min = -Rin - 3;
840 x_condition_max = Rin - 0.2;
841 dist_offset = 2;
842 adjust_bottom_y_pos = false;
843 adjust_bottom_y_neg = false;
844 } else if (Rin == 5 && sensor_height == 3.2) {
845 x_condition_min = -(Rin + 2.6);
846 x_condition_max = Rin + 1.5;
847 adjust_bottom_y_pos = true;
848 adjust_bottom_y_neg = true;
849 x_adjust_bottom_y_pos = 3.5;
850 bottom_y_pos_value = 3.5;
851 bottom_y_neg_value = -3.5;
852 } else if (Rin == 7 && sensor_height == 3.2) {
853 x_condition_min = -Rin - 1;
854 x_condition_max = Rin - 0.2;
855 adjust_bottom_y_pos = true;
856 adjust_bottom_y_neg = true;
857 x_adjust_bottom_y_pos = 3.5;
858 bottom_y_pos_value = 3.5;
859 bottom_y_neg_value = -3.5;
860 } else if (Rin == 5 && sensor_height == 9.6 && sensor_width == 2.5) {
861 x_condition_min = -(Rin + 2.6);
862 x_condition_max = Rin;
863 adjust_bottom_y_pos = true;
864 adjust_bottom_y_neg = true;
865 x_adjust_bottom_y_pos = 3.5;
866 bottom_y_pos_value = 3.5;
867 bottom_y_neg_value = -3.5;
868 } else if (Rin == 7 && sensor_height == 9.6 && sensor_width == 2.5) {
869 x_condition_min = -Rin - 2.6;
870 x_condition_max = Rin + 1;
871 dist_offset = 2;
872 adjust_bottom_y_pos = true;
873 adjust_bottom_y_neg = true;
874 x_adjust_bottom_y_pos = 5.5;
875 bottom_y_pos_value = 3.5;
876 bottom_y_neg_value = -3.5;
877 } else if (Rin == 10 && sensor_height == 9.6 && sensor_width == 5.0) {
878 x_condition_min = -Rin - 4;
879 x_condition_max = Rin;
880 dist_offset = 2;
881 adjust_bottom_y_pos = false;
882 adjust_bottom_y_neg = false;
883 x_adjust_bottom_y_pos = 3.5;
884 bottom_y_pos_value = 3.5;
885 bottom_y_neg_value = -3.5;
886 } else if (Rin == 20 && sensor_height == 9.6 && sensor_width == 5.0) {
887 x_condition_min = -Rin - 4;
888 x_condition_max = Rin;
889 dist_offset = 2;
890 adjust_bottom_y_pos = false;
891 adjust_bottom_y_neg = false;
892 x_adjust_bottom_y_pos = 3.5;
893 bottom_y_pos_value = 3.5;
894 bottom_y_neg_value = -3.5;
895 } else {
896 LOG(warning) << "Different config - to determine offsets needed for " << "Rin = " << Rin << " ; sensor_height = " << sensor_height << " ; sensor_width = " << sensor_width << " layer " << layerNumber;
897 x_condition_min = -Rin - sensor_width;
898 x_condition_max = Rin;
899 adjust_bottom_y_pos = false;
900 adjust_bottom_y_neg = false;
901 }
902
903 offset_Rin_lower = Rin - Rin_offset;
904 offset_Rin_upper = Rout + Rout_offset;
905
906 std::set<std::pair<double, double>> placed_sensors;
907 int sensor_count = 0;
908
909 int placementCounter = 0;
910 bool justSkipped = false;
911
912 std::vector<double> X_positions;
913 std::vector<int> justSkipped1;
914
915 if (sensor_width == 2.5) {
916 // logic for placement - x positions with complete overlap
917 if (face == "front") {
918 X_positions = {-63.4, -60.9, -54.2, -51.7, -45.0, -42.5, -35.8, -33.3, -26.6, -24.1, -17.4, -14.9,
919 -8.2, -5.7, 1.0, 3.5, 10.2, 12.7, 19.4, 21.9, 28.6, 31.1, 37.8, 40.3, 47.0, 49.5,
920 56.2, 58.7, 65.4};
921 justSkipped1 = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
922 } else if (face == "back") {
923 X_positions = {-65.5, -58.8, -56.3, -49.6, -47.1, -40.4, -37.9, -31.2, -28.7, -22.0, -19.5, -12.8,
924 -10.3, -3.6, -1.1, 5.6, 8.1, 14.8, 17.3, 24.0, 26.5, 33.2, 35.7, 42.4, 44.9,
925 51.6, 54.1, 60.8, 63.3};
926 justSkipped1 = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
927 }
928 } else {
929 if (Rin == 10 || Rin == 20) { // v3 paving, rough attempt
930 float overlap = 0.3;
931 // NB: these are left edges
932 float X_start = -2.0 - 13.5 * (sensor_width - overlap);
933 float X_start_pos = 2.0 - 0.5 * (sensor_width - overlap);
934 if (face == "back") {
935 X_start += (sensor_width - overlap);
936 X_start_pos += (sensor_width - overlap);
937 }
938 while (X_start < -2) {
939 X_positions.push_back(X_start);
940 justSkipped1.push_back(1);
941 X_start += 2 * (sensor_width - overlap);
942 }
943 while (X_start_pos < Rout + x_offset - sensor_width) {
944 X_positions.push_back(X_start_pos);
945 justSkipped1.push_back(1);
946 X_start_pos += 2 * (sensor_width - overlap);
947 }
948 } else {
949 // filling for sensors with 2x width, each row skipped
950 if (face == "front") {
951 X_positions = {-63.4, -54.2, -45, -35.8, -26.6, -17.4, -8.2, 1., 10.2, 19.4, 28.6, 37.8, 47., 56.2, 65.4};
952 justSkipped1 = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
953 } else if (face == "back") {
954 X_positions = {-58.8, -49.6, -40.4, -31.2, -22, -12.8, -3.6, 5.6, 14.8, 24, 33.2, 42.4, 51.6, 60.8};
955 justSkipped1 = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
956 }
957 }
958 }
959
960 if (layout_type == "rectangular") {
961
962 double x_start = -Rout;
963 double x_end = Rout;
964
965 std::vector<double> x_positions;
966 for (double x = x_start; x <= x_end; x += sensor_width) {
967 x_positions.push_back(x);
968 }
969
970 int rowCounter = 0;
971 const int rowsToAlternate = 2;
972
973 for (size_t i = 0; i < X_positions.size(); ++i) {
974
975 double x = X_positions[i];
976 bool justSkippedValue = justSkipped1[i];
977
978 std::vector<double> y_positions_positive;
979 std::vector<double> y_positions_negative;
980
981 for (double y = -Rout - Rin_offset; y <= Rout + Rin_offset; y += sensor_height) {
982 std::vector<std::pair<double, double>> corners = {
983 {x, y},
984 {x + sensor_width, y},
985 {x, y + sensor_height},
986 {x + sensor_width, y + sensor_height}};
987
988 bool within_bounds = std::all_of(corners.begin(), corners.end(), [&](const std::pair<double, double>& corner) {
989 double cx = corner.first;
990 double cy = corner.second;
991 return (offset_Rin_lower <= std::sqrt(cx * cx + cy * cy) && std::sqrt(cx * cx + cy * cy) <= offset_Rin_upper);
992 });
993
994 if (within_bounds) {
995 if (y >= 0) {
996 y_positions_positive.push_back(y);
997 } else {
998 y_positions_negative.push_back(y);
999 }
1000 }
1001 }
1002
1003 // adjust y positions near inner circle for positive y
1004 if (x_condition_min <= x && x <= x_condition_max && !y_positions_positive.empty()) {
1005 double first_y_pos = y_positions_positive.front();
1006 double last_y_pos = y_positions_positive.back() - sensor_height;
1007 double top_y_pos = std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout));
1008 double bottom_y_pos = std::max(calculate_y_circle(x, Rin), calculate_y_circle(x + sensor_width, Rin));
1009 double top_distance_pos = top_y_pos - last_y_pos;
1010
1011 if (adjust_bottom_y_pos && x > x_adjust_bottom_y_pos) {
1012 bottom_y_pos = bottom_y_pos_value;
1013 }
1014
1015 double bottom_distance_pos = first_y_pos - bottom_y_pos;
1016
1017 if (std::abs(top_distance_pos + bottom_distance_pos) >= sensor_height) {
1018 for (auto& y : y_positions_positive) {
1019 y -= bottom_distance_pos - 0.2;
1020 }
1021 y_positions_positive.push_back(y_positions_positive.back() + sensor_height);
1022 }
1023 }
1024
1025 // adjust y positions near inner circle for negative y
1026 if (x_condition_min <= x && x <= x_condition_max && !y_positions_negative.empty()) {
1027 double first_y_neg = y_positions_negative.front();
1028 double last_y_neg = y_positions_negative.back() + sensor_height;
1029 double top_y_neg = -std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout));
1030 double bottom_y_neg = -std::max(calculate_y_circle(x, Rin), calculate_y_circle(x + sensor_width, Rin));
1031 double top_distance_neg = -(top_y_neg - first_y_neg);
1032
1033 if (adjust_bottom_y_neg && x > x_adjust_bottom_y_pos) {
1034 bottom_y_neg = bottom_y_neg_value;
1035 }
1036
1037 double bottom_distance_neg = -(last_y_neg - bottom_y_neg);
1038
1039 top_distance_neg = std::abs(top_distance_neg);
1040 bottom_distance_neg = std::abs(bottom_distance_neg);
1041 std::sort(y_positions_negative.begin(), y_positions_negative.end());
1042
1043 if (std::abs(top_distance_neg + bottom_distance_neg) >= sensor_height) {
1044 if (sensor_height == 19.2) {
1045 for (auto& y : y_positions_negative) {
1046 y -= bottom_distance_neg;
1047 }
1048 } else {
1049 for (auto& y : y_positions_negative) {
1050 y += bottom_distance_neg - 0.2;
1051 }
1052 }
1053 y_positions_negative.push_back(y_positions_negative.front() - sensor_height);
1054 }
1055 }
1056
1057 // adjust positions for the rest of the disk
1058 if ((x < x_condition_min || x > x_condition_max) && !y_positions_negative.empty() && !y_positions_positive.empty()) {
1059 double first_y_neg = y_positions_negative.front();
1060 double last_y_pos = y_positions_positive.back() + sensor_height;
1061 double top_y_pos = std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout));
1062 double bottom_y_pos = -top_y_pos;
1063
1064 double top_distance_pos = std::abs(top_y_pos - last_y_pos);
1065 double bottom_distance_pos = std::abs(first_y_neg - bottom_y_pos);
1066
1067 if (top_distance_pos + bottom_distance_pos >= sensor_height) {
1068 for (auto& y : y_positions_positive) {
1069 y += top_distance_pos - 0.2;
1070 }
1071 for (auto& y : y_positions_negative) {
1072 y += top_distance_pos - 0.2;
1073 }
1074 double new_y = y_positions_negative.front() - sensor_height;
1075
1076 if (static_cast<int>(new_y) > static_cast<int>(bottom_y_pos)) {
1077 y_positions_negative.push_back(new_y);
1078 }
1079 }
1080
1081 // Make symmetric adjustments
1082 std::sort(y_positions_negative.begin(), y_positions_negative.end());
1083 std::sort(y_positions_positive.begin(), y_positions_positive.end());
1084
1085 double first_y_pos = y_positions_negative.front();
1086
1087 last_y_pos = y_positions_positive.back() + sensor_height;
1088
1089 top_y_pos = std::min(calculate_y_circle(x, Rout), calculate_y_circle(x + sensor_width, Rout));
1090 bottom_y_pos = -top_y_pos;
1091 top_distance_pos = std::abs(top_y_pos - last_y_pos);
1092 bottom_distance_pos = std::abs(first_y_pos - bottom_y_pos);
1093
1094 double Lb = (bottom_distance_pos + top_distance_pos) / 2;
1095
1096 if (top_distance_pos < Lb) {
1097 double shift = Lb - top_distance_pos;
1098 for (auto& y : y_positions_negative) {
1099 y -= shift;
1100 }
1101 for (auto& y : y_positions_positive) {
1102 y -= shift;
1103 }
1104 } else if (top_distance_pos > Lb) {
1105 double shift = top_distance_pos - Lb;
1106 for (auto& y : y_positions_negative) {
1107 y += shift;
1108 }
1109 for (auto& y : y_positions_positive) {
1110 y += shift;
1111 }
1112 }
1113 }
1114
1115 std::vector<double> y_positions = y_positions_positive;
1116 y_positions.insert(y_positions.end(), y_positions_negative.begin(), y_positions_negative.end());
1117
1118 for (double y : y_positions) {
1119
1120 int SiColor;
1121 double R_material_threshold = 0;
1122
1123 if (placed_sensors.find({x, y}) == placed_sensors.end()) {
1124 placed_sensors.insert({x, y});
1125 TGeoVolume* sensor;
1126
1127 double inactive_width = (sensor_width - active_width) / 2;
1128 double left_inactive_x_shift;
1129 double right_inactive_x_shift;
1130 double active_x_shift_sensor;
1131
1132 if (face == "front") {
1133
1134 double active_x_shift, inactive_x_shift;
1135
1136 if (justSkippedValue) {
1137 active_x_shift = x + inactive_width / 2;
1138 active_x_shift_sensor = active_x_shift + inactive_width;
1139
1140 inactive_x_shift = x - active_width / 2 + inactive_width / 2;
1141 } else {
1142 active_x_shift = x - inactive_width / 2;
1143 active_x_shift_sensor = active_x_shift - inactive_width;
1144
1145 inactive_x_shift = x + active_width / 2 - inactive_width / 2;
1146 }
1147
1148 double inactive_x_shift_left, inactive_x_shift_right;
1149
1150 if (sensor_width == 5.0) {
1151
1152 inactive_x_shift_left = x - sensor_width / 2 + inactive_width;
1153 inactive_x_shift_right = x + sensor_width / 2;
1154 }
1155
1156 std::vector<std::pair<double, double>> corners_shifted = {
1157 {x, y},
1158 {x + sensor_width, y},
1159 {x, y + sensor_height},
1160 {x + sensor_width, y + sensor_height}};
1161
1162 bool within_bounds = true;
1163 for (const auto& corner : corners_shifted) {
1164 double cx = corner.first;
1165 double cy = corner.second;
1166 double dist = std::sqrt(cx * cx + cy * cy);
1167
1168 if (Rin > dist || dist >= Rout) {
1169 within_bounds = false;
1170 break;
1171 }
1172 }
1173
1174 if (within_bounds) {
1175
1176 double r_squared = (x + x_offset) * (x + x_offset) + (y + y_offset) * (y + y_offset);
1177
1178 if (r_squared < R_material_threshold * R_material_threshold) {
1179 silicon_thickness = 0.005;
1180 copper_thickness = 0.00475;
1181 kapton_thickness = 0.03;
1182 epoxy_thickness = 0.0012;
1183
1184 SiColor = kOrange;
1185 } else {
1186 silicon_thickness = 0.01;
1187 copper_thickness = 0.006;
1188 kapton_thickness = 0.03;
1189 epoxy_thickness = 0.0012;
1190
1191 SiColor = kGreen;
1192 }
1193
1194 if (sensor_width == 2.5) {
1195 // silicon
1196 std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1197 sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2);
1198 sensor->SetLineColor(SiColor);
1199 sensor->SetFillColorAlpha(SiColor, 0.4);
1200 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift_sensor + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2));
1201
1202 std::string inactive_name = "FT3inactive_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1203 sensor = geoManager->MakeBox(inactive_name.c_str(), siliconMed, (sensor_width - active_width) / 2, sensor_height / 2, silicon_thickness / 2);
1204 sensor->SetLineColor(kRed);
1205 sensor->SetFillColorAlpha(kRed, 1.0);
1206 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2));
1207
1208 } else {
1209
1210 std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1211 sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2);
1212 sensor->SetLineColor(SiColor);
1213 sensor->SetFillColorAlpha(SiColor, 0.4);
1214 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + x + inactive_width / 2, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2));
1215
1216 std::string inactive_name_left = "FT3inactive_left_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1217 sensor = geoManager->MakeBox(inactive_name_left.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2);
1218 sensor->SetLineColor(kRed);
1219 sensor->SetFillColorAlpha(kRed, 1.0);
1220 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_left, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2));
1221
1222 std::string inactive_name_right = "FT3inactive_right_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1223 sensor = geoManager->MakeBox(inactive_name_right.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2);
1224 sensor->SetLineColor(kRed);
1225 sensor->SetFillColorAlpha(kRed, 1.0);
1226 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_right, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness - silicon_thickness / 2));
1227 }
1228
1229 // silicon-to-FPC epoxy glue
1230 std::string glue_up_name = "FT3glue_up_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1231 sensor = geoManager->MakeBox(glue_up_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2);
1232 sensor->SetLineColor(kBlue);
1233 sensor->SetFillColorAlpha(kBlue, 1.0);
1234 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness - epoxy_thickness / 2));
1235
1236 if (r_squared < R_material_threshold * R_material_threshold) {
1237 std::string alu_name = "FT3aluminum_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1238 sensor = geoManager->MakeBox(alu_name.c_str(), AluminumMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2);
1239 sensor->SetLineColor(kBlack);
1240 sensor->SetFillColorAlpha(kBlack, 0.4);
1241 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness / 2));
1242
1243 } else {
1244 std::string copper_name = "FT3copper_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1245 sensor = geoManager->MakeBox(copper_name.c_str(), copperMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2);
1246 sensor->SetLineColor(kBlack);
1247 sensor->SetFillColorAlpha(kBlack, 0.4);
1248 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness - copper_thickness / 2));
1249 }
1250
1251 // kapton
1252 std::string fpc_name = "FT3fpc_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1253 sensor = geoManager->MakeBox(fpc_name.c_str(), kaptonMed, sensor_width / 2, sensor_height / 2, kapton_thickness / 2);
1254 sensor->SetLineColor(kGreen);
1255 sensor->SetFillColorAlpha(kGreen, 0.4);
1256 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset - epoxy_thickness - kapton_thickness / 2));
1257
1258 // FPC-to-support epoxy glue
1259 std::string glue_down_name = "FT3glue_down_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1260 sensor = geoManager->MakeBox(glue_down_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2);
1261 sensor->SetLineColor(kBlue);
1262 sensor->SetFillColorAlpha(kBlue, 1.0);
1263 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset - epoxy_thickness / 2));
1264 }
1265 } else {
1266 double x_shifted = x;
1267 double inactive_x_shift, active_x_shift;
1268 double active_x_shift_sensor;
1269
1270 if (justSkippedValue) {
1271 active_x_shift = x + inactive_width / 2;
1272 active_x_shift_sensor = active_x_shift + inactive_width;
1273
1274 inactive_x_shift = x - active_width / 2 + inactive_width / 2;
1275 } else {
1276 active_x_shift = x - inactive_width / 2;
1277 active_x_shift_sensor = active_x_shift - inactive_width;
1278
1279 inactive_x_shift = x + active_width / 2 - inactive_width / 2;
1280 }
1281
1282 double inactive_x_shift_left, inactive_x_shift_right;
1283
1284 if (sensor_width == 5.0) {
1285
1286 inactive_x_shift_left = x - sensor_width / 2 + inactive_width;
1287 inactive_x_shift_right = x + sensor_width / 2;
1288 }
1289
1290 std::vector<std::pair<double, double>> corners_shifted = {
1291 {x_shifted, y},
1292 {x_shifted + sensor_width, y},
1293 {x_shifted, y + sensor_height},
1294 {x_shifted + sensor_width, y + sensor_height}};
1295
1296 bool within_bounds = true;
1297 for (const auto& corner : corners_shifted) {
1298 double cx = corner.first;
1299 double cy = corner.second;
1300 double dist = std::sqrt(cx * cx + cy * cy);
1301
1302 if (Rin > dist + dist_offset || dist >= Rout) {
1303 within_bounds = false;
1304 break;
1305 }
1306 }
1307
1308 if (within_bounds) {
1309
1310 double r_squared = (x + x_offset) * (x + x_offset) + (y + y_offset) * (y + y_offset);
1311
1312 if (r_squared < R_material_threshold * R_material_threshold) {
1313 silicon_thickness = 0.005;
1314 copper_thickness = 0.00475; // thinner -> + replaced by alu
1315 kapton_thickness = 0.03;
1316 epoxy_thickness = 0.0006;
1317
1318 SiColor = kOrange;
1319 } else {
1320 silicon_thickness = 0.01;
1321 copper_thickness = 0.006;
1322 kapton_thickness = 0.03;
1323 epoxy_thickness = 0.0012;
1324
1325 SiColor = kGreen;
1326 }
1327
1328 // FPC-to-support epoxy glue
1329 std::string glue_down_name = "FT3glue_down_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1330 sensor = geoManager->MakeBox(glue_down_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2);
1331 sensor->SetLineColor(kBlue);
1332 sensor->SetFillColorAlpha(kBlue, 1.0);
1333 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset + epoxy_thickness / 2));
1334
1335 // Kapton
1336 std::string fpc_name = "FT3fpc_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1337 sensor = geoManager->MakeBox(fpc_name.c_str(), kaptonMed, sensor_width / 2, sensor_height / 2, kapton_thickness / 2);
1338 sensor->SetLineColor(kGreen);
1339 sensor->SetFillColorAlpha(kGreen, 0.4);
1340 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness / 2));
1341
1342 if (r_squared < R_material_threshold * R_material_threshold) {
1343 // replace copper with alu
1344 std::string alu_name = "FT3aluminum_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1345 sensor = geoManager->MakeBox(alu_name.c_str(), AluminumMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2);
1346 sensor->SetLineColor(kBlack);
1347 sensor->SetFillColorAlpha(kBlack, 0.4);
1348 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness / 2));
1349
1350 } else {
1351 std::string copper_name = "FT3copper_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1352 sensor = geoManager->MakeBox(copper_name.c_str(), copperMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2);
1353 sensor->SetLineColor(kBlack);
1354 sensor->SetFillColorAlpha(kBlack, 0.4);
1355 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness / 2));
1356 }
1357
1358 // silicon-to-FPC epoxy glue
1359 std::string glue_up_name = "FT3glue_up_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1360 sensor = geoManager->MakeBox(glue_up_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2);
1361 sensor->SetLineColor(kBlue);
1362 sensor->SetFillColorAlpha(kBlue, 1.0);
1363 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + active_x_shift, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness / 2));
1364
1365 if (sensor_width == 2.5) {
1366
1367 std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1368 sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2);
1369 sensor->SetLineColor(SiColor);
1370 sensor->SetFillColorAlpha(SiColor, 0.4);
1371 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(active_x_shift_sensor + x_offset, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2));
1372
1373 std::string inactive_name = "FT3inactive_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1374 sensor = geoManager->MakeBox(inactive_name.c_str(), siliconMed, (sensor_width - active_width) / 2, sensor_height / 2, silicon_thickness / 2);
1375 sensor->SetLineColor(kRed);
1376 sensor->SetFillColorAlpha(kRed, 1.0);
1377 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2));
1378
1379 } else {
1380 // active (4.6 cm centered)
1381 std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1382 sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2);
1383 sensor->SetLineColor(SiColor);
1384 sensor->SetFillColorAlpha(SiColor, 0.4);
1385 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + x_shifted + inactive_width / 2, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2));
1386
1387 // left inactive strip
1388 std::string inactive_name_left = "FT3inactive_left_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1389 sensor = geoManager->MakeBox(inactive_name_left.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2);
1390 sensor->SetLineColor(kRed);
1391 sensor->SetFillColorAlpha(kRed, 1.0);
1392 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_left, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2));
1393
1394 // right inactive strip
1395 std::string inactive_name_right = "FT3inactive_right_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count);
1396 sensor = geoManager->MakeBox(inactive_name_right.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2);
1397 sensor->SetLineColor(kRed);
1398 sensor->SetFillColorAlpha(kRed, 1.0);
1399 motherVolume->AddNode(sensor, sensor_count++, new TGeoTranslation(x_offset + inactive_x_shift_right, y + y_offset, mZ + z_offset + epoxy_thickness + kapton_thickness + copper_thickness + epoxy_thickness + silicon_thickness / 2));
1400 }
1401 }
1402 }
1403 }
1404 }
1405
1406 rowCounter++;
1407 }
1408 }
1409 LOG(debug) << "FT3Module: done create_layout";
1410}
1411
1412void FT3Module::createModule(double mZ, int layerNumber, int direction, double Rin, double Rout, double overlap, const std::string& face, const std::string& layout_type, TGeoVolume* motherVolume)
1413{
1414
1415 LOG(debug) << "FT3Module: createModule - Layer " << layerNumber << ", Direction " << direction << ", Face " << face;
1416 create_layout(mZ, layerNumber, direction, Rin, Rout, overlap, face, layout_type, motherVolume);
1417 LOG(debug) << "FT3Module: done createModule";
1418}
1419
1420void FT3Module::createModule_staveGeo(double mZ, int layerNumber, int direction,
1421 double Rin, double Rout, double z_offset_local,
1422 const Constants::StaveConfig& staveConfig,
1423 TGeoVolume* motherVolume)
1424{
1425 LOG(debug) << "FT3Module: createModule_staveGeo - Layer " << layerNumber
1426 << " at z=" << mZ << ", Direction " << direction;
1427 create_layout_staveGeo(mZ, layerNumber, direction, Rin, Rout,
1428 z_offset_local, staveConfig, motherVolume);
1429 LOG(debug) << "FT3Module: done createModule_staveGeo";
1430}
std::ostringstream debug
std::pair< double, double > calculate_y_range(double x_left, double x_right, double Rin, double Rout)
double calculate_y_circle(double x, double radius)
Definition FT3Module.cxx:96
std::array< std::array< double, 3 >, 4 > buildStaveTriangle(int direction)
Definition of the FT3Module class.
std::pair< std::pair< double, double >, std::pair< double, double > > PositionRangeType
Definition FT3Module.h:29
std::vector< PositionType > PositionTypes
Definition FT3Module.h:26
std::pair< PositionTypes, PositionTypes > PosNegPositionTypes
Definition FT3Module.h:27
int32_t i
void createModule_staveGeo(double mZ, int layerNumber, int direction, double Rin, double Rout, double z_offset_local, const Constants::StaveConfig &staveConfig, TGeoVolume *motherVolume)
static TGeoMedium * carbonFiberMed
Definition FT3Module.h:48
static TGeoMedium * epoxyMed
Definition FT3Module.h:44
static TGeoMaterial * epoxyMat
Definition FT3Module.h:43
static TGeoMaterial * carbonFiberMat
Definition FT3Module.h:47
static TGeoMixture * kaptonMat
Definition FT3Module.h:41
static TGeoMedium * siliconMed
Definition FT3Module.h:38
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)
static TGeoMedium * copperMed
Definition FT3Module.h:40
static TGeoMaterial * copperMat
Definition FT3Module.h:39
static TGeoMaterial * siliconMat
Definition FT3Module.h:37
static TGeoMedium * AluminumMed
Definition FT3Module.h:46
static TGeoMedium * kaptonMed
Definition FT3Module.h:42
static TGeoMaterial * AluminumMat
Definition FT3Module.h:45
static void initialize_materials()
Definition FT3Module.cxx:50
GLint GLenum GLint x
Definition glcorearb.h:403
GLsizeiptr size
Definition glcorearb.h:659
GLuint color
Definition glcorearb.h:1272
GLenum GLuint GLint GLenum face
Definition glcorearb.h:3184
GLboolean * data
Definition glcorearb.h:298
const double effectiveCarbonThickness_Stave
const double z_offsetStave(double x_midpoint_spacing)
const int staveIdxToID(int staveIdx, unsigned nStavesPerDisc)
const double getStackHeight(unsigned nSensorsPerStack)
const std::vector< unsigned > kSensorsPerStack
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
const std::vector< double > & x_midpoints
const std::vector< double > & y_lengths
const std::map< int, std::pair< double, bool > > & staveID_to_y_midpoint
const std::vector< bool > & staveOnFront
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"