OPAL (Object Oriented Parallel Accelerator Library)  2021.1.99
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 
40 #include "Utilities/LogicalError.h"
41 
42 #include <algorithm>
43 #include <cmath>
44 #include <fstream>
45 #include <iostream>
46 #include <string>
47 #include <vector>
48 
49 using namespace interpolation;
50 
51 extern 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
57 std::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(NULL), 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(NULL), 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 = NULL;
101  if (field.interpolator_m != NULL) {
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 != NULL) {
117  delete interpolator_m;
118  }
119  interpolator_m = interpolator;
120  if (interpolator_m != NULL) {
121  if (interpolator_m->getPointDimension() != 3)
122  throw(LogicalError(
123  "SectorMagneticFieldMap::setInterpolator",
124  "Attempt to load interpolator with PointDimension != 3")
125  );
126  if (interpolator_m->getValueDimension() != 3)
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 == NULL)
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(NULL);
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 /*
193 bool 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 
241 bool 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 
268 void 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 
293 const 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 NULL;
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
339  if (sym != SectorMagneticFieldMap::dipole) {
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 
365 const 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(
396  LogicalError(
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 
419 bool 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 
431 std::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 
481 bool SectorMagneticFieldMap::IO::floatGreaterEqual(double in1, double in2) {
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 }
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
DiffDirection
Definition: Fieldmap.h:54
Inform * gmsg
Definition: Main.cpp:62
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 unsigned int getPointDimension() const =0
virtual Mesh * getMesh() const
Definition: VectorMap.h:91
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