OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
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 <math.h>
43 
44 #include <algorithm>
45 #include <vector>
46 #include <iostream>
47 #include <fstream>
48 #include <string>
49 
50 using namespace interpolation;
51 
52 extern Inform *gmsg;
53 
54 // allow a fairly generous phi tolerance - we don't care about phi much and
55 // calculation can be flaky due to ascii truncation of double and conversions
56 // from polar to Cartesian
58 std::map<std::string, SectorMagneticFieldMap*> SectorMagneticFieldMap::_fields;
59 
61  std::string symmetry,
62  double length_units,
63  double field_units,
64  int polynomial_order,
65  int smoothing_order)
66  : SectorField(file_name), interpolator_m(NULL), symmetry_m(dipole),
67  units_m(6, 1.), filename_m(file_name), phiOffset_m(0.),
68  poly_order_m(polynomial_order), smoothing_order_m(smoothing_order) {
69  units_m[0] *= length_units;
70  units_m[1] *= length_units;
71  units_m[2] *= length_units;
72  units_m[3] *= field_units;
73  units_m[4] *= field_units;
74  units_m[5] *= field_units;
75  setSymmetry(symmetry);
76  if (_fields.find(file_name) == _fields.end()) {
77  readMap();
78  _fields[file_name] = new SectorMagneticFieldMap(*this);
79  } else {
80  SectorMagneticFieldMap* tgt = _fields[file_name];
81  if (symmetry_m != tgt->symmetry_m ||
82  units_m != tgt->units_m ||
83  filename_m != tgt->filename_m) {
84  throw(LogicalError(
85  "SectorMagneticFieldMap::SectorMagneticFieldMap",
86  "Attempt to construct different SectorFieldMaps with same "+
87  std::string("file but different settings")
88  ));
89  }
91  }
92  ThreeDGrid* grid = dynamic_cast<ThreeDGrid*>(interpolator_m->getMesh());
93  phiOffset_m = -grid->minZ();
94 }
95 
97  (const SectorMagneticFieldMap& field)
98  : SectorField(field), interpolator_m(NULL), symmetry_m(field.symmetry_m),
99  units_m(field.units_m),
100  filename_m(field.filename_m), phiOffset_m(field.phiOffset_m) {
101  VectorMap* interpolator = NULL;
102  if (field.interpolator_m != NULL) {
103  interpolator = field.interpolator_m;
104  }
105  setInterpolator(interpolator);
106 }
107 
109  // delete interpolator_m; We reuse the interpolators... hack! should use smart pointer
110 }
111 
113  return interpolator_m;
114 }
115 
117  if (interpolator_m != NULL) {
118  delete interpolator_m;
119  }
120  interpolator_m = interpolator;
121  if (interpolator_m != NULL) {
122  if (interpolator_m->getPointDimension() != 3)
123  throw(LogicalError(
124  "SectorMagneticFieldMap::setInterpolator",
125  "Attempt to load interpolator with PointDimension != 3")
126  );
127  if (interpolator_m->getValueDimension() != 3)
128  throw(LogicalError(
129  "SectorMagneticFieldMap::setInterpolator",
130  "Attempt to load interpolator with ValueDimension != 3"
131  ));
132  ThreeDGrid* grid = dynamic_cast<ThreeDGrid*>(interpolator_m->getMesh());
133  if (grid == NULL)
134  throw(LogicalError(
135  "SectorMagneticFieldMap::setInterpolator",
136  "Attempt to load interpolator with grid not ThreeDGrid"
137  ));
139  (grid->minX(), grid->minY()-grid->maxY(), grid->minZ(),
140  grid->maxX(), grid->maxY(), grid->maxZ(),
141  0., 0., 0.);
142  }
143  getInfo(gmsg);
144 }
145 
148 }
149 
152 }
153 
155  VectorMap* interpolator = IO::readMap
157  setInterpolator(interpolator);
158 
159 }
160 
162  setInterpolator(NULL);
163 }
164 
166  (std::string sym) {
167  if (sym == "None") {
168  return none;
169  }
170  if (sym == "Dipole") {
171  return dipole;
172  }
173  throw(LogicalError(
174  "SectorMagneticFieldMap::StringToSymmetry",
175  "Didn't recognise symmetry type "+sym
176  ));
177 }
178 
181  if (sym == none) {
182  return "None";
183  }
184  if (sym == dipole) {
185  return "Dipole";
186  }
187  throw(LogicalError(
188  "SectorMagneticFieldMap::SymmetryToString",
189  "Didn't recognise symmetry type " + std::to_string(sym)
190  ));
191 }
192 
194  (const Vector_t &R_p, Vector_t &E_p, 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 
208  (const Vector_t &R_c, Vector_t &E_c, Vector_t &B_c) const {
209  // coordinate transform; field is in the x-z plane but OPAL-CYCL assumes
210  // x-y plane; rotate to the start of the bend and into polar coordinates;
211  // apply mirror symmetry about the midplane
212  double radius = (getPolarBoundingBoxMin()[0]+getPolarBoundingBoxMax()[0])/2;
213  double midplane = (getPolarBoundingBoxMin()[1]+getPolarBoundingBoxMax()[1])/2;
214  double R_temp[3] = {R_c(0)+radius, R_c(1), R_c(2)};
215  double B_temp[3] = {0., 0., 0.};
217  bool mirror = R_temp[1] < midplane;
218  if (mirror) {
219  R_temp[1] = midplane + (midplane - R_temp[1]);
220  }
221  // interpolator has phi in 0. to dphi
222  R_temp[2] -= phiOffset_m;
223  // bool symmetryWasApplied = applySymmetry(R_temp);
224  if (!isInBoundingBox(R_temp)) {
225  return true;
226  }
227  interpolator_m->function(R_temp, B_temp);
228  // and here we transform back
229  // we want phi in 0. to dphi
230  if (mirror) {
231  B_temp[0] *= -1; // reflect Bx
232  B_temp[2] *= -1; // reflect Bz
233  }
234  B_c(0) = B_temp[0]*cos(phiOffset_m)-B_temp[2]*sin(phiOffset_m); // x
235  B_c(1) = B_temp[1]; // axial
236  B_c(2) = B_temp[0]*sin(phiOffset_m)+B_temp[2]*cos(phiOffset_m); // z
237  return false;
238 }
239 
240 bool SectorMagneticFieldMap::applySymmetry(double* R_temp) const {
241  double ymin = SectorField::getPolarBoundingBoxMin()[1];
242  if (symmetry_m == dipole && R_temp[1] <= ymin) {
243  R_temp[1] = 2*ymin-R_temp[1];
244  return true;
245  }
246  return false;
247 }
248 
251  _fields.begin(); it != _fields.end(); ++it) {
252  delete (*it).second;
253  }
254  _fields = std::map<std::string, SectorMagneticFieldMap*>();
255 }
256 
258  std::vector<double> bbmin = SectorField::getPolarBoundingBoxMin();
259  std::vector<double> bbmax = SectorField::getPolarBoundingBoxMax();
260  (*msg) << Filename_m << " (3D Sector Magnetostatic);\n"
261  << " zini= " << bbmin[1] << " mm; zfinal= " << bbmax[1] << " mm;\n"
262  << " phiini= " << bbmin[2] << " rad; phifinal= " << bbmax[2] << " rad;\n"
263  << " rini= " << bbmin[0] << " mm; rfinal= " << bbmax[0] << " mm;"
264  << endl;
265 }
266 
267 void SectorMagneticFieldMap::print(std::ostream& out) {
268  std::vector<double> bbmin = SectorField::getPolarBoundingBoxMin();
269  std::vector<double> bbmax = SectorField::getPolarBoundingBoxMax();
270  out << Filename_m << " (3D Sector Magnetostatic);\n"
271  << " zini= " << bbmin[1] << " m; zfinal= " << bbmax[1] << " mm;\n"
272  << " phiini= " << bbmin[2] << " rad; phifinal= " << bbmax[2] << " rad;\n"
273  << " rini= " << bbmin[0] << " m; rfinal= " << bbmax[0] << " mm;\n"
274  << std::endl;
275 }
276 
278  Vector_t &B, const DiffDirection &dir) const {
279  throw(LogicalError("SectorMagneticFieldMap::getFieldDerivative",
280  "Field map derivatives not implemented"));
281 }
282 
284 
285 
287  ThreeDGrid* grid = reinterpret_cast<ThreeDGrid*>(interpolator_m->getMesh());
288  return grid->maxZ() - grid->minZ();
289 }
290 
292 const int SectorMagneticFieldMap::IO::sortOrder_m[3] = {0, 1, 2};
293 
295  std::string file_name,
296  std::vector<double> units,
298  int polynomial_order,
299  int smoothing_order) {
300  try {
301  *gmsg <<"* Opening sector field map " << file_name
302  << " fit order " << polynomial_order
303  << " smoothing order " << smoothing_order << endl;
304  // get raw data
305  std::vector< std::vector<double> > field_points = readLines
306  (file_name, units);
307  // build grid
308  ThreeDGrid* grid = generateGrid(field_points, sym);
309  // build interpolator (convert grid to useful units)
310  if (polynomial_order == 1 && smoothing_order == 1) {
311  VectorMap* interpolator = getInterpolator(field_points, grid, sym);
312  return interpolator;
313  } else {
314  VectorMap* interpolator = getInterpolatorPolyPatch(
315  field_points,
316  grid,
317  sym,
318  polynomial_order,
319  smoothing_order);
320  return interpolator;
321  }
322  } catch(std::exception& exc) {
323  throw(LogicalError(
324  "SectorMagneticFieldMap::IO::ReadMap",
325  "Failed to read file "+file_name+" with "+(&exc)->what()
326  ));
327  }
328  return NULL;
329 }
330 
332  std::vector< std::vector<double> > field_points,
333  ThreeDGrid* grid,
335  int polynomial_order,
336  int smoothing_order) {
337  // too lazy to write code to handle this case - not available to user anyway
338  if (sym != SectorMagneticFieldMap::dipole) {
339  throw(LogicalError(
340  "SectorMagneticFieldMap::IO::getInterpolatorPolyPatch",
341  "Failed to recognise symmetry type"
342  ));
343  }
344  std::vector< std::vector<double> > data(field_points.size(),
345  std::vector<double>(3));
346  for (size_t i = 0; i < field_points.size(); ++i) {
347  data[i][0] = field_points[i][3];
348  data[i][1] = field_points[i][4];
349  data[i][2] = field_points[i][5];
350  }
351  // symmetry is dipole
352  try {
353  *gmsg << "Calculating polynomials..." << endl;
354  PPSolveFactory solver(grid, data, polynomial_order, smoothing_order);
355  PolynomialPatch* patch = solver.solve();
356  *gmsg << " ... done" << endl;
357  return patch;
358  } catch (GeneralClassicException& exc) {
359  throw;
360  }
361 
362 }
363 
364 const std::string SectorMagneticFieldMap::IO::errMsg1 =
365  std::string("field points during read ")+
366  std::string("operation; this could mean that the field ")+
367  std::string("map was not complete, or that OPAL could ")+
368  std::string("not calculate regular grid spacings properly. ")+
369  std::string("Check that the field map is on a cylindrical ")+
370  std::string("grid written in Cartesian coordinates and the ")+
371  std::string("grid points are written with enough precision (")+
372  std::string("check the grid size information above).");
373 
375  (const std::vector< std::vector<double> > field_points,
376  ThreeDGrid* grid,
378  // build field arrays
379  double *** Bx, *** By, *** Bz;
380  int index = 0;
381  Bx = new double**[grid->xSize()];
382  By = new double**[grid->xSize()];
383  Bz = new double**[grid->xSize()];
384  for (int i = 0; i < grid->xSize(); ++i) {
385  Bx[i] = new double*[grid->ySize()];
386  By[i] = new double*[grid->ySize()];
387  Bz[i] = new double*[grid->ySize()];
388  for (int j = 0; j < grid->ySize(); ++j) {
389  Bx[i][j] = new double[grid->zSize()];
390  By[i][j] = new double[grid->zSize()];
391  Bz[i][j] = new double[grid->zSize()];
392  for (int k = 0; k < grid->zSize(); ++k) {
393  if (index >= int(field_points.size())) {
394  throw(
395  LogicalError(
396  "SectorMagneticFieldMap::IO::getInterpolator",
397  "Ran out of "+errMsg1)
398  );
399  }
400  Bx[i][j][k] = field_points[index][3];
401  By[i][j][k] = field_points[index][4];
402  Bz[i][j][k] = field_points[index][5];
403  ++index;
404  }
405  }
406  }
407  if (index != int(field_points.size())) {
408  throw(LogicalError(
409  "SectorMagneticFieldMap::IO::getInterpolator",
410  "Too many "+errMsg1
411  ));
412  }
413  Interpolator3dGridTo3d* interpolator = new Interpolator3dGridTo3d(grid, Bx, By, Bz);
414  VectorMap* interpolatorMap = dynamic_cast<VectorMap*>(interpolator);
415  return interpolatorMap;
416 }
417 
418 bool SectorMagneticFieldMap::IO::comparator(std::vector<double> field_item1,
419  std::vector<double> field_item2) {
420  const int* order = sortOrder_m;
421  if (fabs(field_item1[order[0]] - field_item2[order[0]]) > floatTolerance_m) {
422  return field_item1[order[0]] < field_item2[order[0]];
423  }
424  if (fabs(field_item1[order[1]] - field_item2[order[1]]) > floatTolerance_m) {
425  return field_item1[order[1]] < field_item2[order[1]];
426  }
427  return field_item1[order[2]] < field_item2[order[2]];
428 }
429 
430 std::vector< std::vector<double> > SectorMagneticFieldMap::IO::readLines
431  (std::string file_name, std::vector<double> units) {
432  std::vector< std::vector<double> > field_points;
433  std::string line;
434  if (units.size() != 6) {
435  throw(LogicalError(
436  "SectorMagneticFieldMap::IO::ReadLines",
437  "Units should be of length 6"
438  ));
439  }
440 
441  std::ifstream fin(file_name.c_str());
442  if (!fin || !fin.is_open()) {
443  throw(LogicalError(
444  "SectorMagneticFieldMap::IO::ReadLines",
445  "Failed to open file "+file_name
446  ));
447  }
448  // skip header lines
449  *gmsg << "* Opened "+file_name << endl;
450  for (size_t i = 0; i < 8; ++i) {
451  std::getline(fin, line);
452  }
453  // read in field map
454  int line_number = 0;
455  while (fin) {
456  std::vector<double> field(6);
457  fin >> field[0] >> field[1] >> field[2] >> field[3] >> field[4]
458  >> field[5];
459  if (fin) {
460  for (size_t i = 0; i < 6; ++i) {
461  field[i] *= units[i];
462  }
463  field_points.push_back(field);
464  line_number++;
465  }
466  }
467  *gmsg << "* Read " << line_number << " lines" << endl;
468 
469  // convert coordinates to polar; nb we leave field as cartesian
470  for (size_t i = 0; i < field_points.size(); ++i) {
471  SectorField::convertToPolar(&field_points[i][0]);
472  }
473 
474  // force check sort order
475  std::sort(field_points.begin(), field_points.end(),
477  return field_points;
478 }
479 
480 bool SectorMagneticFieldMap::IO::floatGreaterEqual(double in1, double in2) {
481  in2 = in2*(1+floatTolerance_m)+floatTolerance_m;
482  return in1 > in2;
483 }
484 
486  (const std::vector< std::vector<double> > field_points,
488  std::vector<double> r_grid(1, field_points[0][0]);
489  std::vector<double> y_grid(1, field_points[0][1]);
490  std::vector<double> phi_grid(1, field_points[0][2]);
491  for (size_t i = 0; i < field_points.size(); ++i) {
492  if (floatGreaterEqual(field_points[i][0], r_grid.back())) {
493  r_grid.push_back(field_points[i][0]);
494  }
495  if (floatGreaterEqual(field_points[i][1], y_grid.back())) {
496  y_grid.push_back(field_points[i][1]);
497  }
498  if (floatGreaterEqual(field_points[i][2], phi_grid.back())) {
499  phi_grid.push_back(field_points[i][2]);
500  }
501  }
502 
503  // reflect about y if symmetry is dipole
504  *gmsg << "* Grid size (r, y, phi) = ("
505  << r_grid.size() << ", " << y_grid.size() << ", " << phi_grid.size()
506  << ")" << endl;
507  *gmsg << "* Grid min (r [mm], y [mm], phi [rad]) = ("
508  << r_grid[0] << ", " << y_grid[0] << ", " << phi_grid[0]
509  << ")" << endl;
510  *gmsg << "* Grid max (r [mm], y [mm], phi [rad]) = ("
511  << r_grid.back() << ", " << y_grid.back() << ", " << phi_grid.back()
512  << ")" << endl;
513 
514  ThreeDGrid* grid = new ThreeDGrid(r_grid, y_grid, phi_grid);
515  grid->setConstantSpacing(true);
516  return grid;
517 }
void setInterpolator(interpolation::VectorMap *interpolator)
static std::string SymmetryToString(symmetry sym)
double minY() const
Definition: ThreeDGrid.h:431
static const double fractionalBBPhiTolerance_m
constexpr double e
The value of .
Definition: Physics.h:40
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
static interpolation::VectorMap * readMap(std::string file_name, std::vector< double > units, SectorMagneticFieldMap::symmetry sym, int poly_order, int smoothing_order)
bool getFieldstrength(const Vector_t &R_c, Vector_t &E_c, Vector_t &B_c) const
DiffDirection
Definition: Fieldmap.h:54
interpolation::VectorMap * interpolator_m
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:815
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
Inform * gmsg
Definition: Main.cpp:21
static const std::string errMsg1
virtual unsigned int getPointDimension() const =0
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 bool comparator(std::vector< double > field_item1, std::vector< double > field_item2)
void print(std::ostream &out)
static interpolation::ThreeDGrid * generateGrid(const std::vector< std::vector< double > > field_points, SectorMagneticFieldMap::symmetry sym)
bool getFieldstrengthPolar(const Vector_t &R_p, Vector_t &E_p, Vector_t &B_p) const
std::string Filename_m
Definition: SectorField.h:217
bool applySymmetry(double *R_temp) const
double minX() const
Definition: ThreeDGrid.h:423
static const double floatTolerance_m
static std::vector< std::vector< double > > readLines(std::string file_name, std::vector< double > units)
double maxZ() const
Definition: ThreeDGrid.h:443
double maxY() const
Definition: ThreeDGrid.h:435
virtual Mesh * getMesh() const
Definition: VectorMap.h:91
double maxX() const
Definition: ThreeDGrid.h:427
virtual std::vector< double > getPolarBoundingBoxMax() const
interpolation::VectorMap * getInterpolator()
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
static bool floatGreaterEqual(double in1, double in2)
const std::string name
Patches together many SquarePolynomialVectors to make a multidimensional polynomial spline...
SectorMagneticFieldMap(std::string file_name, std::string symmetry, double length_units, double field_units, int polynomial_order, int smoothing_order)
static std::map< std::string, SectorMagneticFieldMap * > _fields
std::string::iterator iterator
Definition: MSLang.h:16
virtual unsigned int getValueDimension() const =0
static void convertToPolar(double *position)
Definition: SectorField.cpp:54
virtual std::vector< double > getPolarBoundingBoxMin() const
Definition: Inform.h:41
void setConstantSpacing(bool spacing)
Definition: ThreeDGrid.h:469
Logical error exception.
Definition: LogicalError.h:33
void setSymmetry(std::string name)
static symmetry StringToSymmetry(std::string name)
std::string getSymmetry() const
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
PPSolveFactory solves the system of linear equations to interpolate from a grid of points using highe...
static interpolation::VectorMap * getInterpolator(const std::vector< std::vector< double > > field_points, interpolation::ThreeDGrid *grid, SectorMagneticFieldMap::symmetry sym)
double minZ() const
Definition: ThreeDGrid.h:439
std::vector< double > units_m
bool getFieldDerivative(const Vector_t &R, Vector_t &E, Vector_t &B, const DiffDirection &dir) const