|
|
|||
File indexing completed on 2026-05-18 07:47:37
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 /// @addtogroup magnetic_field 0025 /// @{ 0026 0027 /// @brief Base class for interpolated magnetic field providers 0028 /// 0029 /// This class can be used for non-trivial magnetic field implementations. 0030 /// 0031 /// The key idea here is to calculate an interpolated value of the magnetic 0032 /// field from a grid of known field values. In 3D, this means the 0033 /// interpolation is done from the 8 corner points of a *field cell*. The field 0034 /// cell can be retrieved for any given position. Since during typical access 0035 /// patterns, e.g. the propagation, subsequent steps are relatively likely to 0036 /// not cross the field cell boundary, the field cell can be cached. 0037 /// 0038 /// @image html bfield/field_cell.svg "Illustration of the field cell concept. Subsequent steps are clustered in the same field cell. The field cell only needs to be refetched when the propagation crosses into the next grid region." width=60% 0039 /// 0040 class InterpolatedMagneticField : public MagneticFieldProvider { 0041 public: 0042 /// @brief get the number of bins for all axes of the field map 0043 /// 0044 /// @return vector returning number of bins for all field map axes 0045 virtual std::vector<std::size_t> getNBins() const = 0; 0046 0047 /// @brief get the minimum value of all axes of the field map 0048 /// 0049 /// @return vector returning the minima of all field map axes 0050 virtual std::vector<double> getMin() const = 0; 0051 0052 /// @brief get the maximum value of all axes of the field map 0053 /// 0054 /// @return vector returning the maxima of all field map axes 0055 virtual std::vector<double> getMax() const = 0; 0056 0057 /// @brief check whether given 3D position is inside look-up domain 0058 /// 0059 /// @param [in] position global 3D position 0060 /// @return @c true if position is inside the defined look-up grid, 0061 /// otherwise @c false 0062 virtual bool isInside(const Vector3& position) const = 0; 0063 0064 /// Get a field value without checking if the lookup position is within the 0065 /// interpolation domain. 0066 /// 0067 /// @param position The lookup position in 3D 0068 /// @return The field value at @p position 0069 virtual Vector3 getFieldUnchecked(const Vector3& position) const = 0; 0070 }; 0071 0072 /// Interpolates magnetic field value from field values on a given grid 0073 /// 0074 /// This class implements a magnetic field service which is initialized by a 0075 /// field map defined by: 0076 /// - a list of field values on a regular grid in some n-dimensional space, 0077 /// - a transformation of global 3D coordinates onto this n-dimensional 0078 /// space. 0079 /// - a transformation of local n-Dimensional magnetic field coordinates into 0080 /// global (cartesian) 3D coordinates 0081 /// 0082 /// The magnetic field value for a given global position is then determined by: 0083 /// - mapping the position onto the grid, 0084 /// - looking up the magnetic field values on the closest grid points, 0085 /// - doing a linear interpolation of these magnetic field values. 0086 /// 0087 /// Internally, this class uses a *field interpolation cell* to speed up 0088 /// lookups. This cell contains the interpolation points so the grid does not 0089 /// have to be consulted for each lookup. Explicit methods to create such a 0090 /// field cell are provided, but field cell creation is automatically handled 0091 /// by @ref Acts::InterpolatedBFieldMap::makeCache, opaque to the client. 0092 /// 0093 /// This class can leverage spatial symmetries in the magnetic field 0094 /// distribution. For cylindrically symmetric fields (e.g., solenoids, toroids), 0095 /// a 2D rz map can be used instead of a full 3D xyz map, significantly reducing 0096 /// memory 0097 /// requirements and improving performance. Helper functions @ref Acts::fieldMapRZ 0098 /// and @ref Acts::fieldMapXYZ are provided to construct field maps with the 0099 /// appropriate symmetries. 0100 /// 0101 /// @tparam grid_t The Grid type which provides the field storage and 0102 /// interpolation 0103 template <typename grid_t> 0104 class InterpolatedBFieldMap : public InterpolatedMagneticField { 0105 public: 0106 /// Type alias for magnetic field grid 0107 using Grid = grid_t; 0108 /// Type alias for magnetic field vector type 0109 using FieldType = typename Grid::value_type; 0110 /// Dimensionality of the position space for field interpolation 0111 static constexpr std::size_t DIM_POS = Grid::DIM; 0112 0113 /// @brief struct representing smallest grid unit in magnetic field grid 0114 /// 0115 /// This type encapsulates all required information to perform linear 0116 /// interpolation of magnetic field values within a confined spatial region 0117 /// (hyper-box). The cell stores field values at all corner points and 0118 /// performs interpolation for any position within the cell boundaries. 0119 /// This allows for efficient repeated lookups within the same grid cell 0120 /// without consulting the full grid structure. 0121 struct FieldCell { 0122 /// number of corner points defining the confining hyper-box 0123 static constexpr unsigned int N = 1 << DIM_POS; 0124 0125 public: 0126 /// @brief default constructor 0127 /// 0128 /// @param [in] lowerLeft generalized lower-left corner of hyper box 0129 /// (containing the minima of the hyper box along 0130 /// each Dimension) 0131 /// @param [in] upperRight generalized upper-right corner of hyper box 0132 /// (containing the maxima of the hyper box along 0133 /// each Dimension) 0134 /// @param [in] fieldValues field values at the hyper box corners sorted in 0135 /// the canonical order defined in Acts::interpolate 0136 FieldCell(std::array<double, DIM_POS> lowerLeft, 0137 std::array<double, DIM_POS> upperRight, 0138 std::array<Vector3, N> fieldValues) 0139 : m_lowerLeft(std::move(lowerLeft)), 0140 m_upperRight(std::move(upperRight)), 0141 m_fieldValues(std::move(fieldValues)) {} 0142 0143 /// @brief retrieve field at given position 0144 /// 0145 /// @param [in] position global 3D position 0146 /// @return magnetic field value at the given position 0147 /// 0148 /// @pre The given @c position must lie within the current field cell. 0149 Vector3 getField(const Vector<DIM_POS>& position) const { 0150 // defined in Interpolation.hpp 0151 return interpolate(position, m_lowerLeft, m_upperRight, m_fieldValues); 0152 } 0153 0154 /// @brief check whether given 3D position is inside this field cell 0155 /// 0156 /// @param [in] position global 3D position 0157 /// @return @c true if position is inside the current field cell, 0158 /// otherwise @c false 0159 bool isInside(const Vector<DIM_POS>& position) const { 0160 for (unsigned int i = 0; i < DIM_POS; ++i) { 0161 if (position[i] < m_lowerLeft[i] || position[i] >= m_upperRight[i]) { 0162 return false; 0163 } 0164 } 0165 return true; 0166 } 0167 0168 private: 0169 /// generalized lower-left corner of the confining hyper-box 0170 std::array<double, DIM_POS> m_lowerLeft; 0171 0172 /// generalized upper-right corner of the confining hyper-box 0173 std::array<double, DIM_POS> m_upperRight; 0174 0175 /// @brief magnetic field vectors at the hyper-box corners 0176 /// 0177 /// @note These values must be order according to the prescription detailed 0178 /// in Acts::interpolate. 0179 std::array<Vector3, N> m_fieldValues; 0180 }; 0181 0182 /// @brief Cache for field cell to improve performance of field lookups 0183 /// 0184 /// This cache stores the current field cell which contains the interpolation 0185 /// data for a confined region of space. By caching the cell, subsequent 0186 /// lookups at nearby positions (e.g., during track propagation) can avoid 0187 /// expensive grid queries. The cache automatically updates when a position 0188 /// outside the current cell is queried. 0189 struct Cache { 0190 /// @brief Constructor with magnetic field context 0191 explicit Cache(const MagneticFieldContext& /*mctx*/) {} 0192 0193 /// Stored field cell containing interpolation data 0194 std::optional<FieldCell> fieldCell; 0195 /// Flag indicating if the cache has been initialized 0196 bool initialized = false; 0197 }; 0198 0199 /// @brief Config structure for the interpolated B field map 0200 struct Config { 0201 /// @brief mapping of global 3D coordinates (cartesian) 0202 /// onto grid space 0203 std::function<Vector<DIM_POS>(const Vector3&)> transformPos; 0204 0205 /// @brief calculating the global 3D coordinates 0206 /// (cartesian) of the magnetic field with the local n dimensional field and 0207 /// the global 3D position as input 0208 std::function<Vector3(const FieldType&, const Vector3&)> transformBField; 0209 0210 /// @brief grid storing magnetic field values 0211 Grid grid; 0212 0213 /// @brief global B-field scaling factor 0214 /// 0215 /// @note Negative values for @p scale are accepted and will invert the 0216 /// direction of the magnetic field. 0217 double scale = 1.; 0218 }; 0219 0220 /// @brief default constructor 0221 /// 0222 /// @param cfg Configuration containing grid and scaling factor 0223 explicit InterpolatedBFieldMap(Config cfg) : m_cfg{std::move(cfg)} { 0224 typename Grid::index_t minBin{}; 0225 minBin.fill(1); 0226 m_lowerLeft = m_cfg.grid.lowerLeftBinEdge(minBin); 0227 m_upperRight = m_cfg.grid.lowerLeftBinEdge(m_cfg.grid.numLocalBins()); 0228 } 0229 0230 /// @brief retrieve field cell for given position 0231 /// 0232 /// @param [in] position global 3D position 0233 /// @return field cell containing the given global position 0234 /// 0235 /// @pre The given @c position must lie within the range of the underlying 0236 /// magnetic field map. 0237 Result<FieldCell> getFieldCell(const Vector3& position) const { 0238 const auto& gridPosition = m_cfg.transformPos(position); 0239 const auto& indices = m_cfg.grid.localBinsFromPosition(gridPosition); 0240 const auto& lowerLeft = m_cfg.grid.lowerLeftBinEdge(indices); 0241 const auto& upperRight = m_cfg.grid.upperRightBinEdge(indices); 0242 0243 // loop through all corner points 0244 constexpr std::size_t nCorners = 1 << DIM_POS; 0245 std::array<Vector3, nCorners> neighbors{}; 0246 const auto& cornerIndices = m_cfg.grid.closestPointsIndices(gridPosition); 0247 0248 if (!isInsideLocal(gridPosition)) { 0249 return MagneticFieldError::OutOfBounds; 0250 } 0251 0252 std::size_t i = 0; 0253 for (std::size_t index : cornerIndices) { 0254 neighbors.at(i++) = m_cfg.transformBField(m_cfg.grid.at(index), position); 0255 } 0256 0257 assert(i == nCorners); 0258 0259 return FieldCell(lowerLeft, upperRight, std::move(neighbors)); 0260 } 0261 0262 /// @brief get the number of bins for all axes of the field map 0263 /// 0264 /// @return vector returning number of bins for all field map axes 0265 std::vector<std::size_t> getNBins() const final { 0266 auto nBinsArray = m_cfg.grid.numLocalBins(); 0267 return std::vector<std::size_t>(nBinsArray.begin(), nBinsArray.end()); 0268 } 0269 0270 /// @brief get the minimum value of all axes of the field map 0271 /// 0272 /// @return vector returning the minima of all field map axes 0273 std::vector<double> getMin() const final { 0274 return std::vector<double>(m_lowerLeft.begin(), m_lowerLeft.end()); 0275 } 0276 0277 /// @brief get the maximum value of all axes of the field map 0278 /// 0279 /// @return vector returning the maxima of all field map axes 0280 std::vector<double> getMax() const final { 0281 return std::vector<double>(m_upperRight.begin(), m_upperRight.end()); 0282 } 0283 0284 /// @brief check whether given 3D position is inside look-up domain 0285 /// 0286 /// @param [in] position global 3D position 0287 /// @return @c true if position is inside the defined look-up grid, 0288 /// otherwise @c false 0289 bool isInside(const Vector3& position) const final { 0290 return isInsideLocal(m_cfg.transformPos(position)); 0291 } 0292 0293 /// @brief check whether given 3D position is inside look-up domain 0294 /// 0295 /// @param [in] gridPosition local N-D position 0296 /// @return @c true if position is inside the defined look-up grid, 0297 /// otherwise @c false 0298 bool isInsideLocal(const Vector<DIM_POS>& gridPosition) const { 0299 for (unsigned int i = 0; i < DIM_POS; ++i) { 0300 if (gridPosition[i] < m_lowerLeft[i] || 0301 gridPosition[i] >= m_upperRight[i]) { 0302 return false; 0303 } 0304 } 0305 return true; 0306 } 0307 0308 /// @brief Get a const reference on the underlying grid structure 0309 /// 0310 /// @return grid reference 0311 const Grid& getGrid() const { return m_cfg.grid; } 0312 0313 /// @copydoc MagneticFieldProvider::makeCache(const MagneticFieldContext&) const 0314 MagneticFieldProvider::Cache makeCache( 0315 const MagneticFieldContext& mctx) const final { 0316 return MagneticFieldProvider::Cache{std::in_place_type<Cache>, mctx}; 0317 } 0318 0319 /// @brief retrieve field at given position 0320 /// 0321 /// @param [in] position global 3D position 0322 /// @return magnetic field value at the given position 0323 /// 0324 /// @pre The given @c position must lie within the range of the underlying 0325 /// magnetic field map. 0326 Result<Vector3> getField(const Vector3& position) const { 0327 const auto gridPosition = m_cfg.transformPos(position); 0328 if (!isInsideLocal(gridPosition)) { 0329 return Result<Vector3>::failure(MagneticFieldError::OutOfBounds); 0330 } 0331 0332 return Result<Vector3>::success( 0333 m_cfg.transformBField(m_cfg.grid.interpolate(gridPosition), position)); 0334 } 0335 0336 /// Get magnetic field value without bounds checking (faster). 0337 /// 0338 /// @param position Global 3D position for the lookup 0339 /// @return Magnetic field value at the given position 0340 /// 0341 /// @warning No bounds checking is performed. The caller must ensure 0342 /// the position is within the valid range of the field map. 0343 Vector3 getFieldUnchecked(const Vector3& position) const final { 0344 const auto gridPosition = m_cfg.transformPos(position); 0345 return m_cfg.transformBField(m_cfg.grid.interpolate(gridPosition), 0346 position); 0347 } 0348 0349 /// @copydoc MagneticFieldProvider::getField(const Vector3&,MagneticFieldProvider::Cache&) const 0350 Result<Vector3> getField(const Vector3& position, 0351 MagneticFieldProvider::Cache& cache) const final { 0352 Cache& lcache = cache.as<Cache>(); 0353 const auto gridPosition = m_cfg.transformPos(position); 0354 if (!lcache.fieldCell || !(*lcache.fieldCell).isInside(gridPosition)) { 0355 auto res = getFieldCell(position); 0356 if (!res.ok()) { 0357 return Result<Vector3>::failure(res.error()); 0358 } 0359 lcache.fieldCell = *res; 0360 } 0361 return Result<Vector3>::success((*lcache.fieldCell).getField(gridPosition)); 0362 } 0363 0364 private: 0365 Config m_cfg; 0366 0367 typename Grid::point_t m_lowerLeft; 0368 typename Grid::point_t m_upperRight; 0369 }; 0370 0371 /// @} 0372 0373 } // namespace Acts
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|