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