Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-01 07:52:39

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 #pragma once
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Utilities/AxisDefinitions.hpp"
0013 #include "Acts/Utilities/BinningType.hpp"
0014 #include "Acts/Utilities/Helpers.hpp"
0015 #include "Acts/Utilities/ProtoAxis.hpp"
0016 #include "Acts/Utilities/ThrowAssert.hpp"
0017 #include "Acts/Utilities/VectorHelpers.hpp"
0018 
0019 #include <algorithm>
0020 #include <cmath>
0021 #include <memory>
0022 #include <sstream>
0023 #include <string>
0024 #include <utility>
0025 #include <vector>
0026 
0027 namespace Acts {
0028 
0029 /// @class BinningData
0030 ///
0031 ///   This class holds all the data necessary for the bin calculation
0032 ///
0033 ///   phi has a very particular behaviour:
0034 ///   - there's the change around +/- PI
0035 ///
0036 ///   - it can be multiplicative or additive
0037 ///   multiplicative : each major bin has the same sub structure
0038 ///                    i.e. first binnning
0039 ///
0040 /// structure is equidistant
0041 ///   additive : sub structure replaces one bin (and one bin only)
0042 ///
0043 ///
0044 class BinningData {
0045  public:
0046   BinningType type{};        ///< binning type: equidistant, arbitrary
0047   BinningOption option{};    ///< binning option: open, closed
0048   AxisDirection binvalue{};  ///< axis direction: AxisX, AxisY, AxisZ, ...
0049   float min{};               ///< minimum value
0050   float max{};               ///< maximum value
0051   float step{};              ///< binning step
0052   bool zdim{};               ///< zero dimensional binning : direct access
0053 
0054   /// sub structure: describe some sub binning
0055   std::unique_ptr<const BinningData> subBinningData;
0056   /// sub structure: additive or multipicative
0057   bool subBinningAdditive{};
0058 
0059   /// Constructor for 0D binning
0060   ///
0061   /// @param bValue is the axis direction AxisX, AxisY, etc.
0062   /// @param bMin is the minimum value
0063   /// @param bMax is the maximum value
0064   BinningData(AxisDirection bValue, float bMin, float bMax)
0065       : type(equidistant),
0066         option(open),
0067         binvalue(bValue),
0068         min(bMin),
0069         max(bMax),
0070         step((bMax - bMin)),
0071         zdim(true),
0072         subBinningData(nullptr),
0073         m_bins(1),
0074         m_boundaries({{min, max}}),
0075         m_totalBins(1),
0076         m_totalBoundaries(std::vector<float>()),
0077         m_functionPtr(&searchEquidistantWithBoundary) {}
0078 
0079   /// Constructor for equidistant binning
0080   /// and optional sub structure can be
0081   /// multiplicative or additive
0082   ///
0083   /// @param bOption is the binning option : open, closed
0084   /// @param bValue is the axis direction: Axis, AxisY, etc.
0085   /// @param bBins is number of equidistant bins
0086   /// @param bMin is the minimum value
0087   /// @param bMax is the maximum value
0088   /// @param sBinData is (optional) sub structure
0089   /// @param sBinAdditive is the prescription for the sub structure
0090   BinningData(BinningOption bOption, AxisDirection bValue, std::size_t bBins,
0091               float bMin, float bMax,
0092               std::unique_ptr<const BinningData> sBinData = nullptr,
0093               bool sBinAdditive = false)
0094       : type(equidistant),
0095         option(bOption),
0096         binvalue(bValue),
0097         min(bMin),
0098         max(bMax),
0099         step((bMax - bMin) / bBins),
0100         zdim(bBins == 1 ? true : false),
0101         subBinningData(std::move(sBinData)),
0102         subBinningAdditive(sBinAdditive),
0103         m_bins(bBins),
0104         m_boundaries(std::vector<float>()),
0105         m_totalBins(bBins),
0106         m_totalBoundaries(std::vector<float>()) {
0107     // set to equidistant search
0108     m_functionPtr = &searchEquidistantWithBoundary;
0109     // fill the boundary vector for fast access to center & boundaries
0110     m_boundaries.reserve(m_bins + 1);
0111     for (std::size_t ib = 0; ib < m_bins + 1; ++ib) {
0112       m_boundaries.push_back(min + ib * step);
0113     }
0114     // the binning data has sub structure - multiplicative or additive
0115     checkSubStructure();
0116   }
0117 
0118   /// Constructor for non-equidistant binning
0119   ///
0120   /// @param bOption is the binning option : open / closed
0121   /// @param bValue is the axis direction : AxisX, AxisY, etc.
0122   /// @param bBoundaries are the bin boundaries
0123   /// @param sBinData is (optional) sub structure
0124   BinningData(BinningOption bOption, AxisDirection bValue,
0125               const std::vector<float>& bBoundaries,
0126               std::unique_ptr<const BinningData> sBinData = nullptr)
0127       : type(arbitrary),
0128         option(bOption),
0129         binvalue(bValue),
0130         zdim(bBoundaries.size() == 2 ? true : false),
0131         subBinningData(std::move(sBinData)),
0132         subBinningAdditive(true),
0133         m_bins(bBoundaries.size() - 1),
0134         m_boundaries(bBoundaries),
0135         m_totalBins(bBoundaries.size() - 1),
0136         m_totalBoundaries(bBoundaries) {
0137     // assert a no-size case
0138     throw_assert(m_boundaries.size() > 1, "Must have more than one boundary");
0139     min = m_boundaries[0];
0140     max = m_boundaries[m_boundaries.size() - 1];
0141     // set to equidistant search
0142     m_functionPtr = &searchInVectorWithBoundary;
0143     // the binning data has sub structure - multiplicative
0144     checkSubStructure();
0145   }
0146 
0147   /// Copy constructor
0148   ///
0149   /// @param bdata is the source object
0150   BinningData(const BinningData& bdata)
0151       : type(bdata.type),
0152         option(bdata.option),
0153         binvalue(bdata.binvalue),
0154         min(bdata.min),
0155         max(bdata.max),
0156         step(bdata.step),
0157         zdim(bdata.zdim),
0158         subBinningData(nullptr),
0159         subBinningAdditive(bdata.subBinningAdditive),
0160         m_bins(bdata.m_bins),
0161         m_boundaries(bdata.m_boundaries),
0162         m_totalBins(bdata.m_totalBins),
0163         m_totalBoundaries(bdata.m_totalBoundaries) {
0164     // get the binning data
0165     subBinningData =
0166         bdata.subBinningData
0167             ? std::make_unique<const BinningData>(*bdata.subBinningData)
0168             : nullptr;
0169     // set the pointer depending on the type
0170     // set the correct function pointer
0171     if (type == equidistant) {
0172       m_functionPtr = &searchEquidistantWithBoundary;
0173     } else {
0174       m_functionPtr = &searchInVectorWithBoundary;
0175     }
0176   }
0177 
0178   /// Constructor from DirectedProtoAxis
0179   ///
0180   /// @param dpAxis is the ProtoAxis object
0181   ///
0182   explicit BinningData(const DirectedProtoAxis& dpAxis)
0183       : binvalue(dpAxis.getAxisDirection()), subBinningData(nullptr) {
0184     const auto& axis = dpAxis.getAxis();
0185     type = axis.getType() == AxisType::Equidistant ? equidistant : arbitrary;
0186     option = axis.getBoundaryType() == AxisBoundaryType::Closed ? closed : open;
0187     min = static_cast<float>(axis.getMin());
0188     max = static_cast<float>(axis.getMax());
0189     m_bins = axis.getNBins();
0190     step = (max - min) / static_cast<float>(m_bins);
0191     zdim = (m_bins == 1);
0192     m_boundaries.reserve(axis.getBinEdges().size());
0193     for (const auto& edge : axis.getBinEdges()) {
0194       m_boundaries.push_back(static_cast<float>(edge));
0195     }
0196     m_totalBins = m_bins;
0197     m_totalBoundaries = m_boundaries;
0198     // Set the search function pointer based on axis type
0199     m_functionPtr = (type == equidistant) ? &searchEquidistantWithBoundary
0200                                           : &searchInVectorWithBoundary;
0201   }
0202 
0203   /// Assignment operator
0204   ///
0205   /// @param bdata is the source object
0206   BinningData& operator=(const BinningData& bdata) {
0207     if (this != &bdata) {
0208       type = bdata.type;
0209       option = bdata.option;
0210       binvalue = bdata.binvalue;
0211       min = bdata.min;
0212       max = bdata.max;
0213       step = bdata.step;
0214       zdim = bdata.zdim;
0215       subBinningAdditive = bdata.subBinningAdditive;
0216       subBinningData =
0217           bdata.subBinningData
0218               ? std::make_unique<const BinningData>(*bdata.subBinningData)
0219               : nullptr;
0220       m_bins = bdata.m_bins;
0221       m_boundaries = bdata.m_boundaries;
0222       m_totalBins = bdata.m_totalBins;
0223       m_totalBoundaries = bdata.m_totalBoundaries;
0224       // set the correct function pointer
0225       if (type == equidistant) {
0226         m_functionPtr = &searchEquidistantWithBoundary;
0227       } else {
0228         m_functionPtr = &searchInVectorWithBoundary;
0229       }
0230     }
0231     return (*this);
0232   }
0233 
0234   BinningData() = default;
0235   ~BinningData() = default;
0236 
0237   /// Equality operator
0238   ///
0239   /// @param bData is the binning data to be checked against
0240   ///
0241   /// @return a boolean indicating if they are the same
0242   bool operator==(const BinningData& bData) const {
0243     return (type == bData.type && option == bData.option &&
0244             binvalue == bData.binvalue && min == bData.min &&
0245             max == bData.max && step == bData.step && zdim == bData.zdim &&
0246             ((subBinningData == nullptr && bData.subBinningData == nullptr) ||
0247              (subBinningData != nullptr && bData.subBinningData != nullptr &&
0248               (*subBinningData == *bData.subBinningData))) &&
0249             subBinningAdditive == bData.subBinningAdditive);
0250   }
0251 
0252   /// Return the number of bins - including sub bins
0253   std::size_t bins() const { return m_totalBins; }
0254 
0255   /// Return the boundaries  - including sub boundaries
0256   /// @return vector of floats indicating the boundary values
0257   const std::vector<float>& boundaries() const {
0258     if (subBinningData) {
0259       return m_totalBoundaries;
0260     }
0261     return m_boundaries;
0262   }
0263 
0264   /// Take the right float value
0265   ///
0266   /// @param lposition assumes the correct local position expression
0267   ///
0268   /// @return float value according to the binning setup
0269   float value(const Vector2& lposition) const {
0270     // ordered after occurrence
0271     if (binvalue == AxisDirection::AxisR ||
0272         binvalue == AxisDirection::AxisRPhi ||
0273         binvalue == AxisDirection::AxisX ||
0274         binvalue == AxisDirection::AxisTheta) {
0275       return lposition[0];
0276     }
0277 
0278     return lposition[1];
0279   }
0280 
0281   /// Take the right float value
0282   ///
0283   /// @param position is the global position
0284   ///
0285   /// @return float value according to the binning setup
0286   float value(const Vector3& position) const {
0287     using VectorHelpers::eta;
0288     using VectorHelpers::perp;
0289     using VectorHelpers::phi;
0290     // ordered after occurrence
0291     if (binvalue == AxisDirection::AxisR ||
0292         binvalue == AxisDirection::AxisTheta) {
0293       return (perp(position));
0294     }
0295     if (binvalue == AxisDirection::AxisRPhi) {
0296       return (perp(position) * phi(position));
0297     }
0298     if (binvalue == AxisDirection::AxisEta) {
0299       return (eta(position));
0300     }
0301     if (toUnderlying(binvalue) < 3) {
0302       return static_cast<float>(position[toUnderlying(binvalue)]);
0303     }
0304     // phi gauging
0305     return phi(position);
0306   }
0307 
0308   /// Get the center value of a bin
0309   ///
0310   /// @param bin is the bin for which the center value is requested
0311   ///
0312   /// @return float value according to the bin center
0313   float center(std::size_t bin) const {
0314     const std::vector<float>& bvals = boundaries();
0315     // take the center between bin boundaries
0316     float value =
0317         bin < (bvals.size() - 1) ? 0.5 * (bvals[bin] + bvals[bin + 1]) : 0.;
0318     return value;
0319   }
0320 
0321   /// Get the width of a bin
0322   ///
0323   /// @param bin is the bin for which the width is requested
0324   ///
0325   /// @return float value of width
0326   float width(std::size_t bin) const {
0327     const std::vector<float>& bvals = boundaries();
0328     // take the center between bin boundaries
0329     float value = bin < (bvals.size() - 1) ? bvals[bin + 1] - bvals[bin] : 0.;
0330     return value;
0331   }
0332 
0333   /// Check if bin is inside from Vector3
0334   ///
0335   /// @param position is the search position in global coordinated
0336   ///
0337   /// @return boolean if this is inside() method is true
0338   bool inside(const Vector3& position) const {
0339     // closed one is always inside
0340     if (option == closed) {
0341       return true;
0342     }
0343     // all other options
0344     // @todo remove hard-coded tolerance parameters
0345     float val = value(position);
0346     return (val > min - 0.001 && val < max + 0.001);
0347   }
0348 
0349   /// Check if bin is inside from Vector2
0350   ///
0351   /// @param lposition is the search position in global coordinated
0352   ///
0353   /// @return boolean if this is inside() method is true
0354   bool inside(const Vector2& lposition) const {
0355     // closed one is always inside
0356     if (option == closed) {
0357       return true;
0358     }
0359     // all other options
0360     // @todo remove hard-coded tolerance parameters
0361     float val = value(lposition);
0362     return (val > min - 0.001 && val < max + 0.001);
0363   }
0364 
0365   /// Generic search from a 2D position
0366   /// -- corresponds to local coordinate schema
0367   /// @param lposition is the search position in local coordinated
0368   ///
0369   /// @return bin according tot this
0370   std::size_t searchLocal(const Vector2& lposition) const {
0371     if (zdim) {
0372       return 0;
0373     }
0374     return search(value(lposition));
0375   }
0376 
0377   /// Generic search from a 3D position
0378   /// -- corresponds to global coordinate schema
0379   /// @param position is the search position in global coordinated
0380   ///
0381   /// @return bin according tot this
0382   std::size_t searchGlobal(const Vector3& position) const {
0383     if (zdim) {
0384       return 0;
0385     }
0386     return search(value(position));
0387   }
0388 
0389   /// Generic search - forwards to correct function pointer
0390   ///
0391   /// @param value is the searchvalue as float
0392   ///
0393   /// @return bin according tot this
0394   std::size_t search(float value) const {
0395     if (zdim) {
0396       return 0;
0397     }
0398     assert(m_functionPtr != nullptr);
0399     return (!subBinningData) ? (*m_functionPtr)(value, *this)
0400                              : searchWithSubStructure(value);
0401   }
0402 
0403   ///  Generic search with sub structure
0404   /// - forwards to correct function pointer
0405   ///
0406   /// @param value is the searchvalue as float
0407   ///
0408   /// @return bin according tot this
0409   std::size_t searchWithSubStructure(float value) const {
0410     // find the masterbin with the correct function pointer
0411     std::size_t masterbin = (*m_functionPtr)(value, *this);
0412     // additive sub binning -
0413     if (subBinningAdditive) {
0414       // no gauging done, for additive sub structure
0415       return masterbin + subBinningData->search(value);
0416     }
0417     // gauge the value to the subBinData
0418     float gvalue =
0419         value - masterbin * (subBinningData->max - subBinningData->min);
0420     // now go / additive or multiplicative
0421     std::size_t subbin = subBinningData->search(gvalue);
0422     // now return
0423     return masterbin * subBinningData->bins() + subbin;
0424   }
0425 
0426   /// Layer next direction is needed
0427   ///
0428   /// @param position is the start search position
0429   /// @param dir is the direction
0430   /// @todo check if this can be changed
0431   ///
0432   /// @return integer that indicates which direction to move
0433   int nextDirection(const Vector3& position, const Vector3& dir) const {
0434     if (zdim) {
0435       return 0;
0436     }
0437     float val = value(position);
0438     Vector3 probe = position + dir.normalized();
0439     float nextval = value(probe);
0440     return (nextval > val) ? 1 : -1;
0441   }
0442 
0443   /// access to the center value
0444   /// this uses the bin boundary vector, it also works with sub structure
0445   ///
0446   /// @param bin is the bin for which the value is requested, if bin > nbins
0447   /// it is set to max
0448   ///
0449   /// @return the center value of the bin is given
0450   float centerValue(std::size_t bin) const {
0451     if (zdim) {
0452       return 0.5 * (min + max);
0453     }
0454     float bmin = m_boundaries[bin];
0455     float bmax = bin < m_boundaries.size() ? m_boundaries[bin + 1] : max;
0456     return 0.5 * (bmin + bmax);
0457   }
0458 
0459  private:
0460   std::size_t m_bins{};             ///< number of bins
0461   std::vector<float> m_boundaries;  ///< vector of holding the bin boundaries
0462   std::size_t m_totalBins{};        ///< including potential substructure
0463   std::vector<float> m_totalBoundaries;  ///< including potential substructure
0464 
0465   std::size_t (*m_functionPtr)(float,
0466                                const BinningData&){};  /// function pointer
0467 
0468   /// helper method to set the sub structure
0469   void checkSubStructure() {
0470     // sub structure is only checked when sBinData is defined
0471     if (subBinningData) {
0472       m_totalBoundaries.clear();
0473       // (A) additive sub structure
0474       if (subBinningAdditive) {
0475         // one bin is replaced by the sub bins
0476         m_totalBins = m_bins + subBinningData->bins() - 1;
0477         // the tricky one - exchange one bin by many others
0478         m_totalBoundaries.reserve(m_totalBins + 1);
0479         // get the sub bin boundaries
0480         const std::vector<float>& subBinBoundaries =
0481             subBinningData->boundaries();
0482         float sBinMin = subBinBoundaries[0];
0483         // get the min value of the sub bin boundaries
0484         std::vector<float>::const_iterator mbvalue = m_boundaries.begin();
0485         for (; mbvalue != m_boundaries.end(); ++mbvalue) {
0486           // should define numerically stable
0487           if (std::abs((*mbvalue) - sBinMin) < 10e-10) {
0488             // copy the sub bin boundaries into the vector
0489             m_totalBoundaries.insert(m_totalBoundaries.begin(),
0490                                      subBinBoundaries.begin(),
0491                                      subBinBoundaries.end());
0492             ++mbvalue;
0493           } else {
0494             m_totalBoundaries.push_back(*mbvalue);
0495           }
0496         }
0497       } else {  // (B) multiplicative sub structure
0498         // every bin is just replaced by the sub binning structure
0499         m_totalBins = m_bins * subBinningData->bins();
0500         m_totalBoundaries.reserve(m_totalBins + 1);
0501         // get the sub bin boundaries if there are any
0502         const std::vector<float>& subBinBoundaries =
0503             subBinningData->boundaries();
0504         // create the boundary vector
0505         m_totalBoundaries.push_back(min);
0506         for (std::size_t ib = 0; ib < m_bins; ++ib) {
0507           float offset = ib * step;
0508           for (std::size_t isb = 1; isb < subBinBoundaries.size(); ++isb) {
0509             m_totalBoundaries.push_back(offset + subBinBoundaries[isb]);
0510           }
0511         }
0512       }
0513       // sort the total boundary vector
0514       std::ranges::sort(m_totalBoundaries);
0515     }
0516   }
0517 
0518   // Equidistant search
0519   // - fastest method
0520   static std::size_t searchEquidistantWithBoundary(float value,
0521                                                    const BinningData& bData) {
0522     // vanilla
0523 
0524     int bin = static_cast<int>((value - bData.min) / bData.step);
0525     // special treatment of the 0 bin for closed
0526     if (bData.option == closed) {
0527       if (value < bData.min) {
0528         return (bData.m_bins - 1);
0529       }
0530       if (value > bData.max) {
0531         return 0;
0532       }
0533     }
0534     // if outside boundary : return boundary for open, opposite bin for closed
0535     bin = bin < 0 ? ((bData.option == open) ? 0 : (bData.m_bins - 1)) : bin;
0536     return static_cast<std::size_t>(
0537         (bin <= static_cast<int>(bData.m_bins - 1))
0538             ? bin
0539             : ((bData.option == open) ? (bData.m_bins - 1) : 0));
0540   }
0541 
0542   // Search in arbitrary boundary
0543   static std::size_t searchInVectorWithBoundary(float value,
0544                                                 const BinningData& bData) {
0545     // lower boundary
0546     if (value <= bData.m_boundaries[0]) {
0547       return (bData.option == closed) ? (bData.m_bins - 1) : 0;
0548     }
0549     // higher boundary
0550     if (value >= bData.max) {
0551       return (bData.option == closed) ? 0 : (bData.m_bins - 1);
0552     }
0553 
0554     auto lb = std::lower_bound(bData.m_boundaries.begin(),
0555                                bData.m_boundaries.end(), value);
0556     return static_cast<std::size_t>(
0557         std::distance(bData.m_boundaries.begin(), lb) - 1);
0558   }
0559 
0560  public:
0561   /// String screen output method
0562   /// @param indent the current indentation
0563   /// @return a string containing the screen information
0564   std::string toString(const std::string& indent = "") const {
0565     std::stringstream sl;
0566     sl << indent << "BinningData object:" << '\n';
0567     sl << indent << "  - type       : " << static_cast<std::size_t>(type)
0568        << '\n';
0569     sl << indent << "  - option     : " << static_cast<std::size_t>(option)
0570        << '\n';
0571     sl << indent << "  - value      : " << static_cast<std::size_t>(binvalue)
0572        << '\n';
0573     sl << indent << "  - bins       : " << bins() << '\n';
0574     sl << indent << "  - min/max    : " << min << " / " << max << '\n';
0575     if (type == equidistant) {
0576       sl << indent << "  - step       : " << step << '\n';
0577     }
0578     sl << indent << "  - boundaries : | ";
0579     for (const auto& b : boundaries()) {
0580       sl << b << " | ";
0581     }
0582     sl << '\n';
0583     return sl.str();
0584   }
0585 };
0586 }  // namespace Acts