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