LDMX Software
BFieldXYZUtils.h
1#pragma once
2
3#include <fstream>
4#include <functional>
5#include <iostream>
6
7#include "Acts/Definitions/Algebra.hpp"
8#include "Acts/MagneticField/BFieldMapUtils.hpp"
9#include "Acts/MagneticField/InterpolatedBFieldMap.hpp"
10#include "Acts/MagneticField/MagneticFieldContext.hpp"
11#include "Acts/Utilities/AxisFwd.hpp"
12#include "Acts/Utilities/Grid.hpp"
13#include "Acts/Utilities/Interpolation.hpp"
14#include "Acts/Utilities/Result.hpp"
15#include "Framework/Exception/Exception.h"
16
17static const double DIPOLE_OFFSET = 400.; // 400 mm
18
19using InterpolatedMagneticField3 = Acts::InterpolatedBFieldMap<
20 Acts::Grid<Acts::Vector3, Acts::Axis<Acts::AxisType::Equidistant>,
21 Acts::Axis<Acts::AxisType::Equidistant>,
22 Acts::Axis<Acts::AxisType::Equidistant>>>;
23
24using GenericTransformPos = std::function<Acts::Vector3(const Acts::Vector3&)>;
25using GenericTransformBField =
26 std::function<Acts::Vector3(const Acts::Vector3&, const Acts::Vector3&)>;
27
36Acts::Vector3 defaultTransformPos(const Acts::Vector3& pos_);
37
44Acts::Vector3 defaultTransformBField(const Acts::Vector3& field,
45 const Acts::Vector3& /*pos_*/);
46
47void testField(const std::shared_ptr<Acts::MagneticFieldProvider> bfield,
48 const Acts::Vector3& eval_pos,
49 const Acts::MagneticFieldContext& bctx);
50
56size_t localToGlobalBinXyz(std::array<size_t, 3> bins,
57 std::array<size_t, 3> sizes);
58
59inline InterpolatedMagneticField3 rotateFieldMapXYZ(
60 const std::function<size_t(std::array<size_t, 3> binsXYZ,
61 std::array<size_t, 3> nBinsXYZ)>&
62 localToGlobalBin,
63 std::vector<double> xPos, std::vector<double> yPos,
64 std::vector<double> zPos, std::vector<Acts::Vector3> bField,
65 double lengthUnit, double BFieldUnit, bool firstOctant,
66 GenericTransformPos transformPosition,
67 GenericTransformBField transformMagneticField) {
68 // [1] Create Grid
69 // Sort the values
70 std::sort(xPos.begin(), xPos.end());
71 std::sort(yPos.begin(), yPos.end());
72 std::sort(zPos.begin(), zPos.end());
73
74 // Get unique values
75 xPos.erase(std::unique(xPos.begin(), xPos.end()), xPos.end());
76 yPos.erase(std::unique(yPos.begin(), yPos.end()), yPos.end());
77 zPos.erase(std::unique(zPos.begin(), zPos.end()), zPos.end());
78 xPos.shrink_to_fit();
79 yPos.shrink_to_fit();
80 zPos.shrink_to_fit();
81
82 // get the number of bins
83 size_t n_bins_x = xPos.size();
84 size_t n_bins_y = yPos.size();
85 size_t n_bins_z = zPos.size();
86
87 // get the minimum and maximum
88 auto min_max_x = std::minmax_element(xPos.begin(), xPos.end());
89 auto min_max_y = std::minmax_element(yPos.begin(), yPos.end());
90 auto min_max_z = std::minmax_element(zPos.begin(), zPos.end());
91 // Create the axis for the grid
92 // get minima
93 double x_min = *min_max_x.first;
94 double y_min = *min_max_y.first;
95 double z_min = *min_max_z.first;
96 // get maxima
97 double x_max = *min_max_x.second;
98 double y_max = *min_max_y.second;
99 double z_max = *min_max_z.second;
100 // calculate maxima (add one last bin, because bin value always corresponds to
101 // left boundary)
102 double step_z = std::fabs(z_max - z_min) / (n_bins_z - 1);
103 double step_y = std::fabs(y_max - y_min) / (n_bins_y - 1);
104 double step_x = std::fabs(x_max - x_min) / (n_bins_x - 1);
105 x_max += step_x;
106 y_max += step_y;
107 z_max += step_z;
108
109 // If only the first octant is given
110 if (firstOctant) {
111 x_min = -*min_max_x.second;
112 y_min = -*min_max_y.second;
113 z_min = -*min_max_z.second;
114 n_bins_x = 2 * n_bins_x - 1;
115 n_bins_y = 2 * n_bins_y - 1;
116 n_bins_z = 2 * n_bins_z - 1;
117 }
118 Acts::Axis<Acts::AxisType::Equidistant> x_axis(x_min * lengthUnit,
119 x_max * lengthUnit, n_bins_x);
120 Acts::Axis<Acts::AxisType::Equidistant> y_axis(y_min * lengthUnit,
121 y_max * lengthUnit, n_bins_y);
122 Acts::Axis<Acts::AxisType::Equidistant> z_axis(z_min * lengthUnit,
123 z_max * lengthUnit, n_bins_z);
124 // Create the grid
125 using Grid_t =
126 Acts::Grid<Acts::Vector3, Acts::Axis<Acts::AxisType::Equidistant>,
127 Acts::Axis<Acts::AxisType::Equidistant>,
128 Acts::Axis<Acts::AxisType::Equidistant>>;
129 Grid_t grid(
130 std::make_tuple(std::move(x_axis), std::move(y_axis), std::move(z_axis)));
131
132 // [2] Set the bField values
133 for (size_t i = 1; i <= n_bins_x; ++i) {
134 for (size_t j = 1; j <= n_bins_y; ++j) {
135 for (size_t k = 1; k <= n_bins_z; ++k) {
136 Grid_t::index_t indices = {{i, j, k}};
137 std::array<size_t, 3> n_indices = {
138 {xPos.size(), yPos.size(), zPos.size()}};
139 if (firstOctant) {
140 // std::vectors begin with 0 and we do not want the user needing to
141 // take underflow or overflow bins in account this is why we need to
142 // subtract by one
143 size_t m = std::abs(int(i) - (int(xPos.size())));
144 size_t n = std::abs(int(j) - (int(yPos.size())));
145 size_t l = std::abs(int(k) - (int(zPos.size())));
146 Grid_t::index_t indices_first_octant = {{m, n, l}};
147
148 grid.atLocalBins(indices) =
149 bField.at(localToGlobalBin(indices_first_octant, n_indices)) *
150 BFieldUnit;
151
152 } else {
153 // std::vectors begin with 0 and we do not want the user needing to
154 // take underflow or overflow bins in account this is why we need to
155 // subtract by one
156 grid.atLocalBins(indices) =
157 bField.at(localToGlobalBin({{i - 1, j - 1, k - 1}}, n_indices)) *
158 BFieldUnit;
159 }
160 }
161 }
162 }
163 grid.setExteriorBins(Acts::Vector3::Zero());
164
165 // [3] Create the transformation for the position
166 // map (z,x,y) -> (x,y,z)
167
168 /*
169 auto transformPos = [](const Acts::Vector3& pos_, float offset=400.) {
170
171 Acts::Vector3 rot_pos;
172 rot_pos(0)=pos_(1);
173 rot_pos(1)=pos_(2);
174 rot_pos(2)=pos_(0) + offset;
175
176 return rot_pos;
177 };
178
179 */
180
181 // [4] Create the transformation for the bfield
182 // map (Bx,By,Bz) -> (Bx,By,Bz)
183
184 // auto transformBField = [](const Acts::Vector3& field,
185 // const Acts::Vector3& /*pos_*/) {
186 //
187 //
188 // Acts::Vector3 rot_field;
189 //
190 // rot_field(0) = field(2);
191 // rot_field(1) = field(0);
192 // rot_field(2) = field(1);
193
194 // return rot_field;
195 //};
196
197 // [5] Create the mapper and BField Service
198 // with the transformations passed from main producer
199 return Acts::InterpolatedBFieldMap<Grid_t>(
200 {transformPosition, transformMagneticField, std::move(grid)});
201}
202
203// This is a copy of
204// https://github.com/acts-project/acts/blob/main/Examples/Detectors/MagneticField/src/FieldMapTextIo.cpp
205// with additional rotateAxes flag to rotate the axes and field to be in the
206// tracking (ACTS) Frame
207
208inline InterpolatedMagneticField3 makeMagneticFieldMapXyzFromText(
209 std::function<size_t(std::array<size_t, 3> binsXYZ,
210 std::array<size_t, 3> nBinsXYZ)>
211 localToGlobalBin,
212 GenericTransformPos transformPosition,
213 GenericTransformBField transformMagneticField,
214 const std::string& fieldMapFile, Acts::ActsScalar lengthUnit,
215 Acts::ActsScalar BFieldUnit, bool firstOctant, bool rotateAxes) {
217 // Grid position points in x, y and z
218 std::vector<double> x_pos;
219 std::vector<double> y_pos;
220 std::vector<double> z_pos;
221 // components of magnetic field on grid points
222 std::vector<Acts::Vector3> b_field;
223
224 constexpr size_t k_default_size = 1 << 15;
225 // reserve estimated size
226 x_pos.reserve(k_default_size);
227 y_pos.reserve(k_default_size);
228 z_pos.reserve(k_default_size);
229 b_field.reserve(k_default_size);
230 // [1] Read in file and fill values
231 std::ifstream map_file(fieldMapFile.c_str(), std::ios::in);
232 if (!map_file.is_open()) {
233 EXCEPTION_RAISE("BadConf", "BFieldXYZUtils: cannot open field map file '" +
234 fieldMapFile + "'");
235 }
236 std::string line;
237 double pos_x = 0., pos_y = 0., pos_z = 0.;
238 double bx = 0., by = 0., bz = 0.;
239
240 bool header_found = false;
241
242 while (std::getline(map_file, line)) {
243 if (line.empty() || line[0] == '%' || line[0] == '#' || line[0] == ' ' ||
244 line.find_first_not_of(' ') == std::string::npos || !header_found) {
245 if (line.find("Header") != std::string::npos) header_found = true;
246 continue;
247 }
248 std::istringstream tmp(line);
249 tmp >> pos_x >> pos_y >> pos_z >> bx >> by >> bz;
250
251 x_pos.push_back(pos_x);
252 y_pos.push_back(pos_y);
253 z_pos.push_back(pos_z);
254 b_field.push_back(Acts::Vector3(bx, by, bz));
255 }
256 map_file.close();
257
258 if (!header_found) {
259 EXCEPTION_RAISE("BadConf",
260 "BFieldXYZUtils: no 'Header' line found in field map "
261 "file '" +
262 fieldMapFile + "'");
263 }
264 if (b_field.empty()) {
265 EXCEPTION_RAISE("BadConf", "BFieldXYZUtils: no field data read from '" +
266 fieldMapFile + "'");
267 }
268
269 x_pos.shrink_to_fit();
270 y_pos.shrink_to_fit();
271 z_pos.shrink_to_fit();
272 b_field.shrink_to_fit();
273
274 if (rotateAxes) {
275 return rotateFieldMapXYZ(localToGlobalBin, x_pos, y_pos, z_pos, b_field,
276 lengthUnit, BFieldUnit, firstOctant,
277 transformPosition, transformMagneticField);
278 } else
279 return Acts::fieldMapXYZ(localToGlobalBin, x_pos, y_pos, z_pos, b_field,
280 lengthUnit, BFieldUnit, firstOctant);
281}
282
283inline InterpolatedMagneticField3 loadDefaultBField(
284 const std::string& fieldMapFile, GenericTransformPos transformPosition,
285 GenericTransformBField transformMagneticField) {
286 // std::function<Acts::Vector3(const Acts::Vector3&, float)>
287 // transformPosition, std::function<Acts::Vector3(const Acts::Vector3&,const
288 // Acts::Vector3&)> transformMagneticField
289
290 return makeMagneticFieldMapXyzFromText(
291 std::move(localToGlobalBinXyz), transformPosition, transformMagneticField,
292 fieldMapFile,
293 1. * Acts::UnitConstants::mm, // default scale for axes length
294 1000. * Acts::UnitConstants::T, // The map is in kT, so scale it to T
295 false, // not symmetrical
296 true // rotate the axes to tracking frame
297 );
298}
299
300// R =
301//
302// 0 0 1
303// 1 0 0
304// 0 1 0