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