File indexing completed on 2025-02-23 09:21:03
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
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;
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
0152
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
0160 if (nMatElements == 1) {
0161 element = (*elementVector)[0];
0162 }
0163 else {
0164
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
0183
0184 static G4StableIsotopes theIso;
0185
0186 nIsotopes = theIso.GetNumberOfIsotopes(Z);
0187 G4double random = 100.0 * G4UniformRand();
0188
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
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
0216
0217
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
0236 const G4MaterialTable* materialTable = G4Material::GetMaterialTable();
0237 if (materialTable == 0) {
0238 return;
0239 }
0240
0241
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
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
0265
0266
0267
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
0274 for (G4int eidx = 0; eidx < nmfpvals; eidx++)
0275 mfpvals[eidx] = 0.0;
0276
0277
0278
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
0285
0286 for (G4int eidx = 0; eidx < nmfpvals; eidx++) {
0287 mfpvals[eidx] += ndens * sigma(evals[eidx]);
0288 }
0289 }
0290
0291
0292 for (G4int eidx = 0; eidx < nmfpvals; eidx++) {
0293 mfpvals[eidx] = 1.0 / mfpvals[eidx];
0294 }
0295
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
0316
0317
0318
0319
0320 processMaxEnergy = 50000.0 * MeV;
0321 highEnergyLimit = 100.0 * MeV;
0322 lowEnergyLimit = physicsCutoff;
0323 registerDepositedEnergy = 1;
0324 MFPScale = 1.0;
0325
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
0343
0344
0345
0346
0347
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
0377
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
0383
0384
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
0412 }
0413 else if (energy > processMaxEnergy * a1) {
0414 return DBL_MAX;
0415 }
0416 else if (energy > highEnergyLimit * a1)
0417 energy = highEnergyLimit * a1;
0418
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
0440 meanFreePath = mfp(std::min(std::max(energy, mfp.xmin()), mfp.xmax()));
0441
0442
0443
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;
0453 IonizingLoss = 0.0;
0454
0455
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
0466
0467 const G4MaterialCutsCouple* couple = aTrack.GetMaterialCutsCouple();
0468
0469 const G4Material* mat = couple->GetMaterial();
0470
0471 G4double P = 0.0;
0472
0473 if (incidentEnergy < GetRecoilCutoff() * a1) {
0474
0475 DepositEnergy(z1, baseParticle->GetPDGMass() / amu_c2, mat, incidentEnergy);
0476 GetParticleChange().ProposeEnergy(0.0);
0477
0478 validCollision = 0;
0479 }
0480 else {
0481 G4double numberDensity = mat->GetTotNbOfAtomsPerVolume();
0482 G4double lattice = 0.5 / std::pow(numberDensity, 1.0 / 3.0);
0483
0484 G4double length = GetCurrentInteractionLength();
0485 G4double sigopi = 1.0 / (pi * numberDensity * length);
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497 if (sigopi < lattice * lattice) {
0498
0499 P = std::sqrt(-std::log(G4UniformRand()) * sigopi);
0500 }
0501 else {
0502
0503 P = std::sqrt(G4UniformRand()) * lattice;
0504 }
0505
0506 G4double fraction = GetHardeningFraction();
0507 if (fraction && G4UniformRand() < fraction) {
0508
0509
0510 P /= std::sqrt(GetHardeningFactor());
0511 }
0512
0513
0514
0515
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
0521 validCollision = 0;
0522 }
0523 }
0524
0525
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
0552
0553 }
0554
0555 return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
0556 }
0557
0558 G4ScreenedCoulombClassicalKinematics::G4ScreenedCoulombClassicalKinematics()
0559 :
0560
0561
0562
0563
0564 phifunc(c2.const_plugin_function()),
0565 xovereps(c2.linear(0., 0., 0.)),
0566
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;
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);
0584 G4double bb2 = 0.5 * beta * beta;
0585 xx0 = std::sqrt(bb2 + std::sqrt(bb2 * bb2 + rho4));
0586 }
0587 else {
0588 G4double ee = 1.0 / (2.0 * eps);
0589 xx0 = ee + std::sqrt(ee * ee + beta * beta);
0590 if (master->CheckNuclearCollision(A, a1, xx0 * au)) return 0;
0591
0592 }
0593
0594
0595
0596 xovereps.reset(0., 0.0, au / eps);
0597 phifunc.set_function(&(screen->EMphiData.get()));
0598
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
0620
0621 G4double phiprime = phip * au;
0622
0623
0624 G4double lambda0 =
0625 1.0 / std::sqrt(0.5 + beta * beta / (2.0 * xx1 * xx1) - phiprime / (2.0 * eps));
0626
0627
0628
0629
0630
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
0643
0644 G4double thetac1 = pi * beta * alpha / xx1;
0645
0646 G4double sintheta = std::sin(thetac1);
0647 G4double costheta = -std::cos(thetac1);
0648
0649
0650
0651
0652
0653
0654
0655
0656 G4double zeta = std::atan2(sintheta, 1 - costheta);
0657
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;
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
0682
0683
0684
0685 G4double gamma = (1.0 + incidentEnergy / baseParticle->GetPDGMass());
0686 G4double a1 = kin.a1 * gamma;
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
0694 const G4ScreeningTables* screen = kin.crossSection->GetScreening(Z);
0695 G4double au = screen->au;
0696
0697 G4double beta = kin.impactParameter / au;
0698
0699 G4double eps = Ec / (screen->z1 * Z * elm_coupling / au);
0700
0701
0702 G4bool ok = DoScreeningComputation(master, screen, eps, beta);
0703 if (!ok) {
0704 master->SetValidCollision(0);
0705 return;
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;
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
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
0785
0786
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
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
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);
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
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
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);
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
0858
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
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
0872 }
0873
0874
0875 G4double logphiprime0 = (9.67 / 2.0) * (2 * 0.3344 - 1.0);
0876
0877 logphiprime0 *= (1.0 / au);
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
0886
0887
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
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
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
0936
0937
0938
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
0954 G4DataVector energies(sigLen);
0955 G4DataVector data(sigLen);
0956
0957 a1 = standardmass(z1);
0958
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
0975
0976
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
0988 G4ScreeningTables st;
0989
0990 st.EMphiData = screen;
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
0999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011 c2_linear_p<G4double>& c2eps = c2.linear(0.0, 0.0, 1.0);
1012
1013 G4_c2_ptr phiau = screen(c2.linear(0.0, 0.0, au));
1014 G4_c2_ptr x0func(phiau / c2eps);
1015
1016 x0func->set_domain(1e-6 * angstrom / au, 0.9999 * screen->xmax() / au);
1017
1018
1019
1020
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
1026 G4double emax = m1c2;
1027
1028 G4double eratkin = 0.9999 * (4 * a1 * a2) / ((a1 + a2) * (a1 + a2));
1029
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
1043 G4double theta = thetac(gamma * a1, a2, eratio);
1044
1045 G4double eps = cm_energy(a1, a2, ee) / escale;
1046
1047
1048 c2eps.reset(0.0, 0.0, eps);
1049
1050
1051 G4double q = theta / pi;
1052
1053
1054
1055
1056
1057
1058
1059
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 }