ATLAS Offline Software
BFieldMesh.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 #include "CxxUtils/vec.h"
6 // constructor
7 template<class T>
8 BFieldMesh<T>::BFieldMesh(double zmin,
9  double zmax,
10  double rmin,
11  double rmax,
12  double phimin,
13  double phimax,
14  double bscale)
15  : m_scale(bscale)
16  , m_nomScale(bscale)
17 {
18  m_min = { zmin, rmin, phimin };
19  m_max = { zmax, rmax, phimax };
20 }
21 // set ranges
22 template<class T>
23 void
24 BFieldMesh<T>::setRange(double zmin,
25  double zmax,
26  double rmin,
27  double rmax,
28  double phimin,
29  double phimax)
30 {
31  m_min = { zmin, rmin, phimin };
32  m_max = { zmax, rmax, phimax };
33 }
34 
35 // set bscale
36 template<class T>
37 void
38 BFieldMesh<T>::setBscale(double bscale)
39 {
40  m_scale = m_nomScale = bscale;
41 }
42 
43 // scale bscale by a factor
44 template<class T>
45 void
46 BFieldMesh<T>::scaleBscale(double factor)
47 {
48  m_scale = factor * m_nomScale;
49 }
50 
51 //
52 // Reserve space in the vectors to avoid unnecessary memory re-allocations.
53 //
54 template<class T>
55 void
56 BFieldMesh<T>::reserve(int nz, int nr, int nphi, int nfield)
57 {
58  m_mesh[0].reserve(nz);
59  m_mesh[1].reserve(nr);
60  m_mesh[2].reserve(nphi);
61  m_field.reserve(nfield);
62 }
63 
64 template<class T>
65 void
66 BFieldMesh<T>::reserve(int nz, int nr, int nphi)
67 {
68  reserve(nz, nr, nphi, nz * nr * nphi);
69 }
70 
71 // add elements to vectors
72 template<class T>
73 void
74 BFieldMesh<T>::appendMesh(int axis, double value)
75 {
76  m_mesh[axis].push_back(value);
77 }
78 
79 template<class T>
80 void
81 BFieldMesh<T>::appendField(const BFieldVector<T>& field)
82 {
83  m_field.push_back(field);
84 }
85 
86 //
87 // Construct the look-up table to accelerate bin-finding.
88 //
89 template<class T>
90 void
91 BFieldMesh<T>::buildLUT()
92 {
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();
99  double q(width);
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]);
102  }
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
107  ++n;
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]) {
111  m++;
112  }
113  m_LUT[j].push_back(m);
114  }
115  }
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
118 }
119 
120 template<class T>
121 int
122 BFieldMesh<T>::memSize() const
123 {
124  int size = 0;
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();
130  }
131  size += sizeof(BFieldVector<T>) * m_field.capacity();
132  return size;
133 }
134 
135 //
136 // Test if a point (z,r,phi) is inside this mesh region.
137 //
138 template<class T>
139 bool
140 BFieldMesh<T>::inside(double z, double r, double phi) const
141 {
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()) {
146  phi += 2.0 * M_PI;
147  }
148  return (phi >= phimin() && phi <= phimax() && z >= zmin() && z <= zmax() &&
149  r >= rmin() && r <= rmax());
150 }
151 
152 //
153 // Find and return the cache of the bin containing (z,r,phi)
154 //
155 template<class T>
156 void
157 BFieldMesh<T>::getCache(double z,
158  double r,
159  double phi,
160  BFieldCache& cache,
161  double scaleFactor) const
162 {
163  // make sure phi is inside this zone
164  if (phi < phimin()) {
165  phi += 2.0 * M_PI;
166  }
167  // find the mesh, and relative location in the mesh
168  // z
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]) {
173  ++iz;
174  }
175  // r
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]) {
180  ++ir;
181  }
182  // phi
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]) {
187  ++iphi;
188  }
189  // store the bin edges
190  cache.setRange(
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;
195  // store the B scale
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]) };
208 
209  CxxUtils::vec<double, 8> field1;
210  CxxUtils::vconvert(field1, field1I);
211 
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]) };
220 
221  CxxUtils::vec<double, 8> field2;
222  CxxUtils::vconvert(field2, field2I);
223 
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]) };
232 
233  CxxUtils::vec<double, 8> field3;
234  CxxUtils::vconvert(field3, field3I);
235 
236  cache.setField(sf * field1, sf * field2, sf * field3);
237  } else {
238  CxxUtils::vec<double, 8> field1 = {
239  (m_field[im0][0]),
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])
247  };
248 
249  CxxUtils::vec<double, 8> field2 = {
250  (m_field[im0][1]),
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])
258  };
259 
260  CxxUtils::vec<double, 8> field3 = {
261  (m_field[im0][2]),
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])
269  };
270 
271  cache.setField(sf * field1, sf * field2, sf * field3);
272  }
273 }
274 
275 //
276 // Compute the magnetic field (and the derivatives) without caching
277 //
278 template<class T>
279 void
280 BFieldMesh<T>::getB(const double* ATH_RESTRICT xyz,
281  double* ATH_RESTRICT B,
282  double* ATH_RESTRICT deriv) const
283 {
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()) {
291  phi += 2.0 * M_PI;
292  }
293  // is it inside this map?
294  if (!inside(z, r, phi)) { // no
295  B[0] = B[1] = B[2] = 0.0;
296  if (deriv) {
297  for (int i = 0; i < 9; ++i) {
298  deriv[i] = 0.0;
299  }
300  }
301  return;
302  }
303  BFieldCache cache;
304  this->getCache(z, r, phi, cache, 1.0);
305  cache.getB(xyz, r, phi, B, deriv);
306 }
307 // accessors
308 template<class T>
309 double
310 BFieldMesh<T>::min(size_t axis) const
311 {
312  return m_min[axis];
313 }
314 
315 template<class T>
316 double
317 BFieldMesh<T>::max(size_t axis) const
318 {
319  return m_max[axis];
320 }
321 
322 template<class T>
323 double
324 BFieldMesh<T>::zmin() const
325 {
326  return m_min[0];
327 }
328 
329 template<class T>
330 double
331 BFieldMesh<T>::zmax() const
332 {
333  return m_max[0];
334 }
335 
336 template<class T>
337 double
338 BFieldMesh<T>::rmin() const
339 {
340  return m_min[1];
341 }
342 
343 template<class T>
344 double
345 BFieldMesh<T>::rmax() const
346 {
347  return m_max[1];
348 }
349 
350 template<class T>
351 double
352 BFieldMesh<T>::phimin() const
353 {
354  return m_min[2];
355 }
356 
357 template<class T>
358 double
359 BFieldMesh<T>::phimax() const
360 {
361  return m_max[2];
362 }
363 
364 template<class T>
365 unsigned
366 BFieldMesh<T>::nmesh(size_t axis) const
367 {
368  return m_mesh[axis].size();
369 }
370 
371 template<class T>
372 double
373 BFieldMesh<T>::mesh(size_t axis, size_t index) const
374 {
375  return m_mesh[axis][index];
376 }
377 
378 template<class T>
379 unsigned
380 BFieldMesh<T>::nfield() const
381 {
382  return m_field.size();
383 }
384 
385 template<class T>
386 const BFieldVector<T>&
387 BFieldMesh<T>::field(size_t index) const
388 {
389  return m_field[index];
390 }
391 
392 template<class T>
393 double
394 BFieldMesh<T>::bscale() const
395 {
396  return m_scale;
397 }
398