BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSPrimaryGeneratorFileSampler.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 "BDSBunchEventGenerator.hh"
19#include "BDSDebug.hh"
20#include "BDSException.hh"
21#include "BDSOutputLoaderSampler.hh"
22#include "BDSPrimaryGeneratorFileSampler.hh"
23#include "BDSParticleCoords.hh"
24#include "BDSParticleCoordsFull.hh"
25#include "BDSParticleCoordsFullGlobal.hh"
26#include "BDSPhysicalConstants.hh"
27#include "BDSPrimaryVertexInformation.hh"
28#include "BDSPrimaryVertexInformationV.hh"
29#include "BDSUtilities.hh"
30#include "BDSWarning.hh"
31
32#include "G4Event.hh"
33#include "G4EventManager.hh"
34#include "G4LorentzVector.hh"
35#include "G4PrimaryParticle.hh"
36#include "G4PrimaryVertex.hh"
37
38#include "CLHEP/Units/SystemOfUnits.h"
39
40#include "globals.hh"
41
42#include <utility>
43
45 const G4String& fileNameIn,
47 G4bool loopFileIn,
48 G4bool removeUnstableWithoutDecayIn,
49 G4bool warnAboutSkippedParticlesIn):
50 BDSPrimaryGeneratorFile(loopFileIn, bunchIn),
51 reader(nullptr),
52 fileName(fileNameIn),
53 removeUnstableWithoutDecay(removeUnstableWithoutDecayIn),
54 warnAboutSkippedParticles(warnAboutSkippedParticlesIn)
55{
56 std::pair<G4String, G4String> ba = BDS::SplitOnColon(distrType); // before:after
57 samplerName = ba.second;
58 G4cout << __METHOD_NAME__ << "reading sampler named: \"" << samplerName << "\"" << G4endl;
59 referenceBeamMomentumOffset = bunch->ReferenceBeamMomentumOffset();
60 reader = new BDSOutputLoaderSampler(fileName, samplerName);
61 nEventsInFile = reader->NEventsInFile();
62 bunch->SetNEventsInFile(nEventsInFile);
63 bunch->SetNOriginalEvents(reader->NOriginalEvents());
64 G4cout << __METHOD_NAME__ << nEventsInFile << " events found in file" << G4endl;
65 if (!bunch)
66 {throw BDSException(__METHOD_NAME__, "must be constructed with a valid BDSBunchEventGenerator instance");}
68}
69
70BDSPrimaryGeneratorFileSampler::~BDSPrimaryGeneratorFileSampler()
71{
72 delete reader;
73}
74
76{
77 if (!reader)
78 {throw BDSException(__METHOD_NAME__, "no file reader available");}
79
80 currentVertices.clear();
81
82 // currentFileEventIndex is zero counting by nEventsInFile will be 1 greater
83 if (currentFileEventIndex >= nEventsInFile)
84 {
85 endOfFileReached = true;
86 G4cout << __METHOD_NAME__ << "End of file reached. ";
87
88 if (loopFile)
89 {
90 if (OKToLoopFile())
91 {
92 G4cout << "Returning to beginning of file for next event." << G4endl;
93 currentFileEventIndex = 0;
94 endOfFileReached = false;
95 }
96 else
97 {
98 BDS::Warning(__METHOD_NAME__, "file looping requested, but 0 events passed filters - ending.");
99 return;
100 }
101 }
102 else
103 {
104 G4cout << G4endl; // to flush output
105 return;
106 }
107 }
108
109 ReadSingleEvent(currentFileEventIndex, anEvent);
110}
111
113{
114 G4cout << "BDSROOTSamplerLoader::RecreateAdvanceToEvent> Advancing file to event: " << eventOffset << G4endl;
116 SkipEvents(eventOffset);
117}
118
120{
121 vertices.clear();
122 const auto sampler = reader->SamplerDataFloat(index);
123
124 int n = sampler->n;
125 for (int i = 0; i < n; i++)
126 {
127 G4int pdgID = (G4int)sampler->partID[i];
128 G4double xp = (G4double)sampler->xp[i];
129 G4double yp = (G4double)sampler->yp[i];
130 G4double zp = (G4double)sampler->zp[i];
131 G4double p = (G4double)sampler->p[i];
132 G4ThreeVector momentum = G4ThreeVector(xp,yp,zp) * p * CLHEP::GeV;
133 G4double x = (G4double)sampler->x[i] * CLHEP::m;
134 G4double y = (G4double)sampler->y[i] * CLHEP::m;
135 G4double T = (G4double)sampler->T[i] * CLHEP::s;
136 G4ThreeVector localPosition(x,y,0);
137 G4double weight = (G4double)sampler->weight[i];
138 auto g4prim = new G4PrimaryParticle(pdgID, momentum.x(), momentum.y(), momentum.z());
139 g4prim->SetWeight(weight);
140 vertices.emplace_back(DisplacedVertex{localPosition, T, g4prim});
141 }
142}
143
144void BDSPrimaryGeneratorFileSampler::ReadPrimaryParticlesDouble(G4long index)
145{
146 vertices.clear();
147 const auto sampler = reader->SamplerDataDouble(index);
148
149 int n = sampler->n;
150 for (int i = 0; i < n; i++)
151 {
152 G4int pdgID = (G4int)sampler->partID[i];
153 G4double xp = (G4double)sampler->xp[i];
154 G4double yp = (G4double)sampler->yp[i];
155 G4double zp = (G4double)sampler->zp[i];
156 G4double p = (G4double)sampler->p[i];
157 G4ThreeVector momentum = G4ThreeVector(xp,yp,zp) * p;
158 G4double x = (G4double)sampler->x[i] * CLHEP::m;
159 G4double y = (G4double)sampler->y[i] * CLHEP::m;
160 G4double T = (G4double)sampler->T[i] * CLHEP::s;
161 G4ThreeVector localPosition(x,y,0);
162 G4double weight = (G4double)sampler->weight[i];
163 auto g4prim = new G4PrimaryParticle(pdgID, momentum.x(), momentum.y(), momentum.z());
164 g4prim->SetWeight(weight);
165 vertices.emplace_back(DisplacedVertex{localPosition, T, g4prim});
166 }
167}
168
169void BDSPrimaryGeneratorFileSampler::ReadSingleEvent(G4long index, G4Event* anEvent)
170{
171 if (reader->DoublePrecision())
172 {ReadPrimaryParticlesDouble(index);}
173 else
175
176 G4int nParticlesSkipped = 0;
177 for (const auto& xyzVertex : vertices)
178 {
179 const auto vertex = xyzVertex.vertex;
180 // if the particle definition isn't found from the pdgcode in the construction
181 // of G4PrimaryParticle, it means the mass, charge, etc. will be wrong - don't
182 // stack this particle into the vertex.
183 // technically shouldn't happen as bdsim produced this output... but safety first
184 const G4ParticleDefinition* pd = vertex->GetParticleDefinition();
185 G4bool deleteIt = !pd;
186 if (pd && removeUnstableWithoutDecay)
187 {deleteIt = !(pd->GetPDGStable()) && !pd->GetDecayTable();}
188
189 if (deleteIt)
190 {
191 nParticlesSkipped++;
192 continue;
193 }
194
195 G4ThreeVector unitMomentum = vertex->GetMomentumDirection();
196 unitMomentum.transform(referenceBeamMomentumOffset);
197 G4double rp = unitMomentum.perp();
198
199 BDSParticleCoordsFull centralCoords = bunch->GetNextParticleLocal();
200 centralCoords.AddOffset(xyzVertex.xyz, xyzVertex.T); // add on the local offset from the sampler
201
202 BDSParticleCoordsFull local(centralCoords.x,
203 centralCoords.y,
204 centralCoords.z,
205 unitMomentum.x(),
206 unitMomentum.y(),
207 unitMomentum.z(),
208 centralCoords.T,
209 centralCoords.s,
210 vertex->GetTotalEnergy(),
211 vertex->GetWeight());
212
213 if (!bunch->AcceptParticle(local, rp, vertex->GetKineticEnergy(), vertex->GetPDGcode()))
214 {
215 nParticlesSkipped++;
216 continue;
217 }
218
219 BDSParticleCoordsFullGlobal fullCoordsGlobal = bunch->ApplyTransform(local);
220
221 auto g4vtx = new G4PrimaryVertex(fullCoordsGlobal.global.x,
222 fullCoordsGlobal.global.y,
223 fullCoordsGlobal.global.z,
224 fullCoordsGlobal.global.T);
225
226 // ensure it's in the world - not done in primary generator action for event generators
227 if (!VertexInsideWorld(fullCoordsGlobal.global.Position()))
228 {
229 delete g4vtx;
230 nParticlesSkipped++;
231 continue;
232 }
233
234 G4double brho = 0;
235 G4double charge = vertex->GetCharge();
236 G4double momentum = vertex->GetTotalMomentum();
237 if (BDS::IsFinite(charge)) // else leave as 0
238 {
239 brho = momentum / CLHEP::GeV / BDS::cOverGeV / charge;
240 brho *= CLHEP::tesla*CLHEP::m; // rigidity (in Geant4 units)
241 }
242 auto vertexInfo = new BDSPrimaryVertexInformation(fullCoordsGlobal,
243 vertex->GetTotalMomentum(),
244 vertex->GetCharge(),
245 brho,
246 vertex->GetMass(),
247 vertex->GetPDGcode());
248
249 // update momentum in case of a beam line transform
250 auto prim = new G4PrimaryParticle(*vertex);
251 prim->SetMomentumDirection(G4ThreeVector(fullCoordsGlobal.global.xp,
252 fullCoordsGlobal.global.yp,
253 fullCoordsGlobal.global.zp));
254 g4vtx->SetPrimary(prim);
255 g4vtx->SetUserInformation(vertexInfo);
256 currentVertices.push_back(g4vtx);
257 }
258
259 if (nParticlesSkipped > 0 && warnAboutSkippedParticles)
260 {G4cout << __METHOD_NAME__ << nParticlesSkipped << " particles skipped" << G4endl;}
261
262 if (currentVertices.empty())
263 {// no particles found that pass criteria... we can't simulate this event... abort and move on
264 nEventsSkipped++;
265 if (warnAboutSkippedParticles)
266 {G4cout << __METHOD_NAME__ << "no particles found in event number: " << currentFileEventIndex << " in the file" << G4endl;}
267 }
268 else
269 {
270 vertexGeneratedSuccessfully = true;
271 nEventsReadThatPassedFilters++;
272 }
273
274 // clear initially loaded ones
275 for (auto& v : vertices)
276 {delete v.vertex;}
277 vertices.clear();
278
279 currentFileEventIndex++;
280
281 for (auto* v : currentVertices)
282 {anEvent->AddPrimaryVertex(v);}
283 currentVertices.clear();
284}
285
287{
288 if (nEventsToSkip > 0)
289 {G4cout << __METHOD_NAME__ << "skipping " << nEventsToSkip << " into file." << G4endl;}
290 else
291 {return ;}
292 G4long distrFileLoopNTimes = bunch->DistrFileLoopNTimes();
293 G4long nAvailable = nEventsInFile * distrFileLoopNTimes;
294 if ((G4long)nEventsToSkip > nAvailable)
295 {
296 G4String msg = "number of events to skip (" + std::to_string(nEventsToSkip) + ") is greater than the number of events (";
297 msg += std::to_string(nEventsInFile);
298 if (distrFileLoopNTimes > 1)
299 {msg += " x " + std::to_string(distrFileLoopNTimes) + " loops of file";}
300 msg += ") in this file.";
301 throw BDSException("BDSBunchUserFile::RecreateAdvanceToEvent>", msg);
302 }
303 G4long nToSkipSinglePass = nEventsToSkip % nEventsInFile;
304 currentFileEventIndex = nToSkipSinglePass;
305}
A wrapper of BDSBunch to include a filter for the events loaded by an event generator.
G4RotationMatrix ReferenceBeamMomentumOffset() const
Get a rotation matrix according to Xp0 and Yp0.
G4int eventGeneratorNEventsSkip
Cache of limit.
G4bool AcceptParticle(const BDSParticleCoordsFull &coords, G4double rpOriginal, G4double kineticEnergy, G4int pdgID)
G4int DistrFileLoopNTimes() const
Accessor.
BDSParticleCoordsFullGlobal ApplyTransform(const BDSParticleCoordsFull &localIn) const
Definition BDSBunch.cc:291
virtual BDSParticleCoordsFull GetNextParticleLocal()
Definition BDSBunch.cc:271
General exception with possible name of object and message.
Loader of ROOT Event output for receating events.
A set of particle coordinates in both local and global.
A set of particle coordinates including energy and weight.
G4double s
TODO - remove this. Unused. S (global) is calculated from S0 + z.
void AddOffset(const G4ThreeVector &offset, G4double offsetT=0)
Apply an offset to the spatial and T coordinates only - assignment.
virtual void RecreateAdvanceToEvent(G4int eventOffset)
Advance to the correct event number in the file for recreation.
void ReadSingleEvent(G4long index, G4Event *anEvent)
Read sampler hits and put into primary vertices if they pass filters.
std::vector< DisplacedVertex > vertices
Used for transiently loading information.
BDSPrimaryGeneratorFileSampler()=delete
Do not require default constructor.
void ReadPrimaryParticlesFloat(G4long index)
Conversion from HepMC::GenEvent to G4Event.
virtual void GeneratePrimaryVertex(G4Event *anEvent)
Read the next non-empty sampler entry from the file.
Common interface for any primary generators that are file based.
void ThrowExceptionIfRecreateOffsetTooHigh(G4long eventOffset) const
G4bool VertexInsideWorld(const G4ThreeVector &pos) const
Utility function for derived classes to check a position is inside the world.
Full set of coordinates for association with primary vertex.
std::pair< G4String, G4String > SplitOnColon(const G4String &formatAndPath)
static const G4double cOverGeV
speed of light / 1 GeV, used for scaling in brho calculation
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())