Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-20 08:29:49

0001 //
0002 // ********************************************************************
0003 // * License and Disclaimer                                           *
0004 // *                                                                  *
0005 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
0006 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
0007 // * conditions of the Geant4 Software License,  included in the file *
0008 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
0009 // * include a list of copyright holders.                             *
0010 // *                                                                  *
0011 // * Neither the authors of this software system, nor their employing *
0012 // * institutes,nor the agencies providing financial support for this *
0013 // * work  make  any representation or  warranty, express or implied, *
0014 // * regarding  this  software system or assume any liability for its *
0015 // * use.  Please see the license in the file  LICENSE  and URL above *
0016 // * for the full disclaimer and the limitation of liability.         *
0017 // *                                                                  *
0018 // * This  code  implementation is the result of  the  scientific and *
0019 // * technical work of the GEANT4 collaboration.                      *
0020 // * By using,  copying,  modifying or  distributing the software (or *
0021 // * any work based  on the software)  you  agree  to acknowledge its *
0022 // * use  in  resulting  scientific  publications,  and indicate your *
0023 // * acceptance of all terms of the Geant4 Software license.          *
0024 // ********************************************************************
0025 //
0026 /// \file G4ScreenedNuclearRecoil.cc
0027 /// \brief Implementation of the G4ScreenedNuclearRecoil class
0028 
0029 // Class Description
0030 // Process for screened electromagnetic nuclear elastic scattering;
0031 // Physics comes from:
0032 // Marcus H. Mendenhall and Robert A. Weller,
0033 // "Algorithms  for  the rapid  computation  of  classical  cross
0034 // sections  for  screened  Coulomb  collisions  "
0035 // Nuclear  Instruments  and  Methods  in  Physics  Research B58 (1991)  11-17
0036 // The only input required is a screening function phi(r/a) which is the ratio
0037 // of the actual interatomic potential for two atoms with atomic
0038 // numbers Z1 and Z2,
0039 // to the unscreened potential Z1*Z2*e^2/r where e^2 is elm_coupling in
0040 // Geant4 units
0041 //
0042 // First version, April 2004, Marcus H. Mendenhall, Vanderbilt University
0043 //
0044 // 5 May, 2004, Marcus Mendenhall
0045 // Added an option for enhancing hard collisions statistically, to allow
0046 // backscattering calculations to be carried out with much improved event rates,
0047 // without distorting the multiple-scattering broadening too much.
0048 // the method SetCrossSectionHardening(G4double fraction, G4double
0049 //                                     HardeningFactor)
0050 // sets what fraction of the events will be randomly hardened,
0051 // and the factor by which the impact area is reduced for such selected events.
0052 //
0053 // 21 November, 2004, Marcus Mendenhall
0054 // added static_nucleus to IsApplicable
0055 //
0056 // 7 December, 2004, Marcus Mendenhall
0057 // changed mean free path of stopping particle from 0.0 to 1.0*nanometer
0058 // to avoid new verbose warning about 0 MFP in 4.6.2p02
0059 //
0060 // 17 December, 2004, Marcus Mendenhall
0061 // added code to permit screening out overly close collisions which are
0062 // expected to be hadronic, not Coulombic
0063 //
0064 // 19 December, 2004, Marcus Mendenhall
0065 // massive rewrite to add modular physics stages and plug-in cross section table
0066 // computation.  This allows one to select (e.g.) between the normal external
0067 // python process and an embedded python interpreter (which is much faster)
0068 // for generating the tables.
0069 // It also allows one to switch between sub-sampled scattering (event biasing)
0070 // and normal scattering, and between non-relativistic kinematics and
0071 // relativistic kinematic approximations, without having a class for every
0072 // combination. Further, one can add extra stages to the scattering, which can
0073 // implement various book-keeping processes.
0074 //
0075 // January 2007, Marcus Mendenhall
0076 // Reorganized heavily for inclusion in Geant4 Core.  All modules merged into
0077 // one source and header, all historic code removed.
0078 //
0079 // Class Description - End
0080 
0081 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
0082 
0083 #include "G4ScreenedNuclearRecoil.hh"
0084 
0085 #include "globals.hh"
0086 
0087 #include <stdio.h>
0088 
0089 const char* G4ScreenedCoulombCrossSectionInfo::CVSFileVers()
0090 {
0091   return "G4ScreenedNuclearRecoil.cc,v 1.57 2008/05/07 11:51:26 marcus Exp GEANT4 tag ";
0092 }
0093 
0094 #include "c2_factory.hh"
0095 
0096 #include "G4DataVector.hh"
0097 #include "G4DynamicParticle.hh"
0098 #include "G4Element.hh"
0099 #include "G4ElementVector.hh"
0100 #include "G4EmProcessSubType.hh"
0101 #include "G4IonTable.hh"
0102 #include "G4Isotope.hh"
0103 #include "G4IsotopeVector.hh"
0104 #include "G4LindhardPartition.hh"
0105 #include "G4Material.hh"
0106 #include "G4MaterialCutsCouple.hh"
0107 #include "G4ParticleChangeForLoss.hh"
0108 #include "G4ParticleDefinition.hh"
0109 #include "G4ParticleTable.hh"
0110 #include "G4ParticleTypes.hh"
0111 #include "G4ProcessManager.hh"
0112 #include "G4StableIsotopes.hh"
0113 #include "G4Step.hh"
0114 #include "G4Track.hh"
0115 #include "G4VParticleChange.hh"
0116 #include "Randomize.hh"
0117 
0118 #include <iomanip>
0119 #include <iostream>
0120 static c2_factory<G4double> c2;  // this makes a lot of notation shorter
0121 typedef c2_ptr<G4double> c2p;
0122 
0123 G4ScreenedCoulombCrossSection::~G4ScreenedCoulombCrossSection()
0124 {
0125   screeningData.clear();
0126   MFPTables.clear();
0127 }
0128 
0129 const G4double G4ScreenedCoulombCrossSection::massmap[nMassMapElements + 1] = {
0130   0,          1.007940,   4.002602,   6.941000,   9.012182,   10.811000,  12.010700,  14.006700,
0131   15.999400,  18.998403,  20.179700,  22.989770,  24.305000,  26.981538,  28.085500,  30.973761,
0132   32.065000,  35.453000,  39.948000,  39.098300,  40.078000,  44.955910,  47.867000,  50.941500,
0133   51.996100,  54.938049,  55.845000,  58.933200,  58.693400,  63.546000,  65.409000,  69.723000,
0134   72.640000,  74.921600,  78.960000,  79.904000,  83.798000,  85.467800,  87.620000,  88.905850,
0135   91.224000,  92.906380,  95.940000,  98.000000,  101.070000, 102.905500, 106.420000, 107.868200,
0136   112.411000, 114.818000, 118.710000, 121.760000, 127.600000, 126.904470, 131.293000, 132.905450,
0137   137.327000, 138.905500, 140.116000, 140.907650, 144.240000, 145.000000, 150.360000, 151.964000,
0138   157.250000, 158.925340, 162.500000, 164.930320, 167.259000, 168.934210, 173.040000, 174.967000,
0139   178.490000, 180.947900, 183.840000, 186.207000, 190.230000, 192.217000, 195.078000, 196.966550,
0140   200.590000, 204.383300, 207.200000, 208.980380, 209.000000, 210.000000, 222.000000, 223.000000,
0141   226.000000, 227.000000, 232.038100, 231.035880, 238.028910, 237.000000, 244.000000, 243.000000,
0142   247.000000, 247.000000, 251.000000, 252.000000, 257.000000, 258.000000, 259.000000, 262.000000,
0143   261.000000, 262.000000, 266.000000, 264.000000, 277.000000, 268.000000, 281.000000, 272.000000,
0144   285.000000, 282.500000, 289.000000, 287.500000, 292.000000};
0145 
0146 G4ParticleDefinition*
0147 G4ScreenedCoulombCrossSection::SelectRandomUnweightedTarget(const G4MaterialCutsCouple* couple)
0148 {
0149   // Select randomly an element within the material, according to number
0150   // density only
0151   const G4Material* material = couple->GetMaterial();
0152   G4int nMatElements = material->GetNumberOfElements();
0153   const G4ElementVector* elementVector = material->GetElementVector();
0154   const G4Element* element = 0;
0155   G4ParticleDefinition* target = 0;
0156 
0157   // Special case: the material consists of one element
0158   if (nMatElements == 1) {
0159     element = (*elementVector)[0];
0160   }
0161   else {
0162     // Composite material
0163     G4double random = G4UniformRand() * material->GetTotNbOfAtomsPerVolume();
0164     G4double nsum = 0.0;
0165     const G4double* atomDensities = material->GetVecNbOfAtomsPerVolume();
0166 
0167     for (G4int k = 0; k < nMatElements; k++) {
0168       nsum += atomDensities[k];
0169       element = (*elementVector)[k];
0170       if (nsum >= random) break;
0171     }
0172   }
0173 
0174   G4int N = 0;
0175   G4int Z = element->GetZasInt();
0176 
0177   G4int nIsotopes = element->GetNumberOfIsotopes();
0178   if (0 < nIsotopes) {
0179     if (Z <= 92) {
0180       // we have no detailed material isotopic info available,
0181       // so use G4StableIsotopes table up to Z=92
0182       static G4StableIsotopes theIso;
0183       // get a stable isotope table for default results
0184       nIsotopes = theIso.GetNumberOfIsotopes(Z);
0185       G4double random = 100.0 * G4UniformRand();
0186       // values are expressed as percent, sum is 100
0187       G4int tablestart = theIso.GetFirstIsotope(Z);
0188       G4double asum = 0.0;
0189       for (G4int i = 0; i < nIsotopes; i++) {
0190         asum += theIso.GetAbundance(i + tablestart);
0191         N = theIso.GetIsotopeNucleonCount(i + tablestart);
0192         if (asum >= random) break;
0193       }
0194     }
0195     else {
0196       // too heavy for stable isotope table, just use mean mass
0197       N = (G4int)std::floor(element->GetN() + 0.5);
0198     }
0199   }
0200   else {
0201     G4int i;
0202     const G4IsotopeVector* isoV = element->GetIsotopeVector();
0203     G4double random = G4UniformRand();
0204     G4double* abundance = element->GetRelativeAbundanceVector();
0205     G4double asum = 0.0;
0206     for (i = 0; i < nIsotopes; i++) {
0207       asum += abundance[i];
0208       N = (*isoV)[i]->GetN();
0209       if (asum >= random) break;
0210     }
0211   }
0212 
0213   // get the official definition of this nucleus, to get the correct
0214   // value of A note that GetIon is very slow, so we will cache ones
0215   // we have already found ourselves.
0216   ParticleCache::iterator p = targetMap.find(Z * 1000 + N);
0217   if (p != targetMap.end()) {
0218     target = (*p).second;
0219   }
0220   else {
0221     target = G4IonTable::GetIonTable()->GetIon(Z, N, 0.0);
0222     targetMap[Z * 1000 + N] = target;
0223   }
0224   return target;
0225 }
0226 
0227 void G4ScreenedCoulombCrossSection::BuildMFPTables()
0228 {
0229   const G4int nmfpvals = 200;
0230 
0231   std::vector<G4double> evals(nmfpvals), mfpvals(nmfpvals);
0232 
0233   // sum up inverse MFPs per element for each material
0234   const G4MaterialTable* materialTable = G4Material::GetMaterialTable();
0235   if (materialTable == 0) {
0236     return;
0237   }
0238   // G4Exception("G4ScreenedCoulombCrossSection::BuildMFPTables
0239   //- no MaterialTable found)");
0240 
0241   G4int nMaterials = G4Material::GetNumberOfMaterials();
0242 
0243   for (G4int matidx = 0; matidx < nMaterials; matidx++) {
0244     const G4Material* material = (*materialTable)[matidx];
0245     const G4ElementVector& elementVector = *(material->GetElementVector());
0246     const G4int nMatElements = material->GetNumberOfElements();
0247 
0248     const G4Element* element = 0;
0249     const G4double* atomDensities = material->GetVecNbOfAtomsPerVolume();
0250 
0251     G4double emin = 0, emax = 0;
0252     // find innermost range of cross section functions
0253     for (G4int kel = 0; kel < nMatElements; kel++) {
0254       element = elementVector[kel];
0255       G4int Z = (G4int)std::floor(element->GetZ() + 0.5);
0256       const G4_c2_function& ifunc = sigmaMap[Z];
0257       if (!kel || ifunc.xmin() > emin) emin = ifunc.xmin();
0258       if (!kel || ifunc.xmax() < emax) emax = ifunc.xmax();
0259     }
0260 
0261     G4double logint = std::log(emax / emin) / (nmfpvals - 1);
0262     // logarithmic increment for tables
0263 
0264     // compute energy scale for interpolator.  Force exact values at
0265     // both ends to avoid range errors
0266     for (G4int i = 1; i < nmfpvals - 1; i++)
0267       evals[i] = emin * std::exp(logint * i);
0268     evals.front() = emin;
0269     evals.back() = emax;
0270 
0271     // zero out the inverse mfp sums to start
0272     for (G4int eidx = 0; eidx < nmfpvals; eidx++)
0273       mfpvals[eidx] = 0.0;
0274 
0275     // sum inverse mfp for each element in this material and for each
0276     // energy
0277     for (G4int kel = 0; kel < nMatElements; kel++) {
0278       element = elementVector[kel];
0279       G4int Z = (G4int)std::floor(element->GetZ() + 0.5);
0280       const G4_c2_function& sigma = sigmaMap[Z];
0281       G4double ndens = atomDensities[kel];
0282       // compute atom fraction for this element in this material
0283 
0284       for (G4int eidx = 0; eidx < nmfpvals; eidx++) {
0285         mfpvals[eidx] += ndens * sigma(evals[eidx]);
0286       }
0287     }
0288 
0289     // convert inverse mfp to regular mfp
0290     for (G4int eidx = 0; eidx < nmfpvals; eidx++) {
0291       mfpvals[eidx] = 1.0 / mfpvals[eidx];
0292     }
0293     // and make a new interpolating function out of the sum
0294     MFPTables[matidx] = c2.log_log_interpolating_function().load(evals, mfpvals, true, 0, true, 0);
0295   }
0296 }
0297 
0298 G4ScreenedNuclearRecoil::G4ScreenedNuclearRecoil(const G4String& processName,
0299                                                  const G4String& ScreeningKey,
0300                                                  G4bool GenerateRecoils, G4double RecoilCutoff,
0301                                                  G4double PhysicsCutoff)
0302   : G4VDiscreteProcess(processName, fElectromagnetic),
0303     screeningKey(ScreeningKey),
0304     generateRecoils(GenerateRecoils),
0305     avoidReactions(1),
0306     recoilCutoff(RecoilCutoff),
0307     physicsCutoff(PhysicsCutoff),
0308     hardeningFraction(0.0),
0309     hardeningFactor(1.0),
0310     externalCrossSectionConstructor(0),
0311     NIELPartitionFunction(new G4LindhardRobinsonPartition)
0312 {
0313   // for now, point to class instance of this. Doing it by creating a new
0314   // one fails
0315   // to correctly update NIEL
0316   // not even this is needed... done in G4VProcess().
0317   // pParticleChange=&aParticleChange;
0318   processMaxEnergy = 50000.0 * MeV;
0319   highEnergyLimit = 100.0 * MeV;
0320   lowEnergyLimit = physicsCutoff;
0321   registerDepositedEnergy = 1;  // by default, don't hide NIEL
0322   MFPScale = 1.0;
0323   // SetVerboseLevel(2);
0324   AddStage(new G4ScreenedCoulombClassicalKinematics);
0325   AddStage(new G4SingleScatter);
0326   SetProcessSubType(fCoulombScattering);
0327 }
0328 
0329 void G4ScreenedNuclearRecoil::ResetTables()
0330 {
0331   std::map<G4int, G4ScreenedCoulombCrossSection*>::iterator xt = crossSectionHandlers.begin();
0332   for (; xt != crossSectionHandlers.end(); xt++) {
0333     delete (*xt).second;
0334   }
0335   crossSectionHandlers.clear();
0336 }
0337 
0338 void G4ScreenedNuclearRecoil::ClearStages()
0339 {
0340   // I don't think I like deleting the processes here... they are better
0341   // abandoned
0342   // if the creator doesn't get rid of them
0343   // std::vector<G4ScreenedCollisionStage *>::iterator stage=
0344   // collisionStages.begin();
0345   // for(; stage != collisionStages.end(); stage++) delete (*stage);
0346 
0347   collisionStages.clear();
0348 }
0349 
0350 void G4ScreenedNuclearRecoil::SetNIELPartitionFunction(const G4VNIELPartition* part)
0351 {
0352   if (NIELPartitionFunction) delete NIELPartitionFunction;
0353   NIELPartitionFunction = part;
0354 }
0355 
0356 void G4ScreenedNuclearRecoil::DepositEnergy(G4int z1, G4double a1, const G4Material* material,
0357                                             G4double energy)
0358 {
0359   if (!NIELPartitionFunction) {
0360     IonizingLoss += energy;
0361   }
0362   else {
0363     G4double part = NIELPartitionFunction->PartitionNIEL(z1, a1, material, energy);
0364     IonizingLoss += energy * (1 - part);
0365     NIEL += energy * part;
0366   }
0367 }
0368 
0369 G4ScreenedNuclearRecoil::~G4ScreenedNuclearRecoil()
0370 {
0371   ResetTables();
0372 }
0373 
0374 // returns true if it appears the nuclei collided, and we are interested
0375 // in checking
0376 G4bool G4ScreenedNuclearRecoil::CheckNuclearCollision(G4double A, G4double a1, G4double apsis)
0377 {
0378   return avoidReactions
0379          && (apsis < (1.1 * (std::pow(A, 1.0 / 3.0) + std::pow(a1, 1.0 / 3.0)) + 1.4) * fermi);
0380   // nuclei are within 1.4 fm (reduced pion Compton wavelength) of each
0381   // other at apsis,
0382   // this is hadronic, skip it
0383 }
0384 
0385 G4ScreenedCoulombCrossSection* G4ScreenedNuclearRecoil::GetNewCrossSectionHandler(void)
0386 {
0387   G4ScreenedCoulombCrossSection* xc;
0388   if (!externalCrossSectionConstructor)
0389     xc = new G4NativeScreenedCoulombCrossSection;
0390   else
0391     xc = externalCrossSectionConstructor->create();
0392   xc->SetVerbosity(verboseLevel);
0393   return xc;
0394 }
0395 
0396 G4double G4ScreenedNuclearRecoil::GetMeanFreePath(const G4Track& track, G4double,
0397                                                   G4ForceCondition* cond)
0398 {
0399   const G4DynamicParticle* incoming = track.GetDynamicParticle();
0400   G4double energy = incoming->GetKineticEnergy();
0401   G4double a1 = incoming->GetDefinition()->GetPDGMass() / amu_c2;
0402 
0403   G4double meanFreePath;
0404   *cond = NotForced;
0405 
0406   if (energy < lowEnergyLimit || energy < recoilCutoff * a1) {
0407     *cond = Forced;
0408     return 1.0 * nm;
0409     /* catch and stop slow particles to collect their NIEL! */
0410   }
0411   else if (energy > processMaxEnergy * a1) {
0412     return DBL_MAX;  // infinite mean free path
0413   }
0414   else if (energy > highEnergyLimit * a1)
0415     energy = highEnergyLimit * a1;
0416   /* constant MFP at high energy */
0417 
0418   G4double fz1 = incoming->GetDefinition()->GetPDGCharge();
0419   G4int z1 = (G4int)(fz1 / eplus + 0.5);
0420 
0421   std::map<G4int, G4ScreenedCoulombCrossSection*>::iterator xh = crossSectionHandlers.find(z1);
0422   G4ScreenedCoulombCrossSection* xs;
0423 
0424   if (xh == crossSectionHandlers.end()) {
0425     xs = crossSectionHandlers[z1] = GetNewCrossSectionHandler();
0426     xs->LoadData(screeningKey, z1, a1, physicsCutoff);
0427     xs->BuildMFPTables();
0428   }
0429   else
0430     xs = (*xh).second;
0431 
0432   const G4MaterialCutsCouple* materialCouple = track.GetMaterialCutsCouple();
0433   size_t materialIndex = materialCouple->GetMaterial()->GetIndex();
0434 
0435   const G4_c2_function& mfp = *(*xs)[materialIndex];
0436 
0437   // make absolutely certain we don't get an out-of-range energy
0438   meanFreePath = mfp(std::min(std::max(energy, mfp.xmin()), mfp.xmax()));
0439 
0440   // G4cout << "MFP: " << meanFreePath << " index " << materialIndex
0441   //<< " energy " << energy << " MFPScale " << MFPScale << G4endl;
0442 
0443   return meanFreePath * MFPScale;
0444 }
0445 
0446 G4VParticleChange* G4ScreenedNuclearRecoil::PostStepDoIt(const G4Track& aTrack, const G4Step& aStep)
0447 {
0448   validCollision = 1;
0449   pParticleChange->Initialize(aTrack);
0450   NIEL = 0.0;  // default is no NIEL deposited
0451   IonizingLoss = 0.0;
0452 
0453   // do universal setup
0454 
0455   const G4DynamicParticle* incidentParticle = aTrack.GetDynamicParticle();
0456   G4ParticleDefinition* baseParticle = aTrack.GetDefinition();
0457 
0458   G4double fz1 = baseParticle->GetPDGCharge() / eplus;
0459   G4int z1 = (G4int)(fz1 + 0.5);
0460   G4double a1 = baseParticle->GetPDGMass() / amu_c2;
0461   G4double incidentEnergy = incidentParticle->GetKineticEnergy();
0462 
0463   // Select randomly one element and (possibly) isotope in the
0464   // current material.
0465   const G4MaterialCutsCouple* couple = aTrack.GetMaterialCutsCouple();
0466 
0467   const G4Material* mat = couple->GetMaterial();
0468 
0469   G4double P = 0.0;  // the impact parameter of this collision
0470 
0471   if (incidentEnergy < GetRecoilCutoff() * a1) {
0472     // check energy sanity on entry
0473     DepositEnergy(z1, baseParticle->GetPDGMass() / amu_c2, mat, incidentEnergy);
0474     GetParticleChange().ProposeEnergy(0.0);
0475     // stop the particle and bail out
0476     validCollision = 0;
0477   }
0478   else {
0479     G4double numberDensity = mat->GetTotNbOfAtomsPerVolume();
0480     G4double lattice = 0.5 / std::pow(numberDensity, 1.0 / 3.0);
0481     // typical lattice half-spacing
0482     G4double length = GetCurrentInteractionLength();
0483     G4double sigopi = 1.0 / (pi * numberDensity * length);
0484     // this is sigma0/pi
0485 
0486     // compute the impact parameter very early, so if is rejected
0487     // as too far away, little effort is wasted
0488     // this is the TRIM method for determining an impact parameter
0489     // based on the flight path
0490     // this gives a cumulative distribution of
0491     // N(P)= 1-exp(-pi P^2 n l)
0492     // which says the probability of NOT hitting a disk of area
0493     // sigma= pi P^2 =exp(-sigma N l)
0494     // which may be reasonable
0495     if (sigopi < lattice * lattice) {
0496       // normal long-flight approximation
0497       P = std::sqrt(-std::log(G4UniformRand()) * sigopi);
0498     }
0499     else {
0500       // short-flight limit
0501       P = std::sqrt(G4UniformRand()) * lattice;
0502     }
0503 
0504     G4double fraction = GetHardeningFraction();
0505     if (fraction && G4UniformRand() < fraction) {
0506       // pick out some events, and increase the central cross
0507       // section by reducing the impact parameter
0508       P /= std::sqrt(GetHardeningFactor());
0509     }
0510 
0511     // check if we are far enough away that the energy transfer
0512     // must be below cutoff,
0513     // and leave everything alone if so, saving a lot of time.
0514     if (P * P > sigopi) {
0515       if (GetVerboseLevel() > 1)
0516         printf("ScreenedNuclear impact reject: length=%.3f P=%.4f limit=%.4f\n", length / angstrom,
0517                P / angstrom, std::sqrt(sigopi) / angstrom);
0518       // no collision, don't follow up with anything
0519       validCollision = 0;
0520     }
0521   }
0522 
0523   // find out what we hit, and record it in our kinematics block.
0524   kinematics.targetMaterial = mat;
0525   kinematics.a1 = a1;
0526 
0527   if (validCollision) {
0528     G4ScreenedCoulombCrossSection* xsect = GetCrossSectionHandlers()[z1];
0529     G4ParticleDefinition* recoilIon = xsect->SelectRandomUnweightedTarget(couple);
0530     kinematics.crossSection = xsect;
0531     kinematics.recoilIon = recoilIon;
0532     kinematics.impactParameter = P;
0533     kinematics.a2 = recoilIon->GetPDGMass() / amu_c2;
0534   }
0535   else {
0536     kinematics.recoilIon = 0;
0537     kinematics.impactParameter = 0;
0538     kinematics.a2 = 0;
0539   }
0540 
0541   std::vector<G4ScreenedCollisionStage*>::iterator stage = collisionStages.begin();
0542 
0543   for (; stage != collisionStages.end(); stage++)
0544     (*stage)->DoCollisionStep(this, aTrack, aStep);
0545 
0546   if (registerDepositedEnergy) {
0547     pParticleChange->ProposeLocalEnergyDeposit(IonizingLoss + NIEL);
0548     pParticleChange->ProposeNonIonizingEnergyDeposit(NIEL);
0549     // MHM G4cout << "depositing energy, total = "
0550     //<< IonizingLoss+NIEL << " NIEL = " << NIEL << G4endl;
0551   }
0552 
0553   return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
0554 }
0555 
0556 G4ScreenedCoulombClassicalKinematics::G4ScreenedCoulombClassicalKinematics()
0557   :  // instantiate all the needed functions statically, so no allocation is
0558      // done at run time
0559      // we will be solving x^2 - x phi(x*au)/eps - beta^2 == 0.0
0560      // or, for easier scaling, x'^2 - x' au phi(x')/eps - beta^2 au^2
0561      // note that only the last of these gets deleted, since it owns the rest
0562     phifunc(c2.const_plugin_function()),
0563     xovereps(c2.linear(0., 0., 0.)),
0564     // will fill this in with the right slope at run time
0565     diff(c2.quadratic(0., 0., 0., 1.) - xovereps * phifunc)
0566 {}
0567 
0568 G4bool G4ScreenedCoulombClassicalKinematics::DoScreeningComputation(G4ScreenedNuclearRecoil* master,
0569                                                                     const G4ScreeningTables* screen,
0570                                                                     G4double eps, G4double beta)
0571 {
0572   G4double au = screen->au;
0573   G4CoulombKinematicsInfo& kin = master->GetKinematics();
0574   G4double A = kin.a2;
0575   G4double a1 = kin.a1;
0576 
0577   G4double xx0;  // first estimate of closest approach
0578   if (eps < 5.0) {
0579     G4double y = std::log(eps);
0580     G4double mlrho4 = ((((3.517e-4 * y + 1.401e-2) * y + 2.393e-1) * y + 2.734) * y + 2.220);
0581     G4double rho4 = std::exp(-mlrho4);  // W&M eq. 18
0582     G4double bb2 = 0.5 * beta * beta;
0583     xx0 = std::sqrt(bb2 + std::sqrt(bb2 * bb2 + rho4));  // W&M eq. 17
0584   }
0585   else {
0586     G4double ee = 1.0 / (2.0 * eps);
0587     xx0 = ee + std::sqrt(ee * ee + beta * beta);  // W&M eq. 15 (Rutherford value)
0588     if (master->CheckNuclearCollision(A, a1, xx0 * au)) return 0;
0589     // nuclei too close
0590   }
0591 
0592   // we will be solving x^2 - x phi(x*au)/eps - beta^2 == 0.0
0593   // or, for easier scaling, x'^2 - x' au phi(x')/eps - beta^2 au^2
0594   xovereps.reset(0., 0.0, au / eps);  // slope of x*au/eps term
0595   phifunc.set_function(&(screen->EMphiData.get()));
0596   // install interpolating table
0597   G4double xx1, phip, phip2;
0598   G4int root_error;
0599   xx1 = diff->find_root(phifunc.xmin(), std::min(10 * xx0 * au, phifunc.xmax()),
0600                         std::min(xx0 * au, phifunc.xmax()), beta * beta * au * au, &root_error,
0601                         &phip, &phip2)
0602         / au;
0603 
0604   if (root_error) {
0605     G4cout << "Screened Coulomb Root Finder Error" << G4endl;
0606     G4cout << "au " << au << " A " << A << " a1 " << a1 << " xx1 " << xx1 << " eps " << eps
0607            << " beta " << beta << G4endl;
0608     G4cout << " xmin " << phifunc.xmin() << " xmax " << std::min(10 * xx0 * au, phifunc.xmax());
0609     G4cout << " f(xmin) " << phifunc(phifunc.xmin()) << " f(xmax) "
0610            << phifunc(std::min(10 * xx0 * au, phifunc.xmax()));
0611     G4cout << " xstart " << std::min(xx0 * au, phifunc.xmax()) << " target "
0612            << beta * beta * au * au;
0613     G4cout << G4endl;
0614     throw c2_exception("Failed root find");
0615   }
0616 
0617   // phiprime is scaled by one factor of au because phi is evaluated
0618   // at (xx0*au),
0619   G4double phiprime = phip * au;
0620 
0621   // lambda0 is from W&M 19
0622   G4double lambda0 =
0623     1.0 / std::sqrt(0.5 + beta * beta / (2.0 * xx1 * xx1) - phiprime / (2.0 * eps));
0624 
0625   // compute the 6-term Lobatto integral alpha (per W&M 21, with
0626   // different coefficients)
0627   // this is probably completely un-needed but gives the highest
0628   // quality results,
0629   G4double alpha = (1.0 + lambda0) / 30.0;
0630   G4double xvals[] = {0.98302349, 0.84652241, 0.53235309, 0.18347974};
0631   G4double weights[] = {0.03472124, 0.14769029, 0.23485003, 0.18602489};
0632   for (G4int k = 0; k < 4; k++) {
0633     G4double x, ff;
0634     x = xx1 / xvals[k];
0635     ff = 1.0 / std::sqrt(1.0 - phifunc(x * au) / (x * eps) - beta * beta / (x * x));
0636     alpha += weights[k] * ff;
0637   }
0638 
0639   phifunc.unset_function();
0640   // throws an exception if used without setting again
0641 
0642   G4double thetac1 = pi * beta * alpha / xx1;
0643   // complement of CM scattering angle
0644   G4double sintheta = std::sin(thetac1);  // note sin(pi-theta)=sin(theta)
0645   G4double costheta = -std::cos(thetac1);  // note cos(pi-theta)=-cos(theta)
0646   // G4double psi=std::atan2(sintheta, costheta+a1/A);
0647   // lab scattering angle (M&T 3rd eq. 8.69)
0648 
0649   // numerics note:  because we checked above for reasonable values
0650   // of beta which give real recoils,
0651   // we don't have to look too closely for theta -> 0 here
0652   // (which would cause sin(theta)
0653   // and 1-cos(theta) to both vanish and make the atan2 ill behaved).
0654   G4double zeta = std::atan2(sintheta, 1 - costheta);
0655   // lab recoil angle (M&T 3rd eq. 8.73)
0656   G4double coszeta = std::cos(zeta);
0657   G4double sinzeta = std::sin(zeta);
0658 
0659   kin.sinTheta = sintheta;
0660   kin.cosTheta = costheta;
0661   kin.sinZeta = sinzeta;
0662   kin.cosZeta = coszeta;
0663   return 1;  // all OK, collision is valid
0664 }
0665 
0666 void G4ScreenedCoulombClassicalKinematics::DoCollisionStep(G4ScreenedNuclearRecoil* master,
0667                                                            const G4Track& aTrack, const G4Step&)
0668 {
0669   if (!master->GetValidCollision()) return;
0670 
0671   G4ParticleChange& aParticleChange = master->GetParticleChange();
0672   G4CoulombKinematicsInfo& kin = master->GetKinematics();
0673 
0674   const G4DynamicParticle* incidentParticle = aTrack.GetDynamicParticle();
0675   G4ParticleDefinition* baseParticle = aTrack.GetDefinition();
0676 
0677   G4double incidentEnergy = incidentParticle->GetKineticEnergy();
0678 
0679   // this adjustment to a1 gives the right results for soft
0680   // (constant gamma)
0681   // relativistic collisions.  Hard collisions are wrong anyway, since the
0682   // Coulombic and hadronic terms interfere and cannot be added.
0683   G4double gamma = (1.0 + incidentEnergy / baseParticle->GetPDGMass());
0684   G4double a1 = kin.a1 * gamma;  // relativistic gamma correction
0685 
0686   G4ParticleDefinition* recoilIon = kin.recoilIon;
0687   G4double A = recoilIon->GetPDGMass() / amu_c2;
0688   G4int Z = (G4int)((recoilIon->GetPDGCharge() / eplus) + 0.5);
0689 
0690   G4double Ec = incidentEnergy * (A / (A + a1));
0691   // energy in CM frame (non-relativistic!)
0692   const G4ScreeningTables* screen = kin.crossSection->GetScreening(Z);
0693   G4double au = screen->au;  // screening length
0694 
0695   G4double beta = kin.impactParameter / au;
0696   // dimensionless impact parameter
0697   G4double eps = Ec / (screen->z1 * Z * elm_coupling / au);
0698   // dimensionless energy
0699 
0700   G4bool ok = DoScreeningComputation(master, screen, eps, beta);
0701   if (!ok) {
0702     master->SetValidCollision(0);  // flag bad collision
0703     return;  // just bail out without setting valid flag
0704   }
0705 
0706   G4double eRecoil =
0707     4 * incidentEnergy * a1 * A * kin.cosZeta * kin.cosZeta / ((a1 + A) * (a1 + A));
0708   kin.eRecoil = eRecoil;
0709 
0710   if (incidentEnergy - eRecoil < master->GetRecoilCutoff() * a1) {
0711     aParticleChange.ProposeEnergy(0.0);
0712     master->DepositEnergy(int(screen->z1), a1, kin.targetMaterial, incidentEnergy - eRecoil);
0713   }
0714 
0715   if (master->GetEnableRecoils() && eRecoil > master->GetRecoilCutoff() * kin.a2) {
0716     kin.recoilIon = recoilIon;
0717   }
0718   else {
0719     kin.recoilIon = 0;  // this flags no recoil to be generated
0720     master->DepositEnergy(Z, A, kin.targetMaterial, eRecoil);
0721   }
0722 }
0723 
0724 void G4SingleScatter::DoCollisionStep(G4ScreenedNuclearRecoil* master, const G4Track& aTrack,
0725                                       const G4Step&)
0726 {
0727   if (!master->GetValidCollision()) return;
0728 
0729   G4CoulombKinematicsInfo& kin = master->GetKinematics();
0730   G4ParticleChange& aParticleChange = master->GetParticleChange();
0731 
0732   const G4DynamicParticle* incidentParticle = aTrack.GetDynamicParticle();
0733   G4double incidentEnergy = incidentParticle->GetKineticEnergy();
0734   G4double eRecoil = kin.eRecoil;
0735 
0736   G4double azimuth = G4UniformRand() * (2.0 * pi);
0737   G4double sa = std::sin(azimuth);
0738   G4double ca = std::cos(azimuth);
0739 
0740   G4ThreeVector recoilMomentumDirection(kin.sinZeta * ca, kin.sinZeta * sa, kin.cosZeta);
0741   G4ParticleMomentum incidentDirection = incidentParticle->GetMomentumDirection();
0742   recoilMomentumDirection = recoilMomentumDirection.rotateUz(incidentDirection);
0743   G4ThreeVector recoilMomentum =
0744     recoilMomentumDirection * std::sqrt(2.0 * eRecoil * kin.a2 * amu_c2);
0745 
0746   if (aParticleChange.GetEnergy() != 0.0) {
0747     // DoKinematics hasn't stopped it!
0748     G4ThreeVector beamMomentum = incidentParticle->GetMomentum() - recoilMomentum;
0749     aParticleChange.ProposeMomentumDirection(beamMomentum.unit());
0750     aParticleChange.ProposeEnergy(incidentEnergy - eRecoil);
0751   }
0752 
0753   if (kin.recoilIon) {
0754     G4DynamicParticle* recoil =
0755       new G4DynamicParticle(kin.recoilIon, recoilMomentumDirection, eRecoil);
0756 
0757     aParticleChange.SetNumberOfSecondaries(1);
0758     aParticleChange.AddSecondary(recoil);
0759   }
0760 }
0761 
0762 G4bool G4ScreenedNuclearRecoil::IsApplicable(const G4ParticleDefinition& aParticleType)
0763 {
0764   return aParticleType == *(G4Proton::Proton()) || aParticleType.GetParticleType() == "nucleus"
0765          || aParticleType.GetParticleType() == "static_nucleus";
0766 }
0767 
0768 void G4ScreenedNuclearRecoil::BuildPhysicsTable(const G4ParticleDefinition& aParticleType)
0769 {
0770   G4String nam = aParticleType.GetParticleName();
0771   if (nam == "GenericIon" || nam == "proton" || nam == "deuteron" || nam == "triton"
0772       || nam == "alpha" || nam == "He3")
0773   {
0774     G4cout << G4endl << GetProcessName() << ":   for  " << nam
0775            << "    SubType= " << GetProcessSubType()
0776            << "    maxEnergy(MeV)= " << processMaxEnergy / MeV << G4endl;
0777   }
0778 }
0779 
0780 void G4ScreenedNuclearRecoil::DumpPhysicsTable(const G4ParticleDefinition&) {}
0781 
0782 // This used to be the file mhmScreenedNuclearRecoil_native.cc
0783 // it has been included here to collect this file into a smaller
0784 // number of packages
0785 
0786 #include "G4DataVector.hh"
0787 #include "G4Element.hh"
0788 #include "G4ElementVector.hh"
0789 #include "G4Isotope.hh"
0790 #include "G4Material.hh"
0791 #include "G4MaterialCutsCouple.hh"
0792 
0793 #include <vector>
0794 
0795 G4_c2_function& ZBLScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double* auval)
0796 {
0797   static const size_t ncoef = 4;
0798   static G4double scales[ncoef] = {-3.2, -0.9432, -0.4028, -0.2016};
0799   static G4double coefs[ncoef] = {0.1818, 0.5099, 0.2802, 0.0281};
0800 
0801   G4double au = 0.8854 * angstrom * 0.529 / (std::pow(z1, 0.23) + std::pow(z2, 0.23));
0802   std::vector<G4double> r(npoints), phi(npoints);
0803 
0804   for (size_t i = 0; i < npoints; i++) {
0805     G4double rr = (float)i / (float)(npoints - 1);
0806     r[i] = rr * rr * rMax;
0807     // use quadratic r scale to make sampling fine near the center
0808     G4double sum = 0.0;
0809     for (size_t j = 0; j < ncoef; j++)
0810       sum += coefs[j] * std::exp(scales[j] * r[i] / au);
0811     phi[i] = sum;
0812   }
0813 
0814   // compute the derivative at the origin for the spline
0815   G4double phiprime0 = 0.0;
0816   for (size_t j = 0; j < ncoef; j++)
0817     phiprime0 += scales[j] * coefs[j] * std::exp(scales[j] * r[0] / au);
0818   phiprime0 *= (1.0 / au);  // put back in natural units;
0819 
0820   *auval = au;
0821   return c2.lin_log_interpolating_function().load(r, phi, false, phiprime0, true, 0);
0822 }
0823 
0824 G4_c2_function& MoliereScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double* auval)
0825 {
0826   static const size_t ncoef = 3;
0827   static G4double scales[ncoef] = {-6.0, -1.2, -0.3};
0828   static G4double coefs[ncoef] = {0.10, 0.55, 0.35};
0829 
0830   G4double au = 0.8853 * 0.529 * angstrom / std::sqrt(std::pow(z1, 0.6667) + std::pow(z2, 0.6667));
0831   std::vector<G4double> r(npoints), phi(npoints);
0832 
0833   for (size_t i = 0; i < npoints; i++) {
0834     G4double rr = (float)i / (float)(npoints - 1);
0835     r[i] = rr * rr * rMax;
0836     // use quadratic r scale to make sampling fine near the center
0837     G4double sum = 0.0;
0838     for (size_t j = 0; j < ncoef; j++)
0839       sum += coefs[j] * std::exp(scales[j] * r[i] / au);
0840     phi[i] = sum;
0841   }
0842 
0843   // compute the derivative at the origin for the spline
0844   G4double phiprime0 = 0.0;
0845   for (size_t j = 0; j < ncoef; j++)
0846     phiprime0 += scales[j] * coefs[j] * std::exp(scales[j] * r[0] / au);
0847   phiprime0 *= (1.0 / au);  // put back in natural units;
0848 
0849   *auval = au;
0850   return c2.lin_log_interpolating_function().load(r, phi, false, phiprime0, true, 0);
0851 }
0852 
0853 G4_c2_function& LJScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double* auval)
0854 {
0855   // from Loftager, Besenbacher, Jensen & Sorensen
0856   // PhysRev A20, 1443++, 1979
0857   G4double au = 0.8853 * 0.529 * angstrom / std::sqrt(std::pow(z1, 0.6667) + std::pow(z2, 0.6667));
0858   std::vector<G4double> r(npoints), phi(npoints);
0859 
0860   for (size_t i = 0; i < npoints; i++) {
0861     G4double rr = (float)i / (float)(npoints - 1);
0862     r[i] = rr * rr * rMax;
0863     // use quadratic r scale to make sampling fine near the center
0864 
0865     G4double y = std::sqrt(9.67 * r[i] / au);
0866     G4double ysq = y * y;
0867     G4double phipoly = 1 + y + 0.3344 * ysq + 0.0485 * y * ysq + 0.002647 * ysq * ysq;
0868     phi[i] = phipoly * std::exp(-y);
0869     // G4cout << r[i] << " " << phi[i] << G4endl;
0870   }
0871 
0872   // compute the derivative at the origin for the spline
0873   G4double logphiprime0 = (9.67 / 2.0) * (2 * 0.3344 - 1.0);
0874   // #avoid 0/0 on first element
0875   logphiprime0 *= (1.0 / au);  // #put back in natural units
0876 
0877   *auval = au;
0878   return c2.lin_log_interpolating_function().load(r, phi, false, logphiprime0 * phi[0], true, 0);
0879 }
0880 
0881 G4_c2_function& LJZBLScreening(G4int z1, G4int z2, size_t npoints, G4double rMax, G4double* auval)
0882 {
0883   // hybrid of LJ and ZBL, uses LJ if x < 0.25*auniv, ZBL if x > 1.5*auniv, and
0884   /// connector in between.  These numbers are selected so the switchover
0885   // is very near the point where the functions naturally cross.
0886   G4double auzbl, aulj;
0887 
0888   c2p zbl = ZBLScreening(z1, z2, npoints, rMax, &auzbl);
0889   c2p lj = LJScreening(z1, z2, npoints, rMax, &aulj);
0890 
0891   G4double au = (auzbl + aulj) * 0.5;
0892   lj->set_domain(lj->xmin(), 0.25 * au);
0893   zbl->set_domain(1.5 * au, zbl->xmax());
0894 
0895   c2p conn = c2.connector_function(lj->xmax(), lj, zbl->xmin(), zbl, true, 0);
0896   c2_piecewise_function_p<G4double>& pw = c2.piecewise_function();
0897   c2p keepit(pw);
0898   pw.append_function(lj);
0899   pw.append_function(conn);
0900   pw.append_function(zbl);
0901 
0902   *auval = au;
0903   keepit.release_for_return();
0904   return pw;
0905 }
0906 
0907 G4NativeScreenedCoulombCrossSection::~G4NativeScreenedCoulombCrossSection() {}
0908 
0909 G4NativeScreenedCoulombCrossSection::G4NativeScreenedCoulombCrossSection()
0910 {
0911   AddScreeningFunction("zbl", ZBLScreening);
0912   AddScreeningFunction("lj", LJScreening);
0913   AddScreeningFunction("mol", MoliereScreening);
0914   AddScreeningFunction("ljzbl", LJZBLScreening);
0915 }
0916 
0917 std::vector<G4String> G4NativeScreenedCoulombCrossSection::GetScreeningKeys() const
0918 {
0919   std::vector<G4String> keys;
0920   // find the available screening keys
0921   std::map<std::string, ScreeningFunc>::const_iterator sfunciter = phiMap.begin();
0922   for (; sfunciter != phiMap.end(); sfunciter++)
0923     keys.push_back((*sfunciter).first);
0924   return keys;
0925 }
0926 
0927 static inline G4double cm_energy(G4double a1, G4double a2, G4double t0)
0928 {
0929   // "relativistically correct energy in CM frame"
0930   G4double m1 = a1 * amu_c2, mass2 = a2 * amu_c2;
0931   G4double mc2 = (m1 + mass2);
0932   G4double f = 2.0 * mass2 * t0 / (mc2 * mc2);
0933   // old way: return (f < 1e-6) ?  0.5*mc2*f : mc2*(std::sqrt(1.0+f)-1.0);
0934   // formally equivalent to previous, but numerically stable for all
0935   // f without conditional
0936   // uses identity (sqrt(1+x) - 1)(sqrt(1+x) + 1) = x
0937   return mc2 * f / (std::sqrt(1.0 + f) + 1.0);
0938 }
0939 
0940 static inline G4double thetac(G4double m1, G4double mass2, G4double eratio)
0941 {
0942   G4double s2th2 = eratio * ((m1 + mass2) * (m1 + mass2) / (4.0 * m1 * mass2));
0943   G4double sth2 = std::sqrt(s2th2);
0944   return 2.0 * std::asin(sth2);
0945 }
0946 
0947 void G4NativeScreenedCoulombCrossSection::LoadData(G4String screeningKey, G4int z1, G4double a1,
0948                                                    G4double recoilCutoff)
0949 {
0950   static const size_t sigLen = 200;
0951   // since sigma doesn't matter much, a very coarse table will do
0952   G4DataVector energies(sigLen);
0953   G4DataVector data(sigLen);
0954 
0955   a1 = standardmass(z1);
0956   // use standardized values for mass for building tables
0957 
0958   const G4MaterialTable* materialTable = G4Material::GetMaterialTable();
0959   G4int nMaterials = G4Material::GetNumberOfMaterials();
0960 
0961   for (G4int im = 0; im < nMaterials; im++) {
0962     const G4Material* material = (*materialTable)[im];
0963     const G4ElementVector* elementVector = material->GetElementVector();
0964     const G4int nMatElements = material->GetNumberOfElements();
0965 
0966     for (G4int iEl = 0; iEl < nMatElements; iEl++) {
0967       const G4Element* element = (*elementVector)[iEl];
0968       G4int Z = element->GetZasInt();
0969       G4double a2 = element->GetA() * (mole / gram);
0970 
0971       if (sigmaMap.find(Z) != sigmaMap.end()) continue;
0972       // we've already got this element
0973 
0974       // find the screening function generator we need
0975       std::map<std::string, ScreeningFunc>::iterator sfunciter = phiMap.find(screeningKey);
0976       if (sfunciter == phiMap.end()) {
0977         G4ExceptionDescription ed;
0978         ed << "No such screening key <" << screeningKey << ">";
0979         G4Exception("G4NativeScreenedCoulombCrossSection::LoadData", "em0003", FatalException, ed);
0980       }
0981       ScreeningFunc sfunc = (*sfunciter).second;
0982 
0983       G4double au;
0984       G4_c2_ptr screen = sfunc(z1, Z, 200, 50.0 * angstrom, &au);
0985       // generate the screening data
0986       G4ScreeningTables st;
0987 
0988       st.EMphiData = screen;  // save our phi table
0989       st.z1 = z1;
0990       st.m1 = a1;
0991       st.z2 = Z;
0992       st.m2 = a2;
0993       st.emin = recoilCutoff;
0994       st.au = au;
0995 
0996       // now comes the hard part... build the total cross section
0997       // tables from the phi table
0998       // based on (pi-thetac) = pi*beta*alpha/x0, but noting that
0999       // alpha is very nearly unity, always
1000       // so just solve it wth alpha=1, which makes the solution
1001       // much easier
1002       // this function returns an approximation to
1003       // (beta/x0)^2=phi(x0)/(eps*x0)-1 ~ ((pi-thetac)/pi)^2
1004       // Since we don't need exact sigma values, this is good enough
1005       // (within a factor of 2 almost always)
1006       // this rearranges to phi(x0)/(x0*eps) =
1007       // 2*theta/pi - theta^2/pi^2
1008 
1009       c2_linear_p<G4double>& c2eps = c2.linear(0.0, 0.0, 1.0);
1010       // will store an appropriate eps inside this in loop
1011       G4_c2_ptr phiau = screen(c2.linear(0.0, 0.0, au));
1012       G4_c2_ptr x0func(phiau / c2eps);
1013       // this will be phi(x)/(x*eps) when c2eps is correctly set
1014       x0func->set_domain(1e-6 * angstrom / au, 0.9999 * screen->xmax() / au);
1015       // needed for inverse function
1016       // use the c2_inverse_function interface for the root finder
1017       // it is more efficient for an ordered
1018       // computation of values.
1019       G4_c2_ptr x0_solution(c2.inverse_function(x0func));
1020 
1021       G4double m1c2 = a1 * amu_c2;
1022       G4double escale = z1 * Z * elm_coupling / au;
1023       // energy at screening distance
1024       G4double emax = m1c2;
1025       // model is doubtful in very relativistic range
1026       G4double eratkin = 0.9999 * (4 * a1 * a2) / ((a1 + a2) * (a1 + a2));
1027       // #maximum kinematic ratio possible at 180 degrees
1028       G4double cmfact0 = st.emin / cm_energy(a1, a2, st.emin);
1029       G4double l1 = std::log(emax);
1030       G4double l0 = std::log(st.emin * cmfact0 / eratkin);
1031 
1032       if (verbosity >= 1)
1033         G4cout << "Native Screening: " << screeningKey << " " << z1 << " " << a1 << " " << Z << " "
1034                << a2 << " " << recoilCutoff << G4endl;
1035 
1036       for (size_t idx = 0; idx < sigLen; idx++) {
1037         G4double ee = std::exp(idx * ((l1 - l0) / sigLen) + l0);
1038         G4double gamma = 1.0 + ee / m1c2;
1039         G4double eratio = (cmfact0 * st.emin) / ee;
1040         // factor by which ee needs to be reduced to get emin
1041         G4double theta = thetac(gamma * a1, a2, eratio);
1042 
1043         G4double eps = cm_energy(a1, a2, ee) / escale;
1044         // #make sure lab energy is converted to CM for these
1045         // calculations
1046         c2eps.reset(0.0, 0.0, eps);
1047         // set correct slope in this function
1048 
1049         G4double q = theta / pi;
1050         // G4cout << ee << " " << m1c2 << " " << gamma << " "
1051         // << eps << " " << theta << " " << q << G4endl;
1052         // old way using root finder
1053         // G4double x0= x0func->find_root(1e-6*angstrom/au,
1054         // 0.9999*screen.xmax()/au, 1.0, 2*q-q*q);
1055         // new way using c2_inverse_function which caches
1056         // useful information so should be a bit faster
1057         // since we are scanning this in strict order.
1058         G4double x0 = 0;
1059         try {
1060           x0 = x0_solution(2 * q - q * q);
1061         }
1062         catch (c2_exception&) {
1063           G4Exception("G4ScreenedNuclearRecoil::LoadData", "em0003", FatalException,
1064                       "failure in inverse solution to generate MFP tables");
1065         }
1066         G4double betasquared = x0 * x0 - x0 * phiau(x0) / eps;
1067         G4double sigma = pi * betasquared * au * au;
1068         energies[idx] = ee;
1069         data[idx] = sigma;
1070       }
1071       screeningData[Z] = st;
1072       sigmaMap[Z] = c2.log_log_interpolating_function().load(energies, data, true, 0, true, 0);
1073     }
1074   }
1075 }