Geant4 11.4.0
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
G4FermiBreakUpAN.cc
Go to the documentation of this file.
1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26//
27// G4FermiBreakUpAN alternative FermiBreakUp model
28// by A. Novikov (January 2025)
29//
30
31#include "G4FermiBreakUpAN.hh"
32
33#include "G4FermiDataTypes.hh"
36#include "G4FermiParticle.hh"
37#include "G4FermiPhaseDecay.hh"
38#include "G4FermiSplitter.hh"
39#include "G4VFermiFragmentAN.hh"
40
42#include "G4NucleiProperties.hh"
45#include "G4ThreeVector.hh"
46#include "Randomize.hh"
47
48#include <numeric>
49#include <functional>
50
51#ifdef G4VERBOSE
52# define G4FERMI_VERBOSE 1
53#else
54# define G4FERMI_VERBOSE 0
55#endif
56
57#define FERMI_LOG_MSG(verbosity, level, msg) \
58 do { \
59 if (G4FERMI_VERBOSE) { \
60 if ((verbosity) >= (level)) { \
61 G4cout << __FILE__ << ':' << __LINE__ << " in function \"" << __FUNCTION__ << "\"\n" \
62 << msg << G4endl; \
63 } \
64 } \
65 } while (0)
66
67constexpr G4int FERMI_DEBUG = 2;
68
69#define FERMI_LOG_WARN(verbosity, msg) FERMI_LOG_MSG(verbosity, FERMI_WARN, msg)
70#define FERMI_LOG_DEBUG(verbosity, msg) FERMI_LOG_MSG(verbosity, FERMI_DEBUG, msg)
71
72namespace
73{
74constexpr const char* SPACES_OFFSET = " ";
75
76std::size_t SampleWeightDistribution(const std::vector<G4double>& weights)
77{
78 const auto totalWeight = std::accumulate(weights.begin(), weights.end(), 0.);
79 FERMI_ASSERT_MSG(totalWeight > 0., "Invalid weights: all values are zero");
80
81 const auto targetWeight = G4RandFlat::shoot() * totalWeight;
82 G4double cummulativeWeight = 0;
83 for (std::size_t i = 0; i < weights.size(); ++i) {
84 cummulativeWeight += weights[i];
85
86 if (cummulativeWeight >= targetWeight) {
87 return i;
88 }
89 }
90
91 return weights.size() - 1;
92}
93
94G4String LogProducts(const std::vector<G4FermiParticle>& particles)
95{
96 std::ostringstream out;
97
98 out << "[\n";
99 for (const auto& particle : particles) {
100 out << SPACES_OFFSET << particle << ";\n";
101 }
102 out << "]";
103
104 return std::move(out).str();
105}
106
107G4LorentzVector ChangeFrameOfReference(const G4LorentzVector& vec, const G4ThreeVector& boost)
108{
109 auto copy = vec;
110 copy.boost(boost);
111 return copy;
112}
113
114G4String LogSplit(const G4FermiFragmentVector& split)
115{
116 std::ostringstream out;
117
118 out << "[\n";
119 for (const auto fragmentPtr : split) {
120 out << SPACES_OFFSET << *fragmentPtr << ";\n";
121 }
122 out << "]";
123
124 return std::move(out).str();
125}
126
127std::size_t GetSlot(G4FermiAtomicMass atomicMass, G4FermiChargeNumber chargeNumber)
128{
129 const auto mass = static_cast<std::uint32_t>(atomicMass);
130 const auto charge = static_cast<std::uint32_t>(chargeNumber);
131 return (mass * (mass + 1)) / 2 + charge;
132}
133} // namespace
134
135G4FermiBreakUpAN::PossibleSplits::PossibleSplits(const G4FermiAtomicMass maxAtomicMass)
136{
137 const auto maxMass = static_cast<std::uint32_t>(maxAtomicMass);
138 splits_.resize(maxMass * (maxMass + 1) / 2);
139}
140
141const std::vector<G4FermiFragmentVector>&
142G4FermiBreakUpAN::PossibleSplits::GetSplits(const G4FermiAtomicMass atomicMass,
143 const G4FermiChargeNumber chargeNumber) const
144{
145 const auto slot = GetSlot(atomicMass, chargeNumber);
146 return splits_.at(slot);
147}
148
149void G4FermiBreakUpAN::PossibleSplits::InsertSplits(const G4FermiAtomicMass atomicMass,
150 const G4FermiChargeNumber chargeNumber,
151 std::vector<G4FermiFragmentVector>&& splits)
152{
153 const auto slot = GetSlot(atomicMass, chargeNumber);
154
155 if (slot >= splits_.size()) {
156 splits_.resize(slot + static_cast<std::uint32_t>(atomicMass));
157 }
158
159 splits_[slot] = std::move(splits);
160}
161
163 : splits_(G4FermiAtomicMass(MAX_A)),
164 secID_(G4PhysicsModelCatalog::GetModelID("model_G4FermiBreakUpVI")),
165 verbosity_(verbosity)
166{}
167
168std::vector<G4FermiParticle> G4FermiBreakUpAN::BreakItUp(const G4FermiParticle& particle) const
169{
170 FERMI_LOG_DEBUG(verbosity_, "Breaking up particle: " << particle);
171
172 if (particle.GetExcitationEnergy() < 0.) {
173 FERMI_LOG_DEBUG(verbosity_, "G4FermiParticle is stable with excitation energy = "
174 << particle.GetExcitationEnergy());
175 return {particle};
176 }
177
178 const auto& splits = splits_.GetSplits(particle.GetAtomicMass(), particle.GetChargeNumber());
179 FERMI_LOG_DEBUG(verbosity_,
180 "Selecting Split for " << particle << " from " << splits.size() << " splits");
181 if (splits.empty()) {
182 FERMI_LOG_DEBUG(verbosity_, "No splits found");
183 return {particle};
184 }
185
186 // get phase space weights for every split
187 // we can't cache them, because calculations is probabilistic
188 weights_.resize(splits.size());
189 std::transform(splits.begin(), splits.end(), weights_.begin(),
190 [atomicMass = particle.GetAtomicMass(),
191 totalEnergy = particle.GetMomentum().m()](const auto& split) {
192 return G4FermiSplitter::DecayWeight(split, atomicMass, totalEnergy);
193 });
194
195 if (std::all_of(weights_.begin(), weights_.end(), [](auto weight) { return weight == 0.; })) {
196 FERMI_LOG_DEBUG(verbosity_, "Every split has zero weight");
197 return {particle};
198 }
199
200 const auto& chosenSplit = splits[SampleWeightDistribution(weights_)];
201 FERMI_LOG_DEBUG(verbosity_,
202 "From " << splits.size() << " splits chosen split: " << LogSplit(chosenSplit));
203
204 return SplitToParticles(particle, chosenSplit);
205}
206
208{
209 if (G4NucleiProperties::GetNuclearMass(2, 0) <= 0.) {
211 pCBar.ConstructParticle();
212 }
214
215 {
217 pool.Initialize();
219 }
220
221 // order is important here, we use G4FermiFragmentPool to create splits!
222 splits_ = PossibleSplits();
223 for (auto a = 1; a < MAX_A; ++a) {
224 for (auto z = 0; z <= a; ++z) {
225 const auto atomicMass = G4FermiAtomicMass(a);
226 const auto chargeNumber = G4FermiChargeNumber(z);
227
228 splits_.InsertSplits(atomicMass, chargeNumber,
229 G4FermiSplitter::GenerateSplits({atomicMass, chargeNumber}));
230 }
231 }
232}
233
235{
236 return Z < MAX_Z && A < MAX_A;
237}
238
240{
241 if (theNucleus == nullptr || results == nullptr) {
243 ed << "G4Fragment or result G4FragmentVector is not set in FermiBreakUp";
244 G4Exception("G4FermiBreakUpAN::BreakFragment()", "Fermi003", FatalErrorInArgument, ed);
245 return;
246 }
247
248 const auto particle =
250 G4FermiChargeNumber(theNucleus->GetZ_asInt()), theNucleus->GetMomentum());
251 const auto fragments = BreakItUp(particle);
252
253 // decay impossible
254 if (fragments.size() <= 1) { return; }
255
256 const auto creationTime = theNucleus->GetCreationTime();
257 // primary should be deleted
258 delete theNucleus;
259
260 for (const auto& fragment : fragments) {
261 auto fr = new G4Fragment(static_cast<G4int>(fragment.GetAtomicMass()),
262 static_cast<G4int>(fragment.GetChargeNumber()),
263 fragment.GetMomentum());
264 results->push_back(fr);
265 fr->SetCreationTime(creationTime);
266 fr->SetCreatorModelID(secID_);
267 }
268}
269
270std::vector<G4FermiParticle>
271G4FermiBreakUpAN::SplitToParticles(const G4FermiParticle& sourceParticle,
272 const G4FermiFragmentVector& split) const
273{
274 std::vector<G4double> splitMasses(split.size());
275 std::transform(split.begin(), split.end(), splitMasses.begin(),
277
278 G4FermiPhaseDecay phaseSampler;
279 std::vector<G4LorentzVector> particlesMomentum
280 = phaseSampler.CalculateDecay(sourceParticle.GetMomentum(), splitMasses);
281
282 if (particlesMomentum.empty()) {
283 return {sourceParticle};
284 }
285
286 // Go back to the Lab Frame
287 std::vector<G4FermiParticle> particleSplit;
288 particleSplit.reserve(2 * split.size());
289 const auto boostVector = sourceParticle.GetMomentum().boostVector();
290 for (std::size_t fragmentIdx = 0; fragmentIdx < split.size(); ++fragmentIdx) {
291 const auto fragmentMomentum =
292 ChangeFrameOfReference(particlesMomentum[fragmentIdx], boostVector);
293 split[fragmentIdx]->AppendDecayFragments(fragmentMomentum, particleSplit);
294 }
295
296 FERMI_LOG_DEBUG(verbosity_, "Break up products: " << LogProducts(particleSplit));
297 return particleSplit;
298}
@ FatalErrorInArgument
void G4Exception(const char *originOfException, const char *exceptionCode, G4ExceptionSeverity severity, const char *description)
std::ostringstream G4ExceptionDescription
#define FERMI_LOG_DEBUG(verbosity, msg)
constexpr G4int FERMI_DEBUG
#define FERMI_ASSERT_MSG(COND, MSG)
std::vector< G4Fragment * > G4FragmentVector
Definition G4Fragment.hh:65
CLHEP::HepLorentzVector G4LorentzVector
CLHEP::Hep3Vector G4ThreeVector
double G4double
Definition G4Types.hh:83
bool G4bool
Definition G4Types.hh:86
int G4int
Definition G4Types.hh:85
std::vector< const G4VFermiFragmentAN * > G4FermiFragmentVector
const G4double A[17]
Hep3Vector boostVector() const
static void ConstructParticle()
void BreakFragment(G4FragmentVector *results, G4Fragment *theNucleus) override
G4bool IsApplicable(G4int Z, G4int A, G4double eexc) const override
G4FermiBreakUpAN(G4int verbosity=0)
void Initialise() override
std::vector< G4FermiParticle > BreakItUp(const G4FermiParticle &nucleus) const
void Initialize(const DataSource &dataSource)
static G4FermiFragmentPoolAN & Instance()
static G4FermiNucleiProperties & Instance()
const G4LorentzVector & GetMomentum() const
G4double GetExcitationEnergy() const
G4FermiAtomicMass GetAtomicMass() const
G4FermiChargeNumber GetChargeNumber() const
std::vector< G4LorentzVector > CalculateDecay(const G4LorentzVector &totalMomentum, const std::vector< G4double > &fragmentsMass) const
static void GenerateSplits(G4FermiNucleiData nucleiData, std::vector< G4FermiFragmentVector > &splits)
const G4LorentzVector & GetMomentum() const
G4double GetCreationTime() const
G4int GetZ_asInt() const
G4int GetA_asInt() const
static G4double GetNuclearMass(const G4double A, const G4double Z)
G4double GetTotalEnergy() const