diff --git a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h index bb2969b69dc79..d7156b5c92582 100644 --- a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h +++ b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h @@ -40,12 +40,14 @@ struct FT3BaseParam : public o2::conf::ConfigurableParamHelper { Float_t etaOut = 1.5; Float_t Layerx2X0 = 0.01; - // override values from FT3ModuleConstants, inner and outer - bool cutStavesOnNominalRadius_inner = true; - bool cutStavesOnNominalRadius_outer = true; - - // What to place over x=0 line in case of full outer-outer stave: Gap or Sensor - bool placeSensorInMiddleOfStave = false; + // define tolerance allowed for staves to go outside nominal radii + double staveTolMLInner = 0.; + double staveTolMLOuter = 0.; + double staveTolOTInner = 0.; + double staveTolOTOuter = 0.; + + // What to place over x=0 line in case of full outer-outer stave: Gap or Module + bool placeSensorStackInMiddleOfStave = false; // Draw reference circles at inner and outer radius of stave layer, for visualisation bool drawReferenceCircles = false; diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h index 5c976bc3bd902..4f2bfce5c3f1d 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3ModuleConstants.h @@ -23,7 +23,7 @@ namespace o2::ft3::ModuleConstants { /* CURRENT STATUS: - * 25x32mm sensors, 2mm inactive on one side + * 25x29mm sensors, 2mm inactive on one side * Most granular layout is 2x1 sensors, where the one on the right has the inactive region * on the right, and the one on the left has the inactive region on the left. * When stacking 2x1 modules, there is a 0.2mm gap between them. By default, we assume this @@ -35,7 +35,7 @@ namespace o2::ft3::ModuleConstants * | | | | | * | | | | | * | | | | | - * | | | | | 32mm sensor height + * | | | | | 29mm sensor height * | | | | | * | | | | | * ------------------------ @@ -105,8 +105,9 @@ const int CuColor = kOrange; const int kaptonColor = kYellow; const int carbonFiberColor = kGray + 1; -// Struct for stave position configuration (varies between IT/OT) +// Struct for stave position configuration (varies between ML/OT) struct StaveConfig { + const unsigned isML; // whether this config is for ML or OT /* * Constants for staves are written for both positive * and negative x even though they are just mirrored now, @@ -123,7 +124,10 @@ struct StaveConfig { // lengths of staves, their midpoint, and their face const std::vector& y_lengths; const std::vector& x_midpoints; - double x_midpoint_spacing; + const double x_midpoint_spacing; + // whether staves can be placed outside of nominal radii + const double maxToleranceInner; + const double maxToleranceOuter; // which side of the disc do we place the stave? // kSegmentedStave: staggering staves in z (see z_offsetStave) // accessed via stave index, NOT stave ID @@ -149,6 +153,8 @@ const std::vector x_midpoints = { 38.25, 42.75, 47.25, 51.75, 56.25, 60.75, 65.25 // R }; const double x_midpoint_spacing = 4.5; // assume constant for now +const double maxToleranceInner = 0.; // default not allowed inwards +const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation const std::vector staveOnFront = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, // L @@ -174,6 +180,8 @@ const std::vector x_midpoints = { 2.25, 6.75, 11.25, 15.75, 20.25, 24.75, 29.25, 33.75 // R }; const double x_midpoint_spacing = 4.5; +const double maxToleranceInner = 0.; // default not allowed inwards +const double maxToleranceOuter = 3.4; // leave 1mm for layer air encapsulation const std::vector staveOnFront = { 1, 0, 1, 0, 1, 0, 1, 0, // L @@ -186,17 +194,23 @@ inline StaveConfig getStaveConfig(bool isInnerDisk) { if (isInnerDisk) { return StaveConfig{ + true, // isML ML_StavePositions::staveID_to_y_midpoint, ML_StavePositions::y_lengths, ML_StavePositions::x_midpoints, ML_StavePositions::x_midpoint_spacing, + ML_StavePositions::maxToleranceInner, + ML_StavePositions::maxToleranceOuter, ML_StavePositions::staveOnFront}; } else { return StaveConfig{ + false, // isML OT_StavePositions::staveID_to_y_midpoint, OT_StavePositions::y_lengths, OT_StavePositions::x_midpoints, OT_StavePositions::x_midpoint_spacing, + OT_StavePositions::maxToleranceInner, + OT_StavePositions::maxToleranceOuter, OT_StavePositions::staveOnFront}; } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index d8245fa1d34b4..c7f0172b33674 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -459,7 +459,9 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) // shift stave volumes into layer volume, since nominal z_{stave face} = 0 double z_local_offset = z_layer_thickness / 2.0; - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.2, mOuterRadius + 2.5, z_layer_thickness / 2); // margins to ensure staves are fully encapsulated in the layer volume + // ensure staves fully encapsulated in the layer volume, + // but don't cross out of max nominal radii of 38.5cm & 71.5cm respectively (3.5cm tolerance) + TGeoTube* layer = new TGeoTube(mInnerRadius - 0.2, mOuterRadius + 3.49, z_layer_thickness / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); if (ft3Params.drawReferenceCircles) { diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index c439da9d539d0..91aac274dbcba 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -143,11 +143,10 @@ std::pair calculate_y_range( * Rin: the inner radius of the layer * x_left: the x position of the left edge of the sensor to be placed * kSensorStack: the number of sensors to be stacked on top of each other - * tolerance: the tolerance to be subtracted from the maximum y position to avoid - * placing sensors too close to the edge. If this is negative, it effectively - * means that you can place sensors beyond the nominal disc edge - * y_start: the y positions to start placing sensors, - * for positive and negative y respectively + * y_ranges: the y positions to start and end placing sensors, + * for positive and negative y respectively + * absAllowedYRange: the absolute y range allowed for placing sensors, + * used to cut placement if they go past allowed tolerances */ void FT3Module::fill_stave(PosNegPositionTypes& y_positions, double Rin, double Rout, double x_left, unsigned kSensorStack, PositionRangeType y_ranges, @@ -550,7 +549,12 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction unsigned volume_count = 0; // give each subvolume a unique ID // stave triangle cross sections are the same for every stave (direction based) std::array, 4> staveTriangles = buildStaveTriangle(direction); - // Create the stave volumes and fill the y positions where to put sensors on the stave + // declare vector with number of 2xn sensor stacks (modules) -- only used for logging + // each entry is a vector, where each entry is the number of modules of that stack height + std::vector> nSensorStackCountPerStave( + staveConfig.x_midpoints.size(), + std::vector(Constants::kSensorsPerStack.size(), 0)); + std::vector nSensorStackTotal(Constants::kSensorsPerStack.size(), 0); for (unsigned i_stave = 0; i_stave < staveConfig.x_midpoints.size(); i_stave++) { y_positionsPosNeg.emplace_back(PosNegPositionTypes{PositionTypes{}, PositionTypes{}}); const int staveID = Constants::staveIdxToID(i_stave, staveConfig.x_midpoints.size()); @@ -560,16 +564,21 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction // default positive and negative starting points has a gap around x-axis for symmetry double stave_half_length = staveConfig.y_lengths[i_stave] / 2; PositionRangeType y_ranges; - if (ft3Params.placeSensorInMiddleOfStave) { + if (ft3Params.placeSensorStackInMiddleOfStave) { /* - * We want a sensor to cross over the x-axis for coverage at y=0 + * We want a sensor stack to cross over the x-axis for coverage at y=0 * N.B. not necessarily exactly mirrored, only if stack gap is the same - * as the gap between sensors in a stack. + * as the gap between sensors in a stack. Since we start filling with the + * first value in the kSensorsPerStack vector, we offset the first position + * by half of that. + * + * NOTE: TODO: in case the stave is too short to fit one full stack over the middle, + * then we will not be able to place anything since the bottom right/left point of + * the module will already be outside of acceptable bounds -- killing further placement. */ - y_ranges = {{-Constants::sensor2x1_height / 2, - stave_half_length}, - {-Constants::sensor2x1_height / 2 - Constants::stackGap, - -stave_half_length}}; + double stackHeight = Constants::getStackHeight(Constants::kSensorsPerStack[0]); + y_ranges = {{-stackHeight / 2, stave_half_length}, + {-stackHeight / 2 - Constants::stackGap, -stave_half_length}}; } else { /* * Otherwise have a gap around y=0, so sensors are not placed there. @@ -588,23 +597,29 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction } // Define tolerances for cutting staves and placing sensors - double tolerance_inner = -1000; // large negative number to allow given numbers - double tolerance_outer = -1000; + double tolerance_inner, tolerance_outer; + if (staveConfig.isML) { + tolerance_inner = ft3Params.staveTolMLInner; + tolerance_outer = ft3Params.staveTolMLOuter; + } else { + tolerance_inner = ft3Params.staveTolOTInner; + tolerance_outer = ft3Params.staveTolOTOuter; + } // cut staves on nominal inner radius if specified - if (ft3Params.cutStavesOnNominalRadius_inner) { - tolerance_inner = 0.; + if (tolerance_inner > staveConfig.maxToleranceInner) { + tolerance_inner = staveConfig.maxToleranceInner; } - if (ft3Params.cutStavesOnNominalRadius_outer) { - tolerance_outer = 0.; + if (tolerance_outer > staveConfig.maxToleranceOuter) { + tolerance_outer = staveConfig.maxToleranceOuter; } /* - * There are three cases in which we want to mirror the stave around the x-axis, + * There are two cases in which we want to mirror the stave around the x-axis, * which correspond to the stave not going fully from + to - Rout in y. * - * (1) The inner tolerance is 0 (or positive) + * (1) The inner tolerance is 0 (or negative) * a) AND either x_left or x_right lies within the inner radius - * (2) The inner tolerance is large (allow stave placement as wished) + * (2) The inner tolerance is large enough to allow stave placement as wished * a) AND the given stave midpoint is above the inner radius */ double x_left = staveConfig.x_midpoints[i_stave] - Constants::sensor2x1_width / 2; @@ -618,8 +633,8 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction * that there is no lower limit. The upper limit must however be larger than 0, * if it is not, then skip this stave and give a warning. */ - absAllowedYRange.first += tolerance_inner; - absAllowedYRange.second -= tolerance_outer; + absAllowedYRange.first -= tolerance_inner; + absAllowedYRange.second += tolerance_outer; if (absAllowedYRange.first < 0) { absAllowedYRange.first = 0; @@ -637,6 +652,8 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction std::string stave_volume_name = "Stave_" + std::to_string(i_stave) + "_" + std::to_string(layerNumber) + "_" + std::to_string(direction); + + // Create the stave volumes and fill the y positions where to put sensors on the stave addStaveVolume( motherVolume, stave_volume_name, direction, &volume_count, staveConfig.y_lengths[i_stave], staveTriangles, absAllowedYRange, @@ -651,11 +668,27 @@ void FT3Module::create_layout_staveGeo(double mZ, int layerNumber, int direction // now add the sensor positions on the stave for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) { + unsigned nModulesCurr = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size(); fill_stave(y_positionsPosNeg.back(), Rin, Rout, x_left, Constants::kSensorsPerStack[i_kSens], y_ranges, absAllowedYRange); + unsigned nModulesAdded = y_positionsPosNeg.back().first.size() + y_positionsPosNeg.back().second.size() - nModulesCurr; + nSensorStackCountPerStave[i_stave][i_kSens] = nModulesAdded; + nSensorStackTotal[i_kSens] += nModulesAdded; } + std::string moduleDebugStr = "Module size counts for layer " + std::to_string(layerNumber) + " in direction " + std::to_string(direction) + ":\n"; + for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) { + moduleDebugStr += "\t" + std::to_string(nSensorStackCountPerStave[i_stave][i_kSens]) + " modules with " + std::to_string(Constants::kSensorsPerStack[i_kSens]) + " sensors stacked\n"; + } + LOG(debug) << moduleDebugStr; + } + std::string totalModuleInfoStr = + "Total module size counts for layer " + std::to_string(layerNumber) + + " in direction " + std::to_string(direction) + ":\n"; + for (unsigned i_kSens = 0; i_kSens < Constants::kSensorsPerStack.size(); i_kSens++) { + totalModuleInfoStr += "\t" + std::to_string(nSensorStackTotal[i_kSens]) + " modules with " + std::to_string(Constants::kSensorsPerStack[i_kSens]) + " sensors stacked\n"; } + LOG(info) << totalModuleInfoStr; // Create volumes for the sensors and the support materials on top of the stave for (unsigned i_stave = 0; i_stave < staveConfig.x_midpoints.size(); i_stave++) { diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 53ac0a4b12865..e3855dbde6535 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -625,7 +625,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) motherVolume->AddNode(middleBarrelCarbonSupportVolume, 1, nullptr); // Get geometry information from TRK which is already present - float rMinMiddleServices = 38.0f; // cm, start radius of the ML services = maximum radius allowed for sensors (35 cm), plus some margin for disk paving with modules + float rMinMiddleServices = 38.5f; // cm, start radius of the ML services = maximum radius allowed for sensors (35 cm), plus some margin for disk paving with modules const float zMiddleServicesBarrel = 64.5f; // cm, z position of the first barrel ML service disk const float zMiddleServicesBarrelFwdConnection = 143.f; // cm, z position of barrel to forward connection services const float zLengthCylinderMiddleServicesBarrel = zMiddleServicesBarrelFwdConnection - zMiddleServicesBarrel;