EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
eASTMagneticField.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file eASTMagneticField.cc
1 // ********************************************************************
2 //
3 // eASTMagneticField.cc
4 // Header file of eAST Magnetic Field class
5 //
6 // History
7 // June 22nd, 2021 : first implementation
8 //
9 // ********************************************************************
10 
11 #include "eASTMagneticField.hh"
12 
13 #include "G4TransportationManager.hh"
14 #include "G4UImanager.hh"
15 #include "G4FieldManager.hh"
16 
17 #include "G4PropagatorInField.hh"
18 #include "G4Mag_UsualEqRhs.hh"
19 #include "G4ClassicalRK4.hh"
20 
21 #include <fstream>
22 
24 : fFieldMessenger(this,"/eAST/field/","Field properties")
25 {
26  fFieldMessenger.DeclareMethod("create",&eASTMagneticField::CreateField,"Create magnetic field")
27  .SetStates(G4State_PreInit)
28  .SetToBeBroadcasted(false);
29  fFieldMessenger.DeclareMethod("print",&eASTMagneticField::PrintFieldValue,"Print magnetic field [mm]")
30  .SetStates(G4State_PreInit, G4State_Init, G4State_Idle)
31  .SetToBeBroadcasted(false);
32 }
33 
35 {
36  // Point field manager to field
37  auto transportationmanager = G4TransportationManager::GetTransportationManager();
38  fFieldPropagator = transportationmanager->GetPropagatorInField();
39  fFieldManager = transportationmanager->GetFieldManager();
40  fFieldManager->SetDetectorField(this);
41 
42  // Set equation
43  fEquation = new G4Mag_UsualEqRhs(this);
44  fEquationDoF = 6;
45  // Set stepper
46  fStepper = new G4ClassicalRK4(fEquation, fEquationDoF);
47  // Set chord finder
48  fChordFinder = new G4ChordFinder(this,fMinStep,fStepper);
49  fChordFinder->GetIntegrationDriver()->SetVerboseLevel(0);
50  fChordFinder->SetDeltaChord(fDeltaChord);
51  fFieldManager->SetChordFinder(fChordFinder);
52  fFieldManager->SetAccuraciesWithDeltaOneStep(fDeltaOneStep);
53  fFieldManager->SetDeltaIntersection(fDeltaIntersection);
54  fFieldPropagator->SetMinimumEpsilonStep(fEpsMin);
55  fFieldPropagator->SetMaximumEpsilonStep(fEpsMax);
56 }
57 
58 void eASTMagneticField::CreateField(const G4String& name) {
59  fMaps.emplace_back(name);
60 }
61 
63 : fName(name),fFieldMapMessenger(this,"/eAST/field/" + fName + "/","Field map properties")
64 {
65  fFieldMapMessenger.DeclareMethod("load",&eASTMagneticFieldMap::Load,"Load magnetic field")
66  .SetStates(G4State_PreInit)
67  .SetToBeBroadcasted(false);
68  fFieldMapMessenger.DeclareMethod("fieldunit",&eASTMagneticFieldMap::SetFieldUnit,"Set field map value unit")
69  .SetStates(G4State_PreInit)
70  .SetToBeBroadcasted(false);
71  fFieldMapMessenger.DeclareMethod("gridunit",&eASTMagneticFieldMap::SetGridUnit,"Set field map grid unit")
72  .SetStates(G4State_PreInit)
73  .SetToBeBroadcasted(false);
74  fFieldMapMessenger.DeclareMethod("extent",&eASTMagneticFieldMap::SetGridExtent,"Set field map grid extent")
75  .SetStates(G4State_PreInit)
76  .SetToBeBroadcasted(false);
77  fFieldMapMessenger.DeclareMethod("type",&eASTMagneticFieldMap::SetInterpolationType,"Set field map interpolation type")
78  .SetStates(G4State_PreInit)
79  .SetToBeBroadcasted(false);
80  fFieldMapMessenger.DeclareMethod("print",&eASTMagneticFieldMap::PrintFieldValue,"Print magnetic field")
81  .SetStates(G4State_PreInit, G4State_Init, G4State_Idle)
82  .SetToBeBroadcasted(false);
83 }
84 
85 bool eASTMagneticFieldMap::Load(const G4String& filename)
86 {
87  // Do not reload map
88  if (! fMap.empty()) return false;
89 
90  // should protect G4cout's with if ( verboseLevel > 0 ),
91  // but this class doesn't have one
92  G4cout << "Locate Magnet map: Searching for " << filename << G4endl;
93  auto UImanager = G4UImanager::GetUIpointer();
94  auto fullfile = UImanager->FindMacroPath(filename);
95  G4cout << "Locate Magnet map: Using" << fullfile << G4endl;
96 
97  // Create ifstream
98  std::ifstream inputfile(fullfile);
99  if (!inputfile.good()) {
100  G4cout << "WARNING: Couldn't open " << fullfile << G4endl;
101  return false;
102  }
103 
104  // Determine grid size for i = 0,1
105  for (unsigned int i = 0; i < 2; i++) {
106  if (std::get<0>(fGridExtent[i]) < std::get<1>(fGridExtent[i])
107  && std::get<2>(fGridExtent[i]) > 0) {
108  fGridSize[i] = (int) rint (std::get<1>(fGridExtent[i]) - std::get<0>(fGridExtent[i])) / std::get<2>(fGridExtent[i]) + 1;
109  } else {
110  G4cerr << "ERROR: unable to read grid." << G4endl;
111  return false;
112  }
113  }
114 
115  // Determine map type
116  if (std::get<0>(fGridExtent[2]) == std::get<1>(fGridExtent[2])) {
117  G4cout << "Assuming axially symmetric 2D grid." << G4endl;
118  fGridSize[2] = 1;
120  } else {
121  G4cout << "Assuming R,Z,Phi grid." << G4endl;
122  fGridSize[2] = (int) rint (std::get<1>(fGridExtent[2]) - std::get<0>(fGridExtent[2])) / std::get<2>(fGridExtent[2]) + 1;
124  }
125 
126  // Resize map
127  fMap.resize(3,
128  std::vector<std::vector<std::vector<double>>>(fGridSize[0],
129  std::vector<std::vector<double>>(fGridSize[1],
130  std::vector<double>(fGridSize[2],
131  0.0
132  )
133  )
134  )
135  );
136 
137  // Read file from start
138  unsigned int counter = 0;
139  while (inputfile.good()) {
140  double polar[3] = {0.0, 0.0, 0.0};
141  double field[3] = {0.0, 0.0, 0.0};
142  switch (fFileFormat) {
143  case kFileFormatRZPhi:
144  if (! (inputfile >>
145  polar[0] >> polar[1] >> polar[2] >>
146  field[0] >> field[1] >> field[2])) {
147  if (inputfile.eof()) continue;
148  G4cerr << "ERROR: unable to read grid, no 6 columns." << G4endl;
149  return false;
150  }
151  break;
152  case kFileFormatRZ:
153  if (! (inputfile >>
154  polar[0] >> polar[1] >>
155  field[0] >> field[1])) {
156  if (inputfile.eof()) continue;
157  G4cerr << "ERROR: unable to read grid, no 4 columns." << G4endl;
158  return false;
159  }
160  break;
162  G4cout << "ERROR: set field map extent first" << G4endl;
163  return false;
164  break;
165  }
166 
167  // Determine grid index
168  unsigned int index[3] = {0, 0, 0};
169  for (unsigned int i = 0; i < (fFileFormat == kFileFormatRZPhi ? 3: 2); i++) {
170  index[i] = (int) rint ((polar[i] - std::get<0>(fGridExtent[i])) / std::get<2>(fGridExtent[i]));
171  }
172  for (unsigned int i = 0; i < 3; i++) {
173  fMap[i][index[0]][index[1]][index[2]] = field[i] * fFieldUnit;
174  }
175  counter++;
176  }
177 
178  // Check total entries read
179  if (counter != fGridSize[0] * fGridSize[1] * fGridSize[2]) {
180  G4cout << "ERROR: expected " << fGridSize[0] * fGridSize[1] * fGridSize[2] << " entries "
181  << "but read " << counter << G4endl;
182  return false;
183  }
184 
185  return true;
186 }
187 
188 void eASTMagneticFieldMap::AddFieldValue(const G4double point[4], G4double *cartesian) const
189 {
190  // Bounds in z
191  if (point[2] < std::get<0>(fGridExtent[1])*fGridUnit[1]
192  || point[2] > std::get<1>(fGridExtent[1])*fGridUnit[1]) return;
193 
194  // Determine polar coordinate
195  double polar[3] = {
196  sqrt(point[0]*point[0] + point[1]*point[1]),
197  point[2],
198  atan2(point[1], point[0])
199  };
200 
201  // Bounds in r
202  if (polar[0] < std::get<0>(fGridExtent[0])*fGridUnit[0]
203  || polar[0] > std::get<1>(fGridExtent[0])*fGridUnit[0]) return;
204 
205  // Determine cell and local coordinate
206  unsigned int index[3] = {0, 0, 0};
207  double local[3] = {0.0, 0.0, 0.0};
208  for (unsigned int i = 0; i < (fFileFormat == kFileFormatRZPhi ? 3: 2); i++) {
209  index[i] = (int) floor ((polar[i]/fGridUnit[i] - std::get<0>(fGridExtent[i])) / std::get<2>(fGridExtent[i]));
210  local[i] = (polar[i]/fGridUnit[i] - std::get<0>(fGridExtent[i]) - index[i] * std::get<2>(fGridExtent[i])) / std::get<2>(fGridExtent[i]);
211  }
212 
213  // Fall back to linear interpolation at boundary layer
214  EInterpolationType interpolation = fInterpolationType;
215  if (interpolation == kCubic) {
216  if ((index[0] == 0)
217  || (index[1] == 0)
218  || (index[2] == 0 && fFileFormat == kFileFormatRZPhi)
219  || (index[0] == fGridSize[0] - 2)
220  || (index[1] == fGridSize[1] - 2)
221  || (index[2] == fGridSize[2] - 2 && fFileFormat == kFileFormatRZPhi)) {
222  interpolation = kLinear;
223  }
224  }
225 
226  // Get cell corner values
227  size_t n = 8;
228  const int (*map)[3] = kLinearMap;
229  switch (interpolation) {
230  case kLinear:
231  map = kLinearMap;
232  n = 8;
233  break;
234  case kCubic:
235  map = kCubicMap;
236  n = 64;
237  break;
238  }
239  thread_local G4double values[3][64];
240  for (unsigned int i = 0; i < n; i++) {
241  for (unsigned int j = 0; j < 3; j++) {
242  values[j][i] =
243  (fFileFormat == kFileFormatRZPhi ?
244  fMap[j][index[0] + map[i][0]][index[1] + map[i][1]][index[2] + map[i][2]] :
245  fMap[j][index[0] + map[i][0]][index[1] + map[i][1]][0]
246  );
247  }
248  }
249 
250  // Interpolate in cylindrical components
251  double cylindrical[3] = {0.0, 0.0, 0.0};
252  for (unsigned int j = 0; j < 3; j++) {
253  cylindrical[j] = _trilinearInterpolate(values[j], local);
254  }
255 
256  // Rotate into cartesian coordinates:
257  // cartesian[Bx,By,Bz], point[x,y,z]
258  // cylindrical[Br,Bz,Bphi], polar[r,z,phi]
259  if (polar[0] > 0) {
260  cartesian[0] = (cylindrical[0] * point[0] - cylindrical[2] * point[1]) / polar[0];
261  cartesian[1] = (cylindrical[2] * point[0] + cylindrical[0] * point[1]) / polar[0];
262  } else {
263  cartesian[0] = cylindrical[0] * cos(polar[2]) - cylindrical[2] * sin(polar[2]);
264  cartesian[1] = cylindrical[2] * cos(polar[2]) + cylindrical[0] * sin(polar[2]);
265  }
266  cartesian[2] = cylindrical[1];
267 }
268 
270 {
271  G4double B[3] = {0,0,0};
272  G4double p[4] = {point.x(), point.y(), point.z(), 0.0};
273  AddFieldValue(p, B);
274  G4cout << "B" << point/CLHEP::m << " [m] = ";
275  for (unsigned int i = 0; i < 3; i++)
276  G4cout << B[i]/CLHEP::tesla << " ";
277  G4cout << "[T]" << G4endl;
278 }
279 
280 
281 // Set location of cell vertices
282 const int eASTMagneticFieldMap::kLinearMap[8][3] = {
283  {0, 0, 0}, // 00
284  {0, 0, 1},
285  {0, 1, 0},
286  {0, 1, 1},
287  {1, 0, 0}, // 04
288  {1, 0, 1},
289  {1, 1, 0},
290  {1, 1, 1},
291 };
292 const int eASTMagneticFieldMap::kCubicMap[64][3] = {
293  {-1, -1, -1}, // 00
294  {-1, -1, 0},
295  {-1, -1, 1},
296  {-1, -1, 2},
297  {-1, 0, -1}, // 04
298  {-1, 0, 0},
299  {-1, 0, 1},
300  {-1, 0, 2},
301  {-1, 1, -1}, // 08
302  {-1, 1, 0},
303  {-1, 1, 1},
304  {-1, 1, 2},
305  {-1, 2, -1}, // 12
306  {-1, 2, 0},
307  {-1, 2, 1},
308  {-1, 2, 2},
309  {0, -1, -1}, // 16
310  {0, -1, 0},
311  {0, -1, 1},
312  {0, -1, 2},
313  {0, 0, -1}, // 20
314  {0, 0, 0},
315  {0, 0, 1},
316  {0, 0, 2},
317  {0, 1, -1}, // 24
318  {0, 1, 0},
319  {0, 1, 1},
320  {0, 1, 2},
321  {0, 2, -1}, // 28
322  {0, 2, 0},
323  {0, 2, 1},
324  {0, 2, 2},
325  {1, -1, -1}, // 22
326  {1, -1, 0},
327  {1, -1, 1},
328  {1, -1, 2},
329  {1, 0, -1}, // 26
330  {1, 0, 0},
331  {1, 0, 1},
332  {1, 0, 2},
333  {1, 1, -1}, // 40
334  {1, 1, 0},
335  {1, 1, 1},
336  {1, 1, 2},
337  {1, 2, -1}, // 44
338  {1, 2, 0},
339  {1, 2, 1},
340  {1, 2, 2},
341  {2, -1, -1}, // 48
342  {2, -1, 0},
343  {2, -1, 1},
344  {2, -1, 2},
345  {2, 0, -1}, // 52
346  {2, 0, 0},
347  {2, 0, 1},
348  {2, 0, 2},
349  {2, 1, -1}, // 56
350  {2, 1, 0},
351  {2, 1, 1},
352  {2, 1, 2},
353  {2, 2, -1}, // 60
354  {2, 2, 0},
355  {2, 2, 1},
356  {2, 2, 2},
357 };