Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/processes/hadronic/models/nudex/src/G4NuDEXNeutronCaptureModel.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  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 //
 29 //      GEANT4 source file 
 30 //
 31 //      File name:     G4NuDEXNeutronCaptureModel
 32 //
 33 //      Author:        E.Mendoza & A.Ribon
 34 // 
 35 //      Creation date: 29 May 2024
 36 //
 37 //      Description:   This class (a proxy of the class G4NuDEX) uses
 38 //                     the NuDEX model to produce gammas and internal
 39 //                     conversion electrons from neutron capture.
 40 //                     Whenever NuDEX is not applicable, G4PhotonEvaporation
 41 //                     is used.
 42 //                     The implementation of this class follows the code
 43 //                     of the class G4NeutronRadCapture.
 44 //
 45 //      Modifications:
 46 //      
 47 // -------------------------------------------------------------------
 48 //
 49 
 50 #include "G4NuDEXNeutronCaptureModel.hh"
 51 #include "G4NuDEXStatisticalNucleus.hh"
 52 #include "Randomize.hh"
 53 #include "G4SystemOfUnits.hh"
 54 #include "G4PhysicalConstants.hh"
 55 #include "G4LorentzVector.hh"
 56 #include "G4Gamma.hh"
 57 #include "G4Electron.hh"
 58 #include "G4Positron.hh"
 59 #include "G4Deuteron.hh"
 60 #include "G4Triton.hh"
 61 #include "G4He3.hh"
 62 #include "G4Alpha.hh"
 63 #include "G4NucleiProperties.hh"
 64 #include "G4IonTable.hh"
 65 #include "G4ParticleTable.hh"
 66 #include "G4HadronicParameters.hh"
 67 #include "G4DeexPrecoParameters.hh"
 68 #include "G4NuclearLevelData.hh"
 69 #include "G4PhotonEvaporation.hh"
 70 #include "G4PhysicsModelCatalog.hh"
 71 
 72 
 73 G4NuDEXNeutronCaptureModel::G4NuDEXNeutronCaptureModel() : G4HadronicInteraction( "nuDEX_neutronCapture" ) {
 74   for ( G4int i = 0; i < G4NUDEX_MAXZA; i++ ) {
 75     theStatisticalNucleus[i] = nullptr;
 76     HasData[i] = 0;
 77   }
 78   BrOption = -1;
 79   BandWidth = 0;
 80   NuDEXLibDirectory = "";
 81   photonEvaporation = nullptr;
 82   auto ch = G4FindDataDir( "G4NUDEXLIBDATA" );
 83   if ( ch == nullptr ) {
 84     G4Exception( "G4NuDEXNeutronCaptureModel()", "had0707", FatalException, "Environment variable G4NUDEXLIBDATA is not defined" );
 85   } else {
 86     NuDEXLibDirectory = G4String(ch);
 87   }
 88 }
 89 
 90 
 91 void G4NuDEXNeutronCaptureModel::InitialiseModel() {
 92   if ( photonEvaporation != nullptr ) return;
 93   G4DeexPrecoParameters* param = G4NuclearLevelData::GetInstance()->GetParameters();
 94   minExcitation = param->GetMinExcitation();
 95   photonEvaporation = new G4PhotonEvaporation;
 96   photonEvaporation->Initialise();
 97   photonEvaporation->SetICM( true );
 98   secID = G4PhysicsModelCatalog::GetModelID( "model_" + GetModelName() );
 99   lowestEnergyLimit = 10.0*CLHEP::eV;
100   minExcitation = 0.1*CLHEP::keV;
101 }
102 
103 
104 G4NuDEXNeutronCaptureModel::~G4NuDEXNeutronCaptureModel(){
105   for ( G4int i = 0; i < G4NUDEX_MAXZA; i++ ) {
106     if ( theStatisticalNucleus[i] ) delete theStatisticalNucleus[i];
107   }
108 }
109 
110 
111 G4HadFinalState* G4NuDEXNeutronCaptureModel::ApplyYourself( const G4HadProjectile &aTrack, G4Nucleus &theNucleus ) {
112   theParticleChange.Clear();
113   theParticleChange.SetStatusChange( stopAndKill );
114   G4int A = theNucleus.GetA_asInt();
115   G4int Z = theNucleus.GetZ_asInt();
116   G4double time = aTrack.GetGlobalTime();  // Time in the lab frame
117   // Create initial state
118   G4LorentzVector lab4mom( 0.0, 0.0, 0.0, G4NucleiProperties::GetNuclearMass(A, Z) );
119   lab4mom += aTrack.Get4Momentum();
120   G4double systemMass = lab4mom.mag();
121   ++A;  // Compound nucleus: target nucleus + neutron
122   G4double compoundMass = G4NucleiProperties::GetNuclearMass(A, Z);
123   // If the energy available is to small to do anything interesting, gives up
124   if ( systemMass - compoundMass  <= lowestEnergyLimit ) return &theParticleChange;
125   G4ThreeVector boostFromCMtoLAB = lab4mom.boostVector();
126   G4double neutronEnergy = aTrack.GetKineticEnergy();
127 
128   // Try to apply NuDEX
129   //G4int lspin = 0;     // l-spin = 0, 1, 2 --> s-wave, p-wave, d-wave ... 
130   //G4int jspinx2 = -1;  // A negative value of jspinx2 means that is sampled according to the 2J+1 rule.
131   //G4int initialLevel = SelectInitialLevel( Z, A, neutronEnergy, lspin, jspinx2 );
132   G4int initialLevel = -1;  // thermal neutron capture
133   std::vector< char > pType;
134   std::vector< G4double > pEnergy, pTime;
135   G4int npar = GenerateNeutronCaptureCascade( Z, A, neutronEnergy, initialLevel, pType, pEnergy, pTime );
136   if ( npar > 0 ) {  // NuDEX can be applied
137     
138     G4LorentzVector remainingLab4mom = lab4mom;
139     G4double latestEmission = time;
140     // Loop over the EM particles produced by 'GenerateNeutronCaptureCascade' and add them to the
141     // theParticleChange as secondaries. These particles are produced by NuDEX in the nucleus' rest-frame.
142     for ( G4int i = 0; i < npar; i++ ) {
143       G4ParticleDefinition* particleDef = nullptr;
144       if ( pType.at(i) == 'g' ) {
145         particleDef = G4Gamma::Definition();
146       } else if  (pType.at(i) == 'e' ) {
147         particleDef = G4Electron::Definition();
148       } else if ( pType.at(i) == 'p' ) {
149         particleDef = G4Positron::Definition();
150       } else {
151         G4Exception( "G4NUDEXNeutronCaptureModel::ApplyYourself()", "had0707", FatalException, "Unknown particle type" );
152       }
153       G4double phi = G4UniformRand()*twopi;
154       G4double costheta = 2.0*G4UniformRand() - 1.0;
155       G4double sintheta = std::sqrt( 1.0 - costheta*costheta );
156       G4ThreeVector direction( sintheta*std::cos(phi), sintheta*std::sin(phi), costheta );
157       G4double mass = particleDef->GetPDGMass();
158       G4double particle3momMod = std::sqrt( pEnergy.at(i) * ( pEnergy.at(i) + 2.0*mass ) );
159       G4LorentzVector particle4mom( particle3momMod*direction, mass + pEnergy.at(i) );  // In the center-of-mass frame
160       particle4mom.boost( boostFromCMtoLAB );  // Now in the Lab frame
161       G4HadSecondary* secondary = new G4HadSecondary( new G4DynamicParticle( particleDef, particle4mom ) );
162       remainingLab4mom -= particle4mom;
163       // For simplicity, we neglect below the different frames of time (Lab) and pTime (center-of-mass) 
164       secondary->SetTime( time + pTime.at(i) );
165       if ( latestEmission < time + pTime.at(i) ) latestEmission = time + pTime.at(i);
166       secondary->SetCreatorModelID( secID );
167       theParticleChange.AddSecondary( *secondary );
168       delete secondary;
169     }
170     // Treat now the residual nucleus (which is neglected by NuDEX)
171     const G4ParticleDefinition* resNuclDef = nullptr;
172     if      ( Z == 1  &&  A == 2 ) resNuclDef = G4Deuteron::Definition();
173     else if ( Z == 1  &&  A == 3 ) resNuclDef = G4Triton::Definition();
174     else if ( Z == 2  &&  A == 3 ) resNuclDef = G4He3::Definition();
175     else if ( Z == 2  &&  A == 4 ) resNuclDef = G4Alpha::Alpha();
176     else resNuclDef = G4ParticleTable::GetParticleTable()->GetIonTable()->GetIon( Z, A, 0.0, noFloat, 0 );
177     if ( resNuclDef ) {
178       // To conserve energy-momentum, remainingLab4mom should be the Lorentz 4-momentum of the residual nucleus.
179       // Imposing the mass 'compoundMass' to the residual nucleus, and trying to conserve the total energy
180       // while keeping as low as possible the violation of the 3-momentum; in the case that the total energy
181       // cannot be conserved, the residual nucleus is produced at rest (in the Lab frame).
182       G4double resNuclLabEkin = std::max( remainingLab4mom.e() - compoundMass, 0.0 );
183       G4double resNuclLab3momMod = 0.0;
184       G4ThreeVector resNuclLabDir( 0.0, 0.0, 0.0 );
185       if ( resNuclLabEkin > 0.0 ) {
186         resNuclLab3momMod = std::sqrt( resNuclLabEkin * ( resNuclLabEkin + 2.0*compoundMass ) );
187         resNuclLabDir = remainingLab4mom.vect().unit();
188       }
189       G4LorentzVector resNuclLab4mom( resNuclLab3momMod*resNuclLabDir, resNuclLabEkin + compoundMass );
190       G4HadSecondary* secondary = new G4HadSecondary( new G4DynamicParticle( resNuclDef, resNuclLab4mom ) );
191       secondary->SetTime( latestEmission );
192       secondary->SetCreatorModelID( secID );
193       theParticleChange.AddSecondary( *secondary );
194       delete secondary;
195     }
196 
197   } else {  // NuDEX cannot be applied: use G4PhotonEvaporation
198 
199     // Code taken from G4NeutronRadCapture
200 
201     G4Fragment* aFragment = new G4Fragment( A, Z, lab4mom );
202     G4FragmentVector* fv = photonEvaporation->BreakUpFragment( aFragment );
203     if ( fv == nullptr ) fv = new G4FragmentVector;
204     fv->push_back( aFragment );
205     size_t n = fv->size();
206     for ( size_t i = 0; i < n; ++i ) {
207       G4Fragment* f = (*fv)[i];    
208       G4double etot = f->GetMomentum().e();
209       Z = f->GetZ_asInt();
210       A = f->GetA_asInt();
211       const G4ParticleDefinition* theDef = nullptr;
212       if      ( Z == 0  && A == 0 ) { theDef = f->GetParticleDefinition(); }
213       else if ( Z == 1  && A == 2 ) { theDef = G4Deuteron::Definition(); }
214       else if ( Z == 1  && A == 3 ) { theDef = G4Triton::Definition(); }
215       else if ( Z == 2  && A == 3 ) { theDef = G4He3::Definition(); }
216       else if ( Z == 2  && A == 4 ) { theDef = G4Alpha::Definition(); }
217       else {
218         G4double eexc = f->GetExcitationEnergy();
219         if ( eexc <= minExcitation ) eexc = 0.0;
220         theDef = G4ParticleTable::GetParticleTable()->GetIonTable()->GetIon( Z, A, eexc, noFloat, 0 );
221       }
222       G4double ekin = std::max( 0.0, etot - theDef->GetPDGMass() );
223       G4HadSecondary* news = new G4HadSecondary( new G4DynamicParticle( theDef, f->GetMomentum().vect().unit(), ekin ) );
224       G4double timeF = f->GetCreationTime();
225       if ( timeF < 0.0 ) timeF = 0.0;
226       news->SetTime( time + timeF );
227       news->SetCreatorModelID( secID );
228       theParticleChange.AddSecondary( *news );
229       delete news;
230       delete f;
231     }
232     delete fv;
233   }
234   
235   return &theParticleChange;
236 }
237 
238 
239 G4int G4NuDEXNeutronCaptureModel::GenerateNeutronCaptureCascade( G4int theZ, G4int theA, G4double NeutronEnergy, G4int InitialLevel,
240                                    std::vector< char >& pType, std::vector< G4double >& pEnergy,
241                                    std::vector< G4double >& pTime ) {
242   // Returns the number of emitted particles. Returns -1 if the nucleus is not in the database.
243   G4int theZA = 1000*theZ + theA;
244   G4int check = Init( theZA );
245   if ( check < 0 ) return -1;
246   G4double Sn, I0;
247   theStatisticalNucleus[theZA]->GetSnAndI0( Sn, I0 ); Sn *= MeV;  // I0 is the spin of the A-1 nucleus in the g.s.
248   G4double ExcitationEnergy = Sn + (theA-1.0)/(G4double)theA*NeutronEnergy;
249   G4int nPar = theStatisticalNucleus[theZA]->GenerateCascade( InitialLevel, ExcitationEnergy/MeV, pType, pEnergy, pTime );
250   for ( G4int i = 0; i < nPar; i++ ) {
251     pEnergy.at(i) *= MeV;
252     pTime.at(i) *= s;
253   }
254   return nPar;
255 }
256 
257 
258 G4int G4NuDEXNeutronCaptureModel::Init( G4int theZA, unsigned int seed1, unsigned int seed2, unsigned int seed3 ) {
259   if ( HasData[theZA] == -1 ) return -1;
260   if ( HasData[theZA] ==  1 ) return 0;
261   if ( theStatisticalNucleus[theZA] == 0 ) {
262     G4int theZ = theZA/1000;
263     G4int theA = theZA-1000*theZ;
264     theStatisticalNucleus[theZA] = new G4NuDEXStatisticalNucleus( theZ, theA );
265     if ( BandWidth != 0 ) theStatisticalNucleus[theZA]->SetBandWidth( BandWidth );
266     theStatisticalNucleus[theZA]->SetBrOption( BrOption );
267     if ( seed1 > 0 ) theStatisticalNucleus[theZA]->SetRandom1Seed( seed1 );
268     if ( seed2 > 0 ) theStatisticalNucleus[theZA]->SetRandom1Seed( seed2 );
269     if ( seed3 > 0 ) theStatisticalNucleus[theZA]->SetRandom1Seed( seed3 );
270     G4int check = theStatisticalNucleus[theZA]->Init( NuDEXLibDirectory.c_str() );
271     if ( check < 0 ) {
272       HasData[theZA] = -1;
273       return -1;
274     } else {
275       HasData[theZA] = 1;
276     }
277   }
278   return 0;
279 }
280 
281 
282 G4int G4NuDEXNeutronCaptureModel::SelectInitialLevel( G4int theCompoundZ, G4int theCompoundA, G4double NeutronEnergy, G4int lspin, G4int jspinx2 ) {
283   // Initial level for neutron capture. If jspinx2 < 0 it is sampled according to the 2J+1 rule.
284   // l-spin = 0, 1, 2 --> s-wave, p-wave, d-wave ...
285   G4int theZ = theCompoundZ;
286   G4int theA = theCompoundA;
287   G4int theZA = 1000*theZ + theA;
288   G4int check = Init( theZA );
289   if ( check < 0 ) return -1;
290   G4double Sn, I0;
291   theStatisticalNucleus[theZA]->GetSnAndI0( Sn, I0 ); Sn *= MeV;  // I0 is the spin of the A-1 nucleus in the g.s.
292   G4double ExcitationEnergy = Sn + (theA-1.0)/(G4double)theA*NeutronEnergy;
293   if ( lspin < 0 ) lspin = 0;
294   if ( jspinx2 < 0 ) jspinx2 = SampleJ( theZ, theA, lspin );
295   G4bool parity = false;
296   if ( ( I0 >= 0  &&  (lspin%2) == 0 ) || ( I0 < 0  &&  (lspin%2) == 1 ) ) parity = true;
297   G4int InitialLevel = theStatisticalNucleus[theZA]->GetClosestLevel( ExcitationEnergy/MeV, jspinx2, parity );
298   return InitialLevel;
299 }
300 
301 
302 G4int G4NuDEXNeutronCaptureModel ::SampleJ( G4int theCompoundZ, G4int theCompoundA, G4int lspin ) {
303   // Samples J for this l-spin (l-spin = 0, 1, 2 --> s-wave, p-wave, d-wave ...)
304   // The probability will be proportional to 2J+1
305   // Returns Jx2
306   G4int AllowedJx2[100];
307   G4int NAllowedJvals = GetAllowedJx2values( theCompoundZ, theCompoundA, lspin, AllowedJx2 );
308   G4double AllowedJx2CumulProb[100], TotalCumul = 0.0;
309   for ( G4int i = 0; i < NAllowedJvals; i++ ) {
310     AllowedJx2CumulProb[i] = AllowedJx2[i] + 1.0;
311     TotalCumul += AllowedJx2CumulProb[i];
312   }
313   for ( G4int i = 0; i < NAllowedJvals; i++ ) {
314     AllowedJx2CumulProb[i] /= TotalCumul;
315     if ( i > 0 ) AllowedJx2CumulProb[i] += AllowedJx2CumulProb[i-1];
316   }
317   G4double rand = G4UniformRand();
318   G4int i_result = -1;
319   for ( G4int i = 0; i < NAllowedJvals; i++ ) {
320     if ( rand < AllowedJx2CumulProb[i] ) {
321       i_result = i;  break;
322     }
323   }
324   if ( i_result < 0 ) {
325     G4cerr << " ############ Error in " << __FILE__ << ", line " << __LINE__ << " ############"<< G4endl;
326     exit(1);
327   }
328   G4int jspinx2 = AllowedJx2[i_result];
329   return jspinx2;
330 }
331 
332 
333 G4int G4NuDEXNeutronCaptureModel::GetAllowedJx2values( G4int theCompoundZ, G4int theCompoundA, G4int lspin, G4int* jx2vals ) {
334   // Provides the allowed jx2 values in neutron capture for a certain l-spin (l-spin = 0, 1, 2 --> s-wave, p-wave, d-wave ...)
335   G4int theZA = 1000*theCompoundZ + theCompoundA;
336   G4int check = Init( theZA );
337   if ( check < 0 ) return -1;
338   G4double Sn, I0;
339   theStatisticalNucleus[theZA]->GetSnAndI0( Sn, I0 ); Sn *= MeV;  // I0 is the spin of the A-1 nucleus in the g.s.
340   G4int Ix2 = (G4int)( ( std::fabs(I0) + 0.1 )*2.0 );
341   G4int Jx2min = std::min( std::abs( Ix2-1-2*lspin ), std::abs( Ix2+1-2*lspin ) );
342   G4int Jx2max = Ix2 + 1 + 2*lspin;
343   G4int NAllowedJvals = 0;
344   for ( G4int Jx2 = Jx2min; Jx2 <= Jx2max; Jx2 += 2 ) {
345     if ( Jx2 >= 0 ) {
346       jx2vals[NAllowedJvals] = Jx2; 
347       NAllowedJvals++;
348     }
349   }
350   return NAllowedJvals;
351 }
352