BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSFieldQuery.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2024.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "BDSAuxiliaryNavigator.hh"
20#include "BDSDebug.hh"
21#include "BDSException.hh"
22#include "BDSFieldQuery.hh"
23#include "BDSFieldQueryInfo.hh"
24#include "BDSUtilities.hh"
25#include "BDSWarning.hh"
26
27#include "globals.hh"
28#include "G4Field.hh"
29#include "G4FieldManager.hh"
30#include "G4LogicalVolume.hh"
31#include "G4Navigator.hh"
32#include "G4String.hh"
33#include "G4ThreeVector.hh"
34#include "G4Types.hh"
35#include "G4VPhysicalVolume.hh"
36
37#include "CLHEP/Units/SystemOfUnits.h"
38
39#include <cmath>
40#include <fstream>
41#include <iomanip>
42#include <vector>
43
44G4Navigator* BDSFieldQuery::navigator = new G4Navigator();
45
46BDSFieldQuery::BDSFieldQuery():
47 queryMagnetic(false),
48 queryElectric(false),
49 writeX(false),
50 writeY(false),
51 writeZ(false),
52 writeT(false)
53{
54 CleanUp();
55}
56
57BDSFieldQuery::~BDSFieldQuery()
58{;}
59
60void BDSFieldQuery::AttachWorldVolumeToNavigator(G4VPhysicalVolume* worldPVIn)
61{
62 navigator->SetWorldVolume(worldPVIn);
63}
64
66{
67 CloseFiles();
68 queryMagnetic = false;
69 queryElectric = false;
70 writeX = false;
71 writeY = false;
72 writeZ = false;
73 writeT = false;
74 navigator->ResetStackAndState();
75}
76
77void BDSFieldQuery::QueryFields(const std::vector<BDSFieldQueryInfo*>& fieldQueries)
78{
79 for (const auto& fieldQuery : fieldQueries)
80 {QueryField(fieldQuery);}
81}
82
84{
85 CleanUp();
86
87 if (!query)
88 {return;} // protection - now we assume this pointer is always valid in the rest of this class
89
90 // warn the user if a fieldObject is specified. In this context (bdsim) this has no effect
91 // derived class may change this
92 if (query->checkParameters)
94
95 if (query->SpecificPoints())
96 {
98 return;
99 }
100
101 G4cout << "FieldQuery> \"" << query->name << "\" with N (x,y,z,t) points: ("
102 << query->xInfo.n << ", " << query->yInfo.n << ", " << query->zInfo.n << ", " << query->tInfo.n
103 << ")";
104 PrintBAndEInfo(query);
105
106 if (query->printTransform)
107 {G4cout << query->globalTransform << G4endl;}
108
110 BDSAuxiliaryNavigator::ResetNavigatorStates();
111
112 G4double xMin = query->xInfo.min;
113 G4double nStepsX = query->xInfo.n == 1 ? 1 : (G4double)query->xInfo.n-1;
114 G4double xStep = (query->xInfo.max - query->xInfo.min) / nStepsX;
115 if (std::isnan(xStep))
116 {xStep = 1.0;}
117 CheckNStepsAndRange(query->xInfo, "x", query->name);
118
119 G4double yMin = query->yInfo.min;
120 G4double nStepsY = query->yInfo.n == 1 ? 1 : (G4double)query->yInfo.n-1;
121 G4double yStep = (query->yInfo.max - query->yInfo.min) / nStepsY;
122 if (std::isnan(yStep))
123 {yStep = 1.0;}
124 CheckNStepsAndRange(query->yInfo, "y", query->name);
125
126 G4double zMin = query->zInfo.min;
127 G4double nStepsZ = query->zInfo.n == 1 ? 1 : (G4double)query->zInfo.n-1;
128 G4double zStep = (query->zInfo.max - query->zInfo.min) / nStepsZ;
129 if (std::isnan(zStep))
130 {zStep = 1.0;}
131 CheckNStepsAndRange(query->zInfo, "z", query->name);
132
133 G4double tMin = query->tInfo.min;
134 G4double nStepsT = query->yInfo.n == 1 ? 1 : (G4double)query->tInfo.n-1;
135 G4double tStep = (query->tInfo.max - query->tInfo.min) / nStepsT;
136 if (std::isnan(tStep))
137 {tStep = 1.0;}
138 CheckNStepsAndRange(query->tInfo, "t", query->name);
139
140 G4ThreeVector xyzGlobal = G4ThreeVector();
141 const G4AffineTransform& localToGlobalTransform = query->globalTransform;
142 G4AffineTransform globalToLocalTransform = localToGlobalTransform.Inverse();
143
144 G4ThreeVector generalUnitZ(0,0,1);
145 localToGlobalTransform.ApplyAxisTransform(generalUnitZ);
146
147 OpenFiles(query);
148
149 G4double globalFieldValue[6];
150 G4double localFieldValue[6];
151
152 G4double tLocal = tMin;
153 for (G4int i = 0; i < query->tInfo.n; i++)
154 {
155 G4double zLocal = zMin;
156 for (G4int j = 0; j < query->zInfo.n; j++)
157 {
158 G4double yLocal = yMin;
159 for (G4int k = 0; k < query->yInfo.n; k++)
160 {
161 G4double xLocal = xMin;
162 for (G4int l = 0; l < query->xInfo.n; l++)
163 {
164 xyzGlobal = LocalToGlobalPoint(localToGlobalTransform, xLocal, yLocal, zLocal);
165 GetFieldValue(xyzGlobal, generalUnitZ, tLocal, globalFieldValue);
166 GlobalToLocalAxisField(globalToLocalTransform,
167 globalFieldValue,
168 localFieldValue);
169 WriteFieldValue({xLocal, yLocal, zLocal}, tLocal, localFieldValue);
170
171 xLocal += xStep;
172 }
173 yLocal += yStep;
174 }
175 zLocal += zStep;
176 }
177 tLocal += tStep;
178 }
179
180 CloseFiles();
181 G4cout << "FieldQuery> Complete" << G4endl;
182}
183
185 const G4String& dimensionName,
186 const G4String& queryName) const
187{
188 const G4String& d = dimensionName;
189 G4bool nonZeroRange = BDS::IsFinite(std::abs(dimensionInfo.max - dimensionInfo.min));
190 if (dimensionInfo.n > 1 && !nonZeroRange)
191 {
192 G4String msg = "Problem in query \"" + queryName + "\": n"+d+" > 1, but |"+d;
193 msg += "max - "+d+"min| = 0 -> cannot take more than 1 step in 0 range.";
194 throw BDSException(__METHOD_NAME__, msg);
195 }
196 if (dimensionInfo.n == 1 && nonZeroRange)
197 {BDS::Warning("Only 1 point to query in \"" + queryName + "\" over a non-zero range - check n"+d);}
198}
199
201{
202 if (!(query->fieldObject.empty()))
203 {
204 G4String msg = "The \"fieldObject\" variable is specified in the query definition \"" + query->name;
205 msg += "\".\nThis has no effect when using a query in bdsim (instead of bdsinterpolator)\n";
206 msg += "Remove \"fieldObject\" to suppress this warning.";
207 BDS::Warning(msg);
208 }
209}
210
212{
213 const std::vector<BDSFourVector<G4double>> points = query->pointsToQuery;
214
215 G4cout << "FieldQuery> \"" << query->name << "\" with N points: "
216 << points.size() << ", writing to file";
217 PrintBAndEInfo(query);
218
219 OpenFiles(query);
220 G4ThreeVector xyz;
221 G4double t;
222 G4ThreeVector generalUnitZ(0,0,1);
223 G4double globalFieldValue[6];
224 for (auto const& xyzt : points)
225 {
226 xyz = G4ThreeVector(xyzt.x(), xyzt.y(), xyzt.z());
227 t = xyzt.t();
228 GetFieldValue(xyz, generalUnitZ, t, globalFieldValue);
229 WriteFieldValue(xyz, t, globalFieldValue);
230 }
231 CloseFiles();
232 G4cout << "FieldQuery> Complete" << G4endl;
233}
234
236{
237 G4cout << ", writing to file";
238 if (query->queryMagnetic && query->queryElectric)
239 {G4cout << "s B: \"" << query->outfileMagnetic << "\" and E: \"" << query->outfileElectric << "\"" << G4endl;}
240 else if (query->queryMagnetic)
241 {G4cout << " B: \"" << query->outfileMagnetic << "\"" << G4endl;}
242 else
243 {G4cout << " E: \"" << query->outfileElectric << "\"" << G4endl;}
244}
245
247{
248 if (!(query->pointsColumnNames.empty()))
249 {
250 std::map<G4String, G4bool*> columnNamesToFlag = { {"x", &writeX},
251 {"y", &writeY},
252 {"z", &writeZ},
253 {"t", &writeT} };
254 for (const auto& columnName : query->pointsColumnNames)
255 { *(columnNamesToFlag[columnName]) = true;}
256 BDS::Warning(__METHOD_NAME__, "the number of points and min max values in the output header will not be correct when using points");
257 }
258 else
259 {
260 writeX = query->xInfo.n > 1;
261 writeY = query->yInfo.n > 1;
262 writeZ = query->zInfo.n > 1;
263 writeT = query->tInfo.n > 1;
264 }
265
266 if (query->queryMagnetic)
267 {
268 if (BDS::FileExists(query->outfileMagnetic) && !(query->overwriteExistingFiles))
269 {
270 G4String msg = "\"" + query->outfileMagnetic + "\" file already exists and \"overwriteExistingFiles\" in query \"";
271 msg += query->name + "\" is false";
272 throw BDSException(__METHOD_NAME__, msg);
273 }
274 queryMagnetic = true;
275 oFileMagnetic.open(query->outfileMagnetic);
276 WriteHeader(oFileMagnetic, query);
277 }
278
279 if (query->queryElectric)
280 {
281 if (BDS::FileExists(query->outfileElectric) && !(query->overwriteExistingFiles))
282 {
283 G4String msg = "\"" + query->outfileElectric + "\" file already exists and \"overwriteExistingFiles\" in query \"";
284 msg += query->name + "\" is false";
285 throw BDSException(__METHOD_NAME__, msg);
286 }
287 queryElectric = true;
288 oFileElectric.open(query->outfileElectric);
289 WriteHeader(oFileElectric, query);
290 }
291}
292
293void BDSFieldQuery::WriteHeader(std::ofstream& out,
294 const BDSFieldQueryInfo* query) const
295{
296 G4String columns = "! ";
297 if (writeX)
298 {
299 out << "nx> " << query->xInfo.n << "\n";
300 out << "xmin> " << query->xInfo.min/CLHEP::cm << "\n";
301 out << "xmax> " << query->xInfo.max/CLHEP::cm << "\n";
302 columns += " X ";
303 }
304 if (writeY)
305 {
306 out << "ny> " << query->yInfo.n << "\n";
307 out << "ymin> " << query->yInfo.min/CLHEP::cm << "\n";
308 out << "ymax> " << query->yInfo.max/CLHEP::cm << "\n";
309 columns += " Y ";
310 }
311 if (writeZ)
312 {
313 out << "nz> " << query->zInfo.n << "\n";
314 out << "zmin> " << query->zInfo.min/CLHEP::cm << "\n";
315 out << "zmax> " << query->zInfo.max/CLHEP::cm << "\n";
316 columns += " Z ";
317 }
318 if (writeT)
319 {
320 out << "nt> " << query->tInfo.n << "\n";
321 out << "tmin> " << query->tInfo.min/CLHEP::s << "\n";
322 out << "tmax> " << query->tInfo.max/CLHEP::s << "\n";
323 columns += " T ";
324 }
325 columns += " Fx Fy Fz\n";
326 out << columns;
327}
328
330{
331 if (oFileMagnetic.is_open())
332 {oFileMagnetic.close();}
333 if (oFileElectric.is_open())
334 {oFileElectric.close();}
335}
336
337G4ThreeVector BDSFieldQuery::LocalToGlobalPoint(const G4AffineTransform& localToGlobalTransform,
338 G4double xLocal,
339 G4double yLocal,
340 G4double zLocal) const
341{
342 G4ThreeVector xyzGlobal = G4ThreeVector(xLocal, yLocal, zLocal);
343 localToGlobalTransform.ApplyPointTransform(xyzGlobal);
344 return xyzGlobal;
345}
346
347void BDSFieldQuery::GlobalToLocalAxisField(const G4AffineTransform& globalToLocalTransform,
348 const G4double globalBEField[6],
349 G4double localBEField[6])
350{
351 G4ThreeVector B(globalBEField[0], globalBEField[1], globalBEField[2]);
352 G4ThreeVector E(globalBEField[3], globalBEField[4], globalBEField[5]);
353 globalToLocalTransform.ApplyAxisTransform(B);
354 globalToLocalTransform.ApplyAxisTransform(E);
355 for (G4int i = 0; i < 3; i++)
356 {
357 localBEField[i] = B[i];
358 localBEField[i+3] = E[i];
359 }
360}
361
362void BDSFieldQuery::GetFieldValue(const G4ThreeVector& globalXYZ,
363 const G4ThreeVector& globalDirection,
364 G4double tGlobal,
365 G4double fieldValue[6])
366{
367 // always update as 0 as can't predict behaviour of Geant4 - takes fieldValue* as
368 // variable array length, so we rely on it definitely writing E field as well as B??
369 for (G4int i = 0; i < 6; i++)
370 {fieldValue[i] = 0;}
371 G4VPhysicalVolume* pv = navigator->LocateGlobalPointAndSetup(globalXYZ, &globalDirection, /*relativeSearch*/false);
372 if (!pv)
373 {return;} // this would happen if we query a point outside the world
374 G4LogicalVolume* lv = pv->GetLogicalVolume();
375 G4FieldManager* fm = lv->GetFieldManager();
376 if (fm)
377 {
378 const G4Field* field = fm->GetDetectorField();
379 G4double position[4] = {globalXYZ.x(), globalXYZ.y(),globalXYZ.z(), tGlobal};
380 field->GetFieldValue(position, fieldValue);
381 }
382}
383
384void BDSFieldQuery::WriteFieldValue(const G4ThreeVector& xyzLocal,
385 G4double tLocal,
386 const G4double fieldValue[6])
387{
388 if (queryMagnetic)
389 {
390 if (writeX)
391 {oFileMagnetic << std::setprecision(6) << std::setw(10) << xyzLocal.x() / CLHEP::cm << " ";}
392 if (writeY)
393 {oFileMagnetic << std::setprecision(6) << std::setw(10) << xyzLocal.y() / CLHEP::cm << " ";}
394 if (writeZ)
395 {oFileMagnetic << std::setprecision(6) << std::setw(10) << xyzLocal.z() / CLHEP::cm << " ";}
396 if (writeT)
397 {oFileMagnetic << std::setprecision(6) << std::setw(10) << tLocal / CLHEP::s << " ";}
398 for (G4int i = 0; i < 3; i++)
399 {oFileMagnetic << std::setprecision(6) << std::setw(15) << fieldValue[i] / CLHEP::tesla << " ";}
400 oFileMagnetic << "\n";
401 }
402 if (queryElectric)
403 {
404 if (writeX)
405 {oFileElectric << std::setprecision(6) << std::setw(10) << xyzLocal.x() / CLHEP::cm << " ";}
406 if (writeY)
407 {oFileElectric << std::setprecision(6) << std::setw(10) << xyzLocal.y() / CLHEP::cm << " ";}
408 if (writeZ)
409 {oFileElectric << std::setprecision(6) << std::setw(10) << xyzLocal.z() / CLHEP::cm << " ";}
410 if (writeT)
411 {oFileElectric << std::setprecision(6) << std::setw(10) << tLocal / CLHEP::s << " ";}
412 for (G4int i = 3; i < 6; i++)
413 {oFileElectric << std::setprecision(6) << std::setw(15) << fieldValue[i] / (CLHEP::volt/CLHEP::m) << " ";}
414 oFileElectric << "\n";
415 }
416}
General exception with possible name of object and message.
Holder class for all information required for a field query.
G4bool checkParameters
For internal testing use only.
G4bool SpecificPoints() const
Whether to query a specific set of points.
G4String fieldObject
Optional for use in interpolator.
static G4Navigator * navigator
One navigator used - static so we can externally link it up to the world PV.
static void AttachWorldVolumeToNavigator(G4VPhysicalVolume *worldPVIn)
Setup the navigator w.r.t. to a world volume.
virtual void OpenFiles(const BDSFieldQueryInfo *query)
Open potentially both electric and magnetic field map files.
virtual void WriteHeader(std::ofstream &out, const BDSFieldQueryInfo *query) const
Utility to write required header information.
void QueryFields(const std::vector< BDSFieldQueryInfo * > &fieldQueries)
Vector version of above function. Unique output files for each query.
virtual void CleanUp()
Reset any member variables used during a query. Closes any files if open.
G4ThreeVector LocalToGlobalPoint(const G4AffineTransform &localToGlobalTransform, G4double xLocal, G4double yLocal, G4double zLocal) const
Apply a transform to the coordinates. Does not apply to time.
void CheckNStepsAndRange(const BDSFieldQueryInfo::QueryDimensionInfo &dimensionInfo, const G4String &dimensionName, const G4String &queryName) const
Throw an exception if the number of steps is >1 and the difference between max and min is 0.
virtual void CheckIfFieldObjectSpecified(const BDSFieldQueryInfo *query) const
Warn the user if the fieldObject variable is use when it shouldn't be.
void QuerySpecificPoints(const BDSFieldQueryInfo *query)
Different algorithm where we query a specific list of points defined in the query info object.
virtual void CloseFiles()
Close any files if open.
virtual void PrintBAndEInfo(const BDSFieldQueryInfo *query) const
Print out whether B and E files are being generated and which ones.
virtual void QueryField(const BDSFieldQueryInfo *query)
Query the field in the Geant4 model according to information in query.
virtual void GetFieldValue(const G4ThreeVector &globalXYZ, const G4ThreeVector &globalDirection, G4double tGlobal, G4double fieldValue[6])
virtual void WriteFieldValue(const G4ThreeVector &xyzGlobal, G4double tGlobal, const G4double fieldValue[6])
Write an entry ta line of the output file(s). The array is assumed to be Bx,By,Bz,...
void GlobalToLocalAxisField(const G4AffineTransform &globalToLocalTransform, const G4double globalBEField[6], G4double localBEField[6])
Convert a global field axis to a local one.
G4bool FileExists(const G4String &filename)
Checks if filename exists.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())