BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSPrimaryGeneratorFileHEPMC.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#ifdef USE_HEPMC3
19
20#include "BDSBunchEventGenerator.hh"
21#include "BDSDebug.hh"
22#include "BDSEventGeneratorFileType.hh"
23#include "BDSException.hh"
24#include "BDSPrimaryGeneratorFileHEPMC.hh"
25#include "BDSParticleCoords.hh"
26#include "BDSParticleCoordsFull.hh"
27#include "BDSParticleCoordsFullGlobal.hh"
28#include "BDSPhysicalConstants.hh"
29#include "BDSPrimaryVertexInformation.hh"
30#include "BDSPrimaryVertexInformationV.hh"
31#include "BDSUtilities.hh"
32#include "BDSWarning.hh"
33
34#include "G4Event.hh"
35#include "G4LorentzVector.hh"
36#include "G4ParticleDefinition.hh"
37#include "G4PrimaryParticle.hh"
38#include "G4PrimaryVertex.hh"
39#include "G4String.hh"
40#include "G4Types.hh"
41
42#include "HepMC3/Attribute.h"
43#include "HepMC3/GenParticle.h"
44#include "HepMC3/GenVertex.h"
45#include "HepMC3/Reader.h"
46#include "HepMC3/ReaderAscii.h"
47#include "HepMC3/ReaderAsciiHepMC2.h"
48#include "HepMC3/ReaderHEPEVT.h"
49#include "HepMC3/ReaderLHEF.h"
50#ifdef USE_HEPMC3_ROOTIO
51#include "HepMC3/ReaderRoot.h"
52#include "HepMC3/ReaderRootTree.h"
53#endif
54
55#include "CLHEP/Units/SystemOfUnits.h"
56
57#include "globals.hh"
58
59#include <cmath>
60#include <utility>
61#include <vector>
62
63
65 const G4String& fileNameIn,
67 G4bool loopFileIn,
68 G4bool removeUnstableWithoutDecayIn,
69 G4bool warnAboutSkippedParticlesIn):
70 BDSPrimaryGeneratorFile(loopFileIn, bunchIn),
71 hepmcEvent(nullptr),
72 reader(nullptr),
73 fileName(fileNameIn),
74 removeUnstableWithoutDecay(removeUnstableWithoutDecayIn),
75 warnAboutSkippedParticles(warnAboutSkippedParticlesIn)
76{
77 std::pair<G4String, G4String> ba = BDS::SplitOnColon(distrType); // before:after
78 fileType = BDS::DetermineEventGeneratorFileType(ba.second);
79 G4cout << __METHOD_NAME__ << "event generator file format to be " << fileType.ToString() << G4endl;
80 referenceBeamMomentumOffset = bunch->ReferenceBeamMomentumOffset();
81 nEventsInFile = CountEventsInFile();
82 bunch->SetNEventsInFile(nEventsInFile);
83 OpenFile();
84 if (!bunch)
85 {throw BDSException(__METHOD_NAME__, "must be constructed with a valid BDSBunchEventGenerator instance");}
87}
88
89BDSPrimaryGeneratorFileHEPMC::~BDSPrimaryGeneratorFileHEPMC()
90{
91 delete hepmcEvent;
92 delete reader;
93}
94
96{
97 if (!reader)
98 {throw BDSException(__METHOD_NAME__, "no file reader available");}
99
100 G4bool readEventOK = ReadSingleEvent();
101 if (readEventOK)
102 {HepMC2G4(hepmcEvent, anEvent);} // this will update vertexGeneratedSuccessfully if one is created
103}
104
106{
107 G4cout << __METHOD_NAME__ << "advancing file to event: " << eventOffset << G4endl;
109 SkipEvents(eventOffset);
110}
111
113{
114 currentFileEventIndex = 0;
115 endOfFileReached = false;
116 if (usualPrintOut)
117 {G4cout << __METHOD_NAME__ << "Opening file: " << fileName << G4endl;}
118 switch (fileType.underlying())
119 {
120 case BDSEventGeneratorFileType::hepmc2:
121 {reader = new HepMC3::ReaderAsciiHepMC2(fileName); break;}
122 case BDSEventGeneratorFileType::hepmc3:
123 {reader = new HepMC3::ReaderAscii(fileName); break;}
124 case BDSEventGeneratorFileType::hpe:
125 {reader = new HepMC3::ReaderHEPEVT(fileName); break;}
126#ifdef USE_HEPMC3_ROOTIO
127 case BDSEventGeneratorFileType::root:
128 {reader = new HepMC3::ReaderRoot(fileName); break;}
129 case BDSEventGeneratorFileType::treeroot:
130 {reader = new HepMC3::ReaderRootTree(fileName); break;}
131#else
132 case BDSEventGeneratorFileType::root:
133 case BDSEventGeneratorFileType::treeroot:
134 {
135 throw BDSException(__METHOD_NAME__, "HEPMC3 installation not compiled with ROOT support so can't load root file.");
136 break;
137 }
138#endif
139 case BDSEventGeneratorFileType::lhef:
140 {reader = new HepMC3::ReaderLHEF(fileName); break;}
141 default:
142 {break;}
143 }
144}
145
147{
148 if (reader)
149 {reader->close();}
150 delete reader;
151 reader = nullptr;
152 currentFileEventIndex = 0;
153 endOfFileReached = true;
154}
155
157{
158 G4cout << __METHOD_NAME__ << "counting number of events" << G4endl;
159 OpenFile(false);
160 G4long nEvents = 0;
161 while (!reader->failed())
162 {
163 auto tempEvent = new HepMC3::GenEvent();
164 bool readEventOK = reader->read_event(*tempEvent);
165 if (!readEventOK) // warn but continue
166 {G4cout << __METHOD_NAME__ << "error in reading event index " << nEvents << G4endl;}
167 // the reader will read ok beyond the last event (stupid), so we then have to check the file
168 // status again here to know we've not really read an event!
169 if (reader->failed())
170 {continue;}
171 nEvents++;
172 delete tempEvent;
173 }
174 CloseFile();
175 G4cout << __METHOD_NAME__ << nEvents << " events found in file" << G4endl;
176 return nEvents;
177}
178
180{
181 delete hepmcEvent;
182 hepmcEvent = new HepMC3::GenEvent();
183
184 // it will return false if there is a problem with the input stream or in reading one complete event
185 // throw an exception as file inaccessible rather than finished
186 bool readEventOK = reader->read_event(*hepmcEvent);
187 if (!readEventOK)
188 {
189 delete hepmcEvent;
190 hepmcEvent = nullptr;
191 throw BDSException(__METHOD_NAME__, "problem with event generator file \"" + fileName + "\"");
192 }
193
194 if (reader->failed()) // code for end of the file
195 {
196 delete hepmcEvent;
197 hepmcEvent = nullptr;
198 CloseFile();
199
200 if (loopFile)
201 {
202 if (OKToLoopFile())
203 {
204 G4cout << __METHOD_NAME__ << "Returning to beginning of file for next event." << G4endl;
205 OpenFile();
206 }
207 else
208 {BDS::Warning(__METHOD_NAME__, "file looping requested, but 0 events passed filters - ending.");}
209 }
210 return false;
211 }
212 else
213 {
214 currentFileEventIndex++;
215 return true;
216 }
217}
218
220{
221 if (nEventsToSkip > 0)
222 {G4cout << __METHOD_NAME__ << "skipping " << nEventsToSkip << " into file." << G4endl;}
223 else
224 {return ;}
225 G4long distrFileLoopNTimes = bunch->DistrFileLoopNTimes();
226 G4long nAvailable = nEventsInFile * distrFileLoopNTimes;
227 if ((G4long)nEventsToSkip > nAvailable)
228 {
229 G4String msg = "number of events to skip (" + std::to_string(nEventsToSkip) + ") is greater than the number of events (";
230 msg += std::to_string(nEventsInFile);
231 if (distrFileLoopNTimes > 1)
232 {msg += " x " + std::to_string(distrFileLoopNTimes) + " loops of file";}
233 msg += ") in this file.";
234 throw BDSException("BDSBunchUserFile::RecreateAdvanceToEvent>", msg);
235 }
236 G4long nToSkipSinglePass = nEventsToSkip % nEventsInFile;
237 for (G4int i = 0; i < nToSkipSinglePass; i++)
238 {ReadSingleEvent();}
239}
240
241void BDSPrimaryGeneratorFileHEPMC::HepMC2G4(const HepMC3::GenEvent* hepmcevt,
242 G4Event* g4event)
243{
244 BDSParticleCoordsFull centralCoords = bunch->GetNextParticleLocal();
245 // do the transform for the reference particle (if any transform) and use that
246 BDSParticleCoordsFullGlobal centralCoordsGlobal = bunch->ApplyTransform(centralCoords);
247 G4PrimaryVertex* g4vtx = new G4PrimaryVertex(centralCoordsGlobal.global.x,
248 centralCoordsGlobal.global.y,
249 centralCoordsGlobal.global.z,
250 centralCoordsGlobal.global.T);
251
252 double overallWeight = 1.0;
253 if (!(hepmcevt->weights().empty()))
254 {overallWeight = hepmcevt->weight();}
255 std::vector<BDSPrimaryVertexInformation> vertexInfos;
256 G4int nParticlesSkipped = 0;
257 for (const auto& particlePtr : hepmcevt->particles())
258 {
259 const HepMC3::GenParticle* particle = particlePtr.get();
260 if (!particle->children().empty())
261 {continue;} // this particle is not at the end of the tree - ignore
262
263 int pdgcode = particle->pdg_id();
264 const HepMC3::FourVector& fv = particle->momentum();
265 G4LorentzVector p(fv.px(), fv.py(), fv.pz(), fv.e());
266 G4double px = p.x() * CLHEP::GeV;
267 G4double py = p.y() * CLHEP::GeV;
268 G4double pz = p.z() * CLHEP::GeV;
269 G4ThreeVector originalUnitMomentum(px,py,pz);
270 // as the particle is given by momentum (and not Etotal) then we don't need to check Etotal >= rest mass
271 originalUnitMomentum = originalUnitMomentum.unit();
272 G4double rp = std::hypot(originalUnitMomentum.x(), originalUnitMomentum.y());
273
274 // apply any reference coordinate offsets. Copy the vector first.
275 G4ThreeVector unitMomentum = originalUnitMomentum;
276 unitMomentum.transform(referenceBeamMomentumOffset);
277 // it's ok that this G4PrimaryParticle doesn't have the correct momentum direction as we only use it for
278 // total energy and checking - it's direction is updated later based on unitMomentum with a beam line transform.
279 G4PrimaryParticle* g4prim = new G4PrimaryParticle(pdgcode, px, py, pz);
280
281 // if the particle definition isn't found from the pdgcode in the construction
282 // of G4PrimaryParticle, it means the mass, charge, etc will be wrong - don't
283 // stack this particle into the vertex.
284 const G4ParticleDefinition* pd = g4prim->GetParticleDefinition();
285 G4bool deleteIt = !pd;
286 if (pd && removeUnstableWithoutDecay)
287 {deleteIt = !(pd->GetPDGStable()) && !pd->GetDecayTable();}
288
289 if (deleteIt)
290 {
291#ifdef BDSDEBUG
292 G4cout << __METHOD_NAME__ << "skipping particle with PDG ID: " << pdgcode << G4endl;
293#endif
294 delete g4prim;
295 nParticlesSkipped++;
296 continue;
297 }
298
299 BDSParticleCoordsFull local(centralCoords.x,
300 centralCoords.y,
301 centralCoords.z,
302 unitMomentum.x(),
303 unitMomentum.y(),
304 unitMomentum.z(),
305 centralCoords.T,
306 centralCoords.s,
307 g4prim->GetTotalEnergy(),
308 overallWeight);
309
310 if (!bunch->AcceptParticle(local, rp, g4prim->GetKineticEnergy(), pdgcode))
311 {
312 delete g4prim;
313 nParticlesSkipped++;
314 continue;
315 }
316
317 BDSParticleCoordsFullGlobal fullCoords = bunch->ApplyTransform(local);
318 // ensure it's in the world - not done in primary generator action for event generators
319 if (!VertexInsideWorld(fullCoords.global.Position()))
320 {
321 delete g4prim;
322 nParticlesSkipped++;
323 continue;
324 }
325
326 G4double brho = 0;
327 G4double charge = g4prim->GetCharge();
328 G4double momentum = g4prim->GetTotalMomentum();
329 if (BDS::IsFinite(charge)) // else leave as 0
330 {
331 brho = momentum / CLHEP::GeV / BDS::cOverGeV / charge;
332 brho *= CLHEP::tesla*CLHEP::m; // rigidity (in Geant4 units)
333 }
334 BDSPrimaryVertexInformation vertexInfo(fullCoords,
335 g4prim->GetTotalMomentum(),
336 g4prim->GetCharge(),
337 brho,
338 g4prim->GetMass(),
339 pdgcode);
340 vertexInfos.emplace_back(vertexInfo);
341
342 // update momentum in case of a beam line transform
343 g4prim->SetMomentumDirection(G4ThreeVector(fullCoords.global.xp,
344 fullCoords.global.yp,
345 fullCoords.global.zp));
346 g4vtx->SetPrimary(g4prim);
347 }
348
349 if (nParticlesSkipped > 0 && warnAboutSkippedParticles)
350 {G4cout << __METHOD_NAME__ << nParticlesSkipped << " particles skipped" << G4endl;}
351 g4vtx->SetUserInformation(new BDSPrimaryVertexInformationV(vertexInfos));
352
353 if (g4vtx->GetNumberOfParticle() == 0)
354 {// no particles found that pass criteria... we can't simulate this event... abort and move on
355 delete g4vtx;
356 nEventsSkipped++;
357 if (warnAboutSkippedParticles)
358 {G4cout << __METHOD_NAME__ << "no particles found in event number: " << currentFileEventIndex << " in the file" << G4endl;}
359 return;
360 }
361 else
362 {
363 g4event->AddPrimaryVertex(g4vtx);
364 vertexGeneratedSuccessfully = true;
365 nEventsReadThatPassedFilters++;
366 }
367}
368
369#else
370// insert empty function to avoid no symbols warning
371void _SymbolToPreventWarningHEPMC3Reader(){;}
372#endif
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.
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.
virtual void RecreateAdvanceToEvent(G4int eventOffset)
Advance to the correct event number in the file for recreation.
BDSPrimaryGeneratorFileHEPMC()=delete
Do not require default constructor.
virtual void GeneratePrimaryVertex(G4Event *anEvent)
void HepMC2G4(const HepMC3::GenEvent *hepmcevt, G4Event *g4event)
Conversion from HepMC::GenEvent to G4Event.
void OpenFile(G4bool usualPrintOut=true)
Construct the member "reader" and open the file for reading.
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. Vector version.
Full set of coordinates for association with primary vertex.
type underlying() const
return underlying value (can be used in switch statement)
std::pair< G4String, G4String > SplitOnColon(const G4String &formatAndPath)
static const G4double cOverGeV
speed of light / 1 GeV, used for scaling in brho calculation
BDSEventGeneratorFileType DetermineEventGeneratorFileType(G4String distrType)
Function that gives corresponding enum value for string (case-insensitive).
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())