EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
volcor.h
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ envelope geometry headers
5 # Copyright (C) 2016 Randle E. P. Taylor, Rowan M. Thomson,
6 # Marc J. P. Chamberland, D. W. O. Rogers
7 #
8 # This file is part of EGSnrc.
9 #
10 # EGSnrc is free software: you can redistribute it and/or modify it under
11 # the terms of the GNU Affero General Public License as published by the
12 # Free Software Foundation, either version 3 of the License, or (at your
13 # option) any later version.
14 #
15 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
16 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
18 # more details.
19 #
20 # You should have received a copy of the GNU Affero General Public License
21 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
22 #
23 ###############################################################################
24 #
25 # Author: Randle Taylor, 2016
26 #
27 # Contributors: Marc Chamberland
28 # Rowan Thomson
29 # Dave Rogers
30 # Martin Martinov
31 #
32 ###############################################################################
33 #
34 # volcor was developed for the Carleton Laboratory for Radiotherapy Physics.
35 #
36 ###############################################################################
37 */
38 
39 
48 #ifndef VC_VOLCOR
49 #define VC_VOLCOR
50 
51 #include <map>
52 #include <set>
53 #include <cstdlib>
54 #include <fstream>
55 
56 #include "egs_base_geometry.h"
57 #include "egs_functions.h"
58 #include "egs_input.h"
59 #include "egs_timer.h"
60 #include "egs_rndm.h"
61 #include "egs_shapes.h"
62 
63 #ifdef HAS_SOBOL
64  #include "egs_sobol.h"
65 #endif
66 
67 #ifdef HAS_GZSTREAM
68  #include "gzstream.h"
69 #endif
70 
78 namespace volcor {
79 
80 
82 enum VolCorMode {
86 };
87 
89 typedef pair<EGS_BaseGeometry *, int> GeomRegPairT;
90 
91 
94 typedef std::map<int, EGS_I64> HitCounterT;
95 
96 
98 typedef map<int, set<EGS_BaseGeometry *> > RegionGeomSetT;
99 
101 typedef pair<int, EGS_Float> RegVolume;
102 
103 
123 EGS_Float getShapeVolume(EGS_Input *shape_inp) {
124 
125  if (!shape_inp) {
126  return -1;
127  }
128 
129  EGS_Float volume;
130  int err = shape_inp->getInput("shape volume", volume);
131  if (!err) {
132  return volume;
133  }
134 
135  string shape_type;
136  shape_inp->getInput("type", shape_type);
137 
138  if (shape_type == "cylinder") {
139  EGS_Float radius, height;
140  int err = shape_inp->getInput("radius", radius);
141  if (err) {
142  egsFatal("getShapeVolume :: missing 'radius' input for shape\n");
143  }
144 
145  err = shape_inp->getInput("height", height);
146  if (err) {
147  egsFatal("getShapeVolume :: missing 'height' input for shape\n");
148  }
149 
150  return M_PI*radius*radius*height;
151 
152  }
153  else if (shape_type == "sphere") {
154  EGS_Float radius;
155 
156  int err = shape_inp->getInput("radius", radius);
157  if (err) {
158  egsFatal("getShapeVolume :: missing 'radius' input for shape\n");
159  }
160 
161  return 4./3.*M_PI*radius*radius*radius;
162 
163  }
164  else if (shape_type == "spherical shell" || shape_type == "egsSphericalShell") {
165  EGS_Float ri, ro;
166  int hemi;
167  int err = shape_inp->getInput("inner radius", ri);
168  if (err) {
169  egsFatal("getShapeVolume :: missing 'inner radius' input for shape\n");
170  }
171 
172  err = shape_inp->getInput("outer radius", ro);
173  if (err) {
174  egsFatal("getShapeVolume :: missing 'outer radius' input for shape\n");
175  }
176 
177  err = shape_inp->getInput("hemisphere", hemi);
178  if (err) {
179  hemi = 0;
180  }
181  EGS_Float vol = 4./3.*M_PI*(ro*ro*ro - ri*ri*ri);
182  if (hemi != 0) {
183  return vol/2;
184  }
185 
186  return vol;
187 
188  }
189  else if (shape_type == "box") {
190  vector<EGS_Float> box_size;
191  int err = shape_inp->getInput("box size", box_size);
192  if (err) {
193  egsFatal("getShapeVolume :: missing 'box size' input for shape\n");
194  }
195  if (box_size.size() == 3) {
196  return box_size[0]*box_size[1]*box_size[2];
197  }
198  else {
199  return box_size[0]*box_size[0]*box_size[0];
200  }
201  }
202 
203  if (shape_type == "") {
204  egsWarning("Either include a `type` or `shape volume` input key.");
205  }
206  else {
207  egsWarning(
208  "The volume (in cm^3) for shape type '%s' must be specified using a `shape volume` input key.",
209  shape_type.c_str()
210  );
211  }
212 
213  return -1;
214 
215 }
216 
242 class VCOptions {
243 
244 public:
245 
246  const static unsigned long DEFAULT_RAND_POINT_DENSITY = 100000000;
247 
250  rng(NULL), vc_file(""), input(inp), bounds(NULL), sobolAllowed(false) {
251 
252  valid=true;
253 
254  if (!inp) {
255  valid = false;
256  return;
257  }
258 
259  setMode();
260 
261  // set external file to load volume corrections from
262  setVCFile();
263 
264  if (vc_file == "") {
265  // no external file found. Run a MC volume correction
266 
267  int err = setBoundsShape();
268  if (err) {
269  valid = false;
270  return;
271  }
272 
273  setDensity();
274  setRNG();
275  }
276 
277  }
278 
281  if (rng) {
282  delete rng;
283  }
284  if (bounds) {
285  delete bounds;
286  }
287  }
288 
291  return bounds->getRandomPoint(rng);
292  }
293 
294  bool valid;
296  double bounds_volume;
298  EGS_Float density;
299  EGS_Float npoints;
301  VolCorMode mode; /* mode requested by user (defaults to DISCOVERY_ONLY) */
302 
303  EGS_RandomGenerator *rng;
304  string vc_file;
305 
306 protected:
307 
308  EGS_Input *input;
309 
310  EGS_BaseShape *bounds;
311  bool sobolAllowed;
312 
313 
315  void setMode() {
316 
317  vector<string> choices;
318  choices.push_back("discover");
319  choices.push_back("discover and zero volume");
320  choices.push_back("discover and correct volume");
321 
322  mode = (VolCorMode)input->getInput("action", choices, (int)DISCOVERY_ONLY);
323  }
324 
326  void setVCFile() {
327  int err = input->getInput("volume correction file", vc_file);
328  if (err) {
329  vc_file = "";
330  }
331  }
332 
335 
336  EGS_Input *shape_inp = input->takeInputItem("shape");
337  if (!shape_inp) {
338  egsWarning("VolCor::VCOptions::setBoundsShape - no `shape` input found.\n");
339  return 1;
340  }
341 
342  bounds_volume = getShapeVolume(shape_inp);
343  if (bounds_volume < 0) {
344  egsWarning("VolCor::VCOptions::setBoundsShape - unable to calculate volume.\n");
345  delete shape_inp;
346  return 1;
347  }
348 
349  bounds = EGS_BaseShape::createShape(shape_inp);
350  if (!bounds) {
351  egsWarning("VolCor::VCOptions::setBoundsShape - `shape` input not valid.\n");
352  delete shape_inp;
353  return 1;
354  }
355 
356  sobolAllowed = bounds->getObjectType() == "box";
357 
358  delete shape_inp;
359  return 0;
360 
361  }
362 
364  void setDensity() {
365 
366  int err = input->getInput("density of random points (cm^-3)", density);
367  if (err) {
368  egsWarning("The volume correction 'density of random points (cm^-3)' input was not found\n");
369  density = (EGS_Float)DEFAULT_RAND_POINT_DENSITY;
370  }
371 
372  npoints = max(1., density*bounds_volume);
373  }
374 
376  void setRNG() {
377 
378  EGS_Input *rng_input = input->getInputItem("rng definition");
379 
380  if (rng_input) {
381  string type;
382  int err = rng_input->getInput("type", type);
383  if (!err && rng_input->compare(type, "sobol")) {
384  if (!sobolAllowed) {
385  egsWarning(
386  "Sobol QRNG are not allowed for non rectilinear shapes. "
387  "Using default Ranmar instead.\n"
388  );
390  }
391  else {
392 #ifdef HAS_SOBOL
393  rng = new EGS_Sobol(rng_input);
394 #else
395  egsWarning("Sobol RNG requested but not compiled with Sobol support\n");
396 #endif
397  }
398  }
399  else {
400  rng = EGS_RandomGenerator::createRNG(rng_input);
401  }
402 
403  if (!rng) {
404  egsFatal("VolCor::setRNG Invalid 'rng definition'.\n");
405  }
406 
407  }
408  else {
409 #ifdef HAS_SOBOL
410  if (sobolAllowed) {
411  rng = new EGS_Sobol();
412  }
413  else {
414 #endif
416 #ifdef HAS_SOBOL
417  }
418 #endif
419  }
420  }
421 
422 };
423 
425 struct VCResults {
426 
427 public:
428 
429  bool success;
430  EGS_Float time;
431  double density;
432  double npoints;
433  double ninscribed;
434  double bounds_volume;
437  string vc_file;
438 
440  vector<EGS_Float> uncorrected_volumes;
441  vector<EGS_Float> corrected_volumes;
443  VCOptions *options;
444 
445  VCResults():
446  success(false),
447  time(0),
448  density(0),
449  npoints(0),
450  ninscribed(0),
451  bounds_volume(0),
452  inscribed_volume(0),
453  vc_file(""),
454  options(0) {};
455 
456  VCResults(string vcfile):
457  success(false),
458  time(0),
459  density(0),
460  npoints(0),
461  ninscribed(0),
462  bounds_volume(0),
463  inscribed_volume(0),
464  vc_file(vcfile),
465  options(0) {};
466 
467 
468  VCResults(VCOptions *opts): success(false), time(0), inscribed_volume(0), options(opts) {
469  vc_file = opts->vc_file;
470  density = opts->density;
471  npoints = opts->npoints;
472  bounds_volume = opts->bounds_volume;
473  }
474 
476  void outputResults() const {
477 
478  egsInformation(" --------- Volume Correction Results -----------\n");
479  if (vc_file == "") {
480  egsInformation(" Time taken = %.4f s (%.3E s/point) \n", time, time/npoints);
481  egsInformation(" Density of points used = %.3E points/cm^-3\n", density);
482  egsInformation(" Number of points used = %G\n", npoints);
483  egsInformation(" Bounding shape volume = %.5E cm^3\n", bounds_volume);
484  egsInformation(" Inscribed geometry volume = %.5E cm^3\n", inscribed_volume);
485  options->rng->describeRNG();
486  }
487  else {
488  egsInformation(" Time taken = %.4f s\n", time);
489  egsInformation(" Volume correction file = %s\n", vc_file.c_str());
490  }
491 
492  egsInformation(" -----------------------------------------------\n");
493 
494  }
495 
496 };
497 
498 
503 vector<EGS_Float> applyVolumeCorrections(VCOptions *opts, HitCounterT hit_counter, vector<EGS_Float> uncorrected) {
504 
505  bool zero = opts->mode == ZERO_VOL;
506  double bounds_vol = opts->bounds_volume;
507  double npoints = opts->npoints;
508  vector<EGS_Float> corrected_vols(uncorrected);
509 
510  for (HitCounterT::iterator hi = hit_counter.begin(); hi != hit_counter.end(); hi++) {
511 
512  int base_reg = hi->first;
513  if (base_reg < 0) {
514  continue;
515  }
516 
517  int hits = hi->second;
518  EGS_Float corrected = uncorrected[base_reg] - bounds_vol*double(hits)/npoints;
519  corrected_vols[base_reg] = zero ? 0 : corrected;
520  }
521 
522  return corrected_vols;
523 }
524 
529 vector<EGS_Float> applyFileVolumeCorrections(VCOptions *opts, vector<RegVolume> &reg_volumes, vector<EGS_Float> uncorrected) {
530 
531  bool zero = opts->mode == ZERO_VOL;
532  vector<EGS_Float> corrected_vols(uncorrected);
533 
534  for (size_t rvc=0; rvc < reg_volumes.size(); rvc++) {
535  int base_reg = reg_volumes[rvc].first;
536  EGS_Float vol = reg_volumes[rvc].second;
537  corrected_vols[base_reg] = zero ? 0 : vol;
538  }
539 
540  return corrected_vols;
541 }
542 
543 vector<EGS_Float> getUncorrectedVolumes(EGS_BaseGeometry *base) {
544  vector<EGS_Float> uncorrected;
545  for (int ir=0; ir < base->regions(); ir++) {
546  EGS_Float vol = base->getVolume(ir);
547  uncorrected.push_back(vol);
548  }
549  return uncorrected;
550 
551 }
552 
559  vector<EGS_BaseGeometry *> inscribed, vector<EGS_AffineTransform *> transforms) {
560 
561  EGS_Timer timer;
562  timer.start();
563 
564  VCResults results(opts);
565 
566  results.uncorrected_volumes = getUncorrectedVolumes(base);
567 
568  EGS_Vector point;
569 
570  EGS_I64 n_in_inscribed = 0;
571 
572  //use first inscribed object to test if point is in inscribed geom
573  EGS_BaseGeometry *first_inscribed = inscribed[0];
574  EGS_AffineTransform *first_transform = transforms[0];
575 
576  HitCounterT hit_counter;
577 
578  for (EGS_I64 i=0; i < opts->npoints; i++) {
579 
580  point = opts->getRandomPoint();
581 
582  EGS_Vector inscribed_point(point);
583  first_transform->transform(inscribed_point);
584 
585  if (!first_inscribed->isInside(inscribed_point)) {
586  continue;
587  }
588  n_in_inscribed += 1;
589 
590  // transform to every location and check which regions in the base it is
591  for (size_t sidx = 0; sidx < transforms.size(); sidx++) {
592 
593  EGS_Vector transformed(point);
594  transforms[sidx]->transform(transformed);
595  int base_reg = base->isWhere(transformed);
596  bool in_base = base_reg >= 0;
597 
598  if (in_base) {
599  results.regions_with_inscribed[base_reg].insert(inscribed[sidx]);
600  hit_counter[base_reg] += 1;
601  }
602 
603  }
604  }
605 
606  results.inscribed_volume = opts->bounds_volume*(double)n_in_inscribed/(int)opts->npoints;
607 
608  if (opts->mode != DISCOVERY_ONLY) {
609  results.corrected_volumes = applyVolumeCorrections(opts, hit_counter, results.uncorrected_volumes);
610  }
611  else {
612  results.corrected_volumes = results.uncorrected_volumes;
613  }
614 
615  results.success = true;
616 
617  results.time = timer.time();
618 
619  return results;
620 
621 }
622 
623 
624 bool isGZip(istream &vfile) {
625  return (vfile.get() == 0x1f && vfile.get() == 0x8b);
626 }
627 
628 
629 void readVolumes(istream &vfile, vector<RegVolume> &reg_volumes, RegionGeomSetT &reg_with_inscribed, vector<EGS_BaseGeometry *> inscribed) {
630  int nrecords;
631  vfile >> nrecords;
632  for (int rec = 0; rec < nrecords; rec++) {
633  int reg, ninscribed;
634  EGS_Float vol;
635  vfile >> reg >> vol >> ninscribed;
636  egsInformation("loaded %d/%d %f %d\n", reg, nrecords, vol, ninscribed);
637  reg_volumes.push_back(RegVolume(reg, vol));
638  for (int i=0; i < ninscribed; i++) {
639  int gidx;
640  vfile >> gidx;
641  reg_with_inscribed[reg].insert(inscribed[gidx]);
642  }
643  }
644 
645 }
646 
647 int loadVolumes(string fname, vector<RegVolume> &reg_volumes, RegionGeomSetT &reg_with_inscribed, vector<EGS_BaseGeometry *> inscribed) {
648  ifstream vfile(fname.c_str(), ios::binary);
649  if (!vfile.is_open()) {
650  return 1;
651  }
652 
653  if (isGZip(vfile)) {
654  vfile.close();
655 #ifdef HAS_GZSTREAM
656  igzstream gzf(fname.c_str());
657  readVolumes(gzf, reg_volumes, reg_with_inscribed, inscribed);
658  gzf.close();
659 #else
660  egsWarning("Tried to load gzip volume correction file but not compiled with gzstream.\n");
661  return 1;
662 #endif
663  }
664  else {
665  vfile.close();
666  ifstream vfile2(fname.c_str(), ios::in);
667  readVolumes(vfile2, reg_volumes, reg_with_inscribed, inscribed);
668  vfile2.close();
669  }
670 
671  return 0;
672 
673 }
674 
675 
679  vector<EGS_BaseGeometry *> inscribed, vector<EGS_AffineTransform *> transforms) {
680 
681  EGS_Timer timer;
682  timer.start();
683 
684  VCResults results(opts->vc_file);
685  results.uncorrected_volumes = getUncorrectedVolumes(base);
686 
687  vector<RegVolume> reg_volumes;
688  int error = loadVolumes(opts->vc_file, reg_volumes, results.regions_with_inscribed, inscribed);
689 
690  if (error) {
691  egsFatal(
692  "loadFileVolumeCorrections: failed to read "
693  "volumes from file '%s'\n", opts->vc_file.c_str()
694  );
695  }
696  if (opts->mode != DISCOVERY_ONLY) {
697  results.corrected_volumes = applyFileVolumeCorrections(opts, reg_volumes, results.uncorrected_volumes);
698  }
699  else {
700  results.corrected_volumes = results.uncorrected_volumes;
701  }
702  results.success = true;
703 
704  results.time = timer.time();
705 
706  return results;
707 
708 }
709 
710 }
711 
712 #endif
A class providing affine transformations.
void transform(EGS_Vector &v) const
Transforms the vector v.
Base geometry class. Every geometry class must be derived from EGS_BaseGeometry.
virtual EGS_Float getVolume(int ireg)
Calculates the volume of region ireg.
virtual bool isInside(const EGS_Vector &x)=0
Is the position x inside the geometry?
int regions() const
Returns the number of local regions in this geometry.
virtual int isWhere(const EGS_Vector &x)=0
In which region is poisition x?
Base shape class. All shapes in the EGSnrc C++ class library are derived from EGS_BaseShape.
Definition: egs_shapes.h:114
static EGS_BaseShape * createShape(EGS_Input *inp)
Create a shape from the information pointed to by inp.
Definition: egs_shapes.cpp:51
virtual EGS_Vector getRandomPoint(EGS_RandomGenerator *rndm)
Returns a random 3D vector.
Definition: egs_shapes.h:136
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
EGS_Input * getInputItem(const string &key) const
Same as the previous function but now ownership remains with the EGS_Input object.
Definition: egs_input.cpp:245
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
const string & getObjectType() const
Get the object type.
Base random number generator class. All random number generators should be derived from this class.
Definition: egs_rndm.h:67
static EGS_RandomGenerator * defaultRNG(int sequence=0)
Returns a pointer to the default egspp RNG.
Definition: egs_rndm.cpp:465
static EGS_RandomGenerator * createRNG(EGS_Input *inp, int sequence=0)
Create a RNG object from the information pointed to by inp and return a pointer to it.
Definition: egs_rndm.cpp:409
virtual void describeRNG() const
Describe this RNG.
Definition: egs_rndm.h:256
A simple class for measuring CPU time.
Definition: egs_timer.h:57
EGS_Float time()
Returns the CPU time in seconds since start() was called.
Definition: egs_timer.cpp:106
void start()
Starts the time measurement.
Definition: egs_timer.cpp:102
A class representing 3D vectors.
Definition: egs_vector.h:57
Volume correction initialization helper class.
Definition: volcor.h:242
void setRNG()
Definition: volcor.h:376
EGS_Float npoints
Definition: volcor.h:299
double bounds_volume
Definition: volcor.h:296
void setMode()
Definition: volcor.h:315
EGS_Float density
Definition: volcor.h:298
EGS_Vector getRandomPoint()
Definition: volcor.h:290
int setBoundsShape()
Definition: volcor.h:334
void setVCFile()
Definition: volcor.h:326
VCOptions(EGS_Input *inp)
Definition: volcor.h:249
void setDensity()
Definition: volcor.h:364
EGS_BaseGeometry class header file.
Global egspp functions header file.
EGS_Input class header file.
EGS_RandomGenerator class header file.
EGS_BaseShape and shape classes header file.
EGS_Timer class header file.
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.
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.
Region discovery/volume correction for auto envelope geometries.
Definition: volcor.h:78
VCResults findRegionsWithInscribed(VCOptions *opts, EGS_BaseGeometry *base, vector< EGS_BaseGeometry * > inscribed, vector< EGS_AffineTransform * > transforms)
Run the MC simulation.
Definition: volcor.h:558
VCResults loadFileResults(VCOptions *opts, EGS_BaseGeometry *base, vector< EGS_BaseGeometry * > inscribed, vector< EGS_AffineTransform * > transforms)
Load volume corrections from external file.
Definition: volcor.h:678
pair< int, EGS_Float > RegVolume
RegVolumeT is a pair of the form (RegionNumber, Volume)
Definition: volcor.h:101
std::map< int, EGS_I64 > HitCounterT
Definition: volcor.h:94
pair< EGS_BaseGeometry *, int > GeomRegPairT
Definition: volcor.h:89
vector< EGS_Float > applyFileVolumeCorrections(VCOptions *opts, vector< RegVolume > &reg_volumes, vector< EGS_Float > uncorrected)
Apply file volume corrections to base regions.
Definition: volcor.h:529
vector< EGS_Float > applyVolumeCorrections(VCOptions *opts, HitCounterT hit_counter, vector< EGS_Float > uncorrected)
Apply volume corrections to base regions.
Definition: volcor.h:503
VolCorMode
Definition: volcor.h:82
@ ZERO_VOL
Definition: volcor.h:84
@ DISCOVERY_ONLY
Definition: volcor.h:83
@ CORRECT_VOLUME
Definition: volcor.h:85
EGS_Float getShapeVolume(EGS_Input *shape_inp)
Definition: volcor.h:123
map< int, set< EGS_BaseGeometry * > > RegionGeomSetT
Definition: volcor.h:98
Struct used to collect and output results about a volume correction run.
Definition: volcor.h:425
RegionGeomSetT regions_with_inscribed
Definition: volcor.h:439
double inscribed_volume
Definition: volcor.h:435
double density
Definition: volcor.h:431
double ninscribed
Definition: volcor.h:433
vector< EGS_Float > corrected_volumes
Definition: volcor.h:441
double bounds_volume
Definition: volcor.h:434
void outputResults() const
Definition: volcor.h:476
vector< EGS_Float > uncorrected_volumes
Definition: volcor.h:440
EGS_Float time
Definition: volcor.h:430
double npoints
Definition: volcor.h:432