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 # Ernesto Mainegra-Hing
29 #
30 ###############################################################################
31 */
32 
33 
39 #include "egs_base_source.h"
40 #include "egs_rndm.h"
41 #include "egs_alias_table.h"
42 #include "egs_input.h"
43 #include "egs_math.h"
44 
45 #include <cstdio>
46 #include "egs_math.h"
47 #include <fstream>
48 #ifndef NO_SSTREAM
49  #include <sstream>
50  #define S_STREAM std::istringstream
51 #else
52  #include <strstream>
53  #define S_STREAM std::istrstream
54 #endif
55 #include <limits>
56 
57 
58 using namespace std;
59 
60 
61 
75 
76 public:
77 
79  EGS_MonoEnergy(EGS_Float energy) : EGS_BaseSpectrum(), E(energy) {
80  char buf[1024];
81  sprintf(buf,"monoenergetic %g MeV",E);
82  type = buf;
83  };
84  ~EGS_MonoEnergy() {};
85  EGS_Float expectedAverage() const {
86  return E;
87  };
88  EGS_Float maxEnergy() const {
89  return E;
90  };
91 
92 protected:
93 
94  EGS_Float sample(EGS_RandomGenerator *) {
95  return E;
96  };
97 
98  EGS_Float E;
99 
100 };
101 
102 static inline EGS_Float getGaussianRN(EGS_RandomGenerator *rndm) {
103  static bool have_x = false;
104  static EGS_Float the_x;
105  if (have_x) {
106  have_x = false;
107  return the_x;
108  }
109  EGS_Float r = sqrt(-2*log(1-rndm->getUniform()));
110  EGS_Float cphi, sphi;
111  rndm->getAzimuth(cphi,sphi);
112  the_x = r*sphi;
113  have_x = true;
114  return r*cphi;
115 };
116 
134 
135 public:
136 
143  EGS_GaussianSpectrum(EGS_Float mean_energy, EGS_Float Sigma) :
144  EGS_BaseSpectrum(), Eo(mean_energy), sigma(Sigma) {
145  if (Eo <= 0) egsFatal("EGS_GaussianSpectrum: attempt to construct "
146  "a spectrum with a negative mean energy (%g)\n",Eo);
147  if (sigma < 0) {
148  sigma = -sigma*0.4246609; // i.e. assume
149  }
150  // the user has specified FWHM
151  char buf[1024];
152  sprintf(buf,"Gaussian spectrum with Eo = %g and sigma = %g",Eo,sigma);
153  type = buf;
154  if (Eo - 5*sigma > 0) {
155  Emax = Eo + 5*sigma;
156  }
157  else {
158  Emax = 2*Eo;
159  }
160  };
161  ~EGS_GaussianSpectrum() {};
162  EGS_Float expectedAverage() const {
163  return Eo;
164  };
165  EGS_Float maxEnergy() const {
166  return Emax;
167  };
168 
169  EGS_Float sample(EGS_RandomGenerator *rndm) {
170  EGS_Float E;
171  do {
172  E = Eo + sigma*getGaussianRN(rndm);
173  }
174  while (E <= 0 || E > Emax);
175  return E;
176  };
177 
178 protected:
179 
180  EGS_Float Eo;
181  EGS_Float sigma;
188  EGS_Float Emax;
189 };
190 
214 
215 public:
216 
220  EGS_DoubleGaussianSpectrum(EGS_Float mean_energy, EGS_Float sig_left,
221  EGS_Float sig_right) : EGS_BaseSpectrum(), Eo(mean_energy),
222  sleft(sig_left), sright(sig_right) {
223  if (sleft < 0) {
224  sleft = -sleft*0.4246609;
225  }
226  if (sright< 0) {
227  sright= -sright*0.4246609;
228  }
229  Emax = Eo + 4*sright;
230  if (Eo - 4*sleft < 0) egsWarning("EGS_DoubleGaussianSpectrum: "
231  "for Eo=%g, sigma=%g there will be negative energy sampled\n");
232  p = sleft/(sleft + sright);
233  char buf[1024];
234  sprintf(buf,"Double Gaussian spectrum with Eo = %g sig(left) = %g"
235  " and sig(right) = %g",Eo,sleft,sright);
236  type = buf;
237  };
239  EGS_Float maxEnergy() const {
240  return Emax;
241  };
242  EGS_Float expectedAverage() const {
243  return Eo + sqrt(2/M_PI)*(sright-sleft);
244  };
245 
246 protected:
247 
248  EGS_Float Eo;
249  EGS_Float Emax;
250  EGS_Float sleft;
251  EGS_Float sright;
252  EGS_Float p;
255  EGS_Float sample(EGS_RandomGenerator *rndm) {
256  EGS_Float E;
257  if (rndm->getUniform() < p) {
258  do {
259  E = Eo-sleft*fabs(getGaussianRN(rndm));
260  }
261  while (E <= 0);
262  }
263  else {
264  do {
265  E = Eo+sright*fabs(getGaussianRN(rndm));
266  }
267  while (E > Emax);
268  }
269  return E;
270  };
271 
272 };
273 
293 
294 public:
295 
297  EGS_UniformSpectrum(EGS_Float emin, EGS_Float emax) : EGS_BaseSpectrum(),
298  Emin(emin), Emax(emax), de(emax - emin) {
299  char buf[1024];
300  sprintf(buf,"Uniform spectrum with Emin = %g Emax = %g",Emin,Emax);
301  type = buf;
302  };
303  ~EGS_UniformSpectrum() {};
304  EGS_Float maxEnergy() const {
305  return Emax;
306  };
307  EGS_Float expectedAverage() const {
308  return (Emin+Emax)/2;
309  };
310 
311 protected:
312 
313  EGS_Float sample(EGS_RandomGenerator *rndm) {
314  return Emin + rndm->getUniform()*de;
315  };
316 
317  EGS_Float Emin;
318  EGS_Float Emax;
319  EGS_Float de;
320 };
321 
375 
376 public:
377 
392  EGS_TabulatedSpectrum(int N, const EGS_Float *x, const EGS_Float *f,
393  int Type = 1, const char *fname = 0) : EGS_BaseSpectrum(),
394  table(new EGS_AliasTable(N,x,f,Type)) {
395  setType(Type,fname);
396  };
397 
399  delete table;
400  };
401  EGS_Float maxEnergy() const {
402  return table->getMaximum();
403  };
404  EGS_Float expectedAverage() const {
405  return table->getAverage();
406  };
407 
408 protected:
409 
411  void setType(int Type,const char *fname) {
412  if (Type == 0) {
413  type = "tabulated line spectrum";
414  }
415  else if (Type == 1) {
416  type = "tabulated histogram spectrum";
417  }
418  else {
419  type = "tabulated spectrum";
420  }
421  if (fname) {
422  type += " defined in ";
423  type += fname;
424  }
425  else {
426  type += " defined inline";
427  }
428  };
429 
430  EGS_Float sample(EGS_RandomGenerator *rndm) {
431  return table->sample(rndm);
432  };
433 
434 };
435 
436 //
437 // The following function is used to skip potentially present
438 // commas as separators in spectrum files. With it we can write
439 // stream >> input1 >> skipsep >> input2;
440 // and it will work for white space and for comma separated input.
441 //
442 istream &skipsep(istream &in) {
443  char c;
444  for (EGS_I64 loopCount=0; loopCount<=loopMax; ++loopCount) {
445  if (loopCount == loopMax) {
446  egsFatal("skipsep: Too many iterations were required! Input may be invalid, or consider increasing loopMax.");
447  return in;
448  }
449  in.get(c);
450  if (in.eof() || in.fail() || !in.good()) {
451  break;
452  }
453  if (c == ',') {
454  break;
455  }
456  if (!isspace(c)) {
457  in.putback(c);
458  break;
459  }
460  }
461  return in;
462 }
463 
464 static char spec_msg1[] = "EGS_BaseSpectrum::createSpectrum:";
465 
467  if (!input) {
468  egsWarning("%s got null input?\n",spec_msg1);
469  return 0;
470  }
471  EGS_Input *inp = input;
472  bool delete_it = false;
473  if (!input->isA("spectrum")) {
474  inp = input->takeInputItem("spectrum");
475  if (!inp) {
476  egsWarning("%s no 'spectrum' input!\n",spec_msg1);
477  return 0;
478  }
479  delete_it = true;
480  }
481  string stype;
482  int err = inp->getInput("type",stype);
483  if (err) {
484  egsWarning("%s wrong/missing 'type' input\n",spec_msg1);
485  if (delete_it) {
486  delete inp;
487  }
488  return 0;
489  }
490  EGS_BaseSpectrum *spec = 0;
491  if (inp->compare(stype,"monoenergetic")) {
492  EGS_Float Eo;
493  err = inp->getInput("energy",Eo);
494  if (err) egsWarning("%s wrong/missing 'energy' input for a "
495  "monoenergetic spectrum\n",spec_msg1);
496  else {
497  spec = new EGS_MonoEnergy(Eo);
498  }
499  }
500  else if (inp->compare(stype,"Gaussian")) {
501  EGS_Float Eo, sig, fwhm;
502  int err1 = inp->getInput("mean energy",Eo);
503  int err2 = inp->getInput("sigma",sig);
504  int err3 = inp->getInput("fwhm",fwhm);
505  if (err1 || (err2 && err3)) {
506  if (err1) egsWarning("%s wrong/missing 'mean energy' input"
507  " for a Gaussian spectrum\n",spec_msg1);
508  else egsWarning("%s wrong/missing 'sigma' and 'FWHM' input for a "
509  "Gaussian spectrum\n",spec_msg1);
510 
511  }
512  else {
513  if (Eo <= 0) egsWarning("%s mean energy must be positive but your"
514  " input was %g\n",Eo);
515  else {
516  if (!err2) {
517  if (sig <= 0) egsWarning("%s sigma must be positive"
518  " but your input was %g\n",spec_msg1,sig);
519  else {
520  spec = new EGS_GaussianSpectrum(Eo,sig);
521  }
522  }
523  else {
524  if (fwhm <= 0) egsWarning("%s FWHM must be positive"
525  " but your input was %g\n",spec_msg1,fwhm);
526  else {
527  spec = new EGS_GaussianSpectrum(Eo,-fwhm);
528  }
529  }
530  }
531  }
532  }
533  else if (inp->compare(stype,"Double Gaussian")) {
534  EGS_Float Eo;
535  vector<EGS_Float> sig, fwhm;
536  int err1 = inp->getInput("mean energy",Eo);
537  int err2 = inp->getInput("sigma",sig);
538  int err3 = inp->getInput("fwhm",fwhm);
539  if (!err1 && Eo <= 0) {
540  err1 = 1;
541  }
542  if (!err2 && sig.size() != 2) {
543  err2 = 1;
544  }
545  if (!err2 && (sig[0] <= 0 || sig[1] <= 0)) {
546  err2 = 1;
547  }
548  if (!err3 && fwhm.size() != 2) {
549  err3 = 1;
550  }
551  if (!err3 && (fwhm[0] <= 0 || fwhm[1] <= 0)) {
552  err3 = 1;
553  }
554  if (err1 || (err2 && err3)) {
555  if (err1) egsWarning("%s wrong/missing 'mean energy' input"
556  " for a Double Gaussian spectrum\n",spec_msg1);
557  if (err2 && err3) egsWarning("%s wrong/missing 'sigma' and 'FWHM'"
558  " input for a Double Gaussian spectrum\n",spec_msg1);
559  }
560  else {
561  if (!err2 && !err3) egsWarning("%s found 'sigma' and 'FWHM' "
562  "input, using 'sigma'\n",spec_msg1);
563  if (!err2) {
564  spec = new EGS_DoubleGaussianSpectrum(Eo,sig[0],sig[1]);
565  }
566  else {
567  spec = new EGS_DoubleGaussianSpectrum(Eo,-fwhm[0],-fwhm[1]);
568  }
569  }
570  }
571  else if (inp->compare(stype,"uniform")) {
572  vector<EGS_Float> range;
573  EGS_Float Emin, Emax;
574  int err1 = inp->getInput("range",range);
575  int err2 = inp->getInput("minimum energy",Emin);
576  int err3 = inp->getInput("maximum energy",Emax);
577  if (!err2 && !err3 && Emin > Emax) {
578  egsWarning("%s Emin (%g) is greater than Emax (%g)?\n",
579  spec_msg1,Emin,Emax);
580  err2 = 1;
581  err3 = 1;
582  }
583  if (err1 && err2 && err3) egsWarning("%s wrong/missing 'range' and"
584  " 'minimum/maximum energy' input\n",spec_msg1);
585  else {
586  if (!err2 && !err3) {
587  spec = new EGS_UniformSpectrum(Emin,Emax);
588  }
589  else {
590  if (range[0] < range[1]) {
591  spec = new EGS_UniformSpectrum(range[0],range[1]);
592  }
593  else {
594  spec = new EGS_UniformSpectrum(range[1],range[0]);
595  }
596  }
597  }
598  }
599  else if (inp->compare(stype,"tabulated spectrum")) {
600  string spec_file;
601  err = inp->getInput("spectrum file",spec_file);
602  // Expands FIRST environment variable found in spec_file
603  spec_file = egsExpandPath(spec_file);
604  if (!err) {
605  ifstream sdata(spec_file.c_str());
606  if (!sdata) egsWarning("%s failed to open spectrum file %s\n",
607  spec_msg1,spec_file.c_str());
608  else {
609  char title[1024];
610  sdata.getline(title,1023);
611  if (sdata.eof() || sdata.fail() || !sdata.good()) {
612  egsWarning("%s error while reading title of spectrum file"
613  "%s\n",spec_msg1,spec_file.c_str());
614  if (delete_it) {
615  delete inp;
616  }
617  return 0;
618  }
619  if (sdata.eof() || sdata.fail() || !sdata.good()) {
620  egsWarning("%s error while reading spectrum type and "
621  "number of bins in spectrum file %s\n",
622  spec_msg1,spec_file.c_str());
623  if (delete_it) {
624  delete inp;
625  }
626  return 0;
627  }
628  EGS_Float dum;
629  int nbin, mode;
630  sdata >> nbin >> skipsep >> dum >> skipsep >> mode;
631  if (sdata.eof() || sdata.fail() || !sdata.good()) {
632  egsWarning("%s error while reading spectrum type and "
633  "number of bins in spectrum file %s\n",
634  spec_msg1,spec_file.c_str());
635  if (delete_it) {
636  delete inp;
637  }
638  return 0;
639  }
640  if (nbin < 2) {
641  egsWarning("%s nbin in a spectrum must be at least 2\n"
642  " you have %d in the spectrum file %s\n",
643  spec_msg1,nbin,spec_file.c_str());
644  if (delete_it) {
645  delete inp;
646  }
647  return 0;
648  }
649  if (mode < 0 || mode > 3) {
650  egsWarning("%s unknown spectrum type %d in spectrum file"
651  " %s\n",spec_msg1,mode,spec_file.c_str());
652  if (delete_it) {
653  delete inp;
654  }
655  return 0;
656  }
657  EGS_Float *en_array, *f_array;
658  int ibin;
659  f_array = new EGS_Float [nbin];
660  if (mode == 0 || mode == 1) {
661  en_array = new EGS_Float [nbin+1];
662  en_array[0] = dum;
663  ibin=1;
664  }
665  else {
666  en_array = new EGS_Float [nbin];
667  ibin=0;
668  }
669  for (int j=0; j<nbin; j++) {
670  sdata >> en_array[ibin++] >> skipsep >> f_array[j];
671  if (sdata.eof() || sdata.fail() || !sdata.good()) {
672  egsWarning("%s error on line %d in spectrum file %s\n",
673  spec_msg1,j+2,spec_file.c_str());
674  if (delete_it) {
675  delete inp;
676  }
677  delete [] en_array;
678  delete [] f_array;
679  return 0;
680  }
681  if (mode != 2 && ibin > 1) {
682  if (en_array[ibin-1] <= en_array[ibin-2]) {
683  egsWarning("%s energies must be in increasing "
684  "order.\n This is not the case for input on "
685  "lines %d,%d in spectrum file %s\n",
686  spec_msg1,j+2,j+1,spec_file.c_str());
687  if (delete_it) {
688  delete inp;
689  }
690  return 0;
691  }
692  }
693  if (mode == 0) {
694  f_array[j]/=(en_array[ibin-1]-en_array[ibin-2]);
695  }
696  }
697  int itype = 1;
698  if (mode == 2) {
699  itype = 0;
700  }
701  else if (mode == 3) {
702  itype = 2;
703  }
704  int nb = itype == 1 ? nbin+1 : nbin;
705  spec = new EGS_TabulatedSpectrum(nb,en_array,f_array,itype,
706  spec_file.c_str());
707  delete [] en_array;
708  delete [] f_array;
709  }
710  }
711  else {
712  vector<EGS_Float> eners, probs;
713  int itype=1, mode;
714  int err1 = inp->getInput("energies",eners);
715  int err2 = inp->getInput("probabilities",probs);
716  int err3 = inp->getInput("spectrum mode",mode); // according to EGSnrc convention
717  if (err3) {
718  err3 = inp->getInput("spectrum type",mode); // deprecated
719  }
720  if (err3) {
721  egsWarning("%s wrong/missing 'spectrum mode' input\n",spec_msg1);
722  if (delete_it) {
723  delete inp;
724  }
725  return 0;
726  }
727  else {
728  if (mode < 0 || mode > 3) {
729  egsWarning("%s unknown spectrum 'mode' %d"
730  " %s\n",spec_msg1,mode,spec_file.c_str());
731  if (delete_it) {
732  delete inp;
733  }
734  return 0;
735  }
736  if (mode == 2) {
737  itype = 0;
738  }
739  else if (mode == 3) {
740  itype = 2;
741  }
742  }
743  if (err1 || err2) {
744  if (err1) egsWarning("%s wrong/missing 'energies' input\n",
745  spec_msg1);
746  if (err2) egsWarning("%s wrong/missing 'probabilities' "
747  "input\n",spec_msg1);
748  }
749  else {
750  if (itype == 1 && probs.size() != eners.size()-1)
751  egsWarning("%s for spectrum type 1 the number of energies"
752  " must be the number of probabilities + 1\n",
753  spec_msg1);
754  else if ((itype == 0 || itype == 2) &&
755  probs.size() != eners.size())
756  egsWarning("%s for spectrum types 0 and 2 the number of "
757  "energies must be equal to the number of probabilities\n",
758  spec_msg1);
759  else {
760  int nbin = eners.size();
761  int nbin1 = itype == 1 ? nbin-1 : nbin;
762  EGS_Float *x = new EGS_Float [nbin],
763  *f = new EGS_Float [nbin1];
764  int ibin = 0;
765  if (itype == 1) {
766  ibin = 1;
767  x[0] = eners[0];
768  }
769  for (int j=0; j<nbin1; j++) {
770  x[ibin] = eners[ibin];
771  ibin++;
772  f[j] = mode == 0 ? probs[j]/(eners[ibin-1]-eners[ibin-2]) : probs[j];
773  if (itype != 0 && ibin > 1) {
774  if (x[ibin-1] <= x[ibin-2]) {
775  egsWarning("%s energies must be given in "
776  "increasing order\n This is not the case"
777  " for inputs %d and %d (%g,%g) %d\n",
778  spec_msg1,ibin-2,ibin-1,x[ibin-2],x[ibin-1],
779  j);
780  if (delete_it) {
781  delete inp;
782  }
783  delete [] x;
784  delete [] f;
785  return 0;
786  }
787  }
788  }
789  spec = new EGS_TabulatedSpectrum(nbin,x,f,itype,0);
790  delete [] x;
791  delete [] f;
792  }
793  }
794  }
795  }
796  else {
797  egsWarning("%s unknown spectrum type %s\n",spec_msg1,stype.c_str());
798  }
799  if (delete_it) {
800  delete inp;
801  }
802  return spec;
803 }
A class for sampling random values from a given probability distribution using the alias table techni...
EGS_Float sample(EGS_RandomGenerator *rndm) const
Get a random point from this table using the RNG rndm.
Base class for energy spectra. All energy spectra in the EGSnrc C++ class library are derived from th...
static EGS_BaseSpectrum * createSpectrum(EGS_Input *inp)
Create and return a pointer to a spectrum object from the information pointed to by inp.
A double-Gaussian spectrum.
EGS_Float sright
The width of the spectrum right of Eo.
EGS_Float Eo
The mean energy.
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.
EGS_Float Emax
The maximum energy.
EGS_Float sleft
The width of the spectrum left of Eo.
A Gaussian spectrum.
EGS_GaussianSpectrum(EGS_Float mean_energy, EGS_Float Sigma)
Construct a Gaussian spectrum with mean energy mean_energy and width Sigma.
EGS_Float sigma
The Gaussian width.
EGS_Float Emax
The maximum energy.
EGS_Float Eo
The mean energy.
A class for storing information in a tree-like structure of key-value pairs. This class is used throu...
Definition: egs_input.h:182
EGS_Input * takeInputItem(const string &key, bool self=true)
Get the property named key.
Definition: egs_input.cpp:226
static bool compare(const string &s1, const string &s2)
Definition: egs_input.cpp:1170
bool isA(const string &key) const
Definition: egs_input.cpp:278
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
A monoenergetic particle spectrum.
Definition: egs_spectra.cpp:74
EGS_Float E
The spectrum energy.
Definition: egs_spectra.cpp:96
EGS_MonoEnergy(EGS_Float energy)
Construct a monoenergetic spectrum with energy energy.
Definition: egs_spectra.cpp:79
Base random number generator class. All random number generators should be derived from this class.
Definition: egs_rndm.h:67
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
EGS_Float getUniform()
Returns a random number uniformly distributed between zero (inclusive) and 1 (exclusive).
Definition: egs_rndm.h:103
A tabulated spectrum.
EGS_AliasTable * table
The alias table object used to sample energies.
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_Float Emax
The maximum energy.
EGS_Float Emin
The minimum energy.
EGS_Float de
Emax - Emin.
EGS_UniformSpectrum(EGS_Float emin, EGS_Float emax)
Construct a uniform spectrum between emin and emax.
EGS_AliasTable class header file.
EGS_BaseSource class header file.
EGS_Input class header file.
#define EGS_EXPORT
Export symbols from the egspp library.
Definition: egs_libconfig.h:90
Attempts to fix broken math header files.
EGS_RandomGenerator class header file.
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
string egsExpandPath(const string &aname)
Expands first environment variable found in a file name.
const EGS_I64 loopMax
The maximum number of iterations for near-infinite loops.
Definition: egs_functions.h:96
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.