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