Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-23 09:21:03

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