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