EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_fluence_scoring.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ fluence scoring object implementation
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: Ernesto Mainegra-Hing, 2022
25 #
26 # Contributors:
27 #
28 ###############################################################################
29 */
30 
37 #include <string>
38 #include <cstdlib>
39 
40 #include "egs_fluence_scoring.h"
41 #include "egs_input.h"
42 #include "egs_functions.h"
43 
44 static bool EGS_FLUENCE_SCORING_LOCAL inputSet = false;
45 
46 #define REGIONS_ENTRIES 100
47 #define REGIONS_PER_LINE 25
48 
50  EGS_AusgabObject(Name,f), particle_name("photon"),
51  scoring_charge(photon), source_charge(unknown),
52  n_scoring_regions(0), n_source_regions(0),
53  max_reg(-1), active_region(-1),
54  flu_s(0), flu_nbin(128), flu_xmin(0.001), flu_xmax(1.0),
55  norm_u(1.0), flu(0), fluT(0), flu_p(0), fluT_p(0), current_ncase(0),
56  verbose(false), score_primaries(false), score_spe(false),
57  m_primary(0.0), m_tot(0.0) {
58  otype = "EGS_FluenceScoring";
59  flu_a = flu_nbin;
60  flu_a /= (flu_xmax - flu_xmin);
61  flu_b = -flu_xmin*flu_a;
62 }
63 
66  if (flu) {
67  delete [] flu;
68  }
69  if (fluT) {
70  delete fluT;
71  }
72  if (flu_p) {
73  delete [] flu_p;
74  }
75  if (fluT_p) {
76  delete fluT_p;
77  }
78 }
79 
80 void EGS_FluenceScoring::initScoring(EGS_Input *inp) {
81 
82  if (!inp) {
83  egsWarning("AO type %s: null input?\n",otype.c_str());
84  return;
85  }
86 
87  vector<string> name;
88  int the_selection = 0;
89  name.push_back("photon");
90  name.push_back("electron");
91  name.push_back("positron");
92  name.push_back("undefined");
93  the_selection = inp->getInput("scoring particle", name, 3);
94  switch (the_selection) {
95  case 0:
96  particle_name="photon";
97  scoring_charge = photon;
98  break;
99  case 1:
100  particle_name="electron";
101  scoring_charge = electron;
102  break;
103  case 2:
104  particle_name="positron";
105  scoring_charge = positron;
106  break;
107  default:
108  egsFatal("\n******* ERROR *******\n"
109  " Undefined scoring charge!\n"
110  " Aborting!\n"
111  "************************\n");
112  }
113 
114  the_selection = inp->getInput("source particle", name, 3);
115  switch (the_selection) {
116  case 0:
117  source_charge = photon;
118  break;
119  case 1:
120  source_charge = electron;
121  break;
122  case 2:
123  source_charge = positron;
124  break;
125  default:
126  source_charge = unknown;
127  }
128 
129  vector<string> choice;
130  choice.push_back("no");
131  choice.push_back("yes");
132  verbose = inp->getInput("verbose", choice,0);
133  score_primaries = inp->getInput("score primaries",choice,0);
134  score_spe = inp->getInput("score spectrum", choice,0);
135 
136  EGS_Float norma;
137  int err_norm = inp->getInput("normalization",norma);
138  if (!err_norm) {
139  norm_u = norma;
140  }
141  else {
142  norm_u = 1.0;
143  }
144 
145  if (score_spe) {
146  EGS_Float flu_Emin, flu_Emax;
147  EGS_Input *eGrid = inp->takeInputItem("energy grid");
148  if (eGrid) {
149  int err_n = eGrid->getInput("number of bins",flu_nbin);
150  int err_i = eGrid->getInput("minimum kinetic energy",flu_Emin);
151  int err_f = eGrid->getInput("maximum kinetic energy",flu_Emax);
152  if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring"
153  " Missing input: number of bins.\n"
154  " Aborting!\n\n");
155  if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring"
156  " Missing input: minimum kinetic energy.\n"
157  " Aborting!\n\n");
158  if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring"
159  " Missing input: maximum kinetic energy.\n"
160  " Aborting!\n\n");
161 
162 
163  vector<string> scale;
164  scale.push_back("linear");
165  scale.push_back("logarithmic");
166  flu_s = eGrid->getInput("scale",scale,0);
167  if (flu_s == 0) {
168  flu_xmin = flu_Emin;
169  flu_xmax = flu_Emax;
170  }
171  else {
172  flu_xmin = log(flu_Emin);
173  flu_xmax = log(flu_Emax);
174  }
175  flu_a = flu_nbin;
176  flu_a /= (flu_xmax - flu_xmin);
177  flu_b = -flu_xmin*flu_a;
178  /******************************************************
179  Algorithm assigns E in [Ei,Ei+1), one could add extra
180  bin for E = Emax cases. Alternatively, push those
181  events into last bin (bias?) during scoring.
182  Which approach is correct?
183  ******************************************************/
184  //flu_nbin++;
185  }
186  }
187 
188  /*
189  Get source regions where interacting particles
190  are not subjected to classification
191  */
192  vector <int> s_start, s_stop;
193  /*
194  if (!inp->getInput("source regions",s_regionsString) && s_regionsString.length()>0) {
195  using_all_regions = false; // individual regions
196  }
197  else {*/
198  /* Failed reading individual regions, try group of regions! */
199  if (inp->getInput("source regions",s_regionsString) || s_regionsString.length()<=0) {
200  int err1 = inp->getInput("start source region",s_start);
201  int err2 = inp->getInput("stop source region",s_stop);
202  if (!err1 && !err2) {
203  if (s_start.size() == s_stop.size()) { // group of dose regions
204  for (int i=0; i<s_start.size(); i++) {
205  int ir = s_start[i], fr = s_stop[i];
206  if (ir > fr)
207  egsFatal("\nEGS_FluenceScoring::initScoring: \n"
208  " Decreasing start (%d) / stop %d region in source region group %d\n"
209  " Aborting!\n\n", ir, fr, i);
210  for (int ireg=ir; ireg<=fr; ireg++) {
211  s_region.push_back(ireg);
212  }
213  }
214  }
215  else egsFatal(
216  "\nEGS_FluenceScoring::initScoring: \n"
217  " Mismatch in number of start (%d) and stop (%d)\n"
218  " source region groups. Aborting!\n",
219  s_start.size(),s_stop.size());
220  }
221  }
222 
223 }
224 
225 void EGS_FluenceScoring::getSensitiveRegions(EGS_Input *inp) {
226 
227  string listLabel, startLabel, stopLabel;
228 
229  if (otype == "EGS_PlanarFluence") {
230  listLabel = "contributing regions";
231  startLabel = "start contributing region";
232  stopLabel = "stop contributing region";
233  }
234  else if (otype == "EGS_VolumetricFluence") {
235  listLabel = "scoring regions";
236  startLabel = "start region";
237  stopLabel = "stop region";
238  }
239  else {
240  egsFatal("\n*** Unknown fluence scoriung type! Aborting!\n\n");
241  }
242 
243  /* get scoring regions */
244  if (!inp->getInput(listLabel,f_regionsString) && f_regionsString.length()>0) {
245  // Individual regions
246  if (f_regionsString == "ALL") {
247  score_in_all_regions = true;
248  }
249  else {
250  score_in_all_regions = false;
251  }
252  }
253  else {// Groups of regions
254  int err1 = inp->getInput(startLabel,f_start);
255  int err2 = inp->getInput(stopLabel, f_stop);
256  if (!err1 && !err2) {
257  if (f_start.size() == f_stop.size()) { // group of dose regions
258  for (int i=0; i<f_start.size(); i++) {
259  int ir = f_start[i], fr = f_stop[i];
260  if (ir > fr)
261  egsFatal("\nEGS_FluenceScoring::initScoring: \n"
262  " Decreasing start (%d) / stop (%d) in region group %d\n"
263  " Aborting!\n\n", ir, fr, i);
264  for (int ireg=ir; ireg<=fr; ireg++) {
265  f_region.push_back(ireg);
266  }
267  }
268  score_in_all_regions = false;
269  }
270  else egsFatal(
271  "\nEGS_FluenceScoring::initScoring: \n"
272  " Mismatch in number of start (%d) and stop (%d)\n"
273  " region groups. Aborting!\n",
274  f_start.size(),f_stop.size());
275  }
276  }
277 
278 }
279 void EGS_FluenceScoring::getNumberRegions(const string &str, vector<int> &regs) {
280  if (!app)
281  egsFatal("EGS_FluenceScoring::getNumberRegions\n"
282  " Undefined parent application! Aborting!\n");
283 
284  app->getNumberRegions(str, regs);
285 }
286 
287 void EGS_FluenceScoring::getLabelRegions(const string &str, vector<int> &regs) {
288  if (!app)
289  egsFatal("EGS_FluenceScoring::getLabelRegions\n"
290  " Undefined parent application! Aborting!\n");
291 
292  app->getLabelRegions(str, regs);
293 }
294 
295 void EGS_FluenceScoring::setUpRegionFlags() {
296 
297  if (s_regionsString.length() > 0 && !s_region.size()) {
298  getNumberRegions(s_regionsString, s_region);
299  getLabelRegions(s_regionsString, s_region);
300  }
301  // Get the number of source regions. If too many, reset to nreg.
302  n_source_regions = s_region.size() < nreg ? s_region.size() : nreg;
303 
304  if (!score_in_all_regions) {
305  if (f_regionsString.length() > 0 && !f_region.size()) {
306  getNumberRegions(f_regionsString, f_region);
307  getLabelRegions(f_regionsString, f_region);
308  }
309  // Get the number of scoring regions. If too many, reset to nreg
310  n_scoring_regions = f_region.size() < nreg ? f_region.size() : nreg;
311  }
312  else if (score_in_all_regions) {
313  n_scoring_regions = nreg;
314  for (int j=0; j<nreg; j++) {
315  f_region.push_back(j);
316  }
317  }
318 
319  if (n_source_regions == nreg && score_primaries)
320  egsWarning("=> Warning: Setting n_source_regions = nreg \n"
321  " effectively supresses classification\n"
322  " of primaries and secondaries!");
323  for (int i = 0; i < n_source_regions; i++) {
324  is_source[s_region[i]] = true;
325  }
326 
327  for (int i = 0; i < n_scoring_regions; i++) {
328  is_sensitive[f_region[i]] = true;
329  if (f_region[i] > max_reg) {
330  max_reg = f_region[i];
331  }
332  }
333 }
334 
335 void EGS_FluenceScoring::describeMe() {
336 
337  char buf[128];
338 
339  // Report scoring regions in the geometry.
340  if (n_scoring_regions == nreg) {
341  description += "ALL\n";
342  }
343  else {
344  int start = 0, stop = 0, k = 0, entries = 0;
345  bool line_ended;
346  /* List up to 100 scoring groups or regions */
347  while (k < nreg && entries < REGIONS_ENTRIES) {
348  line_ended = false;
349  if (is_sensitive[k]) {
350  start = k;
351  entries++;
352  while (is_sensitive[k] && k < nreg) {
353  k++;
354  }
355  stop = k-1;
356  if (start < stop) {
357  sprintf(buf,"%d-%d",start,stop);
358  }
359  else if (k % REGIONS_PER_LINE) {
360  sprintf(buf," %d",start);
361  }
362  else {
363  sprintf(buf," %d\n",start);
364  line_ended = true;
365  }
366  if (entries == REGIONS_ENTRIES) {
367  sprintf(buf,"... %d\n",max_reg);
368  line_ended = true;
369  }
370  description += buf;
371  }
372  k++;
373  }
374  if (!line_ended) {
375  description += "\n";
376  }
377  }
378 
379  if (score_primaries) {
380  if (source_charge == unknown) {
381  source_charge = scoring_charge;
382  description += " - Unknown source charge due to multi-particle source\n";
383  description += " and no user input defining source particle type!\n";
384  description += " Defaulting to scoring charge: ";
385  }
386  else {
387  description += " - source charge: ";
388  }
389  sprintf(buf,"%d\n",source_charge);
390  description += buf;
391  }
392 
393  if (score_spe) {
394  description += " - scoring in the ";
395  EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin,
396  Emax = flu_s ? exp(flu_xmax):flu_xmax;
397  sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax);
398  description += buf;
399  if (flu_s) {
400  description += " on a logarithmic scale \n";
401  }
402  else {
403  description += " on a linear scale \n";
404  }
405  }
406 }
407 /**********************************************
408  * Class EGS_PlanarFluence Implementation *
409 ***********************************************/
410 
411 
413  EGS_FluenceScoring(Name,f), hits_field(false), Nx(1), Ny(1) {
414  otype = "EGS_PlanarFluence";
415  m_midpoint = EGS_Vector(0,0,5);
416  m_R = 5;
417  m_R2 = 25;
418  field_type = circle;
419  Area = M_PI * m_R2;
420  m_normal = EGS_Vector(0,0,1);
421  m_d = m_normal*m_midpoint;
422 }
423 
426 
427  if (flu) {
428  for (int j=0; j<Nx*Ny; j++) {
429  delete flu[j];
430  }
431  }
432  if (flu_p) {
433  for (int j=0; j<Nx*Ny; j++) {
434  delete flu_p[j];
435  }
436  }
437 }
438 
439 void EGS_PlanarFluence::setApplication(EGS_Application *App) {
440 
442 
443  if (!app) {
444  return;
445  }
446 
447  /***********************************************************
448  Defaults to charge of application source. This applies most
449  of the time, except for bremsstrahlung targets and multiple
450  particle sources such as radiactive sources.
451  Can be changed via 'source particle' input.
452  *************************************************************/
453  if (source_charge == unknown) {
454  source_charge = (ParticleType)app->sourceCharge();
455  }
456 
457 
458  // Get the number of regions in the geometry.
459  nreg = app->getnRegions();
460 
461  /* Initialize arrays with defaults */
462  for (int j=0; j<nreg; j++) {
463 
464  if (!score_in_all_regions) {
465  is_sensitive.push_back(false);
466  }
467  else {
468  is_sensitive.push_back(true);
469  }
470 
471  is_source.push_back(false);
472  }
473 
474  /* Flag scoring and source regions*/
475  setUpRegionFlags();
476 
477  /* Setup fluence scoring arrays, Nx*Ny = 1 for the circle */
478  fluT = new EGS_ScoringArray(Nx*Ny);
479  if (score_spe) {
480  flu = new EGS_ScoringArray* [Nx*Ny];
481  for (int j = 0; j < Nx*Ny; j++) {
482  flu[j] = new EGS_ScoringArray(flu_nbin);
483  }
484  }
485 
486  if (score_primaries) {
487  fluT_p = new EGS_ScoringArray(Nx*Ny);
488  if (score_spe) {
489  flu_p = new EGS_ScoringArray* [Nx*Ny];
490  for (int j = 0; j < Nx*Ny; j++) {
491  flu_p[j] = new EGS_ScoringArray(flu_nbin);
492  }
493  }
494  }
495 
496  describeMe();
497 }
498 
499 void EGS_PlanarFluence::initScoring(EGS_Input *inp) {
500 
501  EGS_Input *pScoringInput;
502 
503  if (!inp) {
504  egsFatal("AO type %s: null input?\n",otype.c_str());
505  return;
506  }
507  else {
508  pScoringInput = inp->takeInputItem("planar scoring");
509  if (! pScoringInput) {
510  egsFatal("AO type %s: Missing planar scoring input?\n",otype.c_str());
511  return;
512  }
513  }
514 
515  EGS_FluenceScoring::initScoring(inp);// common inputs
516 
517  /* Planar scoring by default from all regions */
518  score_in_all_regions = true;
519  EGS_FluenceScoring::getSensitiveRegions(pScoringInput);
520 
521  // Specific plane input
522  vector<EGS_Float> tmp_field;
523  int err2 = pScoringInput->getInput("scoring circle",tmp_field);
524  if (err2) {
525  int err3 = pScoringInput->getInput("scoring rectangle",tmp_field);
526  if (err3 || tmp_field.size() != 4) {
527  egsWarning(
528  "\n\n*** Wrong/missing 'scoring rectangle' input "
529  "setting it to 10 cm X 10 cm field at origin!\n\n");
530  m_midpoint = EGS_Vector();
531  ax = 10;
532  ay = 10;
533  field_type = rectangle;
534  Area = ax*ay;
535  }
536  else {
537  EGS_Float xmin = tmp_field[0],xmax = tmp_field[1],
538  ymin = tmp_field[2],ymax = tmp_field[3];
539  /* scoring plane location in space */
540  m_midpoint = EGS_Vector((xmax+xmin)/2.,(ymax+ymin)/2.,0); // at origin by default
541  /* scoring plane normal */
542  m_normal = EGS_Vector(0,0,1); // default normal along positive z-axis
543  /* define unit vectors on right-handed scoring plane */
544  ux = EGS_Vector(1,0,0);
545  uy = EGS_Vector(0,1,0);
546  /* Request a scoring plane transformation for initial position and orientation */
548  if (t) {
549  t->rotate(m_normal) ;
550  t->transform(m_midpoint);
551  t->rotate(ux);
552  t->rotate(uy);
553  delete t;
554  }
555 
556  /* get screen resolution */
557  vector<int> screen;
558  int err4 = pScoringInput->getInput("resolution",screen);
559  if (err4) {
560  Nx=1;
561  Ny=1;
562  egsWarning(
563  "\n\n*** Missing/wrong 'resolution' input "
564  " Scoring in the whole field\n\n");
565 
566  }
567  else if (screen.size()==1) {
568  Nx = screen[0];
569  Ny = screen[0];
570  }
571  else if (screen.size()==2) {
572  Nx = screen[0];
573  Ny = screen[1];
574  }
575  else if (screen.size()> 2) {
576  Nx = screen[0];
577  Ny = screen[1];
578  egsWarning(
579  "\n\n*** Too many 'resolution' inputs\n"
580  " Using first two entries\n\n");
581 
582  }
583 
584  ax = xmax - xmin;
585  ay = ymax-ymin;
586  vx = ax/Nx;
587  vy = ay/Ny;
588  field_type = rectangle;
589  Area = vx*vy;
590  }
591  }
592  else if (tmp_field.size() != 4) {
593  egsWarning(
594  "\n\n*** Wrong/missing 'scoring circle' input "
595  "setting it to 10 cm diameter field at origin!\n\n");
596  m_midpoint = EGS_Vector();
597  m_R = 5;
598  m_R2 = 25;
599  field_type = circle;
600  Area = M_PI * m_R2;
601  }
602  else {
603  vector<EGS_Float> tmp_normal;
604  int err1 = pScoringInput->getInput("scoring plane normal",tmp_normal);
605  if (err1 || tmp_normal.size() != 3) {
606  egsWarning(
607  "\n\n*** Wrong/missing 'scoring plane normal' input. "
608  "Set along positive z-axis\n\n");
609  m_normal = EGS_Vector(0,0,1);// Default along positive z-axis
610  }
611  else {
612  m_normal = EGS_Vector(tmp_normal[0],tmp_normal[1],
613  tmp_normal[2]);
614  m_normal.normalize();
615  }
616  m_midpoint = EGS_Vector(tmp_field[0],tmp_field[1],
617  tmp_field[2]);
618  m_R = tmp_field[3];
619  m_R2 = tmp_field[3]*tmp_field[3];
620  field_type = circle;
621  Area = M_PI * m_R2;
622  }
623 
624  m_d = m_normal*m_midpoint;
625 }
626 
628  char buf[128];
629  sprintf(buf,"\nPlanar %s fluence scoring\n",particle_name.c_str());
630  description = buf;
631  description += "================================\n";
632 
633  description += " - scoring field normal = ";
634  sprintf(buf,"(%g %g %g)\n",m_normal.x,m_normal.y,m_normal.z);
635  description += buf;
636  description += " - scoring field center = ";
637  sprintf(buf,"(%g %g %g)\n",m_midpoint.x,m_midpoint.y,m_midpoint.z);
638  description += buf;
639  if (field_type == circle) {
640  description += " - scoring field radius = ";
641  sprintf(buf,"%g cm\n",m_R);
642  description += buf;
643  }
644  else if (field_type == rectangle) {
645  description += " - scoring field = ";
646  sprintf(buf,"%g cm X %g cm\n",ax,ay);
647  description += buf;
648  description += " - scoring field resolution = ";
649  sprintf(buf,"%d X %d\n",Nx,Ny);
650  description += buf;
651  }
652  description += " - scoring field distance from origin = ";
653  sprintf(buf,"%g cm\n",m_d);
654  description += buf;
655 
656  description +=" - scoring from region(s): ";
657 
658  EGS_FluenceScoring::describeMe();
659 
660 }
661 
662 void EGS_PlanarFluence::score(const EGS_Particle &p, const int &ivoxel) {
663  EGS_Float up = p.u*m_normal, aup = fabs(up);
664  /**********************************************************
665  Prevent large weights from particles very close to plane.
666  ***********************************************************/
667  if (aup < 0.08) {
668  aup = 0.0871557; // Limit incident angle to 85 degrees
669  }
670  EGS_Float e = p.q ? p.E - app->getRM() : p.E;
671  if (flu_s) {
672  e = log(e); // log scale
673  }
674  EGS_Float ae;
675  int je;
676  EGS_Float auxp = p.wt/aup;
677  /* Score total fluence in corresponding pixel */
678  fluT->score(ivoxel,auxp);
679  if (score_primaries && !p.latch) {
680  fluT_p->score(ivoxel,auxp);
681  }
682  /* Score differential fluence in corresponding pixel */
683  if (score_spe && e > flu_xmin && e <= flu_xmax) {
684  ae = flu_a*e + flu_b;
685  /******************************************************
686  Algorithm assigns E in [Ei,Ei+1), hence push events
687  with E = Emax into last bin (bias?) during scoring.
688  Alternatively add extra bin for E = Emax cases.
689  Which approach is correct?
690  ******************************************************/
691  je = min((int)ae,flu_nbin-1);//je = (int) ae;
692  /******************************************************/
693  if (ivoxel < 0 || ivoxel > Nx*Ny) {
694  egsFatal("\n-> Scoring out of bounds, ivoxel = %d\n",ivoxel);
695  }
696  EGS_ScoringArray *aux = flu[ivoxel];
697  if (je < 0 || je >= flu_nbin) {
698  egsFatal("\n-> Scoring out of bounds, ibin = %d ae = %g E = %g MeV\n",je,ae,e);
699  }
700  aux->score(je,auxp);
701  if (score_primaries && !p.latch) {
702  flu_p[ivoxel]->score(je,auxp);
703  }
704  }
705 }
706 
707 int EGS_PlanarFluence::hitsField(const EGS_Particle &p, EGS_Float *dist) {
708  if (field_type==circle) {
709  EGS_Float xp = p.x*m_normal, up = p.u*m_normal;
710  if ((up > 0 && m_d > xp) ||
711  (up < 0 && m_d < xp)) {
712  EGS_Float t = (m_d - xp)/up;
713  EGS_Vector x1(p.x + p.u*t - m_midpoint);
714  if (dist) {
715  *dist = t;
716  }
717  return x1.length2() < m_R*m_R ? 0:-1;
718  }
719  return -1;
720  }
721  else if (field_type == rectangle) {
722  EGS_Float xp = p.x*m_normal, up = p.u*m_normal;
723  if ((up > 0 && m_d > xp) || (up < 0 && m_d < xp)) {
724  EGS_Float t = (m_d - xp)/up; // distance to plane along u
725  EGS_Vector x1(p.x + p.u*t - m_midpoint);// vector on scoring plane
726  EGS_Float xcomp = x1*ux;// x-direction component
727  EGS_Float ycomp = x1*uy;// y-direction component
728  xcomp = 2*xcomp + ax;
729  ycomp = 2*ycomp + ay;
730  if (xcomp > 0 && xcomp < 2*ax &&
731  ycomp > 0 && ycomp < 2*ay) {
732  int i = int(xcomp/(2*vx)),
733  j = int(ycomp/(2*vy)),
734  k = i + j*Nx;
735  if (dist) {
736  *dist = t;
737  }
738  return k;
739  }
740  }
741  return -1;
742  }
743  else {
744  return -1;
745  }
746 }
747 
748 void EGS_PlanarFluence::ouputPlanarFluence(EGS_ScoringArray *fT, const double &norma) {
749  double fe,dfe,dfer;
750  int count = 0;
751  int ix_digits = getDigits(Nx);
752  int iy_digits = getDigits(Ny);
753  int xy_digits = getDigits(Nx*Ny);
754 
755  if (field_type == circle) {
756  egsInformation("\n pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n"
757  "-----------------------------------------------------\n");
758  }
759  else {
760  egsInformation("\n %*s %*s pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n"
761  "-----------------------------------------------------\n",
762  iy_digits,"iy",ix_digits,"ix",&count);
763  }
764  if (field_type == circle) {
765  int k = 0;
766  egsInformation(" %*d ",xy_digits,k);
767  fT->currentResult(k,fe,dfe);
768  if (fe > 0) {
769  dfer = 100*dfe/fe;
770  }
771  else {
772  dfer = 100;
773  }
774  egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer);
775  }
776  else {
777  for (int j=0; j<Ny; j++) {
778  for (int i=0; i<Nx; i++) {
779  int k = i + j*Nx;
780  egsInformation(" %*d %*d %*d ",iy_digits,j,ix_digits,i,xy_digits,k);
781  fT->currentResult(k,fe,dfe);
782  if (fe > 0) {
783  dfer = 100*dfe/fe;
784  }
785  else {
786  dfer = 100;
787  }
788  egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer);
789  }
790  }
791  }
792 }
793 
794 void EGS_PlanarFluence::ouputResults() {
795 
796  EGS_Float src_norm = 1.0, // default to number of histories in this run
797  Fsrc = app->getFluence();// Fluence or number of primary histories
798  egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n",
799  current_ncase, Fsrc);
800 
801  if (Fsrc) {
802  src_norm = Fsrc/current_ncase; // fluence or primary histories per histories run
803  }
804 
805  string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2";
806  string src_type = app->sourceType();
807  if (src_type == "EGS_BeamSource") {
808  normLabel = "primary history";
809  egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n",
810  src_type.c_str(), src_norm);
811  }
812  else if (src_type == "EGS_CollimatedSource" ||
813  (src_type == "EGS_ParallelBeam" && src_norm != 1)) {
814  egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n",
815  src_type.c_str(), src_norm);
816  }
817  else {
818  egsInformation("\n\n %s normalization = %g (histories per particle)\n\n",
819  src_type.c_str(), src_norm);
820 
821  }
822 
823 
824  double norm = 1.0/src_norm; //per fluence or particle depending on source
825  norm /= Area; //per unit area
826  norm *= norm_u; // times user-requested normalization
827 
828  //egsInformation(" Normalization = %g\n",norm);
829 
830  egsInformation("\n\n Integral fluence\n"
831  " ================\n\n");
832 
833  egsInformation("\n\n Total %s fluence\n", particle_name.c_str());
834  ouputPlanarFluence(fluT, norm);
835 
836  if (score_primaries) {
837  egsInformation("\n\n Primary fluence\n");
838  ouputPlanarFluence(fluT_p, norm);
839  }
840 
841  if (score_spe) {
842  norm *= flu_a;//per unit bin width
843  string suffix = "_" + particle_name + ".agr";
844  string spe_name = app->constructIOFileName(suffix.c_str(),true);
845  ofstream spe_output(spe_name.c_str(),ios::out);
846  if (!spe_output) {
847  egsFatal("\n EGS_PlanarFluence: Error: Failed to open file %s\n",spe_name.c_str());
848  exit(1);
849  }
850 
851  spe_output << "# " << particle_name.c_str() << " fluence \n";
852  spe_output << "# \n";
853  spe_output << "@ legend 0.2, 0.8\n";
854  spe_output << "@ legend box linestyle 0\n";
855  spe_output << "@ legend font 4\n";
856  spe_output << "@ xaxis label \"energy / MeV\"\n";
857  spe_output << "@ xaxis label char size 1.560000\n";
858  spe_output << "@ xaxis label font 4\n";
859  spe_output << "@ xaxis ticklabel font 4\n";
860  spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n";
861  spe_output << "@ yaxis label char size 1.560000\n";
862  spe_output << "@ yaxis label font 4\n";
863  spe_output << "@ yaxis ticklabel font 4\n";
864  spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n";
865  spe_output << "@ title font 4\n";
866  spe_output << "@ title size 1.500000\n";
867  spe_output << "@ subtitle \"for each scoring region\"\n";
868  spe_output << "@ subtitle font 4\n";
869  spe_output << "@ subtitle size 1.000000\n";
870 
871  egsInformation("\n\n Differential fluence\n"
872  " ====================\n\n");
873 
874  int i_graph = 0;
875  double fe,dfe;
876  for (int j=0; j<Ny; j++) {
877  for (int i=0; i<Nx; i++) {
878  int k = i + j*Nx;
879  if (verbose) {
880  egsInformation("\nPixel # %d :",k);
881  }
882  spe_output<<"@ s" << i_graph <<" errorbar linestyle 0\n";
883  spe_output<<"@ s" << i_graph <<" legend \""<<
884  "Voxel # " << k <<"\"\n";
885  spe_output<<"@target G0.S"<< i_graph <<"\n";
886  spe_output<<"@type xydy\n";
887  if (verbose) {
888  egsInformation("\n\n Total \n\n");
889  egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n"
890  "---------------------------------------------\n");
891  }
892  for (int l=0; l<flu_nbin; l++) {
893  flu[k]->currentResult(l,fe,dfe);
894  EGS_Float e = (l+0.5-flu_b)/flu_a;
895  if (flu_s) {
896  e = exp(e);
897  }
898  spe_output<<e<<" "<<fe *norm<<" "<<dfe *norm<< "\n";
899  if (verbose) egsInformation("%11.6f %14.6e %14.6e\n",
900  e,fe*norm,dfe*norm);
901  }
902  spe_output << "&\n";
903 
904  if (score_primaries) {
905  if (verbose) {
906  egsInformation("\n\n Primary\n\n");
907  egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n"
908  "---------------------------------------------\n");
909  }
910  spe_output<<"@ s" << ++i_graph <<" errorbar linestyle 0\n";
911  spe_output<<"@ s" << i_graph <<" legend \""<<
912  "Voxel # " << k <<" (primary)\"\n";
913  spe_output<<"@target G0.S"<< i_graph <<"\n";
914  spe_output<<"@type xydy\n";
915  for (int l=0; l<flu_nbin; l++) {
916  flu_p[k]->currentResult(l,fe,dfe);
917  EGS_Float e = (l+0.5-flu_b)/flu_a;
918  if (flu_s) {
919  e = exp(e);
920  }
921  spe_output<<e<<" "<<fe *norm<<" "<<dfe *norm<< "\n";
922  if (verbose) egsInformation("%11.6f %14.6e %14.6e\n",
923  e,fe*norm,dfe*norm);
924  }
925  spe_output << "&\n";
926  }
927  i_graph++;
928  }
929  }
930  spe_output.close();
931  }
932 
933 }
934 
935 void EGS_PlanarFluence::reportResults() {
936  egsInformation("\nFluence Scoring (%s)\n",name.c_str());
937  //EGS_Float m_tot = m_fluor+ m_compt + m_ray + m_multiple; char per = '%';
938  egsInformation("======================================================\n");
939  egsInformation(" Total %ss reaching field: %g\n",particle_name.c_str(),m_tot);
940  egsInformation(" Primary %ss reaching field: %g\n",particle_name.c_str(),m_primary);
941  //egsInformation(" Non-primary photons reaching field: %g\n",m_tot);
942  egsInformation("======================================================\n");
943 
944  ouputResults();
945 
946 }
947 
948 bool EGS_PlanarFluence::storeState(ostream &data) const {
949  if (!egsStoreI64(data,current_ncase)) {
950  return false;
951  }
952  data << endl;
953  //data << m_primary << " " << m_ray << " " << m_compt << " " << m_fluor << " " << m_multiple;
954  data << m_tot << " " << m_primary;
955  data << endl;
956  if (!data.good()) {
957  return false;
958  }
959 
960  if (!fluT->storeState(data)) {
961  return false;
962  }
963 
964  if (score_spe) {
965  for (int j=0; j<Nx*Ny; j++) {
966  if (!flu[j]->storeState(data)) {
967  return false;
968  }
969  }
970  }
971 
972  if (score_primaries) {
973  if (!fluT_p->storeState(data)) {
974  return false;
975  }
976  if (score_spe) {
977  for (int j=0; j<Nx*Ny; j++) {
978  if (!flu_p[j]->storeState(data)) {
979  return false;
980  }
981  }
982  }
983  }
984 
985  return true;
986 }
987 
988 bool EGS_PlanarFluence::setState(istream &data) {
989  if (!egsGetI64(data,current_ncase)) {
990  return false;
991  }
992  //data >> m_primary >> m_ray >> m_compt >> m_fluor >> m_multiple;
993  data >> m_tot >> m_primary;
994  if (!data.good()) {
995  return false;
996  }
997 
998  if (!fluT->setState(data)) {
999  return false;
1000  }
1001 
1002  if (score_spe) {
1003  for (int j=0; j<Nx*Ny; j++) {
1004  if (!flu[j]->setState(data)) {
1005  return false;
1006  }
1007  }
1008  }
1009 
1010  if (score_primaries) {
1011 
1012  if (!fluT_p->setState(data)) {
1013  return false;
1014  }
1015 
1016  if (score_spe) {
1017  for (int j=0; j<Nx*Ny; j++) {
1018  if (!flu_p[j]->setState(data)) {
1019  return false;
1020  }
1021  }
1022  }
1023 
1024  }
1025  return true;
1026 }
1027 
1028 bool EGS_PlanarFluence::addState(istream &data) {
1029  EGS_I64 tmp_case;
1030  if (!egsGetI64(data,tmp_case)) {
1031  return false;
1032  }
1033  current_ncase += tmp_case;
1034  /* individual contributions */
1035  //EGS_Float tmp_primary, tmp_fluor, tmp_compt, tmp_ray, tmp_multiple;
1036  //data >> tmp_primary >> tmp_ray >> tmp_compt >> tmp_fluor >> tmp_multiple;
1037  EGS_Float tmp_tot, tmp_primary;
1038  data >> tmp_tot >> tmp_primary;
1039  if (!data.good()) {
1040  return false;
1041  }
1042  m_primary += tmp_primary;
1043  m_tot += tmp_tot;
1044  //m_primary += tmp_primary;m_ray += tmp_ray;m_compt += tmp_compt;
1045  //m_fluor += tmp_fluor;m_multiple += tmp_multiple;
1046  /* fluence objects */
1047 
1048  EGS_ScoringArray tgT(Nx*Ny);
1049  if (!tgT.setState(data)) {
1050  return false;
1051  }
1052  (*fluT) += tgT;
1053 
1054  if (score_spe) {
1055  EGS_ScoringArray tg(flu_nbin);
1056  for (int j=0; j<Nx*Ny; j++) {
1057  if (!tg.setState(data)) {
1058  return false;
1059  }
1060  (*flu[j]) += tg;
1061  }
1062  }
1063 
1064  if (score_primaries) {
1065 
1066  EGS_ScoringArray tgT_p(Nx*Ny);
1067  if (!tgT_p.setState(data)) {
1068  return false;
1069  }
1070  (*fluT_p) += tgT_p;
1071 
1072  if (score_spe) {
1073  EGS_ScoringArray tg_p(flu_nbin);
1074  for (int j=0; j<Nx*Ny; j++) {
1075  if (!tg_p.setState(data)) {
1076  return false;
1077  }
1078  (*flu_p[j]) += tg_p;
1079  }
1080  }
1081  }
1082 
1083  return true;
1084 }
1085 
1086 
1087 /**********************************************
1088  * Class EGS_VolumetricFluence Implementation *
1089 ***********************************************/
1090 
1091 
1093  EGS_FluenceScoring(Name,f), flu_stpwr(stpwr)
1094 #ifdef DEBUG
1095  ,one_bin(0), multi_bin(0), max_step(-100.0), n_step_bins(10000)
1096 #endif
1097 {
1098  otype = "EGS_VolumetricFluence";
1099 }
1100 
1103 
1104  if (flu) {
1105  for (int j=0; j<n_scoring_regions; j++) {
1106  delete flu[j];
1107  }
1108  }
1109  if (flu_p) {
1110  for (int j=0; j<n_scoring_regions; j++) {
1111  delete flu_p[j];
1112  }
1113  }
1114 
1115 #ifdef DEBUG
1116  if (binDist) {
1117  delete binDist;
1118  }
1119  if (scoring_charge) {
1120  delete stepDist;
1121  delete relStepDiff;
1122  }
1123 #endif
1124 
1125 }
1126 
1127 void EGS_VolumetricFluence::setApplication(EGS_Application *App) {
1128 
1130 
1131  if (!app) {
1132  return;
1133  }
1134 
1135  /***********************************************************
1136  Defaults to charge of application source. This applies most
1137  of the time, except for bremsstrahlung targets and multiple
1138  particle sources such as radiactive sources.
1139  Can be changed via 'source particle' input.
1140  *************************************************************/
1141  if (source_charge == unknown) {
1142  source_charge = (ParticleType)app->sourceCharge();
1143  }
1144 
1145  // Get the number of regions in the geometry.
1146  nreg = app->getnRegions();
1147 
1148  /* Initialize arrays with defaults */
1149  for (int j=0; j<nreg; j++) {
1150  is_sensitive.push_back(false);
1151  is_source.push_back(false);
1152  volume.push_back(vol_list[0]);// set to either 1.0 or first volume entered
1153  }
1154 
1155  /* Flag scoring and source regions*/
1156  setUpRegionFlags();
1157 
1158  /* Update arrays with user inputs */
1159  for (int i = 0; i < n_scoring_regions; i++) {
1160  if (i < vol_list.size()) {
1161  volume[f_region[i]] = vol_list[i];
1162  }
1163  }
1164 
1165  /* Setup fluence scoring arrays */
1166  fluT = new EGS_ScoringArray(nreg);
1167  if (score_spe) {
1168  flu = new EGS_ScoringArray* [nreg];
1169  for (int j = 0; j < nreg; j++) {
1170  if (is_sensitive[j]) {
1171  flu[j] = new EGS_ScoringArray(flu_nbin);
1172  }
1173  }
1174  }
1175  if (score_primaries) {
1176  fluT_p = new EGS_ScoringArray(nreg);
1177  if (score_spe) {
1178  flu_p = new EGS_ScoringArray* [nreg];
1179  for (int j = 0; j < nreg; j++) {
1180  if (is_sensitive[j]) {
1181  flu_p[j] = new EGS_ScoringArray(flu_nbin);
1182  }
1183  }
1184  }
1185  }
1186 #ifdef DEBUG
1187  binDist = new EGS_ScoringArray(flu_nbin);
1188  int imed_max_range;
1189 #endif
1190 
1191  EGS_Float flu_Emin = flu_s ? exp(flu_xmin) : flu_xmin,
1192  flu_Emax = flu_s ? exp(flu_xmax) : flu_xmax;
1193  EGS_Float bw = flu_s ?(log(flu_Emax / flu_Emin))/flu_nbin :
1194  (flu_Emax - flu_Emin) /flu_nbin;
1195  flu_a_i = bw;
1196  flu_a = 1.0/bw;
1197 
1198  /* Initialize data required to score charged particle fluence */
1199  if (scoring_charge) {
1200  EGS_Float expbw;
1201  /* Pre-calculated values for faster evaluation on log scale */
1202  if (flu_s) {
1203  expbw = exp(bw); // => (Emax/Emin)^(1/nbin)
1204  r_const = 1/(expbw-1);
1205  DE = new EGS_Float [flu_nbin];
1206  a_const = new EGS_Float [flu_nbin];
1207  for (int i = 0; i < flu_nbin; i++) {
1208  DE[i] = flu_Emin*pow(expbw,i)*(expbw-1);
1209  a_const[i] = 1/flu_Emin*pow(1/expbw,i);
1210  }
1211  }
1212  /* Do not score below ECUT - PRM */
1213  if (flu_Emin < app->getEcut() - app->getRM()) {
1214  flu_Emin = app->getEcut() - app->getRM() ;
1215  /* Decrease number of bins, preserve bin width */
1216  flu_nbin = flu_s ?
1217  ceil((log(flu_Emax / flu_Emin))/bw) :
1218  ceil((flu_Emax - flu_Emin) /bw);
1219  }
1220 
1221  /* Pre-calculated values for faster 1/stpwr evaluation */
1222  if (flu_stpwr) {
1223 
1224  int n_media = app->getnMedia();
1225 
1226  EGS_Float lnE, lnEmin, lnEmax, lnEmid;
1227 
1228  i_dedx = new EGS_Interpolator [n_media];// stp powers
1229  dedx_i = new EGS_Interpolator [n_media];// its inverse
1230  for (int j = 0; j < n_media; j++) {
1231  /* Gets charged partcle stopping powers based on the scoring charge */
1232  i_dedx[j] = *(app->getDEDX(j, scoring_charge));
1233  EGS_Float Emin = i_dedx[j].getXmin();
1234  EGS_Float Emax = i_dedx[j].getXmax();
1235  int n = 1 + i_dedx[j].getIndex(Emax);// getIndex returns lower bin limit?
1236  EGS_Float bwidth = (Emax - Emin)/n;
1237 #ifdef DEBUG
1238  egsInformation("---> stpwr in %s : Emin=%g Emax=%g n=%d bw=%g\n",
1239  app->getMediumName(j), exp(Emin), exp(Emax), n, bwidth);
1240 #endif
1241  int nbins = n + 1;
1242  EGS_Float spwr_i[nbins];
1243  for (int k = 0; k < nbins; k++) {
1244  spwr_i[k] = 1.0 / i_dedx[j].interpolate(Emin+k*bwidth);
1245  }
1246  dedx_i[j].initialize(nbins, Emin, Emax, spwr_i);
1247 #ifdef DEBUG
1248  Emin = dedx_i[j].getXmin();
1249  Emax = dedx_i[j].getXmax();
1250  n = 1 + dedx_i[j].getIndex(Emax);// getIndex returns lower bin limit?
1251  bwidth = (Emax - Emin)/n;
1252  egsInformation("---> 1/stpwr in %s : lnEmin=%g lnEmax=%g n=%d bw=%g index(Emax)=%d\n",
1253  app->getMediumName(j), Emin, Emax, n, bwidth, dedx_i[j].getIndexFast(Emax));
1254  for (int k = 0; k < nbins; k++) {
1255  egsInformation(" L(%g MeV) = %g MeV/cm 1/L = %g cm/MeV\n",
1256  exp(Emin+k*bwidth),
1257  i_dedx[j].interpolate(Emin + k*bwidth),
1258  dedx_i[j].interpolate(Emin + k*bwidth));
1259  }
1260 #endif
1261  }
1262 
1263  /* Determine stpwr at middle of each fluence scoring bin */
1264  lnEmin = flu_s ? log(0.5*flu_Emin*(expbw+1)) : 0;
1265  Lmid_i = new EGS_Float [flu_nbin*n_media];
1266  for (int j = 0; j < n_media; j++) {
1267 #ifdef DEBUG
1268  EGS_Float med_max_step = 0, logEmax, logEmin;
1269 #endif
1270  for (int i = 0; i < flu_nbin; i++) {
1271  lnEmid = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*(i+0.5));
1272  Lmid_i[i+j*flu_nbin] = 1/i_dedx[j].interpolate(lnEmid);
1273  //egsInformation(" 1/L(%g MeV) = %g cm/MeV\n",exp(lnEmid),Lmid_i[i+j*flu_nbin]);
1274 #ifdef DEBUG
1275  // Set max_step to maximum range
1276  if (i > 0) {
1277  logEmin = flu_s ? lnEmin + (i-1)*bw : log(flu_Emin+bw*(i-1));
1278  logEmax = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*i);
1279  if (i_dedx[j].interpolate(logEmax) < i_dedx[j].interpolate(logEmin)) {
1280  med_max_step += 1.02*(exp(logEmax) - exp(logEmin))/i_dedx[j].interpolate(logEmax);
1281  }
1282  else {
1283  med_max_step += 1.02*(exp(logEmax) - exp(logEmin))*i_dedx[j].interpolate(logEmin);
1284  }
1285  }
1286 #endif
1287  }
1288 #ifdef DEBUG
1289  if (med_max_step > max_step) {
1290  max_step = med_max_step;
1291  imed_max_range = j;
1292  }
1293 #endif
1294  }
1295  }
1296  }
1297 
1298 #ifdef DEBUG
1299  EGS_Float RCSDA = max_step;
1300  //max_step /= 4.0; // Set to a quarter of the CSDA range
1301  //if ( max_step > 1.0 ) max_step = 1.0;
1302  max_step = 1.0;// reset to 1 cm
1303  step_a = n_step_bins/max_step;
1304  step_b = 0;
1305  relStepDiff = new EGS_ScoringArray(n_step_bins);
1306  stepDist = new EGS_ScoringArray(n_step_bins);
1307  eCases = 0;
1308  egsInformation("\n===> RCSDA(%s) = %g cm for Emax = %g MeV, max_step = %g cm bin width = %g cm\n",
1309  app->getMediumName(imed_max_range), RCSDA,
1310  flu_s ? exp(flu_Emax) : flu_Emax, max_step, 1.0/step_a);
1311 #endif
1312  describeMe();
1313 }
1314 
1315 /*
1316  Takes user inputs and sets up simulation parameters not requiring
1317  invoking an application method. This is later done in setApplication.
1318 */
1319 void EGS_VolumetricFluence::initScoring(EGS_Input *inp) {
1320 
1321  EGS_Input *vScoringInput;
1322 
1323  if (!inp) {
1324  egsFatal("AO type %s: null input?\n",otype.c_str());
1325  return;
1326  }
1327  else {
1328  vScoringInput = inp->takeInputItem("volumetric scoring");
1329  if (! vScoringInput) {
1330  egsFatal("AO type %s: Missing volumetric scoring input?\n",otype.c_str());
1331  return;
1332  }
1333  }
1334 
1335  EGS_FluenceScoring::initScoring(inp);// common inputs
1336 
1337  /* Volumetric scoring by default turned off */
1338  score_in_all_regions = false;
1339  EGS_FluenceScoring::getSensitiveRegions(vScoringInput);
1340 
1341  /* get region volume[s] in g/cm3 */
1342  vector <EGS_Float> v_in;
1343  vScoringInput->getInput("volumes",v_in);
1344 
1345  //================================================
1346  // Check if one volume for each group requested.
1347  // Otherwise pass volumes read and if there is
1348  // a mismatch, then the first volume element
1349  // or 1g/cm3 will be used.
1350  //=================================================
1351  if (! score_in_all_regions && v_in.size() == f_start.size()) {
1352  // groups of regions with same volume
1353  for (int i=0; i<f_start.size(); i++) {
1354  int i_r = f_start[i], f_r = f_stop[i];
1355  for (int ireg=i_r; ireg<=f_r; ireg++) {
1356  vol_list.push_back(v_in[i]);
1357  }
1358  }
1359  }
1360  else if (v_in.size()) {
1361  vol_list = v_in;
1362  }
1363  else {
1364  vol_list.push_back(1.0);
1365  }
1366 
1367  /* Initialize data required to score charged particle fluence */
1368  if (scoring_charge) {
1369  vector<string> method;
1370  method.push_back("flurz");
1371  method.push_back("stpwr"); // 3rd order
1372  method.push_back("stpwrO5"); // 5th order
1373  flu_stpwr = eFluType(vScoringInput->getInput("method",method,1));
1374 
1375  }
1376 
1377 }
1378 
1380  char buf[128];
1381  sprintf(buf,"\nVolumetric %s fluence scoring\n",particle_name.c_str());
1382  description = buf;
1383  description += "===================================\n";
1384 
1385  description +=" - scoring in region(s): ";
1386 
1387  EGS_FluenceScoring::describeMe();
1388 
1389  if (scoring_charge) {
1390  if (flu_stpwr) {
1391  if (flu_stpwr == stpwr) {
1392  description += " O(eps^3) approach: accounts for change in stpwr\n";
1393  description += " along the step with eps=edep/Emid\n";
1394  }
1395  else if (flu_stpwr == stpwrO5) {
1396  description += " O(eps^5) approach: accounts for change in stpwr\n";
1397  description += " along the step with eps=edep/Emid\n";
1398  }
1399  }
1400  else {
1401  description += " Fluence calculated a-la-FLURZ using Lave=EDEP/TVSTEP.\n";
1402  }
1403  }
1404 
1405  if (norm_u != 1.0) {
1406  description += "\n - Non-unity user-requested normalization = ";
1407  sprintf(buf,"%g\n",norm_u);
1408  description += buf;
1409  }
1410 
1411 }
1412 
1413 void EGS_VolumetricFluence::ouputVolumetricFluence(EGS_ScoringArray *fT, const double &norma) {
1414  double fe,dfe,dfer;
1415  int count = 0;
1416  int ir_digits = getDigits(nreg);
1417 
1418  //egsInformation("-> norma = %10.4le\n", norma);
1419 
1420  egsInformation("\n region# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n"
1421  "-----------------------------------------------------\n");
1422  for (int k=0; k<nreg; k++) {
1423  if (!is_sensitive[k]) {
1424  continue;
1425  }
1426  double norm = norma/volume[k]; //per volume
1427  egsInformation(" %*d ",ir_digits,k);
1428  fT->currentResult(k,fe,dfe);
1429  if (fe > 0) {
1430  dfer = 100*dfe/fe;
1431  }
1432  else {
1433  dfer = 100;
1434  }
1435  //egsInformation(" %10.4le +/- %10.4le [%-7.3lf%] %10.4le\n",fe,dfe,dfer,norm);
1436  egsInformation(" %10.4le +/- %10.4le [%-7.3lf%]\n",fe*norm,dfe*norm,dfer);
1437  }
1438 }
1439 
1440 void EGS_VolumetricFluence::ouputResults() {
1441 
1442 
1443  EGS_Float src_norm = 1.0, // default to number of histories in this run
1444  Fsrc = app->getFluence();// Fluence or number of primary histories
1445  egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n",
1446  current_ncase, Fsrc);
1447 
1448  if (Fsrc) {
1449  src_norm = Fsrc/current_ncase; // fluence or primary histories per histories run
1450  }
1451 
1452  string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2";
1453  string src_type = app->sourceType();
1454  if (src_type == "EGS_BeamSource") {
1455  normLabel = "primary history";
1456  egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n",
1457  src_type.c_str(), src_norm);
1458  }
1459  else if (src_type == "EGS_CollimatedSource" ||
1460  (src_type == "EGS_ParallelBeam" && src_norm != 1)) {
1461  egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n",
1462  src_type.c_str(), src_norm);
1463  }
1464  else {
1465  egsInformation("\n\n %s normalization = %g (histories per particle)\n\n",
1466  src_type.c_str(), src_norm);
1467 
1468  }
1469 #ifdef DEBUG
1470  EGS_Float fbins, d_fbins;
1471  egsInformation("\nNumber of covered bins distribution\n"
1472  "--------------------------------------\n");
1473 
1474  int tot_bins = one_bin + multi_bin;
1475  egsInformation("\none_bin = %d [%-7.3lf\%] multi_bin = %d [%-7.3lf\%]\n",
1476  one_bin, 100.0*one_bin/tot_bins, multi_bin,100.0*multi_bin/tot_bins);
1477 
1478  egsInformation("\n # bins freq unc percentage \n");
1479  int bdigits = getDigits(flu_nbin);
1480  for (int i=0; i<flu_nbin; i++) {
1481  binDist->currentResult(i,fbins, d_fbins);
1482  if (fbins) {
1483  d_fbins = 100.0*d_fbins/fbins;
1484  fbins = current_ncase*fbins;
1485  egsInformation(" %*d %11.2f [%-7.3lf\%] %11.2f \%\n",
1486  bdigits, i+1, fbins, d_fbins, 100.*fbins/tot_bins);
1487  }
1488  }
1489 
1490  if (scoring_charge) {
1491  egsInformation("\nDistribution of ratio between computed and taken steps\n"
1492  "------------------------------------------------------\n"
1493  " (Omitted bins with differences less than 0.01\%)\n");
1494  /*
1495  //egsInformation(" Relative step difference: %8.4f%% +/- %8.4f%% [%8.4lf%%]\n",
1496  //egsInformation(" Relative step ratio: %10.4le +/- %10.4le [%8.4lf%%]\n",
1497  */
1498 
1499  EGS_Float step_diff, step_diff_std, step_diff_err, stepMid;
1500  EGS_Float f_scores, f_scores_std;
1501  int idigits = getDigits(eCases);
1502  egsInformation("\n step / cm # scores ratio rel unc\n"
1503  "---------------------------------------------------------\n");
1504  for (int i=0; i < n_step_bins; i++) {
1505  stepDist->currentResult(i, f_scores, f_scores_std);
1506  if (f_scores > 0) {
1507  stepMid = (i + 0.5 - step_b)/step_a;
1508  relStepDiff->currentResult(i, step_diff, step_diff_std);
1509  if (step_diff > 0) {
1510  step_diff_err = 100*step_diff_std/step_diff;
1511  }
1512  else {
1513  step_diff_err = 100;
1514  }
1515  if (abs(step_diff/f_scores - 1.0) > 0.0001D+0)
1516  egsInformation(" %10.4le %*d %10.4le [%8.4lf\%]\n",
1517  stepMid, idigits, int(f_scores*current_ncase), step_diff/f_scores, step_diff_err);
1518  }
1519  }
1520 
1521  }
1522 
1523 #endif
1524 
1525  EGS_Float norm = 1.0/src_norm; // per particle or fluence
1526  norm *= norm_u; // user-requested normalization
1527 
1528  egsInformation("\n\n Integral fluence output\n"
1529  " =======================\n\n");
1530 
1531  egsInformation("\n\n Total %s fluence\n", particle_name.c_str());
1532  ouputVolumetricFluence(fluT, norm);
1533 
1534  if (score_primaries) {
1535  egsInformation("\n\n Primary fluence\n");
1536  ouputVolumetricFluence(fluT_p, norm);
1537  }
1538 
1539  if (verbose) {
1540  egsInformation("\nbw = %g nbins = %d\n", flu_a_i, flu_nbin);
1541  }
1542 
1543  if (score_spe) {
1544  string suffix = "_" + particle_name + ".agr";
1545  string spe_name = app->constructIOFileName(suffix.c_str(),true);
1546  ofstream spe_output(spe_name.c_str(),ios::out);
1547  if (!spe_output) {
1548  egsFatal("\n EGS_VolumetricFluence: Error: Failed to open file %s\n",spe_name.c_str());
1549  exit(1);
1550  }
1551 
1552  spe_output << "# Volumetric " << particle_name.c_str() << " fluence \n";
1553  spe_output << "# \n";
1554  spe_output << "@ legend 0.2, 0.8\n";
1555  spe_output << "@ legend box linestyle 0\n";
1556  spe_output << "@ legend font 4\n";
1557  spe_output << "@ xaxis label \"energy / MeV\"\n";
1558  spe_output << "@ xaxis label char size 1.560000\n";
1559  spe_output << "@ xaxis label font 4\n";
1560  spe_output << "@ xaxis ticklabel font 4\n";
1561  if (src_norm == 1 || normLabel == "primary history") {
1562  spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n";
1563  }
1564  else {
1565  spe_output << "@ yaxis label \"fluence / MeV\\S-1\"\n";
1566  }
1567  spe_output << "@ yaxis label char size 1.560000\n";
1568  spe_output << "@ yaxis label font 4\n";
1569  spe_output << "@ yaxis ticklabel font 4\n";
1570  spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n";
1571  spe_output << "@ title font 4\n";
1572  spe_output << "@ title size 1.500000\n";
1573  spe_output << "@ subtitle \"for each scoring region\"\n";
1574  spe_output << "@ subtitle font 4\n";
1575  spe_output << "@ subtitle size 1.000000\n";
1576 
1577  if (verbose)
1578  egsInformation("\n\n Differential fluence output\n"
1579  " =============================\n\n");
1580  int i_graph = 0;
1581  double fe, dfe;
1582  norm *= scoring_charge ? 1 : flu_a;//per bin width <- implicit for charged particles!
1583  // Loop through the number of regions in the geometry.
1584  for (int j = 0; j < nreg; j++) {
1585 
1586  if (!is_sensitive[j]) {
1587  continue;
1588  }
1589 
1590  double norma = norm/volume[j]; //per volume
1591 
1592  egsInformation("region # %d : ",j);
1593 
1594  if (verbose) {
1595  egsInformation("Volume[%d] = %g", j, volume[j]);
1596  egsInformation(" Normalization = Ncase/Fsrc/V = %g\n",norma);
1597  }
1598  else {
1599  egsInformation(" See Grace plot file %s\n", spe_name.c_str());
1600  }
1601 
1602  if (verbose) {
1603  egsInformation("\nTotal fluence:\n");
1604  }
1605  spe_output<<"@ s"<< i_graph <<" errorbar linestyle 0\n";
1606  spe_output<<"@ s"<< i_graph <<" legend \""<< "total (ir # " << j <<")\"\n";
1607  spe_output<<"@target G0.S"<< i_graph <<"\n";
1608  spe_output<<"@type xydy\n";
1609  if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n"
1610  "---------------------------------------------------\n");
1611  for (int i=0; i<flu_nbin; i++) {
1612  flu[j]->currentResult(i,fe,dfe);
1613  EGS_Float e = (i+0.5-flu_b)/flu_a;
1614  if (flu_s) {
1615  e = exp(e);
1616  }
1617  spe_output << e <<" "<< fe *norma <<" "<< dfe *norma << "\n";
1618  if (verbose) egsInformation("%11.6f %14.6e %14.6e\n",
1619  e, fe*norma, dfe*norma);
1620  }
1621  spe_output << "&\n";
1622 
1623  if (score_primaries) {
1624  if (verbose) {
1625  egsInformation("\nPrimary fluence:\n");
1626  }
1627  spe_output<<"@ s"<< ++i_graph <<" errorbar linestyle 0\n";
1628  spe_output<<"@ s"<< i_graph <<" legend \""<< "primary (ir # " << j <<")\"\n";
1629  spe_output<<"@target G0.S"<< i_graph <<"\n";
1630  spe_output<<"@type xydy\n";
1631  if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n"
1632  "---------------------------------------------------\n");
1633  for (int i=0; i<flu_nbin; i++) {
1634  flu_p[j]->currentResult(i,fe,dfe);
1635  EGS_Float e = (i+0.5-flu_b)/flu_a;
1636  if (flu_s) {
1637  e = exp(e);
1638  }
1639  spe_output << e <<" "<< fe *norma <<" "<< dfe *norma << "\n";
1640  if (verbose) egsInformation("%11.6f %14.6e %14.6e\n",
1641  e, fe*norma, dfe*norma);
1642  }
1643  spe_output << "&\n";
1644  }
1645  i_graph++;
1646  }
1647 
1648  spe_output.close();
1649 
1650  }
1651 
1652 }
1653 
1654 void EGS_VolumetricFluence::reportResults() {
1655 
1656  egsInformation("\nFluence Scoring (%s)\n",name.c_str());
1657  egsInformation("======================================================\n");
1658 
1659  ouputResults();
1660 
1661 }
1662 
1663 bool EGS_VolumetricFluence::storeState(ostream &data) const {
1664  if (!egsStoreI64(data,current_ncase)) {
1665  return false;
1666  }
1667  data << endl;
1668 
1669  if (!data.good()) {
1670  return false;
1671  }
1672 
1673 #ifdef DEBUG
1674  if (scoring_charge) {
1675  if (!relStepDiff->storeState(data)) {
1676  return false;
1677  }
1678  if (!stepDist->storeState(data)) {
1679  return false;
1680  }
1681  }
1682 #endif
1683 
1684  if (!fluT->storeState(data)) {
1685  return false;
1686  }
1687  if (score_spe) {
1688  for (int j = 0; j < nreg; j++) {
1689  if (is_sensitive[j]) {
1690  if (!flu[j]->storeState(data)) {
1691  return false;
1692  }
1693  }
1694  }
1695  }
1696 
1697  if (score_primaries) {
1698  if (!fluT_p->storeState(data)) {
1699  return false;
1700  }
1701  if (score_spe) {
1702  for (int j=0; j < nreg; j++) {
1703  if (is_sensitive[j]) {
1704  if (!flu_p[j]->storeState(data)) {
1705  return false;
1706  }
1707  }
1708  }
1709  }
1710  }
1711 
1712  return true;
1713 }
1714 
1715 bool EGS_VolumetricFluence::setState(istream &data) {
1716 
1717  if (!egsGetI64(data,current_ncase)) {
1718  return false;
1719  }
1720 
1721  if (!data.good()) {
1722  return false;
1723  }
1724 
1725 #ifdef DEBUG
1726  if (scoring_charge) {
1727  if (!relStepDiff->setState(data)) {
1728  return false;
1729  }
1730  if (!stepDist->setState(data)) {
1731  return false;
1732  }
1733  }
1734 #endif
1735 
1736  if (!fluT->setState(data)) {
1737  return false;
1738  }
1739  if (score_spe) {
1740  for (int j=0; j<nreg; j++) {
1741  if (is_sensitive[j]) {
1742  if (!flu[j]->setState(data)) {
1743  return false;
1744  }
1745  }
1746  }
1747  }
1748 
1749  if (score_primaries) {
1750  if (!fluT_p->setState(data)) {
1751  return false;
1752  }
1753  if (score_spe) {
1754  for (int j=0; j < nreg; j++) {
1755  if (is_sensitive[j]) {
1756  if (!flu_p[j]->setState(data)) {
1757  return false;
1758  }
1759  }
1760  }
1761  }
1762  }
1763 
1764  return true;
1765 }
1766 
1767 bool EGS_VolumetricFluence::addState(istream &data) {
1768  EGS_I64 tmp_case;
1769  if (!egsGetI64(data,tmp_case)) {
1770  return false;
1771  }
1772  current_ncase += tmp_case;
1773 
1774  if (!data.good()) {
1775  return false;
1776  }
1777 
1778 #ifdef DEBUG
1779  if (scoring_charge) {
1780  EGS_ScoringArray tmpRelStepDiff(1);
1781  if (!tmpRelStepDiff.setState(data)) {
1782  return false;
1783  }
1784  (*relStepDiff) += tmpRelStepDiff;
1785  }
1786 #endif
1787  /* fluence objects */
1788 
1789  EGS_ScoringArray tgT(nreg);
1790  if (!tgT.setState(data)) {
1791  return false;
1792  }
1793  (*fluT) += tgT;
1794 
1795  if (score_spe) {
1796  EGS_ScoringArray tg(flu_nbin);
1797  for (int j = 0; j < nreg; j++) {
1798  if (is_sensitive[j]) {
1799  if (!tg.setState(data)) {
1800  return false;
1801  }
1802  (*flu[j]) += tg;
1803  }
1804  }
1805  }
1806 
1807  if (score_primaries) {
1808  EGS_ScoringArray tgT_p(nreg);
1809  if (!tgT_p.setState(data)) {
1810  return false;
1811  }
1812  (*fluT_p) += tgT_p;
1813  if (score_spe) {
1814  EGS_ScoringArray tg_p(flu_nbin);
1815  for (int j=0; j < nreg; j++) {
1816  if (is_sensitive[j]) {
1817  if (!tg_p.setState(data)) {
1818  return false;
1819  }
1820  (*flu_p[j]) += tg_p;
1821  }
1822  }
1823  }
1824  }
1825 
1826  return true;
1827 }
1828 
1829 extern "C" {
1830 
1831  static void setInputs() {
1832  inputSet = true;
1833 
1834  setBaseAusgabObjectInputs();
1835 
1836  ausBlockInput->getSingleInput("library")->setValues({"egs_fluence_scoring"});
1837 
1838  // Format: name, isRequired, description, vector string of allowed values
1839  auto typePtr = ausBlockInput->addSingleInput("type", true, "Whether to score volumetric or planar fluence", {"planar", "volumetric"});
1840  ausBlockInput->addSingleInput("scoring particle", true, "Which type of particle we are scoring the fluence for", {"photon", "electron", "positron"});
1841  ausBlockInput->addSingleInput("source particle", false, "Optional, only required to score primary fluence. Defaults to source particles if all the same, or if mixed, defaults to scoring particle.", {"photon", "electron", "positron"});
1842  ausBlockInput->addSingleInput("score primaries", false, "Whether to score primary fluence as well as differential fluence. Defaults to no.", {"no", "yes"});
1843  auto specPtr = ausBlockInput->addSingleInput("score spectrum", false, "Whether to score a spectrum. If so, add an 'energy grid' input block. Defaults to no.", {"no", "yes"});
1844  ausBlockInput->addSingleInput("verbose", false, "Print extra information about the fluence scoring. Defaults to no.", {"no", "yes"});
1845  ausBlockInput->addSingleInput("normalization", false, "Perform a multiplicative normalization to the results. Defaults to 1.");
1846 
1847  auto blockPtr = ausBlockInput->addBlockInput("energy grid");
1848  blockPtr->addDependency(specPtr, "yes");
1849  blockPtr->addSingleInput("number of bins", true, "The number of energy bins in the spectrum");
1850  blockPtr->addSingleInput("minimum kinetic energy", true, "The minimum energy to score in the spectrum");
1851  blockPtr->addSingleInput("maximum kinetic energy", true, "The maximum energy to score in the spectrum");
1852  blockPtr->addSingleInput("scale", false, "Whether to score on a linear or logarithmic scale. Default is linear.", {"linear", "logarithmic"});
1853 
1854  blockPtr = ausBlockInput->addBlockInput("volumetric scoring");
1855  blockPtr->addDependency(typePtr, "volumetric");
1856  auto regionPtr = blockPtr->addSingleInput("scoring regions", false, "A list of regions to score fluence in");
1857  auto startPtr = blockPtr->addSingleInput("start region", false, "For a series of region ranges, list the 'starting region' for each range here (inclusive)");
1858  auto stopPtr = blockPtr->addSingleInput("stop region", false, "For a series of region ranges, list the 'ending region' for each range here (inclusive)");
1859  blockPtr->addSingleInput("volumes", false, "Either a single volume, which will be the same for all regions or a list of individual volumes for each region or region group. Default is 1 cm^3");
1860  blockPtr->addSingleInput("method", false, "The algorithm to use for charged particle scoring (ignored for photon scoring). See documentation for full explanation. Defaults to 'stpwr'.", {"flurz", "stpwr", "stpwrO5"});
1861 
1862  regionPtr->addDependency(startPtr, "", true);
1863  regionPtr->addDependency(stopPtr, "", true);
1864  startPtr->addDependency(regionPtr, "", true);
1865  stopPtr->addDependency(regionPtr, "", true);
1866 
1867  blockPtr = ausBlockInput->addBlockInput("planar scoring");
1868  blockPtr->addDependency(typePtr, "planar");
1869  regionPtr = blockPtr->addSingleInput("contributing regions", false, "A list of regions to score fluence in");
1870  startPtr = blockPtr->addSingleInput("start contributing region", false, "For a series of region ranges, list the 'starting region' for each range here (inclusive)");
1871  stopPtr = blockPtr->addSingleInput("stop contributing region", false, "For a series of region ranges, list the 'ending region' for each range here (inclusive)");
1872  auto circlePtr = blockPtr->addSingleInput("scoring circle", false, "The center point and radius of the circle: x y z R");
1873  auto circleNormPtr = blockPtr->addSingleInput("scoring plane normal", false, "The unit vector for the normal of the scoring circle: ux uy uz");
1874  auto rectPtr = blockPtr->addSingleInput("scoring rectangle", false, "The x and y ranges for the rectangle edges, initially defined in the x-y plane. Use a transformation input block to rotate. Format is: xmin xmax ymin ymax");
1875 
1876  regionPtr->addDependency(startPtr, "", true);
1877  regionPtr->addDependency(stopPtr, "", true);
1878  startPtr->addDependency(regionPtr, "", true);
1879  stopPtr->addDependency(regionPtr, "", true);
1880 
1881  circlePtr->addDependency(rectPtr, "", true);
1882  circleNormPtr->addDependency(rectPtr, "", true);
1883  rectPtr->addDependency(circlePtr, "", true);
1884 
1885  addTransformationBlock(blockPtr);
1886  }
1887 
1888  EGS_FLUENCE_SCORING_EXPORT string getExample() {
1889  string example;
1890  example = {
1891  R"(
1892  # Example of egs_fluence_scoring for planar fluence
1893  #:start ausgab object:
1894  name = id-string # Arbitrary identifying string
1895  library = egs_fluence_scoring # Library name
1896  type = planar # Score on circular or square field
1897  scoring particle = photon, or electron, or positron
1898  source particle = photon, or electron, or positron
1899  # Optional. Only required to score primary fluence.
1900  # Defaults to source particles if all the same.
1901  # In the case of multiple particles,
1902  # defaults to scoring particle. Useful for
1903  # bremsstrahlung targets and radioactive sources.
1904  score primaries = yes or no # Defaults to `no`.
1905  score spectrum = yes or no # Defaults to `no`.
1906  verbose = yes or no # Defaults to `no`.
1907  normalization = norm # User-requested normalization. Defaults to 1.
1908  #########
1909  # If scoring spectrum, define energy grid
1910  # Default: 128 linear energy bins between 1 keV and 1 MeV
1911  #########
1912  :start energy grid:
1913  number of bins = nbins
1914  minimum kinetic energy = Emin
1915  maximum kinetic energy = Emax
1916  scale = linear or logarithmic # Defaults to `linear`.
1917  :stop energy grid:
1918  ########
1919  # Define scoring based on type
1920  ########
1921  :start planar scoring:
1922  # Define contributing regions
1923  contributing regions = ir1 ir2 ... irn
1924  ### Alternatively:
1925  # start contributing region = iri_1, iri_2, ..., iri_n
1926  # stop contributing region = irf_1, irf_2, ..., irf_n
1927  ###
1928  ################################
1929  # If a circular field desired:
1930  ################################
1931  scoring circle = x y z R
1932  scoring plane normal = ux uy uz
1933  ########################################################
1934  # If a rectangular field desired:
1935  #
1936  #scoring rectangle = xmin xmax ymin ymax
1937  #####
1938  # See documentation for EGS_AffineTransform
1939  #####
1940  #:start transformation:
1941  # rotation = 2, 3 or 9 floating point numbers
1942  # translation = tx, ty, tz
1943  #:stop transformation:
1944  ##########################################################
1945  :stop planar scoring:
1946  :stop ausgab object:
1947 
1948  # Example of egs_fluence_scoring for volumetric fluence
1949  #:start ausgab object:
1950  name = id-string # Arbitrary identifying string
1951  library = egs_fluence_scoring # Library name
1952  type = volumetric # Score in a volume
1953  scoring particle = photon, or electron, or positron
1954  source particle = photon, or electron, or positron
1955  # Optional. Only required to score primary fluence.
1956  # Defaults to source particles if all the same.
1957  # In the case of multiple particles,
1958  # defaults to scoring particle. Useful for
1959  # bremsstrahlung targets and radioactive sources.
1960  score primaries = yes or no # Defaults to `no`.
1961  score spectrum = yes or no # Defaults to `no`.
1962  verbose = yes or no # Defaults to `no`.
1963  normalization = norm # User-requested normalization. Defaults to 1.
1964  # If scoring spectrum, define energy grid
1965  # Default: 128 linear energy bins between 1 keV and 1 MeV
1966  :start energy grid:
1967  number of bins = nbins
1968  minimum kinetic energy = Emin
1969  maximum kinetic energy = Emax
1970  scale = linear or logarithmic # Defaults to `linear`.
1971  :stop energy grid:
1972  :start volumetric scoring:
1973  scoring regions = ir1 ir2 ... irn
1974  ### Alternatively:
1975  #start region = iri_1, iri_2, ..., iri_n
1976  #stop region = irf_1, irf_2, ..., irf_n
1977  ###
1978  volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number
1979  # of entries as group of regions, assumes groups of
1980  # equal volume regions. If only one entry, assumes
1981  # equal volumes in all regions. Defaults to 1.
1982  method = flurz or stpwr or stpwrO5 # For charged particle scoring.
1983  #
1984  # flurz => FLURZnrc algorithm
1985  #
1986  # Path length at each energy interval from energy
1987  # deposited EDEP and total particle step TVSTEP.
1988  # Assumes stopping power constancy along the particle's
1989  # step. It might introduce artifacts if ESTEPE or the
1990  # scoring bin width are too large.
1991  #
1992  # stpwr => Accounts for stopping power variation
1993  # along the particle's step. More accurate
1994  # than method used in FLURZnrc albeit about
1995  # about 10% slower in electron beam cases.
1996  #
1997  # Uses an O(3) series expansion of the integral of the
1998  # inverse of the stopping power with respect to energy.
1999  # Stopping power is represented as a linear interpolation
2000  # over a log energy grid.
2001  #
2002  # stpwrO5 => Uses an O(5) series expansion. Slightly slower.
2003  #
2004  # Defaults to `stpwr`.
2005  :stop volumetric scoring:
2006  :stop ausgab object:
2007 )"};
2008  return example;
2009  }
2010 
2011  EGS_FLUENCE_SCORING_EXPORT shared_ptr<EGS_BlockInput> getInputs() {
2012  if (!inputSet) {
2013  setInputs();
2014  }
2015  return ausBlockInput;
2016  }
2017 
2018  EGS_FLUENCE_SCORING_EXPORT EGS_AusgabObject *
2020  const static char *func = "createAusgabObject(fluence_scoring)";
2021  if (!input) {
2022  egsWarning("%s: null input?\n",func);
2023  return 0;
2024  }
2025 
2026  string type;
2027  int error = input->getInput("type",type);
2028  if (!error && input->compare("planar",type)) {
2029  EGS_PlanarFluence *result = new EGS_PlanarFluence("", f);
2030  result->setName(input);
2031  result->initScoring(input);
2032  return result;
2033  }
2034  else if (!error && input->compare("volumetric",type)) {
2035  EGS_VolumetricFluence *result = new EGS_VolumetricFluence("", f);
2036  result->setName(input);
2037  result->initScoring(input);
2038  return result;
2039  }
2040  else {
2041  egsFatal("Invalid fluence type input?\n\n\n");
2042  return 0;
2043  }
2044  }
2045 }
A class providing affine transformations.
void rotate(EGS_Vector &v) const
Applies the rotation to the vector v.
static EGS_AffineTransform * getTransformation(EGS_Input *inp)
Constructs an affine transformation object from the input pointed to by inp and returns a pointer to ...
void transform(EGS_Vector &v) const
Transforms the vector v.
Base class for advanced EGSnrc C++ applications.
void getLabelRegions(const string &str, vector< int > &regs)
Gets the regions for the labels in str and pushes onto regs.
void getNumberRegions(const string &str, vector< int > &regs)
Gets numbers out of str and pushes them onto regs.
string constructIOFileName(const char *extension, bool with_run_dir) const
Constructs and returns the name of an input/output file.
virtual void setApplication(EGS_Application *App)
Set the application this object belongs to.
string description
A short ausgab object description.
EGS_Application * app
The application this object belongs to.
Base class for fluence scoring.
EGS_FluenceScoring(const string &Name="", EGS_ObjectFactory *f=0)
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:1173
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
A class for fast run-time interpolations.
int getIndex(EGS_Float x) const
Get the interpolation index corresponding to x.
void initialize(int nbin, EGS_Float Xmin, EGS_Float Xmax, const EGS_Float *values)
Initialize the interpolator.
EGS_Float interpolate(EGS_Float x) const
Interpolate the function value at x.
EGS_Float getXmax() const
Get the upper interpolation interval limit.
EGS_Float getXmin() const
Get the lower interpolation interval limit.
An object factory.
void setName(EGS_Input *inp)
Set the name of the object from the information provided by inp.
string otype
The object type.
string name
The object name.
Ausgab object for scoring fluence at circular or rectangular fields.
EGS_PlanarFluence(const string &Name="", EGS_ObjectFactory *f=0)
void describeMe()
Sets fluence scoring object description.
A class for scoring an array of quantities (e.g. a dose distribution) in a Monte Carlo simulation.
Definition: egs_scoring.h:220
bool storeState(ostream &data)
Stores the state of the scoring array object into the data stream data.
Definition: egs_scoring.h:317
void score(int ireg, EGS_Float f)
Add f to the score in the element ireg.
Definition: egs_scoring.h:245
void currentResult(int ireg, double &r, double &dr)
Sets r to the result in region ireg and dr to its statistical uncertainty.
Definition: egs_scoring.h:281
bool setState(istream &data)
Sets the state fof the scoring array object from the data in the input stream data.
Definition: egs_scoring.h:341
A class representing 3D vectors.
Definition: egs_vector.h:57
EGS_Float y
y-component
Definition: egs_vector.h:62
EGS_Float z
z-component
Definition: egs_vector.h:63
EGS_Float x
x-component
Definition: egs_vector.h:61
Ausgab object for scoring fluence in arbitrary geometry regions.
void describeMe()
Sets fluence scoring object description.
EGS_VolumetricFluence(const string &Name="", EGS_ObjectFactory *f=0)
A fluence scoring object : header.
ParticleType
Global egspp functions header file.
EGS_Input class header file.
EGS_RADIATIVE_SPLITTING_EXPORT EGS_AusgabObject * createAusgabObject(EGS_Input *input, EGS_ObjectFactory *f)
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,...
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.
A structure holding the information of one particle.
EGS_Vector x
position
EGS_Float E
particle energy in MeV
EGS_Vector u
direction
int latch
latch variable (useful as a flag on many occasions)
EGS_Float wt
statistical weight
int q
particle charge