BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSPhysicsUtilities.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 "G4Version.hh"
19
20#include "BDSDebug.hh"
21#include "BDSException.hh"
22#include "BDSGlobalConstants.hh"
23#include "BDSModularPhysicsList.hh"
24#include "BDSIonDefinition.hh"
25#include "BDSParticleDefinition.hh"
26#if G4VERSION_NUMBER > 1039
27#include "BDSPhysicsChannelling.hh"
28#endif
29#include "BDSPhysicsCutsAndLimits.hh"
30#include "BDSPhysicsEMDissociation.hh"
31#include "BDSPhysicsMilli.hh"
32#include "BDSPhysicsMuonSplitting.hh"
33#include "BDSPhysicsPionExtendedDecays.hh"
34#include "BDSPhysicsUtilities.hh"
35#include "BDSUtilities.hh"
36#include "BDSWarning.hh"
37#include "BDSEmStandardPhysicsOp4Channelling.hh" // included with bdsim
38
39#include "FTFP_BERT.hh"
40#include "globals.hh"
41#include "BDSParticleMilli.hh"
42#include "G4AntiNeutrinoE.hh"
43#include "G4AntiNeutrinoMu.hh"
44#include "G4AntiNeutrinoTau.hh"
45#include "G4AntiNeutron.hh"
46#include "G4AntiProton.hh"
47#include "G4Electron.hh"
48#include "G4EmParameters.hh"
49#include "G4EmStandardPhysics_option4.hh"
50#include "G4EmStandardPhysicsSS.hh"
51#include "G4DynamicParticle.hh"
52#include "G4Gamma.hh"
53#include "G4GenericBiasingPhysics.hh"
54#include "G4GenericIon.hh"
55#include "G4IonElasticPhysics.hh"
56#include "G4IonTable.hh"
57#include "G4KaonMinus.hh"
58#include "G4KaonPlus.hh"
59#include "G4KaonZero.hh"
60#include "G4KaonZeroLong.hh"
61#include "G4KaonZeroShort.hh"
62#include "G4LeptonConstructor.hh"
63#include "G4MuonMinus.hh"
64#include "G4MuonPlus.hh"
65#include "G4NeutrinoE.hh"
66#include "G4NeutrinoMu.hh"
67#include "G4NeutrinoTau.hh"
68#include "G4Neutron.hh"
69#include "G4ParticleTable.hh"
70#include "G4PhysListFactory.hh"
71#include "G4PionMinus.hh"
72#include "G4PionPlus.hh"
73#include "G4PionZero.hh"
74#include "G4Positron.hh"
75#include "G4ProcessManager.hh"
76#include "G4ProcessVector.hh"
77#include "G4ProductionCutsTable.hh"
78#include "G4Proton.hh"
79#include "G4String.hh"
80#include "G4UImanager.hh"
81#include "G4VDecayChannel.hh"
82#if G4VERSION_NUMBER > 1049
83#include "G4ParticleDefinition.hh"
84#include "G4CoupledTransportation.hh"
85#include "G4Transportation.hh"
86#include <utility>
87#endif
88
89#if G4VERSION_NUMBER > 1069
90#include "G4HadronicParameters.hh"
91#endif
92
93#include "parser/beam.h"
94#include "parser/fastlist.h"
95#include "parser/physicsbiasing.h"
96
97#include <iomanip>
98#include <map>
99#include <regex>
100#include <set>
101#include <stdexcept>
102#include <string> // for stoi
103
104G4bool BDS::IsIon(const G4ParticleDefinition* particle)
105{
106 return G4IonTable::IsIon(particle) && particle!=G4Proton::Definition();
107}
108
109G4bool BDS::IsIon(const G4DynamicParticle* particle)
110{
111 return BDS::IsIon(particle->GetDefinition()) || particle->GetTotalOccupancy()>0;
112}
113
114G4VModularPhysicsList* BDS::BuildPhysics(const G4String& physicsList, G4int verbosity)
115{
116 // this must be done BEFORE any physics processes are constructed
117 // set the upper and lower energy levels applicable for all physics processes
119
120 G4VModularPhysicsList* result = nullptr;
121
123
125 G4String physicsListNameLower = BDS::LowerCase(physicsList);
126 G4bool useGeant4Physics = BDS::StrContains(physicsListNameLower, "g4");
127 G4bool completePhysics = BDS::StrContains(physicsListNameLower, "complete");
128 if (g->EnableMillicharge())
129 {
130 G4cout << "Using millicharged physics" << G4endl;
131 result = new FTFP_BERT();
132 auto name = BDSGlobalConstants::Instance()->MillichargeName();
133 result->ReplacePhysics(new BDSPhysicsMilli(name, verbosity));
134 }
135 else if (useGeant4Physics)
136 {
137 // strip off G4_ prefix - from original as G4 factory case-sensitive
138 G4String geant4PhysicsList = physicsList.substr(2);
139 G4PhysListFactory factory;
140 if (!factory.IsReferencePhysList(geant4PhysicsList))
141 {
142 G4cerr << "Unknown Geant4 physics list \"" << geant4PhysicsList << "\"" << G4endl;
143 G4cout << "Available Geant4 hadronic physics lists:" << G4endl;
144 for (const auto &name : factory.AvailablePhysLists())
145 {G4cout << "\"" << name << "\"" << G4endl;}
146 G4cout << "Available Geant4 EM physics lists:" << G4endl;
147 for (const auto &name : factory.AvailablePhysListsEM())
148 {G4cout << "\"" << name << "\"" << G4endl;}
149 throw BDSException(__METHOD_NAME__, "Unknown Geant4 physics list \"" + geant4PhysicsList + "\"");
150 }
151 else
152 {
153 result = factory.GetReferencePhysList(geant4PhysicsList);
154 if (g->G4PhysicsUseBDSIMRangeCuts())
155 {BDS::SetRangeCuts(result);}
156 if (g->MinimumKineticEnergy() > 0 || g->G4PhysicsUseBDSIMCutsAndLimits())
157 {
158 G4cout << "\nAdding cuts and limits physics process to Geant4 reference physics list" << G4endl;
159 G4cout << "This is to enforce BDSIM range cuts and the minimumKinetic energy option.\n";
160 G4cout << "This is done by default for the functionality of BDSIM tracking and should not affect the physics greatly.\n";
161 G4cout << "See the BDSIM manual about Geant4 reference physics lists for details." << G4endl;
162 result->RegisterPhysics(new BDSPhysicsCutsAndLimits(g->ParticlesToExcludeFromCutsAsSet()));
163 }
164 else if (!g->G4PhysicsUseBDSIMCutsAndLimits() && g->Circular())
165 {
166 G4String message = "g4PhysicsUseBDSIMCutsAndLimits turned off but using a circular machine - circular mechanics will be broken";
167 BDS::Warning(__METHOD_NAME__, message);
168 }
169 }
170 }
171 else if (completePhysics)
172 {// we test one by one for the exact name of very specific physics lists
173 if (BDS::StrContains(physicsListNameLower, "channelling"))
174 {
175 G4cout << "Constructing \"" << physicsListNameLower << "\" complete physics list" << G4endl;
176#if G4VERSION_NUMBER > 1039
177 G4bool useEMD = BDS::StrContains(physicsListNameLower, "emd");
178 G4bool regular = BDS::StrContains(physicsListNameLower, "regular");
179 G4bool em4 = BDS::StrContains(physicsListNameLower, "em4");
180 G4bool emss = BDS::StrContains(physicsListNameLower, "emss");
181 // we don't assign 'result' variable or proceed as that would result in the
182 // range cuts being set for a complete physics list that we wouldn't use
183 auto r = BDS::ChannellingPhysicsComplete(useEMD, regular, em4, emss);
184 r->SetVerboseLevel(verbosity);
185 return r;
186#else
187 throw BDSException(__METHOD_NAME__, "Channel physics is not supported with Geant4 versions less than 10.4");
188#endif
189 }
190 else
191 {throw BDSException(__METHOD_NAME__, "Unknown 'complete' physics list \"" + physicsList + "\"");}
192 }
193 else
194 {
195 result = new BDSModularPhysicsList(physicsList);
196 BDS::SetRangeCuts(result, verbosity); // always set our range cuts for our physics list
197 }
198
199 // force construction of the particles - does no harm and helps with
200 // usage of exotic particle beams
201 result->ConstructParticle();
202 result->SetVerboseLevel(verbosity);
203
204 // apply any user-supplied macro to adjust geant4 physics parameters
205 G4UImanager* UIManager = G4UImanager::GetUIpointer();
206 G4String physicsMacro = BDSGlobalConstants::Instance()->Geant4PhysicsMacroFileName();
207 if (!physicsMacro.empty())
208 {
209 G4bool setInExecOptions = BDSGlobalConstants::Instance()->Geant4PhysicsMacroFileNameFromExecOptions();
210 G4String physicsMacroFull = BDS::GetFullPath(physicsMacro, false, setInExecOptions);
211 G4cout << "Applying geant4 physics macro file: " << physicsMacroFull << G4endl;
212 UIManager->ApplyCommand("/control/execute " + physicsMacroFull);
213 }
214
215 G4VUserPhysicsList* resultAsUserPhysicsList = dynamic_cast<G4VUserPhysicsList*>(result);
216 if (resultAsUserPhysicsList)
217 {// have to cast as they shadow functions and aren't virtual :(
218 resultAsUserPhysicsList->DumpCutValuesTable(verbosity);
219 resultAsUserPhysicsList->SetVerboseLevel(verbosity);
220 }
221 return result;
222}
223
224G4int BDS::NBeamParametersSet(const GMAD::Beam& beamDefinition,
225 const std::set<std::string>& keys)
226{
227 G4int nSet = 0;
228 for (const auto& k : keys)
229 {nSet += beamDefinition.HasBeenSet(k) ? 1 : 0;}
230 return nSet;
231}
232
233void BDS::ConflictingParametersSet(const GMAD::Beam& beamDefinition,
234 const std::set<std::string>& keys,
235 G4int nSet,
236 G4bool warnZeroParamsSet,
237 const G4String& unitString)
238{
239 if (nSet > 1)
240 {
241 G4cerr << "Beam> More than one parameter set - there should only be one" << G4endl;
242 for (const auto& k : keys)
243 {G4cerr << std::setw(14) << std::left << k << ": " << std::setw(7) << std::right << beamDefinition.get_value(k) << " " << unitString << G4endl;}
244 throw BDSException(__METHOD_NAME__, "conflicting parameters set");
245 }
246 else if (nSet == 0 && warnZeroParamsSet)
247 {
248 G4cerr << "Beam> One of the following required to be set" << G4endl;
249 for (const auto &k : keys)
250 {G4cerr << std::setw(14) << std::left << k << ": " << std::setw(7) << std::right << beamDefinition.get_value(k) << " " << unitString << G4endl;}
251 throw BDSException(__METHOD_NAME__, "insufficient parameters set");
252 }
253}
254
256 G4double ffact,
257 BDSParticleDefinition*& designParticle,
258 BDSParticleDefinition*& beamParticle,
259 G4bool& beamDifferentFromDesignParticle)
260{
261 if (beamDefinition.particle.empty())
262 {throw BDSException("Beam> no \"particle\" specified (required).");}
263
264 // check only one of the following has been set - ie no conflicting information
265 std::set<std::string> keysDesign = {"energy", "momentum", "kineticEnergy"};
266 G4int nSetDesign = BDS::NBeamParametersSet(beamDefinition, keysDesign);
267 BDS::ConflictingParametersSet(beamDefinition, keysDesign, nSetDesign);
268 std::set<std::string> keysParticle = {"E0", "P0", "Ek0"};
269 G4int nSetParticle = BDS::NBeamParametersSet(beamDefinition, keysParticle);
270
271 designParticle = BDS::ConstructParticleDefinition(beamDefinition.particle,
272 beamDefinition.beamEnergy * CLHEP::GeV,
273 beamDefinition.beamKineticEnergy * CLHEP::GeV,
274 beamDefinition.beamMomentum * CLHEP::GeV,
275 ffact);
276
277 // ensure a default
278 std::string beamParticleName = beamDefinition.beamParticleName.empty() ? beamDefinition.particle : beamDefinition.beamParticleName;
279 beamDifferentFromDesignParticle = nSetParticle > 0 || beamParticleName != beamDefinition.particle;
280 if (nSetParticle > 0)
281 {// at least one specified so use all of the beam particle ones
282 BDS::ConflictingParametersSet(beamDefinition, keysParticle, nSetParticle, false);
283 beamParticle = BDS::ConstructParticleDefinition(beamParticleName,
284 beamDefinition.E0 * CLHEP::GeV,
285 beamDefinition.Ek0 * CLHEP::GeV,
286 beamDefinition.P0 * CLHEP::GeV,
287 ffact);
288 }
289 else if (beamDefinition.beamParticleName != beamDefinition.particle && !beamDefinition.beamParticleName.empty())
290 {// different particle name but no extra E0, Ek0 or P0 -> therefore use general beam defaults
291 beamParticle = BDS::ConstructParticleDefinition(beamParticleName,
292 beamDefinition.beamEnergy * CLHEP::GeV,
293 beamDefinition.beamKineticEnergy * CLHEP::GeV,
294 beamDefinition.beamMomentum * CLHEP::GeV,
295 ffact);
296 }
297 else
298 {
299 beamParticle = new BDSParticleDefinition(*designParticle);
300 }
301}
302
304 G4double totalEnergyIn,
305 G4double kineticEnergyIn,
306 G4double momentumIn,
307 G4double ffact)
308{
309 BDSParticleDefinition* particleDefB = nullptr; // result
310 G4String particleName = BDS::LowerCase(particleNameIn);
311
312 std::map<G4String, G4String> commonSubstitutions = { {"photon", "gamma"},
313 {"electron", "e-"},
314 {"positron", "e+"},
315 {"pion+", "pi+"},
316 {"pion-", "pi-"},
317 {"pion0", "pi0"},
318 {"antiproton", "anti_proton"},
319 {"anti-proton", "anti_proton"} };
320
321 G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
322 std::regex ionParticle("(ion\\s).*");
323 if (std::regex_match(particleName, ionParticle))
324 {
325 G4GenericIon::GenericIonDefinition(); // construct general ion particle
326 auto ionDef = new BDSIonDefinition(particleName); // parse the ion definition
327
328 G4IonTable* ionTable = particleTable->GetIonTable();
330 G4int ionPDGID = G4IonTable::GetNucleusEncoding(ionDef->Z(), ionDef->A());
331 G4double mass = ionTable->GetIonMass(ionDef->Z(), ionDef->A());
332 mass += ionDef->NElectrons()*G4Electron::Definition()->GetPDGMass();
333 G4double charge = ionDef->Charge(); // correct even if overridden
334 particleDefB = new BDSParticleDefinition(particleName, mass, charge,
335 totalEnergyIn, kineticEnergyIn, momentumIn, ffact, ionDef, ionPDGID);
336 delete ionDef;
337 }
338 else
339 {
340 // swap out some common name substitutions for Geant4 ones
341 auto searchName = commonSubstitutions.find(particleName);
342 if (searchName != commonSubstitutions.end())
343 {
344 G4cout << "Substituting particle name \"" << particleName << "\" for the Geant4 name: \"" << searchName->second << "\"" << G4endl;
345 particleName = searchName->second;
346 }
347
348 BDS::ConstructBeamParticleG4(particleName); // enforce construction of some basic particles
349 G4ParticleDefinition* particleDef = nullptr;
350 // try and see if it's an integer and therefore PDG ID, if not search by string
351 try
352 {
353 // we try this because std::stoi can throw a std::invalid_argument or
354 // std::out_of_range exception, both of which inherit std::logic_error
355 int particleID = std::stoi(particleName);
356 // we don't use the G4ParticleTable->FindParticle(int) because it unnecessarily
357 // checks for physics readiness and throws an exception. here we just inspect
358 // the encoding dictionary ourselves. it's all typedeffed but it's std::map<G4int, G4ParticleDefinition*>
359 G4ParticleTable::G4PTblEncodingDictionary* encoding = G4ParticleTable::fEncodingDictionary;
360 auto search = encoding->find(particleID);
361 if (search != encoding->end())
362 {particleDef = search->second;}
363 else
364 {throw BDSException(__METHOD_NAME__,"PDG ID \"" + particleName + "not found in particle table");}
365 }
366 catch (const std::logic_error&) // else, usual way by string search
367 {
368 particleDef = particleTable->FindParticle(particleName);
369 if (!particleDef)
370 {particleDef = particleTable->FindParticle(particleNameIn);} // try with original case
371 }
372 if (!particleDef)
373 {
375 throw BDSException(__METHOD_NAME__, "Particle \"" + particleName + "\" not found.");
376 }
377 particleDefB = new BDSParticleDefinition(particleDef, totalEnergyIn, kineticEnergyIn, momentumIn, ffact);
378 }
379 return particleDefB;
380}
381
382void BDS::ConstructBeamParticleG4(const G4String& name)
383{
384 // note, we compare to the lower case name here, not the Geant4 one as
385 // we will already have converted to lower case when this is used
386 if (name == "proton")
387 {G4Proton::ProtonDefinition();}
388 else if (name == "anti_proton")
389 {G4AntiProton::AntiProtonDefinition();}
390 else if (name == "e-")
391 {G4Electron::ElectronDefinition();}
392 else if (name == "e+")
393 {G4Positron::PositronDefinition();}
394 else if (name == "pi-")
395 {G4PionMinus::PionMinusDefinition();}
396 else if (name == "pi+")
397 {G4PionPlus::PionPlusDefinition();}
398 else if (name == "pi0")
399 {G4PionZero::PionZeroDefinition();}
400 else if (name == "neutron")
401 {G4Neutron::NeutronDefinition();}
402 else if (name == "photon" || name == "gamma")
403 {G4Gamma::Gamma();}
404 else if (name == "mu-")
405 {G4MuonMinus::MuonMinusDefinition();}
406 else if (name == "mu+")
407 {G4MuonPlus::MuonPlusDefinition();}
408 else if (name == "kaon-")
409 {G4KaonMinus::KaonMinusDefinition();}
410 else if (name == "kaon+")
411 {G4KaonPlus::KaonPlusDefinition();}
412 else if (name == "kaon0")
413 {G4KaonZero::KaonZeroDefinition();}
414 else if (name == "kaon0l")
415 {G4KaonZeroLong::KaonZeroLongDefinition();}
416 else if (name == "kaon0s")
417 {G4KaonZeroShort::KaonZeroShortDefinition();}
418 else if (name == "nu_e")
419 {G4NeutrinoE::NeutrinoEDefinition();}
420 else if (name == "anti_nu_e")
421 {G4AntiNeutrinoE::AntiNeutrinoEDefinition();}
422 else if (name == "nu_mu")
423 {G4NeutrinoMu::NeutrinoMuDefinition();}
424 else if (name == "anti_nu_mu")
425 {G4AntiNeutrinoMu::AntiNeutrinoMuDefinition();}
426 else if (name == "nu_tau")
427 {G4NeutrinoTau::NeutrinoTauDefinition();}
428 else if (name == "anti_nu_tau")
429 {G4AntiNeutrinoTau::AntiNeutrinoTauDefinition();}
430 else if (name == BDSGlobalConstants::Instance()->MillichargeName())
431 {ParticleMilli::MillichargeDefinition();}
432 else
433 {
434 G4String msg = "Unknown common beam particle type \"" + name;
435 msg += "\" - if it doesn't work, include all \"all_particles\" in the physicsList option.";
436 BDS::Warning(__METHOD_NAME__, msg);
437 }
438}
439
441{
442 // e-, e+, v_e, v_e(bar)
443 G4Electron::ElectronDefinition();
444 G4Positron::PositronDefinition();
445 G4NeutrinoE::NeutrinoEDefinition();
446 G4AntiNeutrinoE::AntiNeutrinoEDefinition();
447
448 // p, pbar, neutron, anti-neutron
449 G4Proton::ProtonDefinition();
450 G4AntiProton::AntiProtonDefinition();
451 G4Neutron::NeutronDefinition();
452 G4AntiNeutron::AntiNeutronDefinition();
453
454 // photon
455 G4Gamma::Gamma();
456}
457
459{
460 G4LeptonConstructor::ConstructParticle();
461 G4PionPlus::PionPlusDefinition();
462 G4PionMinus::PionMinusDefinition();
463 G4KaonPlus::KaonPlusDefinition();
464 G4KaonMinus::KaonMinusDefinition();
465 G4GenericIon::GenericIonDefinition();
466}
467
468void BDS::PrintPrimaryParticleProcesses(const G4String& primaryParticleName)
469{
470 G4cout << "Register physics processes by name for the primary particle \""
471 << primaryParticleName << "\":" << G4endl;
472
473 auto particle = G4ParticleTable::GetParticleTable()->FindParticle(primaryParticleName);
474 if (!particle)
475 {// could be ion that isn't defined
476#ifdef BDSDEBUG
477 G4cout << __METHOD_NAME__ << "primary particle not defined yet - could be ion" << G4endl;
478#endif
479 return;
480 }
481 auto pl = particle->GetProcessManager()->GetProcessList();
482 for (G4int i = 0; i < (G4int)pl->length(); i++)
483 {G4cout << "\"" << (*pl)[i]->GetProcessName() << "\"" << G4endl;}
484}
485
486
487G4GenericBiasingPhysics* BDS::BuildAndAttachBiasWrapper(const GMAD::FastList<GMAD::PhysicsBiasing>& biases)
488{
489 std::set<const G4ParticleDefinition*> particlesToBias;
490 for (const auto& b : biases)
491 {
492 const G4ParticleDefinition* particle = nullptr;
493 G4String particleName = G4String(b.particle);
494 // this works for "GenericIon" too
495 particle = G4ParticleTable::GetParticleTable()->FindParticle(particleName);
496
497 if (particle)
498 {particlesToBias.insert(particle);}
499 else
500 {throw BDSException(__METHOD_NAME__, "Unknown particle type for biasing: \"" + particleName + "\"");}
501 }
502
503 if (particlesToBias.empty()) // nothing valid to bias
504 {return nullptr;}
505
506 G4GenericBiasingPhysics* physBias = new G4GenericBiasingPhysics();
507 for (auto part : particlesToBias)
508 {
509 const G4String& particleName = part->GetParticleName();
510 G4cout << __METHOD_NAME__ << "wrapping \"" << particleName << "\" for biasing" << G4endl;
511 physBias->Bias(particleName);
512 }
513 return physBias;
514}
515
516void BDS::BuildMuonBiasing(G4VModularPhysicsList* physicsList)
517{
518 auto globals = BDSGlobalConstants::Instance();
519 G4int muonSplittingFactor = globals->MuonSplittingFactor();
520 if (muonSplittingFactor > 1)
521 {
522 G4int muonSplittingFactor2 = globals->MuonSplittingFactor2();
523 G4double muonSplittingThresholdParentEk = globals->MuonSplittingThresholdParentEk();
524 G4double muonSplittingThresholdParentEk2 = globals->MuonSplittingThresholdParentEk2();
525 G4cout << "BDSPhysicsMuonSplitting -> using muon splitting wrapper -> factor of: " << muonSplittingFactor << G4endl;
526 if (muonSplittingThresholdParentEk > 0)
527 {G4cout << "BDSPhysicsMuonSplitting -> minimum parent kinetic energy: " << muonSplittingThresholdParentEk / CLHEP::GeV << " GeV" << G4endl;}
528 if (muonSplittingFactor2 > 1)
529 {
530 G4cout << "BDSPhysicsMuonSplitting -> factor #2: " << muonSplittingFactor2 << " for muons above "
531 << muonSplittingThresholdParentEk / CLHEP::GeV << " GeV" << G4endl;
532 }
533 G4bool excludeW1P = globals->MuonSplittingExcludeWeight1Particles();
534 physicsList->RegisterPhysics(new BDSPhysicsMuonSplitting(muonSplittingFactor, muonSplittingThresholdParentEk,
535 muonSplittingFactor2, muonSplittingThresholdParentEk2,
536 excludeW1P, globals->MuonSplittingExclusionWeight()));
537 }
538}
539
540void BDS::ExtendPionDecayChannels(G4VModularPhysicsList* physicsList)
541{
542 G4cout << "Extending pion decay channels to include BR ~1e-4" << G4endl;
543 // Appended to list of physics lists. Does not matter that the G4Decay process
544 // is constructed and then the decay table is updated. It is read at the decay
545 // time during the simulation by G4Decay.
546 physicsList->RegisterPhysics(new BDSPhysicsPionExtendedDecays());
547}
548
550{
551 BDS::Warning("Muon decays disabled -> muons are artificially stable");
552 G4MuonPlus::Definition()->SetPDGStable(true);
553 G4MuonMinus::Definition()->SetPDGStable(true);
554}
555
557{
558 G4cout << __METHOD_NAME__ << "Defined particles: " << G4endl;
559 auto it = G4ParticleTable::GetParticleTable()->GetIterator();
560 it->reset(); // because there's only 1 iterator due to geant4 design
561 while ((*it)())
562 {G4cout << it->value()->GetParticleName() << " ";}
563 G4cout << G4endl;
564}
565
566#if G4VERSION_NUMBER > 1039
567G4VModularPhysicsList* BDS::ChannellingPhysicsComplete(G4bool useEMD,
568 G4bool regular,
569 G4bool em4,
570 G4bool emss)
571{
572 G4VModularPhysicsList* physlist = new FTFP_BERT();
573 physlist->RegisterPhysics(new G4IonElasticPhysics()); // not included by default in FTFP_BERT
574 G4GenericBiasingPhysics* biasingPhysics = new G4GenericBiasingPhysics();
575 physlist->RegisterPhysics(new BDSPhysicsChannelling());
576 if (!regular)
577 {
578 // replace the EM physics in the Geant4 provided FTFP_BERT composite physics list
579 physlist->ReplacePhysics(new BDSEmStandardPhysicsOp4Channelling());
580 }
581 else if (em4)
582 {
583 physlist->ReplacePhysics(new G4EmStandardPhysics_option4());
584 }
585 else if (emss)
586 {
587 physlist->ReplacePhysics(new G4EmStandardPhysicsSS());
588 }
589
590 // optional electromagnetic dissociation that isn't in FTFP_BERT by default
591 if (useEMD)
592 {
593 G4cout << "Adding EM Dissocation to crystal channelling physics list" << G4endl;
594 physlist->RegisterPhysics(new BDSPhysicsEMDissociation());
595 }
596
597 biasingPhysics->PhysicsBiasAllCharged();
598 physlist->RegisterPhysics(biasingPhysics);
599 if (BDSGlobalConstants::Instance()->MinimumKineticEnergy() > 0 &&
600 BDSGlobalConstants::Instance()->G4PhysicsUseBDSIMCutsAndLimits())
601 {
602 G4cout << "\nWARNING - adding cuts and limits physics process to \"COMPLETE\" physics list" << G4endl;
603 G4cout << "This is to enforce BDSIM range cuts and the minimumKinetic energy option.\n";
604 G4cout << "This can be turned off by setting option, g4PhysicsUseBDSIMCutsAndLimits=0;\n" << G4endl;
605 physlist->RegisterPhysics(new BDSPhysicsCutsAndLimits(BDSGlobalConstants::Instance()->ParticlesToExcludeFromCutsAsSet()));
606 }
607 return physlist;
608}
609#endif
610
611void BDS::SetRangeCuts(G4VModularPhysicsList* physicsList, G4int verbosity)
612{
614
615 // overwrite when explicitly set in options
616 if (globals->DefaultRangeCutsSet())
617 {
618 G4cout << __METHOD_NAME__ << "Default production range cut " << physicsList->GetDefaultCutValue() << " mm" << G4endl;
619 physicsList->SetDefaultCutValue(globals->DefaultRangeCut());
620 }
621 if (globals->ProdCutPhotonsSet())
622 {
623 G4cout << __METHOD_NAME__ << "Photon production range cut " << physicsList->GetCutValue("gamma") << " mm" << G4endl;
624 physicsList->SetCutValue(globals->ProdCutPhotons(), "gamma");
625 }
626 if (globals->ProdCutElectronsSet())
627 {
628 G4cout << __METHOD_NAME__ << "Electron production range cut " << physicsList->GetCutValue("e-") << " mm" << G4endl;
629 physicsList->SetCutValue(globals->ProdCutElectrons(),"e-");
630 }
631 if (globals->ProdCutPositronsSet())
632 {
633 G4cout << __METHOD_NAME__ << "Positron production range cut " << physicsList->GetCutValue("e+") << " mm" << G4endl;
634 physicsList->SetCutValue(globals->ProdCutPositrons(),"e+");
635 }
636 if (globals->ProdCutProtonsSet())
637 {
638 G4cout << __METHOD_NAME__ << "Proton production range cut " << physicsList->GetCutValue("proton") << " mm" << G4endl;
639 physicsList->SetCutValue(globals->ProdCutProtons(), "proton");
640 }
641
642 // inspection of the physics list doesn't work at this point and seems to always return 0 apart from the default
643 G4cout << __METHOD_NAME__ << "Range cuts from inspection of the physics list" << G4endl;
644 G4cout << __METHOD_NAME__ << "Default production range cut " << physicsList->GetDefaultCutValue() << " mm" << G4endl;
645
646#ifdef BDSDEBUG
647 G4cout << __METHOD_NAME__ << "List of all constructed particles by physics lists" << G4endl;
648 for (auto particle : *G4ParticleTable::fDictionary)
649 {G4cout << particle.second->GetParticleName() << ", ";}
650 G4cout << G4endl;
651#endif
652
653 physicsList->DumpCutValuesTable(verbosity);
654}
655
657{
659 G4double energyLimitLow = globals->PhysicsEnergyLimitLow();
660 G4double energyLimitHigh = globals->PhysicsEnergyLimitHigh();
661 G4bool setEnergyLimitLow = BDS::IsFinite(energyLimitLow);
662 G4bool setEnergyLimitHigh = BDS::IsFinite(energyLimitHigh);
663
664 if (setEnergyLimitLow || setEnergyLimitHigh)
665 {
666 auto table = G4ProductionCutsTable::GetProductionCutsTable();
667 G4double defaultEnergyLimitLow = table->GetLowEdgeEnergy();
668 G4double defaultEnergyLimitHigh = table->GetHighEdgeEnergy();
669 G4double elLow = setEnergyLimitLow ? energyLimitLow : defaultEnergyLimitLow;
670 G4double elHigh = setEnergyLimitHigh ? energyLimitHigh : defaultEnergyLimitHigh;
671 table->SetEnergyRange(elLow, elHigh);
672 if (setEnergyLimitLow)
673 {
674 G4cout << __METHOD_NAME__ << "set EM physics low energy limit: "
675 << elLow/CLHEP::MeV << " MeV" << G4endl;
676 }
677 if (setEnergyLimitHigh)
678 {
679 G4cout << __METHOD_NAME__ << "set high energy limit: "
680 << elHigh/CLHEP::TeV << " TeV" << G4endl;
681 if (elHigh > G4EmParameters::Instance()->MaxKinEnergy())
682 {
683 G4cout << __METHOD_NAME__ << "set EM physics Ek limit to " << elHigh/CLHEP::TeV << " TeV" << G4endl;
684 G4EmParameters::Instance()->SetMaxEnergy(elHigh);
685 }
686#if G4VERSION_NUMBER > 1069
687 // this was in a patch of our own before 10.7 and compensates for ion physics
688 if (elHigh > G4HadronicParameters::Instance()->GetMaxEnergy())
689 {
690 G4cout << __METHOD_NAME__ << "set hadronic physics Ek limit to " << elHigh/CLHEP::TeV << " TeV" << G4endl;
691 G4HadronicParameters::Instance()->SetMaxEnergy(elHigh);
692 }
693#endif
694 }
695 }
696}
697
698#if G4VERSION_NUMBER > 1049
700{
701 G4ParticleDefinition* particleDef = particleDefinition->ParticleDefinition();
703}
704
705void BDS::FixGeant105ThreshholdsForParticle(const G4ParticleDefinition* particleDef)
706{
707 // in the case of ions the particle definition isn't available early on so protect
708 // against this
709 if (!particleDef)
710 {return;}
711 // taken from the Geant4.10.5 field01 example
712 // used to compensate for aggressive killing in Geant4.10.5
713 G4double warningEnergy = 1.0 * CLHEP::kiloelectronvolt; // Arbitrary
714 G4double importantEnergy = 10.0 * CLHEP::kiloelectronvolt; // Arbitrary
715 G4double numberOfTrials = 1500; // Arbitrary
716 auto transportPair = BDS::FindTransportation(particleDef);
717 auto transport = transportPair.first;
718 auto coupledTransport = transportPair.second;
719
720 if (transport)
721 {
722 // Change the values of the looping particle parameters of Transportation
723 transport->SetThresholdWarningEnergy(warningEnergy);
724 transport->SetThresholdImportantEnergy(importantEnergy);
725 transport->SetThresholdTrials(numberOfTrials);
726 }
727 else if(coupledTransport)
728 {
729 // Change the values for Coupled Transport
730 coupledTransport->SetThresholdWarningEnergy(warningEnergy);
731 coupledTransport->SetThresholdImportantEnergy(importantEnergy);
732 coupledTransport->SetThresholdTrials(numberOfTrials);
733 }
734}
735
736std::pair<G4Transportation*, G4CoupledTransportation*> BDS::FindTransportation(const G4ParticleDefinition* particleDef)
737{
738 const auto* partPM = particleDef->GetProcessManager();
739
740 G4VProcess* partTransport = partPM->GetProcess("Transportation");
741 auto transport = dynamic_cast<G4Transportation*>(partTransport);
742
743 partTransport = partPM->GetProcess("CoupledTransportation");
744 auto coupledTransport = dynamic_cast<G4CoupledTransportation*>(partTransport);
745
746 return std::make_pair(transport, coupledTransport);
747}
748#endif
Copy of crystal channelling em physics from Geant4.
General exception with possible name of object and message.
A class that holds global options and constants.
static BDSGlobalConstants * Instance()
Access method.
Class to parse an ion particle definition.
Modular physics list based on Geant4 constructors.
Wrapper for particle definition.
G4ParticleDefinition * ParticleDefinition() const
Accessor.
Channelling physics process.
Physics processes required for user tracking limits.
Electromagnetic dissociation for high energy ions.
EM processes for millicharged particles.
High energy muon processes.
A physics constructor to extend the pion decay modes.
std::string particle
beam parameters
Definition beamBase.h:39
double beamKineticEnergy
beam parameters
Definition beamBase.h:42
double beamEnergy
beam parameters
Definition beamBase.h:41
double Ek0
initial beam centroid
Definition beamBase.h:73
std::string beamParticleName
beam parameters
Definition beamBase.h:40
double P0
initial beam centroid
Definition beamBase.h:74
double beamMomentum
beam parameters
Definition beamBase.h:43
double E0
initial beam centroid
Definition beamBase.h:72
Beam class.
Definition beam.h:43
double get_value(std::string name) const
get method (only for doubles)
Definition beam.cc:38
bool HasBeenSet(const std::string &name) const
Whether a parameter has been set using the set_value method or not.
Definition beam.cc:141
List with Efficient Lookup.
Definition fastlist.h:41
void SetRangeCuts(G4VModularPhysicsList *physicsList, G4int verbosity=1)
void ConstructBeamParticleG4(const G4String &name)
void PrintDefinedParticles()
G4bool StrContains(const G4String &str, const G4String &test)
Utility function to simplify lots of syntax changes for pedantic g4 changes.
std::pair< G4Transportation *, G4CoupledTransportation * > FindTransportation(const G4ParticleDefinition *particleDef)
Taken from Geant4 field01 example. Get the two possible transportation processes.
G4String GetFullPath(G4String filename, bool excludeNameFromPath=false, bool useCWDForPrefix=false)
BDSParticleDefinition * ConstructParticleDefinition(const G4String &particleNameIn, G4double totalEnergyIn, G4double kineticEnergyIn, G4double momentumIn, G4double ffact=1)
void ExtendPionDecayChannels(G4VModularPhysicsList *physicsList)
Extend the default pi+- decay channels in Geant4.
G4String LowerCase(const G4String &str)
Utility function to simplify lots of syntax changes for pedantic g4 changes.
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 PrintPrimaryParticleProcesses(const G4String &primaryParticleName)
void BuildMuonBiasing(G4VModularPhysicsList *physicsList)
Build muon splitting biasing and wrap the various processes in the physics list.
void CheckAndSetEnergyValidityRange()
void FixGeant105ThreshholdsForBeamParticle(const BDSParticleDefinition *particleDefinition)
Apply FixGeant105ThreshholdsForParticle to the beam particle definition.
void FixGeant105ThreshholdsForParticle(const G4ParticleDefinition *particleDefinition)
void TurnOffMuonDecay()
Set mu+- as stable so they won't decay.
void ConstructExtendedParticleSet()
void ConstructDesignAndBeamParticle(const GMAD::Beam &beamDefinition, G4double ffact, BDSParticleDefinition *&designParticle, BDSParticleDefinition *&beamParticle, G4bool &beamDifferentFromDesignParticle)
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.
G4VModularPhysicsList * BuildPhysics(const G4String &physicsList, G4int verbosity=1)
G4bool IsIon(const G4ParticleDefinition *particle)
Whether a particle is an ion. A proton is counted NOT as an ion.
void ConstructMinimumParticleSet()
G4VModularPhysicsList * ChannellingPhysicsComplete(G4bool useEMD=false, G4bool regular=false, G4bool em4=false, G4bool emss=false)
Build the physics required for channelling to work correctly.