Geant4 11.3.0
Toolkit for the simulation of the passage of particles through matter
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
G4LENDInelastic.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// //
28// File: G4LENDInelastic.cc //
29// Date: 24 March 2020 //
30// Author: Dennis Wright //
31// //
32// Description: model for inelastic scattering of neutrons, light ions and //
33// gammas at energies of order 20 MeV and lower. //
34// This model uses GIDI particle data which are stored mostly //
35// in spectrum mode. In this mode, spectra are reproduced //
36// for each possible particle type which can result from a //
37// given interaction. Unlike Geant4, this is done without //
38// consideration of event-by-event conservation rules. //
39// Indeed, forcing such conservation on GIDI output products //
40// introduces correlations and distortions in the resulting //
41// spectra which are not present in the data. //
42// //
43// In order to use GIDI data within the Geant4 framework, a //
44// minimal event-by-event baryon number conservation is //
45// enforced which allows deviations of up to 1 GeV without //
46// giving warnings. Neither charge, nor energy, nor momentum //
47// conservation is enforced. Under this scheme, light //
48// fragment (n, p, d, t, alpha) spectra are well reproduced //
49// after a large number of events. Charge and energy //
50// conservation also approach their event-by-event values in //
51// this limit. The mass, charge and energy distributions of //
52// large fragments, however, are not expected to reproduce the //
53// data very well. This is a result of forcing the crude //
54// baryon number conservation and ensuring that the light //
55// fragment spectra are correct. //
56// //
57////////////////////////////////////////////////////////////////////////////////
58
59#include "G4LENDInelastic.hh"
60#include "G4SystemOfUnits.hh"
61#include "G4Nucleus.hh"
62#include "G4IonTable.hh"
63#include <algorithm>
64#include <random>
65
67 G4Nucleus& aTarg)
68{
69 G4ThreeVector projMom = aTrack.Get4Momentum().vect();
70 G4double temp = aTrack.GetMaterial()->GetTemperature();
71
72 G4int iZ = aTarg.GetZ_asInt();
73 G4int iA = aTarg.GetA_asInt();
74 G4int iM = 0;
75 if (aTarg.GetIsotope() != nullptr) iM = aTarg.GetIsotope()->Getm();
76
77 G4double ke = aTrack.GetKineticEnergy();
78
80 theResult->Clear();
81
82 G4GIDI_target* aGIDITarget =
83 get_target_from_map(lend_manager->GetNucleusEncoding(iZ, iA, iM) );
84 if (aGIDITarget == nullptr) {
85// G4cout << " No target found " << G4endl;
86 theParticleChange.Clear();
87 theParticleChange.SetStatusChange(isAlive);
88 theParticleChange.SetEnergyChange(aTrack.GetKineticEnergy());
89 theParticleChange.SetMomentumChange(aTrack.Get4Momentum().vect().unit());
90 return &theParticleChange;
91 }
92// return returnUnchanged(aTrack, theResult);
93
94 // Get GIDI final state products for givern target and projectile
95 G4int loop(0);
96 G4int loopMax = 1000;
97 std::vector<G4GIDI_Product>* products;
98 do {
99 products = aGIDITarget->getOthersFinalState(ke*MeV, temp, MyRNG, NULL);
100 loop++;
101 } while (products == nullptr && loop < loopMax);
102
103 // G4LENDInelastic accepts all light fragments and gammas from GIDI (A < 5)
104 // and removes any heavy fragments which cause large baryon number violation.
105 // Charge and energy non-conservation still occur, but over a large number
106 // of events, this improves on average.
107
108 if (loop > loopMax - 1) {
109// G4cout << " too many loops, return initial state " << G4endl;
110
111 theParticleChange.Clear();
112 theParticleChange.SetStatusChange(isAlive);
113 theParticleChange.SetEnergyChange(aTrack.GetKineticEnergy());
114 theParticleChange.SetMomentumChange(aTrack.Get4Momentum().vect().unit());
115 return &theParticleChange;
116
117// if (aTrack.GetDefinition() == G4Proton::Proton() ||
118// aTrack.GetDefinition() == G4Neutron::Neutron() ) {
119// theResult = preco->ApplyYourself(aTrack, aTarg);
120// } else {
121// theResult = returnUnchanged(aTrack, theResult);
122// }
123
124 } else {
125 G4int iTotZ = iZ + aTrack.GetDefinition()->GetAtomicNumber();
126 G4int iTotA = iA + aTrack.GetDefinition()->GetAtomicMass();
127
128 // Loop over GIDI products and separate light from heavy fragments
129 G4int GZtot(0);
130 G4int GAtot(0);
131 G4int productA(0);
132 G4int productZ(0);
133 std::vector<G4int> lightProductIndex;
134 std::vector<G4int> heavyProductIndex;
135 for (G4int i = 0; i < int( products->size() ); i++ ) {
136 productA = (*products)[i].A;
137 if (productA < 5) {
138 lightProductIndex.push_back(i);
139 GZtot += (*products)[i].Z;
140 GAtot += productA;
141 } else {
142 heavyProductIndex.push_back(i);
143 }
144 }
145
146 // Randomize order of heavies to correct somewhat for sampling bias
147 // std::random_shuffle(heavyProductIndex.begin(), heavyProductIndex.end() );
148 // std::cout << " Heavy product index before shuffle : " ;
149 // for (G4int i = 0; i < int(heavyProductIndex.size() ); i++) std::cout << heavyProductIndex[i] << ", " ;
150 // std::cout << std::endl;
151
152 auto rng = std::default_random_engine {};
153 std::shuffle(heavyProductIndex.begin(), heavyProductIndex.end(), rng);
154
155 // std::cout << " Heavy product index after shuffle : " ;
156 // for (G4int i = 0; i < int(heavyProductIndex.size() ); i++) std::cout << heavyProductIndex[i] << ", " ;
157 // std::cout << std::endl;
158
159 std::vector<G4int> savedHeavyIndex;
160 G4int itest(0);
161 for (G4int i = 0; i < int(heavyProductIndex.size() ); i++) {
162 itest = heavyProductIndex[i];
163 productA = (*products)[itest].A;
164 productZ = (*products)[itest].Z;
165 if ((GAtot + productA <= iTotA) && (GZtot + productZ <= iTotZ) ) {
166 savedHeavyIndex.push_back(itest);
167 GZtot += productZ;
168 GAtot += productA;
169 }
170 }
171
172/*
173 G4cout << " saved light products = ";
174 for (G4int k = 0; k < int(lightProductIndex.size() ); k++ ) {
175 itest = lightProductIndex[k];
176 G4cout << "(" << (*products)[itest].Z << ", " << (*products)[itest].A << "), ";
177 }
178 G4cout << G4endl;
179
180 G4cout << " saved heavy products = ";
181 for (G4int k = 0; k < int(savedHeavyIndex.size() ); k++ ) {
182 itest = savedHeavyIndex[k];
183 G4cout << "(" << (*products)[itest].Z << ", " << (*products)[itest].A << "), ";
184 }
185 G4cout << G4endl;
186*/
187 // Now convert saved products to Geant4 particles
188 // Note that, at least for heavy fragments, GIDI masses and Geant4 masses
189 // have slightly different values.
190
191 G4DynamicParticle* theSec = nullptr;
192 G4ThreeVector Psum;
193 for (G4int i = 0; i < int(lightProductIndex.size() ); i++) {
194 itest = lightProductIndex[i];
195 productZ = (*products)[itest].Z;
196 productA = (*products)[itest].A;
197 theSec = new G4DynamicParticle();
198 if (productA == 1 && productZ == 0) {
200 } else if (productA == 1 && productZ == 1) {
202 } else if (productA == 2 && productZ == 1) {
204 } else if (productA == 3 && productZ == 1) {
206 } else if (productA == 4 && productZ == 2) {
207 theSec->SetDefinition(G4Alpha::Alpha() );
208 } else {
209 theSec->SetDefinition(G4Gamma::Gamma() );
210 }
211
212 G4ThreeVector momentum((*products)[itest].px*MeV,
213 (*products)[itest].py*MeV,
214 (*products)[itest].pz*MeV );
215 Psum += momentum;
216 theSec->SetMomentum(momentum);
217// theResult->AddSecondary(theSec);
218 theParticleChange.AddSecondary(theSec, secID);
219 }
220
221 G4int productM(0);
222 for (G4int i = 0; i < int(savedHeavyIndex.size() ); i++) {
223 itest = savedHeavyIndex[i];
224 productZ = (*products)[itest].Z;
225 productA = (*products)[itest].A;
226 productM = (*products)[itest].m;
227 theSec = new G4DynamicParticle();
228 theSec->SetDefinition(G4IonTable::GetIonTable()->GetIon(productZ,
229 productA,
230 productM) );
231 G4ThreeVector momentum((*products)[itest].px*MeV,
232 (*products)[itest].py*MeV,
233 (*products)[itest].pz*MeV );
234 Psum += momentum;
235 theSec->SetMomentum(momentum);
236// theResult->AddSecondary(theSec);
237 theParticleChange.AddSecondary(theSec, secID);
238 }
239
240 // Create heavy fragment if necessary to try to balance A, Z
241 // Note: this step is only required to prevent warnings at the process level
242 // where "catastrophic" non-conservation tolerances are set to 1 GeV.
243 // The residual generated will not necessarily be the one that would
244 // occur in the actual reaction.
245 if (iTotA - GAtot > 1) {
246 theSec = new G4DynamicParticle();
247 if (iTotZ == GZtot) {
248 // Special case when a nucleus of only neutrons is requested
249 // Violate charge conservation and set Z = 1
250 // G4cout << " Z = 1, A = "<< iTotA - GAtot << " created " << G4endl;
251 theSec->SetDefinition(G4IonTable::GetIonTable()->GetIon(1, iTotA-GAtot, 0) );
252 } else {
253 theSec->SetDefinition(G4IonTable::GetIonTable()->GetIon(iTotZ-GZtot, iTotA-GAtot, 0) );
254 }
255 theSec->SetMomentum(projMom - Psum);
256// theResult->AddSecondary(theSec);
257 theParticleChange.AddSecondary(theSec, secID);
258 }
259 } // loop OK
260
261 delete products;
262// theResult->SetStatusChange( stopAndKill );
263 theParticleChange.SetStatusChange( stopAndKill );
264// return theResult;
265 return &theParticleChange;
266}
@ isAlive
@ stopAndKill
double MyRNG(void *)
CLHEP::Hep3Vector G4ThreeVector
double G4double
Definition G4Types.hh:83
int G4int
Definition G4Types.hh:85
Hep3Vector unit() const
Hep3Vector vect() const
static G4Alpha * Alpha()
Definition G4Alpha.cc:83
static G4Deuteron * Deuteron()
Definition G4Deuteron.cc:90
void SetDefinition(const G4ParticleDefinition *aParticleDefinition)
void SetMomentum(const G4ThreeVector &momentum)
std::vector< G4GIDI_Product > * getOthersFinalState(double e_in, double temperature, double(*rng)(void *), void *rngState)
static G4Gamma * Gamma()
Definition G4Gamma.cc:81
const G4Material * GetMaterial() const
const G4ParticleDefinition * GetDefinition() const
G4double GetKineticEnergy() const
const G4LorentzVector & Get4Momentum() const
static G4IonTable * GetIonTable()
G4int Getm() const
Definition G4Isotope.hh:89
G4HadFinalState * ApplyYourself(const G4HadProjectile &aTrack, G4Nucleus &aTargetNucleus)
G4LENDManager * lend_manager
G4GIDI_target * get_target_from_map(G4int nuclear_code)
G4double GetTemperature() const
static G4Neutron * Neutron()
Definition G4Neutron.cc:101
G4int GetA_asInt() const
Definition G4Nucleus.hh:99
G4int GetZ_asInt() const
Definition G4Nucleus.hh:105
const G4Isotope * GetIsotope()
Definition G4Nucleus.hh:111
G4int GetAtomicNumber() const
G4int GetAtomicMass() const
static G4Proton * Proton()
Definition G4Proton.cc:90
static G4Triton * Triton()
Definition G4Triton.cc:90