BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSWrapperMuonSplitting.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#include "BDSPhysicsVectorLinear.hh"
20#include "BDSWrapperMuonSplitting.hh"
21
22#include "G4ParticleDefinition.hh"
23#include "G4Track.hh"
24#include "G4Types.hh"
25#include "G4VParticleChange.hh"
26#include "G4VProcess.hh"
27
28#include <cmath>
29#include <limits>
30#include <vector>
31
33
34BDSWrapperMuonSplitting::BDSWrapperMuonSplitting(G4VProcess* originalProcess,
35 G4int splittingFactorIn,
36 G4double splittingThresholdEKIn,
37 G4int splittingFactor2In,
38 G4double splittingThresholdEK2In,
39 G4bool excludeWeight1ParticlesIn,
40 G4double muonSplittingExclusionWeightIn):
41 BDSWrapperProcess("MuonSplittingWrapper"),
42 splittingFactor(splittingFactorIn),
43 splittingThresholdEK(splittingThresholdEKIn),
44 splittingFactor2(splittingFactor2In),
45 splittingThresholdEK2(splittingThresholdEK2In),
46 excludeWeight1Particles(excludeWeight1ParticlesIn),
47 muonSplittingExclusionWeight(muonSplittingExclusionWeightIn),
48 splitting(nullptr)
49{
50 RegisterProcess(originalProcess);
51 theProcessSubType = originalProcess->GetProcessSubType();
52 theProcessName = "MuonSplittingWrapper("+originalProcess->GetProcessName()+")";
53 if (splittingFactor2 < splittingFactor)
54 {splittingFactor2 = splittingFactor;} // always want more high energy
55 if (splittingThresholdEK2 < splittingThresholdEK)
56 {splittingThresholdEK2 = splittingThresholdEK;}
57
58 std::vector<G4double> eK = {0.8*splittingThresholdEK, splittingThresholdEK};
59 std::vector<G4double> va = {2.0, static_cast<G4double>(splittingFactorIn)};
60 if (splittingThresholdEK2 > splittingThresholdEK)
61 {
62 eK.push_back(splittingThresholdEK2);
63 va.push_back((G4double)splittingFactor2In);
64 }
65 splitting = new BDSPhysicsVectorLinear(eK, va);
66}
67
68BDSWrapperMuonSplitting::~BDSWrapperMuonSplitting()
69{
70 delete splitting;
71}
72
73G4VParticleChange* BDSWrapperMuonSplitting::PostStepDoIt(const G4Track& track,
74 const G4Step& step)
75{
76 G4VParticleChange* particleChange = pRegProcess->PostStepDoIt(track, step);
77
78 if (splittingFactor == 1)
79 {return particleChange;}
80
81 G4double parentEk = track.GetKineticEnergy();
82 if (parentEk < 0.8*splittingThresholdEK) // smaller of the two thresholds by design
83 {return particleChange;}
84
85 G4int nSecondaries = particleChange->GetNumberOfSecondaries();
86 if (nSecondaries == 0)
87 {return particleChange;}
88
89 // if weight == 1
90 if (excludeWeight1Particles && std::abs(track.GetWeight() - 1.0) > std::numeric_limits<double>::epsilon())
91 {return particleChange;}
92
93 if (track.GetWeight() > muonSplittingExclusionWeight)
94 {return particleChange;}
95
96 G4bool muonPresent = false;
97 std::vector<G4int> secondaryPDGIDs;
98 for (G4int i = 0; i < nSecondaries; i++)
99 {
100 G4int secondaryPDGID = particleChange->GetSecondary(i)->GetDefinition()->GetPDGEncoding();
101 muonPresent = std::abs(secondaryPDGID) == 13 || muonPresent;
102 }
103 if (!muonPresent)
104 {return particleChange;}
105
106 // we keep hold of the tracks and manage their memory
107 std::vector<G4Track*> originalSecondaries;
108 std::vector<G4Track*> originalMuons;
109 for (G4int i = 0; i < nSecondaries; i++)
110 {
111 G4Track* secondary = particleChange->GetSecondary(i);
112 if (std::abs(secondary->GetDefinition()->GetPDGEncoding()) != 13)
113 {originalSecondaries.push_back(secondary);}
114 else
115 {originalMuons.push_back(secondary);}
116 }
117
118 G4int nOriginalSecondaries = nSecondaries;
119
120 particleChange->Clear(); // doesn't delete the secondaries
121
122 G4double spf2 = splitting->Value(parentEk);
123 G4int thisTimeSplittingFactor = static_cast<G4int>(std::round(spf2));
124
125 // Attempt to generate more muons. This might be difficult or rare, so we must
126 // tolerate this and go for up to a number.
127 G4int maxTrials = 10 * thisTimeSplittingFactor;
128 G4int nSuccessfulMuonSplits = 0;
129 G4int iTry = 0;
130 std::vector<G4Track*> newMuons;
131 while (iTry < maxTrials && nSuccessfulMuonSplits < thisTimeSplittingFactor-1)
132 {
133 iTry++;
134 particleChange->Clear(); // wipes the vector of tracks, but doesn't delete them
135 particleChange = pRegProcess->PostStepDoIt(track, step);
136 G4bool success = false;
137 for (G4int i = 0; i < particleChange->GetNumberOfSecondaries(); i++)
138 {
139 G4Track* sec = particleChange->GetSecondary(i);
140 if (std::abs(sec->GetDefinition()->GetPDGEncoding()) == 13)
141 {
142 newMuons.push_back(sec);
143 success = true;
144 }
145 else
146 {delete sec;}
147 }
148 particleChange->Clear();
149 if (success)
150 {nSuccessfulMuonSplits++;}
151 }
152
153 particleChange->Clear();
154 particleChange->SetNumberOfSecondaries(nOriginalSecondaries + static_cast<G4int>(newMuons.size()));
155 particleChange->SetSecondaryWeightByProcess(true);
156 if (nSuccessfulMuonSplits == 0)
157 {// we've cleared the original ones, so we have to put them back
158 for (auto secondary : originalSecondaries)
159 {particleChange->AddSecondary(secondary);}
160 for (auto muon : originalMuons)
161 {particleChange->AddSecondary(muon);}
162 return particleChange;
163 }
164
165 // the original muon(s) count as 1 even if there are 2 of them as it's 1x call to the process
166 G4double weightFactor = 1.0 / (static_cast<G4double>(nSuccessfulMuonSplits) + 1.0);
167 // put in the original secondaries with an unmodified weight
168 for (auto aSecondary : originalSecondaries)
169 {particleChange->AddSecondary(aSecondary);}
170 for (auto originalMuon : originalMuons)
171 {
172 G4double existingWeight = originalMuon->GetWeight();
173 G4double newWeight = existingWeight * weightFactor;
174 originalMuon->SetWeight(newWeight);
175 particleChange->AddSecondary(originalMuon);
176 }
177 for (auto newMuon : newMuons)
178 {
179 G4double existingWeight = newMuon->GetWeight();
180 G4double newWeight = existingWeight * weightFactor;
181 newMuon->SetWeight(newWeight);
182 particleChange->AddSecondary(newMuon);
183 }
185 return particleChange;
186}
A vector of some values vs Ek that are linearly interpolated.
G4double Value(G4double eK) const
Get the linearly interpolated value for a given kinetic energy.
static G4int nCallsThisEvent
Counter for understanding occurence.
virtual G4VParticleChange * PostStepDoIt(const G4Track &track, const G4Step &step)
Do the splitting operation.