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