BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
BDSBunchUserFile.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2022.
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 "BDSBunchUserFile.hh"
20#include "BDSDebug.hh"
21#include "BDSException.hh"
22#include "BDSGlobalConstants.hh"
23#include "BDSIonDefinition.hh"
24#include "BDSParticleDefinition.hh"
25#include "BDSUtilities.hh"
26#include "BDSWarning.hh"
27
28#include "parser/beam.h"
29
30#include "G4IonTable.hh"
31#include "G4ParticleDefinition.hh"
32#include "G4ParticleTable.hh"
33#include "G4String.hh"
34
35#ifdef USE_GZSTREAM
36#include "src-external/gzstream/gzstream.h"
37#endif
38
39#include <algorithm>
40#include <fstream>
41#include <regex>
42#include <set>
43#include <string>
44#include <sstream>
45#include <vector>
46
47template <class T>
49 BDSBunch("user file"),
50 distrFile(""),
51 distrFilePath(""),
52 bunchFormat(""),
53 nlinesIgnore(0),
54 nlinesSkip(0),
55 particleMass(0),
56 lineCounter(0),
57 printedOutFirstTime(false),
58 anEnergyCoordinateInUse(false),
59 changingParticleType(false),
60 matchDistrFileLength(false)
61{
62 ffact = BDSGlobalConstants::Instance()->FFact();
63}
64
65template<class T>
67{
69 if (distrFile.empty())
70 {throw BDSException("BDSBunchUserFile::CheckParameters", "No input file specified for userfile distribution");}
71}
72
73template<class T>
75{
76 CloseBunchFile();
77}
78
79template<class T>
81{
82 if (!printedOutFirstTime)
83 {
84 G4cout << "BDSBunchUserFile::OpenBunchFile> opening " << distrFilePath << G4endl;
85 printedOutFirstTime = true;
86 }
87 lineCounter = 0;
88 InputBunchFile.open(distrFilePath);
89 if (!InputBunchFile.good())
90 {throw BDSException("BDSBunchUserFile::OpenBunchFile>", "Cannot open bunch file " + distrFilePath);}
91}
92
93template<class T>
95{
96 InputBunchFile.clear(); // igzstream doesn't reset eof flags when closing - do manually
97 InputBunchFile.close();
98}
99
100template<class T>
102{
104
105 std::regex colons(":");
106 std::vector<std::string> results;
107 std::sregex_token_iterator iter(bunchFormat.begin(), bunchFormat.end(), colons, -1);
108 std::sregex_token_iterator end;
109 for (; iter != end; ++iter)
110 {
111 std::string res = (*iter).str();
112 results.push_back(res);
113 }
114 for (auto const& token : results)
115 {
116 G4String vari = "";
117 G4String rest = "";
118 if(token.substr(0,1)=="E" || token.substr(0,1)=="P")
119 {
121 if(token.substr(0,2)=="Ek")
122 {//Kinetic energy (longer name).
123 vari = token.substr(0,2);
124 rest = token.substr(2);
125 }
126 else
127 {
128 vari = token.substr(0,1);
129 rest = token.substr(1);
130 }
131 CheckAndParseUnits(vari, rest, BDS::ParseEnergyUnit);
132 }
133 else if(token.substr(0,1)=="t")
134 {
135 vari = token.substr(0, 1);
136 rest = token.substr(1);
137 CheckAndParseUnits(vari, rest, BDS::ParseTimeUnit);
138 }
139 else if( ( token.substr(0,1)=="x" && token.substr(1,1)!="p" ) ||
140 ( token.substr(0,1)=="y" && token.substr(1,1)!="p" ) ||
141 ( token.substr(0,1)=="z" && token.substr(1,1)!="p" ) )
142 {
143 vari = token.substr(0,1);
144 rest = token.substr(1);
145 CheckAndParseUnits(vari, rest, BDS::ParseLengthUnit);
146 }
147 else if ( (token.substr(0,2)=="xp") ||
148 (token.substr(0,2)=="yp") ||
149 (token.substr(0,2)=="zp") )
150 {
151 vari = token.substr(0,2);
152 rest = token.substr(2);
153 CheckAndParseUnits(vari, rest, BDS::ParseAngleUnit);
154 }
155 else if (token.substr(0, 1) == "S")
156 {
157 vari = token.substr(0, 1);
158 rest = token.substr(1);
159 CheckAndParseUnits(vari, rest, BDS::ParseLengthUnit);
160 useCurvilinear = true;
161 }
162 else if(token.substr(0,5)=="pdgid")
163 {
165 sd.name="pdgid";
166 sd.unit=1;
167 fields.push_back(sd);
168 }
169 else if(token.substr(0,1)=="w")
170 {
171 sd.name="weight";
172 sd.unit=1;
173 fields.push_back(sd);
174 }
175 else if (token.substr(0,1)=="-")
176 {
177 // skip
178 sd.name="skip";
179 sd.unit=1;
180 fields.push_back(sd);
181 }
182 else
183 {
184 G4String message = "Cannot determine bunch data format. Failed at token: " + token;
185 throw BDSException(__METHOD_NAME__, message);
186 }
187 }
188
189 std::set<G4String> energyKeys = {"E", "Ek", "P"};
190 CheckConflictingParameters(energyKeys);
191
192 std::set<G4String> zKeys = {"z", "S"};
194}
195
196template <typename T>
197void BDSBunchUserFile<T>::CheckConflictingParameters(const std::set<G4String>& s) const
198{
199 auto inSet = [=](const G4String& v){return s.find(v) != s.end();};
200 auto inSetD = [=](const BDSBunchUserFile::Doublet& v){return inSet(v.name);};
201 int count = std::count_if(fields.begin(), fields.end(), inSetD);
202 if (count > 1)
203 {
204 G4String message = "More than one of the following set in user file columns (\"distrFileFormat\") ";
205 for (const auto& st : s)
206 {
207 message += st;
208 message += ", ";
209 }
210 message += "\nPossibly conflicting information. Ensure only one by skipping others with \"-\" symbol";
211 throw BDSException("BDSBunchUserFile::CheckConflictingParameters>", message);
212 }
213}
214
215template <typename T>
216template <typename U>
217void BDSBunchUserFile<T>::CheckAndParseUnits(const G4String& uName,
218 const G4String& rest,
219 U unitParser)
220{
222 if (rest.empty())
223 {
224 sd.name = uName;
225 sd.unit = 1.0;
226 fields.push_back(sd);
227 return;
228 }
229
230 G4int pos1 = rest.find("[");
231 G4int pos2 = rest.find("]");
232 if (pos1 < 0 || pos2 < 0)
233 {
234 G4String message = "Missing bracket [] in units of \"distrFileFormat\"\n";
235 message += "variable : \"" + uName + "\" and unit \"" + rest + "\"";
236 throw BDSException("BDSBunchUserFile::CheckAndParseUnits>", message);
237 }
238 else
239 {
240 G4String fmt = rest.substr(pos1 + 1, pos2 - 1);
241 sd.name = uName;
242 sd.unit = unitParser(fmt);
243 fields.push_back(sd);
244 }
245}
246
247template<class T>
248void BDSBunchUserFile<T>::skip(std::stringstream& ss, G4int nValues)
249{
250 G4double dummyValue;
251 for (G4int i = 0; i < nValues; i++)
252 {ReadValue(ss, dummyValue);}
253}
254
255template<class T>
257{
259 {
260 G4cout << "BDSBunchUserFile> ignoring " << nlinesIgnore << ", skipping "
261 << nlinesSkip << " lines" << G4endl;
262 std::string line;
263 for (G4int i = 0; i < nlinesIgnore + nlinesSkip; i++)
264 {
265 std::getline(InputBunchFile, line);
266 lineCounter++;
267 }
268 }
269}
270
271template<class T>
273 const GMAD::Beam& beam,
274 const BDSBunchType& distrType,
275 G4Transform3D beamlineTransformIn,
276 const G4double beamlineSIn)
277{
278 BDSBunch::SetOptions(beamParticle, beam, distrType, beamlineTransformIn, beamlineSIn);
279 particleMass = beamParticle->Mass();
280 distrFile = beam.distrFile;
284 nlinesSkip = beam.nlinesSkip;
285 matchDistrFileLength = beam.matchDistrFileLength;
287}
288
289template<class T>
291{
293 SkipLines();
294
295 G4int numLines = 0;
296 std::string line;
297 std::regex comment("^\\#.*");
298 while ( std::getline(InputBunchFile, line) )
299 {
300 if (std::all_of(line.begin(), line.end(), isspace) || std::regex_search(line, comment))
301 {continue;}
302 ++numLines;
303 }
305 return numLines;
306}
307
308template<class T>
310{
311 G4bool nGenerateHasBeenSet = BDSGlobalConstants::Instance()->NGenerateSet();
312 if (matchDistrFileLength && !nGenerateHasBeenSet)
313 {
314 G4int nGenerate = CountLinesInFile();
316 G4cout << "BDSBunchUserFile::Initialise> matchDistrFileLength is True -> simulation " << nGenerate << " events" << G4endl;
317 }
319 SkipLines();
320}
321
322template<class T>
324{
325 // If the end of the file is reached go back to the beginning of the file.
326 // this re reads the same file again - must always print warning
327 G4cout << "BDSBunchUserFile> End of file reached. Returning to beginning of file for next event." << G4endl;
330 SkipLines();
331}
332
333template<class T>
335{
336 G4cout << "BDSBunchUserFile::RecreateAdvanceToEvent> Advancing file to event: " << eventOffset << G4endl;
337 if (InputBunchFile.eof())
338 {EndOfFileAction();}
339 std::string line;
340 for (G4int i = 0; i < eventOffset; i++)
341 {
342 std::getline(InputBunchFile, line);
343 lineCounter++;
344 if (InputBunchFile.eof())
345 {EndOfFileAction();}
346 }
347}
348
349template<class T>
351{
352 // no looping - just read one particle from file
353 return GetNextParticle();
354}
355
356template<class T>
358{
359 if (InputBunchFile.eof())
360 {EndOfFileAction();}
361
362 G4double E = 0, Ek = 0, P = 0, x = 0, y = 0, z = 0, xp = 0, yp = 0, zp = 0, t = 0;
363 G4double weight = 1;
364 G4int type = 0;
365
366 G4bool zpdef = false; //keeps record whether zp has been read from file
367 G4bool tdef = false; //keeps record whether t has been read from file
368
369 // flag whether we're going to update the particle definition
370 G4bool updateParticleDefinition = false;
371
372 // read a whole line at a time for safety - no partially read lines
373 std::string line;
374 std::getline(InputBunchFile, line);
375 lineCounter++;
376
377 // skip empty lines and comment lines (starting with # or !)
378 std::regex comment("^\\s*\\#|\\!.*");
379 G4bool lineIsBad = true;
380 while (lineIsBad)
381 {
382 if (std::all_of(line.begin(), line.end(), isspace) || std::regex_search(line, comment))
383 {
384 if (InputBunchFile.eof())
385 {EndOfFileAction();}
386 else
387 {
388 std::getline(InputBunchFile, line);
389 lineCounter++;
390 continue;
391 }
392 }
393 else
394 {lineIsBad = false;}
395 }
396
397 // no check the line has the right number of 'words' in it ->
398 // split line on white space - doesn't inspect words themselves
399 // checks number of words, ie number of columns is correct
400 std::vector<std::string> results;
401 std::regex wspace("\\s+"); // any whitepsace
402 // -1 here makes it point to the suffix, ie the word rather than the wspace
403 std::sregex_token_iterator iter(line.begin(), line.end(), wspace, -1);
404 std::sregex_token_iterator end;
405 for (; iter != end; ++iter)
406 {
407 std::string res = (*iter).str();
408 results.push_back(res);
409 }
410
411 if (results.size() < fields.size())
412 {// ensure enough columns
413 std::string message = "Invalid line at line " + std::to_string(lineCounter) +
414 ". Expected " + std::to_string(fields.size()) +
415 " columns , but got " + std::to_string(results.size()) +
416 ".";
417 throw BDSException(__METHOD_NAME__, message);
418 }
419
420 std::stringstream ss(line);
421 for (auto it=fields.begin();it!=fields.end();it++)
422 {
423 if(it->name=="skip")
424 {double dummy; ReadValue(ss, dummy);}
425 else if(it->name=="Ek")
426 {ReadValue(ss, Ek); Ek *= (CLHEP::GeV * it->unit);}
427 else if(it->name=="E")
428 {ReadValue(ss, E); E *= (CLHEP::GeV * it->unit);}
429 else if(it->name=="P")
430 {ReadValue(ss, P); P *= (CLHEP::GeV * it->unit);}
431 else if(it->name=="t")
432 {ReadValue(ss, t); t *= (CLHEP::s * it->unit); tdef = true;}
433 else if(it->name=="x")
434 {ReadValue(ss, x); x *= (CLHEP::m * it->unit);}
435 else if(it->name=="y")
436 {ReadValue(ss, y); y *= (CLHEP::m * it->unit);}
437 else if(it->name=="z")
438 {ReadValue(ss, z); z *= (CLHEP::m * it->unit);}
439 else if(it->name=="xp") {ReadValue(ss, xp); xp *= ( CLHEP::radian * it->unit );}
440 else if(it->name=="yp") {ReadValue(ss, yp); yp *= ( CLHEP::radian * it->unit );}
441 else if(it->name=="zp") {ReadValue(ss, zp); zp *= ( CLHEP::radian * it->unit ); zpdef = true;}
442 else if(it->name=="pdgid")
443 {// particle type
444 ReadValue(ss, type);
445 updateParticleDefinition = true; // update particle definition after finished reading line
446 }
447 else if (it->name == "S")
448 {
449 ReadValue(ss, z);
450 z *= CLHEP::m * it->unit;
451 }
452 else if(it->name=="weight")
453 {ReadValue(ss, weight);}
454 }
455
456 // coordinate checks
458 {
460 {// no checking here that only one variable is set - must ensure in this class
463 }
464 else // we must update this call of this function to ensure valid particle definition
465 {updateParticleDefinition = true;}
466 }
467 else // If energy isn't specified, use the central beam energy
468 {E = E0;}
469
470 // compute zp from xp and yp if it hasn't been read from file
471 xp += Xp0;
472 yp += Yp0;
473 if (!zpdef)
474 {zp = CalculateZp(xp,yp,1);}
475 // compute t from z if it hasn't been read from file
476 if (!tdef)
477 {t=0;}
478
479 if (updateParticleDefinition || changingParticleType)
480 {
481 // type is an int so FindParticle(int) is used here
482 G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
483 G4ParticleDefinition* particleDef = nullptr;
484 BDSIonDefinition* ionDef = nullptr;
485 if (type < 1e9) // not a pdg ion
486 {particleDef = particleTable->FindParticle(type);}
487 else
488 {
489 G4IonTable* ionTable = particleTable->GetIonTable();
490 G4int ionA, ionZ, ionLevel;
491 G4double ionE;
492 G4IonTable::GetNucleusByEncoding(type, ionZ, ionA, ionE, ionLevel);
493 ionDef = new BDSIonDefinition(ionA, ionZ, ionZ);
494 particleDef = ionTable->GetIon(ionDef->Z(), ionDef->A(), ionDef->ExcitationEnergy());
495 }
496
497 if (!particleDef)
498 {throw BDSException("BDSBunchUserFile> Particle \"" + std::to_string(type) + "\" not found");}
499 // Wrap in our class that calculates momentum and kinetic energy.
500 // Requires that one of E, Ek, P be non-zero (only one).
501 delete particleDefinition;
502 try
503 {
504 particleDefinition = new BDSParticleDefinition(particleDef, E, Ek, P, ffact, ionDef);
507 }
508 catch (const BDSException& e)
509 {// if we throw an exception the object is invalid for the delete on the next loop
510 particleDefinition = nullptr; // reset back to nullptr for safe delete
511 throw e; // rethrow
512 }
513 }
514
515 return BDSParticleCoordsFull(X0+x,Y0+y,Z0+z,xp,yp,zp,t,z,E,weight);
516}
517
518template <class T>
519template <typename Type>
520void BDSBunchUserFile<T>::ReadValue(std::stringstream& stream, Type& value)
521{
522 stream >> value;
523}
524
526
527#ifdef USE_GZSTREAM
528template class BDSBunchUserFile<igzstream>;
529#endif
530
531namespace BDS
532{
533G4double ParseEnergyUnit(const G4String& fmt)
534{
535 G4double unit=1.;
536 if (fmt=="TeV") unit=1.e3;
537 else if(fmt=="GeV") unit=1;
538 else if(fmt=="MeV") unit=1.e-3;
539 else if(fmt=="KeV") unit=1.e-6;
540 else if(fmt=="eV") unit=1.e-9;
541 else
542 {throw BDSException(__METHOD_NAME__, "Unrecognised energy unit! " +fmt);}
543 return unit;
544}
545
546G4double ParseLengthUnit(const G4String& fmt)
547{
548 G4double unit=1.;
549 if(fmt=="m") unit=1;
550 else if(fmt=="cm") unit=1.e-2;
551 else if(fmt=="mm") unit=1.e-3;
552 else if(fmt=="mum" || fmt=="um") unit=1.e-6;
553 else if(fmt=="nm") unit=1.e-9;
554 else
555 {throw BDSException(__METHOD_NAME__, "Unrecognised length unit! " + fmt);}
556 return unit;
557}
558
559G4double ParseAngleUnit(const G4String& fmt)
560{
561 G4double unit=1.;
562 if(fmt=="rad") unit=1;
563 else if(fmt=="mrad") unit=1.e-3;
564 else if(fmt=="murad" || fmt=="urad") unit=1.e-6;
565 else if(fmt=="nrad") unit=1.e-9;
566 else
567 {throw BDSException(__METHOD_NAME__, "Unrecognised angle unit! " + fmt);}
568 return unit;
569}
570
571G4double ParseTimeUnit(const G4String& fmt)
572{
573 G4double unit=1.;
574 if(fmt=="s") unit=1;
575 else if(fmt=="ms") unit=1.e-3;
576 else if(fmt=="mus" || fmt=="us") unit=1.e-6;
577 else if(fmt=="ns") unit=1.e-9;
578 else if(fmt=="mm/c") unit=(CLHEP::mm/CLHEP::c_light)/CLHEP::s;
579 else if(fmt=="nm/c") unit=(CLHEP::nm/CLHEP::c_light)/CLHEP::s;
580 else
581 {throw BDSException(__METHOD_NAME__, "Unrecognised time unit! " + fmt);}
582 return unit;
583}
584
585}
A bunch distribution that reads a user specified column file.
void CheckConflictingParameters(const std::set< G4String > &s) const
Check conflicting columns aren't specified in file, e.g. P and Ek. Throw exception if wrong.
virtual void RecreateAdvanceToEvent(G4int eventOffset)
G4double ffact
Cache of flip factor from global constants.
void ReadValue(std::stringstream &stream, Type &value)
void SkipLines()
Read lines according to nlinesIgnore.
G4int lineCounter
Line counter.
G4int nlinesIgnore
Number of lines that will be ignored at the start the file.
G4int CountLinesInFile()
Open the file, skip lines, then count number of lines, then close file again.
T InputBunchFile
The file handler. Templated as could be std::ifstream or igzstream for example.
G4int nlinesSkip
Number of lines that will be skipped after the nlinesIgnore.
virtual BDSParticleCoordsFullGlobal GetNextParticleValid(G4int maxTries)
G4bool changingParticleType
Whether the particle type is a column.
void skip(std::stringstream &stream, G4int nvalues)
void ParseFileFormat()
Parse the column tokens and units factors.
virtual void SetOptions(const BDSParticleDefinition *beamParticle, const GMAD::Beam &beam, const BDSBunchType &distrType, G4Transform3D beamlineTransformIn=G4Transform3D::Identity, const G4double beamlineS=0)
G4String distrFilePath
Bunch file including absolute path.
G4String bunchFormat
Format of the file.
G4double particleMass
Cache of nominal beam particle mass.
std::list< Doublet > fields
List of variables to parse on each line.
void CloseBunchFile()
Close the file handler.
virtual void CheckParameters()
virtual BDSParticleCoordsFull GetNextParticleLocal()
Get the next particle.
virtual void Initialise()
Open the file and skip lines.
G4String distrFile
Bunch file.
void OpenBunchFile()
Open the file and check it's open.
G4bool anEnergyCoordinateInUse
Whether Et, Ek or P are in the columns.
The base class for bunch distribution generators.
Definition: BDSBunch.hh:47
G4double Yp0
Centre of distributions.
Definition: BDSBunch.hh:166
G4bool particleDefinitionHasBeenUpdated
Definition: BDSBunch.hh:185
G4bool useCurvilinear
Whether to ignore z and use s and transform for curvilinear coordinates.
Definition: BDSBunch.hh:178
G4double Z0
Centre of distributions.
Definition: BDSBunch.hh:162
G4double X0
Centre of distributions.
Definition: BDSBunch.hh:160
G4double Xp0
Centre of distributions.
Definition: BDSBunch.hh:165
G4double E0
Centre of distributions.
Definition: BDSBunch.hh:168
G4double Y0
Centre of distributions.
Definition: BDSBunch.hh:161
virtual void SetOptions(const BDSParticleDefinition *beamParticle, const GMAD::Beam &beam, const BDSBunchType &distrType, G4Transform3D beamlineTransformIn=G4Transform3D::Identity, const G4double beamlineS=0)
Definition: BDSBunch.cc:78
BDSParticleCoordsFullGlobal GetNextParticle()
Definition: BDSBunch.cc:234
static G4double CalculateZp(G4double xp, G4double yp, G4double Zp0)
Calculate zp safely based on other components.
Definition: BDSBunch.cc:317
BDSParticleDefinition * particleDefinition
Particle definition for bunch - this class owns it.
Definition: BDSBunch.hh:181
virtual void CheckParameters()
Definition: BDSBunch.cc:195
General exception with possible name of object and message.
Definition: BDSException.hh:35
void SetNumberToGenerate(G4int number)
Setter.
static BDSGlobalConstants * Instance()
Access method.
Class to parse an ion particle definition.
G4int A() const
Accessor.
G4double ExcitationEnergy() const
Accessor.
G4int Z() const
Accessor.
A set of particle coordinates in both local and global.
A set of particle coordinates including energy and weight.
Wrapper for particle definition.
G4double Mass() const
Accessor.
void SetEnergies(G4double totalEnergyIn, G4double kineticEnergyIn, G4double momentumIn)
G4double TotalEnergy() const
Accessor.
Improve type-safety of native enum data type in C++.
bool matchDistrFileLength
beam parameters
Definition: beamBase.h:55
std::string distrFileFormat
beam parameters
Definition: beamBase.h:53
std::string distrFile
beam parameters
Definition: beamBase.h:52
int nlinesIgnore
Ignore first lines in the input bunch file.
Definition: beamBase.h:58
int nlinesSkip
Number of event lines to skip after the ignore lines.
Definition: beamBase.h:59
Beam class.
Definition: beam.h:44
Return either G4Tubs or G4CutTubs depending on flat face.
G4String GetFullPath(G4String filename, bool excludeNameFromPath=false, bool useCWDForPrefix=false)
G4double ParseTimeUnit(const G4String &fmt)
G4double ParseEnergyUnit(const G4String &fmt)
G4double ParseAngleUnit(const G4String &fmt)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
G4double ParseLengthUnit(const G4String &fmt)
Struct for name and unit pair.
G4double unit
relative to SI units, i.e. mm=0.001 etc.