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