EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_autoenvelope.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ auto envelope geometry
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 # Alexandre Demelo
32 # Hannah Gallop
33 #
34 ###############################################################################
35 #
36 # egs_autoenvelope was developed for the Carleton Laboratory for
37 # Radiotherapy Physics.
38 #
39 ###############################################################################
40 */
41 
42 
48 #include "egs_input.h"
49 #include "egs_autoenvelope.h"
50 #include "../egs_gtransformed/egs_gtransformed.h"
51 
52 #include <cstdlib>
53 #include <algorithm>
54 #include <numeric>
55 #include <iostream>
56 #include <fstream>
57 
58 #ifdef HAS_SOBOL
59  #include "sobol.h"
60 #endif
61 
62 
63 string EGS_AENVELOPE_LOCAL EGS_AEnvelope::type = "EGS_AEnvelope";
64 string EGS_AENVELOPE_LOCAL EGS_ASwitchedEnvelope::type = "EGS_ASwitchedEnvelope";
65 /* only geometries that support getting regional volume can be used as phantoms */
66 const string EGS_AENVELOPE_LOCAL EGS_AEnvelope::allowed_base_geom_types[] = {"EGS_cSpheres", "EGS_cSphericalShell", "EGS_XYZGeometry", "EGS_RZ"};
67 
68 static char EGS_AENVELOPE_LOCAL geom_class_msg[] = "createGeometry(AEnvelope): %s\n";
69 static char EGS_AENVELOPE_LOCAL base_geom_keyword[] = "base geometry";
70 static char EGS_AENVELOPE_LOCAL inscribed_geom_keyword[] = "inscribed geometry";
71 static char EGS_AENVELOPE_LOCAL inscribed_geom_name_keyword[] = "inscribed geometry name";
72 static char EGS_AENVELOPE_LOCAL transformations_keyword[] = "transformations";
73 static char EGS_AENVELOPE_LOCAL type_keyword[] = "type";
74 static char EGS_AENVELOPE_LOCAL transformation_keyword[] = "transformation";
75 
76 static bool EGS_AENVELOPE_LOCAL inputSet = false;
77 
78 EGS_AEnvelope::EGS_AEnvelope(EGS_BaseGeometry *base_geom,
79  const vector<AEnvelopeAux> inscribed, const string &Name, bool debug, string output_vc_file) :
80  EGS_BaseGeometry(Name), base_geom(base_geom), debug_info(debug), output_vc(output_vc_file) {
81 
82  bool volcor_available = allowedBaseGeomType(base_geom->getType());
83  bool volcor_requested = inscribed.size() > 0 && inscribed[0].vcopts->mode == CORRECT_VOLUME;
84 
85  if (!volcor_available && volcor_requested) {
86 
87  string msg(
88  "EGS_AEnvelope:: Volume correction is not available for geometry type '%s (%s)'. "
89  "Geometry types must implement getVolume. Valid choices are:\n\t"
90  );
91 
92  int end = (int)(sizeof(allowed_base_geom_types)/sizeof(string));
93  for (int i=0; i < end; i++) {
94  msg += allowed_base_geom_types[i] + " ";
95  }
96  msg += "\nPlease set `action = discover -or- correct and zero volume` or use a different base geometry type.\n";
97  egsFatal(msg.c_str(), base_geom->getType().c_str(), base_geom->getName().c_str());
98  }
99 
100 
101  base_geom->ref();
102  nregbase = base_geom->regions();
103  is_convex = base_geom->isConvex();
104  has_rho_scaling = getHasRhoScaling();
105 
106  ninscribed = inscribed.size();
107  if (ninscribed == 0) {
108  egsFatal("EGS_AEnvelope: no inscribed geometries!\n");
109  }
110 
111  // nreg is total regions in geometry = nregbase + sum(nreginscribed_j)
112  nreg = nregbase;
113 
114  // create a transformed copy of the inscribed geometry
115  int global = nreg;
116  for (int geom_idx=0; geom_idx < ninscribed; geom_idx++) {
117 
118  EGS_BaseGeometry *inscribed_geom = inscribed[geom_idx].geom;
119  EGS_AffineTransform *transform = inscribed[geom_idx].transform;
120  VCOptions *vcopt = inscribed[geom_idx].vcopts;
121  EGS_TransformedGeometry *transformed = new EGS_TransformedGeometry(inscribed_geom, *transform);
122 
123  inscribed_geoms.push_back(transformed);
124  transforms.push_back(transform);
125  opts.push_back(vcopt);
126 
127  int ninscribed_reg= transformed->regions();
128  nreg += ninscribed_reg;
129 
130  for (int lreg = 0; lreg < ninscribed_reg; lreg++) {
131  GeomRegPairT local(transformed, lreg);
132  local_to_global_reg[local] = global;
133  global_reg_to_local[global] = local;
134  global++;
135  }
136  }
137 
138  geoms_in_region = new vector<EGS_BaseGeometry *>[nregbase];
139 
140  if (inscribed[0].vcopts->vc_file != "") {
141  vc_results = loadFileResults(inscribed[0].vcopts, base_geom, inscribed_geoms, transforms);
142  egsInformation("loaded from %s\n", inscribed[0].vcopts->vc_file.c_str());
143  }
144  else {
145  // now run volume correction and figure out which regions have inscribed geometries
146  vc_results = findRegionsWithInscribed(inscribed[0].vcopts, base_geom, inscribed_geoms, transforms);
147  }
148 
149 
150  nreg_with_inscribed = 0;
151  for (volcor::RegionGeomSetT::iterator it=vc_results.regions_with_inscribed.begin();
152  it!=vc_results.regions_with_inscribed.end(); it++) {
153  nreg_with_inscribed++;
154  copy(it->second.begin(), it->second.end(), back_inserter(geoms_in_region[it->first]));
155  }
156 
157  if (getNRegWithInscribed() == 0) {
158  egsFatal("EGS_AEnvelope:: Failed to find any regions with inscribed geometries\n");
159  }
160 
161  if (output_vc=="yes" || output_vc=="text" || output_vc=="gzip") {
162  writeVolumeCorrection();
163  }
164 
165  if (debug_info) {
166  printInfo();
167  }
168 
169 }
170 
171 
172 EGS_AEnvelope::~EGS_AEnvelope() {
173  if (!base_geom->deref()) {
174  delete base_geom;
175  }
176 }
177 
178 bool EGS_AEnvelope::allowedBaseGeomType(const string &geom_type) {
179  // Check if the input geometry is one that aenvelope can handle
180 
181  int end = (int)(sizeof(allowed_base_geom_types)/sizeof(string));
182 
183  for (int i=0; i<end; i++) {
184  if (allowed_base_geom_types[i] == geom_type) {
185  return true;
186  }
187  }
188 
189  return false;
190 }
191 
192 int EGS_AEnvelope::getGlobalRegFromLocalReg(EGS_BaseGeometry *g, int local_reg) {
193  return getGlobalRegFromLocal(volcor::GeomRegPairT(g, local_reg));
194 }
195 
196 int EGS_AEnvelope::getGlobalRegFromLocal(const volcor::GeomRegPairT local) const {
197 
198  map<volcor::GeomRegPairT, int>::const_iterator glob_it = local_to_global_reg.find(local);
199  if (glob_it != local_to_global_reg.end()) {
200  return (*glob_it).second;
201  }
202  return -1;
203 }
204 
205 volcor::GeomRegPairT EGS_AEnvelope::getLocalFromGlobalReg(int ireg) const {
206 
207  map<int, volcor::GeomRegPairT>::const_iterator glob_it = global_reg_to_local.find(ireg);
208  if (glob_it != global_reg_to_local.end()) {
209  return (*glob_it).second;
210  }
211  return volcor::GeomRegPairT();
212 }
213 
214 int EGS_AEnvelope::getNRegWithInscribed() const {
215  return nreg_with_inscribed;
216 }
217 
218 bool EGS_AEnvelope::isRealRegion(int ireg) const {
219 
220  bool is_outside = ireg < 0 || ireg >= nreg;
221  if (is_outside) {
222  return false;
223  }
224 
225  bool in_base_geom = ireg < nregbase;
226  if (in_base_geom) {
227  return base_geom->isRealRegion(ireg);
228  }
229 
230  volcor::GeomRegPairT local;
231  local = getLocalFromGlobalReg(ireg);
232  return local.first->isRealRegion(local.second);
233 
234 };
235 
236 
237 bool EGS_AEnvelope::isInside(const EGS_Vector &x) {
238  return base_geom->isInside(x);
239 };
240 
241 
242 int EGS_AEnvelope::isWhere(const EGS_Vector &x) {
243 
244  int base_reg = base_geom->isWhere(x);
245 
246  // which inscribed geometries are in this region
247  vector<EGS_BaseGeometry *> geoms_in_reg = getGeomsInRegion(base_reg);
248 
249  if (base_reg < 0 || geoms_in_reg.size()==0) {
250  return base_reg;
251  }
252 
253  // loop over all inscribed geometries in this region and check
254  // if position is inside any of them
255  for (vector<EGS_BaseGeometry *>::iterator geom=geoms_in_reg.begin(); geom!= geoms_in_reg.end(); ++geom) {
256  int inscribed_reg = (*geom)->isWhere(x);
257  if (inscribed_reg >= 0) {
258  return getGlobalRegFromLocalReg(*geom, inscribed_reg);
259  }
260  }
261 
262  // not in any of the inscribed geometries so must be in envelope region
263  return base_reg;
264 };
265 
266 int EGS_AEnvelope::inside(const EGS_Vector &x) {
267  return isWhere(x);
268 };
269 
270 int EGS_AEnvelope::medium(int ireg) const {
271 
272  if (ireg < nregbase) {
273  return base_geom->medium(ireg);
274  }
275 
276  volcor::GeomRegPairT local = getLocalFromGlobalReg(ireg);
277 
278  return local.first->medium(local.second);
279 };
280 
281 vector<EGS_BaseGeometry *> EGS_AEnvelope::getGeomsInRegion(int ireg) {
282  if (ireg < 0 || ireg >= nregbase) {
283  vector<EGS_BaseGeometry *> empty;
284  return empty;
285  }
286  return geoms_in_region[ireg];
287 };
288 
289 int EGS_AEnvelope::computeIntersections(int ireg, int n, const EGS_Vector &X,
290  const EGS_Vector &u, EGS_GeometryIntersections *isections) {
291 
292  // For a given position x, direction u and region number ireg, this
293  // method returns the number of intersections with the geometry and
294  // distances, medium indeces and relative mass densities for all
295  // intersections, or, if this number is larger than the size n of
296  // isections, -1 (but the n intersections are still put in isections).
297  // If the position is outside, the method checks if the trajectory
298  // intersects the geometry and if yes, puts in the first element of
299  // isections the distance to the entry point and then finds all other
300  // intersections as in the case of x inside.
301 
302  if (n < 1) {
303  return -1;
304  }
305 
306  int isec_idx = 0; // current intersection index
307  EGS_Float t; // distance to next boundary
308  EGS_Float ttot = 0; // total distance along path
309  EGS_Vector x(X); // current position
310 
311  int imed;
312 
313  bool outside_envelope = ireg < 0;
314  bool in_base_geom = ireg >= 0 && ireg < nregbase;
315 
316  if (outside_envelope) {
317  t = 1e30;
318 
319  // region we would hit
320  ireg = howfar(ireg,x,u,t,&imed);
321  bool would_not_intersect = ireg < 0;
322 
323  if (would_not_intersect) {
324  return 0;
325  }
326 
327  isections[0].t = t; // distance to entry point
328  isections[0].rhof = 1;
329  isections[0].ireg = -1;
330  isections[0].imed = -1;
331  ttot = t;
332 
333  x += u*t;
334 
335  isec_idx = 1;
336  }
337  else {
338  // we're already inside the envelope
339  imed = medium(ireg);
340  }
341 
342  // keep looping until we exit the geometry!
343  while (1) {
344  //egsInformation("in loop: j=%d ireg=%d imed=%d x=(%g,%g,%g)\n",
345  // j,ireg,imed,x.x,x.y,x.z);
346  //
347 
348  // why are we setting these here?
349  // on first pass if we were outside envelope then:
350  // ireg is set to entry region
351  // imed is set to med of entry region (set in howfar above)
352  // else
353  // ireg is region of input ireg (current region?)
354  // imed is medium of input ireg (current region?)
355  isections[isec_idx].imed = imed; //
356  isections[isec_idx].ireg = ireg;
357  isections[isec_idx].rhof = getRelativeRho(ireg);
358 
359  if (in_base_geom) { // in one of the regions of the base geometry
360 
361  t = 1e30;
362 
363  // next region will be in base geom by default
364  ireg = base_geom->howfar(ireg,x,u,t,&imed);
365 
366  // if there's inscribed geoms in this region see if we will be intersecting one
367  vector<EGS_BaseGeometry *> geoms_in_reg = getGeomsInRegion(ireg);
368  for (vector<EGS_BaseGeometry *>::iterator geom=geoms_in_reg.begin(); geom!= geoms_in_reg.end(); ++geom) {
369 
370  int inscribed_reg = (*geom)->howfar(-1, x, u, t, &imed);
371 
372  bool hits_inscribed = inscribed_reg >= 0;
373 
374  if (hits_inscribed) {
375  ireg = getGlobalRegFromLocalReg(*geom, inscribed_reg);
376  }
377  }
378 
379  ttot += t;
380  isections[isec_idx++].t = ttot;
381 
382  // left the geometry (implies that no inscribed geoms hit
383  if (ireg < 0) {
384  return isec_idx;
385  }
386 
387 
388  // over limit of number of intersections
389  if (isec_idx >= n) {
390  return -1;
391  }
392 
393  // move to next boundary
394  x += u*t;
395 
396  }
397  else {
398 
399  // in inscribed geometry
400  volcor::GeomRegPairT local = getLocalFromGlobalReg(ireg);
401 
402  int max_isec = n - isec_idx;
403 
404  int ninter_sec = local.first->computeIntersections(local.second, max_isec,
405  x, u, &isections[isec_idx]);
406 
407  // convert intersection region indexes to global indexex
408  int nmax = ninter_sec >= 0 ? ninter_sec + isec_idx : n;
409  for (int i=isec_idx; i < nmax; i++) {
410  isections[i].ireg = getGlobalRegFromLocalReg(local.first, isections[i].ireg);
411  isections[i].t += ttot;
412  }
413 
414  //egsInformation("last intersection: %g\n",isections[nm-1].t);
415  if (ninter_sec < 0) {
416  return ninter_sec;
417  }
418 
419  isec_idx += ninter_sec;
420 
421  if (isec_idx >= n) {
422  return -1;
423  }
424 
425  t = isections[isec_idx-1].t - ttot;
426  x += u*t;
427  ttot = isections[isec_idx-1].t;
428  ireg = base_geom->isWhere(x);
429  //egsInformation("new region: %d\n",ireg);
430 
431  if (ireg < 0) {
432  return isec_idx;
433  }
434 
435  imed = base_geom->medium(ireg);
436  }
437  }
438  return -1;
439 }
440 
441 EGS_Float EGS_AEnvelope::howfarToOutside(int ireg, const EGS_Vector &x, const EGS_Vector &u) {
442 
443  if (ireg < 0) {
444  return 0;
445  }
446 
447  EGS_Float d;
448 
449  bool in_base_geom = ireg < nregbase;
450  if (in_base_geom) {
451  d = base_geom->howfarToOutside(ireg, x, u);
452  }
453  else if (base_geom->regions() == 1) {
454  d = base_geom->howfarToOutside(0, x, u);
455  }
456  else {
457  int ir = base_geom->isWhere(x);
458  d = base_geom->howfarToOutside(ir,x,u);
459  }
460  return d;
461 };
462 
463 
464 int EGS_AEnvelope::howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u,
465  EGS_Float &t, int *newmed, EGS_Vector *normal) {
466 
467  bool inside_geom = ireg >= 0;
468  if (inside_geom) {
469 
470  bool inside_base_geom = ireg < nregbase;
471 
472  if (inside_base_geom) {
473 
474  int base_reg = base_geom->howfar(ireg,x,u,t,newmed,normal);
475  vector<EGS_BaseGeometry *> geoms_in_reg = getGeomsInRegion(ireg);
476 
477  bool no_inscribed_in_reg = geoms_in_reg.size() == 0;
478  if (no_inscribed_in_reg) {
479  return base_reg;
480  }
481 
482  int inscribed_global = -1;
483  for (vector<EGS_BaseGeometry *>::iterator geom=geoms_in_reg.begin(); geom!= geoms_in_reg.end(); ++geom) {
484  int local_reg = (*geom)->howfar(-1, x, u, t, newmed, normal);
485  bool hits_inscribed = local_reg >= 0;
486  if (hits_inscribed) {
487  inscribed_global = getGlobalRegFromLocalReg(*geom, local_reg);
488  }
489  }
490 
491  bool hit_inscribed_first = inscribed_global >= 0;
492 
493  if (hit_inscribed_first) {
494  return inscribed_global;
495  }
496 
497  return base_reg;
498  }
499  else {
500 
501  // if here, we are in an inscribed geometry.
502  volcor::GeomRegPairT local = getLocalFromGlobalReg(ireg);
503 
504  // and then check if we will hit a boundary in this geometry.
505  int new_local = local.first->howfar(local.second, x, u, t, newmed, normal);
506  bool hit_boundary_in_inscribed = new_local >=0;
507  if (hit_boundary_in_inscribed) {
508  return getGlobalRegFromLocalReg(local.first, new_local);
509  }
510 
511  // new_local < 0 implies that we have exited the inscribed geometry
512  // => check to see in which base geometry region we are.
513  int inew = base_geom->isWhere(x + u*t);
514  if (inew >= 0 && newmed) {
515  *newmed = base_geom->medium(inew);
516  }
517  return inew;
518  }
519  }
520 
521  // if here, we are outside the base geometry.
522  // check to see if we will enter.
523  int new_base_reg = base_geom->howfar(ireg,x,u,t,newmed,normal);
524  vector<EGS_BaseGeometry *> geoms_in_reg = getGeomsInRegion(new_base_reg);
525  bool has_geoms_in_reg = geoms_in_reg.size() > 0;
526 
527  //cout << "new reg "<<new_base_reg << " has geoms " << has_geoms_in_reg << " t "<<t<<endl;
528  if (new_base_reg >= 0 && has_geoms_in_reg) {
529 
530  // check if we will be inside an inscribed geometry when we enter
531  for (vector<EGS_BaseGeometry *>::iterator geom=geoms_in_reg.begin(); geom!= geoms_in_reg.end(); ++geom) {
532  int new_local_reg = (*geom)->isWhere(x+u*t);
533  bool landed_in_inscribed = new_local_reg >= 0;
534  if (landed_in_inscribed) {
535  if (newmed) {
536  *newmed = (*geom)->medium(new_local_reg);
537  }
538  return getGlobalRegFromLocalReg(*geom, new_local_reg);
539  }
540 
541  }
542  }
543 
544  return new_base_reg;
545 };
546 
547 
548 EGS_Float EGS_AEnvelope::hownear(int ireg, const EGS_Vector &x) {
549  bool inside_envelope = ireg >= 0;
550 
551  if (!inside_envelope) {
552  return base_geom->hownear(ireg, x);
553  }
554 
555  bool in_base_geom = ireg < nregbase ;
556  if (!in_base_geom) {
557  volcor::GeomRegPairT local = getLocalFromGlobalReg(ireg);
558  return local.first->hownear(local.second, x);
559  }
560 
561  EGS_Float tmin = base_geom->hownear(ireg, x);
562  if (tmin <= 0) {
563  return tmin;
564  }
565  vector<EGS_BaseGeometry *> geoms_in_reg = getGeomsInRegion(ireg);
566  for (vector<EGS_BaseGeometry *>::iterator geom=geoms_in_reg.begin(); geom!= geoms_in_reg.end(); ++geom) {
567  EGS_Float local_t = (*geom)->hownear(-1, x);
568  if (local_t < tmin) {
569  tmin = local_t;
570  if (tmin < 0) {
571  return tmin;
572  }
573  }
574  }
575  return tmin;
576 };
577 
578 bool EGS_AEnvelope::hasBooleanProperty(int ireg, EGS_BPType prop) const {
579  if (ireg >= 0 && ireg < nreg) {
580  if (ireg < nregbase) {
581  return base_geom->hasBooleanProperty(ireg,prop);
582  }
583  volcor::GeomRegPairT local = getLocalFromGlobalReg(ireg);
584  return local.first->hasBooleanProperty(local.second, prop);
585  }
586  return false;
587 };
588 
589 void EGS_AEnvelope::setBooleanProperty(EGS_BPType prop) {
590  setPropertyError("setBooleanProperty()");
591 };
592 
593 void EGS_AEnvelope::addBooleanProperty(int bit) {
594  setPropertyError("addBooleanProperty()");
595 };
596 
597 void EGS_AEnvelope::setBooleanProperty(EGS_BPType prop, int start, int end,int step) {
598  setPropertyError("setBooleanProperty()");
599 };
600 
601 void EGS_AEnvelope::addBooleanProperty(int bit,int start,int end,int step) {
602  setPropertyError("addBooleanProperty()");
603 };
604 
605 int EGS_AEnvelope::getMaxStep() const {
606  int nstep = base_geom->getMaxStep();
607  for (size_t j=0; j< inscribed_geoms.size(); ++j) {
608  nstep += inscribed_geoms[j]->getMaxStep();
609  }
610  return nstep + inscribed_geoms.size();
611 };
612 
613 void EGS_AEnvelope::getNextGeom(EGS_RandomGenerator *rndm) {
614  // calls getNextGeom on its component geometries to update dynamic
615  // geometries in the simulation
616  for (int j=0; j< inscribed_geoms.size(); ++j) {
617  inscribed_geoms[j]->getNextGeom(rndm);
618  }
619  base_geom->getNextGeom(rndm);
620 };
621 
622 void EGS_AEnvelope::updatePosition(EGS_Float time) {
623  // calls updatePosition on its component geometries to update dynamic
624  // geometries in the simulation
625  for (int j=0; j< inscribed_geoms.size(); j++) {
626  inscribed_geoms[j]->updatePosition(time);
627  }
628  base_geom->updatePosition(time);
629 };
630 
631 void EGS_AEnvelope::containsDynamic(bool &hasdynamic) {
632  // calls containsDynamic on its component geometries (only calls if
633  // hasDynamic is false, as if it is true we already found one)
634  for (int j=0; j< inscribed_geoms.size(); j++) {
635  if (!hasdynamic) {
636  inscribed_geoms[j]->containsDynamic(hasdynamic);
637  }
638  }
639  if (!hasdynamic) {
640  base_geom->containsDynamic(hasdynamic);
641  }
642 };
643 
644 bool EGS_AEnvelope::hasRhoScaling() {
645  if (has_rho_scaling) {
646  return has_rho_scaling;
647  }
648 
649  for (int j=0; j< inscribed_geoms.size(); j++) {
650  bool hasRS = inscribed_geoms[j]->hasRhoScaling();
651  if (hasRS) {
652  has_rho_scaling = hasRS;
653  return has_rho_scaling;
654  }
655  }
656 
657  return base_geom->hasRhoScaling();
658 };
659 
660 void EGS_AEnvelope::finishInitialization() {
661  for (int j=0; j< inscribed_geoms.size(); j++) {
662  inscribed_geoms[j]->finishInitialization();
663  }
664  base_geom->finishInitialization();
665 };
666 
667 
668 EGS_Float EGS_AEnvelope::getVolume(int ireg) {
669 
670  if (ireg < 0) {
671  return -1;
672  }
673  else if (ireg < nregbase) {
674  return vc_results.corrected_volumes[ireg];
675  }
676 
677  volcor::GeomRegPairT local = global_reg_to_local[ireg];
678 
679  return local.first->getVolume(local.second);
680 };
681 
682 EGS_Float EGS_AEnvelope::getCorrectionRatio(int ireg) {
683 
684  if (0 <= ireg && ireg < nregbase) {
685  return vc_results.corrected_volumes[ireg]/vc_results.uncorrected_volumes[ireg];
686  }
687 
688  return 1;
689 };
690 
691 
692 /* Print information about the geometry. If `print debug info = yes` is present
693  * in the geometry, extra information about which regions of the base geometry
694  * contain inscribed geometries and how their volumes were corrected.
695 */
696 void EGS_AEnvelope::printInfo() const {
697 
699 
700  if (debug_info) {
701 
702  for (int ir=0; ir < nregbase; ir++) {
703  if (fabs(vc_results.uncorrected_volumes[ir] - vc_results.corrected_volumes[ir]) > 1E-8) {
704  egsInformation(" volume of region %d was corrected from %.5E g to %.5E g\n",
705  ir, vc_results.uncorrected_volumes[ir], vc_results.corrected_volumes[ir]);
706  }
707  }
708 
709  for (int ir=0; ir < nregbase; ir++) {
710  if (geoms_in_region[ir].size() > 0) {
711  egsInformation(" region %d has %d inscribed geometries\n", ir, geoms_in_region[ir].size());
712  }
713  }
714  }
715 
716  vc_results.outputResults();
717 
718  egsInformation(" base geometry = %s (type %s) nreg=%d\n",base_geom->getName().c_str(),
719  base_geom->getType().c_str(), nregbase, base_geom->regions());
720  egsInformation(" # of inscribed geometries= %d in %d regions\n",inscribed_geoms.size(), getNRegWithInscribed());
721 
722 
723  egsInformation("=======================================================\n");
724 }
725 
726 void EGS_AEnvelope::writeVCToFile(ostream &out) {
727 
728  vector<int> to_write;
729 
730  for (int i=0; i < base_geom->regions(); i++) {
731  EGS_Float cor = vc_results.corrected_volumes[i];
732  EGS_Float uncor = vc_results.uncorrected_volumes[i];
733  bool has_correction = fabs(cor-uncor) > 1E-8;
734  if (has_correction) {
735  to_write.push_back(i);
736  }
737  }
738 
739  size_t nrecords = to_write.size();
740  out << nrecords << endl;
741 
742  for (size_t i=0; i<to_write.size(); i++) {
743  int ir = to_write[i];
744  EGS_Float cor = vc_results.corrected_volumes[ir];
745 
746  // find out the indexes of geometries inscribed in this region (if any)
747  vector<EGS_BaseGeometry *> geoms_in_reg = getGeomsInRegion(ir);
748  vector<int> geom_idxs_in_reg;
749  for (size_t i=0; i<geoms_in_reg.size(); i++) {
750  size_t pos = find(inscribed_geoms.begin(), inscribed_geoms.end(), geoms_in_reg[i]) - inscribed_geoms.begin();
751  if (pos < inscribed_geoms.size()) {
752  geom_idxs_in_reg.push_back(pos);
753  }
754  }
755 
756  int ninscribed = (int)geom_idxs_in_reg.size();
757 
758  out << ir << " " << cor;
759  // write number of and indexes of geometries inscribed in this region
760  out << " " << ninscribed;
761  for (size_t gidx = 0; gidx < geom_idxs_in_reg.size(); gidx++) {
762  out << " " <<geom_idxs_in_reg[gidx];
763  }
764  out << endl;
765  }
766 
767 }
768 
769 void EGS_AEnvelope::writeVolumeCorrection() {
770 
771  egsInformation("\nVolume Correction Output File \n%s\n", string(80,'=').c_str());
772 
773  string gname = base_geom->getName();
774  string fname = gname+".autoenv.volcor";
775  fname += (output_vc == "gzip" ? ".gz" : "");
776  string mode = (output_vc == "gzip" ? "gzip" : "text");
777 
779  "Writing volume correction file for %s to %s file %s\n",
780  gname.c_str(), mode.c_str(), fname.c_str());
781 
782  if (output_vc == "gzip") {
783 #ifdef HAS_GZSTREAM
784  ogzstream outgz(fname.c_str());
785  writeVCToFile(outgz);
786  outgz.close();
787 #endif
788  }
789  else {
790  ofstream out(fname.c_str());
791  writeVCToFile(out);
792  out.close();
793  }
794 
795 }
796 
797 
798 /* check if the base or any of inscribed geometries have density scaling on */
799 bool EGS_AEnvelope::getHasRhoScaling() {
800 
802  if (has_rho_scaling) {
803  return true;
804  }
805 
806  for (size_t geom_idx=0; geom_idx < inscribed_geoms.size(); geom_idx++) {
807  if (inscribed_geoms[geom_idx]->hasRhoScaling()) {
808  return true;
809  }
810  }
811  return false;
812 
813 }
814 
815 
816 void EGS_AEnvelope::setMedia(EGS_Input *,int,const int *) {
817  egsWarning("EGS_AEnvelope::setMedia: don't use this method. Use the\n"
818  " setMedia() methods of the geometry objects that make up this geometry\n");
819 }
820 
821 
822 void EGS_AEnvelope::setRelativeRho(int start, int end, EGS_Float rho) {
823  setRelativeRho(0);
824 }
825 
826 
827 void EGS_AEnvelope::setRelativeRho(EGS_Input *) {
828  egsWarning("EGS_AEnvelope::setRelativeRho(): don't use this method."
829  " Use the\n setRelativeRho methods of the geometry objects that make up"
830  " this geometry\n");
831 }
832 
833 
834 EGS_Float EGS_AEnvelope::getRelativeRho(int ireg) const {
835  if (ireg < 0 || ireg >= nreg) {
836  return 1;
837  }
838  if (ireg < nregbase) {
839  EGS_Float v = base_geom->getRelativeRho(ireg);
840  return v;
841  }
842  volcor::GeomRegPairT local = getLocalFromGlobalReg(ireg);
843  if (local.first) {
844  return local.first->getRelativeRho(local.second);
845  }
846  return 1;
847 };
848 
849 
850 /*************************************************************************/
851 /* Switched envelope *****************************************************/
852 /*************************************************************************/
853 
854 EGS_ASwitchedEnvelope::EGS_ASwitchedEnvelope(EGS_BaseGeometry *base_geom,
855  const vector<AEnvelopeAux> inscribed, const string &Name, bool debug, string output_vc_file):
856  EGS_AEnvelope(base_geom, inscribed, Name, debug, output_vc_file) {
857 
858  // activate a single geometry
859  cur_ptr = 0;
860  active_inscribed.push_back(inscribed_geoms[cur_ptr]);
861 
862 };
863 
864 
865 //TODO: this gets called a lot and is probably quite slow. Instead of doing a
866 //set intersection on every call we can probably do it once when activated
867 //geometries change and cache it
868 vector<EGS_BaseGeometry *> EGS_ASwitchedEnvelope::getGeomsInRegion(int ireg) {
869  // find which geometries are present AND active in this region
870  vector<EGS_BaseGeometry *> active_in_reg;
871  if ((0 <= ireg) && (ireg < nregbase)) {
872  set_intersection(
873  geoms_in_region[ireg].begin(), geoms_in_region[ireg].end(),
874  active_inscribed.begin(), active_inscribed.end(),
875  back_inserter(active_in_reg)
876  );
877  }
878 
879  return active_in_reg;
880 
881 }
882 
883 
884 void EGS_ASwitchedEnvelope::setActiveGeometries(vector<EGS_BaseGeometry *> geoms) {
885  active_inscribed.clear();
886  active_inscribed = geoms;
887  cur_ptr = -1;
888  if (geoms.size() <= 0) {
889  return;
890  }
891 
892  for (size_t i=0; i < inscribed_geoms.size(); i++) {
893  if (inscribed_geoms[i] == geoms[0]) {
894  cur_ptr = i;
895  return;
896  }
897  }
898 }
899 
900 
901 void EGS_ASwitchedEnvelope::setActiveGeometries(vector<int> geom_indexes) {
902 
903  active_inscribed.clear();
904 
905  for (size_t i = 0; i < geom_indexes.size(); i++) {
906  int idx = geom_indexes[i];
907  if (idx < 0 || idx >= ninscribed) {
908  egsFatal("EGS_ASwitchedEnvelope:: %d is not a valid geometry index\n", idx);
909  }
910  active_inscribed.push_back(inscribed_geoms[i]);
911  }
912 
913  cur_ptr = geom_indexes[0];
914 
915 }
916 
917 
919 
920  if ((0 <= ireg) && (ireg < nregbase)) {
921  size_t nactive = getGeomsInRegion(ireg).size();
922  return nactive > 0;
923  }
924 
925  return false;
926 }
927 
928 
930 
931  if ((0 <= ireg) && (ireg < nregbase)) {
932  size_t nactive = getGeomsInRegion(ireg).size();
933  size_t ntotal = EGS_AEnvelope::getGeomsInRegion(ireg).size();
934  return nactive < ntotal;
935  }
936 
937  return false;
938 
939 }
940 
941 void EGS_ASwitchedEnvelope::setActiveByIndex(int inscribed_index) {
942  vector<EGS_BaseGeometry *> to_activate;
943  to_activate.push_back(inscribed_geoms[inscribed_index]);
944  setActiveGeometries(to_activate);
945  cur_ptr = inscribed_index;
946 }
947 
948 void EGS_ASwitchedEnvelope::activateByIndex(int inscribed_index) {
949  active_inscribed.push_back(inscribed_geoms[inscribed_index]);
950 }
951 
952 void EGS_ASwitchedEnvelope::deactivateByIndex(int inscribed_index) {
953  vector<EGS_BaseGeometry *>::iterator loc = find(
954  active_inscribed.begin(), active_inscribed.end(),
955  inscribed_geoms[inscribed_index]
956  );
957  if (loc != active_inscribed.end()) {
958  active_inscribed.erase(loc);
959  }
960 }
961 
963  cur_ptr = cur_ptr == ninscribed -1 ? 0 : cur_ptr + 1;
964  vector<EGS_BaseGeometry *> to_activate;
965  to_activate.push_back(inscribed_geoms[cur_ptr]);
966  setActiveGeometries(to_activate);
967 }
968 
969 
970 vector<EGS_AffineTransform *> EGS_AEnvelope::createTransforms(EGS_Input *input) {
971 
972  vector<EGS_AffineTransform *> transforms;
973  if (input) {
974  EGS_Input *i;
975 
976  while ((i = input->takeInputItem(transformation_keyword))) {
978  if (!transform) {
979  egsWarning("Invalid transform input given\n");
980 
981  }
982  transforms.push_back(transform);
983  delete i;
984 
985  }
986  }
987 
988  return transforms;
989 
990 }
991 
992 
993 extern "C" {
994 
995  static void setInputs() {
996  inputSet = true;
997 
998  setBaseGeometryInputs(false);
999 
1000  geomBlockInput->getSingleInput("library")->setValues({"egs_autoenvelope"});
1001 
1002  // Format: name, isRequired, description, vector string of allowed values
1003  geomBlockInput->addSingleInput("type", false, "The type of auto envelope. A switched envelope only provides extra functionality in applications that specifically utilize it.", {"EGS_AEnvelope", "EGS_ASwitchedEnvelope"});
1004  geomBlockInput->addSingleInput("base geometry", true, "The name of a predefined geometry that other geometries will be inscribed inside. They must not touch the surfaces of this geometry.");
1005  geomBlockInput->addSingleInput("print debug info", false, "Whether or not to output additional debugging information. Defaults to No.", {"Yes", "No"});
1006  geomBlockInput->addSingleInput("output volume correction file", false, "Whether to output, or the format for the output of a file containing volume corrections. Defaults to No. Using Yes is equivalent to Text.", {"Yes", "No", "Text", "gzip"});
1007 
1008  auto inscribedPtr = geomBlockInput->addBlockInput("inscribed geometry", true);
1009  inscribedPtr->addSingleInput("inscribed geometry name", true, "Name of a previously defined geometry");
1010  auto transfPtr = inscribedPtr->addBlockInput("transformations", false);
1011  addTransformationBlock(transfPtr);
1012 
1013  auto regionPtr = inscribedPtr->addBlockInput("region discovery", true);
1014  regionPtr->addSingleInput("action", false, "Optionally apply volume corrections or zero volumes. Defaults to Discover.", {"discover", "discover and correct volume", "discover and zero volume"});
1015  auto volFilePtr = regionPtr->addSingleInput("volume correction file", false, "The path to a volume correction file to use instead of calculating volumes by sampling.");
1016  regionPtr->addSingleInput("density of random points (cm^-3)", false, "Sampling density for volume corrections. Defaults to 1e8.");
1017 
1018  auto shapePtr = regionPtr->addBlockInput("shape", false);
1019  setShapeInputs(shapePtr);
1020 
1021  addRngDefinitionBlock(regionPtr);
1022  }
1023 
1024  EGS_AENVELOPE_EXPORT shared_ptr<EGS_BlockInput> getInputs() {
1025  if (!inputSet) {
1026  setInputs();
1027  }
1028  return geomBlockInput;
1029  }
1030 
1031  EGS_AENVELOPE_EXPORT EGS_BaseGeometry *createGeometry(EGS_Input *input) {
1032 
1033  if (!input) {
1034  egsWarning(geom_class_msg, "null input");
1035  return 0;
1036  }
1037 
1038  bool debug;
1039  vector<string> debug_choices;
1040  debug_choices.push_back("no");
1041  debug_choices.push_back("yes");
1042  debug = input->getInput("print debug info", debug_choices, 0);
1043 
1044  int output_vc_file_choice;
1045  vector<string> vc_file_choices;
1046  vc_file_choices.push_back("no");
1047  vc_file_choices.push_back("yes");
1048  vc_file_choices.push_back("text");
1049  vc_file_choices.push_back("gzip");
1050  output_vc_file_choice = input->getInput("output volume correction file", vc_file_choices, 0);
1051  string output_vc_file = vc_file_choices[output_vc_file_choice];
1052 
1053 #ifndef HAS_GZSTREAM
1054  if (output_vc_file == "gzip") {
1055  egsWarning(
1056  "GZip file output requested but not compiled with gzstream.\n"
1057  "Please recompile with gzstream support.\n"
1058  );
1059  return 0;
1060  }
1061 #endif
1062 
1063 
1064 
1065  string base_geom_name;
1066  int err = input->getInput(base_geom_keyword, base_geom_name);
1067  if (err) {
1068  egsWarning(geom_class_msg, ("'"+string(base_geom_keyword)+"' input not found").c_str());
1069  return 0;
1070  }
1071 
1072  string type;
1073  err = input->getInput(type_keyword, type);
1074  if (err) {
1075  type = "AEnvelope";
1076  }
1077 
1078  EGS_BaseGeometry *base_geom = EGS_BaseGeometry::getGeometry(base_geom_name);
1079  if (!base_geom) {
1080  egsWarning(geom_class_msg, ("Unable to find geometry '"+base_geom_name+"'").c_str());
1081  return 0;
1082  }
1083 
1084  EGS_Input *inscribed_input = input->takeInputItem(inscribed_geom_keyword);
1085  if (!inscribed_input) {
1086  egsWarning(geom_class_msg, ("Missing '"+string(inscribed_geom_keyword)+"' input item").c_str());
1087  return 0;
1088  }
1089 
1090  string inscribed_geom_name;
1091  err = inscribed_input->getInput(inscribed_geom_name_keyword, inscribed_geom_name);
1092  if (err) {
1093  egsWarning(geom_class_msg, ("'"+string(inscribed_geom_name_keyword)+"' input not found").c_str());
1094  return 0;
1095  }
1096 
1097  EGS_BaseGeometry *inscribed_geom = EGS_BaseGeometry::getGeometry(inscribed_geom_name);
1098  if (!inscribed_geom) {
1099  egsWarning(geom_class_msg, ("Unable to find geometry '"+inscribed_geom_name+"'").c_str());
1100  return 0;
1101  }
1102 
1103  vector<EGS_AffineTransform *> transforms;
1104  EGS_Input *trans_input = inscribed_input->takeInputItem(transformations_keyword);
1105  if (trans_input) {
1106  transforms = EGS_AEnvelope::createTransforms(trans_input);
1107  }
1108 
1109  delete trans_input;
1110 
1111  EGS_Input *volcor_input = inscribed_input->takeInputItem("region discovery");
1112  VCOptions *vcopts = new VCOptions(volcor_input);
1113  if (!vcopts->valid) {
1114  egsWarning(geom_class_msg, "Missing or invalid 'region discovery' input item");
1115  return 0;
1116  }
1117 
1118  delete inscribed_input;
1119 
1120 
1121  vector<AEnvelopeAux> inscribed;
1122  if (transforms.size()>0) {
1123  for (size_t i=0; i < transforms.size(); i++) {
1124  inscribed.push_back(AEnvelopeAux(inscribed_geom, transforms[i], vcopts));
1125  }
1126  }
1127  else {
1128  EGS_AffineTransform *unityt = new EGS_AffineTransform();
1129  inscribed.push_back(AEnvelopeAux(inscribed_geom, unityt, vcopts));
1130  }
1131 
1132  EGS_BaseGeometry *result;
1133  if (type == "EGS_ASwitchedEnvelope") {
1134  result = new EGS_ASwitchedEnvelope(base_geom, inscribed, "", (bool)debug, output_vc_file);
1135  }
1136  else {
1137  result = new EGS_AEnvelope(base_geom, inscribed, "", (bool)debug, output_vc_file);
1138  }
1139 
1140  result->setName(input);
1141  return result;
1142  }
1143 
1144 }
A fast envelope geometry with automatic region detection.
vector< EGS_BaseGeometry * > inscribed_geoms
The inscribed geometries.
vector< EGS_AffineTransform * > transforms
The inscribed geometries.
static vector< EGS_AffineTransform * > createTransforms(EGS_Input *inpt)
Take a block of transformations and return vector of EGS_AffineTransforms.
static string type
Geometry type.
static bool allowedBaseGeomType(const string &geom_type)
function for checking whether a given geometry type is allowed to be used as a base geometry
int nregbase
Number of regions in the base geometry.
EGS_BaseGeometry * base_geom
The envelope geometry.
void setMedia(EGS_Input *, int, const int *)
Don't set media for an envelope geometry.
int ninscribed
Number of regions in the base geometry.
This geometry type allows you to activate and deactivate inscribed geometries in custom egspp user co...
void deactivateByIndex(int inscribed_index)
bool hasInactiveGeom(int ireg)
void activateByIndex(int inscribed_index)
void setActiveGeometries(vector< EGS_BaseGeometry * > geoms)
void setActiveByIndex(int inscribed_index)
static string type
Geometry type.
bool hasActiveGeom(int ireg)
A class providing affine transformations.
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 geometry class. Every geometry class must be derived from EGS_BaseGeometry.
virtual EGS_Float hownear(int ireg, const EGS_Vector &x)=0
Calculate the distance to a boundary for position x in any direction.
int deref()
Decrease the reference count to this geometry.
virtual bool hasBooleanProperty(int ireg, EGS_BPType prop) const
Is the boolean property prop set for region ireg ?
virtual EGS_Float howfarToOutside(int ireg, const EGS_Vector &x, const EGS_Vector &u)
virtual const string & getType() const =0
Get the geometry type.
int nreg
Number of local regions in this geometry.
virtual int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u, EGS_Float &t, int *newmed=0, EGS_Vector *normal=0)=0
Calculate the distance to a boundary from x along the direction u.
bool has_rho_scaling
Does this geometry have relative mass density scvaling?
virtual bool hasRhoScaling()
Does this geometry object have a mass density scaling feature?
virtual bool isRealRegion(int ireg) const
Returnes true if ireg is a real region, false otherwise.
void setName(EGS_Input *inp)
Set the name of the geometry from the input inp.
virtual bool isInside(const EGS_Vector &x)=0
Is the position x inside the geometry?
const string & getName() const
Get the name of this geometry.
virtual EGS_Float getRelativeRho(int ireg) const
Get the relative mass density in region ireg.
bool isConvex() const
Is the geometry convex?
virtual int getMaxStep() const
Returns the maximum number of steps through the geometry.
virtual int medium(int ireg) const
Returns the medium index in region ireg.
int regions() const
Returns the number of local regions in this geometry.
virtual void printInfo() const
Print information about this geometry.
int ref()
Increase the reference count to this geometry.
static EGS_BaseGeometry * getGeometry(const string &Name)
Get a pointer to the geometry named Name.
virtual int isWhere(const EGS_Vector &x)=0
In which region is poisition x?
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
Base random number generator class. All random number generators should be derived from this class.
Definition: egs_rndm.h:90
A transformed geometry.
A class representing 3D vectors.
Definition: egs_vector.h:57
Volume correction initialization helper class.
Definition: volcor.h:242
An envelope geometry with automatic inscribed region detection (inspired by EGS_FastEnvelope)
EGS_GLIB_EXPORT EGS_BaseGeometry * createGeometry(EGS_Input *input)
Definition: egs_glib.cpp:84
EGS_Input 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.
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< EGS_BaseGeometry *, int > GeomRegPairT
Definition: volcor.h:89
@ CORRECT_VOLUME
Definition: volcor.h:85
A helper class for initializing auto envelopes.
EGS_I32 ireg
region index
EGS_Float t
distance to next region boundary
EGS_Float rhof
relative mass density in that region
EGS_I32 imed
medium index
vector< EGS_Float > corrected_volumes
Definition: volcor.h:441
void outputResults() const
Definition: volcor.h:476
vector< EGS_Float > uncorrected_volumes
Definition: volcor.h:440