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
16static const double DIPOLE_OFFSET = 400.; // 400 mm
17
18using InterpolatedMagneticField3 = Acts::InterpolatedBFieldMap<
19 Acts::Grid<Acts::Vector3, Acts::Axis<Acts::AxisType::Equidistant>,
20 Acts::Axis<Acts::AxisType::Equidistant>,
21 Acts::Axis<Acts::AxisType::Equidistant>>>;
22
23using GenericTransformPos = std::function<Acts::Vector3(const Acts::Vector3&)>;
24using GenericTransformBField =
25 std::function<Acts::Vector3(const Acts::Vector3&, const Acts::Vector3&)>;
26
34Acts::Vector3 default_transformPos(const Acts::Vector3& pos);
35
42Acts::Vector3 default_transformBField(const Acts::Vector3& field,
43 const Acts::Vector3& /*pos*/);
44
45void testField(const std::shared_ptr<Acts::MagneticFieldProvider> bfield,
46 const Acts::Vector3& eval_pos,
47 const Acts::MagneticFieldContext& bctx);
48
54size_t localToGlobalBin_xyz(std::array<size_t, 3> bins,
55 std::array<size_t, 3> sizes);
56
57inline InterpolatedMagneticField3 rotateFieldMapXYZ(
58 const std::function<size_t(std::array<size_t, 3> binsXYZ,
59 std::array<size_t, 3> nBinsXYZ)>&
60 localToGlobalBin,
61 std::vector<double> xPos, std::vector<double> yPos,
62 std::vector<double> zPos, std::vector<Acts::Vector3> bField,
63 double lengthUnit, double BFieldUnit, bool firstOctant,
64 GenericTransformPos transformPosition,
65 GenericTransformBField transformMagneticField) {
66 // [1] Create Grid
67 // Sort the values
68 std::sort(xPos.begin(), xPos.end());
69 std::sort(yPos.begin(), yPos.end());
70 std::sort(zPos.begin(), zPos.end());
71
72 // Get unique values
73 xPos.erase(std::unique(xPos.begin(), xPos.end()), xPos.end());
74 yPos.erase(std::unique(yPos.begin(), yPos.end()), yPos.end());
75 zPos.erase(std::unique(zPos.begin(), zPos.end()), zPos.end());
76 xPos.shrink_to_fit();
77 yPos.shrink_to_fit();
78 zPos.shrink_to_fit();
79
80 // get the number of bins
81 size_t nBinsX = xPos.size();
82 size_t nBinsY = yPos.size();
83 size_t nBinsZ = zPos.size();
84
85 // get the minimum and maximum
86 auto minMaxX = std::minmax_element(xPos.begin(), xPos.end());
87 auto minMaxY = std::minmax_element(yPos.begin(), yPos.end());
88 auto minMaxZ = std::minmax_element(zPos.begin(), zPos.end());
89 // Create the axis for the grid
90 // get minima
91 double xMin = *minMaxX.first;
92 double yMin = *minMaxY.first;
93 double zMin = *minMaxZ.first;
94 // get maxima
95 double xMax = *minMaxX.second;
96 double yMax = *minMaxY.second;
97 double zMax = *minMaxZ.second;
98 // calculate maxima (add one last bin, because bin value always corresponds to
99 // left boundary)
100 double stepZ = std::fabs(zMax - zMin) / (nBinsZ - 1);
101 double stepY = std::fabs(yMax - yMin) / (nBinsY - 1);
102 double stepX = std::fabs(xMax - xMin) / (nBinsX - 1);
103 xMax += stepX;
104 yMax += stepY;
105 zMax += stepZ;
106
107 // If only the first octant is given
108 if (firstOctant) {
109 xMin = -*minMaxX.second;
110 yMin = -*minMaxY.second;
111 zMin = -*minMaxZ.second;
112 nBinsX = 2 * nBinsX - 1;
113 nBinsY = 2 * nBinsY - 1;
114 nBinsZ = 2 * nBinsZ - 1;
115 }
116 Acts::Axis<Acts::AxisType::Equidistant> xAxis(xMin * lengthUnit,
117 xMax * lengthUnit, nBinsX);
118 Acts::Axis<Acts::AxisType::Equidistant> yAxis(yMin * lengthUnit,
119 yMax * lengthUnit, nBinsY);
120 Acts::Axis<Acts::AxisType::Equidistant> zAxis(zMin * lengthUnit,
121 zMax * lengthUnit, nBinsZ);
122 // Create the grid
123 using Grid_t =
124 Acts::Grid<Acts::Vector3, Acts::Axis<Acts::AxisType::Equidistant>,
125 Acts::Axis<Acts::AxisType::Equidistant>,
126 Acts::Axis<Acts::AxisType::Equidistant>>;
127 Grid_t grid(
128 std::make_tuple(std::move(xAxis), std::move(yAxis), std::move(zAxis)));
129
130 // [2] Set the bField values
131 for (size_t i = 1; i <= nBinsX; ++i) {
132 for (size_t j = 1; j <= nBinsY; ++j) {
133 for (size_t k = 1; k <= nBinsZ; ++k) {
134 Grid_t::index_t indices = {{i, j, k}};
135 std::array<size_t, 3> nIndices = {
136 {xPos.size(), yPos.size(), zPos.size()}};
137 if (firstOctant) {
138 // std::vectors begin with 0 and we do not want the user needing to
139 // take underflow or overflow bins in account this is why we need to
140 // subtract by one
141 size_t m = std::abs(int(i) - (int(xPos.size())));
142 size_t n = std::abs(int(j) - (int(yPos.size())));
143 size_t l = std::abs(int(k) - (int(zPos.size())));
144 Grid_t::index_t indicesFirstOctant = {{m, n, l}};
145
146 grid.atLocalBins(indices) =
147 bField.at(localToGlobalBin(indicesFirstOctant, nIndices)) *
148 BFieldUnit;
149
150 } else {
151 // std::vectors begin with 0 and we do not want the user needing to
152 // take underflow or overflow bins in account this is why we need to
153 // subtract by one
154 grid.atLocalBins(indices) =
155 bField.at(localToGlobalBin({{i - 1, j - 1, k - 1}}, nIndices)) *
156 BFieldUnit;
157 }
158 }
159 }
160 }
161 grid.setExteriorBins(Acts::Vector3::Zero());
162
163 // [3] Create the transformation for the position
164 // map (z,x,y) -> (x,y,z)
165
166 /*
167 auto transformPos = [](const Acts::Vector3& pos, float offset=400.) {
168
169 Acts::Vector3 rot_pos;
170 rot_pos(0)=pos(1);
171 rot_pos(1)=pos(2);
172 rot_pos(2)=pos(0) + offset;
173
174 return rot_pos;
175 };
176
177 */
178
179 // [4] Create the transformation for the bfield
180 // map (Bx,By,Bz) -> (Bx,By,Bz)
181
182 // auto transformBField = [](const Acts::Vector3& field,
183 // const Acts::Vector3& /*pos*/) {
184 //
185 //
186 // Acts::Vector3 rot_field;
187 //
188 // rot_field(0) = field(2);
189 // rot_field(1) = field(0);
190 // rot_field(2) = field(1);
191
192 // return rot_field;
193 //};
194
195 // [5] Create the mapper and BField Service
196 // with the transformations passed from main producer
197 return Acts::InterpolatedBFieldMap<Grid_t>(
198 {transformPosition, transformMagneticField, std::move(grid)});
199}
200
201// This is a copy of
202// https://github.com/acts-project/acts/blob/main/Examples/Detectors/MagneticField/src/FieldMapTextIo.cpp
203// with additional rotateAxes flag to rotate the axes and field to be in the
204// tracking (ACTS) Frame
205
206inline InterpolatedMagneticField3 makeMagneticFieldMapXyzFromText(
207 std::function<size_t(std::array<size_t, 3> binsXYZ,
208 std::array<size_t, 3> nBinsXYZ)>
209 localToGlobalBin,
210 GenericTransformPos transformPosition,
211 GenericTransformBField transformMagneticField,
212 const std::string& fieldMapFile, Acts::ActsScalar lengthUnit,
213 Acts::ActsScalar BFieldUnit, bool firstOctant, bool rotateAxes) {
215 // Grid position points in x, y and z
216 std::vector<double> xPos;
217 std::vector<double> yPos;
218 std::vector<double> zPos;
219 // components of magnetic field on grid points
220 std::vector<Acts::Vector3> bField;
221
222 constexpr size_t kDefaultSize = 1 << 15;
223 // reserve estimated size
224 xPos.reserve(kDefaultSize);
225 yPos.reserve(kDefaultSize);
226 zPos.reserve(kDefaultSize);
227 bField.reserve(kDefaultSize);
228 // [1] Read in file and fill values
229 std::ifstream map_file(fieldMapFile.c_str(), std::ios::in);
230 std::string line;
231 double x = 0., y = 0., z = 0.;
232 double bx = 0., by = 0., bz = 0.;
233
234 bool headerFound = false;
235
236 while (std::getline(map_file, line)) {
237 if (line.empty() || line[0] == '%' || line[0] == '#' || line[0] == ' ' ||
238 line.find_first_not_of(' ') == std::string::npos || !headerFound) {
239 if (line.find("Header") != std::string::npos) headerFound = true;
240 continue;
241 }
242 std::istringstream tmp(line);
243 tmp >> x >> y >> z >> bx >> by >> bz;
244
245 xPos.push_back(x);
246 yPos.push_back(y);
247 zPos.push_back(z);
248 bField.push_back(Acts::Vector3(bx, by, bz));
249 }
250 map_file.close();
251
252 if (!headerFound) {
253 std::cout << "MAP LOADING ERROR:: line containing the word 'Header' not "
254 "found in the BMap."
255 << std::endl;
256 }
257
258 xPos.shrink_to_fit();
259 yPos.shrink_to_fit();
260 zPos.shrink_to_fit();
261 bField.shrink_to_fit();
262
263 if (rotateAxes) {
264 return rotateFieldMapXYZ(localToGlobalBin, xPos, yPos, zPos, bField,
265 lengthUnit, BFieldUnit, firstOctant,
266 transformPosition, transformMagneticField);
267 } else
268 return Acts::fieldMapXYZ(localToGlobalBin, xPos, yPos, zPos, bField,
269 lengthUnit, BFieldUnit, firstOctant);
270}
271
272inline InterpolatedMagneticField3 loadDefaultBField(
273 const std::string& fieldMapFile, GenericTransformPos transformPosition,
274 GenericTransformBField transformMagneticField) {
275 // std::function<Acts::Vector3(const Acts::Vector3&, float)>
276 // transformPosition, std::function<Acts::Vector3(const Acts::Vector3&,const
277 // Acts::Vector3&)> transformMagneticField
278
279 return makeMagneticFieldMapXyzFromText(
280 std::move(localToGlobalBin_xyz), transformPosition,
281 transformMagneticField, fieldMapFile,
282 1. * Acts::UnitConstants::mm, // default scale for axes length
283 1000. * Acts::UnitConstants::T, // The map is in kT, so scale it to T
284 false, // not symmetrical
285 true // rotate the axes to tracking frame
286 );
287}
288
289// R =
290//
291// 0 0 1
292// 1 0 0
293// 0 1 0