File indexing completed on 2026-03-28 07:45:39
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include "Acts/MagneticField/ToroidField.hpp"
0016
0017 #include "Acts/Definitions/Units.hpp"
0018
0019 #include <cmath>
0020 #include <numbers>
0021 #include <stdexcept>
0022
0023 namespace Acts {
0024
0025
0026
0027
0028
0029 ToroidField::ToroidField() : ToroidField(Config{}) {}
0030
0031 ToroidField::ToroidField(Config cfg) : m_cfg(std::move(cfg)) {
0032
0033 const int nC = m_cfg.layout.nCoils;
0034 if (m_cfg.barrelSigns.empty()) {
0035 m_cfg.barrelSigns.assign(nC, +1);
0036 }
0037 if (m_cfg.ectSigns.empty()) {
0038 m_cfg.ectSigns.assign(2 * nC, +1);
0039 }
0040
0041 if (static_cast<int>(m_cfg.barrelSigns.size()) != nC) {
0042 throw std::invalid_argument(
0043 "ToroidField: barrelSigns size must equal nCoils");
0044 }
0045 if (static_cast<int>(m_cfg.ectSigns.size()) != 2 * nC) {
0046 throw std::invalid_argument(
0047 "ToroidField: ectSigns size must equal 2*nCoils");
0048 }
0049
0050 buildGeometry();
0051 }
0052
0053
0054
0055
0056
0057 MagneticFieldProvider::Cache ToroidField::makeCache(
0058 const MagneticFieldContext& mctx) const {
0059 static_cast<void>(mctx);
0060 return MagneticFieldProvider::Cache(std::in_place_type<Cache>);
0061 }
0062
0063 Result<Vector3> ToroidField::getField(
0064 const Vector3& position, MagneticFieldProvider::Cache& cache) const {
0065 (void)cache;
0066
0067 const double X = position.x();
0068 const double Y = position.y();
0069 const double Z = position.z();
0070
0071 double bx = 0.0;
0072 double by = 0.0;
0073 double bz = 0.0;
0074
0075 constexpr double mu0 = 4e-7 * std::numbers::pi;
0076 const double prefBarrel =
0077 (mu0 * static_cast<double>(m_cfg.barrel.Nturns) * m_cfg.barrel.I) /
0078 (4.0 * std::numbers::pi);
0079 const double prefECT =
0080 (mu0 * static_cast<double>(m_cfg.ect.Nturns) * m_cfg.ect.I) /
0081 (4.0 * std::numbers::pi);
0082 const double eps = m_cfg.layout.eps;
0083
0084
0085 accumulateBarrelField(X, Y, Z, eps, prefBarrel, bx, by, bz);
0086 accumulateEndcapField(X, Y, Z, eps, prefECT, bx, by, bz);
0087
0088 return Result<Vector3>::success(Vector3(bx, by, bz));
0089 }
0090
0091
0092
0093
0094
0095 void ToroidField::accumulateBarrelField(double X, double Y, double Z,
0096 double eps, double pref, double& bx,
0097 double& by, double& bz) const {
0098 for (std::size_t i = 0; i < m_mids_barrel.size(); ++i) {
0099 const auto& mid = m_mids_barrel[i];
0100 const auto& dl = m_segs_barrel[i];
0101
0102 const double rx = X - mid[0];
0103 const double ry = Y - mid[1];
0104 const double rz = Z - mid[2];
0105
0106 const double r2 = rx * rx + ry * ry + rz * rz + eps;
0107 const double invr = 1.0 / std::sqrt(r2);
0108 const double invr3 = invr / r2;
0109
0110 const double cx = dl[1] * rz - dl[2] * ry;
0111 const double cy = dl[2] * rx - dl[0] * rz;
0112 const double cz = dl[0] * ry - dl[1] * rx;
0113
0114 bx += pref * cx * invr3;
0115 by += pref * cy * invr3;
0116 bz += pref * cz * invr3;
0117 }
0118 }
0119
0120 void ToroidField::accumulateEndcapField(double X, double Y, double Z,
0121 double eps, double pref, double& bx,
0122 double& by, double& bz) const {
0123 for (std::size_t i = 0; i < m_mids_ect.size(); ++i) {
0124 const auto& mid = m_mids_ect[i];
0125 const auto& dl = m_segs_ect[i];
0126
0127 const double rx = X - mid[0];
0128 const double ry = Y - mid[1];
0129 const double rz = Z - mid[2];
0130
0131 const double r2 = rx * rx + ry * ry + rz * rz + eps;
0132 const double invr = 1.0 / std::sqrt(r2);
0133 const double invr3 = invr / r2;
0134
0135 const double cx = dl[1] * rz - dl[2] * ry;
0136 const double cy = dl[2] * rx - dl[0] * rz;
0137 const double cz = dl[0] * ry - dl[1] * rx;
0138
0139 bx += pref * cx * invr3;
0140 by += pref * cy * invr3;
0141 bz += pref * cz * invr3;
0142 }
0143 }
0144
0145
0146
0147 std::vector<std::array<float, 2>> ToroidField::ectRacetrackRadial(
0148 float Lrho, float Lz, int nArc, int nStraight, bool close) {
0149 const float rr = 0.5f * Lrho;
0150 const float rz = 0.5f * Lz;
0151 std::vector<std::array<float, 2>> pts;
0152 pts.reserve(
0153 static_cast<std::size_t>(2 * nArc + 2 * nStraight + (close ? 1 : 0)));
0154
0155
0156 for (int i = 0; i < nStraight; ++i) {
0157 const float t = static_cast<float>(i) / static_cast<float>(nStraight);
0158 const float z = (+rz) * (1.0f - t) + (-rz) * t;
0159 pts.push_back({+rr, z});
0160 }
0161
0162 for (int i = 0; i < nArc; ++i) {
0163 const float th = static_cast<float>(std::numbers::pi / 2) +
0164 (static_cast<float>(i) / static_cast<float>(nArc)) *
0165 static_cast<float>(-std::numbers::pi);
0166 const float rho = rr * std::sin(th);
0167 const float z = -rz;
0168 pts.push_back({rho, z});
0169 }
0170
0171 for (int i = 0; i < nStraight; ++i) {
0172 const float t = static_cast<float>(i) / static_cast<float>(nStraight);
0173 const float z = (-rz) * (1.0f - t) + (+rz) * t;
0174 pts.push_back({-rr, z});
0175 }
0176
0177 for (int i = 0; i < nArc; ++i) {
0178 const float th = -static_cast<float>(std::numbers::pi) / 2 +
0179 (static_cast<float>(i) / static_cast<float>(nArc)) *
0180 static_cast<float>(std::numbers::pi);
0181 const float rho = rr * std::sin(th);
0182 const float z = +rz;
0183 pts.push_back({rho, z});
0184 }
0185 if (close &&
0186 (pts.front()[0] != pts.back()[0] || pts.front()[1] != pts.back()[1])) {
0187 pts.push_back(pts.front());
0188 }
0189 return pts;
0190 }
0191
0192
0193 std::vector<std::array<double, 2>> ToroidField::racetrackRZ(double a, double b,
0194 double Lz, int nArc,
0195 int nStraight,
0196 bool close) {
0197 const double r = 0.5 * b;
0198 const double rz = 0.5 * Lz;
0199 const double zTop = rz - r;
0200 const double zBot = -rz + r;
0201
0202 std::vector<std::array<double, 2>> pts;
0203 pts.reserve(
0204 static_cast<std::size_t>(2 * nArc + 2 * nStraight + (close ? 1 : 0)));
0205
0206
0207 for (int i = 0; i < nStraight; ++i) {
0208 const double t = static_cast<double>(i) / static_cast<double>(nStraight);
0209 const double z = zTop * (1.0 - t) + zBot * t;
0210 pts.push_back({+0.5 * a, z});
0211 }
0212
0213 for (int i = 0; i < nArc; ++i) {
0214 const double t = static_cast<double>(i) / static_cast<double>(nArc);
0215 const double th = -std::numbers::pi / 2 - t * (std::numbers::pi / 2);
0216 const double cx = +0.5 * a - r;
0217 const double cz = -rz + r;
0218 const double rho = cx + r * std::cos(th);
0219 const double z = cz + r * std::sin(th);
0220 pts.push_back({rho, z});
0221 }
0222
0223 for (int i = 0; i < nStraight; ++i) {
0224 const double t = static_cast<double>(i) / static_cast<double>(nStraight);
0225 const double z = zBot * (1.0 - t) + zTop * t;
0226 pts.push_back({-0.5 * a, z});
0227 }
0228
0229 for (int i = 0; i < nArc; ++i) {
0230 const double t = static_cast<double>(i) / static_cast<double>(nArc);
0231 const double th = +std::numbers::pi / 2 - t * (std::numbers::pi / 2);
0232 const double cx = -0.5 * a + r;
0233 const double cz = +rz - r;
0234 const double rho = cx + r * std::cos(th);
0235 const double z = cz + r * std::sin(th);
0236 pts.push_back({rho, z});
0237 }
0238 if (close &&
0239 (pts.front()[0] != pts.back()[0] || pts.front()[1] != pts.back()[1])) {
0240 pts.push_back(pts.front());
0241 }
0242 return pts;
0243 }
0244
0245 void ToroidField::buildSegsMidsRZ(const std::vector<std::array<double, 2>>& rz,
0246 std::vector<std::array<double, 2>>& d_rz,
0247 std::vector<std::array<double, 2>>& m_rz) {
0248 d_rz.clear();
0249 m_rz.clear();
0250 d_rz.reserve(rz.size() - 1);
0251 m_rz.reserve(rz.size() - 1);
0252 for (std::size_t i = 0; i + 1 < rz.size(); ++i) {
0253 const double dr = rz[i + 1][0] - rz[i][0];
0254 const double dz = rz[i + 1][1] - rz[i][1];
0255 d_rz.push_back({dr, dz});
0256 m_rz.push_back(
0257 {0.5f * (rz[i + 1][0] + rz[i][0]), 0.5 * (rz[i + 1][1] + rz[i][1])});
0258 }
0259 }
0260
0261 void ToroidField::mapRingToXYZ(double l,
0262 const std::vector<std::array<double, 2>>& m_rz,
0263 const std::vector<std::array<double, 2>>& d_rz,
0264 double phi, int sign, double zShift,
0265 std::vector<std::array<double, 3>>& mids_out,
0266 std::vector<std::array<double, 3>>& segs_out) {
0267 const double ct = std::cos(phi);
0268 const double st = std::sin(phi);
0269 const double s = (sign >= 0) ? 1.0 : -1.0;
0270
0271 for (const auto& rm : m_rz) {
0272 const double rho = rm[0];
0273 const double zz = rm[1] + zShift;
0274 const double rxy = l + rho;
0275 mids_out.push_back({rxy * ct, rxy * st, zz});
0276 }
0277 for (const auto& dlrz : d_rz) {
0278 const double dr = dlrz[0];
0279 const double dz = dlrz[1];
0280 segs_out.push_back({s * (dr * ct), s * (dr * st), s * dz});
0281 }
0282 }
0283
0284 void ToroidField::buildGeometry() {
0285
0286 const double rEndB = 0.5 * m_cfg.barrel.b;
0287 const double lB = 0.5 * (m_cfg.barrel.R_in + m_cfg.barrel.R_out);
0288 const double aB = (m_cfg.barrel.R_out - m_cfg.barrel.R_in) - 2.0 * rEndB;
0289
0290 const auto rz_barrel =
0291 racetrackRZ(aB, m_cfg.barrel.b, m_cfg.barrel.c, m_cfg.layout.nArc,
0292 m_cfg.layout.nStraight, m_cfg.layout.closeLoop);
0293 std::vector<std::array<double, 2>> d_rzB;
0294 std::vector<std::array<double, 2>> m_rzB;
0295 buildSegsMidsRZ(rz_barrel, d_rzB, m_rzB);
0296
0297
0298 const double lE = 0.5 * (m_cfg.ect.R_in + m_cfg.ect.R_out) / UnitConstants::m;
0299 const double rEndECT = 0.5 * m_cfg.ect.b / UnitConstants::m;
0300 const double aE =
0301 (m_cfg.ect.R_out - m_cfg.ect.R_in) / UnitConstants::m - 2.0 * rEndECT;
0302 const auto rz_ect = racetrackRZ(
0303 aE, m_cfg.ect.b / UnitConstants::m,
0304 m_cfg.ect.c / UnitConstants::m, m_cfg.layout.nArc,
0305 m_cfg.layout.nStraight, m_cfg.layout.closeLoop);
0306 std::vector<std::array<double, 2>> d_rzE;
0307 std::vector<std::array<double, 2>> m_rzE;
0308 buildSegsMidsRZ(rz_ect, d_rzE, m_rzE);
0309
0310
0311 const int nC = m_cfg.layout.nCoils;
0312 const double th0 = static_cast<float>(m_cfg.layout.theta0);
0313 const double dth = static_cast<float>(m_cfg.layout.thetaStep);
0314
0315
0316 m_mids_barrel.clear();
0317 m_segs_barrel.clear();
0318 m_mids_ect.clear();
0319 m_segs_ect.clear();
0320
0321 m_mids_barrel.reserve(static_cast<std::size_t>(nC) * m_rzB.size());
0322 m_segs_barrel.reserve(static_cast<std::size_t>(nC) * d_rzB.size());
0323 m_mids_ect.reserve(static_cast<std::size_t>(2 * nC) * m_rzE.size());
0324 m_segs_ect.reserve(static_cast<std::size_t>(2 * nC) * d_rzE.size());
0325
0326
0327 for (int k = 0; k < nC; ++k) {
0328 const double phi = th0 + static_cast<double>(k) * dth;
0329 const int sign = m_cfg.barrelSigns[k];
0330 mapRingToXYZ(lB, m_rzB, d_rzB, phi, sign, 0.0, m_mids_barrel,
0331 m_segs_barrel);
0332 }
0333
0334
0335 const float zECT =
0336 0.5f * static_cast<float>(m_cfg.barrel.c / UnitConstants::m) -
0337 0.5f * static_cast<float>(m_cfg.ect.c / UnitConstants::m) +
0338 2.0f * static_cast<float>(m_cfg.ect.gap / UnitConstants::m);
0339
0340
0341 for (int k = 0; k < nC; ++k) {
0342 const double phi = th0 + static_cast<double>(k) * dth;
0343 const int sign = m_cfg.ectSigns[k];
0344 mapRingToXYZ(lE, m_rzE, d_rzE, phi, sign, +zECT, m_mids_ect,
0345 m_segs_ect);
0346 }
0347
0348 for (int k = 0; k < nC; ++k) {
0349 const double phi = th0 + static_cast<double>(k) * dth;
0350 const int sign = m_cfg.ectSigns[nC + k];
0351 mapRingToXYZ(lE, m_rzE, d_rzE, phi, sign, -zECT, m_mids_ect,
0352 m_segs_ect);
0353 }
0354 }
0355
0356 }