Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:16

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