EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_radionuclide_source.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ radionuclide source
5 # Copyright (C) 2015 National Research Council Canada
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Author: Reid Townson, 2016
25 #
26 # Contributors: Martin Martinov
27 # Ernesto Mainegra-Hing
28 #
29 ###############################################################################
30 */
31 
32 
39 #include "egs_input.h"
40 #include "egs_math.h"
41 #include "egs_application.h"
42 
44  EGS_ObjectFactory *f) : EGS_BaseSource(input,f),
45  baseSource(0), q_allowed(0), decays(0), activity(1), sCount(0) {
46 
47  int err;
48  vector<int> tmp_q;
49  err = input->getInput("charge", tmp_q);
50  if (!err) {
51  if (std::find(q_allowed.begin(), q_allowed.end(), -1) != q_allowed.end()
52  && std::find(q_allowed.begin(), q_allowed.end(), 0) != q_allowed.end()
53  && std::find(q_allowed.begin(), q_allowed.end(), 1) != q_allowed.end()
54  && std::find(q_allowed.begin(), q_allowed.end(), 2) != q_allowed.end()
55  ) {
56  q_allowAll = true;
57  }
58  else {
59  q_allowAll = false;
60  }
61  q_allowed = tmp_q;
62  }
63  else {
64  q_allowAll = true;
65  q_allowed.push_back(-1);
66  q_allowed.push_back(1);
67  q_allowed.push_back(0);
68  q_allowed.push_back(2);
69  }
70 
71  // Create the decay spectra
72  count = 0;
73  sCount = 0;
74  Emax = 0;
75  unsigned int i = 0;
76  EGS_Float spectrumWeightTotal = 0;
77  disintegrationOccurred = true;
78  ishower = -1;
79  time = 0;
80  lastDisintTime = 0;
81  while (input->getInputItem("spectrum")) {
82 
83  egsInformation("**********************************************\n");
84 
85  decays.push_back(createSpectrum(input));
86 
87  // If spectrum creation failed skip to the next spectrum block
88  // We check the getShowerIndex function to ensure this cast to a
89  // radionuclide spectrum succeeded. Other spectra will fail!
90  if (!decays[i] || decays[i]->getShowerIndex() != -1) {
91  decays.pop_back();
92  continue;
93  }
94 
95  EGS_Float spectrumMaxE = decays[i]->maxEnergy();
96  if (spectrumMaxE > Emax) {
97  Emax = spectrumMaxE;
98  }
99 
100  spectrumWeightTotal += decays[i]->getSpectrumWeight();
101 
102  ++i;
103  }
104  if (decays.size() < 1) {
105  egsFatal("\nEGS_RadionuclideSource: Error: No spectrum of type EGS_RadionuclideSpectrum was defined.\n");
106  }
107 
108  // Normalize the spectrum weights
109  for (i=0; i<decays.size(); ++i) {
110  decays[i]->setSpectrumWeight(
111  decays[i]->getSpectrumWeight() / spectrumWeightTotal);
112 
113  if (i > 0) {
114  decays[i]->setSpectrumWeight(
115  decays[i]->getSpectrumWeight() +
116  decays[i-1]->getSpectrumWeight());
117  }
118  }
119 
120  // Get the activity
121  EGS_Float tmp_A;
122  err = input->getInput("activity", tmp_A);
123  if (!err) {
124  activity = tmp_A;
125  }
126  else {
127  activity = 1;
128  }
129 
130  egsInformation("EGS_RadionuclideSource: Activity [disintegrations/s]: %e\n",
131  activity);
132 
133  // Get the experiment time
134  // This puts a limit on emission times - particles beyond the
135  // limit are discarded
136  err = input->getInput("experiment time", experimentTime);
137  if (err) {
138  experimentTime = 0.;
139  }
140 
141  if (experimentTime > 0.) {
142  egsInformation("EGS_RadionuclideSource: Experiment time [s]: %e\n",
143  experimentTime);
144  }
145  else {
146  egsInformation("EGS_RadionuclideSource: Experiment time will not limit the simulation.\n");
147  }
148 
149  // Get the active application
151 
152  // Check for deprecated inputs
153  string dummy;
154  err = input->getInput("source type",dummy);
155  int err2 = input->getInput("geometry",dummy);
156  if (!err || !err2) {
157  egsWarning("\nEGS_RadionuclideSource: Warning: Inputs for defining the radionuclide source as an isotropic or collimated source (e.g. 'source type') are deprecated. Please see the documentation - define the type of source separately and then refer to it using the new 'base source' input.\n");
158  }
159 
160  // Import base source
161  err = input->getInput("base source",sName);
162  if (err) {
163  egsFatal("\nEGS_RadionuclideSource: Error: Base source must be defined\n"
164  "using 'base source = some_name'\n");
165  }
166  baseSource = EGS_BaseSource::getSource(sName);
167  if (!baseSource) {
168  egsFatal("\nEGS_RadionuclideSource: Error: no source named %s"
169  " is defined\n",sName.c_str());
170  }
171 
172  // Initialize emission type to signify nothing has happened yet
173  emissionType = 99;
174 
175  // Finish
176  setUp();
177 }
178 
179 static char spec_msg1[] = "EGS_RadionuclideSource::createSpectrum:";
180 
181 EGS_RadionuclideSpectrum *EGS_RadionuclideSource::createSpectrum(EGS_Input *input) {
182 
183  // Read inputs for the spectrum
184  if (!input) {
185  egsWarning("%s got null input?\n",spec_msg1);
186  return 0;
187  }
188  EGS_Input *inp = input;
189  bool delete_it = false;
190  if (!input->isA("spectrum")) {
191  inp = input->takeInputItem("spectrum");
192  if (!inp) {
193  egsWarning("%s no 'spectrum' input!\n",spec_msg1);
194  return 0;
195  }
196  delete_it = true;
197  }
198 
199  egsInformation("EGS_RadionuclideSource::createSpectrum: Initializing radionuclide spectrum...\n");
200 
201  string nuclide;
202  int err = inp->getInput("nuclide",nuclide);
203  if (err) {
204  err = inp->getInput("isotope",nuclide);
205  if (err) {
206  err = inp->getInput("radionuclide",nuclide);
207  if (err) {
208  egsWarning("%s wrong/missing 'nuclide' input\n",spec_msg1);
209  return 0;
210  }
211  }
212  }
213 
214  EGS_Float relativeActivity;
215  err = inp->getInput("relative activity",relativeActivity);
216  if (err) {
217  relativeActivity = 1;
218  }
219 
220  // Determine whether to sample X-Rays and Auger electrons
221  // using the ensdf data (options: eadl, ensdf or none)
222  string tmp_relaxType, relaxType;
223  err = inp->getInput("atomic relaxations", tmp_relaxType);
224  if (!err) {
225  relaxType = tmp_relaxType;
226  }
227  else {
228  relaxType = "eadl";
229  }
230  if (inp->compare(relaxType,"ensdf")) {
231  relaxType = "ensdf";
232  egsInformation("EGS_RadionuclideSource::createSpectrum: Fluorescence and auger from the ensdf file will be used.\n");
233  }
234  else if (inp->compare(relaxType,"eadl")) {
235  relaxType = "eadl";
236  egsInformation("EGS_RadionuclideSource::createSpectrum: Fluorescence and auger from the ensdf file will be ignored. EADL relaxations will be used.\n");
237  }
238  else if (inp->compare(relaxType,"none") || inp->compare(relaxType,"off") || inp->compare(relaxType,"no")) {
239  relaxType = "off";
240  egsInformation("EGS_RadionuclideSource::createSpectrum: Fluorescence and auger from the ensdf file will be ignored. No relaxations following radionuclide disintegrations will be modelled.\n");
241  }
242  else {
243  egsFatal("EGS_RadionuclideSource::createSpectrum: Error: Invalid selection for 'atomic relaxations'. Use 'eadl' (default), 'ensdf' or 'off'.\n");
244  }
245 
246  // Determine whether to output beta energy spectra to files
247  // (options: yes or no)
248  string tmp_outputBetaSpectra, outputBetaSpectra;
249  err = inp->getInput("output beta spectra", tmp_outputBetaSpectra);
250  if (!err) {
251  outputBetaSpectra = tmp_outputBetaSpectra;
252 
253  if (inp->compare(outputBetaSpectra,"yes")) {
254  egsInformation("EGS_RadionuclideSource::createSpectrum: Beta energy spectra will be output to files.\n");
255  }
256  else if (inp->compare(outputBetaSpectra,"no")) {
257  egsInformation("EGS_RadionuclideSource::createSpectrum: Beta energy spectra will not be output to files.\n");
258  }
259  else {
260  egsFatal("EGS_RadionuclideSource::createSpectrum: Error: Invalid selection for 'output beta spectra'. Use 'no' (default) or 'yes'.\n");
261  }
262  }
263  else {
264  outputBetaSpectra = "no";
265  }
266 
267  // Determine whether to score alpha energy locally or discard it
268  // By default, the energy is discarded
269  string tmp_alphaScoring;
270  bool scoreAlphasLocally = false;
271  err = inp->getInput("alpha scoring", tmp_alphaScoring);
272  if (!err) {
273  if (inp->compare(tmp_alphaScoring,"local")) {
274  scoreAlphasLocally = true;
275  egsInformation("EGS_RadionuclideSource::createSpectrum: Alpha particles will deposit energy locally, in the same region as creation.\n");
276  }
277  else if (inp->compare(tmp_alphaScoring,"discard")) {
278  scoreAlphasLocally = false;
279  egsInformation("EGS_RadionuclideSource::createSpectrum: Alpha particles will be discarded (no transport or energy deposition).\n");
280  }
281  else {
282  egsFatal("EGS_RadionuclideSource::createSpectrum: Error: Invalid selection for 'alpha scoring'. Use 'discard' (default) or 'local'.\n");
283  }
284  }
285 
286  // Determine whether to score alpha energy locally or discard it
287  // By default, the energy is discarded
288  string tmp_allowMultiTransition;
289  bool allowMultiTransition = false;
290  err = inp->getInput("extra transition approximation", tmp_allowMultiTransition);
291  if (!err) {
292  if (inp->compare(tmp_allowMultiTransition,"on")) {
293  allowMultiTransition = true;
294  egsInformation("EGS_RadionuclideSource::createSpectrum: Extra transition approximation is on. If the intensity away from a level in a radionuclide daughter is larger than the intensity feeding the level (e.g. decays to that level), then additional transitions away from that level will be sampled. They will not be correlated with decays, but the spectrum will produce emission rates to match both the decay intensities and the internal transition intensities from the ensdf file.\n");
295  }
296  else if (inp->compare(tmp_allowMultiTransition,"off")) {
297  allowMultiTransition = false;
298  egsInformation("EGS_RadionuclideSource::createSpectrum: Extra transition approximation is off.\n");
299  }
300  else {
301  egsFatal("EGS_RadionuclideSource::createSpectrum: Error: Invalid selection for 'extra transition approximation'. Use 'off' (default) or 'on'.\n");
302  }
303  }
304 
305  // For ensdf input, first check for the input argument
306  string ensdf_file;
307  err = inp->getInput("ensdf file",ensdf_file);
308 
309  // If not passed as input, find the ensdf file in the
310  // directory $HEN_HOUSE/spectra/lnhb/ensdf/
311  if (err) {
312 
314  if (app) {
315  ensdf_file = egsJoinPath(app->getHenHouse(),"spectra");
316  ensdf_file = egsJoinPath(ensdf_file.c_str(),"lnhb");
317  ensdf_file = egsJoinPath(ensdf_file.c_str(),"ensdf");
318  }
319  else {
320  char *hen_house = getenv("HEN_HOUSE");
321  if (!hen_house) {
322 
323  egsWarning("EGS_RadionuclideSource::createSpectrum: "
324  "No active application and HEN_HOUSE not defined.\n"
325  "Assuming local directory for spectra\n");
326  ensdf_file = "./";
327  }
328  else {
329  ensdf_file = egsJoinPath(hen_house,"spectra");
330  ensdf_file = egsJoinPath(ensdf_file.c_str(),"lnhb");
331  ensdf_file = egsJoinPath(ensdf_file.c_str(),"ensdf");
332  }
333  }
334  ensdf_file = egsJoinPath(ensdf_file.c_str(),nuclide.append(".txt"));
335  }
336 
337  // Check that the ensdf file exists
338  ifstream ensdf_fh;
339  ensdf_fh.open(ensdf_file.c_str(),ios::in);
340  if (!ensdf_fh.is_open()) {
341  egsWarning("EGS_RadionuclideSource::createSpectrum: failed to open ensdf file %s"
342  " for reading\n",ensdf_file.c_str());
343  return 0;
344  }
345  ensdf_fh.close();
346 
347  // Create the spectrum
348  EGS_RadionuclideSpectrum *spec = new EGS_RadionuclideSpectrum(nuclide, ensdf_file, relativeActivity, relaxType, outputBetaSpectra, scoreAlphasLocally, allowMultiTransition);
349 
350  return spec;
351 }
352 
354  &q, int &latch, EGS_Float &E, EGS_Float &wt, EGS_Vector &x, EGS_Vector
355  &u) {
356 
357  unsigned int i = 0;
358  if (decays.size() > 1 && disintegrationOccurred) {
359  // Sample a uniform random number
360  EGS_Float uRand = rndm->getUniform();
361 
362  // Sample which spectrum to use
363  for (i=0; i<decays.size(); ++i) {
364  if (uRand < decays[i]->getSpectrumWeight()) {
365  break;
366  }
367  }
368  }
369 
370  // Check if the emission time is within the experiment time limit
371  if (experimentTime <= 0. || time < experimentTime) {
372  // Keep this particle
373  }
374  else {
375  // If the particle was emitted outside the
376  // experiment time window, just set the energy to zero to discard
377  E = 0;
378  return ishower+1;
379  }
380 
381  EGS_I64 ishowerOld = decays[i]->getShowerIndex();
382 
383  E = decays[i]->sample(rndm);
384 
385  EGS_I64 ishowerNew = decays[i]->getShowerIndex();
386  if (ishowerNew > ishowerOld) {
387  disintegrationOccurred = true;
388  time = lastDisintTime + -log(1.-rndm->getUniform()) / activity * (ishowerNew - ishowerOld);
389 
390  lastDisintTime = time;
391  ishower += (ishowerNew - ishowerOld);
392  }
393  else {
394  disintegrationOccurred = false;
395  // The time returned from the spectrum is just the time
396  // since the last disintegration event, so this is only
397  // non-zero for internal transitions. This is why the
398  // times are added here - each transition occurs only after
399  // the delay of the previous.
400  time += decays[i]->getTime();
401  }
402 
403  q = decays[i]->getCharge();
404  int qTemp(q), latchTemp(latch);
405  EGS_Float ETemp(E);
406  baseSource->getNextParticle(rndm, qTemp, latchTemp, ETemp, wt, x, u);
407  sCount++;
408  latch = 0;
409 
410  if (disintegrationOccurred) {
411  xOfDisintegration = x;
412  }
413  else {
414  x = xOfDisintegration;
415  }
416 
417  // Now that we have the position of the particle, we can
418  // find the region and deposit any sub-threshold contributions locally
419  // This includes edep from relaxations, and alpha particle energy
420  EGS_Float edep = decays[i]->getEdep();
421  if (edep > 0) {
422  app->setEdep(edep);
423  int ireg = app->isWhere(x);
424  app->userScoring(3, ireg);
425  }
426 
427  // Check if the charge is allowed
428  if (q_allowAll || std::find(q_allowed.begin(), q_allowed.end(), q) != q_allowed.end()) {
429  // Keep the particle
430  }
431  else {
432  // Don't transport the particle
433  E = 0;
434  }
435 
436  // If the energy is zero, also set the weight to zero
437  // If you don't do this, electrons will still be given their rest
438  // mass energy in shower()
439  if (E < epsilon) {
440  wt = 0;
441  }
442 
443  return ishower+1;
444 }
445 
446 void EGS_RadionuclideSource::setUp() {
447  otype = "EGS_RadionuclideSource";
448  if (!isValid()) {
449  description = "Invalid radionuclide source";
450  }
451  else {
452  description = "Radionuclide production in base source ";
453  description += sName;
454 
455  description += " with:";
456  if (std::find(q_allowed.begin(), q_allowed.end(), -1) !=
457  q_allowed.end()) {
458  description += " electrons";
459  }
460  if (std::find(q_allowed.begin(), q_allowed.end(), 0) != q_allowed.end()) {
461  description += " photons";
462  }
463  if (std::find(q_allowed.begin(), q_allowed.end(), 1) != q_allowed.end()) {
464  description += " positrons";
465  }
466  if (std::find(q_allowed.begin(), q_allowed.end(), 1) != q_allowed.end()) {
467  description += " alphas";
468  }
469 
470  description += "\nBase source description:\n";
471  description += baseSource->getSourceDescription();
472  }
473 }
474 
475 bool EGS_RadionuclideSource::storeState(ostream &data_out) const {
476  for (unsigned int i=0; i<decays.size(); ++i) {
477  if (!decays[i]->storeState(data_out)) {
478  return false;
479  }
480  }
481  egsStoreI64(data_out,count);
482  egsStoreI64(data_out,sCount);
483  baseSource->storeState(data_out);
484 
485  return true;
486 }
487 
488 bool EGS_RadionuclideSource::addState(istream &data) {
489  for (unsigned int i=0; i<decays.size(); ++i) {
490  if (!decays[i]->setState(data)) {
491  return false;
492  }
493  ishower += decays[i]->getShowerIndex();
494  }
495  EGS_I64 tmp_val;
496  egsGetI64(data,tmp_val);
497  count += tmp_val;
498  egsGetI64(data,tmp_val);
499  sCount += tmp_val;
500  baseSource->addState(data);
501 
502  return true;
503 }
504 
506  for (unsigned int i=0; i<decays.size(); ++i) {
507  decays[i]->resetCounter();
508  }
509  ishower = 0;
510  count = 0;
511  sCount = 0;
512  baseSource->resetCounter();
513 }
514 
515 bool EGS_RadionuclideSource::setState(istream &data) {
516  for (unsigned int i=0; i<decays.size(); ++i) {
517  if (!decays[i]->setState(data)) {
518  return false;
519  }
520  }
521  egsGetI64(data,count);
522  egsGetI64(data,sCount);
523  baseSource->setState(data);
524 
525  return true;
526 }
527 
528 extern "C" {
529 
530  EGS_RADIONUCLIDE_SOURCE_EXPORT EGS_BaseSource *createSource(EGS_Input
531  *input, EGS_ObjectFactory *f) {
532  return
533  createSourceTemplate<EGS_RadionuclideSource>(input,f,"radionuclide "
534  "source");
535  }
536 
537 }
538 
539 EGS_RadionuclideSpectrum::EGS_RadionuclideSpectrum(const string nuclide, const string ensdf_file,
540  const EGS_Float relativeActivity, const string relaxType, const string outputBetaSpectra, const bool scoreAlphasLocally, const bool allowMultiTransition) {
541 
542  // For now, hard-code verbose mode
543  // 0 - minimal output
544  // 1 - some output of ensdf data and normalized intensities
545  // 2 - verbose output
546  int verbose = 0;
547 
548  // Read in the data file for the nuclide
549  // and build the decay structure
550  decays = new EGS_Ensdf(nuclide, ensdf_file, relaxType, allowMultiTransition, verbose);
551 
552  // Normalize the emission and transition intensities
553  decays->normalizeIntensities();
554 
555  // Get the beta energy spectra
556  betaSpectra = new EGS_RadionuclideBetaSpectrum(decays, outputBetaSpectra);
557 
558  // Get the particle records from the decay scheme
559  myBetas = decays->getBetaRecords();
560  myAlphas = decays->getAlphaRecords();
561  myGammas = decays->getGammaRecords();
562  myMetastableGammas = decays->getMetastableGammaRecords();
563  myUncorrelatedGammas = decays->getUncorrelatedGammaRecords();
564  myLevels = decays->getLevelRecords();
565  xrayIntensities = decays->getXRayIntensities();
566  xrayEnergies = decays->getXRayEnergies();
567  augerIntensities = decays->getAugerIntensities();
568  augerEnergies = decays->getAugerEnergies();
569 
570  // Initialization
571  currentLevel = 0;
572  Emax = 0;
573  currentTime = 0;
574  ishower = -1; // Start with ishower -1 so first shower has index 0
575  totalGammaEnergy = 0;
576  relaxationType = relaxType;
577  scoreAlphasLocal = scoreAlphasLocally;
578 
579  // Get the maximum energy for emissions
580  for (vector<BetaRecordLeaf *>::iterator beta = myBetas.begin();
581  beta != myBetas.end(); beta++) {
582 
583  double energy = (*beta)->getFinalEnergy();
584  if (Emax < energy) {
585  Emax = energy;
586  }
587  }
588  for (vector<AlphaRecord *>::iterator alpha = myAlphas.begin();
589  alpha != myAlphas.end(); alpha++) {
590 
591  double energy = (*alpha)->getFinalEnergy();
592  if (Emax < energy) {
593  Emax = energy;
594  }
595  }
596  for (vector<GammaRecord *>::iterator gamma = myGammas.begin();
597  gamma != myGammas.end(); gamma++) {
598 
599  double energy = (*gamma)->getDecayEnergy();
600  if (Emax < energy) {
601  Emax = energy;
602  }
603  }
604  for (vector<GammaRecord *>::iterator gamma = myUncorrelatedGammas.begin();
605  gamma != myUncorrelatedGammas.end(); gamma++) {
606 
607  double energy = (*gamma)->getDecayEnergy();
608  if (Emax < energy) {
609  Emax = energy;
610  }
611  }
612  for (unsigned int i=0; i < xrayEnergies.size(); ++i) {
613  numSampledXRay.push_back(0);
614  if (Emax < xrayEnergies[i]) {
615  Emax = xrayEnergies[i];
616  }
617  }
618  for (unsigned int i=0; i < augerEnergies.size(); ++i) {
619  numSampledAuger.push_back(0);
620  if (Emax < augerEnergies[i]) {
621  Emax = augerEnergies[i];
622  }
623  }
624 
625  // Set the weight of the spectrum
626  spectrumWeight = relativeActivity;
627 
628  if (verbose) {
629  egsInformation("EGS_RadionuclideSpectrum: Emax: %f\n",Emax);
630  egsInformation("EGS_RadionuclideSpectrum: Relative activity: %f\n",relativeActivity);
631  }
632 
633  // Set the application
635 };
636 
637 
639 
640  egsInformation("\nSampled %s emissions:\n", decays->radionuclide.c_str());
641  egsInformation("========================\n");
642 
643  if (ishower < 1) {
644  egsWarning("EGS_RadionuclideSpectrum::printSampledEmissions: Warning: The number of disintegrations (tracked by `ishower`) is less than 1.\n");
645  return;
646  }
647 
648  egsInformation("Energy | Intensity per 100 decays (adjusted by %f)\n", decays->decayDiscrepancy);
649  if (myBetas.size() > 0) {
650  egsInformation("Beta records:\n");
651  }
652  for (vector<BetaRecordLeaf *>::iterator beta = myBetas.begin();
653  beta != myBetas.end(); beta++) {
654 
655  egsInformation("%f %f\n", (*beta)->getFinalEnergy(),
656  ((EGS_Float)(*beta)->getNumSampled()/(ishower+1))*100);
657  }
658  if (myAlphas.size() > 0) {
659  egsInformation("Alpha records:\n");
660  }
661  for (vector<AlphaRecord *>::iterator alpha = myAlphas.begin();
662  alpha != myAlphas.end(); alpha++) {
663 
664  egsInformation("%f %f\n", (*alpha)->getFinalEnergy(),
665  ((EGS_Float)(*alpha)->getNumSampled()/(ishower+1))*100);
666  }
667  if (myGammas.size() > 0) {
668  egsInformation("Gamma records (E,Igamma,Ice,Ipp):\n");
669  }
670  EGS_I64 totalNumSampled = 0;
671  for (vector<GammaRecord *>::iterator gamma = myGammas.begin();
672  gamma != myGammas.end(); gamma++) {
673 
674  totalNumSampled += (*gamma)->getGammaSampled();
675  egsInformation("%f %f %.4e %.4e\n", (*gamma)->getDecayEnergy(),
676  ((EGS_Float)(*gamma)->getGammaSampled()/(ishower+1))*100,
677  ((EGS_Float)(*gamma)->getICSampled()/(ishower+1))*100,
678  ((EGS_Float)(*gamma)->getIPSampled()/(ishower+1))*100
679  );
680  }
681  if (myGammas.size() > 0) {
682  if (totalNumSampled > 0) {
683  egsInformation("Average gamma energy: %f\n",
684  totalGammaEnergy / totalNumSampled);
685  }
686  else {
687  egsInformation("Zero gamma transitions occurred.\n");
688  }
689  }
690  if (myUncorrelatedGammas.size() > 0) {
691  egsInformation("Uncorrelated gamma records (E,Igamma,Ice,Ipp):\n");
692  }
693  for (vector<GammaRecord *>::iterator gamma = myUncorrelatedGammas.begin();
694  gamma != myUncorrelatedGammas.end(); gamma++) {
695 
696  egsInformation("%f %f %.4e %.4e\n", (*gamma)->getDecayEnergy(),
697  ((EGS_Float)(*gamma)->getGammaSampled()/(ishower+1))*100,
698  ((EGS_Float)(*gamma)->getICSampled()/(ishower+1))*100,
699  ((EGS_Float)(*gamma)->getIPSampled()/(ishower+1))*100
700  );
701  }
702  if (xrayEnergies.size() > 0) {
703  egsInformation("X-Ray records:\n");
704  }
705  for (unsigned int i=0; i < xrayEnergies.size(); ++i) {
706  egsInformation("%f %f\n", xrayEnergies[i],
707  ((EGS_Float)numSampledXRay[i]/(ishower+1))*100);
708  }
709  if (augerEnergies.size() > 0) {
710  egsInformation("Auger records:\n");
711  }
712  for (unsigned int i=0; i < augerEnergies.size(); ++i) {
713  egsInformation("%f %f\n", augerEnergies[i],
714  ((EGS_Float)numSampledAuger[i]/(ishower+1))*100);
715  }
716  egsInformation("\n");
717 }
718 
719 
721 
722  // The energy of the sampled particle
723  EGS_Float E;
724  // Local energy depositions
725  edep = 0;
726  // Time delay of this particle
727  currentTime = 0;
728  // The type of emission particle
729  emissionType = 0;
730 
731  // Check for relaxation particles due to shell vacancies in the daughter
732  // These are created from internal transitions or electron capture
733  if (relaxParticles.size() > 0) {
734 
735  // Get the energy and charge of the last particle on the list
736  EGS_RelaxationParticle p = relaxParticles.pop();
737  E = p.E;
738  currentQ = p.q;
739 
740  emissionType = 1;
741 
742  return E;
743  }
744 
745  // Sample a uniform random number
746  EGS_Float u = rndm->getUniform();
747 
748  // If the daughter is in an excited state
749  // check for transitions
750  if (currentLevel && currentLevel->levelCanDecay() && currentLevel->getEnergy() > epsilon) {
751 
752  for (vector<GammaRecord *>::iterator gamma = myGammas.begin();
753  gamma != myGammas.end(); gamma++) {
754 
755  if ((*gamma)->getLevelRecord() == currentLevel) {
756 
757  if (u < (*gamma)->getTransitionIntensity()) {
758 
759  // A gamma transition may either be a gamma emission
760  // or an internal conversion electron
761  EGS_Float u2 = 0;
762  if ((*gamma)->getGammaIntensity() < 1) {
763  u2 = rndm->getUniform();
764  }
765 
766  // Sample how long
767  // it took for this transition to occur
768  // time = -halflife / ln(2) * log(1-u)
769  double hl = currentLevel->getHalfLife();
770  if (hl > 0) {
771  currentTime = -hl * log(1.-rndm->getUniform()) /
772  0.693147180559945309417232121458176568075500134360255254120680009493393;
773  }
774 
775  // Determine whether multiple gamma transitions occur
776  if (rndm->getUniform() < (*gamma)->getMultiTransitionProb()) {
777  multiTransitions.push_back(currentLevel);
778  }
779 
780  // Update the level of the daughter
781  currentLevel = (*gamma)->getFinalLevel();
782 
783  // If a gamma emission occurs
784  if (u2 < (*gamma)->getGammaIntensity()) {
785 
786  (*gamma)->incrGammaSampled();
787 
788  currentQ = (*gamma)->getCharge();
789 
790  E = (*gamma)->getDecayEnergy();
791 
792  totalGammaEnergy += E;
793 
794  emissionType = 2;
795 
796  return E;
797 
798  }
799  else if (u2 < (*gamma)->getICIntensity()) {
800  (*gamma)->incrICSampled();
801  currentQ = -1;
802  emissionType = 3;
803 
804  if ((*gamma)->icIntensity.size()) {
805 
806  // Determine which shell the conversion electron
807  // comes from. This will create a shell vacancy
808  EGS_Float u3 = rndm->getUniform();
809 
810  for (unsigned int i=0; i<(*gamma)->icIntensity.size(); ++i) {
811  if (u3 < (*gamma)->icIntensity[i]) {
812 
813  E = (*gamma)->getDecayEnergy() - (*gamma)->getBindingEnergy(i);
814 
815  // egsInformation("test %d %f %f %f\n",i,(*gamma)->getDecayEnergy(),decays->getRelaxations()->getBindingEnergy(decays->Z,i),E);
816 
817  // Add relaxation particles to the source stack
818  if (relaxationType == "eadl") {
819 
820  // Generate relaxation particles for a
821  // shell vacancy i
822  (*gamma)->relax(i,app->getEcut()-app->getRM(),app->getPcut(),rndm,edep,relaxParticles);
823  }
824 
825  // Return the conversion electron
826  return E;
827  }
828  }
829  }
830  return 0;
831  }
832  else {
833  (*gamma)->incrIPSampled();
834  emissionType = 13;
835 
836  // Internal pair production results in a positron
837  // and electron pair
838 
839  //TODO: This is left for future work, we need to
840  // determine the energies of the electron/positron
841  // pair (sample uniformly?) and then determine the
842  // corresponding directions. It might be best to do
843  // this in the source instead of the spectrum.
844 
845  currentQ = 1;
846  return 0;
847  }
848  }
849  }
850  }
851 
852  currentLevel = 0;
853  return 0;
854  }
855 
856  // If we have determined that multiple transitions will occur from some
857  // levels, here we set the current level to an excited state, and return.
858  // The radionuclide source will then sample again using the excited level.
859  if (multiTransitions.size() > 0) {
860  currentLevel = multiTransitions.back();
861  multiTransitions.pop_back();
862  return 0;
863  }
864 
865  // ============================
866  // Sample which decay occurs
867  // ============================
868  currentTime = 0;
869 
870  // Beta-, beta+ and electron capture
871  for (vector<BetaRecordLeaf *>::iterator beta = myBetas.begin();
872  beta != myBetas.end(); beta++) {
873  if (u < (*beta)->getBetaIntensity()) {
874 
875  // Increment the shower number
876  ishower++;
877 
878  // Increment the counter of betas and get the charge
879  (*beta)->incrNumSampled();
880  currentQ = (*beta)->getCharge();
881 
882  // Set the energy level of the daughter
883  currentLevel = (*beta)->getLevelRecord();
884 
885  // For beta+ records we decide between
886  // branches for beta+ or electron capture
887  if (currentQ == 1) {
888  // For positron emission, continue as usual
889  if ((*beta)->getPositronIntensity() > epsilon && rndm->getUniform() < (*beta)->getPositronIntensity()) {
890 
891  }
892  else {
893 
894  if (relaxationType == "eadl" && (*beta)->ecShellIntensity.size()) {
895  // Determine which shell the electron capture
896  // occurs in. This will create a shell vacancy
897  EGS_Float u3 = rndm->getUniform();
898 
899  for (unsigned int i=0; i<(*beta)->ecShellIntensity.size(); ++i) {
900  if (u3 < (*beta)->ecShellIntensity[i]) {
901 
902  // Generate relaxation particles for a
903  // shell vacancy i
904  (*beta)->relax(i,app->getEcut()-app->getRM(),app->getPcut(),rndm,edep,relaxParticles);
905 
906  emissionType = 4;
907 
908  return 0;
909  }
910  }
911  }
912 
913  // For electron capture, there is no emitted particle
914  // (only a neutrino)
915  // so we return a 0 energy particle
916  emissionType = 4;
917  return 0;
918  }
919  emissionType = 5;
920  }
921  else {
922  emissionType = 6;
923  }
924 
925  // Sample the energy from the spectrum alias table
926  E = (*beta)->getSpectrum()->sample(rndm);
927 
928  return E;
929  }
930  }
931 
932  // Alphas
933  for (vector<AlphaRecord *>::iterator alpha = myAlphas.begin();
934  alpha != myAlphas.end(); alpha++) {
935  if (u < (*alpha)->getAlphaIntensity()) {
936 
937  // Increment the shower number
938  ishower++;
939 
940  // Increment the counter of alphas and get the charge
941  (*alpha)->incrNumSampled();
942  currentQ = (*alpha)->getCharge();
943 
944  // Set the energy level of the daughter
945  currentLevel = (*alpha)->getLevelRecord();
946 
947  // Score alpha energy depositions locally,
948  // because alpha transport is not modeled in EGSnrc.
949  // This is an approximation!
950  if (scoreAlphasLocal) {
951  edep += (*alpha)->getFinalEnergy();
952  }
953 
954  emissionType = 7;
955 
956  // For alphas we simulate a disintegration but the
957  // transport will not be performed so return 0
958  return 0;
959  }
960  }
961 
962  // Metastable "decays" that will result in internal transitions
963  for (vector<GammaRecord *>::iterator gamma = myMetastableGammas.begin();
964  gamma != myMetastableGammas.end(); gamma++) {
965  if (u < (*gamma)->getTransitionIntensity()) {
966 
967  // Increment the shower number
968  ishower++;
969 
970  // Set the energy level of the daughter as though a
971  // disintegration just occurred
972  currentLevel = (*gamma)->getLevelRecord();
973 
974  emissionType = 8;
975 
976  // No particle returned
977  return 0;
978  }
979  }
980 
981  // Uncorrelated internal transitions
982  for (vector<GammaRecord *>::iterator gamma = myUncorrelatedGammas.begin();
983  gamma != myUncorrelatedGammas.end(); gamma++) {
984  if (u < (*gamma)->getTransitionIntensity()) {
985 
986  // A gamma transition may either be a gamma emission
987  // or an internal conversion electron
988  EGS_Float u2 = 0;
989  if ((*gamma)->getGammaIntensity() < 1) {
990  u2 = rndm->getUniform();
991  }
992 
993  // If a gamma emission occurs
994  if (u2 < (*gamma)->getGammaIntensity()) {
995 
996  (*gamma)->incrGammaSampled();
997 
998  currentQ = (*gamma)->getCharge();
999 
1000  E = (*gamma)->getDecayEnergy();
1001 
1002  totalGammaEnergy += E;
1003 
1004  emissionType = 11;
1005 
1006  return E;
1007 
1008  }
1009  else if (u2 < (*gamma)->getICIntensity()) {
1010  (*gamma)->incrICSampled();
1011  currentQ = -1;
1012  emissionType = 12;
1013 
1014  if ((*gamma)->icIntensity.size()) {
1015 
1016  // Determine which shell the conversion electron
1017  // comes from. This will create a shell vacancy
1018  EGS_Float u3 = rndm->getUniform();
1019 
1020  for (unsigned int i=0; i<(*gamma)->icIntensity.size(); ++i) {
1021  if (u3 < (*gamma)->icIntensity[i]) {
1022 
1023  E = (*gamma)->getDecayEnergy() - (*gamma)->getBindingEnergy(i);
1024 
1025  // Add relaxation particles to the source stack
1026  if (relaxationType == "eadl") {
1027 
1028  // Generate relaxation particles for a
1029  // shell vacancy i
1030  (*gamma)->relax(i,app->getEcut()-app->getRM(),app->getPcut(),rndm,edep,relaxParticles);
1031  }
1032 
1033  // Return the conversion electron
1034  return E;
1035  }
1036  }
1037  }
1038  return 0;
1039  }
1040  else {
1041  (*gamma)->incrIPSampled();
1042  emissionType = 14;
1043 
1044  // Internal pair production results in a positron
1045  // and electron pair
1046 
1047  //TODO: This is left for future work, we need to
1048  // determine the energies of the electron/positron
1049  // pair (sample uniformly?) and then determine the
1050  // corresponding directions. It might be best to do
1051  // this in the source instead of the spectrum.
1052 
1053  currentQ = 1;
1054  return 0;
1055  }
1056  }
1057  }
1058 
1059  // XRays from the ensdf
1060  for (unsigned int i=0; i < xrayIntensities.size(); ++i) {
1061  if (u < xrayIntensities[i]) {
1062 
1063  numSampledXRay[i]++;
1064  currentQ = 0;
1065 
1066  E = xrayEnergies[i];
1067 
1068  emissionType = 9;
1069 
1070  return E;
1071  }
1072  }
1073 
1074  // Auger electrons from the ensdf
1075  for (unsigned int i=0; i < augerIntensities.size(); ++i) {
1076  if (u < augerIntensities[i]) {
1077 
1078  numSampledAuger[i]++;
1079  currentQ = -1;
1080 
1081  E = augerEnergies[i];
1082 
1083  emissionType = 10;
1084 
1085  return E;
1086  }
1087  }
1088 
1089  // If we get here, fission occurs
1090  // Count it as a disintegration and return 0
1091  ishower++;
1092  return 0;
1093 }
Base class for advanced EGSnrc C++ applications.
static EGS_Application * activeApplication()
Get the active application.
int userScoring(int iarg, int ir=-1)
User scoring function for accumulation of results and VRT implementation.
const string & getHenHouse() const
Returns the HEN_HOUSE directory.
Base source class. All particle sources must be derived from this class.
virtual bool addState(istream &data_in)
Add data from the stream data_in to the source state.
const char * getSourceDescription() const
Get a short description of this source.
static EGS_BaseSource * getSource(const string &Name)
Get a pointer to the source named Name.
virtual void resetCounter()
Reset the source state.
string description
A short source description.
virtual EGS_I64 getNextParticle(EGS_RandomGenerator *rndm, int &q, int &latch, EGS_Float &E, EGS_Float &wt, EGS_Vector &x, EGS_Vector &u)=0
Sample the next source particle from the source probability distribution.
virtual bool setState(istream &data_in)
Set the source state based on data from the stream data_in.
virtual bool storeState(ostream &data_out) const
Store the source state into the stream data_out.
The ensdf class for reading ensdf format data files.
Definition: egs_ensdf.h:492
A class for storing information in a tree-like structure of key-value pairs. This class is used throu...
Definition: egs_input.h:182
EGS_Input * takeInputItem(const string &key, bool self=true)
Get the property named key.
Definition: egs_input.cpp:226
static bool compare(const string &s1, const string &s2)
Definition: egs_input.cpp:1170
bool isA(const string &key) const
Definition: egs_input.cpp:278
EGS_Input * getInputItem(const string &key) const
Same as the previous function but now ownership remains with the EGS_Input object.
Definition: egs_input.cpp:245
int getInput(const string &key, vector< string > &values) const
Assign values to an array of strings from an input identified by key.
Definition: egs_input.cpp:338
An object factory.
string otype
The object type.
Beta spectrum generation for EGS_RadionuclideSpectrum.
EGS_RadionuclideSource(EGS_Input *, EGS_ObjectFactory *f=0)
Constructor from input file.
bool setState(istream &data)
Set the source state according to the data in the stream data.
bool isValid() const
Checks the validity of the source.
bool storeState(ostream &data_out) const
Store the source state to the data stream data_out.
void resetCounter()
Reset the source to a state with zero sampled particles.
EGS_I64 getNextParticle(EGS_RandomGenerator *rndm, int &q, int &latch, EGS_Float &E, EGS_Float &wt, EGS_Vector &x, EGS_Vector &u)
Gets the next particle from the radionuclide spectra.
bool addState(istream &data)
Add the source state from the stream data to the current state.
EGS_I64 getShowerIndex() const
Returns the shower index of the most recent particle.
A radionuclide spectrum.
void printSampledEmissions()
Print the sampled emission intensities.
EGS_RadionuclideSpectrum(const string nuclide, const string ensdf_file, const EGS_Float relativeActivity, const string relaxType, const string outputBetaSpectra, const bool scoreAlphasLocally, const bool allowMultiTransition)
Construct a radionuclide spectrum.
EGS_Float sample(EGS_RandomGenerator *rndm)
Sample an event from the spectrum, returns the energy of the emitted particle.
Base random number generator class. All random number generators should be derived from this class.
Definition: egs_rndm.h:67
EGS_Float getUniform()
Returns a random number uniformly distributed between zero (inclusive) and 1 (exclusive).
Definition: egs_rndm.h:103
A class representing 3D vectors.
Definition: egs_vector.h:57
EGS_Application class header file.
EGS_Input class header file.
Attempts to fix broken math header files.
A radionuclide source.
bool EGS_EXPORT egsStoreI64(ostream &data, EGS_I64 n)
Writes the 64 bit integer n to the output stream data and returns true on success,...
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
bool EGS_EXPORT egsGetI64(istream &data, EGS_I64 &n)
Reads a 64 bit integer from the stream data and assigns it to n. Returns true on success,...
const EGS_Float epsilon
The epsilon constant for floating point comparisons.
Definition: egs_functions.h:62
string egsJoinPath(const string &first, const string &second)
Join two path variables (or a path and a file name) using the platform specific directory separator a...
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.
int q
charge (0-photon, -1=electron)
EGS_Float E
energy in MeV