GArMagneticField.cxx
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////////
2 /// \file MPDMagneticField_service.cc
3 ///
4 /// \author trj@fnal.gov, from the nutools example by David McKee
5 //////////////////////////////////////////////////////////////////////////
6 /// \class MPDMagneticField MPDMagneticField.h
7 /// The initial implementation will be trivial: simply supporting a
8 /// constant field in a named detector volume. In principle we should
9 /// read a full field map from an external file of some kind.
10 ///
11 /// FHICL values so far
12 ///
13 /// - "UseField" a integer. When 0 we don't even instantiate a
14 /// Magnetic field object. Describes the description to use.
15 /// - "Constant Field" a vector< double > which should have three
16 /// elements and is interpreted in Tesla
17 /// - "MagnetizedVolume" names the G4logical volume to which the
18 /// field should be attached
19 //////////////////////////////////////////////////////////////////////////
20 
21 // Framework includes
22 #include "fhiclcpp/ParameterSet.h"
23 #include "art_root_io/TFileService.h"
26 
27 // nutools includes
29 
30 #include "TGeoManager.h"
31 #include "TMath.h"
32 #include "TVector3.h"
33 
34 #include <chrono>
35 #include <cmath>
36 #include <string>
37 #include <sys/stat.h>
38 #include <vector>
39 #include <fstream>
40 
41 #include "cetlib/search_path.h"
42 
43 namespace mag
44 {
45 
47 {
48  this->reconfigure(pset);
49 }
50 
51 //------------------------------------------------------------
53 {
54  fGlobalScaleFactor = pset.get<float>("GlobalScaleFactor", 1.0);
55  fUnitFactor = pset.get<float>("UnitFactor", 1.);
56  auto fieldDescriptions = pset.get<std::vector<fhicl::ParameterSet>>("FieldDescriptions");
57 
58  MagneticFieldDescription fieldDescription;
59  for(auto itr : fieldDescriptions)
60  {
61  fieldDescription.fMode = (mag::MagFieldMode_t)(itr.get<int>("UseField"));
62 
63  // if the mode is turned to off, no point looking for a volume or
64  // trying to put a description into the fFieldDescriptions data member.
65  // if all input field descriptions are set to fMode = kNoBFieldMode, then the
66  // methods to return the fields will not go into the loop over fFieldDescriptions
67  // and will just return a 0 field.
68  if(fieldDescription.fMode == mag::kNoBFieldMode)
69  continue;
70 
71  fieldDescription.fScaleFactor = itr.get<float>("ScaleFactor", 1.0);
72  fieldDescription.fVolume = itr.get<std::string>("MagnetizedVolume");
73  fieldDescription.fGeoVol = gGeoManager->FindVolumeFast(fieldDescription.fVolume.c_str());
74 
75  // check that we have a good volume
76  if(fieldDescription.fGeoVol == nullptr)
77  {
78  throw cet::exception("GArMagneticField")
79  << "cannot locate volume " << fieldDescription.fVolume << " in gGeoManager, bail";
80  }
81 
82  // These need to be read as types that FHICL know about, but they
83  // are used by Geant, so I store them in Geant4 types.
84  std::vector<double> defaultField = {0, 0, 0};
85  std::vector<double> field = itr.get<std::vector<double>>("ConstantField", defaultField);
86 
87  // Force the dimension of the field definition
88  field.resize(3);
89  for(size_t i = 0; i < 3; ++i)
90  fieldDescription.fField[i] = field[i];
91 
92  if(fieldDescription.fMode == mag::kFieldRZMapMode)
93  {
94  fieldDescription.fFieldMapFilename = itr.get<std::string>("FieldMapFilename");
95  cet::search_path sp("FW_SEARCH_PATH");
96  std::string fn2 = fieldDescription.fFieldMapFilename;
97  std::string fullname;
98  sp.find_file(fn2, fullname);
99 
100  struct stat sb;
101  if(fullname.empty() || stat(fullname.c_str(), &sb) != 0)
102  {
103  throw cet::exception("RadioGen")
104  << "Input magnetic field file " << fn2 << " not found in FW_SEARCH_PATH!\n";
105  }
106 
107  auto start = std::chrono::steady_clock::now();
108  struct RZFieldMap rzmap;
109  ReadRZFile(fullname, rzmap);
111 
112  std::vector<double> ZAxis = itr.get<std::vector<double>>("ZAxis");
113  rzmap.ZAxis.SetXYZ(ZAxis[0], ZAxis[1], ZAxis[2]);
114 
115  std::vector<double> CoordOffset = itr.get<std::vector<double>>("CoordOffset");
116  rzmap.CoordOffset.SetXYZ(CoordOffset[0], CoordOffset[1], CoordOffset[2]);
117 
118  std::chrono::duration<double> elapsed_seconds = end - start;
119  std::cout << "GArMagneticField: Finished reading RZ map in " << elapsed_seconds.count() << "s, now setting it: " << rzmap.dr
120  << " " << rzmap.dz << std::endl;
121  std::cout << "Array sizes (R, Z): " << rzmap.br.size() << " " << rzmap.br[0].size()
122  << std::endl;
123  fieldDescription.fRZFieldMap = rzmap;
124  }
125 
126  if(fieldDescription.fMode == mag::kFieldXYZMapMode)
127  {
128  fieldDescription.fFieldMapFilename = itr.get<std::string>("FieldMapFilename");
129  cet::search_path sp("FW_SEARCH_PATH");
130  std::string fn2 = fieldDescription.fFieldMapFilename;
131  std::string fullname;
132  sp.find_file(fn2, fullname);
133 
134  struct stat sb;
135  if(fullname.empty() || stat(fullname.c_str(), &sb) != 0)
136  {
137  throw cet::exception("RadioGen")
138  << "Input magnetic field file " << fn2 << " not found in FW_SEARCH_PATH!\n";
139  }
140 
141  auto start = std::chrono::steady_clock::now();
142  struct XYZFieldMap xyzmap;
143  ReadXYZFile(fullname, xyzmap, fUnitFactor);
145 
146  std::vector<double> ZAxis
147  = itr.get<std::vector<double>>("ZAxis", std::vector<double>{0.0, 0.0, 1.0});
148  xyzmap.ZAxis.SetXYZ(ZAxis[0], ZAxis[1], ZAxis[2]);
149 
150  xyzmap.CoordOffset.SetXYZ(xyzmap.xo, xyzmap.yo, xyzmap.zo);
151  std::chrono::duration<double> elapsed_seconds = end - start;
152  std::cout << "GArMagneticField: Finished reading XYZ map in " << elapsed_seconds.count()
153  << "s." << std::endl;
154 
155  xyzmap.UseSymmetry = itr.get<bool>("UseSymmetry", false);
156  std::cout << "GArMagneticField: Is map symmetric - " << std::boolalpha
157  << xyzmap.UseSymmetry << std::endl;
158  fieldDescription.fXYZFieldMap = xyzmap;
159  }
160 
161  fFieldDescriptions.push_back(fieldDescription);
162  }
163 
164  return;
165 }
166 
167 //------------------------------------------------------------
168 G4ThreeVector const GArMagneticField::FieldAtPoint(G4ThreeVector const& p) const
169 {
170  // check that the input point is in the magnetized volume
171  // Use the gGeoManager to determine what node the point
172  // is in
173  double point[3] = {p.x(), p.y(), p.z()};
174  G4ThreeVector zerofield(0, 0, 0);
175  G4ThreeVector calculatedfield = zerofield;
176 
177  // loop over the field descriptions to see if the point is in any of them
178  for(const auto& fd : fFieldDescriptions)
179  {
180  // we found a node, see if its name is the same as
181  // the volume with the field
182 
183  if(fd.fGeoVol->Contains(point))
184  {
185  // "Automatic" for now just means constant field.
186  if(fd.fMode == mag::kConstantBFieldMode || fd.fMode == mag::kAutomaticBFieldMode)
187  {
188  calculatedfield = fd.fField;
189  }
190  else if(fd.fMode == mag::kNoBFieldMode)
191  {
192  calculatedfield = zerofield;
193  }
194  else if(fd.fMode == mag::kFieldRZMapMode)
195  {
196  calculatedfield = CalcRZField(p, fd.fRZFieldMap);
197  }
198  else if(fd.fMode == mag::kFieldXYZMapMode)
199  {
200  calculatedfield = CalcXYZField(p, fd.fXYZFieldMap);
201  }
202  else
203  {
204  throw cet::exception("GArMagneticField_service: Ununderstood field mode: ")
205  << fd.fMode;
206  }
207  }
208  calculatedfield *= fd.fScaleFactor;
209  }
210 
211  calculatedfield *= fGlobalScaleFactor;
212 
213  // std::cout << "field at point: " << p << " field: " << calculatedfield << std::endl;
214  return calculatedfield;
215 }
216 
217 //------------------------------------------------------------
218 G4ThreeVector const GArMagneticField::UniformFieldInVolume(std::string const& volName) const
219 {
220  // if the input volume name is the same as the magnetized volume
221  // return the uniform field
222 
223  for(auto fd : fFieldDescriptions)
224  {
225  if(fd.fVolume.compare(volName) == 0)
226  return fd.fField;
227  }
228 
229  // if we get here, we can't find a field
230  return G4ThreeVector(0);
231 }
232 
233 //------------------------------------------------------------
234 void GArMagneticField::ReadRZFile(const std::string& filename, RZFieldMap& rzmap)
235 {
236  std::cout << "GArMagneticField: Opening magnetic field RZ map in: " << filename << std::endl;
237  std::ifstream inFile(filename, std::ios::in);
239 
240  int rcounter = -1;
241  float rcur = 0;
242  float zcur = 0;
243  rzmap.dr = 0;
244  rzmap.dz = 0;
245 
246  while(std::getline(inFile, line))
247  {
248  float x = 0;
249  float y = 0;
250  float z = 0;
251  float bx = 0;
252  float by = 0;
253  float bz = 0;
254  float b = 0;
255  std::stringstream linestream(line);
256  linestream >> x >> y >> z >> bx >> by >> bz >> b;
257  // we get this in meters. convert to cm
258  x *= 100.0;
259  y *= 100.0;
260  z *= 100.0;
261  float r = TMath::Sqrt(x * x + y * y);
262  float br = bx; // Vladimir's map defines it this way.
263 
264  std::vector<float> emptyvec;
265  if(TMath::Abs(r - rcur) > 0.001 || rcounter < 0)
266  {
267  rzmap.br.push_back(emptyvec);
268  rzmap.bz.push_back(emptyvec);
269  rcounter++;
270  if(r > rcur + 0.001)
271  {
272  rzmap.dr = r - rcur;
273  }
274  rcur = r;
275  }
276  rzmap.br[rcounter].push_back(br);
277  rzmap.bz[rcounter].push_back(bz);
278  if(z - zcur > 0.001)
279  {
280  rzmap.dz = z - zcur;
281  }
282  zcur = z;
283  }
284 }
285 
286 //------------------------------------------------------------
287 void GArMagneticField::ReadXYZFile(const std::string& filename, XYZFieldMap& xyzmap, const float unitFactor)
288 {
289  std::cout << "GArMagneticField: Opening magnetic field XYZ map in: " << filename << std::endl;
290  std::fstream fin(filename, std::fstream::in);
292 
293  int xcount{-1}, ycount{-1}, zcount{-1};
294  float xcurr{0}, ycurr{0}, zcurr{0};
295 
296  while(std::getline(fin >> std::ws, line))
297  {
298  std::stringstream ss(line);
299 
300  if(ss.str().front() == '#')
301  continue;
302 
303  ss >> xyzmap.xo >> xyzmap.yo >> xyzmap.zo >> xyzmap.dx >> xyzmap.dy >> xyzmap.dz;
304 
305  // Position coordinates need to be in mm
306  xyzmap.xo *= unitFactor;
307  xyzmap.yo *= unitFactor;
308  xyzmap.zo *= unitFactor;
309  xyzmap.dx *= unitFactor;
310  xyzmap.dy *= unitFactor;
311  xyzmap.dz *= unitFactor;
312 
313  break;
314  }
315 
316  while(std::getline(fin >> std::ws, line))
317  {
318  float x{0}, y{0}, z{0}, fx{0}, fy{0}, fz{0}, f{0};
319  std::stringstream ss(line);
320 
321  if(ss.str().front() == '#')
322  continue;
323 
324  ss >> x >> y >> z >> fx >> fy >> fz >> f;
325 
326  // Position coordinates need to be in mm
327  x *= unitFactor;
328  y *= unitFactor;
329  z *= unitFactor;
330 
331  if(std::abs(x - xcurr) > 0.0 || xcount < 0)
332  {
333  xcurr = x;
334  xcount += 1;
335  ycount = -1;
336  xyzmap.fx.emplace_back(std::vector<std::vector<float>>{});
337  xyzmap.fy.emplace_back(std::vector<std::vector<float>>{});
338  xyzmap.fz.emplace_back(std::vector<std::vector<float>>{});
339  }
340 
341  if(std::abs(y - ycurr) > 0.0 || ycount < 0)
342  {
343  ycurr = y;
344  ycount += 1;
345  zcount = -1;
346  xyzmap.fx[xcount].emplace_back(std::vector<float>{});
347  xyzmap.fy[xcount].emplace_back(std::vector<float>{});
348  xyzmap.fz[xcount].emplace_back(std::vector<float>{});
349  }
350 
351  xyzmap.fx[xcount][ycount].push_back(fx);
352  xyzmap.fy[xcount][ycount].push_back(fy);
353  xyzmap.fz[xcount][ycount].push_back(fz);
354 
355  if(std::abs(z - zcurr) > 0.0 || zcount < 0)
356  {
357  zcurr = z;
358  zcount += 1;
359  }
360  }
361 }
362 
363 //------------------------------------------------------------
364 G4ThreeVector GArMagneticField::CalcRZField(G4ThreeVector const& p, RZFieldMap const& rzm) const
365 {
366  // check for validity
367  if(rzm.dr <= 0)
368  {
369  throw cet::exception("GArMagneticField: RZ map bad R spacing:") << rzm.dr;
370  }
371  if(rzm.dz <= 0)
372  {
373  throw cet::exception("GArMagneticField: RZ map bad Z spacing:") << rzm.dz;
374  }
375 
376  TVector3 pv(p.x(), p.y(), p.z());
377  TVector3 ploc = pv - rzm.CoordOffset;
378  float zloc = ploc.Dot(rzm.ZAxis);
379  float rloc = (ploc.Cross(rzm.ZAxis)).Mag();
380  float azloc = TMath::Abs(zloc);
381  if(azloc > rzm.dz * rzm.nz() || rloc > rzm.dr * rzm.nr())
382  {
383  return G4ThreeVector(0, 0, 0);
384  }
385  else
386  {
387  TVector3 fv(0, 0, 0);
388  size_t ibinr = rloc / rzm.dr;
389  size_t ibinz = azloc / rzm.dz;
390  float bzcomponent = rzm.bz.at(ibinr).at(ibinz); // closest grid point
391 
392  // cheesy interpolation in just one direction at a time. A better interpolation
393  // would be to stretch a surface around four points.
394  // also this interpolation naively only interpolates Bz as a function of Z and
395  // br as a function of r. the four-point surface would be more symmetrical
396 
397  if(ibinz == 0)
398  {
399  float dbz
400  = ((rzm.bz.at(ibinr).at(ibinz + 1) - bzcomponent) / rzm.dz) * (azloc - (int)azloc);
401  bzcomponent += dbz;
402  }
403  else
404  {
405  float dbz = (bzcomponent - (rzm.bz.at(ibinr).at(ibinz - 1)) / rzm.dz)
406  * (azloc - rzm.dz * ((int)azloc / rzm.dz));
407  bzcomponent += dbz;
408  }
409  fv = bzcomponent * rzm.ZAxis; // z component of field.
410 
411  float bradial = rzm.br.at(ibinr).at(ibinz);
412  if(ibinr == 0)
413  {
414  float dbr = ((rzm.br.at(ibinr + 1).at(ibinz) - bradial) / rzm.dr)
415  * (rloc - rzm.dr * ((int)rloc / rzm.dr));
416  bradial += dbr;
417  }
418  else
419  {
420  float dbr = (bradial - (rzm.br.at(ibinr - 1).at(ibinz)) / rzm.dr)
421  * (rloc - rzm.dr * ((int)rloc / rzm.dr));
422  bradial += dbr;
423  }
424 
425  // calculate radial direction of the field
426 
427  TVector3 rperp = ploc - zloc * rzm.ZAxis;
428  float mrp = rperp.Mag();
429 
430  // we only can have a radial component of the field if we are at r>0
431  if(mrp != 0)
432  {
433  rperp *= (1.0 / mrp);
434  if(zloc > 0) // swap the radial field direction based on the sign of z.
435  {
436  fv += rperp * bradial;
437  }
438  else
439  {
440  fv -= rperp * bradial;
441  }
442  }
443  G4ThreeVector fvg(fv.X(), fv.Y(), fv.Z());
444  return fvg;
445  }
446 }
447 
448 //------------------------------------------------------------
449 G4ThreeVector GArMagneticField::CalcXYZField(G4ThreeVector const& p, XYZFieldMap const& fd) const
450 {
451  const float x = fd.UseSymmetry ? std::abs(p.x() - fd.xo) : p.x() - fd.xo;
452  const float y = fd.UseSymmetry ? std::abs(p.y() - fd.yo) : p.y() - fd.yo;
453  const float z = fd.UseSymmetry ? std::abs(p.z() - fd.zo) : p.z() - fd.zo;
454 
455  Interpolator interp;
456  const float bx = interp.interpolate(x, y, z, fd.fx, fd.dx, fd.dy, fd.dz, 0.0, 0.0, 0.0);
457  const float by = interp.interpolate(x, y, z, fd.fy, fd.dx, fd.dy, fd.dz, 0.0, 0.0, 0.0);
458  const float bz = interp.interpolate(x, y, z, fd.fz, fd.dx, fd.dy, fd.dz, 0.0, 0.0, 0.0);
459 
460  return G4ThreeVector(bx, by, bz);
461 }
462 
463 //------------------------------------------------------------
465 
466 //------------------------------------------------------------
467 float Interpolator::interpolate(const float* point,
468  const std::vector<std::vector<std::vector<float>>>& g,
469  const float* delta, const float* offset) const
470 {
471  return interpolate(point[0], point[1], point[2], g, delta[0], delta[1], delta[2], offset[0],
472  offset[1], offset[2]);
473 }
474 
475 //------------------------------------------------------------
476 float Interpolator::interpolate(float x, float y, float z,
477  const std::vector<std::vector<std::vector<float>>>& g, float hx,
478  float hy, float hz, float xo, float yo, float zo) const
479 {
480  float v = 0;
481 
482  const int xsize = g.size();
483  const int ysize = g[0].size();
484  const int zsize = g[0][0].size();
485 
486  const float xp = (x - xo) / hx;
487  const float yp = (y - yo) / hy;
488  const float zp = (z - zo) / hz;
489 
490  const int x_idx = std::floor(xp);
491  const int y_idx = std::floor(yp);
492  const int z_idx = std::floor(zp);
493 
494  for(auto i : {x_idx - 1, x_idx, x_idx + 1, x_idx + 2})
495  {
496  for(auto j : {y_idx - 1, y_idx, y_idx + 1, y_idx + 2})
497  {
498  for(auto k : {z_idx - 1, z_idx, z_idx + 1, z_idx + 2})
499  {
500  if(i < 0 || j < 0 || k < 0)
501  continue;
502 
503  if(i >= xsize || j >= ysize || k >= zsize)
504  continue;
505 
506  const float x_c = conv_kernel(xp - i);
507  const float y_c = conv_kernel(yp - j);
508  const float z_c = conv_kernel(zp - k);
509  v += g[i][j][k] * x_c * y_c * z_c;
510  }
511  }
512  }
513 
514  return v;
515 }
516 
517 //------------------------------------------------------------
518 float Interpolator::conv_kernel(float s) const
519 {
520  float v = 0;
521  float z = std::abs(s);
522 
523  if(0 <= z && z < 1)
524  v = 1 + (0.5) * z * z * (3 * z - 5);
525 
526  else if(1 < z && z < 2)
527  v = 2 - z * (4 + 0.5 * z * (z - 5));
528 
529  return v;
530 }
531 
532 } // namespace mag
end
while True: pbar.update(maxval-len(onlies[E][S])) #print iS, "/", len(onlies[E][S]) found = False for...
static constexpr double g
Definition: Units.h:144
std::string string
Definition: nybbler.cc:12
struct vector vector
G4ThreeVector const FieldAtPoint(G4ThreeVector const &p=G4ThreeVector(0)) const override
string filename
Definition: train.py:213
void reconfigure(fhicl::ParameterSet const &pset)
void ReadXYZFile(const std::string &filename, XYZFieldMap &xyzmap, const float unitFactor)
std::vector< MagneticFieldDescription > fFieldDescriptions
Descriptions of the fields.
T abs(T value)
GArMagneticField(fhicl::ParameterSet const &pset)
T get(std::string const &key) const
Definition: ParameterSet.h:271
void ReadRZFile(const std::string &filename, RZFieldMap &rzmap)
p
Definition: test.py:223
float interpolate(const float *point, const std::vector< std::vector< std::vector< float >>> &g, const float *delta, const float *offset) const
float conv_kernel(float s) const
G4ThreeVector const UniformFieldInVolume(std::string const &volName) const override
TFile * inFile
Definition: makeDST.cxx:36
G4ThreeVector CalcRZField(G4ThreeVector const &p, RZFieldMap const &rzmap) const
void line(double t, double *p, double &x, double &y, double &z)
static bool * b
Definition: config.cpp:1043
G4ThreeVector CalcXYZField(G4ThreeVector const &p, XYZFieldMap const &xyzmap) const
list x
Definition: train.py:276
static QCString * s
Definition: config.cpp:1042
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
QTextStream & endl(QTextStream &s)