19#include "BDSArray1DCoords.hh"
20#include "BDSArray2DCoords.hh"
21#include "BDSArray3DCoords.hh"
22#include "BDSArray4DCoords.hh"
24#include "BDSException.hh"
25#include "BDSFieldLoaderBDSIM.hh"
26#include "BDSFieldValue.hh"
27#include "BDSUtilities.hh"
32#include "CLHEP/Units/SystemOfUnits.h"
46#include "src-external/gzstream/gzstream.h"
54 headerMustBePositiveKeys({
"nx",
"ny",
"nz",
"nt"}),
55 indexOfFirstFieldValue(0)
58 {BDSDimensionType::x, {
"nx",
"xmin",
"xmax"}},
59 {BDSDimensionType::y, {
"ny",
"ymin",
"ymax"}},
60 {BDSDimensionType::z, {
"nz",
"zmin",
"zmax"}},
61 {BDSDimensionType::t, {
"nt",
"tmin",
"tmax"}}
64 {BDSDimensionType::x, CLHEP::cm},
65 {BDSDimensionType::y, CLHEP::cm},
66 {BDSDimensionType::z, CLHEP::cm},
67 {BDSDimensionType::t, CLHEP::s},
81 std::vector<G4String> allKeys = {
"nx",
"ny",
"nz",
"nt",
82 "xmin",
"xmax",
"ymin",
"ymax",
83 "zmin",
"zmax",
"tmin",
"tmax"};
84 for (
auto& key : allKeys)
86 for (
auto& key : headerMustBePositiveKeys)
90 indexOfFirstFieldValue = 0;
130 const unsigned int nDim)
132 G4String functionName =
"BDSIM Field Format> ";
136 long long unsigned int currentLineNumber = 0;
139 bool validFile = file.rdbuf()->is_open();
141 bool validFile = file.is_open();
144 {
throw BDSException(__METHOD_NAME__,
"Invalid file name or no such file named \"" + fileName +
"\"");}
146 {G4cout << functionName <<
"Loading \"" << fileName <<
"\"" << G4endl;}
149 unsigned long xIndex = 0;
150 unsigned long yIndex = 0;
151 unsigned long zIndex = 0;
152 G4bool intoData =
false;
158 G4int n1 = 1, n2 = 1, n3 = 1, n4 = 1;
161 G4String nominalOrder =
"xyzt";
163 float maximumFieldValue = 0;
164 float minimumFieldValue = 0;
168 std::getline(file, line);
172 if (std::all_of(line.begin(), line.end(), isspace))
175 if (currentLineNumber > 100)
177 G4String msg = functionName+
"did not encounter column specification line starting with '!' in the first 100 lines\n";
178 msg +=
"This does not appear to be a BDSIM-format field map.";
192 std::smatch matchHeaderNumber;
193 std::regex keyValue(R
"((\w*)\s*>\s*([0-9eE.+-]+))");
194 if (std::regex_search(line, matchHeaderNumber, keyValue))
196 if (matchHeaderNumber.size() < 2)
197 {Terminate(functionName+
"Invalid key definition in field format: " + line);}
200 G4String key = G4String(matchHeaderNumber[1]);
204 if (header.find(key) == header.end())
205 {Terminate(functionName+
"Invalid key \"" + key +
"\" in header");}
209 {value = std::stod(matchHeaderNumber[2]);}
210 catch (
const std::invalid_argument&)
211 {Terminate(functionName+
"Invalid argument " + std::string(matchHeaderNumber[2]));}
212 catch (
const std::out_of_range&)
213 {Terminate(functionName+
"Number out of range " + std::string(matchHeaderNumber[2]));}
215 if (std::find(headerMustBePositiveKeys.begin(), headerMustBePositiveKeys.end(), key) != headerMustBePositiveKeys.end())
218 {Terminate(functionName+
"Number of points in dimension must be greater than 0 -> see \"" + key +
"\"");}
226 std::smatch matchHeaderString;
230 std::regex keyWord(R
"((\w+)\s*>\s*([a-zA-Z]{1,4})\b\s*)");
231 if (std::regex_search(line, matchHeaderString, keyWord))
233 loopOrder = G4String(matchHeaderString[2]);
235 G4bool test1 = loopOrder ==
"xyzt";
236 G4bool test2 = loopOrder ==
"tzyx";
237 if (! (test1 || test2) )
238 {Terminate(functionName+
"loopOrder> header variable is not one of (\"xyzt\" or \"tzyx\")");}
245 std::regex columnRow(
"^\\s*!");
246 if (std::regex_search(line, columnRow))
250 std::regex afterExclamation(
"\\s*!\\s*(.+)");
252 std::regex_search(line, match, afterExclamation);
253 std::string restOfLine = match[1];
254 std::string columnName;
255 std::istringstream restOfLineSS(restOfLine);
256 std::vector<G4String> columnNames;
257 while (restOfLineSS >> columnName)
260 if (columnName.find(
"Fx") != std::string::npos)
261 {xIndex = nColumns;
continue;}
262 else if (columnName.find(
"Fy") != std::string::npos)
263 {yIndex = nColumns;
continue;}
264 else if (columnName.find(
"Fz") != std::string::npos)
265 {zIndex = nColumns;
continue;}
267 {columnNames.emplace_back(columnName);}
269 lineData.resize(nColumns + 1);
270 indexOfFirstFieldValue = std::min({xIndex, yIndex, zIndex});
272 for (
const auto& key : headerMustBePositiveKeys)
275 {Terminate(functionName+
"Number of points in dimension must be greater than 0 -> see \"" + key +
"\"");}
278 if (nColumns < (nDim + 3))
279 {Terminate(functionName+
"Too few columns for " + std::to_string(nDim) +
"D field loading");}
287 auto keys = dimKeyMap[firstDim];
288 double unit = dimUnitsMap[firstDim];
289 n1 = G4int(header[keys.number]);
291 header[keys.min] * unit,
292 header[keys.max] * unit,
300 auto fKeys = dimKeyMap[firstDim];
301 double fUnit = dimUnitsMap[firstDim];
302 auto sKeys = dimKeyMap[secondDim];
303 double sUnit = dimUnitsMap[secondDim];
304 n1 = G4int(header[fKeys.number]);
305 n2 = G4int(header[sKeys.number]);
307 header[fKeys.min] * fUnit,
308 header[fKeys.max] * fUnit,
309 header[sKeys.min] * sUnit,
310 header[sKeys.max] * sUnit,
320 auto fKeys = dimKeyMap[firstDim];
321 double fUnit = dimUnitsMap[firstDim];
322 auto sKeys = dimKeyMap[secondDim];
323 double sUnit = dimUnitsMap[secondDim];
324 auto tKeys = dimKeyMap[thirdDim];
325 double tUnit = dimUnitsMap[thirdDim];
326 n1 = G4int(header[fKeys.number]);
327 n2 = G4int(header[sKeys.number]);
328 n3 = G4int(header[tKeys.number]);
330 header[fKeys.min] * fUnit,
331 header[fKeys.max] * fUnit,
332 header[sKeys.min] * sUnit,
333 header[sKeys.max] * sUnit,
334 header[tKeys.min] * tUnit,
335 header[tKeys.max] * tUnit,
343 n1 = G4int(header[
"nx"]);
344 n2 = G4int(header[
"ny"]);
345 n3 = G4int(header[
"nz"]);
346 n4 = G4int(header[
"nt"]);
348 header[
"xmin"] * CLHEP::cm, header[
"xmax"] * CLHEP::cm,
349 header[
"ymin"] * CLHEP::cm, header[
"ymax"] * CLHEP::cm,
350 header[
"zmin"] * CLHEP::cm, header[
"zmax"] * CLHEP::cm,
351 header[
"tmin"] * CLHEP::s, header[
"tmax"] * CLHEP::s);
363 if (loopOrder ==
"tzyx")
365 for (G4int i = 0; i < n1; i++)
367 for (G4int j = 0; j < n2; j++)
369 for (G4int k = 0; k < n3; k++)
371 for (G4int l = 0; l < n4; l++)
373 if (!std::getline(file, line))
374 {Terminate(functionName +
"unexpected end to file on line number " + std::to_string(currentLineNumber));}
376 if (std::all_of(line.begin(), line.end(), isspace))
378 ProcessData(line, xIndex, yIndex, zIndex);
379 (*result)(i, j, k, l) = fv;
380 float mag = fv.mag();
381 maximumFieldValue = std::max(maximumFieldValue, mag);
382 minimumFieldValue = std::min(minimumFieldValue, mag);
390 for (G4int l = 0; l < n4; l++)
392 for (G4int k = 0; k < n3; k++)
394 for (G4int j = 0; j < n2; j++)
396 for (G4int i = 0; i < n1; i++)
398 if (!std::getline(file, line))
399 {Terminate(functionName +
"unexpected end to file on line number " + std::to_string(currentLineNumber));}
401 if (std::all_of(line.begin(), line.end(), isspace))
403 ProcessData(line, xIndex, yIndex, zIndex);
404 (*result)(i, j, k, l) = fv;
405 float mag = fv.mag();
406 maximumFieldValue = std::max(maximumFieldValue, mag);
407 minimumFieldValue = std::min(minimumFieldValue, mag);
415 G4cout << functionName <<
"Loaded " << currentLineNumber <<
" lines from file" << G4endl;
416 G4cout << functionName <<
"(Min | Max) field magnitudes in loaded file (before scaling): (" << minimumFieldValue <<
" | " << maximumFieldValue <<
")" << G4endl;
421 const unsigned long xIndex,
422 const unsigned long yIndex,
423 const unsigned long zIndex)
425 std::istringstream liness(line);
427 std::fill(lineData.begin(), lineData.end(), 0);
430 for (
unsigned long i = 1; i < nColumns+1; ++i)
433 if (i < indexOfFirstFieldValue)
434 {value *= CLHEP::cm;}
1D array with spatial mapping derived from BDSArray4DCoords.
2D array with spatial mapping derived from BDSArray4DCoords.
3D array with spatial mapping derived from BDSArray4DCoords.
Overlay of 4D array that provides uniform only spatial coordinate mapping.
General exception with possible name of object and message.
Loader for BDSIM format fields.
BDSArray3DCoords * Load3D(const G4String &fileName)
Load a 3D array.
void CleanUp()
Ensure any member variables are reset between usages.
BDSArray4DCoords * Load4D(const G4String &fileName)
Load a 4D array.
BDSArray1DCoords * Load1D(const G4String &fileName)
Load a 1D array.
void Terminate(const G4String &message="")
Close file and exit program in case of an error.
void Load(const G4String &fileName, const unsigned int nDim)
General loader for any number of dimensions.
void ProcessData(const std::string &line, const unsigned long xIndex, const unsigned long yIndex=0, const unsigned long zIndex=0)
BDSArray2DCoords * Load2D(const G4String &fileName)
Load a 2D array.
BDSDimensionType DetermineDimensionType(G4String dimensionType)
Determine the output format to be used from the input string.
G4String LowerCase(const G4String &str)
Utility function to simplify lots of syntax changes for pedantic g4 changes.