2 Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
5 #include "CxxUtils/vec.h"
8 BFieldMesh<T>::BFieldMesh(double zmin,
18 m_min = { zmin, rmin, phimin };
19 m_max = { zmax, rmax, phimax };
24 BFieldMesh<T>::setRange(double zmin,
31 m_min = { zmin, rmin, phimin };
32 m_max = { zmax, rmax, phimax };
38 BFieldMesh<T>::setBscale(double bscale)
40 m_scale = m_nomScale = bscale;
43 // scale bscale by a factor
46 BFieldMesh<T>::scaleBscale(double factor)
48 m_scale = factor * m_nomScale;
52 // Reserve space in the vectors to avoid unnecessary memory re-allocations.
56 BFieldMesh<T>::reserve(int nz, int nr, int nphi, int nfield)
58 m_mesh[0].reserve(nz);
59 m_mesh[1].reserve(nr);
60 m_mesh[2].reserve(nphi);
61 m_field.reserve(nfield);
66 BFieldMesh<T>::reserve(int nz, int nr, int nphi)
68 reserve(nz, nr, nphi, nz * nr * nphi);
71 // add elements to vectors
74 BFieldMesh<T>::appendMesh(int axis, double value)
76 m_mesh[axis].push_back(value);
81 BFieldMesh<T>::appendField(const BFieldVector<T>& field)
83 m_field.push_back(field);
87 // Construct the look-up table to accelerate bin-finding.
91 BFieldMesh<T>::buildLUT()
93 for (int j = 0; j < 3; ++j) { // z, r, phi
94 // align the m_mesh edges to m_min/m_max
95 m_mesh[j].front() = m_min[j];
96 m_mesh[j].back() = m_max[j];
97 // determine the unit size, q, to be used in the LUTs
98 const double width = m_mesh[j].back() - m_mesh[j].front();
100 for (unsigned i = 0; i < m_mesh[j].size() - 1; ++i) {
101 q = std::min(q, m_mesh[j][i + 1] - m_mesh[j][i]);
103 // find the number of units in the LUT
104 int n = int(width / q) + 1;
105 q = width / (n + 0.5);
106 m_invUnit[j] = 1.0 / q; // new unit size
108 int m = 0; // mesh number
109 for (int i = 0; i < n; ++i) { // LUT index
110 if (i * q + m_mesh[j].front() > m_mesh[j][m + 1]) {
113 m_LUT[j].push_back(m);
116 m_roff = m_mesh[2].size(); // index offset for incrementing r by 1
117 m_zoff = m_roff * m_mesh[1].size(); // index offset for incrementing z by 1
122 BFieldMesh<T>::memSize() const
125 size += sizeof(double) * 10;
126 size += sizeof(int) * 2;
127 for (int i = 0; i < 3; ++i) {
128 size += sizeof(double) * m_mesh[i].capacity();
129 size += sizeof(int) * m_LUT[i].capacity();
131 size += sizeof(BFieldVector<T>) * m_field.capacity();
136 // Test if a point (z,r,phi) is inside this mesh region.
140 BFieldMesh<T>::inside(double z, double r, double phi) const
142 // assume phi is in [-pi,pi].
143 // phimax() is in [0,2pi], but phimin() may be < 0 if the range crosses phi =
144 // 0. we have to test twice to get all possible cases.
145 if (phi < phimin()) {
148 return (phi >= phimin() && phi <= phimax() && z >= zmin() && z <= zmax() &&
149 r >= rmin() && r <= rmax());
153 // Find and return the cache of the bin containing (z,r,phi)
157 BFieldMesh<T>::getCache(double z,
161 double scaleFactor) const
163 // make sure phi is inside this zone
164 if (phi < phimin()) {
167 // find the mesh, and relative location in the mesh
169 const std::vector<double>& mz(m_mesh[0]);
170 int iz = static_cast<int>((z - zmin()) * m_invUnit[0]); // index to LUT
171 iz = m_LUT[0][iz]; // tentative mesh index from LUT
172 if (z > mz[iz + 1]) {
176 const std::vector<double>& mr(m_mesh[1]);
177 int ir = static_cast<int>((r - rmin()) * m_invUnit[1]); // index to LUT
178 ir = m_LUT[1][ir]; // tentative mesh index from LUT
179 if (r > mr[ir + 1]) {
183 const std::vector<double>& mphi(m_mesh[2]);
184 int iphi = static_cast<int>((phi - phimin()) * m_invUnit[2]); // index to LUT
185 iphi = m_LUT[2][iphi]; // tentative mesh index from LUT
186 if (phi > mphi[iphi + 1]) {
189 // store the bin edges
191 mz[iz], mz[iz + 1], mr[ir], mr[ir + 1], mphi[iphi], mphi[iphi + 1]);
192 // store the B field at the 8 corners of the bin in the cache
193 const int im0 = iz * m_zoff + ir * m_roff + iphi; // index of the first corner
194 const double sf = scaleFactor;
196 cache.setBscale(m_scale);
197 // In the usual case the m_field is of type short int
198 // else (special case) can be double
199 if constexpr (std::is_same<T, short>::value) {
200 CxxUtils::vec<int, 8> field1I = { (m_field[im0][0]),
201 (m_field[im0 + m_roff][0]),
202 (m_field[im0 + m_zoff][0]),
203 (m_field[im0 + m_zoff + m_roff][0]),
204 (m_field[im0 + 1][0]),
205 (m_field[im0 + m_roff + 1][0]),
206 (m_field[im0 + m_zoff + 1][0]),
207 (m_field[im0 + m_zoff + m_roff + 1][0]) };
209 CxxUtils::vec<double, 8> field1;
210 CxxUtils::vconvert(field1, field1I);
212 CxxUtils::vec<int, 8> field2I = { (m_field[im0][1]),
213 (m_field[im0 + m_roff][1]),
214 (m_field[im0 + m_zoff][1]),
215 (m_field[im0 + m_zoff + m_roff][1]),
216 (m_field[im0 + 1][1]),
217 (m_field[im0 + m_roff + 1][1]),
218 (m_field[im0 + m_zoff + 1][1]),
219 (m_field[im0 + m_zoff + m_roff + 1][1]) };
221 CxxUtils::vec<double, 8> field2;
222 CxxUtils::vconvert(field2, field2I);
224 CxxUtils::vec<int, 8> field3I = { (m_field[im0][2]),
225 (m_field[im0 + m_roff][2]),
226 (m_field[im0 + m_zoff][2]),
227 (m_field[im0 + m_zoff + m_roff][2]),
228 (m_field[im0 + 1][2]),
229 (m_field[im0 + m_roff + 1][2]),
230 (m_field[im0 + m_zoff + 1][2]),
231 (m_field[im0 + m_zoff + m_roff + 1][2]) };
233 CxxUtils::vec<double, 8> field3;
234 CxxUtils::vconvert(field3, field3I);
236 cache.setField(sf * field1, sf * field2, sf * field3);
238 CxxUtils::vec<double, 8> field1 = {
240 (m_field[im0 + m_roff][0]),
241 (m_field[im0 + m_zoff][0]),
242 (m_field[im0 + m_zoff + m_roff][0]),
243 (m_field[im0 + 1][0]),
244 (m_field[im0 + m_roff + 1][0]),
245 (m_field[im0 + m_zoff + 1][0]),
246 (m_field[im0 + m_zoff + m_roff + 1][0])
249 CxxUtils::vec<double, 8> field2 = {
251 (m_field[im0 + m_roff][1]),
252 (m_field[im0 + m_zoff][1]),
253 (m_field[im0 + m_zoff + m_roff][1]),
254 (m_field[im0 + 1][1]),
255 (m_field[im0 + m_roff + 1][1]),
256 (m_field[im0 + m_zoff + 1][1]),
257 (m_field[im0 + m_zoff + m_roff + 1][1])
260 CxxUtils::vec<double, 8> field3 = {
262 (m_field[im0 + m_roff][2]),
263 (m_field[im0 + m_zoff][2]),
264 (m_field[im0 + m_zoff + m_roff][2]),
265 (m_field[im0 + 1][2]),
266 (m_field[im0 + m_roff + 1][2]),
267 (m_field[im0 + m_zoff + 1][2]),
268 (m_field[im0 + m_zoff + m_roff + 1][2])
271 cache.setField(sf * field1, sf * field2, sf * field3);
276 // Compute the magnetic field (and the derivatives) without caching
280 BFieldMesh<T>::getB(const double* ATH_RESTRICT xyz,
281 double* ATH_RESTRICT B,
282 double* ATH_RESTRICT deriv) const
284 // cylindrical coordinates
285 const double x = xyz[0];
286 const double y = xyz[1];
287 const double z = xyz[2];
288 const double r = sqrt(x * x + y * y);
289 double phi = std::atan2(y, x);
290 if (phi < phimin()) {
293 // is it inside this map?
294 if (!inside(z, r, phi)) { // no
295 B[0] = B[1] = B[2] = 0.0;
297 for (int i = 0; i < 9; ++i) {
304 this->getCache(z, r, phi, cache, 1.0);
305 cache.getB(xyz, r, phi, B, deriv);
310 BFieldMesh<T>::min(size_t axis) const
317 BFieldMesh<T>::max(size_t axis) const
324 BFieldMesh<T>::zmin() const
331 BFieldMesh<T>::zmax() const
338 BFieldMesh<T>::rmin() const
345 BFieldMesh<T>::rmax() const
352 BFieldMesh<T>::phimin() const
359 BFieldMesh<T>::phimax() const
366 BFieldMesh<T>::nmesh(size_t axis) const
368 return m_mesh[axis].size();
373 BFieldMesh<T>::mesh(size_t axis, size_t index) const
375 return m_mesh[axis][index];
380 BFieldMesh<T>::nfield() const
382 return m_field.size();
386 const BFieldVector<T>&
387 BFieldMesh<T>::field(size_t index) const
389 return m_field[index];
394 BFieldMesh<T>::bscale() const