EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_dose_scoring.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ dose scoring object
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, 2012
25 #
26 # Contributors: Frederic Tessier
27 # Reid Townson
28 # Hubert Ho
29 # Blake Walters
30 # Max Orok
31 # Martin Martinov
32 # Dave Macrillo
33 # Matt Ronan
34 # Nigel Vezeau
35 # Lou Thompson
36 # Hannah Gallop
37 #
38 ###############################################################################
39 #
40 # A general dose calculation tool. Since it is outside the scope of the
41 # EGS_Application class, a few utility methods are needed in EGS_Application
42 # to retrieve information regarding media, geometry and source.
43 #
44 # Dose is output for each dose region without any spacial information. For a
45 # more detailed dose scoring grid it is better to create a user code which
46 # could produce 3D dose distributions and graph data.
47 #
48 # Can be used in any C++ user code by entering the proper input block in
49 # the ausgab object definition block.
50 #
51 # Prints out dose in each dose scoring region.
52 #
53 # Dose scoring regions can be defined by input using one the following
54 # syntax for individual regions:
55 #
56 # :start ausgab object definition:
57 # :start ausgab object:
58 # library = egs_dose_scoring
59 # name = some_name
60 # volume = V0 V1 ... Vn
61 # dose regions = IR0 IR1 ... IRn
62 # :stop ausgab object:
63 # :stop ausgab object definition:
64 #
65 # or by groups of consecutive regions:
66 #
67 # :start ausgab object definition:
68 # :start ausgab object:
69 # library = egs_dose_scoring
70 # name = some_name
71 # volume = V0 V1 ... Vn
72 # dose start region = IRI_0 ... IRI_N
73 # dose stop region = IRE_0 ... IRE_N
74 # :stop ausgab object:
75 # :stop ausgab object definition:
76 #
77 # where one can have same number of volume entries as group of regions (n=N)
78 # or same number of volume entries as number of individual regions. If there
79 # are more dose scoring regions than volume entries, a default volume is use for
80 # those regions without volume information. This default can be either the first
81 # volume entry or 1 g/cm3.
82 #
83 # TODO:
84 #
85 # - Classify dose as contributed by primary or scattered particles
86 # - Custom classification using latch
87 # - Dose to medium calculation
88 #
89 ###############################################################################
90 */
91 
92 
98 #include <fstream>
99 #include <string>
100 #include <cstdlib>
101 
102 #include "egs_dose_scoring.h"
103 #include "egs_input.h"
104 #include "egs_functions.h"
105 #include "egs_mesh.h"
106 
107 static bool EGS_DOSE_SCORING_LOCAL inputSet = false;
108 
109 EGS_DoseScoring::EGS_DoseScoring(const string &Name,
110  EGS_ObjectFactory *f) :
111  EGS_AusgabObject(Name,f), dose(0), doseM(0), doseF(0),
112  norm_u(1.0), nreg(0), nmedia(0), max_dreg(-1), max_medl(0),
113  m_lastCase(-1),score_medium_dose(false), score_region_dose(false), output_dose_file(false) {
114  otype = "EGS_DoseScoring";
115 }
116 
117 EGS_DoseScoring::~EGS_DoseScoring() {
118  if (dose) {
119  delete dose;
120  }
121  if (doseM) {
122  delete doseM;
123  }
124  if (doseF) {
125  delete doseF;
126  }
127 }
128 
129 void EGS_DoseScoring::setApplication(EGS_Application *App) {
131  if (!app) {
132  return;
133  }
134 
135  if (d_regionString.length() > 0) {
136  getNumberRegions(d_regionString, d_region);
137  getLabelRegions(d_regionString, d_region);
138  }
139 
140  // Get the number of regions in the geometry.
141  nreg = app->getnRegions();
142  // Get the number of media in the input file
143  nmedia = app->getnMedia();
144  // determine maximum medium name length
145  char buf[64];
146  int count = 0;
147  int imed=0;
148  max_medl = 6; // length of the string "medium" is the minimum
149  for (imed=0; imed < nmedia; imed++) {
150  sprintf(buf,"%s%n",app->getMediumName(imed),&count);
151  if (count > max_medl) {
152  max_medl = count;
153  }
154  }
155  if (d_region.size()>nreg)
156  egsWarning("\n*********************************************"
157  "\n WARNING:"
158  "\nRequesting %d dose scoring regions, but there"
159  "\nare only %d geometrical regions!"
160  "\nscoring will be done in all regions!"
161  "\n*********************************************",
162  d_region.size(),nreg);
163  // Flag dose scoring regions in geometry
164  // and set their volume.
165  if (score_region_dose) {
166  if (d_region.size() && d_region.size() < nreg) { // specific dose regions provided
167  // Get maximum dose scoring region
168  for (vector<int>::iterator it = d_region.begin(); it < d_region.end(); it++) {
169  if (*it > max_dreg) {
170  max_dreg = *it;
171  }
172  }
173  for (int j=0; j<nreg; j++) {
174  d_reg_index.push_back(-1);
175  vol.push_back(vol_list[0]);// set to either 1.0 or first volume entered
176  }
177  for (int i=0; i<d_region.size(); i++) {
178  d_reg_index[d_region[i]]=i;
179  if (i < vol_list.size()) {
180  vol[d_region[i]] = vol_list[i];
181  }
182  }
183  if (score_region_dose) {
184  dose = new EGS_ScoringArray(d_region.size());
185  }
186  }
187  else { // scoring in all regions
188  max_dreg = nreg-1;
189  for (int j=0; j<nreg; j++) {
190  d_reg_index.push_back(j);
191  if (j < vol_list.size()) {
192  vol.push_back(vol_list[j]);
193  }
194  else { // more regions than volumes, use default 1 g/cm3 or first volume entered
195  vol.push_back(vol_list[0]);
196  }
197  }
198  if (score_region_dose) {
199  dose = new EGS_ScoringArray(nreg);
200  }
201  }
202  }
203  // If requested request memory for medium dose
204  if (score_medium_dose) {
205  doseM = new EGS_ScoringArray(nmedia);
206  }
207  if (!score_region_dose) { // setup volumes
208  if (vol_list.size() == 1) {
209  vol.push_back(vol_list[0]);
210  }
211  else {
212  for (int j=0; j<nreg; j++) {
213  if (j < vol_list.size()) {
214  vol.push_back(vol_list[j]);
215  }
216  else { // more regions than volumes, use default 1 g/cm3 or first volume entered
217  vol.push_back(vol_list[0]);
218  }
219  }
220  }
221  }
222 
223  if (output_dose_file) {
224  //set df_reg to default (non-scoring) values
225  for (int i=0; i<nreg; i++) {
226  df_reg.push_back(-1);
227  }
228 
229  // Determine the region no. in the specified geometry and the corresponding global reg. no.
230  if(file_type==0) {
231 
232  int nx=dose_geom->getNRegDir(0);
233  int ny=dose_geom->getNRegDir(1);
234  int nz=dose_geom->getNRegDir(2);
235 
236  int globalOffset = app->getGlobalRegionOffset(dose_geom->getName());
237  if(globalOffset < 0) {
238  globalOffset = 0;
239  }
240  int count = 0;
241  for (int k=0; k<nz; k++) {
242  for (int j=0; j<ny; j++) {
243  for (int i=0; i<nx; i++) {
244  df_reg[globalOffset+count]=count;
245  //egsInformation("%d %d\n", df_reg[globalOffset+count], globalOffset+count);
246  ++count;
247  }
248  }
249  }
250 
251  //create an egs_scoring_array of the appropriate size
252  doseF = new EGS_ScoringArray(nx*ny*nz);
253 
254  } else if(file_type==1 || file_type==2) {
255  int globalOffset = app->getGlobalRegionOffset(dose_geom->getName());
256  int count = 0;
257 
258  EGS_Mesh *mesh = dynamic_cast<EGS_Mesh *>(dose_geom);
259  if(!mesh) {
260  egsFatal("\nEGS_DoseScoring:: Error: Could not cast %s to EGS_Mesh.\n", dose_geom->getName().c_str());
261  }
262 
263  for (int i = 0; i < mesh->num_elements(); i++) {
264  df_reg[globalOffset+i]=i;
265  }
266 
267  doseF = new EGS_ScoringArray(mesh->num_elements());
268  }
269  }
270 
271  description = "\n*******************************************\n";
272  description += "Dose Scoring Object (";
273  description += name;
274  description += ")\n";
275  description += "*******************************************\n";
276  if (dose) {
277  description +="\n - Regions in dose calculator :";
278  sprintf(buf,"%d\n\n",dose->regions());
279  description += buf;
280  }
281  if (doseM) {
282  description += " - Medium dose will be calculated\n";
283  }
284  description += "\n--------------------------------------\n";
285  sprintf(buf,"%*s %*s rho/(g/cm^3)\n",max_medl/2,"medium",max_medl/2," ");
286  description += buf;
287  description += "--------------------------------------\n";
288  for (imed=0; imed < nmedia; imed++) {
289  sprintf(buf,"%-*s",max_medl,app->getMediumName(imed));
290  description += buf;
291  description += " ";
292  sprintf(buf,"%11.8f",app->getMediumRho(imed));
293  description += buf;
294  description += "\n";
295  }
296  description += "--------------------------------------\n";
297  if (norm_u != 1.0) {
298  description += " Non-unity user-requested normalization = ";
299  sprintf(buf,"%g\n",norm_u);
300  description += buf;
301  }
302 
303  vol_list.clear();
304  if (d_region.size()) {
305  d_region.clear();
306  }
307  if (doseF) {
308  description += "\nWill output dose to file:\n";
309  description += " Geometry name: " + dose_geom->getName();
310  description += "\n File format: ";
311  string ext;
312  if (file_type==0) {
313  ext = "3ddose";
314  } else if (file_type==1) {
315  ext = "vtk";
316  } else if (file_type==2) {
317  ext = "csv";
318  }
319  description += " " + ext;
320  description += "\n File name: ";
321  df_name=getObjectName() + "." + ext;
322  df_name=egsJoinPath(app->getAppDir(),df_name);
323  description += df_name;
324  }
325  description += "\n*******************************************\n\n";
326 }
327 
328 void EGS_DoseScoring::getNumberRegions(const string &str, vector<int> &regs) {
329  app->getNumberRegions(str, regs);
330 }
331 
332 void EGS_DoseScoring::getLabelRegions(const string &str, vector<int> &regs) {
333  app->getLabelRegions(str, regs);
334 }
335 
336 void EGS_DoseScoring::reportResults() {
337  egsInformation("\n======================================================\n");
338  egsInformation("Dose Scoring Object(%s)\n",name.c_str());
339  egsInformation("======================================================\n");
340  EGS_Float normD = 1., normE=1.;
341  const double JOULES_PER_MEV = 1.602176634e-13;
342  int count = 0;
343  EGS_Float F = app->getFluence();
344  egsInformation("=> last case = %lld fluence = %g\n", m_lastCase, F);
345  /* Normalize to actual source fluence */
346  normE = m_lastCase/F*norm_u;
347  normD = JOULES_PER_MEV * 1000. * normE;
348  int irmax_digits = getDigits(max_dreg);
349  if (irmax_digits < 2) {
350  irmax_digits = 2;
351  }
352 
353  EGS_Mesh *mesh;
354  if(doseF) {
355  if(file_type == 0) {
356 
357  } else if(file_type == 1 || file_type == 2) {
358  mesh = dynamic_cast<EGS_Mesh *>(dose_geom);
359  if(!mesh) {
360  egsFatal("\nEGS_DoseScoring:: Error: Could not cast %s to EGS_Mesh.\n", dose_geom->getName().c_str());
361  }
362  }
363  }
364 
365  string line;
366  double r,dr;
367  if (dose) {
368  if (normE==1) {
369  egsInformation("\n\n==> Summary of region dosimetry (per particle)\n\n");
371  "%*s %*s %12s %12s Edep (MeV) D (Gy) %n\n",
372  irmax_digits,"ir",max_medl,"medium","rho (g/cm^3)","Volume (cm^3)",&count);
373  }
374  else {
375  egsInformation("\n==> Summary of region dosimetry (per fluence)\n");
377  "%*s %*s %12s %12s Edep (MeV*cm^2) D (Gy*cm^2) %n\n",
378  irmax_digits,"ir",max_medl,"medium","rho (g/cm^3)","Volume (cm^3)",&count);
379  }
380  line.append(count,'-');
381  egsInformation("%s\n",line.c_str());
382 
383  /* Compute deposited energy and dose */
384  for (int ireg = 0; ireg < nreg; ireg++) {
385  if (d_reg_index[ireg]>=0) {
386  if (!(app->isRealRegion(ireg))) {
387  continue;
388  }
389  int imed = app->getMedium(ireg);
390  EGS_Float rho;
391  EGS_Float mass;
392 
393  // If we're told about an output dose file then we
394  // can use the geometry it points to to calculate masses
395  // Otherwise, dose to XYZ or mesh geometries could be incorrect if they provide a different density for a given material aside from its default
396  if(doseF) {
397  if(file_type == 0) {
398  // If this region corresponds to one in the dose grid
399  if(df_reg[ireg] >= 0) {
400  rho = getRealRho(df_reg[ireg]);
401  mass = dose_geom->getVolume(df_reg[ireg])*rho;
402  } else {
403  rho = app->getMediumRho(imed);
404  mass = rho*vol[ireg];
405  }
406  } else if(file_type == 1 || file_type == 2) {
407  // If this region corresponds to one in the dose grid
408  if(df_reg[ireg] >= 0) {
409  rho = mesh->element_density(df_reg[ireg]);
410  mass = rho * mesh->element_volume(df_reg[ireg]);
411  } else {
412  rho = app->getMediumRho(imed);
413  mass = rho*vol[ireg];
414  }
415  }
416  } else {
417  rho = app->getMediumRho(imed);
418  mass = vol[ireg]*rho;
419  }
420 
421  dose->currentResult(d_reg_index[ireg],r,dr);
422  if (r > 0) {
423  dr = dr/r;
424  }
425  else {
426  dr=1;
427  }
428  egsInformation("%*d %-*s %11.8f %13.6f %12.4e +/- %-7.3f%% %10.4e +/- %-7.3f%%\n",
429  irmax_digits,ireg,max_medl,app->getMediumName(imed),rho,vol[ireg],r*normE,dr*100.,r*normD/mass,dr*100.);
430  }
431  }
432  egsInformation("%s\n",line.c_str());
433  }
434  if (doseM) {
435  vector<EGS_Float> massM(nmedia,0);
436  int imed = 0;
437 
438  // Use any user-provided volumes
439  for (int ir=0; ir<nreg; ir++) {
440  if (app->isRealRegion(ir)) {
441  imed = app->getMedium(ir);
442  // skip vacuum regions
443  if (imed == -1) {
444  continue;
445  }
446 
447  if(doseF) {
448  if(file_type == 0) {
449  // If this region corresponds to one in the dose grid
450  if(df_reg[ir] >= 0) {
451  massM[imed] += dose_geom->getVolume(df_reg[ir])*getRealRho(df_reg[ir]);
452  } else {
453  EGS_Float volume = vol.size() > 1 ? vol[ir]:vol[0];
454  massM[imed] += app->getMediumRho(imed)*volume;
455  }
456  } else if(file_type == 1 || file_type == 2) {
457  // If this region corresponds to one in the dose grid
458  if(df_reg[ir] >= 0) {
459  massM[imed] += mesh->element_density(df_reg[ir]) * mesh->element_volume(df_reg[ir]);
460  } else {
461  EGS_Float volume = vol.size() > 1 ? vol[ir]:vol[0];
462  massM[imed] += app->getMediumRho(imed)*volume;
463  }
464  }
465  } else {
466  EGS_Float volume = vol.size() > 1 ? vol[ir]:vol[0];
467  massM[imed] += app->getMediumRho(imed)*volume;
468  }
469  }
470  }
471 
472  if (normE==1) {
473  egsInformation("\n\n==> Summary of media dosimetry (per particle)\n");
475  "%*s %*sMass/[g] Edep/[MeV] D/[Gy] %n\n",
476  max_medl/2,"medium",max_medl/2," ",&count);
477  }
478  else {
479  egsInformation("\n\n==> Summary of media dosimetry (per fluence)\n");
481  "%*s %*sMass/[g] Edep/[MeV*cm2] D/[Gy*cm2] %n\n",
482  max_medl/2,"medium",max_medl/2," ",&count);
483  }
484  line="";
485  line.append(count,'-');
486  egsInformation("%s\n",line.c_str());
487  /* Compute deposited energy in medium (MeV)*/
488  for (int im=0; im<nmedia; im++) {
489  doseM->currentResult(im,r,dr);
490  if (r > 0) {
491  dr = dr/r;
493  "%-*s %7.4e %10.4e +/- %-7.3f%% %10.4e +/- %-7.3f%%\n",
494  max_medl,app->getMediumName(im),massM[im],r*normE,dr*100.,r*normD/massM[im],dr*100.);
495  }
496  }
497  egsInformation("%s\n",line.c_str());
498  }
499  if (doseF) {
500  if (app->getIparallel()>0) {
501  egsInformation("\n EGS_DoseScoring: This is one of a number of parallel jobs. Will only output dose file on combining results.\n");
502  }
503  else {
504  outputDoseFile(normD);
505  }
506  }
507  egsInformation("\n======================================================\n");
508 }
509 
510 void EGS_DoseScoring::outputDoseFile(const EGS_Float &normD) {
511  double r,dr;
512  ofstream df_out;
513  egsInformation("\n EGS_DoseScoring: Writing dose data for %s... \n",dose_geom->getName().c_str());
514  egsInformation(" Output file name: %s\n",df_name.c_str());
515 
516  // 3ddose
517  if (file_type==0) {
518  //open file
519  df_out.open(df_name.c_str());
520  if (!df_out) {
521  egsFatal("\n EGS_DoseScoring: Error: Failed to open file %s\n",df_name.c_str());
522  exit(1);
523  }
524  //output data
525  int nx=dose_geom->getNRegDir(0);
526  int ny=dose_geom->getNRegDir(1);
527  int nz=dose_geom->getNRegDir(2);
528  //output no. of voxels in x,y,z
529  df_out << nx << " " << ny << " " << nz << "\n";
530  //use single precision real for output
531  float bound, dose, doseun;
532  //output voxel boundaries
533  for (int i=0; i<=nx; i++) {
534  bound=dose_geom->getBound(0,i);
535  df_out << bound << " ";
536  }
537  df_out << "\n";
538  for (int j=0; j<=ny; j++) {
539  bound=dose_geom->getBound(1,j);
540  df_out << bound << " ";
541  }
542  df_out << "\n";
543  for (int k=0; k<=nz; k++) {
544  bound=dose_geom->getBound(2,k);
545  df_out << bound << " ";
546  }
547  df_out << "\n";
548  //divide dose by mass and output
549  for (int i=0; i<nx*ny*nz; i++) {
550  doseF->currentResult(i,r,dr);
551  EGS_Float mass = dose_geom->getVolume(i)*getRealRho(i); //local reg.
552  dose=r*normD/mass;
553  df_out << dose << " ";
554  }
555  df_out << "\n";
556  //output uncertainties
557  for (int i=0; i<nx*ny*nz; i++) {
558  doseF->currentResult(i,r,dr);
559  if (r > 0) {
560  dr = dr/r;
561  }
562  else {
563  dr=1;
564  }
565  doseun=dr;
566  df_out << doseun << " ";
567  }
568  df_out << "\n";
569  df_out.close();
570 
571  // vtk for mesh
572  } else if(file_type==1) {
573  EGS_Mesh *mesh = dynamic_cast<EGS_Mesh *>(dose_geom);
574  if(!mesh) {
575  egsFatal("\nEGS_DoseScoring:: Error: Could not cast %s to EGS_Mesh.\n", dose_geom->getName().c_str());
576  }
577 
578  // Open file
579  df_out.open(df_name.c_str());
580  if (!df_out) {
581  egsFatal("\n EGS_DoseScoring: Error: Failed to open file %s\n",df_name.c_str());
582  exit(1);
583  }
584 
585  //output data
586  df_out << std::setprecision(std::numeric_limits<double>::max_digits10);
587  // legacy header
588  df_out << "# vtk DataFile Version 4.1\n"
589  "EGS_Mesh results\n"
590  "ASCII\n"
591  "DATASET UNSTRUCTURED_GRID\n"
592  "POINTS " << mesh->num_nodes() << " double\n";
593 
594  // point data
595  for (int i = 0; i < mesh->num_nodes(); i++) {
596  const EGS_Vector &node = mesh->node_coordinates(i);
597  df_out << node.x << " " << node.y << " " << node.z << "\n";
598  }
599  // 5 numbers per line
600  df_out << "CELLS " << mesh->num_elements() << " "
601  << 5 * mesh->num_elements() << "\n";
602  // unstructured grid
603  for (int i = 0; i < mesh->num_elements(); i++) {
604  const auto &node_offsets = mesh->element_node_offsets(i);
605  // four nodes per tetrahedron
606  df_out << "4 " << node_offsets[0] << " " << node_offsets[1] << " " <<
607  node_offsets[2] << " " << node_offsets[3] << "\n";
608  }
609 
610  df_out << "CELL_TYPES " << mesh->num_elements() << "\n";
611  for (int i = 0; i < mesh->num_elements(); i++) {
612  // vtk code for tetrahedron
613  df_out << "10\n";
614  }
615 
616  // doses
617  df_out << "CELL_DATA " << mesh->num_elements() << "\n";
618  // adjust number here vvvv if the number of fields written out changes
619  df_out << "FIELD FieldData 2\n";
620  // %20 url-encoded space, Paraview errors on space character
621  df_out << "dose%20[Gy/fluence] 1 " << mesh->num_elements() << " double\n";
622 
623  for (int i = 0; i < mesh->num_elements(); i++) {
624  double e_dep, uncert;
625  doseF->currentResult(i,e_dep,uncert);
626 
627  const auto mass = mesh->element_density(i) * mesh->element_volume(i);
628 
629  df_out << normD *e_dep / mass << "\n";
630  }
631 
632  // uncertainties
633  df_out << "uncertainty%20[%25] 1 " << mesh->num_elements() << " double\n";
634  for (int i = 0; i < mesh->num_elements(); i++) {
635  double e_dep, uncert;
636  doseF->currentResult(i,e_dep,uncert);
637 
638  // if edep is exactly zero, there is 100% uncertainty
639  if (e_dep == 0.0) {
640  df_out << 100.0 << "\n";
641  }
642  else {
643  df_out << uncert / e_dep * 100.0 << "\n";
644  }
645  }
646  // csv for mesh
647  } else if(file_type==2) {
648  EGS_Mesh *mesh = dynamic_cast<EGS_Mesh *>(dose_geom);
649  if(!mesh) {
650  egsFatal("\nEGS_DoseScoring:: Error: Could not cast %s to EGS_Mesh.\n", dose_geom->getName().c_str());
651  }
652 
653  // Open file
654  df_out.open(df_name.c_str());
655  if (!df_out) {
656  egsFatal("\n EGS_DoseScoring: Error: Failed to open file %s\n",df_name.c_str());
657  exit(1);
658  }
659 
660  df_out << std::setprecision(std::numeric_limits<double>::max_digits10);
661 
662  // header
663  df_out << "x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4,dose,uncertainty\n";
664 
665  for (int i = 0; i < mesh->num_elements(); i++) {
666  // Get node id's for each element
667  // four nodes per tetrahedron
668  const auto &node_offsets = mesh->element_node_offsets(i);
669 
670  // Look up those coordinates for each of the 4 nodes
671  for (int j = 0; j < 4; j++) {
672  const EGS_Vector &node = mesh->node_coordinates(node_offsets[j]);
673  df_out << node.x << "," << node.y << "," << node.z << ",";
674  }
675 
676  // Output dose
677  double e_dep, uncert;
678  doseF->currentResult(i,e_dep,uncert);
679  const auto mass = mesh->element_density(i) * mesh->element_volume(i);
680 
681  df_out << normD * e_dep / mass << ",";
682 
683  // Output uncertainty in %
684  // if edep is exactly zero, there is 100% uncertainty
685  if (e_dep == 0.0) {
686  df_out << 100.0 << "\n";
687  }
688  else {
689  df_out << uncert / e_dep * 100.0 << "\n";
690  }
691  }
692  } else {
693  egsFatal("\n EGS_DoseScoring: Warning: Dose output file type not recognized.\n");
694  }
695 }
696 
697 bool EGS_DoseScoring::storeState(ostream &data) const {
698  //egsInformation("Storing EGS_DoseScoring...\n");
699  if (!egsStoreI64(data,m_lastCase)) {
700  return false;
701  }
702  data << endl;
703  if (dose && !dose->storeState(data)) {
704  return false;
705  }
706  if (doseM && !doseM->storeState(data)) {
707  return false;
708  }
709  if (doseF && !doseF->storeState(data)) {
710  return false;
711  }
712  return true;
713 }
714 
715 bool EGS_DoseScoring::setState(istream &data) {
716  if (!egsGetI64(data,m_lastCase)) {
717  return false;
718  }
719  if (dose && !dose->setState(data)) {
720  return false;
721  }
722  if (doseM && !doseM->setState(data)) {
723  return false;
724  }
725  if (doseF && !doseF->setState(data)) {
726  return false;
727  }
728  return true;
729 }
730 
731 bool EGS_DoseScoring::addState(istream &data) {
732  int err = addTheStates(data);
733  if (err) {
734  egsInformation("Error code: %d",err);
735  return false;
736  }
737  return true;
738 }
739 int EGS_DoseScoring::addTheStates(istream &data) {
740  EGS_I64 tmp_case;
741  if (!egsGetI64(data,tmp_case)) {
742  return 4401;
743  }
744  m_lastCase += tmp_case;
745  if (dose) {
746  EGS_ScoringArray tmp(nreg);
747  if (!tmp.setState(data)) {
748  return 4402;
749  }
750  (*dose) += tmp;
751  }
752  if (doseM) {
753  EGS_ScoringArray tmpM(nmedia);
754  if (!tmpM.setState(data)) {
755  return 4403;
756  }
757  (*doseM) += tmpM;
758  }
759  if (doseF) {
760  unsigned long numElements = 0;
761  if(file_type == 0) {
762  int nx=dose_geom->getNRegDir(0);
763  int ny=dose_geom->getNRegDir(1);
764  int nz=dose_geom->getNRegDir(2);
765  numElements = nx*ny*nz;
766  } else if(file_type == 1 || file_type == 2) {
767  EGS_Mesh *mesh = dynamic_cast<EGS_Mesh *>(dose_geom);
768  numElements = mesh->num_elements();
769  }
770 
771  EGS_ScoringArray tmpF(numElements);
772  if (!tmpF.setState(data)) {
773  return 4404;
774  }
775  (*doseF) += tmpF;
776  }
777  return 0;
778 }
779 
780 void EGS_DoseScoring::resetCounter() {
781  m_lastCase = 0;
782  if (dose) {
783  dose->reset();
784  }
785  if (doseM) {
786  doseM->reset();
787  }
788  if (doseF) {
789  doseF->reset();
790  }
791 }
792 
793 //*********************************************************************
794 // Process input for this ausgab object
795 //
796 // volume: Set to 1 cm3 by default
797 // One entry => same size dose scoring regions
798 // Several entries => volume for each dose region
799 // SAME as dose region number
800 // or else default value used
801 //
802 // dose scoring regions: Defaults to all regions in geometry
803 // dose regions => Individual regions
804 // dose start/stop region => Groups of consecutive
805 // dose regions
806 //
807 // If there is a mismatch between number of regions and volumes
808 // default volume values will be used. Default volume will be either
809 // the first volume entry or 1 g/cm3 if no volume entry found.
810 //
811 // TODO:
812 // - Classify in primary, scattered and total dose
813 // - Specify for wich media to score or not the dose
814 //
815 //**********************************************************************
816 extern "C" {
817 
818  static void setInputs() {
819  inputSet = true;
820 
821  setBaseAusgabObjectInputs();
822 
823  ausBlockInput->getSingleInput("library")->setValues({"egs_dose_scoring"});
824 
825  // Format: name, isRequired, description, vector string of allowed values
826  ausBlockInput->addSingleInput("medium dose", false, "Requests the dose deposited in each medium to be scored, default is no", {"yes", "no"});
827  ausBlockInput->addSingleInput("region dose", false, "Requests the dose deposited in each region to be scored, default is yes", {"yes", "no"});
828  ausBlockInput->addSingleInput("volume", false, "Either a single volume, which will be the same for all regions or a list of individual volumes for each region in 'dose regions', default is 1 cm^3");
829  auto dosePtr = ausBlockInput->addSingleInput("dose regions", false, "A list of individual regions to report the dose in");
830  auto startPtr = ausBlockInput->addSingleInput("dose start region", false, "For a series of region ranges, list the 'starting region' for each range here (inclusive)");
831  auto stopPtr = ausBlockInput->addSingleInput("dose stop region", false, "For a series of region ranges, list the 'ending region' for each range here (inclusive)");
832 
833  dosePtr->addDependency(startPtr, "", true);
834  dosePtr->addDependency(stopPtr, "", true);
835  startPtr->addDependency(dosePtr, "", true);
836  stopPtr->addDependency(dosePtr, "", true);
837 
838  // This can only be used if one of the geometries is an EGS_XYZGeometry
839  auto blockPtr = ausBlockInput->addBlockInput("output dose file");
840  blockPtr->addSingleInput("geometry name", true, "The name of a predefined EGS_XYZGeometry");
841  blockPtr->addSingleInput("file type", true, "The type of file", {"3ddose"});
842  }
843 
844  EGS_DOSE_SCORING_EXPORT string getExample() {
845  string example;
846  example = {
847  R"(
848  # Example of egs_dose_scoring
849  #:start ausgab object:
850  library = egs_dose_scoring
851  name = my_score
852  medium dose = yes # no (default)
853  region dose = no # yes (default)
854  volume = v1 ... vn # in cm**3
855  dose regions = ir1 ... irn # individual regions
856  :stop ausgab object:
857 )"};
858  return example;
859  }
860 
861  EGS_DOSE_SCORING_EXPORT shared_ptr<EGS_BlockInput> getInputs() {
862  if (!inputSet) {
863  setInputs();
864  }
865  return ausBlockInput;
866  }
867 
868  EGS_DOSE_SCORING_EXPORT EGS_AusgabObject *createAusgabObject(EGS_Input *input,
869  EGS_ObjectFactory *f) {
870  const static char *func = "createAusgabObject(dose_scoring)";
871  if (!input) {
872  egsWarning("%s: null input?\n",func);
873  return 0;
874  }
875  vector <EGS_Float> v_in;// voxel volume[s] in g/cm3
876  /* get voxel volume */
877  input->getInput("volume",v_in);
878  /* get dose scoring mode: region dose, medium dose or both */
879  vector<string> allowed_mode;
880  allowed_mode.push_back("no");
881  allowed_mode.push_back("yes");
882  int d_in_medium = input->getInput("medium dose",allowed_mode,0);
883  int d_in_region = input->getInput("region dose",allowed_mode,1);
884 
885  /* get dose regions */
886  string d_regionsString;
887  vector <int> d_regions;
888  bool using_all_regions=true;
889  vector <int> d_start, d_stop;
890  if (!input->getInput("dose regions",d_regionsString)&& d_regionsString.length()>0) {
891  using_all_regions = false; // individual regions
892  }
893  else {
894  int err1 = input->getInput("dose start region",d_start);
895  int err2 = input->getInput("dose stop region",d_stop);
896  if (!err1 && !err2) {
897  if (d_start.size()==d_stop.size()) { // group of dose regions
898  for (int i=0; i<d_start.size(); i++) {
899  int ir = d_start[i], fr = d_stop[i];
900  for (int ireg=ir; ireg<=fr; ireg++) {
901  d_regions.push_back(ireg);
902  }
903  }
904  using_all_regions = false;
905  }
906  else egsWarning(
907  "%s: Mismatch in start and stop dose region groups !!!\n"
908  " Calculating dose in ALL regions.\n",func);
909  }
910  }
911  EGS_Float norma = 1.0;
912  int err04 = input->getInput("normalization",norma);
913 
914  //================================================
915  // Check if one volume for each group requested.
916  // If not just pass the volumes read and if there
917  // is a mismatch, then the first volume element
918  // or 1 g/cm3 will be used.
919  //=================================================
920  vector <EGS_Float> volin;
921  // groups of regions with same volume
922  if (! using_all_regions && v_in.size()== d_start.size()) {
923  for (int i=0; i<d_start.size(); i++) {
924  int ir = d_start[i], fr = d_stop[i];
925  for (int ireg=ir; ireg<=fr; ireg++) {
926  volin.push_back(v_in[i]);
927  }
928  }
929  }
930  else { // all other possibilities handled here
931  volin = v_in;
932  }
933 
934  //see if the user wants to output the dose to a file for an EGS_XYZGeometry
935  bool outputdosefile=false;
936  EGS_Input *fileinp = input->takeInputItem("output dose file");
937  EGS_BaseGeometry *dgeom;
938  int ftype;
939  if (fileinp) {
940  //get geometry name and filename and do some checks
941  string gname;
942  int err05 = fileinp->getInput("geometry name",gname);
943  if (err05) {
944  egsFatal("EGS_DoseScoring: Output dose file: missing/incorrect input for name of geometry.\n");
945  }
946  else {
947  dgeom = EGS_BaseGeometry::getGeometry(gname);
948  if (!dgeom) {
949  egsFatal("EGS_DoseScoring: Output dose file: %s does not name an existing geometry\n",gname.c_str());
950  }
951  else if (dgeom->getType()!="EGS_XYZGeometry" && dgeom->getType()!="EGS_Mesh") {
952  egsFatal("EGS_DoseScoring: Output dose file: %s is not an EGS_XYZGeometry or EGS_Mesh.\n",gname.c_str());
953  }
954  else {
955  string str;
956  if (fileinp->getInput("file type", str) < 0) {
957  ftype = 0;
958  }
959  else {
960  vector<string> allowed_ftype;
961  allowed_ftype.push_back("3ddose");
962  allowed_ftype.push_back("vtk");
963  allowed_ftype.push_back("csv");
964  ftype = fileinp->getInput("file type", allowed_ftype, -1);
965  if (ftype < 0) {
966  egsFatal("EGS_DoseScoring: Output dose file: Invalid file type. The supported types are: 3ddose, vtk, and csv.\n");
967  }
968  }
969  outputdosefile=true;
970  }
971 
972  if(ftype == 0 && dgeom->getType()!="EGS_XYZGeometry") {
973  egsFatal("EGS_DoseScoring: Output dose file: For 3ddose format, the geometry must be of type EGS_XYZGeometry.\n");
974  } else if ((ftype == 1 || ftype == 2) && dgeom->getType()!="EGS_Mesh") {
975  egsFatal("EGS_DoseScoring: Output dose file: For vtk and csv formats, the geometry must be of type EGS_Mesh.\n");
976  }
977  }
978  }
979 
980 
981  //=================================================
982 
983  /* Setup dose scoring object with input parameters */
984  EGS_DoseScoring *result = new EGS_DoseScoring("",f);
985  if (volin.size()==1) {
986  result->setVol(volin[0]); // one size for all regions
987  }
988  else if (volin.size()) {
989  result->setVol(volin); // regions with their volumes
990  }
991  else {
992  result->setVol(1.0); // default value if no entry
993  }
994  if (!using_all_regions) {
995  if (d_regions.size() > 0) {
996  result->setDoseRegions(d_regions);
997  }
998  else {
999  result->setDoseRegions(d_regionsString);
1000  }
1001  }
1002  if (d_in_medium) {
1003  result->setMediumScoring(true);
1004  }
1005  if (d_in_region) {
1006  result->setRegionScoring(true);
1007  }
1008  if (outputdosefile) {
1009  result->setOutputFile(true,dgeom,ftype);
1010  }
1011  result->setName(input);
1012  if (!err04) {
1013  result->setUserNorm(norma);
1014  }
1015  return result;
1016  }
1017 }
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.
int getMedium(int ireg)
Returns the medium index in region ireg using C-style indexing.
int getGlobalRegionOffset(const string geomName)
Get the global region number for the first region in the geometry.
bool isRealRegion(int ireg)
Returns true if ireg is a real region, false otherwise.
const string & getAppDir() const
Returns the absolute path to the user code directory.
void getNumberRegions(const string &str, vector< int > &regs)
Gets numbers out of str and pushes them onto regs.
int getIparallel() const
Returns the job number in a parallel run.
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 geometry class. Every geometry class must be derived from EGS_BaseGeometry.
virtual const string & getType() const =0
Get the geometry type.
virtual EGS_Float getVolume(int ireg)
Calculates the volume of region ireg.
const string & getName() const
Get the name of this geometry.
virtual int getNRegDir(int idir)
virtual EGS_Float getBound(int idir, int ind)
Returns region boundaries in direction determined by idir.
static EGS_BaseGeometry * getGeometry(const string &Name)
Get a pointer to the geometry named Name.
A dose scoring object: header.
EGS_ScoringArray * doseF
Scoring dose in each region in the specified geometry.
EGS_ScoringArray * doseM
Scoring dose in each medium.
EGS_I64 m_lastCase
The event set via setCurrentCase()
EGS_ScoringArray * dose
Scoring in each dose scoring region.
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:229
int getInput(const string &key, vector< string > &values) const
Assign values to an array of strings from an input identified by key.
Definition: egs_input.cpp:341
A tetrahedral mesh geometry.
Definition: egs_mesh.h:243
EGS_Float element_density(int i) const
Returns the density in g/cm3 of element i.
Definition: egs_mesh.h:283
int num_elements() const
Returns the number of mesh elements.
Definition: egs_mesh.h:261
int num_nodes() const
Returns the number of unique mesh nodes.
Definition: egs_mesh.h:266
EGS_Float element_volume(int i) const
Returns the volume in cm3 of element i.
Definition: egs_mesh.h:277
const EGS_Vector & node_coordinates(int node_offset) const
Definition: egs_mesh.h:377
const std::array< int, 4 > & element_node_offsets(int element) const
Given an element offset, return its four node offsets.
Definition: egs_mesh.h:382
An object factory.
void setName(EGS_Input *inp)
Set the name of the object from the information provided by inp.
const string & getObjectName() const
Get the object name.
string name
The object name.
A class for scoring an array of quantities (e.g. a dose distribution) in a Monte Carlo simulation.
Definition: egs_scoring.h:220
int regions() const
Returns the number of regions (or elements or bins, the most appropriate term depending on the way th...
Definition: egs_scoring.h:407
bool storeState(ostream &data)
Stores the state of the scoring array object into the data stream data.
Definition: egs_scoring.h:317
void currentResult(int ireg, double &r, double &dr)
Sets r to the result in region ireg and dr to its statistical uncertainty.
Definition: egs_scoring.h:281
void reset()
Reset the scoring array to a pristine state.
Definition: egs_scoring.h:369
bool setState(istream &data)
Sets the state fof the scoring array object from the data in the input stream data.
Definition: egs_scoring.h:341
A class representing 3D vectors.
Definition: egs_vector.h:57
EGS_Float y
y-component
Definition: egs_vector.h:62
EGS_Float z
z-component
Definition: egs_vector.h:63
EGS_Float x
x-component
Definition: egs_vector.h:61
A dose scoring ausgab object.
Global egspp functions header file.
EGS_Input class header file.
Tetrahedral mesh geometry: header.
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,...
string egsJoinPath(const string &first, const string &second)
Join two path variables (or a path and a file name) using the platform specific directory separator a...
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.