Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 08:13:21

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Geometry/SurfaceArrayCreator.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Surfaces/PlanarBounds.hpp"
0013 #include "Acts/Surfaces/Surface.hpp"
0014 #include "Acts/Surfaces/SurfaceArray.hpp"
0015 #include "Acts/Utilities/BinningType.hpp"
0016 #include "Acts/Utilities/Helpers.hpp"
0017 #include "Acts/Utilities/IAxis.hpp"
0018 
0019 #include <algorithm>
0020 #include <numbers>
0021 #include <stdexcept>
0022 
0023 namespace Acts {
0024 
0025 using VectorHelpers::perp;
0026 using VectorHelpers::phi;
0027 
0028 std::unique_ptr<SurfaceArray> SurfaceArrayCreator::surfaceArrayOnCylinder(
0029     const GeometryContext& gctx,
0030     std::vector<std::shared_ptr<const Surface>> surfaces, std::size_t binsPhi,
0031     std::size_t binsZ, std::optional<ProtoLayer> protoLayerOpt,
0032     const Transform3& transform) const {
0033   std::vector<const Surface*> surfacesRaw = unpackSmartPointers(surfaces);
0034   // Check if we have proto layer, else build it
0035   ProtoLayer protoLayer =
0036       protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
0037 
0038   ACTS_VERBOSE("Creating a SurfaceArray on a cylinder");
0039   ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.");
0040   ACTS_VERBOSE(" -- with phi x z  = " << binsPhi << " x " << binsZ << " = "
0041                                       << binsPhi * binsZ << " bins.");
0042 
0043   Transform3 ftransform = transform;
0044   ProtoAxis pAxisPhi =
0045       createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisPhi,
0046                             protoLayer, ftransform, binsPhi);
0047   ProtoAxis pAxisZ = createEquidistantAxis(
0048       gctx, surfacesRaw, AxisDirection::AxisZ, protoLayer, ftransform, binsZ);
0049 
0050   double R = protoLayer.medium(AxisDirection::AxisR, true);
0051 
0052   std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
0053       makeSurfaceGridLookup2D<AxisBoundaryType::Closed,
0054                               AxisBoundaryType::Bound>(
0055           Surface::SurfaceType::Cylinder, ftransform, R, 0, pAxisPhi, pAxisZ);
0056 
0057   sl->fill(gctx, surfacesRaw);
0058   completeBinning(gctx, *sl, surfacesRaw);
0059 
0060   return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
0061                                         ftransform);
0062 }
0063 
0064 std::unique_ptr<SurfaceArray> SurfaceArrayCreator::surfaceArrayOnCylinder(
0065     const GeometryContext& gctx,
0066     std::vector<std::shared_ptr<const Surface>> surfaces, BinningType bTypePhi,
0067     BinningType bTypeZ, std::optional<ProtoLayer> protoLayerOpt,
0068     const Transform3& transform) const {
0069   std::vector<const Surface*> surfacesRaw = unpackSmartPointers(surfaces);
0070   // check if we have proto layer, else build it
0071   ProtoLayer protoLayer =
0072       protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
0073 
0074   double R = protoLayer.medium(AxisDirection::AxisR, true);
0075 
0076   ProtoAxis pAxisPhi;
0077   ProtoAxis pAxisZ;
0078 
0079   Transform3 ftransform = transform;
0080 
0081   if (bTypePhi == equidistant) {
0082     pAxisPhi = createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisPhi,
0083                                      protoLayer, ftransform, 0);
0084   } else {
0085     pAxisPhi = createVariableAxis(gctx, surfacesRaw, AxisDirection::AxisPhi,
0086                                   protoLayer, ftransform);
0087   }
0088 
0089   if (bTypeZ == equidistant) {
0090     pAxisZ = createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisZ,
0091                                    protoLayer, ftransform);
0092   } else {
0093     pAxisZ = createVariableAxis(gctx, surfacesRaw, AxisDirection::AxisZ,
0094                                 protoLayer, ftransform);
0095   }
0096 
0097   std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
0098       makeSurfaceGridLookup2D<AxisBoundaryType::Closed,
0099                               AxisBoundaryType::Bound>(
0100           Surface::SurfaceType::Cylinder, ftransform, R, 0, pAxisPhi, pAxisZ);
0101 
0102   sl->fill(gctx, surfacesRaw);
0103   completeBinning(gctx, *sl, surfacesRaw);
0104 
0105   // get the number of bins
0106   auto axes = sl->getAxes();
0107   std::size_t bins0 = axes.at(0)->getNBins();
0108   std::size_t bins1 = axes.at(1)->getNBins();
0109 
0110   ACTS_VERBOSE("Creating a SurfaceArray on a cylinder");
0111   ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.");
0112   ACTS_VERBOSE(" -- with phi x z  = " << bins0 << " x " << bins1 << " = "
0113                                       << bins0 * bins1 << " bins.");
0114 
0115   return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
0116                                         ftransform);
0117 }
0118 
0119 std::unique_ptr<SurfaceArray> SurfaceArrayCreator::surfaceArrayOnDisc(
0120     const GeometryContext& gctx,
0121     std::vector<std::shared_ptr<const Surface>> surfaces, std::size_t binsR,
0122     std::size_t binsPhi, std::optional<ProtoLayer> protoLayerOpt,
0123     const Transform3& transform) const {
0124   std::vector<const Surface*> surfacesRaw = unpackSmartPointers(surfaces);
0125   // check if we have proto layer, else build it
0126   ProtoLayer protoLayer =
0127       protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
0128 
0129   ACTS_VERBOSE("Creating a SurfaceArray on a disc");
0130 
0131   Transform3 ftransform = transform;
0132   ProtoAxis pAxisR = createEquidistantAxis(
0133       gctx, surfacesRaw, AxisDirection::AxisR, protoLayer, ftransform, binsR);
0134   ProtoAxis pAxisPhi =
0135       createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisPhi,
0136                             protoLayer, ftransform, binsPhi);
0137 
0138   double Z = protoLayer.medium(AxisDirection::AxisZ, true);
0139   ACTS_VERBOSE("- z-position of disc estimated as " << Z);
0140 
0141   std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
0142       makeSurfaceGridLookup2D<AxisBoundaryType::Bound,
0143                               AxisBoundaryType::Closed>(
0144           Surface::SurfaceType::Disc, ftransform, 0, Z, pAxisR, pAxisPhi);
0145 
0146   // get the number of bins
0147   auto axes = sl->getAxes();
0148   std::size_t bins0 = axes.at(0)->getNBins();
0149   std::size_t bins1 = axes.at(1)->getNBins();
0150 
0151   ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.");
0152   ACTS_VERBOSE(" -- with r x phi  = " << bins0 << " x " << bins1 << " = "
0153                                       << bins0 * bins1 << " bins.");
0154   sl->fill(gctx, surfacesRaw);
0155   completeBinning(gctx, *sl, surfacesRaw);
0156 
0157   return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
0158                                         ftransform);
0159 }
0160 
0161 std::unique_ptr<SurfaceArray> SurfaceArrayCreator::surfaceArrayOnDisc(
0162     const GeometryContext& gctx,
0163     std::vector<std::shared_ptr<const Surface>> surfaces, BinningType bTypeR,
0164     BinningType bTypePhi, std::optional<ProtoLayer> protoLayerOpt,
0165     const Transform3& transform) const {
0166   std::vector<const Surface*> surfacesRaw = unpackSmartPointers(surfaces);
0167   // check if we have proto layer, else build it
0168   ProtoLayer protoLayer =
0169       protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
0170 
0171   ACTS_VERBOSE("Creating a SurfaceArray on a disc");
0172 
0173   ProtoAxis pAxisPhi;
0174   ProtoAxis pAxisR;
0175 
0176   Transform3 ftransform = transform;
0177   Transform3 itransform = ftransform.inverse();
0178 
0179   if (bTypeR == equidistant) {
0180     pAxisR = createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisR,
0181                                    protoLayer, ftransform);
0182   } else {
0183     pAxisR = createVariableAxis(gctx, surfacesRaw, AxisDirection::AxisR,
0184                                 protoLayer, ftransform);
0185   }
0186 
0187   // if we have more than one R ring, we need to figure out
0188   // the number of phi bins.
0189   if (pAxisR.nBins > 1) {
0190     // more than one R-Ring, we need to adjust
0191     // this FORCES equidistant binning
0192     std::vector<std::vector<const Surface*>> phiModules(pAxisR.nBins);
0193     for (const auto& srf : surfacesRaw) {
0194       Vector3 bpos =
0195           itransform * srf->referencePosition(gctx, AxisDirection::AxisR);
0196       std::size_t bin = pAxisR.getBin(perp(bpos));
0197       phiModules.at(bin).push_back(srf);
0198     }
0199 
0200     std::vector<std::size_t> nPhiModules;
0201     auto matcher = m_cfg.surfaceMatcher;
0202     auto equal = [&gctx, &matcher](const Surface* a, const Surface* b) {
0203       return matcher(gctx, AxisDirection::AxisPhi, a, b);
0204     };
0205 
0206     std::transform(
0207         phiModules.begin(), phiModules.end(), std::back_inserter(nPhiModules),
0208         [&equal,
0209          this](const std::vector<const Surface*>& surfaces_) -> std::size_t {
0210           return this->findKeySurfaces(surfaces_, equal).size();
0211         });
0212 
0213     // @FIXME: Problem: phi binning runs rotation to optimize
0214     // for bin edges. This FAILS after this modification, since
0215     // the bin count is the one from the lowest module-count bin,
0216     // but the rotation is done considering all bins.
0217     // This might be resolved through bin completion, but not sure.
0218     // @TODO: check in extrapolation
0219     std::size_t nBinsPhi =
0220         (*std::min_element(nPhiModules.begin(), nPhiModules.end()));
0221     pAxisPhi = createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisPhi,
0222                                      protoLayer, ftransform, nBinsPhi);
0223 
0224   } else {
0225     // use regular determination
0226     if (bTypePhi == equidistant) {
0227       pAxisPhi = createEquidistantAxis(
0228           gctx, surfacesRaw, AxisDirection::AxisPhi, protoLayer, ftransform, 0);
0229     } else {
0230       pAxisPhi = createVariableAxis(gctx, surfacesRaw, AxisDirection::AxisPhi,
0231                                     protoLayer, ftransform);
0232     }
0233   }
0234 
0235   double Z = protoLayer.medium(AxisDirection::AxisZ, true);
0236   ACTS_VERBOSE("- z-position of disc estimated as " << Z);
0237 
0238   std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
0239       makeSurfaceGridLookup2D<AxisBoundaryType::Bound,
0240                               AxisBoundaryType::Closed>(
0241           Surface::SurfaceType::Disc, ftransform, 0, Z, pAxisR, pAxisPhi);
0242 
0243   // get the number of bins
0244   auto axes = sl->getAxes();
0245   std::size_t bins0 = axes.at(0)->getNBins();
0246   std::size_t bins1 = axes.at(1)->getNBins();
0247 
0248   ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.");
0249   ACTS_VERBOSE(" -- with r x phi  = " << bins0 << " x " << bins1 << " = "
0250                                       << bins0 * bins1 << " bins.");
0251 
0252   sl->fill(gctx, surfacesRaw);
0253   completeBinning(gctx, *sl, surfacesRaw);
0254 
0255   return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
0256                                         ftransform);
0257 }
0258 
0259 /// SurfaceArrayCreator interface method - create an array on a plane
0260 std::unique_ptr<SurfaceArray> SurfaceArrayCreator::surfaceArrayOnPlane(
0261     const GeometryContext& gctx,
0262     std::vector<std::shared_ptr<const Surface>> surfaces, std::size_t bins1,
0263     std::size_t bins2, AxisDirection aDir,
0264     std::optional<ProtoLayer> protoLayerOpt,
0265     const Transform3& transform) const {
0266   std::vector<const Surface*> surfacesRaw = unpackSmartPointers(surfaces);
0267   // check if we have proto layer, else build it
0268   ProtoLayer protoLayer =
0269       protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
0270 
0271   ACTS_VERBOSE("Creating a SurfaceArray on a plance");
0272   ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.");
0273   ACTS_VERBOSE(" -- with " << bins1 << " x " << bins2 << " = " << bins1 * bins2
0274                            << " bins.");
0275   Transform3 ftransform = transform;
0276   // Build the grid
0277   std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl;
0278 
0279   // Axis along the binning
0280   switch (aDir) {
0281     case AxisDirection::AxisX: {
0282       ProtoAxis pAxis1 =
0283           createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisY,
0284                                 protoLayer, ftransform, bins1);
0285       ProtoAxis pAxis2 =
0286           createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisZ,
0287                                 protoLayer, ftransform, bins2);
0288       sl = makeSurfaceGridLookup2D<AxisBoundaryType::Bound,
0289                                    AxisBoundaryType::Bound>(
0290           Surface::SurfaceType::Plane, ftransform, 0, 0, pAxis1, pAxis2);
0291       break;
0292     }
0293     case AxisDirection::AxisY: {
0294       ProtoAxis pAxis1 =
0295           createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisX,
0296                                 protoLayer, ftransform, bins1);
0297       ProtoAxis pAxis2 =
0298           createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisZ,
0299                                 protoLayer, ftransform, bins2);
0300       sl = makeSurfaceGridLookup2D<AxisBoundaryType::Bound,
0301                                    AxisBoundaryType::Bound>(
0302           Surface::SurfaceType::Plane, ftransform, 0, 0, pAxis1, pAxis2);
0303       break;
0304     }
0305     case AxisDirection::AxisZ: {
0306       ProtoAxis pAxis1 =
0307           createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisX,
0308                                 protoLayer, ftransform, bins1);
0309       ProtoAxis pAxis2 =
0310           createEquidistantAxis(gctx, surfacesRaw, AxisDirection::AxisY,
0311                                 protoLayer, ftransform, bins2);
0312       sl = makeSurfaceGridLookup2D<AxisBoundaryType::Bound,
0313                                    AxisBoundaryType::Bound>(
0314           Surface::SurfaceType::Plane, ftransform, 0, 0, pAxis1, pAxis2);
0315       break;
0316     }
0317     default: {
0318       throw std::invalid_argument(
0319           "SurfaceArrayCreator::"
0320           "surfaceArrayOnPlane: Invalid binning "
0321           "direction");
0322     }
0323   }
0324 
0325   sl->fill(gctx, surfacesRaw);
0326   completeBinning(gctx, *sl, surfacesRaw);
0327 
0328   return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
0329                                         ftransform);
0330   //!< @todo implement - take from ATLAS complex TRT builder
0331 }
0332 
0333 std::vector<const Surface*> SurfaceArrayCreator::findKeySurfaces(
0334     const std::vector<const Surface*>& surfaces,
0335     const std::function<bool(const Surface*, const Surface*)>& equal) const {
0336   std::vector<const Surface*> keys;
0337   for (const auto& srfA : surfaces) {
0338     bool exists = false;
0339     for (const auto& srfB : keys) {
0340       if (equal(srfA, srfB)) {
0341         exists = true;
0342         break;
0343       }
0344     }
0345     if (!exists) {
0346       keys.push_back(srfA);
0347     }
0348   }
0349 
0350   return keys;
0351 }
0352 
0353 std::size_t SurfaceArrayCreator::determineBinCount(
0354     const GeometryContext& gctx, const std::vector<const Surface*>& surfaces,
0355     AxisDirection aDir) const {
0356   auto matcher = m_cfg.surfaceMatcher;
0357   auto equal = [&gctx, &aDir, &matcher](const Surface* a, const Surface* b) {
0358     return matcher(gctx, aDir, a, b);
0359   };
0360   std::vector<const Surface*> keys = findKeySurfaces(surfaces, equal);
0361 
0362   return keys.size();
0363 }
0364 
0365 SurfaceArrayCreator::ProtoAxis SurfaceArrayCreator::createVariableAxis(
0366     const GeometryContext& gctx, const std::vector<const Surface*>& surfaces,
0367     AxisDirection aDir, const ProtoLayer& protoLayer,
0368     Transform3& transform) const {
0369   if (surfaces.empty()) {
0370     throw std::logic_error(
0371         "No surfaces handed over for creating arbitrary bin utility!");
0372   }
0373   // BinningOption is open for z and r, in case of phi binning reset later
0374   // the vector with the binning Values (boundaries for each bin)
0375 
0376   // bind matcher with binning type
0377   auto matcher = m_cfg.surfaceMatcher;
0378   // find the key surfaces
0379   auto equal = [&gctx, &aDir, &matcher](const Surface* a, const Surface* b) {
0380     return matcher(gctx, aDir, a, b);
0381   };
0382   std::vector<const Surface*> keys = findKeySurfaces(surfaces, equal);
0383 
0384   std::vector<AxisScalar> aDirs;
0385   if (aDir == AxisDirection::AxisPhi) {
0386     std::stable_sort(
0387         keys.begin(), keys.end(), [&gctx](const Surface* a, const Surface* b) {
0388           return (phi(a->referencePosition(gctx, AxisDirection::AxisPhi)) <
0389                   phi(b->referencePosition(gctx, AxisDirection::AxisPhi)));
0390         });
0391 
0392     AxisScalar maxPhi =
0393         0.5 *
0394         (phi(keys.at(0)->referencePosition(gctx, AxisDirection::AxisPhi)) +
0395          phi(keys.at(1)->referencePosition(gctx, AxisDirection::AxisPhi)));
0396 
0397     // create rotation, so that maxPhi is +pi
0398     AxisScalar angle = -(std::numbers::pi + maxPhi);
0399     transform = (transform)*AngleAxis3(angle, Vector3::UnitZ());
0400 
0401     // iterate over all key surfaces, and use their mean position as aDirs,
0402     // but
0403     // rotate using transform from before
0404     AxisScalar previous =
0405         phi(keys.at(0)->referencePosition(gctx, AxisDirection::AxisPhi));
0406     // go through key surfaces
0407     for (std::size_t i = 1; i < keys.size(); i++) {
0408       const Surface* surface = keys.at(i);
0409       // create central binning values which is the mean of the center
0410       // positions in the binning direction of the current and previous
0411       // surface
0412       AxisScalar edge = 0.5 * (previous + phi(surface->referencePosition(
0413                                               gctx, AxisDirection::AxisPhi))) +
0414                         angle;
0415       aDirs.push_back(edge);
0416       previous = phi(surface->referencePosition(gctx, AxisDirection::AxisPhi));
0417     }
0418 
0419     // segments
0420     unsigned int segments = 72;
0421 
0422     // get the bounds of the last surfaces
0423     const Surface* backSurface = keys.back();
0424     const PlanarBounds* backBounds =
0425         dynamic_cast<const PlanarBounds*>(&(backSurface->bounds()));
0426     if (backBounds == nullptr) {
0427       ACTS_ERROR(
0428           "Given SurfaceBounds are not planar - not implemented for "
0429           "other bounds yet! ");
0430     }
0431     // get the global vertices
0432     std::vector<Vector3> backVertices =
0433         makeGlobalVertices(gctx, *backSurface, backBounds->vertices(segments));
0434     AxisScalar maxBValue = phi(*std::max_element(
0435         backVertices.begin(), backVertices.end(),
0436         [](const Vector3& a, const Vector3& b) { return phi(a) < phi(b); }));
0437 
0438     aDirs.push_back(maxBValue);
0439 
0440     aDirs.push_back(std::numbers::pi_v<AxisScalar>);
0441 
0442   } else if (aDir == AxisDirection::AxisZ) {
0443     std::stable_sort(
0444         keys.begin(), keys.end(), [&gctx](const Surface* a, const Surface* b) {
0445           return (a->referencePosition(gctx, AxisDirection::AxisZ).z() <
0446                   b->referencePosition(gctx, AxisDirection::AxisZ).z());
0447         });
0448 
0449     aDirs.push_back(protoLayer.min(AxisDirection::AxisZ));
0450     aDirs.push_back(protoLayer.max(AxisDirection::AxisZ));
0451 
0452     // the z-center position of the previous surface
0453     AxisScalar previous =
0454         keys.front()->referencePosition(gctx, AxisDirection::AxisZ).z();
0455     // go through key surfaces
0456     for (auto surface = keys.begin() + 1; surface != keys.end(); surface++) {
0457       // create central binning values which is the mean of the center
0458       // positions in the binning direction of the current and previous
0459       // surface
0460       aDirs.push_back(
0461           0.5 *
0462           (previous +
0463            (*surface)->referencePosition(gctx, AxisDirection::AxisZ).z()));
0464       previous = (*surface)->referencePosition(gctx, AxisDirection::AxisZ).z();
0465     }
0466   } else {  // AxisDirection::AxisR
0467     std::stable_sort(
0468         keys.begin(), keys.end(), [&gctx](const Surface* a, const Surface* b) {
0469           return (perp(a->referencePosition(gctx, AxisDirection::AxisR)) <
0470                   perp(b->referencePosition(gctx, AxisDirection::AxisR)));
0471         });
0472 
0473     aDirs.push_back(protoLayer.min(AxisDirection::AxisR));
0474     aDirs.push_back(protoLayer.max(AxisDirection::AxisR));
0475 
0476     // the r-center position of the previous surface
0477     AxisScalar previous =
0478         perp(keys.front()->referencePosition(gctx, AxisDirection::AxisR));
0479 
0480     // go through key surfaces
0481     for (auto surface = keys.begin() + 1; surface != keys.end(); surface++) {
0482       // create central binning values which is the mean of the center
0483       // positions in the binning direction of the current and previous
0484       // surface
0485       aDirs.push_back(0.5 * (previous + perp((*surface)->referencePosition(
0486                                             gctx, AxisDirection::AxisR))));
0487       previous =
0488           perp((*surface)->referencePosition(gctx, AxisDirection::AxisR));
0489     }
0490   }
0491   std::ranges::sort(aDirs);
0492   ACTS_VERBOSE("Create variable binning Axis for binned SurfaceArray");
0493   ACTS_VERBOSE("    AxisDirection: " << aDir);
0494   ACTS_VERBOSE("    Number of bins: " << (aDirs.size() - 1));
0495   ACTS_VERBOSE("    (Min/Max) = (" << aDirs.front() << "/" << aDirs.back()
0496                                        << ")");
0497 
0498   ProtoAxis pAxis;
0499   pAxis.bType = arbitrary;
0500   pAxis.axisDir = aDir;
0501   pAxis.binEdges = aDirs;
0502   pAxis.nBins = aDirs.size() - 1;
0503 
0504   return pAxis;
0505 }
0506 
0507 SurfaceArrayCreator::ProtoAxis SurfaceArrayCreator::createEquidistantAxis(
0508     const GeometryContext& gctx, const std::vector<const Surface*>& surfaces,
0509     AxisDirection aDir, const ProtoLayer& protoLayer, Transform3& transform,
0510     std::size_t nBins) const {
0511   if (surfaces.empty()) {
0512     throw std::logic_error(
0513         "No surfaces handed over for creating equidistant axis!");
0514   }
0515   // check the binning type first
0516 
0517   double minimum = protoLayer.min(aDir, false);
0518   double maximum = protoLayer.max(aDir, false);
0519 
0520   std::size_t binNumber = 0;
0521   if (nBins == 0) {
0522     // determine bin count
0523     binNumber = determineBinCount(gctx, surfaces, aDir);
0524   } else {
0525     // use bin count
0526     binNumber = nBins;
0527   }
0528 
0529   // bind matcher & context with binning type
0530   auto matcher = m_cfg.surfaceMatcher;
0531 
0532   // now check the binning value
0533   if (aDir == AxisDirection::AxisPhi) {
0534     minimum = protoLayer.min(AxisDirection::AxisPhi, true);
0535     maximum = protoLayer.max(AxisDirection::AxisPhi, true);
0536 
0537     if (m_cfg.doPhiBinningOptimization) {
0538       minimum = -std::numbers::pi;
0539       maximum = std::numbers::pi;
0540 
0541       // Phi binning
0542       // set the binning option for phi
0543       // sort first in phi
0544       const Surface* maxElem = *std::max_element(
0545           surfaces.begin(), surfaces.end(),
0546           [&gctx](const Surface* a, const Surface* b) {
0547             return phi(a->referencePosition(gctx, AxisDirection::AxisR)) <
0548                    phi(b->referencePosition(gctx, AxisDirection::AxisR));
0549           });
0550 
0551       // rotate to max phi module plus one half step
0552       // this should make sure that phi wrapping at +- pi
0553       // never falls on a module center
0554       double surfaceMax =
0555           phi(maxElem->referencePosition(gctx, AxisDirection::AxisR));
0556       double gridStep = 2 * std::numbers::pi / binNumber;
0557       double gridMax = std::numbers::pi - 0.5 * gridStep;
0558       double angle = gridMax - surfaceMax;
0559 
0560       // replace given transform ref
0561       transform = transform * AngleAxis3(angle, Vector3::UnitZ());
0562     }
0563   }
0564 
0565   // assign the bin size
0566   ACTS_VERBOSE("Create equidistant binning Axis for binned SurfaceArray");
0567   ACTS_VERBOSE("    AxisDirection: " << aDir);
0568   ACTS_VERBOSE("    Number of bins: " << binNumber);
0569   ACTS_VERBOSE("    (Min/Max) = (" << minimum << "/" << maximum << ")");
0570 
0571   ProtoAxis pAxis;
0572   pAxis.max = maximum;
0573   pAxis.min = minimum;
0574   pAxis.bType = equidistant;
0575   pAxis.axisDir = aDir;
0576   pAxis.nBins = binNumber;
0577 
0578   return pAxis;
0579 }
0580 
0581 std::vector<Vector3> SurfaceArrayCreator::makeGlobalVertices(
0582     const GeometryContext& gctx, const Surface& surface,
0583     const std::vector<Vector2>& locVertices) const {
0584   std::vector<Vector3> globVertices;
0585   for (auto& vertex : locVertices) {
0586     Vector3 globVertex = surface.localToGlobal(gctx, vertex, Vector3());
0587     globVertices.push_back(globVertex);
0588   }
0589   return globVertices;
0590 }
0591 
0592 }  // namespace Acts