Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-17 07:58:34

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/MagneticField/MagneticFieldContext.hpp"
0013 #include "Acts/MagneticField/MagneticFieldError.hpp"
0014 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
0015 #include "Acts/Utilities/Interpolation.hpp"
0016 #include "Acts/Utilities/Result.hpp"
0017 
0018 #include <functional>
0019 #include <optional>
0020 #include <vector>
0021 
0022 namespace Acts {
0023 
0024 class InterpolatedMagneticField : public MagneticFieldProvider {
0025  public:
0026   /// @brief get the number of bins for all axes of the field map
0027   ///
0028   /// @return vector returning number of bins for all field map axes
0029   virtual std::vector<std::size_t> getNBins() const = 0;
0030 
0031   /// @brief get the minimum value of all axes of the field map
0032   ///
0033   /// @return vector returning the minima of all field map axes
0034   virtual std::vector<double> getMin() const = 0;
0035 
0036   /// @brief get the maximum value of all axes of the field map
0037   ///
0038   /// @return vector returning the maxima of all field map axes
0039   virtual std::vector<double> getMax() const = 0;
0040 
0041   /// @brief check whether given 3D position is inside look-up domain
0042   ///
0043   /// @param [in] position global 3D position
0044   /// @return @c true if position is inside the defined look-up grid,
0045   ///         otherwise @c false
0046   virtual bool isInside(const Vector3& position) const = 0;
0047 
0048   /// Get a field value without checking if the lookup position is within the
0049   /// interpolation domain.
0050   ///
0051   /// @param position The lookup position in 3D
0052   /// @return The field value at @p position
0053   virtual Vector3 getFieldUnchecked(const Vector3& position) const = 0;
0054 };
0055 
0056 /// @ingroup MagneticField
0057 /// @brief interpolate magnetic field value from field values on a given grid
0058 ///
0059 /// This class implements a magnetic field service which is initialized by a
0060 /// field map defined by:
0061 /// - a list of field values on a regular grid in some n-Dimensional space,
0062 /// - a transformation of global 3D coordinates onto this n-Dimensional
0063 /// space.
0064 /// - a transformation of local n-Dimensional magnetic field coordinates into
0065 /// global (cartesian) 3D coordinates
0066 ///
0067 /// The magnetic field value for a given global position is then determined by:
0068 /// - mapping the position onto the grid,
0069 /// - looking up the magnetic field values on the closest grid points,
0070 /// - doing a linear interpolation of these magnetic field values.
0071 ///
0072 /// @tparam grid_t The Grid type which provides the field storage and
0073 /// interpolation
0074 template <typename grid_t>
0075 class InterpolatedBFieldMap : public InterpolatedMagneticField {
0076  public:
0077   /// Type alias for magnetic field grid
0078   using Grid = grid_t;
0079   /// Type alias for magnetic field vector type
0080   using FieldType = typename Grid::value_type;
0081   /// Dimensionality of the position space for field interpolation
0082   static constexpr std::size_t DIM_POS = Grid::DIM;
0083 
0084   /// @brief struct representing smallest grid unit in magnetic field grid
0085   ///
0086   /// This type encapsulate all required information to perform linear
0087   /// interpolation of magnetic field values within a confined 3D volume.
0088   struct FieldCell {
0089     /// number of corner points defining the confining hyper-box
0090     static constexpr unsigned int N = 1 << DIM_POS;
0091 
0092    public:
0093     /// @brief default constructor
0094     ///
0095     /// @param [in] lowerLeft   generalized lower-left corner of hyper box
0096     ///                         (containing the minima of the hyper box along
0097     ///                         each Dimension)
0098     /// @param [in] upperRight  generalized upper-right corner of hyper box
0099     ///                         (containing the maxima of the hyper box along
0100     ///                         each Dimension)
0101     /// @param [in] fieldValues field values at the hyper box corners sorted in
0102     ///                         the canonical order defined in Acts::interpolate
0103     FieldCell(std::array<double, DIM_POS> lowerLeft,
0104               std::array<double, DIM_POS> upperRight,
0105               std::array<Vector3, N> fieldValues)
0106         : m_lowerLeft(std::move(lowerLeft)),
0107           m_upperRight(std::move(upperRight)),
0108           m_fieldValues(std::move(fieldValues)) {}
0109 
0110     /// @brief retrieve field at given position
0111     ///
0112     /// @param [in] position global 3D position
0113     /// @return magnetic field value at the given position
0114     ///
0115     /// @pre The given @c position must lie within the current field cell.
0116     Vector3 getField(const ActsVector<DIM_POS>& position) const {
0117       // defined in Interpolation.hpp
0118       return interpolate(position, m_lowerLeft, m_upperRight, m_fieldValues);
0119     }
0120 
0121     /// @brief check whether given 3D position is inside this field cell
0122     ///
0123     /// @param [in] position global 3D position
0124     /// @return @c true if position is inside the current field cell,
0125     ///         otherwise @c false
0126     bool isInside(const ActsVector<DIM_POS>& position) const {
0127       for (unsigned int i = 0; i < DIM_POS; ++i) {
0128         if (position[i] < m_lowerLeft[i] || position[i] >= m_upperRight[i]) {
0129           return false;
0130         }
0131       }
0132       return true;
0133     }
0134 
0135    private:
0136     /// generalized lower-left corner of the confining hyper-box
0137     std::array<double, DIM_POS> m_lowerLeft;
0138 
0139     /// generalized upper-right corner of the confining hyper-box
0140     std::array<double, DIM_POS> m_upperRight;
0141 
0142     /// @brief magnetic field vectors at the hyper-box corners
0143     ///
0144     /// @note These values must be order according to the prescription detailed
0145     ///       in Acts::interpolate.
0146     std::array<Vector3, N> m_fieldValues;
0147   };
0148 
0149   struct Cache {
0150     /// @brief Constructor with magnetic field context
0151     explicit Cache(const MagneticFieldContext& /*mctx*/) {}
0152 
0153     std::optional<FieldCell> fieldCell;
0154     bool initialized = false;
0155   };
0156 
0157   /// @brief  Config structure for the interpolated B field map
0158   struct Config {
0159     /// @brief mapping of global 3D coordinates (cartesian)
0160     /// onto grid space
0161     std::function<ActsVector<DIM_POS>(const Vector3&)> transformPos;
0162 
0163     /// @brief calculating the global 3D coordinates
0164     /// (cartesian) of the magnetic field with the local n dimensional field and
0165     /// the global 3D position as input
0166     std::function<Vector3(const FieldType&, const Vector3&)> transformBField;
0167 
0168     /// @brief grid storing magnetic field values
0169     Grid grid;
0170 
0171     /// @brief global B-field scaling factor
0172     ///
0173     /// @note Negative values for @p scale are accepted and will invert the
0174     ///       direction of the magnetic field.
0175     double scale = 1.;
0176   };
0177 
0178   /// @brief default constructor
0179   ///
0180   /// @param cfg Configuration containing grid and scaling factor
0181   explicit InterpolatedBFieldMap(Config cfg) : m_cfg{std::move(cfg)} {
0182     typename Grid::index_t minBin{};
0183     minBin.fill(1);
0184     m_lowerLeft = m_cfg.grid.lowerLeftBinEdge(minBin);
0185     m_upperRight = m_cfg.grid.lowerLeftBinEdge(m_cfg.grid.numLocalBins());
0186   }
0187 
0188   /// @brief retrieve field cell for given position
0189   ///
0190   /// @param [in] position global 3D position
0191   /// @return field cell containing the given global position
0192   ///
0193   /// @pre The given @c position must lie within the range of the underlying
0194   ///      magnetic field map.
0195   Result<FieldCell> getFieldCell(const Vector3& position) const {
0196     const auto& gridPosition = m_cfg.transformPos(position);
0197     const auto& indices = m_cfg.grid.localBinsFromPosition(gridPosition);
0198     const auto& lowerLeft = m_cfg.grid.lowerLeftBinEdge(indices);
0199     const auto& upperRight = m_cfg.grid.upperRightBinEdge(indices);
0200 
0201     // loop through all corner points
0202     constexpr std::size_t nCorners = 1 << DIM_POS;
0203     std::array<Vector3, nCorners> neighbors;
0204     const auto& cornerIndices = m_cfg.grid.closestPointsIndices(gridPosition);
0205 
0206     if (!isInsideLocal(gridPosition)) {
0207       return MagneticFieldError::OutOfBounds;
0208     }
0209 
0210     std::size_t i = 0;
0211     for (std::size_t index : cornerIndices) {
0212       neighbors.at(i++) = m_cfg.transformBField(m_cfg.grid.at(index), position);
0213     }
0214 
0215     assert(i == nCorners);
0216 
0217     return FieldCell(lowerLeft, upperRight, std::move(neighbors));
0218   }
0219 
0220   /// @brief get the number of bins for all axes of the field map
0221   ///
0222   /// @return vector returning number of bins for all field map axes
0223   std::vector<std::size_t> getNBins() const final {
0224     auto nBinsArray = m_cfg.grid.numLocalBins();
0225     return std::vector<std::size_t>(nBinsArray.begin(), nBinsArray.end());
0226   }
0227 
0228   /// @brief get the minimum value of all axes of the field map
0229   ///
0230   /// @return vector returning the minima of all field map axes
0231   std::vector<double> getMin() const final {
0232     return std::vector<double>(m_lowerLeft.begin(), m_lowerLeft.end());
0233   }
0234 
0235   /// @brief get the maximum value of all axes of the field map
0236   ///
0237   /// @return vector returning the maxima of all field map axes
0238   std::vector<double> getMax() const final {
0239     return std::vector<double>(m_upperRight.begin(), m_upperRight.end());
0240   }
0241 
0242   /// @brief check whether given 3D position is inside look-up domain
0243   ///
0244   /// @param [in] position global 3D position
0245   /// @return @c true if position is inside the defined look-up grid,
0246   ///         otherwise @c false
0247   bool isInside(const Vector3& position) const final {
0248     return isInsideLocal(m_cfg.transformPos(position));
0249   }
0250 
0251   /// @brief check whether given 3D position is inside look-up domain
0252   ///
0253   /// @param [in] gridPosition local N-D position
0254   /// @return @c true if position is inside the defined look-up grid,
0255   ///         otherwise @c false
0256   bool isInsideLocal(const ActsVector<DIM_POS>& gridPosition) const {
0257     for (unsigned int i = 0; i < DIM_POS; ++i) {
0258       if (gridPosition[i] < m_lowerLeft[i] ||
0259           gridPosition[i] >= m_upperRight[i]) {
0260         return false;
0261       }
0262     }
0263     return true;
0264   }
0265 
0266   /// @brief Get a const reference on the underlying grid structure
0267   ///
0268   /// @return grid reference
0269   const Grid& getGrid() const { return m_cfg.grid; }
0270 
0271   /// @copydoc MagneticFieldProvider::makeCache(const MagneticFieldContext&) const
0272   MagneticFieldProvider::Cache makeCache(
0273       const MagneticFieldContext& mctx) const final {
0274     return MagneticFieldProvider::Cache{std::in_place_type<Cache>, mctx};
0275   }
0276 
0277   /// @brief retrieve field at given position
0278   ///
0279   /// @param [in] position global 3D position
0280   /// @return magnetic field value at the given position
0281   ///
0282   /// @pre The given @c position must lie within the range of the underlying
0283   ///      magnetic field map.
0284   Result<Vector3> getField(const Vector3& position) const {
0285     const auto gridPosition = m_cfg.transformPos(position);
0286     if (!isInsideLocal(gridPosition)) {
0287       return Result<Vector3>::failure(MagneticFieldError::OutOfBounds);
0288     }
0289 
0290     return Result<Vector3>::success(
0291         m_cfg.transformBField(m_cfg.grid.interpolate(gridPosition), position));
0292   }
0293 
0294   /// Get magnetic field value without bounds checking (faster).
0295   ///
0296   /// @param position Global 3D position for the lookup
0297   /// @return Magnetic field value at the given position
0298   ///
0299   /// @warning No bounds checking is performed. The caller must ensure
0300   ///          the position is within the valid range of the field map.
0301   Vector3 getFieldUnchecked(const Vector3& position) const final {
0302     const auto gridPosition = m_cfg.transformPos(position);
0303     return m_cfg.transformBField(m_cfg.grid.interpolate(gridPosition),
0304                                  position);
0305   }
0306 
0307   /// @copydoc MagneticFieldProvider::getField(const Vector3&,MagneticFieldProvider::Cache&) const
0308   Result<Vector3> getField(const Vector3& position,
0309                            MagneticFieldProvider::Cache& cache) const final {
0310     Cache& lcache = cache.as<Cache>();
0311     const auto gridPosition = m_cfg.transformPos(position);
0312     if (!lcache.fieldCell || !(*lcache.fieldCell).isInside(gridPosition)) {
0313       auto res = getFieldCell(position);
0314       if (!res.ok()) {
0315         return Result<Vector3>::failure(res.error());
0316       }
0317       lcache.fieldCell = *res;
0318     }
0319     return Result<Vector3>::success((*lcache.fieldCell).getField(gridPosition));
0320   }
0321 
0322  private:
0323   Config m_cfg;
0324 
0325   typename Grid::point_t m_lowerLeft;
0326   typename Grid::point_t m_upperRight;
0327 };
0328 
0329 }  // namespace Acts