File indexing completed on 2026-09-16 08:19:45
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/MagneticField/BFieldMapUtils.hpp"
0010
0011 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0012 #include "Acts/MagneticField/SolenoidBField.hpp"
0013 #include "Acts/MagneticField/ToroidField.hpp"
0014 #include "Acts/Utilities/Axis.hpp"
0015 #include "Acts/Utilities/Grid.hpp"
0016 #include "Acts/Utilities/Helpers.hpp"
0017 #include "Acts/Utilities/VectorHelpers.hpp"
0018
0019 #include <cmath>
0020 #include <cstddef>
0021 #include <cstdlib>
0022 #include <limits>
0023 #include <stdexcept>
0024
0025 namespace Acts {
0026
0027 using VectorHelpers::perp;
0028 using VectorHelpers::phi;
0029
0030 InterpolatedBFieldMap<
0031 Grid<Vector2, Axis<AxisType::Equidistant>, Axis<AxisType::Equidistant>>>
0032 fieldMapRZ(const std::function<std::size_t(std::array<std::size_t, 2> binsRZ,
0033 std::array<std::size_t, 2> nBinsRZ)>&
0034 localToGlobalBin,
0035 std::vector<double> rPos, std::vector<double> zPos,
0036 const std::vector<Vector2>& bField, double lengthUnit,
0037 double BFieldUnit, bool firstQuadrant) {
0038
0039 const auto [rMin, rMax, rBinCount] = detail::getMinMaxAndBinCount(rPos);
0040 auto [zMin, zMax, zBinCount] = detail::getMinMaxAndBinCount(zPos);
0041
0042 const std::size_t nBinsR = rBinCount;
0043 std::size_t nBinsZ = zBinCount;
0044
0045 if (firstQuadrant) {
0046 zMin = -zPos[nBinsZ - 1];
0047 nBinsZ = 2 * nBinsZ - 1;
0048 }
0049
0050
0051 Axis rAxis(rMin * lengthUnit, rMax * lengthUnit, nBinsR);
0052 Axis zAxis(zMin * lengthUnit, zMax * lengthUnit, nBinsZ);
0053
0054
0055 Grid grid(Type<Vector2>, std::move(rAxis), std::move(zAxis));
0056 using Grid_t = decltype(grid);
0057
0058
0059 const std::array<std::size_t, 2> nIndices = {{rBinCount, zBinCount}};
0060 for (std::size_t i = 1; i <= nBinsR; ++i) {
0061 for (std::size_t j = 1; j <= nBinsZ; ++j) {
0062 Grid_t::index_t indices = {{i, j}};
0063
0064
0065
0066 if (firstQuadrant) {
0067 std::size_t n = std::abs(static_cast<std::ptrdiff_t>(j) -
0068 static_cast<std::ptrdiff_t>(zBinCount));
0069
0070 grid.atLocalBins(indices) =
0071 bField.at(localToGlobalBin({{i - 1, n}}, nIndices)) * BFieldUnit;
0072 } else {
0073 grid.atLocalBins(indices) =
0074 bField.at(localToGlobalBin({{i - 1, j - 1}}, nIndices)) *
0075 BFieldUnit;
0076 }
0077 }
0078 }
0079 grid.setExteriorBins(Vector2::Zero());
0080
0081
0082 auto transformPos = [](const Vector3& pos) {
0083 return Vector2(perp(pos), pos.z());
0084 };
0085
0086
0087 auto transformBField = [](const Vector2& field, const Vector3& pos) {
0088 const double rSinTheta2 = pos.x() * pos.x() + pos.y() * pos.y();
0089 double cosPhi = 1.;
0090 double sinPhi = 0.;
0091
0092 if (rSinTheta2 > std::numeric_limits<double>::min()) {
0093 const double invRsinTheta = 1. / std::sqrt(rSinTheta2);
0094 cosPhi = pos.x() * invRsinTheta;
0095 sinPhi = pos.y() * invRsinTheta;
0096 }
0097
0098 return Vector3(field.x() * cosPhi, field.x() * sinPhi, field.y());
0099 };
0100
0101
0102 return InterpolatedBFieldMap<Grid_t>(
0103 {transformPos, transformBField, std::move(grid)});
0104 }
0105
0106 InterpolatedBFieldMap<
0107 Grid<Vector3, Axis<AxisType::Equidistant>, Axis<AxisType::Equidistant>,
0108 Axis<AxisType::Equidistant>>>
0109 fieldMapXYZ(
0110 const std::function<std::size_t(std::array<std::size_t, 3> binsXYZ,
0111 std::array<std::size_t, 3> nBinsXYZ)>&
0112 localToGlobalBin,
0113 std::vector<double> xPos, std::vector<double> yPos,
0114 std::vector<double> zPos, const std::vector<Vector3>& bField,
0115 double lengthUnit, double BFieldUnit, bool firstOctant) {
0116
0117 auto [xMin, xMax, xBinCount] = detail::getMinMaxAndBinCount(xPos);
0118 auto [yMin, yMax, yBinCount] = detail::getMinMaxAndBinCount(yPos);
0119 auto [zMin, zMax, zBinCount] = detail::getMinMaxAndBinCount(zPos);
0120
0121 std::size_t nBinsX = xBinCount;
0122 std::size_t nBinsY = yBinCount;
0123 std::size_t nBinsZ = zBinCount;
0124
0125 if (firstOctant) {
0126 xMin = -xPos[nBinsX - 1];
0127 nBinsX = 2 * nBinsX - 1;
0128 yMin = -yPos[nBinsY - 1];
0129 nBinsY = 2 * nBinsY - 1;
0130 zMin = -zPos[nBinsZ - 1];
0131 nBinsZ = 2 * nBinsZ - 1;
0132 }
0133
0134 Axis xAxis(xMin * lengthUnit, xMax * lengthUnit, nBinsX);
0135 Axis yAxis(yMin * lengthUnit, yMax * lengthUnit, nBinsY);
0136 Axis zAxis(zMin * lengthUnit, zMax * lengthUnit, nBinsZ);
0137
0138 Grid grid(Type<Vector3>, std::move(xAxis), std::move(yAxis),
0139 std::move(zAxis));
0140 using Grid_t = decltype(grid);
0141
0142
0143 const std::array<std::size_t, 3> nIndices = {
0144 {xBinCount, yBinCount, zBinCount}};
0145
0146 auto calcAbsDiff = [](std::size_t val, std::size_t binCount) {
0147 return std::abs(static_cast<std::ptrdiff_t>(val) -
0148 static_cast<std::ptrdiff_t>(binCount));
0149 };
0150
0151 for (std::size_t i = 1; i <= nBinsX; ++i) {
0152 for (std::size_t j = 1; j <= nBinsY; ++j) {
0153 for (std::size_t k = 1; k <= nBinsZ; ++k) {
0154 Grid_t::index_t indices = {{i, j, k}};
0155
0156
0157
0158 if (firstOctant) {
0159 const std::size_t l = calcAbsDiff(i, xBinCount);
0160 const std::size_t m = calcAbsDiff(j, yBinCount);
0161 const std::size_t n = calcAbsDiff(k, zBinCount);
0162
0163 grid.atLocalBins(indices) =
0164 bField.at(localToGlobalBin({{l, m, n}}, nIndices)) * BFieldUnit;
0165 } else {
0166 grid.atLocalBins(indices) =
0167 bField.at(localToGlobalBin({{i - 1, j - 1, k - 1}}, nIndices)) *
0168 BFieldUnit;
0169 }
0170 }
0171 }
0172 }
0173 grid.setExteriorBins(Vector3::Zero());
0174
0175
0176 auto transformPos = [](const Vector3& pos) { return pos; };
0177
0178
0179 auto transformBField = [](const Vector3& field, const Vector3& ) {
0180 return field;
0181 };
0182
0183
0184 return InterpolatedBFieldMap<Grid_t>(
0185 {transformPos, transformBField, std::move(grid)});
0186 }
0187
0188 InterpolatedBFieldMap<
0189 Grid<Vector2, Axis<AxisType::Equidistant>, Axis<AxisType::Equidistant>>>
0190 solenoidFieldMap(const std::pair<double, double>& rLim,
0191 const std::pair<double, double>& zLim,
0192 const std::pair<std::size_t, std::size_t>& nBins,
0193 const SolenoidBField& field) {
0194 auto [rMin, rMax] = rLim;
0195 auto [zMin, zMax] = zLim;
0196 const auto [nBinsR, nBinsZ] = nBins;
0197
0198 double stepZ = std::abs(zMax - zMin) / (nBinsZ - 1);
0199 double stepR = std::abs(rMax - rMin) / (nBinsR - 1);
0200 rMax += stepR;
0201 zMax += stepZ;
0202
0203
0204 Axis rAxis(rMin, rMax, nBinsR);
0205 Axis zAxis(zMin, zMax, nBinsZ);
0206
0207
0208 Grid grid(Type<Vector2>, std::move(rAxis), std::move(zAxis));
0209 using Grid_t = decltype(grid);
0210
0211
0212 auto transformPos = [](const Vector3& pos) {
0213 return Vector2(perp(pos), pos.z());
0214 };
0215
0216
0217 auto transformBField = [](const Vector2& bField, const Vector3& pos) {
0218 const double rSinTheta2 = pos.x() * pos.x() + pos.y() * pos.y();
0219 double cosPhi = 1.;
0220 double sinPhi = 0.;
0221
0222 if (rSinTheta2 > std::numeric_limits<double>::min()) {
0223 const double invRsinTheta = 1. / std::sqrt(rSinTheta2);
0224 cosPhi = pos.x() * invRsinTheta;
0225 sinPhi = pos.y() * invRsinTheta;
0226 }
0227
0228 return Vector3(bField.x() * cosPhi, bField.x() * sinPhi, bField.y());
0229 };
0230
0231
0232
0233 for (std::size_t i = 0; i <= nBinsR + 1; i++) {
0234 for (std::size_t j = 0; j <= nBinsZ + 1; j++) {
0235 Grid_t::index_t index({i, j});
0236 if (i == 0 || j == 0 || i == nBinsR + 1 || j == nBinsZ + 1) {
0237
0238 grid.atLocalBins(index) = Grid_t::value_type(0, 0);
0239 } else {
0240
0241 Grid_t::point_t lowerLeft = grid.multiAxis().getLowerLeftBinEdge(index);
0242
0243 Vector2 B = field.getField(Vector2(lowerLeft[0], lowerLeft[1]));
0244 grid.atLocalBins(index) = B;
0245 }
0246 }
0247 }
0248
0249
0250 InterpolatedBFieldMap<Grid_t> map(
0251 {transformPos, transformBField, std::move(grid)});
0252 return map;
0253 }
0254
0255 InterpolatedBFieldMap<
0256 Grid<Vector3, Axis<AxisType::Equidistant>, Axis<AxisType::Equidistant>,
0257 Axis<AxisType::Equidistant>>>
0258 toroidFieldMapCyl(
0259 const std::pair<double, double>& rLim,
0260 const std::pair<double, double>& phiLim,
0261 const std::pair<double, double>& zLim,
0262 const std::tuple<std::size_t, std::size_t, std::size_t>& nBins,
0263 const ToroidField& field) {
0264
0265 MagneticFieldContext ctx;
0266 auto cache = field.makeCache(ctx);
0267
0268 auto [rMin, rMax] = rLim;
0269 auto [pMin, pMax] = phiLim;
0270 auto [zMin, zMax] = zLim;
0271 const auto& [nBinsR, nBinsP, nBinsZ] = nBins;
0272
0273
0274 if (nBinsR < 2 || nBinsP < 2 || nBinsZ < 2) {
0275 throw std::invalid_argument(
0276 "toroidFieldMapCyl: each dimension needs at least 2 bins");
0277 }
0278
0279
0280
0281 double stepR = std::abs(rMax - rMin) / static_cast<double>(nBinsR - 1);
0282 double stepP = std::abs(pMax - pMin) / static_cast<double>(nBinsP - 1);
0283 double stepZ = std::abs(zMax - zMin) / static_cast<double>(nBinsZ - 1);
0284 rMax += stepR;
0285 pMax += stepP;
0286 zMax += stepZ;
0287
0288
0289 Axis rAxis(rMin, rMax, nBinsR);
0290 Axis pAxis(pMin, pMax, nBinsP);
0291 Axis zAxis(zMin, zMax, nBinsZ);
0292
0293
0294 Grid grid(Type<Vector3>, std::move(rAxis), std::move(pAxis),
0295 std::move(zAxis));
0296 using Grid_t = decltype(grid);
0297
0298
0299 auto transformPos = [](const Vector3& pos) {
0300 const double r2 = pos.x() * pos.x() + pos.y() * pos.y();
0301 const double r = std::sqrt(r2);
0302 double phi = 0.0;
0303 if (r2 > std::numeric_limits<double>::min()) {
0304 phi = std::atan2(pos.y(), pos.x());
0305 }
0306 return Vector3(r, phi,
0307 pos.z());
0308 };
0309
0310
0311 auto transformBField = [](const Vector3& bCyl, const Vector3& pos) {
0312 const double r2 = pos.x() * pos.x() + pos.y() * pos.y();
0313 double cosPhi = 1.0;
0314 double sinPhi = 0.0;
0315 if (r2 > std::numeric_limits<double>::min()) {
0316 const double invR = 1.0 / std::sqrt(r2);
0317 cosPhi = pos.x() * invR;
0318 sinPhi = pos.y() * invR;
0319 }
0320 const double Br = bCyl.x();
0321 const double Bphi = bCyl.y();
0322 const double Bz = bCyl.z();
0323
0324 const double Bx = Br * cosPhi - Bphi * sinPhi;
0325 const double By = Br * sinPhi + Bphi * cosPhi;
0326 return Vector3(Bx, By, Bz);
0327 };
0328
0329
0330 for (std::size_t ir = 0; ir <= nBinsR + 1; ++ir) {
0331 for (std::size_t ip = 0; ip <= nBinsP + 1; ++ip) {
0332 for (std::size_t iz = 0; iz <= nBinsZ + 1; ++iz) {
0333 Grid_t::index_t index({ir, ip, iz});
0334 if (ir == 0 || ip == 0 || iz == 0 || ir == nBinsR + 1 ||
0335 ip == nBinsP + 1 || iz == nBinsZ + 1) {
0336 grid.atLocalBins(index) = Grid_t::value_type(0.0, 0.0, 0.0);
0337 } else {
0338 const Grid_t::point_t ll =
0339 grid.multiAxis().getLowerLeftBinEdge(index);
0340 const double r = ll[0];
0341 const double phi = ll[1];
0342 const double z = ll[2];
0343
0344 const double x = r * std::cos(phi);
0345 const double y = r * std::sin(phi);
0346
0347
0348 auto res = field.getField(Vector3(x, y, z), cache);
0349
0350 Vector3 Bxyz(0.0, 0.0, 0.0);
0351 if (res.ok()) {
0352 Bxyz = *res;
0353 }
0354
0355
0356 double cosPhi =
0357 (r > std::numeric_limits<double>::min()) ? std::cos(phi) : 1.0;
0358 double sinPhi =
0359 (r > std::numeric_limits<double>::min()) ? std::sin(phi) : 0.0;
0360
0361 const double Br = Bxyz.x() * cosPhi + Bxyz.y() * sinPhi;
0362 const double Bphi = -Bxyz.x() * sinPhi + Bxyz.y() * cosPhi;
0363 const double Bz = Bxyz.z();
0364
0365 grid.atLocalBins(index) = Grid_t::value_type(Br, Bphi, Bz);
0366 }
0367 }
0368 }
0369 }
0370
0371
0372 InterpolatedBFieldMap<Grid_t> map(
0373 {transformPos, transformBField, std::move(grid)});
0374 return map;
0375 }
0376
0377 InterpolatedBFieldMap<
0378 Grid<Vector3, Axis<AxisType::Equidistant>, Axis<AxisType::Equidistant>,
0379 Axis<AxisType::Equidistant>>>
0380 toroidFieldMapXYZ(
0381 const std::pair<double, double>& xLim,
0382 const std::pair<double, double>& yLim,
0383 const std::pair<double, double>& zLim,
0384 const std::tuple<std::size_t, std::size_t, std::size_t>& nBins,
0385 const ToroidField& field) {
0386
0387 MagneticFieldContext ctx;
0388 auto cache = field.makeCache(ctx);
0389
0390 auto [xMin, xMax] = xLim;
0391 auto [yMin, yMax] = yLim;
0392 auto [zMin, zMax] = zLim;
0393 const auto& [nBinsX, nBinsY, nBinsZ] = nBins;
0394
0395 if (nBinsX < 2 || nBinsY < 2 || nBinsZ < 2) {
0396 throw std::invalid_argument(
0397 "toroidFieldMapXYZ: each dimension needs at least 2 bins");
0398 }
0399
0400 double stepX = std::abs(xMax - xMin) / static_cast<double>(nBinsX - 1);
0401 double stepY = std::abs(yMax - yMin) / static_cast<double>(nBinsY - 1);
0402 double stepZ = std::abs(zMax - zMin) / static_cast<double>(nBinsZ - 1);
0403 xMax += stepX;
0404 yMax += stepY;
0405 zMax += stepZ;
0406
0407 Axis xAxis(xMin, xMax, nBinsX);
0408 Axis yAxis(yMin, yMax, nBinsY);
0409 Axis zAxis(zMin, zMax, nBinsZ);
0410
0411 Grid grid(Type<Vector3>, std::move(xAxis), std::move(yAxis),
0412 std::move(zAxis));
0413 using Grid_t = decltype(grid);
0414
0415
0416 auto transformPos = [](const Vector3& pos) { return pos; };
0417
0418
0419 auto transformBField = [](const Vector3& bField, const Vector3& ) {
0420 return bField;
0421 };
0422
0423 for (std::size_t ix = 0; ix <= nBinsX + 1; ++ix) {
0424 for (std::size_t iy = 0; iy <= nBinsY + 1; ++iy) {
0425 for (std::size_t iz = 0; iz <= nBinsZ + 1; ++iz) {
0426 Grid_t::index_t index({ix, iy, iz});
0427 if (ix == 0 || iy == 0 || iz == 0 || ix == nBinsX + 1 ||
0428 iy == nBinsY + 1 || iz == nBinsZ + 1) {
0429 grid.atLocalBins(index) = Grid_t::value_type(0.0, 0.0, 0.0);
0430 } else {
0431 const Grid_t::point_t ll =
0432 grid.multiAxis().getLowerLeftBinEdge(index);
0433 const double x = ll[0];
0434 const double y = ll[1];
0435 const double z = ll[2];
0436
0437 auto res = field.getField(Vector3(x, y, z), cache);
0438 Vector3 B(0.0, 0.0, 0.0);
0439 if (res.ok()) {
0440 B = *res;
0441 }
0442 grid.atLocalBins(index) = Grid_t::value_type(B[0], B[1], B[2]);
0443 }
0444 }
0445 }
0446 }
0447
0448 InterpolatedBFieldMap<Grid_t> map(
0449 {transformPos, transformBField, std::move(grid)});
0450 return map;
0451 }
0452
0453 }