@@ -93,6 +93,44 @@ double calculate_y_circle(double x, double radius)
9393 return (x * x < radius * radius) ? std::sqrt (radius * radius - x * x) : 0 ;
9494}
9595
96+ std::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+
96134/*
97135 * This function is a helper function which will pad out the stave with sensors
98136 * until there is no more space available.
@@ -111,54 +149,60 @@ double calculate_y_circle(double x, double radius)
111149 * y_start: the y positions to start placing sensors,
112150 * for positive and negative y respectively
113151 */
114- void FT3Module::fill_stave (PosNegPositionTypes& y_positions, double Rout,
115- double x_left, unsigned kSensorStack , double tolerance ,
116- std::pair< double , double > y_start={ 0 , 0 } )
152+ void FT3Module::fill_stave (PosNegPositionTypes& y_positions, double Rin, double Rout,
153+ double x_left, unsigned kSensorStack , double tolerance_inner ,
154+ double tolerance_outer, PositionRangeType y_ranges )
117155{
118156 // start with upper half of the stave, then mirror to the bottom half
119157 double x_right = x_left + Constants::sensor2x1_width;
120- double y_top = y_start.first ;
121- // either start at given start position, or at the top of the last placed sensors
122- if (!y_positions.first .empty ()) {
123- y_top = y_positions.first .back ().first
124- + Constants::sensor2x1_height * y_positions.first .back ().second
125- + Constants::sensor2x1_gap;
126- }
127158 // add the height of kSensorStack sensors + the gaps in between them
128- double sensorTileHeight = Constants::sensor2x1_height * kSensorStack
129- + Constants::sensor2x1_gap * ( kSensorStack - 1 ) ;
159+ double sensorStackHeight = Constants::getStackHeight ( kSensorStack );
160+ double sensorAbsStackYShift = sensorStackHeight + Constants::stackGap ;
130161
131- double max_y_abs;
132- // y_max(x_left) > y_max(x_right) means that the top of the sensor
133- // will hit the outer radius at x_right first
134- if (x_left > -Constants::single_sensor_width) {
135- // tolerance already in maximum y position
136- max_y_abs = calculate_y_circle (x_right, Rout) - tolerance;
162+ std::pair<double , double > absAllowedYRange =
163+ calculate_y_range (x_left, x_right, Rin, Rout);
164+
165+ // shift allowed range by tolerance. Note that this sum can be negative, but
166+ // that is not a problem, it just means that we can always place sensors over the edge
167+ absAllowedYRange.first += tolerance_inner;
168+ absAllowedYRange.second -= tolerance_outer;
169+ // in case a big tolerance is given, cut on the given range instead
170+ double max_sensor_y_abs = std::min (absAllowedYRange.second , y_ranges.first .second );
171+
172+ double y_top; // top half of the xy grid
173+ // either start at given value (adjusted for tolerance), or at last placed sensors
174+ if (!y_positions.first .empty ()) { // sensors already placed
175+ y_top = y_positions.first .back ().first + sensorAbsStackYShift;
176+ } else if (y_ranges.first .first < absAllowedYRange.first ) {
177+ // given starting y is lower than the minimum allowed y, start at the latter.
178+ y_top = absAllowedYRange.first ;
137179 } else {
138- max_y_abs = calculate_y_circle (x_left, Rout) - tolerance;
180+ // given starting y is acceptable, start there
181+ y_top = y_ranges.first .first ;
139182 }
140- unsigned n_sensors_placed = y_positions.first .size () + y_positions.second .size ();
141-
142- while ( (y_top + sensorTileHeight) <= max_y_abs ) {
183+ while ( (y_top + sensorStackHeight) <= max_sensor_y_abs ) {
143184 y_positions.first .emplace_back (y_top, kSensorStack );
144- y_top += sensorTileHeight + Constants::sensor2x1_gap ;
185+ y_top += sensorAbsStackYShift ;
145186 }
146187
147188 // now we do the same for the negative y positions
148189 // they do not have to be exactly mirrored, hence done separately
149- double y_bottom = y_start. second ;
190+ double y_bottom;
150191 if (!y_positions.second .empty ()) {
151192 // subtract instead to move further down
152- y_bottom = y_positions.second .back ().first
153- - Constants::sensor2x1_height * y_positions.second .back ().second
154- - Constants::sensor2x1_gap;
193+ y_bottom = y_positions.second .back ().first - sensorAbsStackYShift;
194+ } else if (y_ranges.second .first > -absAllowedYRange.first ) {
195+ // given starting y is closer to x-axis than max allowed y, start at the latter.
196+ y_bottom = -absAllowedYRange.first ;
197+ } else {
198+ // given starting y is acceptable, start there
199+ y_bottom = y_ranges.second .first ;
155200 }
156-
157- while ( (y_bottom - sensorTileHeight ) >= -max_y_abs ) {
201+ // fill in the sensors on negative y
202+ while ( (y_bottom - sensorStackHeight ) >= -max_sensor_y_abs ) {
158203 y_positions.second .emplace_back (y_bottom, kSensorStack );
159- y_bottom -= (sensorTileHeight + Constants::sensor2x1_gap) ;
204+ y_bottom -= sensorAbsStackYShift ;
160205 }
161- unsigned sensors_placed_after = y_positions.first .size () + y_positions.second .size ();
162206}
163207
164208/*
@@ -167,7 +211,8 @@ void FT3Module::fill_stave(PosNegPositionTypes& y_positions, double Rout,
167211 */
168212void FT3Module::addStaveVolume (
169213 TGeoVolume* motherVolume, std::string volumeName, int direction,
170- unsigned * volume_count, double staveLength,
214+ unsigned * volume_count, double staveLength, double tolerance_inner,
215+ double tolerance_outer, double Rin, double Rout,
171216 double x_mid, double y_mid, double z_stave_shift_abs)
172217{
173218 // Set some constants for readability
@@ -215,20 +260,48 @@ void FT3Module::addStaveVolume(
215260 zv_inner[2 ] = zv_inner[1 ];
216261 xv_inner[2 ] = -xv_inner[1 ];
217262
218- // create the extruded volumes from z=0 (later y=0 after rotation) to stave length
219- // and not from midpoint - staveLength/2 to midpoint + staveLength/2,
220- // translate after rotation
263+ /*
264+ * create the extruded volumes from z=0 (later y=0 after rotation) to stave length
265+ * and not from midpoint - staveLength/2 to midpoint + staveLength/2, translate later
266+ *
267+ * Note also that we first need to check if the length is allowed given the inner
268+ * and outer radius of the layer.
269+ */
270+ double x_left = x_mid - Constants::single_sensor_width;
271+ double x_right = x_mid + Constants::single_sensor_width;
272+ std::pair<double , double > absAllowedYRange =
273+ calculate_y_range (x_left, x_right, Rin, Rout);
274+
275+ // shift allowed range by tolerance. Note that this sum can be negative, but
276+ // that is not a problem, it just means that we can always place staves over the edge
277+ absAllowedYRange.first += tolerance_inner;
278+ absAllowedYRange.second -= tolerance_outer;
279+
280+ double maxStaveLength = absAllowedYRange.second - absAllowedYRange.first ;
281+ double staveLengthToUse = std::min (staveLength, maxStaveLength);
282+ if (staveLengthToUse <= 0 ) {
283+ LOG (warning) << " Stave " << volumeName << " has non-positive length after applying "
284+ << " tolerance, skipping stave. Max allowed length: " << maxStaveLength
285+ << " tolerance inner: " << tolerance_inner
286+ << " tolerance outer: " << tolerance_outer;
287+ return ;
288+ }
289+ double staveLengthDiff = staveLength - staveLengthToUse;
290+ if (staveLengthDiff > 0 ) { // had to cut it, hence y_mid must move too
291+ y_mid = absAllowedYRange.first + staveLengthToUse / 2 ;
292+ }
293+
221294 TGeoXtru* staveFull = new TGeoXtru (2 );
222295 staveFull->SetName (( volumeName + " _Xtru_outer" ).c_str ());
223296 staveFull->DefinePolygon (3 , xv_outer, zv_outer);
224297 staveFull->DefineSection (0 , 0 );
225- staveFull->DefineSection (1 , staveLength );
298+ staveFull->DefineSection (1 , staveLengthToUse );
226299
227300 TGeoXtru* staveInner = new TGeoXtru (2 );
228301 staveInner->SetName (( volumeName + " _Xtru_inner" ).c_str ());
229302 staveInner->DefinePolygon (3 , xv_inner, zv_inner);
230303 staveInner->DefineSection (0 , 0 );
231- staveInner->DefineSection (1 , staveLength );
304+ staveInner->DefineSection (1 , staveLengthToUse );
232305
233306 TGeoCompositeShape* staveShape = new TGeoCompositeShape (
234307 (volumeName + " _shape" ).c_str (),
@@ -250,7 +323,7 @@ void FT3Module::addStaveVolume(
250323 */
251324 double z_shift = (direction == 1 ) ? z_stave_shift_abs : -z_stave_shift_abs;
252325 TGeoCombiTrans* combiTrans =
253- new TGeoCombiTrans (x_mid, y_mid - staveLength / 2 , z_shift, rot);
326+ new TGeoCombiTrans (x_mid, y_mid - staveLengthToUse / 2 , z_shift, rot);
254327 motherVolume->AddNode (staveVolume,
255328 *volume_count,
256329 combiTrans);
@@ -452,14 +525,27 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction
452525 double y_midpoint = 0 .;
453526 bool mirrorStaveAroundX = false ;
454527 // default positive and negative starting points has a gap around x-axis
455- std::pair<double , double > y_start{0 ., -Constants::sensor2x1_gap};
528+ PositionRangeType y_ranges = {{0 , Constants::y_lengths[i_stave]},
529+ {-Constants::stackGap, -Constants::y_lengths[i_stave]}};
456530 auto y_midpoint_it = Constants::staveID_to_y_midpoint.find (staveID);
457531 if ( y_midpoint_it != Constants::staveID_to_y_midpoint.end () ) {
458532 // there is a defined midpoint for this stave, use this for starting points
459533 y_midpoint = y_midpoint_it->second .first ; // avoid double map lookup
460534 mirrorStaveAroundX = y_midpoint_it->second .second ;
461- double y_start_pos = y_midpoint - Constants::y_lengths[i_stave] / 2 ;
462- y_start = {y_start_pos, -y_start_pos};
535+ double y_diff_abs = Constants::y_lengths[i_stave] / 2 ;
536+ y_ranges.first = {y_midpoint - y_diff_abs, y_midpoint + y_diff_abs};
537+ y_ranges.second = {-y_midpoint + y_diff_abs, -y_midpoint - y_diff_abs};
538+ }
539+
540+ // Define tolerances for cutting staves and placing sensors
541+ double tolerance_inner = -1000 ; // large negative number to allow given numbers
542+ double tolerance_outer = -1000 ;
543+ // cut staves on nominal inner radius if specified
544+ if (ft3Params.cutStavesOnNominalRadius_inner ) {
545+ tolerance_inner = 0 .;
546+ }
547+ if (ft3Params.cutStavesOnNominalRadius_outer ) {
548+ tolerance_outer = 0 .;
463549 }
464550
465551 // Get whether the stave is shifted backward or not
@@ -470,40 +556,44 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction
470556 " _" + std::to_string (direction);
471557 addStaveVolume (
472558 motherVolume, stave_volume_name, direction, &volume_count,
473- Constants::y_lengths[i_stave], Constants::x_midpoints[i_stave],
559+ Constants::y_lengths[i_stave], tolerance_inner, tolerance_outer,
560+ Rin, Rout, Constants::x_midpoints[i_stave],
474561 y_midpoint, mZ + z_stave_shift_abs
475562 );
476- if (y_midpoint > Rin) {
477- // stave midpoint is beyond nominal inner radius,
478- // so we can reasonably expect to mirror sensors around the x-axis
563+ /*
564+ * There are three cases in which we want to mirror the stave around the x-axis,
565+ * which correspond to the stave not going fully from + to - Rout in y.
566+ *
567+ * (1) The inner tolerance is 0 (or positive)
568+ * a) AND either x_left or x_right lies within the inner radius
569+ * (2) The inner tolerance is large (allow stave placement as wished)
570+ * a) AND the given stave midpoint is above the inner radius
571+ */
572+ double x_left = Constants::x_midpoints[i_stave] - Constants::sensor2x1_width / 2 ;
573+ double x_right = x_left + Constants::sensor2x1_width;
574+ bool mirrorStaveAroundXAxis = false ;
575+ if (tolerance_inner >= 0 ) {
576+ double x_innermost = std::min (std::abs (x_left), std::abs (x_right));
577+ mirrorStaveAroundXAxis = (x_innermost < Rin);
578+ } else { // Have negative tolerance, so can place staves as wished
579+ mirrorStaveAroundXAxis = (y_midpoint > Rin);
580+ }
581+ // Now create the mirrored stave
582+ if (mirrorStaveAroundXAxis) {
479583 addStaveVolume (
480584 motherVolume, stave_volume_name + " _mirrored" , direction, &volume_count,
481- Constants::y_lengths[i_stave], Constants::x_midpoints[i_stave],
585+ Constants::y_lengths[i_stave], tolerance_inner, tolerance_outer,
586+ Rin, Rout, Constants::x_midpoints[i_stave],
482587 -y_midpoint, mZ + z_stave_shift_abs
483588 );
484589 }
485590
486- double x_left = Constants::x_midpoints[i_stave] - Constants::sensor2x1_width / 2 ;
487- double x_right = x_left + Constants::sensor2x1_width;
488- double tolerance = -Constants::sensor_stack_height; // allow one sensor placement beyond
489- // cut staves on nominal inner radius if specified
490- if (ft3Params.cutStavesOnNominalRadius ) {
491- double min_y_at_x;
492- if (x_left * x_right < 0 ) {
493- // stave crosses y-axis, so we start at y=Rin
494- min_y_at_x = Rin;
495- } else if (x_left > 0 ) {
496- // stave is on the right side, so minimum y is at x_left
497- min_y_at_x = calculate_y_circle (x_left, Rin);
498- } else {
499- // stave is on the left side, so minimum y is at x_right
500- min_y_at_x = calculate_y_circle (x_right, Rin);
501- }
502- y_start = {min_y_at_x, -min_y_at_x};
503- tolerance = 0 .; // no tolerance in case of cutting at nominal radius
591+ // now add the sensor positions on the stave
592+ for (unsigned i_kSens = 0 ; i_kSens < Constants::kSensorsPerStack .size (); i_kSens++) {
593+ fill_stave (y_positionsPosNeg.back (), Rin, Rout, x_left,
594+ Constants::kSensorsPerStack [i_kSens], tolerance_inner,
595+ tolerance_outer, y_ranges);
504596 }
505- fill_stave (y_positionsPosNeg.back (), Rout, x_left, Constants::kSensorsPerStack ,
506- tolerance, y_start);
507597 }
508598
509599 for (unsigned i_stave = 0 ; i_stave < Constants::x_midpoints.size (); i_stave++) {
0 commit comments