Geant4 11.2.2
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
G4DNAPTBIonisationModel.cc
Go to the documentation of this file.
1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26// Authors: S. Meylan and C. Villagrasa (IRSN, France)
27// Models come from
28// M. Bug et al, Rad. Phys and Chem. 130, 459-479 (2017)
29//
30
32
35#include "G4LossTableManager.hh"
37#include "G4SystemOfUnits.hh"
40 const G4ParticleDefinition*, const G4String& nam,
41 const G4bool isAuger)
42 : G4VDNAModel(nam, applyToMaterial)
43{
44 if (isAuger) {
45 // create the PTB Auger model
46 fpDNAPTBAugerModel = std::make_unique<G4DNAPTBAugerModel>("e-_G4DNAPTBAugerModel");
47 }
48 fpTHF = G4Material::GetMaterial("THF", false);
49 fpPY = G4Material::GetMaterial("PY", false);
50 fpPU = G4Material::GetMaterial("PU", false);
51 fpTMP = G4Material::GetMaterial("TMP", false);
52 fpG4_WATER = G4Material::GetMaterial("G4_WATER", false);
53 fpBackbone_THF = G4Material::GetMaterial("backbone_THF", false);
54 fpCytosine_PY = G4Material::GetMaterial("cytosine_PY", false);
55 fpThymine_PY = G4Material::GetMaterial("thymine_PY", false);
56 fpAdenine_PU = G4Material::GetMaterial("adenine_PU", false);
57 fpBackbone_TMP = G4Material::GetMaterial("backbone_TMP", false);
58 fpGuanine_PU = G4Material::GetMaterial("guanine_PU", false);
59 fpN2 = G4Material::GetMaterial("N2", false);
60}
61
62//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
63
65 const G4DataVector& /*cuts*/)
66{
67 if (isInitialised) {
68 return;
69 }
70 if (verboseLevel > 3) {
71 G4cout << "Calling G4DNAPTBIonisationModel::Initialise()" << G4endl;
72 }
73
74 G4double scaleFactor = 1e-16 * cm * cm;
75 G4double scaleFactorBorn = (1.e-22 / 3.343) * m * m;
76
79
80 //*******************************************************
81 // Cross section data
82 //*******************************************************
83 std::size_t index;
84 if (particle == electronDef) {
85 // Raw materials
86 //
87 // MPietrzak
88 if (fpN2 != nullptr) {
89 index = fpN2->GetIndex();
90 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_N2",
91 "dna/sigmadiff_cumulated_ionisation_e-_PTB_N2", scaleFactor);
92 SetLowELimit(index, particle, 15.5 * eV);
93 SetHighELimit(index, particle, 1.02 * MeV);
94 }
95
96 // MPietrzak
97
98 if (fpTHF != nullptr) {
99 index = fpTHF->GetIndex();
100 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_THF",
101 "dna/sigmadiff_cumulated_ionisation_e-_PTB_THF", scaleFactor);
102 SetLowELimit(index, particle, 12. * eV);
103 SetHighELimit(index, particle, 1. * keV);
104 }
105
106 if (fpPY != nullptr) {
107 index = fpPY->GetIndex();
108 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_PY",
109 "dna/sigmadiff_cumulated_ionisation_e-_PTB_PY", scaleFactor);
110 SetLowELimit(index, particle, 12. * eV);
111 SetHighELimit(index, particle, 1. * keV);
112 }
113
114 if (fpPU != nullptr) {
115 index = fpPU->GetIndex();
116 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_PU",
117 "dna/sigmadiff_cumulated_ionisation_e-_PTB_PU", scaleFactor);
118 SetLowELimit(index, particle, 12. * eV);
119 SetHighELimit(index, particle, 1. * keV);
120 }
121 if (fpTMP != nullptr) {
122 index = fpTMP->GetIndex();
123 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_TMP",
124 "dna/sigmadiff_cumulated_ionisation_e-_PTB_TMP", scaleFactor);
125 SetLowELimit(index, particle, 12. * eV);
126 SetHighELimit(index, particle, 1. * keV);
127 }
128
129 if (fpG4_WATER != nullptr) {
130 index = fpG4_WATER->GetIndex();
131 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e_born",
132 "dna/sigmadiff_ionisation_e_born", scaleFactorBorn);
133 SetLowELimit(index, particle, 12. * eV);
134 SetHighELimit(index, particle, 1. * keV);
135 }
136 // DNA materials
137 //
138 if (fpBackbone_THF != nullptr) {
139 index = fpBackbone_THF->GetIndex();
140 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_THF",
141 "dna/sigmadiff_cumulated_ionisation_e-_PTB_THF", scaleFactor * 33. / 30);
142 SetLowELimit(index, particle, 12. * eV);
143 SetHighELimit(index, particle, 1. * keV);
144 }
145
146 if (fpCytosine_PY != nullptr) {
147 index = fpCytosine_PY->GetIndex();
148 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_PY",
149 "dna/sigmadiff_cumulated_ionisation_e-_PTB_PY", scaleFactor * 42. / 30);
150 SetLowELimit(index, particle, 12. * eV);
151 SetHighELimit(index, particle, 1. * keV);
152 }
153
154 if (fpThymine_PY != nullptr) {
155 index = fpThymine_PY->GetIndex();
156 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_PY",
157 "dna/sigmadiff_cumulated_ionisation_e-_PTB_PY", scaleFactor * 48. / 30);
158 SetLowELimit(index, particle, 12. * eV);
159 SetHighELimit(index, particle, 1. * keV);
160 }
161
162 if (fpAdenine_PU != nullptr) {
163 index = fpAdenine_PU->GetIndex();
164 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_PU",
165 "dna/sigmadiff_cumulated_ionisation_e-_PTB_PU", scaleFactor * 50. / 44);
166 SetLowELimit(index, particle, 12. * eV);
167 SetHighELimit(index, particle, 1. * keV);
168 }
169
170 if (fpGuanine_PU != nullptr) {
171 index = fpGuanine_PU->GetIndex();
172 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_PU",
173 "dna/sigmadiff_cumulated_ionisation_e-_PTB_PU", scaleFactor * 56. / 44);
174 SetLowELimit(index, particle, 12. * eV);
175 SetHighELimit(index, particle, 1. * keV);
176 }
177
178 if (fpBackbone_TMP != nullptr) {
179 index = fpBackbone_TMP->GetIndex();
180 AddCrossSectionData(index, particle, "dna/sigma_ionisation_e-_PTB_TMP",
181 "dna/sigmadiff_cumulated_ionisation_e-_PTB_TMP", scaleFactor * 33. / 50);
182 SetLowELimit(index, particle, 12. * eV);
183 SetHighELimit(index, particle, 1. * keV);
184 }
185 }
186
187 else if (particle == protonDef) {
188 G4String particleName = particle->GetParticleName();
189
190 // Raw materials
191 //
192 if (fpTHF != nullptr) {
193 index = fpTHF->GetIndex();
194 AddCrossSectionData(index, particle, "dna/sigma_ionisation_p_HKS_THF",
195 "dna/sigmadiff_cumulated_ionisation_p_PTB_THF", scaleFactor);
196 SetLowELimit(index, particle, 70. * keV);
197 SetHighELimit(index, particle, 10. * MeV);
198 }
199
200 if (fpPY != nullptr) {
201 index = fpPY->GetIndex();
202 AddCrossSectionData(index, particle, "dna/sigma_ionisation_p_HKS_PY",
203 "dna/sigmadiff_cumulated_ionisation_p_PTB_PY", scaleFactor);
204 SetLowELimit(index, particle, 70. * keV);
205 SetHighELimit(index, particle, 10. * MeV);
206 }
207 /*
208 AddCrossSectionData("PU",
209 particleName,
210 "dna/sigma_ionisation_e-_PTB_PU",
211 "dna/sigmadiff_cumulated_ionisation_e-_PTB_PU",
212 scaleFactor);
213 SetLowELimit("PU", particleName2, 70.*keV);
214 SetHighELimit("PU", particleName2, 10.*keV);
215*/
216
217 if (fpTMP != nullptr) {
218 index = fpTMP->GetIndex();
219 AddCrossSectionData(index, particle, "dna/sigma_ionisation_p_HKS_TMP",
220 "dna/sigmadiff_cumulated_ionisation_p_PTB_TMP", scaleFactor);
221 SetLowELimit(index, particle, 70. * keV);
222 SetHighELimit(index, particle, 10. * MeV);
223 }
224 }
225 // *******************************************************
226 // deal with composite materials
227 // *******************************************************
229 LoadCrossSectionData(particle);
231 fpModelData = this;
232 }
233 else {
234 auto dataModel = dynamic_cast<G4DNAPTBIonisationModel*>(
236 if (dataModel == nullptr) {
237 G4cout << "G4DNAPTBIonisationModel::Initialise:: not good modelData" << G4endl;
238 G4Exception("G4DNAPTBIonisationModel::Initialise", "PTB0004", FatalException,
239 "not good modelData");
240 }
241 else {
242 fpModelData = dataModel;
243 }
244 }
245 // initialise DNAPTBAugerModel
246 if (fpDNAPTBAugerModel) {
247 fpDNAPTBAugerModel->Initialise();
248 }
250 isInitialised = true;
251}
252
253//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
254
256 const G4ParticleDefinition* p,
257 G4double ekin, G4double /*emin*/,
258 G4double /*emax*/)
259{
260 // initialise the cross section value (output value)
261 G4double sigma(0);
262
263 // Get the current particle name
264 const G4String& particleName = p->GetParticleName();
265 const std::size_t& matID = material->GetIndex();
266
267 // Set the low and high energy limits
268 G4double lowLim = fpModelData->GetLowELimit(matID, p);
269 G4double highLim = fpModelData->GetHighELimit(matID, p);
270
271 // Check that we are in the correct energy range
272 if (ekin >= lowLim && ekin < highLim) {
273 // Get the map with all the model data tables
274 auto tableData = fpModelData->GetData();
275 if ((*tableData)[matID][p] == nullptr) {
276 G4Exception("G4DNAPTBIonisationModel::CrossSectionPerVolume", "em00236", FatalException,
277 "No model is registered");
278 }
279 // Retrieve the cross section value for the current material, particle and energy values
280 sigma = (*tableData)[matID][p]->FindValue(ekin);
281
282 if (verboseLevel > 2) {
283 G4cout << "__________________________________" << G4endl;
284 G4cout << "°°° G4DNAPTBIonisationModel - XS INFO START" << G4endl;
285 G4cout << "°°° Kinetic energy(eV)=" << ekin / eV << " particle : " << particleName << G4endl;
286 G4cout << "°°° Cross section per " << matID << " index molecule (cm^2)=" << sigma / cm / cm
287 << G4endl;
288 G4cout << "°°° G4DNAPTBIonisationModel - XS INFO END" << G4endl;
289 }
290 }
291
292 // Return the cross section value
293 auto MolDensity =
295 return sigma * MolDensity;
296}
297
298//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
299
300void G4DNAPTBIonisationModel::SampleSecondaries(std::vector<G4DynamicParticle*>* fvect,
301 const G4MaterialCutsCouple* pCouple,
302 const G4DynamicParticle* aDynamicParticle,
303 G4double /*tmin*/, G4double /*tmax*/)
304{
305 // Get the current particle energy
306 G4double k = aDynamicParticle->GetKineticEnergy();
307 const std::size_t& materialID = pCouple->GetMaterial()->GetIndex();
308
309 // Get the current particle name
310 const auto& p = aDynamicParticle->GetDefinition();
311 auto materialName = pCouple->GetMaterial()->GetName();
312 // Get the energy limits
313 G4double lowLim = fpModelData->GetLowELimit(materialID, p);
314 G4double highLim = fpModelData->GetHighELimit(materialID, p);
315
316 // Check if we are in the correct energy range
317 if (k >= lowLim && k < highLim) {
318 G4ParticleMomentum primaryDirection = aDynamicParticle->GetMomentumDirection();
319 G4double particleMass = aDynamicParticle->GetDefinition()->GetPDGMass();
320 G4double totalEnergy = k + particleMass;
321 G4double pSquare = k * (totalEnergy + particleMass);
322 G4double totalMomentum = std::sqrt(pSquare);
323
324 // Get the ionisation shell from a random sampling
325 G4int ionizationShell = fpModelData->RandomSelectShell(k, p, materialID);
326
327 // Get the binding energy from the ptbStructure class
328 G4double bindingEnergy = ptbStructure.IonisationEnergy(ionizationShell, materialID);
329
330 // Initialize the secondary kinetic energy to a negative value.
331 G4double secondaryKinetic(-1000 * eV);
332
333 if (fpG4_WATER == nullptr || materialID != fpG4_WATER->GetIndex()) {
334 // Get the energy of the secondary particle
335 secondaryKinetic = fpModelData->RandomizeEjectedElectronEnergyFromCumulated(
336 aDynamicParticle->GetDefinition(), k / eV, ionizationShell, materialID);
337 }
338 else {
339 secondaryKinetic = fpModelData->RandomizeEjectedElectronEnergy(
340 aDynamicParticle->GetDefinition(), k, ionizationShell, materialID);
341 }
342
343 if (secondaryKinetic <= 0) {
344 G4cout << "Fatal error *************************************** " << secondaryKinetic / eV
345 << G4endl;
346 G4cout << "secondaryKinetic: " << secondaryKinetic / eV << G4endl;
347 G4cout << "k: " << k / eV << G4endl;
348 G4cout << "shell: " << ionizationShell << G4endl;
349 G4cout << "material:" << materialName << G4endl;
350 G4Exception("G4DNAPTBIonisationModel::SampleSecondaries", "em0026", FatalException,
351 "Fatal error:: scatteredEnergy <= 0");
352 }
353
354 G4double cosTheta = 0.;
355 G4double phi = 0.;
356 RandomizeEjectedElectronDirection(aDynamicParticle->GetDefinition(), k, secondaryKinetic,
357 cosTheta, phi);
358
359 G4double sinTheta = std::sqrt(1. - cosTheta * cosTheta);
360 G4double dirX = sinTheta * std::cos(phi);
361 G4double dirY = sinTheta * std::sin(phi);
362 G4double dirZ = cosTheta;
363 G4ThreeVector deltaDirection(dirX, dirY, dirZ);
364 deltaDirection.rotateUz(primaryDirection);
365
366 // The model is written only for electron and thus we want the change the direction of the
367 // incident electron after each ionization. However, if other particle are going to be
368 // introduced within this model the following should be added:
369 //
370 // Check if the particle is an electron
371 if (aDynamicParticle->GetDefinition() == G4Electron::ElectronDefinition()) {
372 // If yes do the following code until next commented "else" statement
373
374 G4double deltaTotalMomentum =
375 std::sqrt(secondaryKinetic * (secondaryKinetic + 2. * electron_mass_c2));
376 G4double finalPx =
377 totalMomentum * primaryDirection.x() - deltaTotalMomentum * deltaDirection.x();
378 G4double finalPy =
379 totalMomentum * primaryDirection.y() - deltaTotalMomentum * deltaDirection.y();
380 G4double finalPz =
381 totalMomentum * primaryDirection.z() - deltaTotalMomentum * deltaDirection.z();
382 G4double finalMomentum = std::sqrt(finalPx * finalPx + finalPy * finalPy + finalPz * finalPz);
383 finalPx /= finalMomentum;
384 finalPy /= finalMomentum;
385 finalPz /= finalMomentum;
386
387 G4ThreeVector direction(finalPx, finalPy, finalPz);
388 if (direction.unit().getX() > 1 || direction.unit().getY() > 1 || direction.unit().getZ() > 1)
389 {
390 G4cout << "Fatal error ****************************" << G4endl;
391 G4cout << "direction problem " << direction.unit() << G4endl;
392 G4Exception("G4DNAPTBIonisationModel::SampleSecondaries", "em0017", FatalException,
393 "Fatal error:: direction problem");
394 }
395
396 // Give the new direction to the particle
398 }
399 // If the particle is not an electron
400 else {
402 }
403
404 // note that secondaryKinetic is the energy of the delta ray, not of all secondaries.
405 G4double scatteredEnergy = k - bindingEnergy - secondaryKinetic;
406
407 if (scatteredEnergy <= 0) {
408 G4cout << "Fatal error ****************************" << G4endl;
409 G4cout << "k: " << k / eV << G4endl;
410 G4cout << "secondaryKinetic: " << secondaryKinetic / eV << G4endl;
411 G4cout << "shell: " << ionizationShell << G4endl;
412 G4cout << "bindingEnergy: " << bindingEnergy / eV << G4endl;
413 G4cout << "scatteredEnergy: " << scatteredEnergy / eV << G4endl;
414 G4cout << "material: " << materialName << G4endl;
415 G4Exception("G4DNAPTBIonisationModel::SampleSecondaries", "em0016", FatalException,
416 "Fatal error:: scatteredEnergy <= 0");
417 }
418
419 // Set the new energy of the particle
421
422 // Set the energy deposited by the ionization
423 fParticleChangeForGamma->ProposeLocalEnergyDeposit(k - scatteredEnergy - secondaryKinetic);
424
425 // Create the new particle with its characteristics
426 auto dp = new G4DynamicParticle(G4Electron::Electron(), deltaDirection, secondaryKinetic);
427 fvect->push_back(dp);
428
429 // Check if the auger model is activated (ie instanciated)
430 if (fpDNAPTBAugerModel) {
431 // run the PTB Auger model
432 if (materialName != "G4_WATER") {
433 fpDNAPTBAugerModel->ComputeAugerEffect(fvect, materialName, bindingEnergy);
434 }
435 }
436 }
437}
438
439//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
440
441void G4DNAPTBIonisationModel::ReadDiffCSFile(const std::size_t& materialID,
442 const G4ParticleDefinition* p, const G4String& file,
443 const G4double& scaleFactor)
444{
445 // To read and save the informations contained within the differential cross section files
446
447 // get the path of the G4LEDATA data folder
448 const char* path = G4FindDataDir("G4LEDATA");
449 // if it is not found then quit and print error message
450 if (path == nullptr) {
451 G4Exception("G4DNAPTBIonisationModel::ReadAllDiffCSFiles", "em0006", FatalException,
452 "G4LEDATA environment variable was not set.");
453 return;
454 }
455
456 // build the fullFileName path of the data file
457 std::ostringstream fullFileName;
458 fullFileName << path << "/" << file << ".dat";
459
460 // open the data file
461 std::ifstream diffCrossSection(fullFileName.str().c_str());
462 // error if file is not there
463 std::stringstream endPath;
464 if (!diffCrossSection) {
465 endPath << "Missing data file: " << file;
466 G4Exception("G4DNAPTBIonisationModel::Initialise", "em0003", FatalException,
467 endPath.str().c_str());
468 }
469
470 // load data from the file
471 fTMapWithVec[materialID][p].push_back(0.);
472
473 G4String line;
474
475 // read the file until we reach the end of file point
476 // fill fTMapWithVec, diffCrossSectionData, fEnergyTransferData, fProbaShellMap and
477 // fEMapWithVector
478 while (std::getline(diffCrossSection, line)) {
479 // check if the line is comment or empty
480 //
481 std::istringstream testIss(line);
482 G4String test;
483 testIss >> test;
484 // check first caracter to determine if following information is data or comments
485 if (test == "#") {
486 // skip the line by beginning a new while loop.
487 continue;
488 }
489 // check if line is empty
490 if (line.empty()) {
491 // skip the line by beginning a new while loop.
492 continue;
493 }
494 //
495 // end of the check
496
497 // transform the line into a iss
498 std::istringstream iss(line);
499
500 // Initialise the variables to be filled
501 G4double T;
502 G4double E;
503
504 // Filled T and E with the first two numbers of each file line
505 iss >> T >> E;
506
507 // Fill the fTMapWithVec container with all the different T values contained within the file.
508 // Duplicate must be avoided and this is the purpose of the if statement
509 if (T != fTMapWithVec[materialID][p].back()) fTMapWithVec[materialID][p].push_back(T);
510
511 // iterate on each shell of the corresponding material
512 for (int shell = 0, eshell = ptbStructure.NumberOfLevels(materialID); shell < eshell; ++shell) {
513 // map[material][particle][shell][T][E]=diffCrossSectionValue
514 // Fill the map with the informations of the input file
515 iss >> diffCrossSectionData[materialID][p][shell][T][E];
516
517 if (fpG4_WATER != nullptr && fpG4_WATER->GetIndex() != materialID) {
518 // map[material][particle][shell][T][CS]=E
519 // Fill the map
520 fEnergySecondaryData[materialID][p][shell][T]
521 [diffCrossSectionData[materialID][p][shell][T][E]] = E;
522
523 // map[material][particle][shell][T]=CS_vector
524 // Fill the vector within the map
525 fProbaShellMap[materialID][p][shell][T].push_back(
526 diffCrossSectionData[materialID][p][shell][T][E]);
527 }
528 else {
529 diffCrossSectionData[materialID][p][shell][T][E] *= scaleFactor;
530 fEMapWithVector[materialID][p][T].push_back(E);
531 }
532 }
533 }
534}
535
536//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
537
538G4double G4DNAPTBIonisationModel::RandomizeEjectedElectronEnergy(
539 const G4ParticleDefinition* particleDefinition, G4double k, G4int shell, const std::size_t& materialID)
540{
541 if (particleDefinition == G4Electron::ElectronDefinition()) {
542 // G4double Tcut=25.0E-6;
543 G4double maximumEnergyTransfer;
544 ((k + ptbStructure.IonisationEnergy(shell, materialID)) / 2. > k)
545 ? maximumEnergyTransfer = k
546 : maximumEnergyTransfer = (k + ptbStructure.IonisationEnergy(shell, materialID)) / 2.;
547
548 // SI : original method
549 /*
550 G4double crossSectionMaximum = 0.;
551 for(G4double value=waterStructure.IonisationEnergy(shell); value<=maximumEnergyTransfer;
552 value+=0.1*eV)
553 {
554 G4double differentialCrossSection = DifferentialCrossSection(particleDefinition, k/eV,
555 value/eV, shell); if(differentialCrossSection >= crossSectionMaximum) crossSectionMaximum =
556 differentialCrossSection;
557 }
558 */
559
560 // SI : alternative method
561
562 // if (k > Tcut)
563 //{
564 G4double crossSectionMaximum = 0.;
565
566 G4double minEnergy = ptbStructure.IonisationEnergy(shell, materialID);
567 G4double maxEnergy = maximumEnergyTransfer;
568 G4int nEnergySteps = 50;
569 G4double value(minEnergy);
570 G4double stpEnergy(std::pow(maxEnergy / value, 1. / static_cast<G4double>(nEnergySteps - 1)));
571 G4int step(nEnergySteps);
572 while (step > 0) {
573 step--;
574 G4double differentialCrossSection =
575 DifferentialCrossSection(particleDefinition, k / eV, value / eV, shell, materialID);
576 if (differentialCrossSection >= crossSectionMaximum)
577 crossSectionMaximum = differentialCrossSection;
578 value *= stpEnergy;
579 }
580 //
581
582 G4double secondaryElectronKineticEnergy = 0.;
583
584 do {
585 secondaryElectronKineticEnergy =
587 * (maximumEnergyTransfer - ptbStructure.IonisationEnergy(shell, materialID));
588
589 } while (
590 G4UniformRand() * crossSectionMaximum > DifferentialCrossSection(
591 particleDefinition, k / eV,
592 (secondaryElectronKineticEnergy + ptbStructure.IonisationEnergy(shell, materialID)) / eV,
593 shell, materialID));
594
595 return secondaryElectronKineticEnergy;
596 }
597
598 if (particleDefinition == G4Proton::ProtonDefinition()) {
599 G4double maximumKineticEnergyTransfer = 4. * (electron_mass_c2 / proton_mass_c2) * k;
600
601 G4double crossSectionMaximum = 0.;
602 for (G4double value = ptbStructure.IonisationEnergy(shell, materialID);
603 value <= 4. * ptbStructure.IonisationEnergy(shell, materialID); value += 0.1 * eV)
604 {
605 G4double differentialCrossSection =
606 DifferentialCrossSection(particleDefinition, k / eV, value / eV, shell, materialID);
607 if (differentialCrossSection >= crossSectionMaximum)
608 crossSectionMaximum = differentialCrossSection;
609 }
610
611 G4double secondaryElectronKineticEnergy = 0.;
612 do {
613 secondaryElectronKineticEnergy = G4UniformRand() * maximumKineticEnergyTransfer;
614 } while (
615 G4UniformRand() * crossSectionMaximum >= DifferentialCrossSection(
616 particleDefinition, k / eV,
617 (secondaryElectronKineticEnergy + ptbStructure.IonisationEnergy(shell, materialID)) / eV,
618 shell, materialID));
619
620 return secondaryElectronKineticEnergy;
621 }
622
623 return 0;
624}
625
626//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
627
628void G4DNAPTBIonisationModel::RandomizeEjectedElectronDirection(const G4ParticleDefinition* p,
629 G4double k, G4double secKinetic,
630 G4double& cosTheta, G4double& phi)
631{
633 phi = twopi * G4UniformRand();
634 if (secKinetic < 50. * eV)
635 cosTheta = (2. * G4UniformRand()) - 1.;
636 else if (secKinetic <= 200. * eV) {
637 if (G4UniformRand() <= 0.1)
638 cosTheta = (2. * G4UniformRand()) - 1.;
639 else
640 cosTheta = G4UniformRand() * (std::sqrt(2.) / 2);
641 }
642 else {
643 G4double sin2O = (1. - secKinetic / k) / (1. + secKinetic / (2. * electron_mass_c2));
644 cosTheta = std::sqrt(1. - sin2O);
645 }
646 }
647 else if (p == G4Proton::ProtonDefinition()) {
648 G4double maxSecKinetic = 4. * (electron_mass_c2 / proton_mass_c2) * k;
649 phi = twopi * G4UniformRand();
650
651 // cosTheta = std::sqrt(secKinetic / maxSecKinetic);
652
653 // Restriction below 100 eV from Emfietzoglou (2000)
654
655 (secKinetic > 100 * eV) ? cosTheta = std::sqrt(secKinetic / maxSecKinetic)
656 : cosTheta = (2. * G4UniformRand()) - 1.;
657 }
658}
659
660//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
661
662double G4DNAPTBIonisationModel::DifferentialCrossSection(const G4ParticleDefinition* p, G4double k,
663 G4double energyTransfer,
664 G4int ionizationLevelIndex,
665 const std::size_t& materialID)
666{
667 G4double sigma = 0.;
668 G4double shellEnergy(ptbStructure.IonisationEnergy(ionizationLevelIndex, materialID));
669 G4double kSE(energyTransfer - shellEnergy);
670
671 if (energyTransfer >= shellEnergy) {
672 G4double valueT1 = 0;
673 G4double valueT2 = 0;
674 G4double valueE21 = 0;
675 G4double valueE22 = 0;
676 G4double valueE12 = 0;
677 G4double valueE11 = 0;
678
679 G4double xs11 = 0;
680 G4double xs12 = 0;
681 G4double xs21 = 0;
682 G4double xs22 = 0;
683
685 // k should be in eV and energy transfer eV also
686 auto t2 =
687 std::upper_bound(fTMapWithVec[materialID][p].begin(), fTMapWithVec[materialID][p].end(), k);
688 auto t1 = t2 - 1;
689
690 // SI : the following condition avoids situations where energyTransfer >last vector element
691 if (kSE <= fEMapWithVector[materialID][p][(*t1)].back()
692 && kSE <= fEMapWithVector[materialID][p][(*t2)].back())
693 {
694 auto e12 = std::upper_bound(fEMapWithVector[materialID][p][(*t1)].begin(),
695 fEMapWithVector[materialID][p][(*t1)].end(), kSE);
696 auto e11 = e12 - 1;
697
698 auto e22 = std::upper_bound(fEMapWithVector[materialID][p][(*t2)].begin(),
699 fEMapWithVector[materialID][p][(*t2)].end(), kSE);
700 auto e21 = e22 - 1;
701
702 valueT1 = *t1;
703 valueT2 = *t2;
704 valueE21 = *e21;
705 valueE22 = *e22;
706 valueE12 = *e12;
707 valueE11 = *e11;
708
709 xs11 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT1][valueE11];
710 xs12 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT1][valueE12];
711 xs21 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT2][valueE21];
712 xs22 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT2][valueE22];
713 }
714 }
715
716 if (p == G4Proton::ProtonDefinition()) {
717 // k should be in eV and energy transfer eV also
718 auto t2 =
719 std::upper_bound(fTMapWithVec[materialID][p].begin(), fTMapWithVec[materialID][p].end(), k);
720 auto t1 = t2 - 1;
721
722 auto e12 = std::upper_bound(fEMapWithVector[materialID][p][(*t1)].begin(),
723 fEMapWithVector[materialID][p][(*t1)].end(), kSE);
724 auto e11 = e12 - 1;
725
726 auto e22 = std::upper_bound(fEMapWithVector[materialID][p][(*t2)].begin(),
727 fEMapWithVector[materialID][p][(*t2)].end(), kSE);
728 auto e21 = e22 - 1;
729
730 valueT1 = *t1;
731 valueT2 = *t2;
732 valueE21 = *e21;
733 valueE22 = *e22;
734 valueE12 = *e12;
735 valueE11 = *e11;
736
737 xs11 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT1][valueE11];
738 xs12 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT1][valueE12];
739 xs21 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT2][valueE21];
740 xs22 = diffCrossSectionData[materialID][p][ionizationLevelIndex][valueT2][valueE22];
741 }
742
743 G4double xsProduct = xs11 * xs12 * xs21 * xs22;
744
745 if (xsProduct != 0.) {
746 sigma = QuadInterpolator(valueE11, valueE12, valueE21, valueE22, xs11, xs12, xs21, xs22,
747 valueT1, valueT2, k, kSE);
748 }
749 }
750
751 return sigma;
752}
753
754//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
755
756G4double G4DNAPTBIonisationModel::RandomizeEjectedElectronEnergyFromCumulated(
757 const G4ParticleDefinition* p, G4double k, G4int ionizationLevelIndex, const std::size_t& materialID)
758{
759 // k should be in eV
760
761 // Schematic explanation.
762 // We will do an interpolation to get a final E value (ejected electron energy).
763 // 1/ We choose a random number between 0 and 1 (ie we select a cumulated cross section).
764 // 2/ We look for T_lower and T_upper.
765 // 3/ We look for the cumulated corresponding cross sections and their associated E values.
766 //
767 // T_low | CS_low_1 -> E_low_1
768 // | CS_low_2 -> E_low_2
769 // T_up | CS_up_1 -> E_up_1
770 // | CS_up_2 -> E_up_2
771 //
772 // 4/ We interpolate to get our E value.
773 //
774 // T_low | CS_low_1 -> E_low_1 -----
775 // | |----> E_low --
776 // | CS_low_2 -> E_low_2 ----- |
777 // | ---> E_final
778 // T_up | CS_up_1 -> E_up_1 ------- |
779 // | |----> E_up ---
780 // | CS_up_2 -> E_up_2 -------
781
782 // Initialize some values
783 //
784 G4double ejectedElectronEnergy = 0.;
785 G4double valueK1 = 0;
786 G4double valueK2 = 0;
787 G4double valueCumulCS21 = 0;
788 G4double valueCumulCS22 = 0;
789 G4double valueCumulCS12 = 0;
790 G4double valueCumulCS11 = 0;
791 G4double secElecE11 = 0;
792 G4double secElecE12 = 0;
793 G4double secElecE21 = 0;
794 G4double secElecE22 = 0;
795 G4String particleName = p->GetParticleName();
796
797 // ***************************************************************************
798 // Get a random number between 0 and 1 to compare with the cumulated CS
799 // ***************************************************************************
800 //
801 // It will allow us to choose an ejected electron energy with respect to the CS.
802 G4double random = G4UniformRand();
803
804 // **********************************************
805 // Take the input from the data tables
806 // **********************************************
807
808 // Cumulated tables are like this: T E cumulatedCS1 cumulatedCS2 cumulatedCS3
809 // We have two sets of loaded data: fTMapWithVec which contains data about T (incident particle
810 // energy) and fProbaShellMap which contains cumulated cross section data. Since we already have a
811 // specific T energy value which could not be explicitly in the table, we must interpolate all the
812 // values.
813
814 // First, we select the upper and lower T data values surrounding our T value (ie "k").
815 auto k2 =
816 std::upper_bound(fTMapWithVec[materialID][p].begin(), fTMapWithVec[materialID][p].end(), k);
817 auto k1 = k2 - 1;
818
819 // Check if we have found a k2 value (0 if we did not found it).
820 // A missing k2 value can be caused by a energy to high for the data table,
821 // Ex : table done for 12*eV -> 1000*eV and k=2000*eV
822 // then k2 = 0 and k1 = max of the table.
823 // To detect this, we check that k1 is not superior to k2.
824 if (*k1 > *k2) {
825 // Error
826 G4cerr << "**************** Fatal error ******************" << G4endl;
827 G4cerr << "G4DNAPTBIonisationModel::RandomizeEjectedElectronEnergyFromCumulated" << G4endl;
828 G4cerr << "You have *k1 > *k2 with k1 " << *k1 << " and k2 " << *k2 << G4endl;
829 G4cerr
830 << "This may be because the energy of the incident particle is to high for the data table."
831 << G4endl;
832 G4cerr << "Particle energy (eV): " << k << G4endl;
833 exit(EXIT_FAILURE);
834 }
835
836 // We have a random number and we select the cumulated cross section data values surrounding our
837 // random number. But we need to do that for each T value (ie two T values) previously selected.
838 //
839 // First one.
840 auto cumulCS12 =
841 std::upper_bound(fProbaShellMap[materialID][p][ionizationLevelIndex][(*k1)].begin(),
842 fProbaShellMap[materialID][p][ionizationLevelIndex][(*k1)].end(), random);
843 auto cumulCS11 = cumulCS12 - 1;
844 // Second one.
845 auto cumulCS22 =
846 std::upper_bound(fProbaShellMap[materialID][p][ionizationLevelIndex][(*k2)].begin(),
847 fProbaShellMap[materialID][p][ionizationLevelIndex][(*k2)].end(), random);
848 auto cumulCS21 = cumulCS22 - 1;
849
850 // Now that we have the "values" through pointers, we access them.
851 valueK1 = *k1;
852 valueK2 = *k2;
853 valueCumulCS11 = *cumulCS11;
854 valueCumulCS12 = *cumulCS12;
855 valueCumulCS21 = *cumulCS21;
856 valueCumulCS22 = *cumulCS22;
857
858 // *************************************************************
859 // Do the interpolation to get the ejected electron energy
860 // *************************************************************
861
862 // Here we will get four E values corresponding to our four cumulated cross section values
863 // previously selected. But we need to take into account a specific case: we have selected a shell
864 // by using the ionisation cross section table and, since we get two T values, we could have
865 // differential cross sections (or cumulated) equal to 0 for the lower T and not for the upper T.
866 // When looking for the cumulated cross section values which surround the selected random number
867 // (for the lower T), the upper_bound method will only found 0 values. Thus, the upper_bound
868 // method will return the last E value present in the table for the selected T. The last E value
869 // being the highest, we will later perform an interpolation between a high E value (for the lower
870 // T) and a small E value (for the upper T). This is inconsistent because if the cross section are
871 // equal to zero for the lower T then it means it is not possible to ionize and, thus, to have a
872 // secondary electron. But, in our situation, it is possible to ionize for the upper T AND for an
873 // interpolate T value between Tupper Tlower. That's why the final E value should be interpolate
874 // between 0 and the E value (upper T).
875 //
876 if (cumulCS12 == fProbaShellMap[materialID][p][ionizationLevelIndex][(*k1)].end()) {
877 // Here we are in the special case and we force Elower1 and Elower2 to be equal at 0 for the
878 // interpolation.
879 secElecE11 = 0;
880 secElecE12 = 0;
881 secElecE21 = fEnergySecondaryData[materialID][p][ionizationLevelIndex][valueK2][valueCumulCS21];
882 secElecE22 = fEnergySecondaryData[materialID][p][ionizationLevelIndex][valueK2][valueCumulCS22];
883
884 valueCumulCS11 = 0;
885 valueCumulCS12 = 0;
886 }
887 else {
888 // No special case, interpolation will happen as usual.
889 secElecE11 = fEnergySecondaryData[materialID][p][ionizationLevelIndex][valueK1][valueCumulCS11];
890 secElecE12 = fEnergySecondaryData[materialID][p][ionizationLevelIndex][valueK1][valueCumulCS12];
891 secElecE21 = fEnergySecondaryData[materialID][p][ionizationLevelIndex][valueK2][valueCumulCS21];
892 secElecE22 = fEnergySecondaryData[materialID][p][ionizationLevelIndex][valueK2][valueCumulCS22];
893 }
894
895 ejectedElectronEnergy =
896 QuadInterpolator(valueCumulCS11, valueCumulCS12, valueCumulCS21, valueCumulCS22, secElecE11,
897 secElecE12, secElecE21, secElecE22, valueK1, valueK2, k, random);
898
899 // **********************************************
900 // Some tests for debugging
901 // **********************************************
902
903 G4double bindingEnergy(ptbStructure.IonisationEnergy(ionizationLevelIndex, materialID) / eV);
904 if (k - ejectedElectronEnergy - bindingEnergy <= 0 || ejectedElectronEnergy <= 0) {
905 G4cout << "k " << k << G4endl;
906 G4cout << "material ID : " << materialID << G4endl;
907 G4cout << "secondaryKin " << ejectedElectronEnergy << G4endl;
908 G4cout << "shell " << ionizationLevelIndex << G4endl;
909 G4cout << "bindingEnergy " << bindingEnergy << G4endl;
910 G4cout << "scatteredEnergy " << k - ejectedElectronEnergy - bindingEnergy << G4endl;
911 G4cout << "rand " << random << G4endl;
912 G4cout << "surrounding k values: valueK1 valueK2\n" << valueK1 << " " << valueK2 << G4endl;
913 G4cout << "surrounding E values: secElecE11 secElecE12 secElecE21 secElecE22\n"
914 << secElecE11 << " " << secElecE12 << " " << secElecE21 << " " << secElecE22 << " "
915 << G4endl;
916 G4cout
917 << "surrounding cumulCS values: valueCumulCS11 valueCumulCS12 valueCumulCS21 valueCumulCS22\n"
918 << valueCumulCS11 << " " << valueCumulCS12 << " " << valueCumulCS21 << " " << valueCumulCS22
919 << " " << G4endl;
921 errmsg << "*****************************" << G4endl;
922 errmsg << "Fatal error, EXIT." << G4endl;
923 G4Exception("G4DNAPTBIonisationModel::RandomizeEjectedElectronEnergyFromCumulated", "",
924 FatalException, errmsg);
925 exit(EXIT_FAILURE);
926 }
927
928 return ejectedElectronEnergy * eV;
929}
930
931//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
932
933G4double G4DNAPTBIonisationModel::LogLogInterpolate(G4double e1, G4double e2, G4double e,
934 G4double xs1, G4double xs2)
935{
936 G4double value(0);
937
938 // Switch to log-lin interpolation for faster code
939
940 if ((e2 - e1) != 0 && xs1 != 0 && xs2 != 0) {
941 G4double d1 = std::log10(xs1);
942 G4double d2 = std::log10(xs2);
943 value = std::pow(10., (d1 + (d2 - d1) * (e - e1) / (e2 - e1)));
944 }
945
946 // Switch to lin-lin interpolation for faster code
947 // in case one of xs1 or xs2 (=cum proba) value is zero
948
949 if ((e2 - e1) != 0 && (xs1 == 0 || xs2 == 0)) {
950 G4double d1 = xs1;
951 G4double d2 = xs2;
952 value = (d1 + (d2 - d1) * (e - e1) / (e2 - e1));
953 }
954
955 return value;
956}
957
958//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
959
960G4double G4DNAPTBIonisationModel::QuadInterpolator(G4double e11, G4double e12, G4double e21,
961 G4double e22, G4double xs11, G4double xs12,
962 G4double xs21, G4double xs22, G4double t1,
963 G4double t2, G4double t, G4double e)
964{
965 G4double interpolatedvalue1, interpolatedvalue2, value;
966 (xs11 != xs12) ? interpolatedvalue1 = LogLogInterpolate(e11, e12, e, xs11, xs12)
967 : interpolatedvalue1 = xs11;
968
969 (xs21 != xs22) ? interpolatedvalue2 = LogLogInterpolate(e21, e22, e, xs21, xs22)
970 : interpolatedvalue2 = xs21;
971
972 (interpolatedvalue1 != interpolatedvalue2)
973 ? value = LogLogInterpolate(t1, t2, t, interpolatedvalue1, interpolatedvalue2)
974 : value = interpolatedvalue1;
975 return value;
976}
const char * G4FindDataDir(const char *)
@ FatalException
void G4Exception(const char *originOfException, const char *exceptionCode, G4ExceptionSeverity severity, const char *description)
std::ostringstream G4ExceptionDescription
double G4double
Definition G4Types.hh:83
bool G4bool
Definition G4Types.hh:86
int G4int
Definition G4Types.hh:85
G4GLOB_DLL std::ostream G4cerr
#define G4endl
Definition G4ios.hh:67
G4GLOB_DLL std::ostream G4cout
#define G4UniformRand()
Definition Randomize.hh:52
double z() const
Hep3Vector unit() const
double getZ() const
double x() const
double y() const
double getX() const
Hep3Vector & rotateUz(const Hep3Vector &)
double getY() const
static G4DNAMaterialManager * Instance()
void SetMasterDataModel(const DNAModelType &t, G4VEmModel *m)
G4VEmModel * GetModel(const DNAModelType &t)
const std::vector< G4double > * GetNumMolPerVolTableFor(const G4Material *) const
Retrieve a table of molecular densities (number of molecules per unit volume) in the G4 unit system f...
static G4DNAMolecularMaterial * Instance()
The G4DNAPTBIonisationModel class Implements the PTB ionisation model.
G4ParticleChangeForGamma * fParticleChangeForGamma
void Initialise(const G4ParticleDefinition *particle, const G4DataVector &data) override
Initialise Method called once at the beginning of the simulation. It is used to setup the list of the...
void SampleSecondaries(std::vector< G4DynamicParticle * > *, const G4MaterialCutsCouple *, const G4DynamicParticle *, G4double tmin, G4double tmax) override
SampleSecondaries If the model is selected for the ModelInterface then SampleSecondaries will be call...
G4DNAPTBIonisationModel(const G4String &applyToMaterial="all", const G4ParticleDefinition *p=nullptr, const G4String &nam="DNAPTBIonisationModel", const G4bool isAuger=true)
G4DNAPTBIonisationModel Constructor.
G4double CrossSectionPerVolume(const G4Material *material, const G4ParticleDefinition *p, G4double ekin, G4double emin, G4double emax) override
CrossSectionPerVolume Mandatory for every model the CrossSectionPerVolume method is in charge of retu...
G4double IonisationEnergy(G4int level, const size_t &materialName)
G4int NumberOfLevels(const size_t &materialName)
const G4ThreeVector & GetMomentumDirection() const
G4ParticleDefinition * GetDefinition() const
G4double GetKineticEnergy() const
static G4Electron * ElectronDefinition()
Definition G4Electron.cc:86
static G4Electron * Electron()
Definition G4Electron.cc:91
const G4Material * GetMaterial() const
std::size_t GetIndex() const
const G4String & GetName() const
static G4Material * GetMaterial(const G4String &name, G4bool warning=true)
void SetProposedKineticEnergy(G4double proposedKinEnergy)
void ProposeMomentumDirection(const G4ThreeVector &Pfinal)
const G4String & GetParticleName() const
static G4Proton * ProtonDefinition()
Definition G4Proton.cc:85
The G4VDNAModel class.
void LoadCrossSectionData(const G4ParticleDefinition *particleName)
LoadCrossSectionData Method to loop on all the registered materials in the model and load the corresp...
void SetLowELimit(const size_t &materialID, const G4ParticleDefinition *particle, G4double lim)
SetLowEnergyLimit.
void SetHighELimit(const size_t &materialID, const G4ParticleDefinition *particle, G4double lim)
SetHighEnergyLimit.
G4double GetHighELimit(const size_t &materialID, const G4ParticleDefinition *particle)
GetHighEnergyLimit.
G4int RandomSelectShell(const G4double &k, const G4ParticleDefinition *particle, const size_t &materialName)
RandomSelectShell Method to randomely select a shell from the data table uploaded....
void AddCrossSectionData(const size_t &materialName, const G4ParticleDefinition *particleName, const G4String &fileCS, const G4String &fileDiffCS, const G4double &scaleFactor)
AddCrossSectionData Method used during the initialization of the model class to add a new material....
G4double GetLowELimit(const size_t &materialID, const G4ParticleDefinition *particle)
GetLowEnergyLimit.
MaterialParticleMapData * GetData()
GetTableData.
G4bool isInitialised
G4ParticleChangeForGamma * GetParticleChangeForGamma()
G4bool IsLocked() const
void ProposeLocalEnergyDeposit(G4double anEnergyPart)
G4double bindingEnergy(G4int A, G4int Z)