BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSBunch.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 "BDSAcceleratorModel.hh"
19#include "BDSBeamline.hh"
20#include "BDSBunch.hh"
21#include "BDSDebug.hh"
22#include "BDSException.hh"
23#include "BDSGlobalConstants.hh"
24#include "BDSIonDefinition.hh"
25#include "BDSParticleCoords.hh"
26#include "BDSParticleCoordsFull.hh"
27#include "BDSParticleCoordsFullGlobal.hh"
28#include "BDSParticleDefinition.hh"
29#include "BDSPhysicsUtilities.hh"
30#include "BDSUtilities.hh"
31
32#include "parser/beam.h"
33
34#include "G4IonTable.hh"
35#include "G4ParticleTable.hh"
36#include "G4ThreeVector.hh"
37#include "G4Transform3D.hh"
38#include "G4TwoVector.hh"
39#include "G4Version.hh"
40
41#include "CLHEP/Geometry/Point3D.h"
42
43#include <cmath>
44#include <limits>
45#include <set>
46#include <string>
47
48
49BDSBunch::BDSBunch():
50 BDSBunch("reference")
51{;}
52
53BDSBunch::BDSBunch(const G4String& nameIn):
54 name(nameIn),
55 X0(0.0), Y0(0.0), Z0(0.0), S0(0.0), T0(0.0),
56 Xp0(0.0), Yp0(0.0), Zp0(0.0), E0(0.0), P0(0.0),
57 tilt(0.0),
58 sigmaT(0.0), sigmaP(0.0), sigmaE(0.0), sigmaEk(0.0),
59 useBunchTiming(false),
60 currentBunchIndex(0),
61 eventsPerBunch(1), // so we don't have 0 division
62 bunchPeriod(0),
63 useCurvilinear(false),
64 particleDefinition(nullptr),
65 particleDefinitionHasBeenUpdated(false),
66 finiteTilt(false),
67 finiteSigmaE(true),
68 finiteSigmaT(true),
69 generatePrimariesOnly(false),
70 beamlineTransform(G4Transform3D()),
71 beamlineS(0),
72 mass2(0.0),
73 beamline(nullptr)
74{;}
75
76BDSBunch::~BDSBunch()
77{
78 delete particleDefinition;
79}
80
82 const GMAD::Beam& beam,
83 const BDSBunchType& /*distrType*/,
84 G4Transform3D beamlineTransformIn,
85 G4double beamlineSIn)
86{
87 particleDefinition = new BDSParticleDefinition(*beamParticle); // copy it so this instance owns it
88
89 // back the starting point up by length safety to avoid starting on a boundary
90 G4ThreeVector unitZBeamline = G4ThreeVector(0,0,-1).transform(beamlineTransformIn.getRotation());
91 G4ThreeVector translation = BDSGlobalConstants::Instance()->LengthSafety() * unitZBeamline;
92 beamlineTransform = G4Transform3D(beamlineTransformIn.getRotation(), beamlineTransformIn.getTranslation()+translation);
93
94 beamlineS = beamlineSIn;
95
96 X0 = beam.X0 * CLHEP::m;
97 Y0 = beam.Y0 * CLHEP::m;
98 Z0 = beam.Z0 * CLHEP::m;
99 S0 = beam.S0 * CLHEP::m;
100 T0 = beam.T0 * CLHEP::s;
101 Xp0 = beam.Xp0 * CLHEP::rad;
102 Yp0 = beam.Yp0 * CLHEP::rad;
103 E0 = particleDefinition->TotalEnergy(); // already calculated and set earlier depending on available parameters
105 tilt = beam.tilt * CLHEP::rad;
106 sigmaT = beam.sigmaT;
107 sigmaP = beam.sigmaP;
108 sigmaE = beam.sigmaE;
109 sigmaEk = beam.sigmaEk;
110
111 // bunch offset timing parameters
112 G4bool bff = BDS::IsFinite(beam.bunchFrequency);
113 G4bool bpf = BDS::IsFinite(beam.bunchPeriod);
114 if (bff && bpf)
115 {throw BDSException(__METHOD_NAME__, "only one of \"bunchFrequency\" and \"bunchPeriod\" can be set");}
116 else if (bff || bpf)
117 {
118 useBunchTiming = true;
120 if (eventsPerBunch <= 0)
121 {throw BDSException(__METHOD_NAME__, "\"eventsPerBunch\" <= 0. Must be > 0");}
122 bunchPeriod = bpf ? beam.bunchPeriod*CLHEP::s : (1.0 / beam.bunchFrequency)*CLHEP::s;
123 }
124 else if (!bff && !bpf && beam.eventsPerBunch > 0) // eventsPerBunch > 0 implies expecting bunches but no frequency or period given
125 {throw BDSException(__METHOD_NAME__, "one of \"bunchFrequency\" or \"bunchPeriod\" must be specified if \"eventsPerBunch\" > 0");}
126
130 G4bool finiteSigmaP = BDS::IsFinite(sigmaP);
131 G4bool finiteSigmaEk = BDS::IsFinite(sigmaEk);
132
133 std::set<std::string> keysDesign = {"sigmaE", "sigmaEk", "sigmaP"};
134 G4int nSetDesign = BDS::NBeamParametersSet(beam, keysDesign);
135 BDS::ConflictingParametersSet(beam, keysDesign, nSetDesign, false, "GeV");// warn only if too many set
136 if (finiteSigmaE)
137 {
138 sigmaP = (1./std::pow(beamParticle->Beta(),2)) * sigmaE; // dE/E = (beta^2) dP/P
139 sigmaEk = (beamParticle->TotalEnergy() / beamParticle->KineticEnergy()) * sigmaE;
140 }
141 else if (finiteSigmaP)
142 {
143 sigmaE = std::pow(beamParticle->Beta(),2) * sigmaP;
144 sigmaEk = (beamParticle->TotalEnergy() / beamParticle->KineticEnergy()) * sigmaE;
145 }
146 else if (finiteSigmaEk)
147 {
148 sigmaE = sigmaEk * (beamParticle->KineticEnergy() / beamParticle->TotalEnergy());
149 sigmaP = (1./std::pow(beamParticle->Beta(),2)) * sigmaE; // dE/E = (beta^2) dP/P
150 }
151 // else they'll all be 0 - no need for a calculation
152
153 finiteSigmaE = finiteSigmaE || finiteSigmaP || finiteSigmaEk; // finiteSigmaE used to know whether any variation in other classes
154 if (finiteSigmaE)
155 {
156 G4cout << "Beam> sigmaP: " << sigmaP << G4endl;
157 G4cout << "Beam> sigmaE: " << sigmaE << G4endl;
158 G4cout << "Beam> sigmaEk: " << sigmaEk << G4endl;
159 }
160
161 Zp0 = CalculateZp(Xp0,Yp0,beam.Zp0);
162
163 if (S0 > beamlineS)
164 {
165#ifdef BDSDEBUG
166 G4cout << __METHOD_NAME__ << "using curvilinear transform" << G4endl;
167#endif
168 if (BDS::IsFinite(Z0))
169 {throw BDSException(__METHOD_NAME__, "both Z0 and S0 are defined - please define only one!");}
170 useCurvilinear = true;
171 }
172}
173
175 const GMAD::Beam& beam,
176 G4double& emittGeometricX,
177 G4double& emittGeometricY,
178 G4double& emittNormalisedX,
179 G4double& emittNormalisedY)
180{
181 std::set<std::string> keysDesignX = {"emitx", "emitnx"};
182 G4int nSetDesignX = BDS::NBeamParametersSet(beam, keysDesignX);
183 BDS::ConflictingParametersSet(beam, keysDesignX, nSetDesignX);
184 if (BDS::IsFinite(beam.emitNX))
185 {
186 emittNormalisedX = G4double(beam.emitNX);
187 emittGeometricX = G4double(beam.emitNX) / beamParticle->Gamma();
188 }
189 else
190 {
191 emittGeometricX = G4double(beam.emitx);
192 emittNormalisedX = G4double(beam.emitx) * beamParticle->Gamma();
193 }
194
195 std::set<std::string> keysDesignY = {"emity", "emitny"};
196 G4int nSetDesignY = BDS::NBeamParametersSet(beam, keysDesignY);
197 BDS::ConflictingParametersSet(beam, keysDesignY, nSetDesignY);
198 if (BDS::IsFinite(beam.emitNY))
199 {
200 emittNormalisedY = G4double(beam.emitNY);
201 emittGeometricY = G4double(beam.emitNY) / beamParticle->Gamma();}
202 else
203 {
204 emittGeometricY = G4double(beam.emity);
205 emittNormalisedY = G4double(beam.emity) * beamParticle->Gamma();
206 }
207
208 G4cout << __METHOD_NAME__ << "Geometric (x): " << emittGeometricX
209 << ", Normalised (x): " << emittNormalisedX << G4endl;
210 G4cout << __METHOD_NAME__ << "Geometric (y): " << emittGeometricY
211 << ", Normalised (y): " << emittNormalisedY << G4endl;
212}
213
214void BDSBunch::CalculateBunchIndex(G4int eventIndex)
215{
216 if (!useBunchTiming)
217 {return;}
218 // calculate this freshly each time so it doesn't rely on incremental event indices
219 currentBunchIndex = (G4int)std::floor((G4double)eventIndex / (G4double)eventsPerBunch);
220}
221
223{
224 if (sigmaE < 0)
225 {throw BDSException(__METHOD_NAME__, "sigmaE " + std::to_string(sigmaE) + " < 0!");}
226 if (sigmaT < 0)
227 {throw BDSException(__METHOD_NAME__, "sigmaT " + std::to_string(sigmaT) + " < 0!");}
228}
229
232
234{
235 particleDefinitionHasBeenUpdated = false; // reset flag for this call
236 // use a separate flag to record whether the particle definitions has
237 // been updated as subsequent calls to GetNextParticle may reset it to
238 // false but it was updated in the first call
239 G4bool flag = false;
240
241 // continue generating particles until positive finite kinetic energy.
242 G4int n = 0;
244 while (n < maxTries) // prevent infinite loops
245 {
246 ++n;
247 coords = GetNextParticle();
249
250 // ensure total energy is greater than the rest mass
251 if ((coords.local.totalEnergy - particleDefinition->Mass()) > 0)
252 {break;}
253 }
254 if (n >= maxTries)
255 {throw BDSException(__METHOD_NAME__, "unable to generate coordinates above rest mass after 100 attempts.");}
256
258 return coords;
259}
260
270
272{
274 Xp0, Yp0, Zp0,
275 T0, S0, E0, /*weight=*/1.0);
276 return local;
277}
278
279void BDSBunch::RecreateAdvanceToEvent(G4int eventOffset)
280{
281 CalculateBunchIndex(eventOffset);
282}
283
284void BDSBunch::BeginOfRunAction(G4int /*numberOfEvents*/,
285 G4bool /*batchMode*/)
286{;}
287
288void BDSBunch::SetGeneratePrimariesOnly(G4bool generatePrimariesOnlyIn)
289{generatePrimariesOnly = generatePrimariesOnlyIn;}
290
292{
294 if (useCurvilinear) // i.e. S0 is finite - use beam line
295 {result = ApplyCurvilinearTransform(localIn);}
296 else // just general beam line offset
298 if (useBunchTiming)
299 {ApplyBunchTiming(result);}
300
301 // up until now the S0 is the S to offset the particle from the start of the
302 // line by finding that coordinate on the line. S0 = 0 means the same initial
303 // S of the beamline whatever that is. Therefore, we must now add that on to
304 // the coordinate for output purposes after we are finished applying any
305 // transform to the particle.
306 result.local.s += beamlineS;
307 return result;
308}
309
311{
312 G4TwoVector xy(localIn.x, localIn.y);
313 G4TwoVector xpyp(localIn.xp, localIn.yp);
314 xy.rotate(tilt);
315 xpyp.rotate(tilt);
316 localIn.x = xy.x();
317 localIn.y = xy.y();
318 localIn.xp = xpyp.x();
319 localIn.yp = xpyp.y();
320}
321
323{
324 G4double tOffset = ((G4double)currentBunchIndex) * bunchPeriod;
325 coordsIn.global.T += tOffset;
326}
327
329{
330 if (generatePrimariesOnly) // no beam line built so no possible transform
331 {return BDSParticleCoordsFullGlobal(localIn, (BDSParticleCoords)localIn);}
332
333 if (!beamline)
334 {// initialise cache of beam line pointer
335 beamline = BDSAcceleratorModel::Instance()->BeamlineMain();
336 if (!beamline)
337 {throw BDSException(__METHOD_NAME__, "no beamline constructed!");}
338 }
339
340 // 'c' for curvilinear
341 G4int beamlineIndex = 0;
342 G4double S = S0 + localIn.z;
343 G4double sMin = beamline->GetSMinimum();
344 G4double sMax = beamline->GetSMaximum();
345 if (S < sMin - 1*CLHEP::m)
346 {throw BDSException(__METHOD_NAME__, "S less than minimum S (" + std::to_string(sMin) + ") - 1m for particle.");}
347 if (S > sMax + 1*CLHEP::m)
348 {throw BDSException(__METHOD_NAME__, "S greater than maximum S (" + std::to_string(sMax) + ") + 1m for particle.");}
349
350 G4Transform3D cTrans = beamline->GetGlobalEuclideanTransform(S,
351 localIn.x,
352 localIn.y,
353 &beamlineIndex);
354 // rotate the momentum vector
355 G4ThreeVector cMom = G4ThreeVector(localIn.xp, localIn.yp, localIn.zp).transform(cTrans.getRotation());
356 // translation contains displacement from origin already - including any local offset
357 G4ThreeVector cPos = cTrans.getTranslation();
358
359 G4double tOffset = S / CLHEP::c_light; // we assume the velocity of light for timing
360
361 BDSParticleCoords global = BDSParticleCoords(cPos.x(), cPos.y(), cPos.z(),
362 cMom.x(), cMom.y(), cMom.z(),
363 localIn.T + tOffset);
364
366 result.beamlineIndex = beamlineIndex;
367#ifdef BDSDEBUG
368 G4cout << __METHOD_NAME__ << result << G4endl;
369#endif
370 return result;
371}
372
373G4double BDSBunch::CalculateZp(G4double xp, G4double yp, G4double Zp0In)
374{
375 G4double zp;
376 G4double transMom = std::pow(xp, 2) + std::pow(yp, 2);
377
378 if (transMom > (1 - std::numeric_limits<double>::epsilon()))
379 {throw BDSException(__METHOD_NAME__, "xp, yp too large, xp: " + std::to_string(xp) + " yp: " + std::to_string(yp));}
380 if (Zp0In < 0)
381 {zp = -std::sqrt(1.0 - transMom);}
382 else
383 {zp = std::sqrt(1.0 - transMom);}
384
385 return zp;
386}
387
389{
391 {return;}
392
393 G4IonTable* ionTable = G4ParticleTable::GetParticleTable()->GetIonTable();
395 G4ParticleDefinition* ionParticleDef = ionTable->GetIon(ionDefinition->Z(),
396 ionDefinition->A(),
397 ionDefinition->ExcitationEnergy());
399 // Note we don't need to take care of electrons here. These are automatically
400 // allocated by Geant4 when it converts the primary vertex to a dynamic particle
401 // (in the process of constructing a track from it) (done in G4PrimaryTransformer)
402 // this relies on the charge being set correctly - Geant4 detects this isn't the same
403 // as Z and adds electrons accordingly.
404#if G4VERSION_NUMBER > 1049
405 // in the case of ions the particle definition is only available now
406 // fix the looping thresholds now it's available
408#endif
409}
const BDSBeamline * BeamlineMain() const
Accessor.
G4Transform3D GetGlobalEuclideanTransform(G4double s, G4double x=0, G4double y=0, G4int *indexOfFoundElement=nullptr) const
The base class for bunch distribution generators.
Definition BDSBunch.hh:46
BDSParticleCoordsFullGlobal ApplyCurvilinearTransform(const BDSParticleCoordsFull &localIn) const
Calculate the global coordinates from curvilinear coordinates of a beam line.
Definition BDSBunch.cc:328
G4double sigmaE
Centre of distributions.
Definition BDSBunch.hh:183
G4bool finiteTilt
Definition BDSBunch.hh:204
virtual void BeginOfRunAction(G4int numberOfEvents, G4bool batchMode)
Definition BDSBunch.cc:284
const BDSBeamline * beamline
A reference to the fully constructed beamline that's lazily instantiated.
Definition BDSBunch.hh:222
bool useBunchTiming
Bunch offset in time parameters.
Definition BDSBunch.hh:188
G4double Yp0
Centre of distributions.
Definition BDSBunch.hh:176
G4double T0
Centre of distributions.
Definition BDSBunch.hh:174
G4bool finiteSigmaE
Flags to ignore random number generator in case of no finite E or T.
Definition BDSBunch.hh:206
G4double tilt
Centre of distributions.
Definition BDSBunch.hh:180
BDSParticleCoordsFullGlobal ApplyTransform(const BDSParticleCoordsFull &localIn) const
Definition BDSBunch.cc:291
virtual void RecreateAdvanceToEvent(G4int eventOffset)
Definition BDSBunch.cc:279
void ApplyTilt(BDSParticleCoordsFull &localIn) const
Apply a rotation about unitZ for the local coordinates according to member variable tilt.
Definition BDSBunch.cc:310
G4double S0
Centre of distributions.
Definition BDSBunch.hh:173
G4int currentBunchIndex
Bunch offset in time parameters.
Definition BDSBunch.hh:189
G4double P0
central momentum
Definition BDSBunch.hh:179
G4bool particleDefinitionHasBeenUpdated
Definition BDSBunch.hh:202
G4bool generatePrimariesOnly
Definition BDSBunch.hh:212
virtual void SetGeneratePrimariesOnly(G4bool generatePrimariesOnlyIn)
Definition BDSBunch.cc:288
G4double sigmaP
Centre of distributions.
Definition BDSBunch.hh:182
G4bool useCurvilinear
Whether to ignore z and use s and transform for curvilinear coordinates.
Definition BDSBunch.hh:195
G4Transform3D beamlineTransform
Transform that beam line starts with that will also be applied to coordinates.
Definition BDSBunch.hh:216
void ApplyBunchTiming(BDSParticleCoordsFullGlobal &localIn) const
Add on the offset in T for the current bunch number (i*bunchPeriod).
Definition BDSBunch.cc:322
G4double sigmaT
Centre of distributions.
Definition BDSBunch.hh:181
G4double Z0
Centre of distributions.
Definition BDSBunch.hh:172
void CalculateBunchIndex(G4int eventIndex)
Calculate which bunch index we should be at given an event index.
Definition BDSBunch.cc:214
virtual BDSParticleCoordsFull GetNextParticleLocal()
Definition BDSBunch.cc:271
G4double bunchPeriod
Bunch offset in time parameters.
Definition BDSBunch.hh:191
G4double X0
Centre of distributions.
Definition BDSBunch.hh:170
G4double Zp0
Centre of distributions.
Definition BDSBunch.hh:177
virtual void Initialise()
Any initialisation - to be used after SetOptions, then CheckParameters.
Definition BDSBunch.cc:230
G4double Xp0
Centre of distributions.
Definition BDSBunch.hh:175
G4double E0
Centre of distributions.
Definition BDSBunch.hh:178
G4double sigmaEk
Centre of distributions.
Definition BDSBunch.hh:184
static void SetEmittances(const BDSParticleDefinition *beamParticle, const GMAD::Beam &beam, G4double &emittGeometricX, G4double &emittGeometricY, G4double &emittNormalisedX, G4double &emittNormalisedY)
Definition BDSBunch.cc:174
G4double Y0
Centre of distributions.
Definition BDSBunch.hh:171
virtual void SetOptions(const BDSParticleDefinition *beamParticle, const GMAD::Beam &beam, const BDSBunchType &distrType, G4Transform3D beamlineTransformIn=G4Transform3D::Identity, const G4double beamlineS=0)
Definition BDSBunch.cc:81
virtual void UpdateIonDefinition()
Definition BDSBunch.cc:388
G4double beamlineS
Beamline initial S position.
Definition BDSBunch.hh:218
BDSParticleCoordsFullGlobal GetNextParticle()
Definition BDSBunch.cc:261
G4bool finiteSigmaT
Flags to ignore random number generator in case of no finite E or T.
Definition BDSBunch.hh:207
static G4double CalculateZp(G4double xp, G4double yp, G4double Zp0)
Calculate zp safely based on other components.
Definition BDSBunch.cc:373
BDSParticleDefinition * particleDefinition
Particle definition for bunch - this class owns it.
Definition BDSBunch.hh:198
virtual BDSParticleCoordsFullGlobal GetNextParticleValid(G4int maxTries=100)
Definition BDSBunch.cc:233
G4int eventsPerBunch
Bunch offset in time parameters.
Definition BDSBunch.hh:190
virtual void CheckParameters()
Definition BDSBunch.cc:222
General exception with possible name of object and message.
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.
G4int beamlineIndex
Optional index for where transform was found.
A set of particle coordinates including energy and weight.
G4double s
TODO - remove this. Unused. S (global) is calculated from S0 + z.
A set of particle coordinates.
BDSParticleCoords ApplyTransform(const G4Transform3D &transform) const
Apply a transform to the coordinates and return a copy of them transformed.
Wrapper for particle definition.
G4double Mass() const
Accessor.
G4double Momentum() const
Accessor.
G4double KineticEnergy() const
Accessor.
G4double Beta() const
Accessor.
G4double Gamma() const
Accessor.
BDSIonDefinition * IonDefinition() const
Accessor.
G4double TotalEnergy() const
Accessor.
G4bool IsAnIon() const
Accessor.
void UpdateG4ParticleDefinition(G4ParticleDefinition *particleIn)
Improve type-safety of native enum data type in C++.
double sigmaE
for the gaussian, elliptic shell, ring distributions
Definition beamBase.h:84
double emity
initial twiss parameters
Definition beamBase.h:89
double bunchFrequency
Bunch offsets in time.
Definition beamBase.h:63
double emitNY
initial twiss parameters
Definition beamBase.h:90
int eventsPerBunch
Bunch offsets in time.
Definition beamBase.h:65
double tilt
tilt of beam applied as rotation about unit local z
Definition beamBase.h:78
double emitNX
initial twiss parameters
Definition beamBase.h:90
double S0
initial beam centroid
Definition beamBase.h:69
double bunchPeriod
Bunch offsets in time.
Definition beamBase.h:64
double emitx
initial twiss parameters
Definition beamBase.h:89
double sigmaT
bunch length
Definition beamBase.h:81
double Zp0
initial beam centroid
Definition beamBase.h:70
double Z0
initial beam centroid
Definition beamBase.h:69
double X0
initial beam centroid
Definition beamBase.h:69
double Xp0
initial beam centroid
Definition beamBase.h:70
double Yp0
initial beam centroid
Definition beamBase.h:70
double Y0
initial beam centroid
Definition beamBase.h:69
double T0
initial beam centroid
Definition beamBase.h:71
Beam class.
Definition beam.h:43
void ConflictingParametersSet(const GMAD::Beam &beamDefinition, const std::set< std::string > &keys, G4int nSet, G4bool warnZeroParamsSet=true, const G4String &unitString="")
Throw an exception if too few or too many parameters are set for the supplied keys.
void FixGeant105ThreshholdsForParticle(const G4ParticleDefinition *particleDefinition)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
G4int NBeamParametersSet(const GMAD::Beam &beamDefinition, const std::set< std::string > &keys)
Count how many out of the set of keys in a beam instance are set.