OPAL (Object Oriented Parallel Accelerator Library) 2022.1
OPAL
SectorMagneticFieldMap.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012, Chris Rogers
3 * All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * 1. Redistributions of source code must retain the above copyright notice,
7 * this list of conditions and the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright notice,
9 * this list of conditions and the following disclaimer in the documentation
10 * and/or other materials provided with the distribution.
11 * 3. Neither the name of STFC nor the names of its contributors may be used to
12 * endorse or promote products derived from this software without specific
13 * prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28
30// Grid on which field values are stored
33// Linear interpolation routines
36// Higher order interpolation routines
39
41
42#include <algorithm>
43#include <cmath>
44#include <fstream>
45#include <iostream>
46#include <string>
47#include <vector>
48
49using namespace interpolation;
50
51extern Inform *gmsg;
52
53// allow a fairly generous phi tolerance - we don't care about phi much and
54// calculation can be flaky due to ascii truncation of double and conversions
55// from polar to Cartesian
57std::map<std::string, SectorMagneticFieldMap*> SectorMagneticFieldMap::_fields;
58
60 std::string symmetry,
61 double length_units,
62 double field_units,
63 int polynomial_order,
64 int smoothing_order)
65 : SectorField(file_name), interpolator_m(nullptr), symmetry_m(dipole),
66 units_m(6, 1.), filename_m(file_name), phiOffset_m(0.),
67 poly_order_m(polynomial_order), smoothing_order_m(smoothing_order) {
68 units_m[0] *= length_units;
69 units_m[1] *= length_units;
70 units_m[2] *= length_units;
71 units_m[3] *= field_units;
72 units_m[4] *= field_units;
73 units_m[5] *= field_units;
75 if (_fields.find(file_name) == _fields.end()) {
76 readMap();
77 _fields[file_name] = new SectorMagneticFieldMap(*this);
78 } else {
79 SectorMagneticFieldMap* tgt = _fields[file_name];
80 if (symmetry_m != tgt->symmetry_m ||
81 units_m != tgt->units_m ||
82 filename_m != tgt->filename_m) {
83 throw(LogicalError(
84 "SectorMagneticFieldMap::SectorMagneticFieldMap",
85 "Attempt to construct different SectorFieldMaps with same "+
86 std::string("file but different settings")
87 ));
88 }
90 }
91 ThreeDGrid* grid = dynamic_cast<ThreeDGrid*>(interpolator_m->getMesh());
92 phiOffset_m = -grid->minZ();
93}
94
96 (const SectorMagneticFieldMap& field)
97 : SectorField(field), interpolator_m(nullptr), symmetry_m(field.symmetry_m),
98 units_m(field.units_m),
99 filename_m(field.filename_m), phiOffset_m(field.phiOffset_m) {
100 VectorMap* interpolator = nullptr;
101 if (field.interpolator_m != nullptr) {
102 interpolator = field.interpolator_m;
103 }
104 setInterpolator(interpolator);
105}
106
108 // delete interpolator_m; We reuse the interpolators... hack! should use smart pointer
109}
110
112 return interpolator_m;
113}
114
116 if (interpolator_m != nullptr) {
117 delete interpolator_m;
118 }
119 interpolator_m = interpolator;
120 if (interpolator_m != nullptr) {
122 throw(LogicalError(
123 "SectorMagneticFieldMap::setInterpolator",
124 "Attempt to load interpolator with PointDimension != 3")
125 );
127 throw(LogicalError(
128 "SectorMagneticFieldMap::setInterpolator",
129 "Attempt to load interpolator with ValueDimension != 3"
130 ));
131 ThreeDGrid* grid = dynamic_cast<ThreeDGrid*>(interpolator_m->getMesh());
132 if (grid == nullptr)
133 throw(LogicalError(
134 "SectorMagneticFieldMap::setInterpolator",
135 "Attempt to load interpolator with grid not ThreeDGrid"
136 ));
138 (grid->minX(), grid->minY()-grid->maxY(), grid->minZ(),
139 grid->maxX(), grid->maxY(), grid->maxZ(),
140 0., 0., 0.);
141 }
142 getInfo(gmsg);
143}
144
147}
148
151}
152
154 VectorMap* interpolator = IO::readMap
156 setInterpolator(interpolator);
157
158}
159
161 setInterpolator(nullptr);
162}
163
165 (std::string sym) {
166 if (sym == "None") {
167 return none;
168 }
169 if (sym == "Dipole") {
170 return dipole;
171 }
172 throw(LogicalError(
173 "SectorMagneticFieldMap::StringToSymmetry",
174 "Didn't recognise symmetry type "+sym
175 ));
176}
177
180 if (sym == none) {
181 return "None";
182 }
183 if (sym == dipole) {
184 return "Dipole";
185 }
186 throw(LogicalError(
187 "SectorMagneticFieldMap::SymmetryToString",
188 "Didn't recognise symmetry type " + std::to_string(sym)
189 ));
190}
191
192/*
193bool SectorMagneticFieldMap::getFieldstrengthPolar
194 (const Vector_t &R_p, Vector_t &, Vector_t &B_p) const {
195 // vector_t::operator[i] const returns by value, not by const reference
196 // so we need to make an array here
197 double R_temp[3] = {R_p[0], R_p[1], R_p[2]};
198 R_temp[2] += phiOffset_m;
199 // bounding box
200 if (!isInBoundingBox(R_temp))
201 return true;
202 interpolator_m->function(R_temp, &B_p[0]);
203 SectorField::convertToPolar(R_temp, &(B_p[0]));
204 return false;
205}
206*/
207
209 const Vector_t &R_c, Vector_t &/*E_c*/, Vector_t &B_c) const {
210 // coordinate transform; field is in the x-z plane but OPAL-CYCL assumes
211 // x-y plane; rotate to the start of the bend and into polar coordinates;
212 // apply mirror symmetry about the midplane
213 double radius = (getPolarBoundingBoxMin()[0]+getPolarBoundingBoxMax()[0])/2;
214 double midplane = (getPolarBoundingBoxMin()[1]+getPolarBoundingBoxMax()[1])/2;
215 double R_temp[3] = {R_c(0)+radius, R_c(1), R_c(2)};
216 double B_temp[3] = {0., 0., 0.};
218 bool mirror = R_temp[1] < midplane;
219 if (mirror) {
220 R_temp[1] = midplane + (midplane - R_temp[1]);
221 }
222 // interpolator has phi in 0. to dphi
223 R_temp[2] -= phiOffset_m;
224 // bool symmetryWasApplied = applySymmetry(R_temp);
225 if (!isInBoundingBox(R_temp)) {
226 return true;
227 }
228 interpolator_m->function(R_temp, B_temp);
229 // and here we transform back
230 // we want phi in 0. to dphi
231 if (mirror) {
232 B_temp[0] *= -1; // reflect Bx
233 B_temp[2] *= -1; // reflect Bz
234 }
235 B_c(0) = B_temp[0]*cos(phiOffset_m)-B_temp[2]*sin(phiOffset_m); // x
236 B_c(1) = B_temp[1]; // axial
237 B_c(2) = B_temp[0]*sin(phiOffset_m)+B_temp[2]*cos(phiOffset_m); // z
238 return false;
239}
240
241bool SectorMagneticFieldMap::applySymmetry(double* R_temp) const {
242 double ymin = SectorField::getPolarBoundingBoxMin()[1];
243 if (symmetry_m == dipole && R_temp[1] <= ymin) {
244 R_temp[1] = 2*ymin-R_temp[1];
245 return true;
246 }
247 return false;
248}
249
252 _fields.begin(); it != _fields.end(); ++it) {
253 delete (*it).second;
254 }
255 _fields = std::map<std::string, SectorMagneticFieldMap*>();
256}
257
259 std::vector<double> bbmin = SectorField::getPolarBoundingBoxMin();
260 std::vector<double> bbmax = SectorField::getPolarBoundingBoxMax();
261 (*msg) << Filename_m << " (3D Sector Magnetostatic);\n"
262 << " zini= " << bbmin[1] << " mm; zfinal= " << bbmax[1] << " mm;\n"
263 << " phiini= " << bbmin[2] << " rad; phifinal= " << bbmax[2] << " rad;\n"
264 << " rini= " << bbmin[0] << " mm; rfinal= " << bbmax[0] << " mm;"
265 << endl;
266}
267
268void SectorMagneticFieldMap::print(std::ostream& out) {
269 std::vector<double> bbmin = SectorField::getPolarBoundingBoxMin();
270 std::vector<double> bbmax = SectorField::getPolarBoundingBoxMax();
271 out << Filename_m << " (3D Sector Magnetostatic);\n"
272 << " zini= " << bbmin[1] << " m; zfinal= " << bbmax[1] << " mm;\n"
273 << " phiini= " << bbmin[2] << " rad; phifinal= " << bbmax[2] << " rad;\n"
274 << " rini= " << bbmin[0] << " m; rfinal= " << bbmax[0] << " mm;\n"
275 << std::endl;
276}
277
279 Vector_t &/*B*/, const DiffDirection &/*dir*/) const {
280 throw(LogicalError("SectorMagneticFieldMap::getFieldDerivative",
281 "Field map derivatives not implemented"));
282}
283
285
286
288 ThreeDGrid* grid = reinterpret_cast<ThreeDGrid*>(interpolator_m->getMesh());
289 return grid->maxZ() - grid->minZ();
290}
291
293const int SectorMagneticFieldMap::IO::sortOrder_m[3] = {0, 1, 2};
294
296 std::string file_name,
297 std::vector<double> units,
299 int polynomial_order,
300 int smoothing_order) {
301 try {
302 *gmsg <<"* Opening sector field map " << file_name
303 << " fit order " << polynomial_order
304 << " smoothing order " << smoothing_order << endl;
305 // get raw data
306 std::vector< std::vector<double> > field_points = readLines
307 (file_name, units);
308 // build grid
309 ThreeDGrid* grid = generateGrid(field_points, sym);
310 // build interpolator (convert grid to useful units)
311 if (polynomial_order == 1 && smoothing_order == 1) {
312 VectorMap* interpolator = getInterpolator(field_points, grid, sym);
313 return interpolator;
314 } else {
315 VectorMap* interpolator = getInterpolatorPolyPatch(
316 field_points,
317 grid,
318 sym,
319 polynomial_order,
320 smoothing_order);
321 return interpolator;
322 }
323 } catch(std::exception& exc) {
324 throw(LogicalError(
325 "SectorMagneticFieldMap::IO::ReadMap",
326 "Failed to read file "+file_name+" with "+(&exc)->what()
327 ));
328 }
329 return nullptr;
330}
331
333 std::vector< std::vector<double> > field_points,
334 ThreeDGrid* grid,
336 int polynomial_order,
337 int smoothing_order) {
338 // too lazy to write code to handle this case - not available to user anyway
340 throw(LogicalError(
341 "SectorMagneticFieldMap::IO::getInterpolatorPolyPatch",
342 "Failed to recognise symmetry type"
343 ));
344 }
345 std::vector< std::vector<double> > data(field_points.size(),
346 std::vector<double>(3));
347 for (size_t i = 0; i < field_points.size(); ++i) {
348 data[i][0] = field_points[i][3];
349 data[i][1] = field_points[i][4];
350 data[i][2] = field_points[i][5];
351 }
352 // symmetry is dipole
353 try {
354 *gmsg << "Calculating polynomials..." << endl;
355 PPSolveFactory solver(grid, data, polynomial_order, smoothing_order);
356 PolynomialPatch* patch = solver.solve();
357 *gmsg << " ... done" << endl;
358 return patch;
359 } catch (GeneralClassicException& exc) {
360 throw;
361 }
362
363}
364
365const std::string SectorMagneticFieldMap::IO::errMsg1 =
366 std::string("field points during read ")+
367 std::string("operation; this could mean that the field ")+
368 std::string("map was not complete, or that OPAL could ")+
369 std::string("not calculate regular grid spacings properly. ")+
370 std::string("Check that the field map is on a cylindrical ")+
371 std::string("grid written in Cartesian coordinates and the ")+
372 std::string("grid points are written with enough precision (")+
373 std::string("check the grid size information above).");
374
376 (const std::vector< std::vector<double> > field_points,
377 ThreeDGrid* grid,
379 // build field arrays
380 double *** Bx, *** By, *** Bz;
381 int index = 0;
382 Bx = new double**[grid->xSize()];
383 By = new double**[grid->xSize()];
384 Bz = new double**[grid->xSize()];
385 for (int i = 0; i < grid->xSize(); ++i) {
386 Bx[i] = new double*[grid->ySize()];
387 By[i] = new double*[grid->ySize()];
388 Bz[i] = new double*[grid->ySize()];
389 for (int j = 0; j < grid->ySize(); ++j) {
390 Bx[i][j] = new double[grid->zSize()];
391 By[i][j] = new double[grid->zSize()];
392 Bz[i][j] = new double[grid->zSize()];
393 for (int k = 0; k < grid->zSize(); ++k) {
394 if (index >= int(field_points.size())) {
395 throw(
397 "SectorMagneticFieldMap::IO::getInterpolator",
398 "Ran out of "+errMsg1)
399 );
400 }
401 Bx[i][j][k] = field_points[index][3];
402 By[i][j][k] = field_points[index][4];
403 Bz[i][j][k] = field_points[index][5];
404 ++index;
405 }
406 }
407 }
408 if (index != int(field_points.size())) {
409 throw(LogicalError(
410 "SectorMagneticFieldMap::IO::getInterpolator",
411 "Too many "+errMsg1
412 ));
413 }
414 Interpolator3dGridTo3d* interpolator = new Interpolator3dGridTo3d(grid, Bx, By, Bz);
415 VectorMap* interpolatorMap = dynamic_cast<VectorMap*>(interpolator);
416 return interpolatorMap;
417}
418
419bool SectorMagneticFieldMap::IO::comparator(std::vector<double> field_item1,
420 std::vector<double> field_item2) {
421 const int* order = sortOrder_m;
422 if (std::abs(field_item1[order[0]] - field_item2[order[0]]) > floatTolerance_m) {
423 return field_item1[order[0]] < field_item2[order[0]];
424 }
425 if (std::abs(field_item1[order[1]] - field_item2[order[1]]) > floatTolerance_m) {
426 return field_item1[order[1]] < field_item2[order[1]];
427 }
428 return field_item1[order[2]] < field_item2[order[2]];
429}
430
431std::vector< std::vector<double> > SectorMagneticFieldMap::IO::readLines
432 (std::string file_name, std::vector<double> units) {
433 std::vector< std::vector<double> > field_points;
434 std::string line;
435 if (units.size() != 6) {
436 throw(LogicalError(
437 "SectorMagneticFieldMap::IO::ReadLines",
438 "Units should be of length 6"
439 ));
440 }
441
442 std::ifstream fin(file_name.c_str());
443 if (!fin || !fin.is_open()) {
444 throw(LogicalError(
445 "SectorMagneticFieldMap::IO::ReadLines",
446 "Failed to open file "+file_name
447 ));
448 }
449 // skip header lines
450 *gmsg << "* Opened "+file_name << endl;
451 for (size_t i = 0; i < 8; ++i) {
452 std::getline(fin, line);
453 }
454 // read in field map
455 int line_number = 0;
456 while (fin) {
457 std::vector<double> field(6);
458 fin >> field[0] >> field[1] >> field[2] >> field[3] >> field[4]
459 >> field[5];
460 if (fin) {
461 for (size_t i = 0; i < 6; ++i) {
462 field[i] *= units[i];
463 }
464 field_points.push_back(field);
465 line_number++;
466 }
467 }
468 *gmsg << "* Read " << line_number << " lines" << endl;
469
470 // convert coordinates to polar; nb we leave field as cartesian
471 for (size_t i = 0; i < field_points.size(); ++i) {
472 SectorField::convertToPolar(&field_points[i][0]);
473 }
474
475 // force check sort order
476 std::sort(field_points.begin(), field_points.end(),
478 return field_points;
479}
480
482 in2 = in2*(1+floatTolerance_m)+floatTolerance_m;
483 return in1 > in2;
484}
485
487 (const std::vector< std::vector<double> > field_points,
489 std::vector<double> r_grid(1, field_points[0][0]);
490 std::vector<double> y_grid(1, field_points[0][1]);
491 std::vector<double> phi_grid(1, field_points[0][2]);
492 for (size_t i = 0; i < field_points.size(); ++i) {
493 if (floatGreaterEqual(field_points[i][0], r_grid.back())) {
494 r_grid.push_back(field_points[i][0]);
495 }
496 if (floatGreaterEqual(field_points[i][1], y_grid.back())) {
497 y_grid.push_back(field_points[i][1]);
498 }
499 if (floatGreaterEqual(field_points[i][2], phi_grid.back())) {
500 phi_grid.push_back(field_points[i][2]);
501 }
502 }
503
504 // reflect about y if symmetry is dipole
505 *gmsg << "* Grid size (r, y, phi) = ("
506 << r_grid.size() << ", " << y_grid.size() << ", " << phi_grid.size()
507 << ")" << endl;
508 *gmsg << "* Grid min (r [mm], y [mm], phi [rad]) = ("
509 << r_grid[0] << ", " << y_grid[0] << ", " << phi_grid[0]
510 << ")" << endl;
511 *gmsg << "* Grid max (r [mm], y [mm], phi [rad]) = ("
512 << r_grid.back() << ", " << y_grid.back() << ", " << phi_grid.back()
513 << ")" << endl;
514
515 ThreeDGrid* grid = new ThreeDGrid(r_grid, y_grid, phi_grid);
516 grid->setConstantSpacing(true);
517 return grid;
518}
Inform * gmsg
Definition: Main.cpp:61
DiffDirection
Definition: Fieldmap.h:54
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
const std::string name
constexpr double e
The value of.
Definition: Physics.h:39
std::string::iterator iterator
Definition: MSLang.h:16
Patches together many SquarePolynomialVectors to make a multidimensional polynomial spline.
PPSolveFactory solves the system of linear equations to interpolate from a grid of points using highe...
void setConstantSpacing(bool spacing)
Definition: ThreeDGrid.h:468
virtual unsigned int getValueDimension() const =0
virtual Mesh * getMesh() const
Definition: VectorMap.h:91
virtual unsigned int getPointDimension() const =0
virtual void function(const double *point, double *value) const =0
virtual std::vector< double > getPolarBoundingBoxMin() const
bool isInBoundingBox(const double R_p[]) const
Definition: SectorField.h:224
std::string Filename_m
Definition: SectorField.h:217
virtual std::vector< double > getPolarBoundingBoxMax() const
static void convertToPolar(double *position)
Definition: SectorField.cpp:54
void setPolarBoundingBox(double bbMinR, double bbMinY, double bbMinPhi, double bbMaxR, double bbMaxY, double bbMaxPhi, double bbTolR, double bbTolY, double bbTolPhi)
Definition: SectorField.cpp:87
handles field map grids with sector geometry
bool getFieldstrength(const Vector_t &R_c, Vector_t &E_c, Vector_t &B_c) const
bool applySymmetry(double *R_temp) const
interpolation::VectorMap * interpolator_m
std::vector< double > units_m
void setInterpolator(interpolation::VectorMap *interpolator)
static std::string SymmetryToString(symmetry sym)
SectorMagneticFieldMap(std::string file_name, std::string symmetry, double length_units, double field_units, int polynomial_order, int smoothing_order)
bool getFieldDerivative(const Vector_t &R, Vector_t &E, Vector_t &B, const DiffDirection &dir) const
static symmetry StringToSymmetry(std::string name)
void setSymmetry(std::string name)
interpolation::VectorMap * getInterpolator()
static const double fractionalBBPhiTolerance_m
void print(std::ostream &out)
static std::map< std::string, SectorMagneticFieldMap * > _fields
static const std::string errMsg1
static interpolation::ThreeDGrid * generateGrid(const std::vector< std::vector< double > > field_points, SectorMagneticFieldMap::symmetry sym)
static std::vector< std::vector< double > > readLines(std::string file_name, std::vector< double > units)
static bool comparator(std::vector< double > field_item1, std::vector< double > field_item2)
static const double floatTolerance_m
static interpolation::VectorMap * getInterpolatorPolyPatch(const std::vector< std::vector< double > > field_points, interpolation::ThreeDGrid *grid, SectorMagneticFieldMap::symmetry sym, int poly_order, int smoothing_order)
static interpolation::VectorMap * getInterpolator(const std::vector< std::vector< double > > field_points, interpolation::ThreeDGrid *grid, SectorMagneticFieldMap::symmetry sym)
static bool floatGreaterEqual(double in1, double in2)
static interpolation::VectorMap * readMap(std::string file_name, std::vector< double > units, SectorMagneticFieldMap::symmetry sym, int poly_order, int smoothing_order)
Logical error exception.
Definition: LogicalError.h:33
Definition: Inform.h:42