File indexing completed on 2026-09-20 08:29:49
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
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;
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
0150
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
0158 if (nMatElements == 1) {
0159 element = (*elementVector)[0];
0160 }
0161 else {
0162
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
0181
0182 static G4StableIsotopes theIso;
0183
0184 nIsotopes = theIso.GetNumberOfIsotopes(Z);
0185 G4double random = 100.0 * G4UniformRand();
0186
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
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
0214
0215
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
0234 const G4MaterialTable* materialTable = G4Material::GetMaterialTable();
0235 if (materialTable == 0) {
0236 return;
0237 }
0238
0239
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
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
0263
0264
0265
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
0272 for (G4int eidx = 0; eidx < nmfpvals; eidx++)
0273 mfpvals[eidx] = 0.0;
0274
0275
0276
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
0283
0284 for (G4int eidx = 0; eidx < nmfpvals; eidx++) {
0285 mfpvals[eidx] += ndens * sigma(evals[eidx]);
0286 }
0287 }
0288
0289
0290 for (G4int eidx = 0; eidx < nmfpvals; eidx++) {
0291 mfpvals[eidx] = 1.0 / mfpvals[eidx];
0292 }
0293
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
0314
0315
0316
0317
0318 processMaxEnergy = 50000.0 * MeV;
0319 highEnergyLimit = 100.0 * MeV;
0320 lowEnergyLimit = physicsCutoff;
0321 registerDepositedEnergy = 1;
0322 MFPScale = 1.0;
0323
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
0341
0342
0343
0344
0345
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
0375
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
0381
0382
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
0410 }
0411 else if (energy > processMaxEnergy * a1) {
0412 return DBL_MAX;
0413 }
0414 else if (energy > highEnergyLimit * a1)
0415 energy = highEnergyLimit * a1;
0416
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
0438 meanFreePath = mfp(std::min(std::max(energy, mfp.xmin()), mfp.xmax()));
0439
0440
0441
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;
0451 IonizingLoss = 0.0;
0452
0453
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
0464
0465 const G4MaterialCutsCouple* couple = aTrack.GetMaterialCutsCouple();
0466
0467 const G4Material* mat = couple->GetMaterial();
0468
0469 G4double P = 0.0;
0470
0471 if (incidentEnergy < GetRecoilCutoff() * a1) {
0472
0473 DepositEnergy(z1, baseParticle->GetPDGMass() / amu_c2, mat, incidentEnergy);
0474 GetParticleChange().ProposeEnergy(0.0);
0475
0476 validCollision = 0;
0477 }
0478 else {
0479 G4double numberDensity = mat->GetTotNbOfAtomsPerVolume();
0480 G4double lattice = 0.5 / std::pow(numberDensity, 1.0 / 3.0);
0481
0482 G4double length = GetCurrentInteractionLength();
0483 G4double sigopi = 1.0 / (pi * numberDensity * length);
0484
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495 if (sigopi < lattice * lattice) {
0496
0497 P = std::sqrt(-std::log(G4UniformRand()) * sigopi);
0498 }
0499 else {
0500
0501 P = std::sqrt(G4UniformRand()) * lattice;
0502 }
0503
0504 G4double fraction = GetHardeningFraction();
0505 if (fraction && G4UniformRand() < fraction) {
0506
0507
0508 P /= std::sqrt(GetHardeningFactor());
0509 }
0510
0511
0512
0513
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
0519 validCollision = 0;
0520 }
0521 }
0522
0523
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
0550
0551 }
0552
0553 return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
0554 }
0555
0556 G4ScreenedCoulombClassicalKinematics::G4ScreenedCoulombClassicalKinematics()
0557 :
0558
0559
0560
0561
0562 phifunc(c2.const_plugin_function()),
0563 xovereps(c2.linear(0., 0., 0.)),
0564
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;
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);
0582 G4double bb2 = 0.5 * beta * beta;
0583 xx0 = std::sqrt(bb2 + std::sqrt(bb2 * bb2 + rho4));
0584 }
0585 else {
0586 G4double ee = 1.0 / (2.0 * eps);
0587 xx0 = ee + std::sqrt(ee * ee + beta * beta);
0588 if (master->CheckNuclearCollision(A, a1, xx0 * au)) return 0;
0589
0590 }
0591
0592
0593
0594 xovereps.reset(0., 0.0, au / eps);
0595 phifunc.set_function(&(screen->EMphiData.get()));
0596
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
0618
0619 G4double phiprime = phip * au;
0620
0621
0622 G4double lambda0 =
0623 1.0 / std::sqrt(0.5 + beta * beta / (2.0 * xx1 * xx1) - phiprime / (2.0 * eps));
0624
0625
0626
0627
0628
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
0641
0642 G4double thetac1 = pi * beta * alpha / xx1;
0643
0644 G4double sintheta = std::sin(thetac1);
0645 G4double costheta = -std::cos(thetac1);
0646
0647
0648
0649
0650
0651
0652
0653
0654 G4double zeta = std::atan2(sintheta, 1 - costheta);
0655
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;
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
0680
0681
0682
0683 G4double gamma = (1.0 + incidentEnergy / baseParticle->GetPDGMass());
0684 G4double a1 = kin.a1 * gamma;
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
0692 const G4ScreeningTables* screen = kin.crossSection->GetScreening(Z);
0693 G4double au = screen->au;
0694
0695 G4double beta = kin.impactParameter / au;
0696
0697 G4double eps = Ec / (screen->z1 * Z * elm_coupling / au);
0698
0699
0700 G4bool ok = DoScreeningComputation(master, screen, eps, beta);
0701 if (!ok) {
0702 master->SetValidCollision(0);
0703 return;
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;
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
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
0783
0784
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
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
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);
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
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
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);
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
0856
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
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
0870 }
0871
0872
0873 G4double logphiprime0 = (9.67 / 2.0) * (2 * 0.3344 - 1.0);
0874
0875 logphiprime0 *= (1.0 / au);
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
0884
0885
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
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
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
0934
0935
0936
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
0952 G4DataVector energies(sigLen);
0953 G4DataVector data(sigLen);
0954
0955 a1 = standardmass(z1);
0956
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
0973
0974
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
0986 G4ScreeningTables st;
0987
0988 st.EMphiData = screen;
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
0997
0998
0999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009 c2_linear_p<G4double>& c2eps = c2.linear(0.0, 0.0, 1.0);
1010
1011 G4_c2_ptr phiau = screen(c2.linear(0.0, 0.0, au));
1012 G4_c2_ptr x0func(phiau / c2eps);
1013
1014 x0func->set_domain(1e-6 * angstrom / au, 0.9999 * screen->xmax() / au);
1015
1016
1017
1018
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
1024 G4double emax = m1c2;
1025
1026 G4double eratkin = 0.9999 * (4 * a1 * a2) / ((a1 + a2) * (a1 + a2));
1027
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
1041 G4double theta = thetac(gamma * a1, a2, eratio);
1042
1043 G4double eps = cm_energy(a1, a2, ee) / escale;
1044
1045
1046 c2eps.reset(0.0, 0.0, eps);
1047
1048
1049 G4double q = theta / pi;
1050
1051
1052
1053
1054
1055
1056
1057
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 }