EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_spectra.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ spectra
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: Iwan Kawrakow, 2005
25 #
26 # Contributors: Frederic Tessier
27 # Reid Townson
28 #
29 ###############################################################################
30 */
31 
32 
38 #include "egs_base_source.h"
39 #include "egs_rndm.h"
40 #include "egs_alias_table.h"
41 #include "egs_input.h"
42 #include "egs_math.h"
43 #include "egs_ensdf.h"
44 #include "egs_application.h"
46 
47 #include <cstdio>
48 #include "egs_math.h"
49 #include <fstream>
50 #ifndef NO_SSTREAM
51  #include <sstream>
52  #define S_STREAM std::istringstream
53 #else
54  #include <strstream>
55  #define S_STREAM std::istrstream
56 #endif
57 
58 #include <complex>
59 #include <limits>
60 
61 
62 using namespace std;
63 
65  egsInformation("expected average energy: %g\n",expectedAverage());
66  EGS_Float e=0,de=0;
67  getSampledAverage(e,de);
68  egsInformation("sampled average energy: %g +/- %g\n",e,de);
69 }
70 
84 
85 public:
86 
88  EGS_MonoEnergy(EGS_Float energy) : EGS_BaseSpectrum(), E(energy) {
89  char buf[1024];
90  sprintf(buf,"monoenergetic %g MeV",E);
91  type = buf;
92  };
93  ~EGS_MonoEnergy() {};
94  EGS_Float expectedAverage() const {
95  return E;
96  };
97  EGS_Float maxEnergy() const {
98  return E;
99  };
100 
101 protected:
102 
103  EGS_Float sample(EGS_RandomGenerator *) {
104  return E;
105  };
106 
107  EGS_Float E;
108 
109 };
110 
111 static inline EGS_Float getGaussianRN(EGS_RandomGenerator *rndm) {
112  static bool have_x = false;
113  static EGS_Float the_x;
114  if (have_x) {
115  have_x = false;
116  return the_x;
117  }
118  EGS_Float r = sqrt(-2*log(1-rndm->getUniform()));
119  EGS_Float cphi, sphi;
120  rndm->getAzimuth(cphi,sphi);
121  the_x = r*sphi;
122  have_x = true;
123  return r*cphi;
124 };
125 
143 
144 public:
145 
152  EGS_GaussianSpectrum(EGS_Float mean_energy, EGS_Float Sigma) :
153  EGS_BaseSpectrum(), Eo(mean_energy), sigma(Sigma) {
154  if (Eo <= 0) egsFatal("EGS_GaussianSpectrum: attempt to construct "
155  "a spectrum with a negative mean energy (%g)\n",Eo);
156  if (sigma < 0) {
157  sigma = -sigma*0.4246609; // i.e. assume
158  }
159  // the user has specified FWHM
160  char buf[1024];
161  sprintf(buf,"Gaussian spectrum with Eo = %g and sigma = %g",Eo,sigma);
162  type = buf;
163  if (Eo - 5*sigma > 0) {
164  Emax = Eo + 5*sigma;
165  }
166  else {
167  Emax = 2*Eo;
168  }
169  };
170  ~EGS_GaussianSpectrum() {};
171  EGS_Float expectedAverage() const {
172  return Eo;
173  };
174  EGS_Float maxEnergy() const {
175  return Emax;
176  };
177 
178  EGS_Float sample(EGS_RandomGenerator *rndm) {
179  EGS_Float E;
180  do {
181  E = Eo + sigma*getGaussianRN(rndm);
182  }
183  while (E <= 0 || E > Emax);
184  return E;
185  };
186 
187 protected:
188 
189  EGS_Float Eo;
190  EGS_Float sigma;
191 
197  EGS_Float Emax;
198 };
199 
223 
224 public:
225 
229  EGS_DoubleGaussianSpectrum(EGS_Float mean_energy, EGS_Float sig_left,
230  EGS_Float sig_right) : EGS_BaseSpectrum(), Eo(mean_energy),
231  sleft(sig_left), sright(sig_right) {
232  if (sleft < 0) {
233  sleft = -sleft*0.4246609;
234  }
235  if (sright< 0) {
236  sright= -sright*0.4246609;
237  }
238  Emax = Eo + 4*sright;
239  if (Eo - 4*sleft < 0) egsWarning("EGS_DoubleGaussianSpectrum: "
240  "for Eo=%g, sigma=%g there will be negative energy sampled\n");
241  p = sleft/(sleft + sright);
242  char buf[1024];
243  sprintf(buf,"Double Gaussian spectrum with Eo = %g sig(left) = %g"
244  " and sig(right) = %g",Eo,sleft,sright);
245  type = buf;
246  };
248  EGS_Float maxEnergy() const {
249  return Emax;
250  };
251  EGS_Float expectedAverage() const {
252  return Eo + sqrt(2/M_PI)*(sright-sleft);
253  };
254 
255 protected:
256 
257  EGS_Float Eo;
258  EGS_Float Emax;
259  EGS_Float sleft;
260  EGS_Float sright;
261  EGS_Float p;
264  EGS_Float sample(EGS_RandomGenerator *rndm) {
265  EGS_Float E;
266  if (rndm->getUniform() < p) {
267  do {
268  E = Eo-sleft*fabs(getGaussianRN(rndm));
269  }
270  while (E <= 0);
271  }
272  else {
273  do {
274  E = Eo+sright*fabs(getGaussianRN(rndm));
275  }
276  while (E > Emax);
277  }
278  return E;
279  };
280 
281 };
282 
302 
303 public:
304 
306  EGS_UniformSpectrum(EGS_Float emin, EGS_Float emax) : EGS_BaseSpectrum(),
307  Emin(emin), Emax(emax), de(emax - emin) {
308  char buf[1024];
309  sprintf(buf,"Uniform spectrum with Emin = %g Emax = %g",Emin,Emax);
310  type = buf;
311  };
312  ~EGS_UniformSpectrum() {};
313  EGS_Float maxEnergy() const {
314  return Emax;
315  };
316  EGS_Float expectedAverage() const {
317  return (Emin+Emax)/2;
318  };
319 
320 protected:
321 
322  EGS_Float sample(EGS_RandomGenerator *rndm) {
323  return Emin + rndm->getUniform()*de;
324  };
325 
326  EGS_Float Emin;
327  EGS_Float Emax;
328  EGS_Float de;
329 };
330 
384 
385 public:
386 
401  EGS_TabulatedSpectrum(int N, const EGS_Float *x, const EGS_Float *f,
402  int Type = 1, const char *fname = 0) : EGS_BaseSpectrum(),
403  table(new EGS_AliasTable(N,x,f,Type)) {
404  setType(Type,fname);
405  };
406 
408  delete table;
409  };
410  EGS_Float maxEnergy() const {
411  return table->getMaximum();
412  };
413  EGS_Float expectedAverage() const {
414  return table->getAverage();
415  };
416 
417 protected:
418 
419  EGS_AliasTable *table;
420  void setType(int Type,const char *fname) {
421  if (Type == 0) {
422  type = "tabulated line spectrum";
423  }
424  else if (Type == 1) {
425  type = "tabulated histogram spectrum";
426  }
427  else {
428  type = "tabulated spectrum";
429  }
430  if (fname) {
431  type += " defined in ";
432  type += fname;
433  }
434  else {
435  type += " defined inline";
436  }
437  };
438 
439  EGS_Float sample(EGS_RandomGenerator *rndm) {
440  return table->sample(rndm);
441  };
442 
443 };
444 
445 
457 
458 public:
461  EGS_RadionuclideBetaSpectrum(EGS_Ensdf *decays, const string outputBetaSpectra) {
462 
464  rm = app->getRM();
465 
466  vector<BetaRecordLeaf *> myBetas = decays->getBetaRecords();
467 
468  for (vector<BetaRecordLeaf *>::iterator beta = myBetas.begin();
469  beta != myBetas.end(); beta++) {
470 
471  // Skip electron capture records
472  if ((*beta)->getCharge() == 1 &&
473  (*beta)->getPositronIntensity() == 0) {
474  continue;
475  }
476 
477  unsigned short int daughterZ = (*beta)->getZ();
478 
479  egsInformation("EGS_RadionuclideBetaSpectrum: "
480  "Energy, Z, A, forbidden: %f %d %d %d\n",
481  (*beta)->getFinalEnergy(), daughterZ,
482  (*beta)->getAtomicWeight(), (*beta)->getForbidden()
483  );
484 
485  const int nbin=1000;
486  EGS_Float *e = new EGS_Float [nbin];
487  EGS_Float *spec = new EGS_Float [nbin];
488  EGS_Float *spec_y = new EGS_Float [nbin];
489 
490  double de, s_y, factor, se_y;
491 
492  ncomps=1; // if we increase this, then we must fill the remainder
493  area[0]=1.0;
494  rel[0]=1.0;
495 
496  emax = (*beta)->getFinalEnergy();
497  zzz[0] = (double)daughterZ;
498  rmass = (*beta)->getAtomicWeight();
499 
500  // These are some special cases where fudge factors are used!
501  // Specify lamda[0]=4 for special shape factors
502 
503  // For Cl-36 (ref: nuc. phys. 99a, 625,(67))
504  // Only the beta- spectrum
505  if (daughterZ == 18 && (*beta)->getCharge() == -1) {
506  lamda[0] = 4;
507  }
508  // For I-129 (ref: phys. rev. 95, 458, 54))
509  // The beta- spectrum with 151 keV endpoint
510  else if (daughterZ == 54 && emax < 0.154 && emax > 0.150) {
511  lamda[0] = 4;
512  }
513  // For Cs-137 (ref: nuc. phys. 112a, 156, (68))
514  // The beta- spectrum with 1175 keV endpoint
515  else if (daughterZ == 56 && emax > 1.173 && emax < 1.177) {
516  lamda[0] = 4;
517  }
518  // For Tl-204 (ref: can. j. phys., 45, 2621, (67))
519  // There is only 1 beta- spectrum
520  else if (daughterZ == 82) {
521  lamda[0] = 4;
522  }
523  // For Bi-210 (ref: nuc. phys., 31, 293, (62))
524  // There is only 1 beta- spectrum
525  else if (daughterZ == 84) {
526  lamda[0] = 4;
527  }
528  else {
529  lamda[0] = (*beta)->getForbidden();
530  }
531 
532  // For positrons from zzz negative (just how the spectrum code
533  // was designed)
534  if ((*beta)->getCharge() == 1) {
535  zzz[0] *= -1;
536  }
537 
538  etop[0]=emax;
539 
540  // prbs july 9, 2007 moved here from before src loop.
541  // also, now tabulate based on
542  // endpoint, using roughly nbin bins across spectrum. Do this by
543  // rounding
544  // endpoint E0 up to nearest 100 keV, and dividing by NBIN to get
545  // binwidth.
546 
547  de=((int)(etop[0]*10.0+1)/10.)/nbin; // round up to nearest 100kev;
548  // /= NBIN
549  //cout << "Binwidth " << de << endl;
550 
551  for (int ib=0; ib<nbin; ib++) {
552  e[ib]=de+ib*de;
553 // egsInformation("%.12f, %.12f\n", e[ib], etop[0]);
554  }
555 
556  s_y=0.0;
557  se_y=0.0;
558  for (int ib=0; ib<nbin; ib++) {
559 
560  if (e[ib]<=emax) {
561  sp(e[ib],spec_y[ib],factor);
562  }
563  else {
564  spec_y[ib]=0.0;
565  }
566 
567  s_y=s_y+spec_y[ib];
568  se_y=se_y+spec_y[ib]*e[ib];
569  }
570 
571  for (int ib=0; ib<nbin; ib++) {
572  spec[ib]=1/de*(spec_y[ib]/s_y);
573 // cout << e[ib] << " " << spec[ib] << endl;
574  }
575 
576  EGS_AliasTable *bspec = new EGS_AliasTable(nbin,e,spec,1);
577  (*beta)->setSpectrum(bspec);
578 
579  // Write the spectrum to a file
580  if (outputBetaSpectra == "yes") {
581 
582  ostringstream ostr;
583  ostr << decays->radionuclide << "_" << emax << ".spec";
584 
585  egsInformation("EGS_RadionuclideBetaSpectrum: Outputting beta spectrum to file: %s\n", ostr.str().c_str());
586 
587  ofstream specStream;
588  specStream.open(ostr.str().c_str());
589  for (int ib=0; ib<nbin; ib++) {
590  spec[ib]=1/de*(spec_y[ib]/s_y);
591  specStream << e[ib] << " " << spec[ib] << endl;
592  }
593  specStream.close();
594  }
595  }
596  }
597 
598 protected:
599 
600  complex<double> cgamma(complex<double> z) {
601 
602  static const int g=7;
603  static const double pi = 3.1415926535897932384626433832795028841972;
604  static const double p[g+2] = {0.99999999999980993, 676.5203681218851,
605  -1259.1392167224028,
606  771.32342877765313,
607  -176.61502916214059,
608  12.507343278686905,
609  -0.13857109526572012,
610  9.9843695780195716e-6,
611  1.5056327351493116e-7
612  };
613 
614  if (real(z)<0.5) {
615  return pi / (sin(pi*z)*cgamma(double(1.0)-z));
616  }
617 
618  z -= 1.0;
619  complex<double> x=p[0];
620  for (int i=1; i<g+2; i++) {
621  x += p[i]/(z+complex<double>(i,0));
622  }
623  complex<double> t = z + (g + double(0.5));
624 
625  return double(sqrt(2.*pi)) * pow(t,z+double(0.5)) * exp(-t) * x;
626  }
627 
628  complex<double> clgamma(complex<double> z) {
629  complex<double> u, v, h, p, r;
630 
631  static const double pi = 3.1415926535897932384626433832795028841972;
632  static const double c1 = 9.189385332046727e-1;
633  static const double c2 = 1.144729885849400;
634  static const double c[10] = {8.333333333333333e-2,
635  -2.777777777777777e-3,
636  7.936507936507936e-4,
637  -5.952380952380952e-4,
638  8.417508417508417e-4,
639  -1.917526917526917e-3,
640  6.410256410256410e-3,
641  -2.955065359477124e-2,
642  1.796443723688305e-1,
643  -1.392432216905901
644  };
645 
646  static const double hf = 0.5;
647 
648  double x = real(z);
649  double y = imag(z);
650  h = 0;
651 
652  if (y == 0 && -abs(x) == int(x)) {
653  return 0;
654  }
655  else {
656  double ya = abs(y);
657  if (x < 0) {
658  u = double(1.) - complex<double>(x, ya);
659  }
660  else {
661  u = complex<double>(x, ya);
662  }
663 
664  h = 0;
665  double ur = real(u);
666  double ui, a;
667  if (ur < 7.) {
668  ui = imag(u);
669  a = atan2(ui,ur);
670  h = u;
671  for (int i=1; i<=6-int(ur); i++) {
672  ur = ur + 1;
673  u = complex<double>(ur, ui);
674  h = h * u;
675  a = a + atan2(ui, ur);
676  }
677  h = complex<double>(hf * log(pow(real(h),2) + pow(imag(h),2)),
678  a);
679 
680  u = double(1.) + u;
681  }
682 
683  r = double(1.) / pow(u,2);
684  p = r * c[9];
685 
686  for (int i=8; i>=1; i--) {
687  p = r * (c[i] + p);
688  }
689 
690  h = c1 + (u-hf)*log(u) - u + (c[0]+p) / u - h;
691 
692  if (x < 0.) {
693  ur = double(int(x)) - 1.;
694  ui = pi * (x-ur);
695  x = pi * ya;
696  double t = exp(-x-x);
697  a = sin(ui);
698  t = x + hf * log(t*pow(a,2)+pow(hf*(1.-t),2));
699  a = atan2(cos(ui)*tanh(x),a) - ur*pi;
700  h = c2 - complex<double>(t,a) - h;
701  }
702  if (y < 0) {
703  h = conj(h);
704  }
705  }
706 
707  return h;
708  }
709 
710  void slfact(double p, double z, double radf, double xl[4]) {
711 
712  double ff[4];
713  double dfac[4]= {1.0, 3.0, 15.0, 105.0};
714  double pi,c137,az,w,rad,pr,y,x1,gk,bb,cc,dd,x2;
715 
716  complex<double> aa;
717 
718  pi = acos(-1.0);
719  c137= 137.036; // 1/ fine structure constant
720  az = z/c137;
721  w = sqrt(p*p+1.0);
722  rad = radf/386.159;
723  pr = p*rad;
724  y = az*w/p;
725 
726  for (int k=1; k<=4; k++) {
727 
728  gk = sqrt(k*k-az*az);
729  x1 = pow((pow(p,k-1)/dfac[k-1]), 2);
730 
731  aa = clgamma(complex<double>(gk,y));
732  double aa_real = real(aa);
733 
734  bb=lgamma((double)k);
735  cc=lgamma(2.0*k +1.0);
736  dd=lgamma(2.0*gk+1.0);
737 
738  ff[k-1] =
739  pow(2.0*pr, 2.0*(gk-k)) *
740  exp(pi*y+2.0*(aa_real+cc-bb-dd)) *
741  (k+gk)/(2.0*k);
742 
743  x2 =
744  1.0 -
745  az*pr*(2.0*w*(2.0*k+1.0)/(p*k*(2.0*gk+1.0)) -
746  2.0*p*gk/(w*k*(2.0*gk+1.0))) -
747  2.0*k*pr*pr/((2.0*k+1.0)*(k+gk));
748  xl[k-1] = x1*ff[k-1]/ff[0]*x2;
749  }
750 
751  return;
752  }
753 
754  void bsp(double e, double &bspec, double &factor) {
755 
756  // *****************************************************************
757  // Calculates n(e) (unnormalized) for one spectral component,
758  // specified by:zz,emax,lamda, where
759  //
760  // lamda = 1 first forbidden
761  // lamda = 2 second forbidden
762  // lamda = 3 third forbidden
763  // lamda = 0 otherwise
764  // lamda = 4 for a few nuclides whose experimental shape don't
765  // fit theory. fudge factors are applied to the allowed
766  // shape.
767  // zz charge of the daughter nucleus
768  // emax maximum beta energy in mev.
769  // w and tsq in mc**2 units
770  // e and emax in mev units.
771  // v is the screening correction for atomic electrons
772  // for the thomas-fermi model of the atom
773  // v = 1.13 * (alpha)**2 * z**(4/3)
774  //
775  //
776 
777  double pi,c137,zab,v,z,x,w,psq,p,y,qsq,g,cab,f,radf;
778 
779  double xl[4];
780  complex<double> c;
781  complex<double> a;
782 
783  bspec=0.0;
784  if (e>emax) {
785  return;
786  }
787 
788  pi =acos(-1.);
789  c137=137.036; // 1/ fine structure constant
790 
791  zab = abs(zz);
792  v = 1.13*pow(zab,1.333)/pow(c137,2); // Screening correction
793  v = copysign(v,zz);
794  z = zab/c137;
795  x = sqrt(1.0-z*z); // s parameter
796  w = 1.0+(e/rm)-v; // Total energy of b particle
797  if (w<1.0000001) {
798  bspec = 0.;
799  return;
800  }
801  //if(w<1.00001) w=1.00001;
802  psq = w*w-double(1.0);
803  p = sqrt(psq); // Momemtum of beta particle
804  y = z*w/p; // eta = alpha * z * e / p
805  y = copysign(y,zz);
806  qsq = 3.83*pow(emax-e,2);
807 
808  if (e <= 1.0e-5) {
809  g=0.0; // Low energy approximation
810  if (zz>=0.0) {
811  g = qsq*2.0*pi*pow(z,(2.0*x-1.0));
812  }
813  }
814  else {
815  a = complex<double>(x,y);
816  c = cgamma(a);
817  cab = abs(c);
818  f = pow(psq,x-1.0)*exp(pi*y)*pow(cab,2);
819  g = f*p*w*qsq;
820  }
821 
822  factor = 1.0; // Necessary to calculate kurie plot (not done)
823  bspec = g;
824  if (lam == 0) {
825  return;
826  }
827 
828  radf = 1.2*pow(rmass,0.333); // Nuclear radius
829  slfact(p,zz,radf,xl);
830 
831  if (lam==1) {
832  bspec = g*(qsq*xl[0]+9.0*xl[1]);
833  return;
834  }
835  else if (lam==2) {
836  bspec = g*(pow(qsq,2)*xl[0]+30.0*qsq*xl[1]+225.0*xl[2]);
837  return;
838  }
839  else if (lam==3) {
840  bspec = g*(pow(qsq,3.0)*xl[0]+63.0*pow(qsq,2)*xl[1]+
841  1575.0*qsq*xl[2] + 11025.0*xl[3]);
842  return;
843  }
844  else { // lam==4
845 
846  // Fudge factors for nuclides whose experimental spectra don't
847  // seem to fit theory.
848 
849  // for cl36 (ref: nuc. phys. 99a, 625,(67))
850  if (zab == 18.0) {
851  bspec = bspec*(qsq*xl[0]+20.07*xl[1]);
852  }
853 
854  // for i129 (ref: phys. rev. 95, 458, 54))
855  if (zab == 54.) {
856  bspec = bspec*(psq+10.0*qsq);
857  }
858 
859  // for cs-ba137 (ref: nuc. phys. 112a, 156, (68))
860  if (zab == 56.) {
861  bspec = bspec*(qsq*xl[0]+0.045*xl[1]);
862  }
863 
864  // for tl204 (ref: can. j. phys., 45, 2621, (67))
865  if (zab == 82.) {
866  bspec = bspec*(1.0-1.677*e+ 2.77*e*e);
867  }
868 
869  // for bi210 (ref: nuc. phys., 31, 293, (62))
870  if (zab == 84.) {
871  bspec = bspec*(1.78-2.35*e+e*e);
872  }
873 
874  return;
875  }
876  }
877 
878  // Sums weighted, normalized spectral components to give total spectrum
879  void sp(double e, double &spec, double &factor) {
880 
881  double bspec;
882 
883  spec=0.0;
884  for (int icomp=0; icomp<ncomps; icomp++) {
885  zz = zzz[icomp];
886  emax= etop[icomp];
887  lam = lamda[icomp];
888  bsp(e,bspec,factor);
889  spec= spec+bspec*rel[icomp]/area[icomp];
890  }
891  }
892 
893 private:
894  EGS_Float rm;
895  double zz,emax,rmass;
896  double zzz[9],etop[9],rel[9],area[9],lamda[9];
897  int lam, ncomps;
898 };
899 
900 
1019 
1020 public:
1021 
1024  EGS_RadionuclideSpectrum(const string nuclide, const string ensdf_file,
1025  const EGS_Float relativeActivity, const string relaxType, const string outputBetaSpectra, const bool scoreAlphasLocally, const bool allowMultiTransition) :
1026  EGS_BaseSpectrum() {
1027 
1028  // For now, hard-code verbose mode
1029  // 0 - minimal output
1030  // 1 - some output of ensdf data and normalized intensities
1031  // 2 - verbose output
1032  int verbose = 0;
1033 
1034  // Read in the data file for the nuclide
1035  // and build the decay structure
1036  decays = new EGS_Ensdf(nuclide, ensdf_file, relaxType, allowMultiTransition, verbose);
1037 
1038  // Normalize the emission and transition intensities
1039  decays->normalizeIntensities();
1040 
1041  // Get the beta energy spectra
1042  betaSpectra = new EGS_RadionuclideBetaSpectrum(decays, outputBetaSpectra);
1043 
1044  // Get the particle records from the decay scheme
1045  myBetas = decays->getBetaRecords();
1046  myAlphas = decays->getAlphaRecords();
1047  myGammas = decays->getGammaRecords();
1048  myMetastableGammas = decays->getMetastableGammaRecords();
1049  myUncorrelatedGammas = decays->getUncorrelatedGammaRecords();
1050  myLevels = decays->getLevelRecords();
1051  xrayIntensities = decays->getXRayIntensities();
1052  xrayEnergies = decays->getXRayEnergies();
1053  augerIntensities = decays->getAugerIntensities();
1054  augerEnergies = decays->getAugerEnergies();
1055 
1056  // Initialization
1057  currentLevel = 0;
1058  Emax = 0;
1059  currentTime = 0;
1060  ishower = -1; // Start with ishower -1 so first shower has index 0
1061  totalGammaEnergy = 0;
1062  relaxationType = relaxType;
1063  scoreAlphasLocal = scoreAlphasLocally;
1064 
1065  // Get the maximum energy for emissions
1066  for (vector<BetaRecordLeaf *>::iterator beta = myBetas.begin();
1067  beta != myBetas.end(); beta++) {
1068 
1069  double energy = (*beta)->getFinalEnergy();
1070  if (Emax < energy) {
1071  Emax = energy;
1072  }
1073  }
1074  for (vector<AlphaRecord *>::iterator alpha = myAlphas.begin();
1075  alpha != myAlphas.end(); alpha++) {
1076 
1077  double energy = (*alpha)->getFinalEnergy();
1078  if (Emax < energy) {
1079  Emax = energy;
1080  }
1081  }
1082  for (vector<GammaRecord *>::iterator gamma = myGammas.begin();
1083  gamma != myGammas.end(); gamma++) {
1084 
1085  double energy = (*gamma)->getDecayEnergy();
1086  if (Emax < energy) {
1087  Emax = energy;
1088  }
1089  }
1090  for (vector<GammaRecord *>::iterator gamma = myUncorrelatedGammas.begin();
1091  gamma != myUncorrelatedGammas.end(); gamma++) {
1092 
1093  double energy = (*gamma)->getDecayEnergy();
1094  if (Emax < energy) {
1095  Emax = energy;
1096  }
1097  }
1098  for (unsigned int i=0; i < xrayEnergies.size(); ++i) {
1099  numSampledXRay.push_back(0);
1100  if (Emax < xrayEnergies[i]) {
1101  Emax = xrayEnergies[i];
1102  }
1103  }
1104  for (unsigned int i=0; i < augerEnergies.size(); ++i) {
1105  numSampledAuger.push_back(0);
1106  if (Emax < augerEnergies[i]) {
1107  Emax = augerEnergies[i];
1108  }
1109  }
1110 
1111  // Set the weight of the spectrum
1112  spectrumWeight = relativeActivity;
1113 
1114  if (verbose) {
1115  egsInformation("EGS_RadionuclideSpectrum: Emax: %f\n",Emax);
1116  egsInformation("EGS_RadionuclideSpectrum: Relative activity: %f\n",relativeActivity);
1117  }
1118 
1119  // Set the application
1121  };
1122 
1125  if (decays) {
1126  delete decays;
1127  }
1128  if (betaSpectra) {
1129  delete betaSpectra;
1130  }
1131  };
1132 
1140  EGS_Float maxEnergy() const {
1141  return Emax;
1142  };
1143 
1145  int getCharge() const {
1146  return currentQ;
1147  }
1148 
1150  double getTime() const {
1151  return currentTime;
1152  }
1153 
1155  EGS_I64 getShowerIndex() const {
1156  return ishower;
1157  }
1158 
1160  EGS_Float getEdep() const {
1161  return edep;
1162  }
1163 
1165  EGS_Float getSpectrumWeight() const {
1166  return spectrumWeight;
1167  }
1168 
1170  void setSpectrumWeight(EGS_Float newWeight) {
1171  spectrumWeight = newWeight;
1172  }
1173 
1192  unsigned int getEmissionType() const {
1193  return emissionType;
1194  }
1195 
1199 
1200  egsInformation("\nSampled %s emissions:\n", decays->radionuclide.c_str());
1201  egsInformation("========================\n");
1202 
1203  if (ishower < 1) {
1204  egsWarning("EGS_RadionuclideSpectrum::printSampledEmissions: Warning: The number of disintegrations (tracked by `ishower`) is less than 1.\n");
1205  return;
1206  }
1207 
1208  egsInformation("Energy | Intensity per 100 decays (adjusted by %f)\n", decays->decayDiscrepancy);
1209  if (myBetas.size() > 0) {
1210  egsInformation("Beta records:\n");
1211  }
1212  for (vector<BetaRecordLeaf *>::iterator beta = myBetas.begin();
1213  beta != myBetas.end(); beta++) {
1214 
1215  egsInformation("%f %f\n", (*beta)->getFinalEnergy(),
1216  ((EGS_Float)(*beta)->getNumSampled()/(ishower+1))*100);
1217  }
1218  if (myAlphas.size() > 0) {
1219  egsInformation("Alpha records:\n");
1220  }
1221  for (vector<AlphaRecord *>::iterator alpha = myAlphas.begin();
1222  alpha != myAlphas.end(); alpha++) {
1223 
1224  egsInformation("%f %f\n", (*alpha)->getFinalEnergy(),
1225  ((EGS_Float)(*alpha)->getNumSampled()/(ishower+1))*100);
1226  }
1227  if (myGammas.size() > 0) {
1228  egsInformation("Gamma records (E,Igamma,Ice,Ipp):\n");
1229  }
1230  EGS_I64 totalNumSampled = 0;
1231  for (vector<GammaRecord *>::iterator gamma = myGammas.begin();
1232  gamma != myGammas.end(); gamma++) {
1233 
1234  totalNumSampled += (*gamma)->getGammaSampled();
1235  egsInformation("%f %f %.4e %.4e\n", (*gamma)->getDecayEnergy(),
1236  ((EGS_Float)(*gamma)->getGammaSampled()/(ishower+1))*100,
1237  ((EGS_Float)(*gamma)->getICSampled()/(ishower+1))*100,
1238  ((EGS_Float)(*gamma)->getIPSampled()/(ishower+1))*100
1239  );
1240  }
1241  if (myGammas.size() > 0) {
1242  if (totalNumSampled > 0) {
1243  egsInformation("Average gamma energy: %f\n",
1244  totalGammaEnergy / totalNumSampled);
1245  }
1246  else {
1247  egsInformation("Zero gamma transitions occurred.\n");
1248  }
1249  }
1250  if (myUncorrelatedGammas.size() > 0) {
1251  egsInformation("Uncorrelated gamma records (E,Igamma,Ice,Ipp):\n");
1252  }
1253  for (vector<GammaRecord *>::iterator gamma = myUncorrelatedGammas.begin();
1254  gamma != myUncorrelatedGammas.end(); gamma++) {
1255 
1256  egsInformation("%f %f %.4e %.4e\n", (*gamma)->getDecayEnergy(),
1257  ((EGS_Float)(*gamma)->getGammaSampled()/(ishower+1))*100,
1258  ((EGS_Float)(*gamma)->getICSampled()/(ishower+1))*100,
1259  ((EGS_Float)(*gamma)->getIPSampled()/(ishower+1))*100
1260  );
1261  }
1262  if (xrayEnergies.size() > 0) {
1263  egsInformation("X-Ray records:\n");
1264  }
1265  for (unsigned int i=0; i < xrayEnergies.size(); ++i) {
1266  egsInformation("%f %f\n", xrayEnergies[i],
1267  ((EGS_Float)numSampledXRay[i]/(ishower+1))*100);
1268  }
1269  if (augerEnergies.size() > 0) {
1270  egsInformation("Auger records:\n");
1271  }
1272  for (unsigned int i=0; i < augerEnergies.size(); ++i) {
1273  egsInformation("%f %f\n", augerEnergies[i],
1274  ((EGS_Float)numSampledAuger[i]/(ishower+1))*100);
1275  }
1276  egsInformation("\n");
1277  }
1278 
1279  bool storeState(ostream &data) const {
1280  return egsStoreI64(data,ishower);
1281  }
1282 
1283  bool setState(istream &data) {
1284  return egsGetI64(data,ishower);
1285  }
1286 
1287  void resetCounter() {
1288  currentLevel = 0;
1289  currentTime = 0;
1290  ishower = -1;
1291  totalGammaEnergy = 0;
1292  }
1293 
1294 protected:
1296  EGS_Float sample(EGS_RandomGenerator *rndm) {
1297 
1298  // The energy of the sampled particle
1299  EGS_Float E;
1300  // Local energy depositions
1301  edep = 0;
1302  // Time delay of this particle
1303  currentTime = 0;
1304  // The type of emission particle
1305  emissionType = 0;
1306 
1307  // Check for relaxation particles due to shell vacancies in the daughter
1308  // These are created from internal transitions or electron capture
1309  if (relaxParticles.size() > 0) {
1310 
1311  // Get the energy and charge of the last particle on the list
1312  EGS_RelaxationParticle p = relaxParticles.pop();
1313  E = p.E;
1314  currentQ = p.q;
1315 
1316  emissionType = 1;
1317 
1318  return E;
1319  }
1320 
1321  // Sample a uniform random number
1322  EGS_Float u = rndm->getUniform();
1323 
1324  // If the daughter is in an excited state
1325  // check for transitions
1326  if (currentLevel && currentLevel->levelCanDecay() && currentLevel->getEnergy() > epsilon) {
1327 
1328  for (vector<GammaRecord *>::iterator gamma = myGammas.begin();
1329  gamma != myGammas.end(); gamma++) {
1330 
1331  if ((*gamma)->getLevelRecord() == currentLevel) {
1332 
1333  if (u < (*gamma)->getTransitionIntensity()) {
1334 
1335  // A gamma transition may either be a gamma emission
1336  // or an internal conversion electron
1337  EGS_Float u2 = 0;
1338  if ((*gamma)->getGammaIntensity() < 1) {
1339  u2 = rndm->getUniform();
1340  }
1341 
1342  // Sample how long
1343  // it took for this transition to occur
1344  // time = -halflife / ln(2) * log(1-u)
1345  double hl = currentLevel->getHalfLife();
1346  if (hl > 0) {
1347  currentTime = -hl * log(1.-rndm->getUniform()) /
1348  0.693147180559945309417232121458176568075500134360255254120680009493393;
1349  }
1350 
1351  // Determine whether multiple gamma transitions occur
1352  if (rndm->getUniform() < (*gamma)->getMultiTransitionProb()) {
1353  multiTransitions.push_back(currentLevel);
1354  }
1355 
1356  // Update the level of the daughter
1357  currentLevel = (*gamma)->getFinalLevel();
1358 
1359  // If a gamma emission occurs
1360  if (u2 < (*gamma)->getGammaIntensity()) {
1361 
1362  (*gamma)->incrGammaSampled();
1363 
1364  currentQ = (*gamma)->getCharge();
1365 
1366  E = (*gamma)->getDecayEnergy();
1367 
1368  totalGammaEnergy += E;
1369 
1370  emissionType = 2;
1371 
1372  return E;
1373 
1374  }
1375  else if (u2 < (*gamma)->getICIntensity()) {
1376  (*gamma)->incrICSampled();
1377  currentQ = -1;
1378  emissionType = 3;
1379 
1380  if ((*gamma)->icIntensity.size()) {
1381 
1382  // Determine which shell the conversion electron
1383  // comes from. This will create a shell vacancy
1384  EGS_Float u3 = rndm->getUniform();
1385 
1386  for (unsigned int i=0; i<(*gamma)->icIntensity.size(); ++i) {
1387  if (u3 < (*gamma)->icIntensity[i]) {
1388 
1389  E = (*gamma)->getDecayEnergy() - (*gamma)->getBindingEnergy(i);
1390 
1391  // egsInformation("test %d %f %f %f\n",i,(*gamma)->getDecayEnergy(),decays->getRelaxations()->getBindingEnergy(decays->Z,i),E);
1392 
1393  // Add relaxation particles to the source stack
1394  if (relaxationType == "eadl") {
1395 
1396  // Generate relaxation particles for a
1397  // shell vacancy i
1398  (*gamma)->relax(i,app->getEcut()-app->getRM(),app->getPcut(),rndm,edep,relaxParticles);
1399  }
1400 
1401  // Return the conversion electron
1402  return E;
1403  }
1404  }
1405  }
1406  return 0;
1407  }
1408  else {
1409  (*gamma)->incrIPSampled();
1410  emissionType = 13;
1411 
1412  // Internal pair production results in a positron
1413  // and electron pair
1414 
1415  //TODO: This is left for future work, we need to
1416  // determine the energies of the electron/positron
1417  // pair (sample uniformly?) and then determine the
1418  // corresponding directions. It might be best to do
1419  // this in the source instead of the spectrum.
1420 
1421  currentQ = 1;
1422  return 0;
1423  }
1424  }
1425  }
1426  }
1427 
1428  currentLevel = 0;
1429  return 0;
1430  }
1431 
1432  // If we have determined that multiple transitions will occur from some
1433  // levels, here we set the current level to an excited state, and return.
1434  // The radionuclide source will then sample again using the excited level.
1435  if (multiTransitions.size() > 0) {
1436  currentLevel = multiTransitions.back();
1437  multiTransitions.pop_back();
1438  return 0;
1439  }
1440 
1441  // ============================
1442  // Sample which decay occurs
1443  // ============================
1444  currentTime = 0;
1445 
1446  // Beta-, beta+ and electron capture
1447  for (vector<BetaRecordLeaf *>::iterator beta = myBetas.begin();
1448  beta != myBetas.end(); beta++) {
1449  if (u < (*beta)->getBetaIntensity()) {
1450 
1451  // Increment the shower number
1452  ishower++;
1453 
1454  // Increment the counter of betas and get the charge
1455  (*beta)->incrNumSampled();
1456  currentQ = (*beta)->getCharge();
1457 
1458  // Set the energy level of the daughter
1459  currentLevel = (*beta)->getLevelRecord();
1460 
1461  // For beta+ records we decide between
1462  // branches for beta+ or electron capture
1463  if (currentQ == 1) {
1464  // For positron emission, continue as usual
1465  if ((*beta)->getPositronIntensity() > epsilon && rndm->getUniform() < (*beta)->getPositronIntensity()) {
1466 
1467  }
1468  else {
1469 
1470  if (relaxationType == "eadl" && (*beta)->ecShellIntensity.size()) {
1471  // Determine which shell the electron capture
1472  // occurs in. This will create a shell vacancy
1473  EGS_Float u3 = rndm->getUniform();
1474 
1475  for (unsigned int i=0; i<(*beta)->ecShellIntensity.size(); ++i) {
1476  if (u3 < (*beta)->ecShellIntensity[i]) {
1477 
1478  // Generate relaxation particles for a
1479  // shell vacancy i
1480  (*beta)->relax(i,app->getEcut()-app->getRM(),app->getPcut(),rndm,edep,relaxParticles);
1481 
1482  emissionType = 4;
1483 
1484  return 0;
1485  }
1486  }
1487  }
1488 
1489  // For electron capture, there is no emitted particle
1490  // (only a neutrino)
1491  // so we return a 0 energy particle
1492  emissionType = 4;
1493  return 0;
1494  }
1495  emissionType = 5;
1496  }
1497  else {
1498  emissionType = 6;
1499  }
1500 
1501  // Sample the energy from the spectrum alias table
1502  E = (*beta)->getSpectrum()->sample(rndm);
1503 
1504  return E;
1505  }
1506  }
1507 
1508  // Alphas
1509  for (vector<AlphaRecord *>::iterator alpha = myAlphas.begin();
1510  alpha != myAlphas.end(); alpha++) {
1511  if (u < (*alpha)->getAlphaIntensity()) {
1512 
1513  // Increment the shower number
1514  ishower++;
1515 
1516  // Increment the counter of alphas and get the charge
1517  (*alpha)->incrNumSampled();
1518  currentQ = (*alpha)->getCharge();
1519 
1520  // Set the energy level of the daughter
1521  currentLevel = (*alpha)->getLevelRecord();
1522 
1523  // Score alpha energy depositions locally,
1524  // because alpha transport is not modeled in EGSnrc.
1525  // This is an approximation!
1526  if (scoreAlphasLocal) {
1527  edep += (*alpha)->getFinalEnergy();
1528  }
1529 
1530  emissionType = 7;
1531 
1532  // For alphas we simulate a disintegration but the
1533  // transport will not be performed so return 0
1534  return 0;
1535  }
1536  }
1537 
1538  // Metastable "decays" that will result in internal transitions
1539  for (vector<GammaRecord *>::iterator gamma = myMetastableGammas.begin();
1540  gamma != myMetastableGammas.end(); gamma++) {
1541  if (u < (*gamma)->getTransitionIntensity()) {
1542 
1543  // Increment the shower number
1544  ishower++;
1545 
1546  // Set the energy level of the daughter as though a
1547  // disintegration just occurred
1548  currentLevel = (*gamma)->getLevelRecord();
1549 
1550  emissionType = 8;
1551 
1552  // No particle returned
1553  return 0;
1554  }
1555  }
1556 
1557  // Uncorrelated internal transitions
1558  for (vector<GammaRecord *>::iterator gamma = myUncorrelatedGammas.begin();
1559  gamma != myUncorrelatedGammas.end(); gamma++) {
1560  if (u < (*gamma)->getTransitionIntensity()) {
1561 
1562  // A gamma transition may either be a gamma emission
1563  // or an internal conversion electron
1564  EGS_Float u2 = 0;
1565  if ((*gamma)->getGammaIntensity() < 1) {
1566  u2 = rndm->getUniform();
1567  }
1568 
1569  // If a gamma emission occurs
1570  if (u2 < (*gamma)->getGammaIntensity()) {
1571 
1572  (*gamma)->incrGammaSampled();
1573 
1574  currentQ = (*gamma)->getCharge();
1575 
1576  E = (*gamma)->getDecayEnergy();
1577 
1578  totalGammaEnergy += E;
1579 
1580  emissionType = 11;
1581 
1582  return E;
1583 
1584  }
1585  else if (u2 < (*gamma)->getICIntensity()) {
1586  (*gamma)->incrICSampled();
1587  currentQ = -1;
1588  emissionType = 12;
1589 
1590  if ((*gamma)->icIntensity.size()) {
1591 
1592  // Determine which shell the conversion electron
1593  // comes from. This will create a shell vacancy
1594  EGS_Float u3 = rndm->getUniform();
1595 
1596  for (unsigned int i=0; i<(*gamma)->icIntensity.size(); ++i) {
1597  if (u3 < (*gamma)->icIntensity[i]) {
1598 
1599  E = (*gamma)->getDecayEnergy() - (*gamma)->getBindingEnergy(i);
1600 
1601  // Add relaxation particles to the source stack
1602  if (relaxationType == "eadl") {
1603 
1604  // Generate relaxation particles for a
1605  // shell vacancy i
1606  (*gamma)->relax(i,app->getEcut()-app->getRM(),app->getPcut(),rndm,edep,relaxParticles);
1607  }
1608 
1609  // Return the conversion electron
1610  return E;
1611  }
1612  }
1613  }
1614  return 0;
1615  }
1616  else {
1617  (*gamma)->incrIPSampled();
1618  emissionType = 14;
1619 
1620  // Internal pair production results in a positron
1621  // and electron pair
1622 
1623  //TODO: This is left for future work, we need to
1624  // determine the energies of the electron/positron
1625  // pair (sample uniformly?) and then determine the
1626  // corresponding directions. It might be best to do
1627  // this in the source instead of the spectrum.
1628 
1629  currentQ = 1;
1630  return 0;
1631  }
1632  }
1633  }
1634 
1635  // XRays from the ensdf
1636  for (unsigned int i=0; i < xrayIntensities.size(); ++i) {
1637  if (u < xrayIntensities[i]) {
1638 
1639  numSampledXRay[i]++;
1640  currentQ = 0;
1641 
1642  E = xrayEnergies[i];
1643 
1644  emissionType = 9;
1645 
1646  return E;
1647  }
1648  }
1649 
1650  // Auger electrons from the ensdf
1651  for (unsigned int i=0; i < augerIntensities.size(); ++i) {
1652  if (u < augerIntensities[i]) {
1653 
1654  numSampledAuger[i]++;
1655  currentQ = -1;
1656 
1657  E = augerEnergies[i];
1658 
1659  emissionType = 10;
1660 
1661  return E;
1662  }
1663  }
1664 
1665  // If we get here, fission occurs
1666  // Count it as a disintegration and return 0
1667  ishower++;
1668  return 0;
1669  };
1670 
1673  EGS_Float expectedAverage() const {
1674  return 0;
1675  };
1676 
1677 private:
1678 
1679  EGS_Ensdf *decays;
1680  vector<BetaRecordLeaf *> myBetas;
1681  vector<AlphaRecord *> myAlphas;
1682  vector<GammaRecord *> myGammas,
1683  myMetastableGammas,
1684  myUncorrelatedGammas;
1685  vector<LevelRecord *> myLevels;
1686  vector<double> xrayIntensities,
1687  xrayEnergies,
1688  augerIntensities,
1689  augerEnergies;
1690  vector<EGS_I64> numSampledXRay,
1691  numSampledAuger;
1692  vector<const LevelRecord *> multiTransitions;
1694  const LevelRecord *currentLevel;
1695  int currentQ;
1696  unsigned int emissionType;
1697  EGS_Float currentTime,
1698  Emax,
1699  spectrumWeight,
1700  totalGammaEnergy,
1701  edep;
1702  EGS_I64 ishower;
1703  string relaxationType;
1704  bool scoreAlphasLocal;
1705 
1706  EGS_RadionuclideBetaSpectrum *betaSpectra;
1707  EGS_Application *app;
1708 };
1709 
1710 //
1711 // The following function is used to skip potentially present
1712 // commas as separators in spectrum files. With it we can write
1713 // stream >> input1 >> skipsep >> input2;
1714 // and it will work for white space and for comma separated input.
1715 //
1716 istream &skipsep(istream &in) {
1717  char c;
1718  for (EGS_I64 loopCount=0; loopCount<=loopMax; ++loopCount) {
1719  if (loopCount == loopMax) {
1720  egsFatal("skipsep: Too many iterations were required! Input may be invalid, or consider increasing loopMax.");
1721  return in;
1722  }
1723  in.get(c);
1724  if (in.eof() || in.fail() || !in.good()) {
1725  break;
1726  }
1727  if (c == ',') {
1728  break;
1729  }
1730  if (!isspace(c)) {
1731  in.putback(c);
1732  break;
1733  }
1734  }
1735  return in;
1736 }
1737 
1738 static char spec_msg1[] = "EGS_BaseSpectrum::createSpectrum:";
1739 
1741  if (!input) {
1742  egsWarning("%s got null input?\n",spec_msg1);
1743  return 0;
1744  }
1745  EGS_Input *inp = input;
1746  bool delete_it = false;
1747  if (!input->isA("spectrum")) {
1748  inp = input->takeInputItem("spectrum");
1749  if (!inp) {
1750  egsWarning("%s no 'spectrum' input!\n",spec_msg1);
1751  return 0;
1752  }
1753  delete_it = true;
1754  }
1755  string stype;
1756  int err = inp->getInput("type",stype);
1757  if (err) {
1758  egsWarning("%s wrong/missing 'type' input\n",spec_msg1);
1759  if (delete_it) {
1760  delete inp;
1761  }
1762  return 0;
1763  }
1764  EGS_BaseSpectrum *spec = 0;
1765  if (inp->compare(stype,"monoenergetic")) {
1766  EGS_Float Eo;
1767  err = inp->getInput("energy",Eo);
1768  if (err) egsWarning("%s wrong/missing 'energy' input for a "
1769  "monoenergetic spectrum\n",spec_msg1);
1770  else {
1771  spec = new EGS_MonoEnergy(Eo);
1772  }
1773  }
1774  else if (inp->compare(stype,"Gaussian")) {
1775  EGS_Float Eo, sig, fwhm;
1776  int err1 = inp->getInput("mean energy",Eo);
1777  int err2 = inp->getInput("sigma",sig);
1778  int err3 = inp->getInput("fwhm",fwhm);
1779  if (err1 || (err2 && err3)) {
1780  if (err1) egsWarning("%s wrong/missing 'mean energy' input"
1781  " for a Gaussian spectrum\n",spec_msg1);
1782  else egsWarning("%s wrong/missing 'sigma' and 'FWHM' input for a "
1783  "Gaussian spectrum\n",spec_msg1);
1784 
1785  }
1786  else {
1787  if (Eo <= 0) egsWarning("%s mean energy must be positive but your"
1788  " input was %g\n",Eo);
1789  else {
1790  if (!err2) {
1791  if (sig <= 0) egsWarning("%s sigma must be positive"
1792  " but your input was %g\n",spec_msg1,sig);
1793  else {
1794  spec = new EGS_GaussianSpectrum(Eo,sig);
1795  }
1796  }
1797  else {
1798  if (fwhm <= 0) egsWarning("%s FWHM must be positive"
1799  " but your input was %g\n",spec_msg1,fwhm);
1800  else {
1801  spec = new EGS_GaussianSpectrum(Eo,-fwhm);
1802  }
1803  }
1804  }
1805  }
1806  }
1807  else if (inp->compare(stype,"Double Gaussian")) {
1808  EGS_Float Eo;
1809  vector<EGS_Float> sig, fwhm;
1810  int err1 = inp->getInput("mean energy",Eo);
1811  int err2 = inp->getInput("sigma",sig);
1812  int err3 = inp->getInput("fwhm",fwhm);
1813  if (!err1 && Eo <= 0) {
1814  err1 = 1;
1815  }
1816  if (!err2 && sig.size() != 2) {
1817  err2 = 1;
1818  }
1819  if (!err2 && (sig[0] <= 0 || sig[1] <= 0)) {
1820  err2 = 1;
1821  }
1822  if (!err3 && fwhm.size() != 2) {
1823  err3 = 1;
1824  }
1825  if (!err3 && (fwhm[0] <= 0 || fwhm[1] <= 0)) {
1826  err3 = 1;
1827  }
1828  if (err1 || (err2 && err3)) {
1829  if (err1) egsWarning("%s wrong/missing 'mean energy' input"
1830  " for a Double Gaussian spectrum\n",spec_msg1);
1831  if (err2 && err3) egsWarning("%s wrong/missing 'sigma' and 'FWHM'"
1832  " input for a Double Gaussian spectrum\n",spec_msg1);
1833  }
1834  else {
1835  if (!err2 && !err3) egsWarning("%s found 'sigma' and 'FWHM' "
1836  "input, using 'sigma'\n",spec_msg1);
1837  if (!err2) {
1838  spec = new EGS_DoubleGaussianSpectrum(Eo,sig[0],sig[1]);
1839  }
1840  else {
1841  spec = new EGS_DoubleGaussianSpectrum(Eo,-fwhm[0],-fwhm[1]);
1842  }
1843  }
1844  }
1845  else if (inp->compare(stype,"uniform")) {
1846  vector<EGS_Float> range;
1847  EGS_Float Emin, Emax;
1848  int err1 = inp->getInput("range",range);
1849  int err2 = inp->getInput("minimum energy",Emin);
1850  int err3 = inp->getInput("maximum energy",Emax);
1851  if (!err2 && !err3 && Emin > Emax) {
1852  egsWarning("%s Emin (%g) is greater than Emax (%g)?\n",
1853  spec_msg1,Emin,Emax);
1854  err2 = 1;
1855  err3 = 1;
1856  }
1857  if (err1 && err2 && err3) egsWarning("%s wrong/missing 'range' and"
1858  " 'minimum/maximum energy' input\n",spec_msg1);
1859  else {
1860  if (!err2 && !err3) {
1861  spec = new EGS_UniformSpectrum(Emin,Emax);
1862  }
1863  else {
1864  if (range[0] < range[1]) {
1865  spec = new EGS_UniformSpectrum(range[0],range[1]);
1866  }
1867  else {
1868  spec = new EGS_UniformSpectrum(range[1],range[0]);
1869  }
1870  }
1871  }
1872  }
1873  else if (inp->compare(stype,"tabulated spectrum")) {
1874  string spec_file;
1875  err = inp->getInput("spectrum file",spec_file);
1876  // Expands FIRST environment variable found in spec_file
1877  spec_file = egsExpandPath(spec_file);
1878  if (!err) {
1879  ifstream sdata(spec_file.c_str());
1880  if (!sdata) egsWarning("%s failed to open spectrum file %s\n",
1881  spec_msg1,spec_file.c_str());
1882  else {
1883  char title[1024];
1884  sdata.getline(title,1023);
1885  if (sdata.eof() || sdata.fail() || !sdata.good()) {
1886  egsWarning("%s error while reading title of spectrum file"
1887  "%s\n",spec_msg1,spec_file.c_str());
1888  if (delete_it) {
1889  delete inp;
1890  }
1891  return 0;
1892  }
1893  if (sdata.eof() || sdata.fail() || !sdata.good()) {
1894  egsWarning("%s error while reading spectrum type and "
1895  "number of bins in spectrum file %s\n",
1896  spec_msg1,spec_file.c_str());
1897  if (delete_it) {
1898  delete inp;
1899  }
1900  return 0;
1901  }
1902  EGS_Float dum;
1903  int nbin, mode;
1904  sdata >> nbin >> skipsep >> dum >> skipsep >> mode;
1905  if (sdata.eof() || sdata.fail() || !sdata.good()) {
1906  egsWarning("%s error while reading spectrum type and "
1907  "number of bins in spectrum file %s\n",
1908  spec_msg1,spec_file.c_str());
1909  if (delete_it) {
1910  delete inp;
1911  }
1912  return 0;
1913  }
1914  if (nbin < 2) {
1915  egsWarning("%s nbin in a spectrum must be at least 2\n"
1916  " you have %d in the spectrum file %s\n",
1917  spec_msg1,nbin,spec_file.c_str());
1918  if (delete_it) {
1919  delete inp;
1920  }
1921  return 0;
1922  }
1923  if (mode < 0 || mode > 3) {
1924  egsWarning("%s unknown spectrum type %d in spectrum file"
1925  " %s\n",spec_msg1,mode,spec_file.c_str());
1926  if (delete_it) {
1927  delete inp;
1928  }
1929  return 0;
1930  }
1931  EGS_Float *en_array, *f_array;
1932  int ibin;
1933  f_array = new EGS_Float [nbin];
1934  if (mode == 0 || mode == 1) {
1935  en_array = new EGS_Float [nbin+1];
1936  en_array[0] = dum;
1937  ibin=1;
1938  }
1939  else {
1940  en_array = new EGS_Float [nbin];
1941  ibin=0;
1942  }
1943  for (int j=0; j<nbin; j++) {
1944  sdata >> en_array[ibin++] >> skipsep >> f_array[j];
1945  if (sdata.eof() || sdata.fail() || !sdata.good()) {
1946  egsWarning("%s error on line %d in spectrum file %s\n",
1947  spec_msg1,j+2,spec_file.c_str());
1948  if (delete_it) {
1949  delete inp;
1950  }
1951  delete [] en_array;
1952  delete [] f_array;
1953  return 0;
1954  }
1955  if (mode != 2 && ibin > 1) {
1956  if (en_array[ibin-1] <= en_array[ibin-2]) {
1957  egsWarning("%s energies must be in increasing "
1958  "order.\n This is not the case for input on "
1959  "lines %d,%d in spectrum file %s\n",
1960  spec_msg1,j+2,j+1,spec_file.c_str());
1961  if (delete_it) {
1962  delete inp;
1963  }
1964  return 0;
1965  }
1966  }
1967  if (mode == 0) {
1968  f_array[j]/=(en_array[ibin-1]-en_array[ibin-2]);
1969  }
1970  }
1971  int itype = 1;
1972  if (mode == 2) {
1973  itype = 0;
1974  }
1975  else if (mode == 3) {
1976  itype = 2;
1977  }
1978  int nb = itype == 1 ? nbin+1 : nbin;
1979  spec = new EGS_TabulatedSpectrum(nb,en_array,f_array,itype,
1980  spec_file.c_str());
1981  delete [] en_array;
1982  delete [] f_array;
1983  }
1984  }
1985  else {
1986  vector<EGS_Float> eners, probs;
1987  int itype=1, mode;
1988  int err1 = inp->getInput("energies",eners);
1989  int err2 = inp->getInput("probabilities",probs);
1990  int err3 = inp->getInput("spectrum mode",mode); // according to EGSnrc convention
1991  if (err3) {
1992  err3 = inp->getInput("spectrum type",mode); // deprecated
1993  }
1994  if (err3) {
1995  egsWarning("%s wrong/missing 'spectrum mode' input\n",spec_msg1);
1996  if (delete_it) {
1997  delete inp;
1998  }
1999  return 0;
2000  }
2001  else {
2002  if (mode < 0 || mode > 3) {
2003  egsWarning("%s unknown spectrum 'mode' %d"
2004  " %s\n",spec_msg1,mode,spec_file.c_str());
2005  if (delete_it) {
2006  delete inp;
2007  }
2008  return 0;
2009  }
2010  if (mode == 2) {
2011  itype = 0;
2012  }
2013  else if (mode == 3) {
2014  itype = 2;
2015  }
2016  }
2017  if (err1 || err2) {
2018  if (err1) egsWarning("%s wrong/missing 'energies' input\n",
2019  spec_msg1);
2020  if (err2) egsWarning("%s wrong/missing 'probabilities' "
2021  "input\n",spec_msg1);
2022  }
2023  else {
2024  if (itype == 1 && probs.size() != eners.size()-1)
2025  egsWarning("%s for spectrum type 1 the number of energies"
2026  " must be the number of probabilities + 1\n",
2027  spec_msg1);
2028  else if ((itype == 0 || itype == 2) &&
2029  probs.size() != eners.size())
2030  egsWarning("%s for spectrum types 0 and 2 the number of "
2031  "energies must be equal to the number of probabilities\n",
2032  spec_msg1);
2033  else {
2034  int nbin = eners.size();
2035  int nbin1 = itype == 1 ? nbin-1 : nbin;
2036  EGS_Float *x = new EGS_Float [nbin],
2037  *f = new EGS_Float [nbin1];
2038  int ibin = 0;
2039  if (itype == 1) {
2040  ibin = 1;
2041  x[0] = eners[0];
2042  }
2043  for (int j=0; j<nbin1; j++) {
2044  x[ibin] = eners[ibin];
2045  ibin++;
2046  f[j] = mode == 0 ? probs[j]/(eners[ibin-1]-eners[ibin-2]) : probs[j];
2047  if (itype != 0 && ibin > 1) {
2048  if (x[ibin-1] <= x[ibin-2]) {
2049  egsWarning("%s energies must be given in "
2050  "increasing order\n This is not the case"
2051  " for inputs %d and %d (%g,%g) %d\n",
2052  spec_msg1,ibin-2,ibin-1,x[ibin-2],x[ibin-1],
2053  j);
2054  if (delete_it) {
2055  delete inp;
2056  }
2057  delete [] x;
2058  delete [] f;
2059  return 0;
2060  }
2061  }
2062  }
2063  spec = new EGS_TabulatedSpectrum(nbin,x,f,itype,0);
2064  delete [] x;
2065  delete [] f;
2066  }
2067  }
2068  }
2069  }
2070  else if (inp->compare(stype,"radionuclide")) {
2071  egsInformation("EGS_BaseSpectrum::createSpectrum: Initializing radionuclide spectrum...\n");
2072 
2073  string nuclide;
2074  err = inp->getInput("nuclide",nuclide);
2075  if (err) {
2076  err = inp->getInput("isotope",nuclide);
2077  if (err) {
2078  err = inp->getInput("radionuclide",nuclide);
2079  if (err) {
2080  egsWarning("%s wrong/missing 'nuclide' input\n",spec_msg1);
2081  return 0;
2082  }
2083  }
2084  }
2085 
2086  EGS_Float relativeActivity;
2087  err = inp->getInput("relative activity",relativeActivity);
2088  if (err) {
2089  relativeActivity = 1;
2090  }
2091 
2092  // Determine whether to sample X-Rays and Auger electrons
2093  // using the ensdf data (options: eadl, ensdf or none)
2094  string tmp_relaxType, relaxType;
2095  err = inp->getInput("atomic relaxations", tmp_relaxType);
2096  if (!err) {
2097  relaxType = tmp_relaxType;
2098  }
2099  else {
2100  relaxType = "eadl";
2101  }
2102  if (inp->compare(relaxType,"ensdf")) {
2103  relaxType = "ensdf";
2104  egsInformation("EGS_BaseSpectrum::createSpectrum: Fluorescence and auger from the ensdf file will be used.\n");
2105  }
2106  else if (inp->compare(relaxType,"eadl")) {
2107  relaxType = "eadl";
2108  egsInformation("EGS_BaseSpectrum::createSpectrum: Fluorescence and auger from the ensdf file will be ignored. EADL relaxations will be used.\n");
2109  }
2110  else if (inp->compare(relaxType,"none") || inp->compare(relaxType,"off") || inp->compare(relaxType,"no")) {
2111  relaxType = "off";
2112  egsInformation("EGS_BaseSpectrum::createSpectrum: Fluorescence and auger from the ensdf file will be ignored. No relaxations following radionuclide disintegrations will be modelled.\n");
2113  }
2114  else {
2115  egsFatal("EGS_BaseSpectrum::createSpectrum: Error: Invalid selection for 'atomic relaxations'. Use 'eadl' (default), 'ensdf' or 'off'.\n");
2116  }
2117 
2118  // Determine whether to output beta energy spectra to files
2119  // (options: yes or no)
2120  string tmp_outputBetaSpectra, outputBetaSpectra;
2121  err = inp->getInput("output beta spectra", tmp_outputBetaSpectra);
2122  if (!err) {
2123  outputBetaSpectra = tmp_outputBetaSpectra;
2124 
2125  if (inp->compare(outputBetaSpectra,"yes")) {
2126  egsInformation("EGS_BaseSpectrum::createSpectrum: Beta energy spectra will be output to files.\n");
2127  }
2128  else if (inp->compare(outputBetaSpectra,"no")) {
2129  egsInformation("EGS_BaseSpectrum::createSpectrum: Beta energy spectra will not be output to files.\n");
2130  }
2131  else {
2132  egsFatal("EGS_BaseSpectrum::createSpectrum: Error: Invalid selection for 'output beta spectra'. Use 'no' (default) or 'yes'.\n");
2133  }
2134  }
2135  else {
2136  outputBetaSpectra = "no";
2137  }
2138 
2139  // Determine whether to score alpha energy locally or discard it
2140  // By default, the energy is discarded
2141  string tmp_alphaScoring;
2142  bool scoreAlphasLocally = false;
2143  err = inp->getInput("alpha scoring", tmp_alphaScoring);
2144  if (!err) {
2145  if (inp->compare(tmp_alphaScoring,"local")) {
2146  scoreAlphasLocally = true;
2147  egsInformation("EGS_BaseSpectrum::createSpectrum: Alpha particles will deposit energy locally, in the same region as creation.\n");
2148  }
2149  else if (inp->compare(tmp_alphaScoring,"discard")) {
2150  scoreAlphasLocally = false;
2151  egsInformation("EGS_BaseSpectrum::createSpectrum: Alpha particles will be discarded (no transport or energy deposition).\n");
2152  }
2153  else {
2154  egsFatal("EGS_BaseSpectrum::createSpectrum: Error: Invalid selection for 'alpha scoring'. Use 'discard' (default) or 'local'.\n");
2155  }
2156  }
2157 
2158  // Determine whether to score alpha energy locally or discard it
2159  // By default, the energy is discarded
2160  string tmp_allowMultiTransition;
2161  bool allowMultiTransition = false;
2162  err = inp->getInput("extra transition approximation", tmp_allowMultiTransition);
2163  if (!err) {
2164  if (inp->compare(tmp_allowMultiTransition,"on")) {
2165  allowMultiTransition = true;
2166  egsInformation("EGS_BaseSpectrum::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");
2167  }
2168  else if (inp->compare(tmp_allowMultiTransition,"off")) {
2169  allowMultiTransition = false;
2170  egsInformation("EGS_BaseSpectrum::createSpectrum: Extra transition approximation is off.\n");
2171  }
2172  else {
2173  egsFatal("EGS_BaseSpectrum::createSpectrum: Error: Invalid selection for 'extra transition approximation'. Use 'off' (default) or 'on'.\n");
2174  }
2175  }
2176 
2177  // For ensdf input, first check for the input argument
2178  string ensdf_file;
2179  err = inp->getInput("ensdf file",ensdf_file);
2180 
2181  // If not passed as input, find the ensdf file in the
2182  // directory $HEN_HOUSE/spectra/lnhb/ensdf/
2183  if (err) {
2184 
2186  if (app) {
2187  ensdf_file = egsJoinPath(app->getHenHouse(),"spectra");
2188  ensdf_file = egsJoinPath(ensdf_file.c_str(),"lnhb");
2189  ensdf_file = egsJoinPath(ensdf_file.c_str(),"ensdf");
2190  }
2191  else {
2192  char *hen_house = getenv("HEN_HOUSE");
2193  if (!hen_house) {
2194 
2195  egsWarning("EGS_BaseSpectrum::createSpectrum: "
2196  "No active application and HEN_HOUSE not defined.\n"
2197  "Assuming local directory for spectra\n");
2198  ensdf_file = "./";
2199  }
2200  else {
2201  ensdf_file = egsJoinPath(hen_house,"spectra");
2202  ensdf_file = egsJoinPath(ensdf_file.c_str(),"lnhb");
2203  ensdf_file = egsJoinPath(ensdf_file.c_str(),"ensdf");
2204  }
2205  }
2206  ensdf_file = egsJoinPath(ensdf_file.c_str(),nuclide.append(".txt"));
2207  }
2208 
2209  // Check that the ensdf file exists
2210  ifstream ensdf_fh;
2211  ensdf_fh.open(ensdf_file.c_str(),ios::in);
2212  if (!ensdf_fh.is_open()) {
2213  egsWarning("EGS_BaseSpectrum::createSpectrum: failed to open ensdf file %s"
2214  " for reading\n",ensdf_file.c_str());
2215  return 0;
2216  }
2217  ensdf_fh.close();
2218 
2219  // Create the spectrum
2220  spec = new EGS_RadionuclideSpectrum(nuclide, ensdf_file, relativeActivity, relaxType, outputBetaSpectra, scoreAlphasLocally, allowMultiTransition);
2221  }
2222  else {
2223  egsWarning("%s unknown spectrum type %s\n",spec_msg1,stype.c_str());
2224  }
2225  if (delete_it) {
2226  delete inp;
2227  }
2228  return spec;
2229 }
EGS_UniformSpectrum(EGS_Float emin, EGS_Float emax)
Construct a uniform spectrum between emin and emax.
A radionuclide spectrum.
~EGS_RadionuclideSpectrum()
Destructor.
#define EGS_EXPORT
Export symbols from the egspp library.
Definition: egs_libconfig.h:90
const EGS_I64 loopMax
The maximum number of iterations for near-infinite loops.
Definition: egs_functions.h:95
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, false on failure.
EGS_Float de
Emax - Emin.
EGS_AliasTable class header file.
void setSpectrumWeight(EGS_Float newWeight)
Set the relative weight assigned to this spectrum.
A class for sampling random values from a given probability distribution using the alias table techni...
EGS_BaseSource class header file.
EGS_Float expectedAverage() const
Not implemented - returns 0.
bool isA(const string &key) const
Definition: egs_input.cpp:278
void reportAverageEnergy() const
Report the average energy (expected and actually sampled).
Definition: egs_spectra.cpp:64
EGS_TabulatedSpectrum(int N, const EGS_Float *x, const EGS_Float *f, int Type=1, const char *fname=0)
Construct a tabulated spectrum from the energies x and the probabilities f.
A uniform energy spectrum.
EGS_Input class header file.
A monoenergetic particle spectrum.
Definition: egs_spectra.cpp:83
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, false on failure.
EGS_DoubleGaussianSpectrum(EGS_Float mean_energy, EGS_Float sig_left, EGS_Float sig_right)
Construct a double-Gaussian spectrum with mean energy mean_energy and widths sig_left and sig_right...
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...
void getAzimuth(EGS_Float &cphi, EGS_Float &sphi)
Sets cphi and sphi to the cosine and sine of a random angle uniformely distributed between 0 and ...
Definition: egs_rndm.h:138
A tabulated spectrum.
EGS_Float sample(EGS_RandomGenerator *rndm) const
Get a random point from this table using the RNG rndm.
EGS_Float sigma
The Gaussian width.
EGS_Float Emax
The maximum energy.
EGS_AdvancedApplication class header file.
EGS_Float Emax
The maximum energy.
The ensdf library header file.
EGS_I64 getShowerIndex() const
Get the shower index of the most recent emission.
double getTime() const
Get the emission time of the most recent emission.
unsigned int getEmissionType() const
Get the emission type of the most recent source particle.
static EGS_BaseSpectrum * createSpectrum(EGS_Input *inp)
Create and return a pointer to a spectrum object from the information pointed to by inp...
int q
charge (0-photon, -1=electron)
A Gaussian spectrum.
EGS_Float getSpectrumWeight() const
Get the relative weight assigned to this spectrum.
Base random number generator class. All random number generators should be derived from this class...
Definition: egs_rndm.h:67
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
The ensdf class for reading ensdf format data files.
Definition: egs_ensdf.h:492
EGS_Float Emax
The maximum energy.
string egsExpandPath(const string &aname)
Expands first environment variable found in a file name.
EGS_RandomGenerator class header file.
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...
static EGS_Application * activeApplication()
Get the active application.
Attempts to fix broken math header files.
EGS_Float E
energy in MeV
EGS_Float getUniform()
Returns a random number uniformly distributed between zero (inclusive) and 1 (exclusive).
Definition: egs_rndm.h:103
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.
const EGS_Float epsilon
The epsilon constant for floating point comparisons.
Definition: egs_functions.h:61
EGS_Float sright
The width of the spectrum right of Eo.
const string & getHenHouse() const
Returns the HEN_HOUSE directory.
EGS_RadionuclideBetaSpectrum(EGS_Ensdf *decays, const string outputBetaSpectra)
Construct beta spectra for a radionuclide.
A double-Gaussian spectrum.
EGS_Float sleft
The width of the spectrum left of Eo.
A class for storing information in a tree-like structure of key-value pairs. This class is used throu...
Definition: egs_input.h:182
static bool compare(const string &s1, const string &s2)
Definition: egs_input.cpp:1170
Base class for energy spectra. All energy spectra in the EGSnrc C++ class library are derived from th...
Beta spectrum generation for EGS_RadionuclideSpectrum.
EGS_MonoEnergy(EGS_Float energy)
Construct a monoenergetic spectrum with energy energy.
Definition: egs_spectra.cpp:88
EGS_GaussianSpectrum(EGS_Float mean_energy, EGS_Float Sigma)
Construct a Gaussian spectrum with mean energy mean_energy and width Sigma.
EGS_Input * takeInputItem(const string &key, bool self=true)
Get the property named key.
Definition: egs_input.cpp:226
EGS_Float getEdep() const
Get energy that should be deposited locally from relaxations/alphas.
EGS_Application class header file.
EGS_Float maxEnergy() const
Returns the maximum energy that may be emitted.
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
Base class for advanced EGSnrc C++ applications.
EGS_Float sample(EGS_RandomGenerator *rndm)
Sample an event from the spectrum, returns the energy of the emitted particle.
int getCharge() const
Get the charge of the most recent emission.
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.