EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_cones.h
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ cones geometry headers
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: Iwan Kawrakow, 2005
25 #
26 # Contributors: Frederic Tessier
27 # Ernesto Mainegra-Hing
28 # Reid Townson
29 # Marc Chamberland
30 # Randle Taylor
31 #
32 ###############################################################################
33 */
34 
35 
41 #ifndef EGS_CONES_
42 #define EGS_CONES_
43 
44 #include "egs_base_geometry.h"
45 #include "egs_functions.h"
46 #include "egs_math.h"
47 
48 using namespace std;
49 
50 #ifdef WIN32
51 
52  #ifdef BUILD_CONES_DLL
53  #define EGS_CONES_EXPORT __declspec(dllexport)
54  #else
55  #define EGS_CONES_EXPORT __declspec(dllimport)
56  #endif
57  #define EGS_CONES_LOCAL
58 
59 #else
60 
61  #ifdef HAVE_VISIBILITY
62  #define EGS_CONES_EXPORT __attribute__ ((visibility ("default")))
63  #define EGS_CONES_LOCAL __attribute__ ((visibility ("hidden")))
64  #else
65  #define EGS_CONES_EXPORT
66  #define EGS_CONES_LOCAL
67  #endif
68 
69 #endif
70 
71 // the geometry methods of cones are fairly CPU intensive
72 // => we will not bother to use templates to have separate
73 // versions for x-cones, y-cones, etc.
74 
126 class EGS_CONES_EXPORT EGS_SimpleCone : public EGS_BaseGeometry {
127 
128 protected:
129 
130  EGS_Vector xo; // the cone apex.
131  EGS_Vector a; // the cone axis.
132  EGS_Float gamma, g12; // tangent of opening angle and 1+gamma^2
133  EGS_Float g12i; // 1/sqrt(g12)
134  EGS_Float d1; // (-) closing plane distance (if closed)
135  EGS_Float Ro, Ro2; // radius at base (if closed) and Ro^2
136  bool open; // true if cone is open
137  bool is_cyl; // true if cone is cylindrical (within hard-coded tolerance)
138  static string type;
139 
140 public:
141 
142  // constructor
143  EGS_SimpleCone(const EGS_Vector &Xo, const EGS_Vector &A, EGS_Float Gamma,
144  const EGS_Float *distance=0, const string &N="")
145 
146  : EGS_BaseGeometry(N), xo(Xo), a(A), gamma(Gamma), g12(1+Gamma*Gamma),
147  open(true), is_cyl(false) {
148 
149  a.normalize();
150  g12i = 1/sqrt(g12);
151 
152  // closed cone
153  if (distance) {
154  if (*distance > 0) {
155  open = false;
156  d1 = -(*distance);
157  Ro = -d1*gamma;
158  Ro2 = Ro*Ro;
159  }
160  else {
161  egsWarning("EGS_SimpleCone: the distance to the closing plane must be positive\n");
162  }
163  }
164  nreg = 1;
165  }
166 
167  // constructor
168  EGS_SimpleCone(const EGS_Vector &Xo, const EGS_Vector &A, EGS_Float dz,
169  EGS_Float Rtop, EGS_Float Rbottom, bool Open=true, const string &N="")
170 
171  : EGS_BaseGeometry(N), a(A), open(Open), is_cyl(false) {
172 
173  a.normalize();
174 
175  // avoid round-off problems.
176  if (fabs(Rtop - Rbottom) < boundaryTolerance) { // flag cylinders to avoid round-off problems
177  is_cyl = true;
178  xo = Xo;
179  Ro = Rtop;
180  Ro2 = Ro*Ro;
181  gamma = 0;
182  g12 = 1;
183  g12i = 1;
184  d1 = -dz;
185  }
186  else if (Rtop < Rbottom) { // cone is opening along axis vector a
187  EGS_Float aux = dz*Rtop/(Rbottom-Rtop);
188  xo = Xo - a*aux;
189  gamma = Rbottom/(aux+dz);
190  g12 = 1 + gamma*gamma;
191  g12i = 1/sqrt(g12);
192  d1 = -dz-aux;
193  }
194  else { // cone is closing along axis vector a
195  EGS_Float aux = dz*Rbottom/(Rtop-Rbottom);
196  xo = Xo + a*(aux+dz);
197  a *= (-1);
198  gamma = Rtop/(aux+dz);
199  g12 = 1 + gamma*gamma;
200  g12i = 1/sqrt(g12);
201  d1 = -dz-aux;
202  }
203  }
204 
205  // destructor
206  ~EGS_SimpleCone() {}
207 
208  // isInside
209  bool isInside(const EGS_Vector &x) {
210 
211  EGS_Vector xp(x-xo); // vector from cone apex xo to test point x
212  EGS_Float aa = xp*a; // projection of xp on axis vector a
213 
214  // check bounds along cone axis
215  if (!open && (aa < 0 || aa+d1 > 0)) {
216  return false;
217  }
218 
219  // check cylinder radius
220  if (is_cyl) {
221  EGS_Float r2 = xp.length2() - aa*aa;
222  if (r2 <= Ro2) {
223  return true;
224  }
225  return false;
226  }
227 
228  // check cone radius
229  EGS_Float r2 = xp.length2();
230  if (r2 <= aa*aa*g12) {
231  return true;
232  }
233  return false;
234  }
235 
236  // isWhere
237  int isWhere(const EGS_Vector &x) {
238  if (isInside(x)) {
239  return 0;
240  }
241  return -1;
242  }
243 
244  // inside
245  int inside(const EGS_Vector &x) {
246  return isWhere(x);
247  }
248 
249  // howfar
250  int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u,
251  EGS_Float &t, int *newmed=0, EGS_Vector *normal=0) {
252 
253  EGS_Vector xp(x-xo); // vector from cone apex xo to test point x
254  EGS_Float aa = xp*a; // projection of xp on cone axis a
255  EGS_Float b = u*a; // cos(t) between axis a and step vector u
256  EGS_Float r2 = xp.length2(); // length^2 of xp
257  EGS_Float c = u*xp; // projection of xp on step vector u
258 
259  // handle cylinder
260  if (is_cyl) {
261 
262  // u is parallel to a (u and a are normalized)
263  if (fabs(b) >= 1) {
264  return ireg;
265  }
266 
267  EGS_Float A = 1 - b*b; // A = 1-cos^2(t) = sin^2(t)
268  EGS_Float B = c - b*aa; // B = u * [xp - (xp*a)a] = u * n
269  EGS_Float C = r2 - aa*aa - Ro2; // C = n^2 - R0^2
270  EGS_Float d; // distance to cylinder along u
271 
272  // from inside cylinder
273  if (!ireg) {
274  EGS_Float D = B*B-A*C; // R0^2 - [ n^2 - ((n*u)/sint)^2 ]
275  if (D < 0 || (C > 0 && B > 0)) {
276  d = 0;
277  }
278  // ^ hopefully a precision problem.
279  else {
280  d = B<=0 ? (sqrt(D)-B)/A : -C/(sqrt(D)+B);
281  }
282  }
283  // from outside cylinder
284  else {
285  if (C < 0) { // inside cylinder: fp precision error
286  if (B < 0) {
287  d = 0; // assume we are on the cylinder
288  }
289  else {
290  return ireg; // u points away from cylinder
291  }
292  }
293  else {
294  if (B >= 0) {
295  return ireg; // u points away from cylinder
296  }
297  EGS_Float D = B*B-A*C; // R0^2 - [ n^2 - ((n*u)/sint)^2 ]
298  if (D < 0) {
299  return ireg; // u line does not intersect cylinder
300  }
301  d = C/(sqrt(D)-B); // solve (n + d*(u-a(u*a)))^2 - R0^2 = 0
302  }
303  }
304 
305  // distance to cylinder is longer than step length
306  if (d > t) {
307  return ireg;
308  }
309 
310  // intersection with cylinder is beyond boundary planes
311  if (!open) {
312  EGS_Float aux = aa + d*b;
313  if (aux < 0 || aux+d1 > 0) {
314  return ireg;
315  }
316  }
317 
318  // intersect with cylinder
319  t = d;
320  if (newmed && ireg < 0) {
321  *newmed = med;
322  }
323  if (normal) {
324  EGS_Float lam = aa+b*d;
325  EGS_Vector aux(xp+u*d-a*lam);
326  aux.normalize();
327  *normal = ireg < 0 ? aux : aux*(-1);
328  }
329 
330  return (!ireg ? -1 : 0);
331  }
332 
333  // handle odd case where position numerically coincides with apex
334  if (!xp.length2()) {
335 
336  int inew;
337 
338  // u is aiming inside the cone angle
339  if (b >= g12i) { // g12i = cos(t_cone); b = cos(t)
340  inew = 0;
341  if (ireg < 0) { // we are coming from outside
342  t = 0;
343  if (newmed) {
344  *newmed = med;
345  }
346  if (normal) {
347  *normal = a*(-1);
348  }
349  }
350  else if (!open && b>0) { // check closing plane
351  EGS_Float tt = -d1/b; // tt = -(aa + d1)/b but aa=0 if here
352  if (tt <= t) {
353  t = tt;
354  inew = -1;
355  if (normal) {
356  *normal = a*(-1);
357  }
358  }
359  }
360  }
361  // u is aiming outside the cone angle
362  else {
363  inew = -1;
364  if (ireg >= 0) { // we are coming from inside
365  t = 0;
366  if (normal) {
367  *normal = a;
368  }
369  }
370  }
371 
372  // return new region index
373  return inew;
374  }
375 
376  // handle closing plane
377  if (!open) {
378 
379  // from outside cone
380  if (ireg < 0) {
381  if (aa+d1 > 0 && b < 0) { // outside closing plane, moving towards it
382  EGS_Float tt = -(aa+d1)/b; // distance to closing plane
383  if (tt <= t) {
384  EGS_Float r2p = r2 + tt*(tt + 2*c);
385  if (r2p <= d1*d1*g12) { // enter via the closing plane
386  if (newmed) {
387  *newmed = med;
388  }
389  if (normal) {
390  *normal = a;
391  }
392  t = tt;
393  return 0;
394  }
395  }
396 
397  // if we are inside the conical surface, the only way to enter is via the
398  // closing plane. but if (r2 <= aa*aa*g12), we didn't enter: simply return
399  if (r2 <= aa*aa*g12) {
400  return ireg;
401  }
402  }
403  }
404  // from inside cone
405  else {
406  if (b > 0) { // moving towards the closing plane.
407  EGS_Float tt = -(aa+d1)/b; // distance to closing plane
408  if (tt <= t) {
409  EGS_Float r2p = r2 + tt*(tt + 2*c);
410  if (r2p <= d1*d1*g12) { // exit via the closing plane
411  if (newmed) {
412  *newmed = -1;
413  }
414  if (normal) {
415  *normal = a*(-1);
416  }
417  // avoid negative distances to the exit plane,
418  // which may lead to endless loops in CD geometries
419  t = tt < 0 ? 0 : tt;
420  return -1;
421  }
422  }
423  }
424  }
425  }
426 
427  // At this point, we cannot enter or exit via the closing plane; all we need to check
428  // is the conical surface. solve for t: (x+t*u)^2 = g12*[(x+t*u)*a]^2 (i.e., point
429  // x+t*u is on cone surface).
430  // solution: t = (-B +- sqrt(B^2-A*C))/A
431 
432  EGS_Float A = 1 - b*b*g12; // 1 - cos^2(t)/cos^2(t_cone)
433  EGS_Float B = c - aa*b*g12;
434  EGS_Float C = r2 - aa*aa*g12;
435  EGS_Float tt = veryFar;
436  EGS_Float lam = -1;
437 
438  if (fabs(A) < boundaryTolerance) {
439  // moving parallel to the cone surface (A=0, within hard-coded tolerance):
440  // solution: t = -C/(2*B), if t>0
441 
442  if ((!ireg && B>0) || (ireg && B<0)) {
443  EGS_Float ttt = -C/(2*B); // solution
444  lam = aa+b*ttt; // (x+t*u)*a >= 0: on "positive" cone
445  if (ttt >= 0 && lam >= 0) {
446  tt = ttt; // distance to cone surface
447  }
448  }
449  }
450  else {
451  // general solution, guarding against numerical instability
452  // solution: t = (-B +- sqrt(B^2-A*C))/A
453  //
454  // pick the smallest positive root
455  // A*C > 0 => roots have same sign
456  // A*C < 0 => roots have opposite sign
457  //
458  // if C<0, then if A>0 pick '+ root',
459  // else if A<0 we must have B>0 for positive roots: pick '+ root'
460  // (guard against cancellation if B>0)
461  //
462  // if C>0, then if A<0 roots have opposite signs: pick '- root' (-|A| flips sign)
463  // else if A>0 we must have B<0 for positive roots: pick '- root'
464  // (guard against cancellation if B<0)
465  //
466  // by testing for ireg instead of the sign of C, we:
467  // 1) avoid numerical instability at C=0
468  // 2) reverse the choice for the upper cone: largest positive root or negative one.
469 
470  EGS_Float ttt = -1;
471  EGS_Float D = B*B-A*C; // determinant
472  if (D<0) {
473  return ireg; // no real solution: no intersection
474  }
475 
476  if (!ireg && !(A<0 && B<0)) { // inside cone
477  if (B>0) {
478  ttt = -C/(B+sqrt(D));
479  }
480  else {
481  ttt = (sqrt(D)-B)/A;
482  }
483  }
484  else if (ireg && !(A>0 && B>0)) { // outside cone
485  if (B<0) {
486  ttt = C/(sqrt(D)-B);
487  }
488  else {
489  ttt = -(sqrt(D)+B)/A;
490  }
491  }
492  else {
493  return ireg; // no intersection
494  }
495 
496  lam = aa+b*ttt; // (x+t*u)*a >= 0: on "positive" cone
497  if (ttt >= -boundaryTolerance && lam >= 0) {
498  tt = ttt;
499  }
500  }
501 
502  // distance too far, or intersection beyond bounding plane
503  if (tt > t || (!open && ireg && d1 + aa + tt*b > 0)) {
504  return ireg;
505  }
506 
507 
508  // set distance and new region
509  t = tt;
510  int inew = ireg ? 0 : -1;
511 
512  // set medium and surface normal
513  if (newmed) {
514  *newmed = inew ? -1 : med;
515  }
516  if (normal) {
517  *normal = xp + u*t - a*(lam*g12);
518  normal->normalize();
519  if (!ireg) {
520  *normal *= (-1);
521  }
522  }
523 
524  // return new region
525  return inew;
526  }
527 
528 
529  // hownear
530  EGS_Float hownear(int ireg, const EGS_Vector &x) {
531 
532  EGS_Vector xp(x-xo); // position with respect to cone origin
533  EGS_Float aa = xp*a; // position along cone axis
534  EGS_Float ag = aa*gamma; // cone radius at axis position
535  EGS_Float r2 = xp.length2(); // distance squared to cone origin
536 
537  // cylindrical case
538  if (is_cyl) {
539 
540  // open, or within bounding planes
541  if (open || (aa > 0 && aa+d1 < 0)) {
542  r2 = sqrt(r2 - aa*aa); // radial distance from axis
543  if (ireg) {
544  return r2-Ro; // from outside
545  }
546  else {
547  return Ro-r2; // from inside
548  }
549  }
550 
551  r2 = sqrt(r2 - aa*aa); // radial distance from axis
552 
553  // inside the radius of cylinder: shortest distance is to closing plane
554  if (r2 < Ro) {
555  if (aa<0) {
556  return -aa; // distance to "0" closing plane
557  }
558  else {
559  return aa+d1; // distance to "d1" closing plane
560  }
561  }
562 
563  // outside the radius of cylinder: shortest distance is to cylinder cap "corner"
564  EGS_Float aux = r2-Ro;
565  if (aa<0) {
566  return sqrt(aa*aa + aux*aux); // distance to "0" cap
567  }
568  EGS_Float tp = aa+d1;
569  return sqrt(tp*tp + aux*aux); // distance to "d1" cap
570 
571  }
572 
573  // inside "negative" cone lobe
574  if (aa < 0 && ag*ag > r2*g12) { // cone tip is the nearest point
575  return sqrt(r2);
576  }
577 
578  // compute useful distances
579  EGS_Float r2a2 = r2-aa*aa; // distance^2 to cone axis
580  EGS_Float r2a = sqrt(r2a2); // distance to cone axis
581  EGS_Float tc = fabs((ag-r2a)*g12i); // distance to cone (perp. to cone surface)
582 
583  // open cone: tc is the shortest distance
584  if (open) {
585  return tc;
586  }
587 
588  // closed cone: check closing plane
589  EGS_Float tp = d1+aa; // signed axial distance to closing plane
590 
591  // inside cone
592  if (!ireg) {
593  tp = -tp; // adjust sign of distance
594  if (tp < tc) {
595  tc = tp; // pick shortest distance
596  }
597  }
598  // outside, but inside conical surface
599  else if (tp > 0 && r2 < aa*aa*g12) {
600  if (r2a2 > Ro2) { // outside the cone base cylinder
601  EGS_Float aux = r2a - Ro;
602  tc = sqrt(tp*tp + aux*aux); // nearest point is cone base "corner"
603  }
604  else {
605  tc = tp; // inside the cone base cylinder
606  }
607  }
608  // outside, and outside conical surface
609  else if (tp + tc*gamma*g12i > 0) { // nearest point is cone base "corner"
610  EGS_Float aux = r2a + d1*gamma; // distance to cone base cylinder
611  tc = sqrt(aux*aux + tp*tp); // distance to cone base "corner"
612  }
613 
614  // return shortest distance to cone
615  return tc;
616  }
617 
618  // getType accessor
619  const string &getType() const {
620  return type;
621  }
622 
623  // printInfo accessor
624  void printInfo() const;
625 
626  // getApex accessor
627  EGS_Vector getApex() const {
628  return xo;
629  }
630 
631  // getAxis accessor
632  EGS_Vector getAxis() const {
633  return a;
634  }
635 
636  // getGamma accessor
637  EGS_Float getGamma() const {
638  return gamma;
639  }
640 
641  // getRadius accessor
642  EGS_Float getRadius(const EGS_Vector &x) const {
643  if (is_cyl) {
644  return Ro;
645  }
646  EGS_Float xp = (x-xo)*a;
647  if (xp <= 0) {
648  return 0;
649  }
650  return xp*gamma;
651  }
652 };
653 
654 
655 /*
656 // A set of cones having the same axis and opening angle and
657 // apexes along the cone axis:
658 //
659 // /\
660 // / \
661 // / \
662 // / /\ \
663 // / / \ \
664 // / / \ \
665 // / / /\ \ \
666 // / / / \ \ \
667 //
668 // That sort of geometry is useful for modelling e.g. the tip of
669 // many ionization chambers.
670 */
671 
742 
743  EGS_Vector *xo; // the cone apexes
744  EGS_Vector a; // the cone axis.
745  EGS_Float gamma, g12; // tangent of opening angle and 1 + gamma^2
746  EGS_Float g12i; // 1/sqrt(g12)
747  EGS_Float *d; // distances from the first apex.
748  int nc; // number of cones
749  static string type;
750 
751 public:
752 
753  // constructor
754  EGS_ParallelCones(int Nc, const EGS_Vector &Xo, const EGS_Vector &A, EGS_Float Gamma,
755  const EGS_Float *distances, const string &N="")
756 
757  : EGS_BaseGeometry(N), a(A), gamma(Gamma), g12(1+Gamma*Gamma), nc(Nc) {
758 
759  a.normalize();
760  g12i = 1/sqrt(g12);
761 
762  if (nc < 1) {
763  nc=1; // enforce nc >= 1
764  }
765  nreg = nc; // one region per cone
766  xo = new EGS_Vector[nc]; // vector of cone apex positions
767  xo[0] = Xo; // apex of first cone
768 
769  // more than one cone
770  if (nc > 1) {
771  EGS_Float
772  d_old = 0;
773  d = new EGS_Float [nc-1];
774  for (int j=1; j<nc; j++) {
775  d[j-1] = distances[j-1];
776  if (d[j-1] <= d_old) {
777  egsFatal("EGS_ParallelCones: " "distances must be in increasing order\n");
778  }
779  xo[j] = xo[0] + a*d[j-1];
780  d_old = d[j-1];
781  }
782  }
783  }
784 
785  // destructor
786  ~EGS_ParallelCones() {
787  delete [] xo;
788  if (nc > 1) {
789  delete [] d;
790  }
791  }
792 
793  // isInside
794  bool isInside(const EGS_Vector &x) {
795  EGS_Vector xp(x-xo[0]); // current position with respect to apex
796  EGS_Float aa = xp*a; // current axial position
797  if (aa < 0) {
798  return false; // on "negative" side of cones
799  }
800  EGS_Float r2 = xp.length2(); // distance^2 to apex
801  if (r2 <= aa*aa*g12) {
802  return true; // inside outer cone
803  }
804  return false;
805  };
806 
807  // isWhere
808  int isWhere(const EGS_Vector &x) {
809  EGS_Vector xp(x-xo[0]); // current position with respect to apex
810  EGS_Float aa = xp*a; // current axial position
811  if (aa < 0) {
812  return -1; // on "negative" side of apex
813  }
814  EGS_Float r2 = xp.length2(); // distance^2 to apex
815  if (r2 > aa*aa*g12) {
816  return -1; // outside outer cone
817  }
818  EGS_Float r2a2 = r2 - aa*aa; // distance to cone set axis
819  for (int j=0; j<nc-1; j++) { // loop over all inner cones
820  EGS_Float aj = aa - d[j]; // distance to apex of cone j
821  EGS_Float ajg = aj*gamma; // length of cone j'th side up to aj
822  if (aj < 0 || r2a2 > ajg*ajg) {
823  return j; // on "negative" side or outside of cone j
824  }
825  }
826  return nc-1; // then it must be in last cone
827  }
828 
829  // inside
830  int inside(const EGS_Vector &x) {
831  return isWhere(x);
832  }
833 
834  // howfar
835  int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u, EGS_Float &t,
836  int *newmed=0, EGS_Vector *normal=0) {
837 
838  // not in innermost cone
839  if (ireg < nc-1) {
840  EGS_Vector xp; // a position
841  bool hit = false; // howfar hit flag
842 
843  // apex position of "next" inner cone
844  if (ireg < 0) {
845  xp = x-xo[0]; // apex of outer cone
846  }
847  else {
848  xp = x-xo[ireg+1]; // apex of next inner cone
849  }
850 
851  // general solution
852  EGS_Float aa = xp*a; // axial position in current cone
853  EGS_Float b = u*a; // cos(t), t = angle between a and u
854  EGS_Float r2 = xp.length2(); // distance^2 to current cone apex
855  EGS_Float c = u*xp; // projection of xp on u
856  EGS_Float A = 1 - b*b*g12;
857  EGS_Float B = c - aa*b*g12;
858  EGS_Float C = r2 - aa*aa*g12;
859  EGS_Float tt = veryFar;
860  EGS_Float lam = -1;
861 
862  // moving parallel to cone surface
863  if (fabs(A) < boundaryTolerance) { // guarding against /0 in general solution
864  EGS_Float ttt = -C/(2*B); // distance to hit
865  lam = aa+b*ttt; // axial position of hit
866  if (ttt >= 0 && lam >= 0) {
867  tt = ttt;
868  hit = true;
869  }
870  }
871  // general solution
872  else {
873  EGS_Float D = B*B-A*C;
874  if (D >= 0 && !(A > 0 && B > 0)) {
875  EGS_Float ttt = B < 0 ? C/(sqrt(D)-B) : -(sqrt(D)+B)/A;
876  lam = aa+b*ttt;
877  if (ttt >= 0 && lam >= 0) {
878  tt = ttt;
879  hit = true;
880  }
881  }
882  }
883  if (tt <= t) {
884  int inew = ireg < 0 ? 0 : ireg+1;
885  t = tt;
886  if (newmed) {
887  *newmed = medium(inew);
888  }
889  if (normal) {
890  *normal = xp + u*t - a*(lam*g12);
891  normal->normalize();
892  }
893  return inew;
894  }
895  if (ireg < 0 || hit) {
896  return ireg;
897  }
898  }
899 
900  // inside and not hitting the next inner cone.
901  // check the previous outer cone
902  EGS_Vector xp(x-xo[ireg]);
903  EGS_Float aa = xp*a, b = u*a, r2 = xp.length2(), c = u*xp;
904  EGS_Float A = 1 - b*b*g12, B = c - aa*b*g12, C = r2 - aa*aa*g12;
905  EGS_Float to = veryFar, lamo = -1;
906  if (fabs(A) < boundaryTolerance) { // moving parallel to the cone surface.
907  // for the outer cone we only have a solution if a*u < 0.
908  // i.e. if we are moving towards the apex.
909  if (b < 0) {
910  EGS_Float ttt = -C/(2*B);
911  lamo = aa+b*ttt;
912  if (ttt >= 0 && lamo >= 0) {
913  to = ttt;
914  }
915  }
916  }
917  else {
918  EGS_Float D = B*B-A*C;
919  // avoid numerical problems when |A*C| << |B|
920  if (D >= 0 && !(A < 0 && B < 0)) {
921  EGS_Float ttt = B > 0 ? -C/(B+sqrt(D)) : (sqrt(D)-B)/A;
922  lamo = aa+b*ttt;
923  if (ttt >= 0 && lamo >= 0) {
924  to = ttt;
925  }
926  }
927  }
928  if (to <= t) {
929  t = to;
930  int inew = ireg-1;
931  if (newmed) {
932  *newmed = inew < 0 ? -1 : medium(inew);
933  }
934  if (normal) {
935  *normal = xp + u*t - a*(lamo*g12);
936  normal->normalize();
937  }
938  return inew;
939  }
940  return ireg;
941  }
942 
943  // hownear
944  EGS_Float hownear(int ireg, const EGS_Vector &x) {
945  EGS_Float tc = veryFar;
946  if (ireg < 0 || ireg < nc-1) {
947  EGS_Vector xp;
948  if (ireg < 0) {
949  xp = x - xo[0];
950  }
951  else {
952  xp = x - xo[ireg+1];
953  }
954  EGS_Float aa = xp*a;
955  EGS_Float ag = aa*gamma;
956  EGS_Float r2 = xp.length2();
957  if (aa < 0 && ag*ag > r2*g12) {
958  tc = sqrt(r2);
959  }
960  else {
961  tc = fabs((ag-sqrt(r2-aa*aa))*g12i);
962  }
963  if (ireg < 0) {
964  return tc;
965  }
966  }
967  EGS_Vector xp(x-xo[ireg]);
968  EGS_Float aa = xp*a;
969  EGS_Float ag = aa*gamma;
970  EGS_Float r2 = xp.length2();
971  EGS_Float tco = fabs((ag-sqrt(r2-aa*aa))*g12i);
972  return tco < tc ? tco : tc;
973  }
974 
975  // getMaxStep
976  int getMaxStep() const {
977  return 2*nreg + 2;
978  }
979 
980  // getType accessor
981  const string &getType() const {
982  return type;
983  }
984 
985  // printInfo
986  void printInfo() const;
987 };
988 
989 
990 // A set of cones having the same apex and axis but different opening angles.
991 // If flag = 0, only cones below the apex.
992 // = 1, cones below and above the apex but excluding the space
993 // outside of the last cone
994 // = 2, all space
995 
1062 
1063  EGS_Vector xo; // the common apex.
1064  EGS_Vector a; // the axis.
1065  EGS_Float *gamma, *g12, *g12i;
1066  int nc;
1067  int flag;
1068 
1069  static string type;
1070 
1071 public:
1072 
1073  EGS_ConeSet(const EGS_Vector &Xo, const EGS_Vector &A, int Nc,
1074  EGS_Float *Gamma, int Flag = 0, const string &Name = "") :
1075  EGS_BaseGeometry(Name), xo(Xo), a(A), flag(Flag) {
1076  a.normalize();
1077  if (Nc < 1) {
1078  egsFatal("EGS_ConeSet: number of cones must be positive\n");
1079  }
1080  nc = Nc;
1081  gamma = new EGS_Float [nc];
1082  g12 = new EGS_Float [nc];
1083  g12i = new EGS_Float [nc];
1084  if (flag < 0 || flag > 2) {
1085  egsWarning("EGS_ConeSet: flag must be 0,1 or 2, reseting to 0\n");
1086  flag = 0;
1087  }
1088  for (int j=0; j<nc; j++) {
1089  if (Gamma[j] <= 0) {
1090  egsFatal("EGS_ConeSet: gamma's must be positive\n");
1091  }
1092  if (j > 0) {
1093  if (Gamma[j] <= gamma[j-1]) egsFatal("EGS_ConeSet: "
1094  "gamma's must be in increasing order\n");
1095  }
1096  gamma[j] = Gamma[j];
1097  g12[j] = 1 + gamma[j]*gamma[j];
1098  g12i[j] = 1/sqrt(g12[j]);
1099  }
1100  if (flag == 0) {
1101  nreg = nc;
1102  }
1103  else {
1104  nreg = 2*nc + 1;
1105  }
1106  if (flag == 1) {
1107  is_convex = false;
1108  }
1109  }
1110 
1111  ~EGS_ConeSet() {
1112  delete [] gamma;
1113  delete [] g12;
1114  delete [] g12i;
1115  }
1116 
1117  bool isInside(const EGS_Vector &x) {
1118  if (flag == 2) {
1119  return true;
1120  }
1121  EGS_Vector xp(x-xo);
1122  EGS_Float aa = xp*a;
1123  if (flag == 0 && aa < 0) {
1124  return false;
1125  }
1126  EGS_Float r2 = xp.length2();
1127  if (r2 <= aa*aa*g12[nc-1]) {
1128  return true;
1129  }
1130  return false;
1131  }
1132 
1133  int isWhere(const EGS_Vector &x) {
1134  EGS_Vector xp(x-xo);
1135  EGS_Float aa = xp*a;
1136  if (flag == 0 && aa < 0) {
1137  return -1;
1138  }
1139  EGS_Float r2 = xp.length2();
1140  int j;
1141  for (j=0; j<nc; j++) if (r2 <= aa*aa*g12[j]) {
1142  break;
1143  }
1144  if (j < nc) {
1145  if (aa >= 0) {
1146  return j;
1147  }
1148  return 2*nc-j;
1149  }
1150  if (flag != 2) {
1151  return -1;
1152  }
1153  return nc;
1154  }
1155 
1156  bool isRealRegion(int ireg) const {
1157  if (ireg < 0 || ireg >= nreg) {
1158  return false;
1159  }
1160  return flag != 1 ? true : (ireg != nc);
1161  }
1162 
1163  int inside(const EGS_Vector &x) {
1164  return isWhere(x);
1165  }
1166 
1167  int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u,
1168  EGS_Float &t, int *newmed = 0, EGS_Vector *normal = 0) {
1169  EGS_Vector xp(x-xo);
1170  EGS_Float aa = xp*a, b = u*a, r2 = xp.length2(), c = u*xp;
1171  //if( debug ) egsWarning(" ireg = %d x = (%g,%g,%g) a = %g"
1172  // " b = %g r2 = %g c = %g\n",ireg,x.x,x.y,x.z,aa,b,r2,c);
1173  if (!xp.length2()) {
1174  // handle odd case where position coincides with apex
1175  int inew = isWhere(xp + u);
1176  if (inew != ireg) {
1177  t = 0;
1178  if (normal) {
1179  *normal = a;
1180  }
1181  if (newmed) {
1182  *newmed = inew >= 0 ? medium(inew) : -1;
1183  }
1184  }
1185  return inew;
1186  }
1187  if (ireg != 0 && ireg != 2*nc) {
1188  // Not in the inner-most cone.
1189  // If outside (ireg < 0 || flag=2 and ireg=nc), check for
1190  // intersection with the outer-most cone
1191  // If inside, check for intersection with the inner cone.
1192  EGS_Float gam12;
1193  if (ireg < 0 || ireg == nc) {
1194  gam12 = g12[nc-1];
1195  }
1196  else {
1197  gam12 = ireg < nc ? g12[ireg-1] : g12[2*nc-ireg-1];
1198  }
1199  EGS_Float A=1-b*b*gam12, B=c-aa*b*gam12, C=r2-aa*aa*gam12;
1200  //if( debug ) egsWarning(" gam12 = %g A = %g B = %g "
1201  // "C = %g\n",gam12,A,B,C);
1202  EGS_Float tt = -1, lam;
1203  bool hit = false;
1204  if (fabs(A) < boundaryTolerance) {
1205  if ((ireg < nc && b > 0) || (ireg >= nc && b < 0)) {
1206  tt = -C/(2*B);
1207  }
1208  }
1209  else {
1210  EGS_Float D = B*B-A*C;
1211  if (D >= 0 && !(A > 0 && B > 0)) {
1212  tt = B < 0 ? C/(sqrt(D)-B) : -(sqrt(D)+B)/A;
1213  }
1214  }
1215  if (tt >= 0) { // possible intersection
1216  lam = aa+b*tt;
1217  int inew;
1218  if (ireg == nc || ireg == -1) {
1219  // outside the outer-most cone.
1220  // if flag=1 or flag=2, we are allowed to hit either
1221  // of the two cones
1222  if (flag) {
1223  hit = true;
1224  inew = lam >= 0 ? nc-1 : nc+1;
1225  }
1226  // if flag=0, we are allowed to hit only the lower
1227  // cone (lam >= 0)
1228  else if (!flag && lam>=0) {
1229  hit = true;
1230  inew = nc-1;
1231  }
1232  }
1233  else {
1234  // inside. In this case we are only allowed to hit
1235  // the cone on the same side of the apex.
1236  if (lam*aa >= 0) {
1237  hit = true;
1238  inew = lam >= 0 ? ireg-1 : ireg+1;
1239  }
1240  }
1241  if (hit && tt <= t) {
1242  t = tt;
1243  if (newmed) {
1244  *newmed = medium(inew);
1245  }
1246  if (normal) {
1247  *normal = xp + u*t - a*(lam*gam12);
1248  normal->normalize();
1249  }
1250  return inew;
1251  }
1252  }
1253  if (ireg < 0 || ireg == nc || hit) {
1254  return ireg;
1255  }
1256  }
1257  //EGS_Float gam12 = ireg < nc-1 ? g12[ireg] : g12[2*nc-ireg];
1258  EGS_Float gam12 = ireg < nc ? g12[ireg] : g12[2*nc-ireg];
1259  EGS_Float A=1-b*b*gam12, B=c-aa*b*gam12, C=r2-aa*aa*gam12;
1260  EGS_Float tt=-1;
1261  if (fabs(A) < boundaryTolerance) {
1262  if ((ireg < nc && b < 0) || (ireg > nc && b > 0)) {
1263  tt = -C/(2*B);
1264  }
1265  }
1266  else {
1267  EGS_Float D = B*B-A*C;
1268  // avoid numerical problems when |A*C| << |B|
1269  if (D >= 0 && !(A < 0 && B < 0)) {
1270  tt = B > 0 ? -C/(B+sqrt(D)) : (sqrt(D)-B)/A;
1271  }
1272  }
1273  if (tt < 0) {
1274  return ireg;
1275  }
1276  EGS_Float lam = aa + b*tt;
1277  if ((ireg < nc && lam < 0) || (ireg > nc && lam > 0)) {
1278  return ireg;
1279  }
1280  if (tt <= t) {
1281  t = tt;
1282  int inew;
1283  if (ireg < nc) {
1284  inew = ireg+1;
1285  if (inew == nc && flag < 2) {
1286  inew = -1;
1287  }
1288  }
1289  else {
1290  inew = ireg-1;
1291  if (inew == nc && flag < 2) {
1292  inew = -1;
1293  }
1294  }
1295  if (newmed) {
1296  *newmed = inew < 0 ? -1 : medium(inew);
1297  }
1298  if (normal) {
1299  *normal = xp + u*t - a*(lam*gam12);
1300  normal->normalize();
1301  }
1302  return inew;
1303  }
1304  return ireg;
1305  }
1306 
1307  EGS_Float hownear(int ireg, const EGS_Vector &x) {
1308  EGS_Float tc = veryFar;
1309  EGS_Vector xp(x-xo);
1310  EGS_Float aa = xp*a, r2 = xp.length2(), ag;
1311  //egsWarning("hownear: ireg = %d x = (%g,%g,%g) aa = %g r2 = %g\n",
1312  // ireg,x.x,x.y,x.z,aa,r2);
1313  if (ireg != 0 && ireg != 2*nc) {
1314  int i;
1315  if (ireg < 0 || ireg == nc) {
1316  i = nc-1;
1317  }
1318  else {
1319  i = ireg < nc ? ireg-1 : 2*nc-ireg-1;
1320  }
1321  ag = aa*gamma[i];
1322  if (flag == 0 && aa < 0 && ag*ag > r2*g12[i]) {
1323  tc = sqrt(r2);
1324  }
1325  else {
1326  tc = fabs((fabs(ag)-sqrt(r2-aa*aa))*g12i[i]);
1327  }
1328  //egsWarning(" i = %d ag = %g g12i = %g tc = %g\n",i,ag,g12i[i],tc);
1329  if (ireg < 0 || ireg == nc) {
1330  return tc;
1331  }
1332  }
1333  EGS_Float gam12i;
1334  if (ireg < nc) {
1335  ag = aa*gamma[ireg];
1336  gam12i = g12i[ireg];
1337  }
1338  else {
1339  ag = -aa*gamma[2*nc-ireg];
1340  gam12i = g12i[2*nc-ireg];
1341  }
1342  EGS_Float tco = fabs((ag-sqrt(r2-aa*aa))*gam12i);
1343  //egsWarning(" ag = %g tco = %g\n",ag,tco);
1344  return tco < tc ? tco : tc;
1345  }
1346 
1347  int getMaxStep() const {
1348  return 4*nreg + 2;
1349  }
1350 
1351  const string &getType() const {
1352  return type;
1353  }
1354 
1355  void printInfo() const;
1356 };
1357 
1358 //
1359 // A cone stack is a collection of cones with possible
1360 // different opening angles, apexes, etc., stacked together
1361 // This is very similar to the cone stack component module in BEAM
1362 //
1363 //
1364 #include <vector>
1365 using std::vector;
1366 
1464 
1465 public:
1466 
1467  // constructor (empty cone stack)
1468  EGS_ConeStack(const EGS_Vector &Xo, const EGS_Vector &A, const string &Name)
1469  : EGS_BaseGeometry(Name), xo(Xo), a(A), nl(0), nltot(0), nmax(0), same_Rout(true), Rout(0),
1470  Rout2(0) {
1471  a.normalize();
1472  }
1473 
1474  // destructor
1475  ~EGS_ConeStack() {
1476  clear(true);
1477  }
1478 
1479  // add a layer
1480  void addLayer(EGS_Float thick, const vector<EGS_Float> &rtop,
1481  const vector<EGS_Float> &rbottom,
1482  const vector<string> &med_names);
1483 
1484  // get medium
1485  int medium(int ireg) const {
1486  int il = ireg/nmax;
1487  int ir = ireg - il*nmax;
1488  return cones[il][ir]->medium(0);
1489  }
1490 
1491  // isInside
1492  bool isInside(const EGS_Vector &x) {
1493  EGS_Float p = x*a;
1494  if (p < pos[0] || p > pos[nl]) {
1495  return false;
1496  }
1497  int il = findRegion(p,nl+1,pos);
1498  return cones[il][nr[il]-1]->isInside(x);
1499  }
1500 
1501  // isWhere
1502  int isWhere(const EGS_Vector &x) {
1503  EGS_Float p = x*a;
1504  if (p < pos[0] || p > pos[nl]) {
1505  return -1;
1506  }
1507  int il = findRegion(p,nl+1,pos);
1508  int ir = isWhere(il,x);
1509  return ir < 0 ? -1 : il*nmax+ir;
1510  }
1511 
1512  // inside
1513  int inside(const EGS_Vector &x) {
1514  return isWhere(x);
1515  }
1516 
1517  // isRealRegion
1518  bool isRealRegion(int ireg) const {
1519  if (ireg < 0 || ireg >= nreg) {
1520  return false;
1521  }
1522  int il = ireg/nmax;
1523  int ir = ireg - il*nmax;
1524  return (ir < nr[il]);
1525  }
1526 
1527  // howfar
1528  int howfar(int ireg, const EGS_Vector &x, const EGS_Vector &u,
1529  EGS_Float &t, int *newmed=0, EGS_Vector *normal=0) {
1530 
1531  EGS_Float xp = x*a;
1532  EGS_Float up = u*a;
1533  int dir;
1534 
1535  // inside conestack
1536  if (ireg >= 0) {
1537  EGS_Float tp;
1538  int il = ireg/nmax; // layer index
1539 
1540  // calculate distance to plane
1541  if (up > 0) { // u points along conestack axis
1542  dir = 1; // positive direction
1543  tp = (pos[il+1]-xp)/up; // distance to 'next' plane
1544  }
1545  else if (up < 0) { // u points against conestack axis
1546  dir = -1; // negative direction
1547  tp = (pos[il]-xp)/up; // distance to 'previous' plane
1548  }
1549  else { // u is perpendicular to a (u*a = 0)
1550  dir = 0; // null direction
1551  tp = veryFar; // init to large distance
1552  }
1553  bool hitp = false; // assume we don't hit the plane
1554  if (tp <= t) { // check against maximum distance t
1555  hitp = true; // we hit the plane
1556  t = tp; // set t = distance to plane
1557  }
1558 
1559  // distance to outer cone
1560  int ir = ireg - il*nmax; // cone index in current layer
1561  bool hitc = false; // assume we don't hit the cone
1562  int irnew = cones[il][ir]->howfar(0,x,u,t,newmed,normal);
1563  if (irnew < 0) {
1564  hitc = true; // hit the next cone
1565  irnew = ir < nr[il]-1 ? ir+1 : -1; // next cone region, or -1 if beyond last cone
1566  }
1567 
1568  // distance to inner cone (for all but innermost cone)
1569  if (ir > 0) {
1570  int irnew1 = cones[il][ir-1]->howfar(-1,x,u,t,newmed,normal);
1571  if (!irnew1) {
1572  hitc = true; // hit the previous cone
1573  irnew = ir-1; // previous cone region
1574  }
1575  }
1576 
1577  // hit a cone boundary
1578  if (hitc) {
1579  if (irnew < 0) {
1580  return irnew; // we are moving out of conestack, return -1
1581  }
1582  if (newmed) {
1583  *newmed = cones[il][irnew]->medium(0);
1584  }
1585  return il*nmax + irnew;
1586  }
1587 
1588  // did not hit a plane (nor a cone: should not happen!)
1589  if (!hitp) {
1590  return ireg;
1591  }
1592 
1593  // change layer
1594  int ilnew = il + dir; // new layer index
1595  if (normal) {
1596  *normal = dir > 0 ? a*(-1) : a;
1597  }
1598  if (ilnew < 0 || ilnew >= nl) {
1599  return -1; // beyond conestack bounding planes => outside
1600  }
1601 
1602  // next layer radii are congruent (add nmax to ireg)
1603  if (dir > 0 && flag[il] >= 2) {
1604  if (newmed) {
1605  *newmed = cones[il+1][ir]->medium(0);
1606  }
1607  return ireg+nmax;
1608  }
1609 
1610  // previous layer radii are congruent (subtract nmax from ireg)
1611  if (dir < 0 && (flag[il] == 1 || flag[il] == 3)) {
1612  if (newmed) {
1613  *newmed = cones[il-1][ir]->medium(0);
1614  }
1615  return ireg-nmax;
1616  }
1617 
1618  // figure out region index in new layer
1619  il += dir; // new layer index
1620  EGS_Vector tmp(x+u*t); // position of the hit on new layer
1621  ir = isWhere(il,tmp); // get region index in that layer
1622  if (ir<0) {
1623  return ir; // moved out of constack
1624  }
1625  if (newmed) {
1626  *newmed = cones[il][ir]->medium(0);
1627  }
1628  return il*nmax+ir; // return overall new region index
1629  }
1630 
1631  // outside of conestack
1632 
1633  EGS_Float ttot=0; // cumulative travel distance
1634  EGS_Float tp; // distance to next plane
1635  EGS_Vector tmp(x); // running position
1636  int il; // current layer index
1637 
1638  // outside the first boundary plane
1639  if (xp <= pos[0]) {
1640  if (up <= 0) {
1641  return ireg; // moving away from conestack: no hit
1642  }
1643  tp = (pos[0]-xp)/up; // distance to first boundary plane
1644  if (tp > t) {
1645  return ireg; // distance greater than maximum t: no hit
1646  }
1647  tmp += u*tp; // move to first plane
1648  ttot = tp; // increase cumulative travel distance
1649  il = 0; // index of current layer
1650  int ir = isWhere(0, tmp); // region index in first layer
1651  if (ir >= 0) { // hit inside a cone in first layer: we're done
1652  //***************************************************************
1653  // EMH April 12 2013: Called from outside, but isWhere reports it is inside.
1654  // This is due to either the particle at a boundary or to round-off errors near a boundary.
1655  // To avoid getting into an infinite loop, check again if particle exits
1656  // the geometry from region ir.
1657  //
1658  // FT December 4 2013:
1659  // this isWhere call is using the tmp position and the LOCAL ir region number in
1660  // layer 0 of the ConeStack, so it is not inconsistent if x is outside and ir>=0.
1661  // BUT indeed we may be glancing on a "corner" of the ConeStack, so we should still
1662  // check if a subsequent call to howfar(ir,tmp,...) takes us outside within boundaryTolerance.
1663  // It that case we are not really entering the geometry.
1664  EGS_Float tb = veryFar;
1665  int inew_g = howfar(ir,tmp,u,tb,0,normal);
1666  if (inew_g < 0 && tb <= boundaryTolerance) {
1667  return ireg; // exits geometry
1668  }
1669  //***************************************************************
1670  t = tp; // distance to hit is distance to plane
1671  if (newmed) {
1672  *newmed = cones[0][ir]->medium(0);
1673  }
1674  if (normal) {
1675  *normal = a*(-1);
1676  }
1677  return ir; // return region number
1678  }
1679  }
1680 
1681  // outside the last boundary plane
1682  else if (xp >= pos[nl]) {
1683  if (up >= 0) {
1684  return ireg; // moving away from the conestack: no hit
1685  }
1686  tp = (pos[nl]-xp)/up; // distance to last boundary plane
1687  if (tp > t) {
1688  return ireg; // distance greater than maximum t: no hit
1689  }
1690  tmp += u*tp; // hit position on last plane
1691  ttot = tp; // increase cumulative travel distance
1692  il = nl-1; // index of last plane
1693  int ir = isWhere(il, tmp); // region index in last layer
1694  if (ir >= 0) { // hit inside a cone in first layer: we're done
1695  //***************************************************************
1696  // EMH April 12 2013: Called from outside, but isWhere reports it is inside.
1697  // This is due to either the particle at a boundary or to round-off errors near a boundary.
1698  // To avoid getting into an infinite loop, check again if particle exits
1699  // the geometry from region ir.
1700  //
1701  // FT December 4 2013:
1702  // this isWhere call is using the tmp position and the LOCAL ir region number in
1703  // layer il of the ConeStack, so it is not inconsistent if x is outside and ir>=0.
1704  // BUT indeed we may be glancing on a "corner" of the ConeStack, so we should still
1705  // check if a subsequent call to howfar(il*nmax+ir) takes us outside within boundaryTolerance.
1706  // It that case we are not really entering the geometry.
1707  EGS_Float tb = veryFar;
1708  int inew_g = howfar(il*nmax+ir,tmp,u,tb,0,normal);
1709  if (inew_g < 0 && tb <= boundaryTolerance) {
1710  return ireg; // exits geometry
1711  }
1712  //***************************************************************
1713  t = tp; // distance to hit is distance to plane
1714  if (newmed) {
1715  *newmed = cones[il][ir]->medium(0);
1716  }
1717  if (normal) {
1718  *normal = a; // set normal
1719  }
1720  return il*nmax+ir; // return region number
1721  }
1722  }
1723 
1724  // outside conestack, but within conestack boundary planes
1725  else {
1726 
1727  if (same_Rout) {
1728  // all outer "cones" are actually cylinders and all have the same radius. This
1729  // simplifies the logic a lot: just need to check against outer cylindrical cone of
1730  // the first layer: cones[0][nr[0]-1]. Don't forget to use temporary and normal
1731  // pointers here so that normal is only updated if the hit lies within the
1732  // conestack boundary planes!
1733 
1734  EGS_Float tt = t;
1735  EGS_Vector tmp_normal;
1736 
1737  //***************************************************************
1738  // EMH, April 12 2013: To prevent round-off error situations
1739  // where particles are stuck near boundaries or when a particle
1740  // seats at a boundary, check both, where it is, and whether it exits
1741  // the geometry. irnow SHOULD be -1, but if a particle is at a boundary or
1742  // very close to one, isWhere returns irnow >= 0 values.
1743  int irnow = isWhere(x);// irnow > 0 indicates round-off error near boundary
1744  // irnow = -1 indicates proper call from outside
1745  // howfar call using irnow instead of -1 in the hope particle gets out of boundary
1746  //
1747  // int irnew = cones[0][nr[0]-1]->howfar(irnow,x,u,tt,0,&tmp_normal);
1748  //
1749  // FT December 10 2013:
1750  // the howfar call above does not work because it uses irnow for the call to
1751  // SimpleCone->howfar: SimpleCone regions can only be 0 (inside) or -1 (outside).
1752  // If we find the inconsistent condition irnow >= 0 (inside, but call from outside),
1753  // then we are on a boundary. We see if howfar takes us out within boundaryTolerance.
1754  // If so, then we are not really entering the geometry.
1755 
1756  // fp inconsistency: irnow >= 0 (inside) but called with ireg = -1 (outside)
1757  if (irnow >= 0) {
1758  EGS_Float tb = veryFar;
1759  int inew_g = howfar(irnow,x,u,tb,0,&tmp_normal);
1760  if (inew_g < 0 && tb <= boundaryTolerance) {
1761  return ireg; // exits geometry
1762  }
1763  }
1764  //***************************************************************
1765 
1766  int irnew = cones[0][nr[0]-1]->howfar(ireg,x,u,tt,0,&tmp_normal);
1767 
1768  if (irnew < 0) {
1769  return -1; // no hit
1770  }
1771  EGS_Float aux = xp + up*tt; // axis position of hit point on outer cylinder
1772  if (aux < pos[0] || aux > pos[nl] ||// beyond conestack bounding planes
1773  (aux == pos[0] && up <= 0) ||// on first plane, going out
1774  (aux == pos[nl] && up >= 0)) { // on last plane, going out
1775  return -1; // => no hit: we're done
1776  }
1777  il = findRegion(aux,nl+1,pos); // layer index for axis position aux
1778  if (newmed) {
1779  *newmed = cones[il][nr[il]-1]->medium(0);
1780  }
1781  if (normal) {
1782  *normal = tmp_normal; // set normal to normal from howfar
1783  }
1784  t = tt; // set distance
1785  return il*nmax + nr[il]-1; // return region index
1786  }
1787 
1788  // get layer index for current position, projected on the conestack axis
1789  il = findRegion(xp,nl+1,pos);
1790 
1791  // guard agains round-off errors by checking if the position
1792  // is inside the outer cone in this layer (it shouldn't be, as the
1793  // particle is outside) and returning if it is.
1794  bool isc = cones[il][nr[il]-1]->isInside(x);
1795  if (isc) {
1796  // IK, March 7 2008: same problem as in CD geometry.
1797  // we think we are outside but we just found we are inside.
1798  // Hopefully a roundoff problem.
1799  EGS_Float tp = veryFar;
1800 
1801  if (up > 0) {
1802  dir = 1;
1803  tp = (pos[il+1] - xp)/up;
1804  }
1805  else if (up < 0) {
1806  dir = -1;
1807  tp = (pos[il] - xp)/up;
1808  }
1809  else {
1810  // prevent compiler from complaining about use of
1811  // uninitialized value of dir (even though tp will
1812  // always be greater than epsilon in this case).
1813  dir = 0;
1814  }
1815  if (tp < boundaryTolerance) {
1816  il += dir;
1817  if (il < 0 || il >= nl) {
1818  return ireg;
1819  }
1820  isc = cones[il][nr[il]-1]->isInside(x);
1821  }
1822 
1823  if (isc) {
1824  EGS_Float tc = veryFar;
1825  int isc_new = cones[il][nr[il]-1]->howfar(0,x,u,tc);
1826  if (!(isc_new < 0 && tc < boundaryTolerance)) {
1827  egsWarning("EGS_ConeStack::howfar: called from the outside"
1828  " but I find x=(%g,%g,%g) to be inside\n", x.x,x.y,x.z);
1829  egsWarning("layer=%d distance to planes=%g\n",il,tp);
1830  egsWarning("distance to outer cone=%g\n",tc);
1831  error_flag = 1;
1832  return ireg;
1833  }
1834  }
1835  }
1836  }
1837 
1838  // traverse layers until we hit a cone, or else move beyond conestack boundary planes
1839  for (EGS_I64 loopCount=0; loopCount<=loopMax; ++loopCount) {
1840  if (loopCount == loopMax) {
1841  egsFatal("EGS_ConeStack::howfar: Too many iterations were required! Input may be invalid, or consider increasing loopMax.");
1842  return -1;
1843  }
1844 
1845  // calculate distance to next plane boundary
1846  if (up > 0) { // moving along conestack axis a
1847  dir = 1; // positive direction
1848  tp = (pos[il+1] - xp)/up; // total distance to next plane
1849  }
1850  else if (up < 0) { // moving against conestack axis a
1851  dir = -1; // negative direction
1852  tp = (pos[il] - xp)/up; // total distance to previous plane
1853  }
1854  else { // moving perpendicular to axis (u*a = 0)
1855  dir = 0; // null direction
1856  tp = veryFar; // init to large distance
1857  }
1858 
1859  // distance to outer cone in layer 'il'
1860  EGS_Float tt = t - ttot; // remaining maximum distance
1861  int tmp_med;
1862  EGS_Vector tmp_normal;
1863  int irnew = cones[il][nr[il]-1]->howfar(-1,tmp,u,tt,&tmp_med,&tmp_normal);
1864  if (!irnew) { // hit the outer cone
1865  if (tp > ttot + tt) { // plane is further than cone: we're done
1866  t = ttot+tt; // final distance to conestack
1867  if (newmed) {
1868  *newmed = tmp_med; // set media
1869  }
1870  if (normal) {
1871  *normal = tmp_normal; // set normal
1872  }
1873  return il*nmax+nr[il]-1; // return final region index
1874  }
1875  }
1876  if (tp > t || !dir) {
1877  break; // guard against glancing hits
1878  }
1879  il += dir; // move to previous or next layer
1880  if (il < 0 || il >= nl) {
1881  break; // enforce conestack bounding planes
1882  }
1883  ttot = tp; // increase cumulative travel distance
1884  tmp = x + u*tp; // move along to position hit on next plane
1885 
1886  int itest = isWhere(il,tmp); // check where we are in next layer
1887  if (itest >= 0) { // we are inside a cone in next layer
1888  t = ttot; // update distance
1889  if (newmed) {
1890  *newmed = cones[il][itest]->medium(0);
1891  }
1892  if (normal) {
1893  *normal = dir > 0 ? a*(-1) : a;
1894  }
1895  return il*nmax + itest; // return final region index
1896  }
1897  }
1898  return ireg;
1899  }
1900 
1901  // hownear
1902  EGS_Float hownear(int ireg, const EGS_Vector &x) {
1903 
1904  EGS_Float xp = x*a; // current position along the axis
1905  EGS_Float tp, tc; // distances
1906 
1907  // inside the conestack
1908  if (ireg >= 0) {
1909  int il = ireg/nmax; // current layer index
1910  int ir = ireg - il*nmax; // region index in current layer
1911  tp = min(xp-pos[il],pos[il+1]-xp); // min of distance to planes on either side
1912  tc = cones[il][ir]->hownear(0,x); // distance to outer conical boundary
1913  if (ir > 0) {
1914  tc = min(tc,cones[il][ir-1]->hownear(-1,x)); // to inner conical boundary
1915  }
1916  return min(tp,tc); // return minimum distance
1917  }
1918 
1919  // outside the conestack; just check distances to layer planes, which is overkill, but in
1920  // general it would be worse to check hownear on the outer cones in all layer! To mitigate
1921  // hownear calls to layer planes, one can inscribe the conestack in a fitting envelope.
1922 
1923  if (xp <= pos[0]) {
1924  return pos[0] - xp; // distance to first layer plane
1925  }
1926  if (xp >= pos[nl]) {
1927  return xp - pos[nl]; // distance to last layer plane
1928  }
1929  int il = findRegion(xp,nl+1,pos); // find current layer index
1930  tp = min(xp-pos[il],pos[il+1]-xp); // min of distance to planes on either side
1931  return min(tp,cones[il][nr[il]-1]->hownear(-1,x)); // min dist. to planes and outer cone
1932 
1933  }
1934 
1935  // getMaxStep
1936  int getMaxStep() const {
1937  int nstep = 0;
1938  for (int j=0; j<nl; ++j) {
1939  nstep += 2*nr[j] + 1;
1940  }
1941  return nstep + 1;
1942  }
1943 
1944  // getType
1945  const string &getType() const {
1946  return type;
1947  }
1948 
1949  // printInfo
1950  void printInfo() const;
1951 
1952  // nLayer
1953  int nLayer() const {
1954  return nl;
1955  }
1956 
1957  // shiftLabels
1958  void shiftLabelRegions(const int i, const int index) {
1959  for (size_t k=0; k<labels[i].regions.size(); k++) {
1960  labels[i].regions[k] += index*nmax;
1961  }
1962  }
1963 
1964 
1965 
1966 protected:
1967 
1968  EGS_Vector xo; // the position of the top layer
1969  EGS_Vector a; // the axis
1970  int nl; // number of layers
1971  int nltot; // size of the arrays
1972  int nmax; // max. number of radii in all layers
1973  bool same_Rout; // true, if all layers have same outer radius
1974  EGS_Float Rout, Rout2; // outer radius (if same_Rout is true)
1975  EGS_Float *pos; // the plane positions dividing the layers
1976  int *nr; // number of radii in each layer
1977  int *flag; // a flag for each layer:
1978  // = 0 -> top and bottom radii different from adjacent layers
1979  // = 1 -> top radii are the same as bottom of previous layer
1980  // = 2 -> bottom radii are the same as top of next layer
1981  // = 3 -> top and bottom radii same as adjacent layers
1982  EGS_SimpleCone ** *cones; // the cones for each layer.
1983  static string type;
1984 
1985  // resize
1986  void resize();
1987 
1988  // clear
1989  void clear(bool);
1990 
1991  // isWhere
1992  inline int isWhere(int il, const EGS_Vector &x) {
1993  if (!cones[il][nr[il]-1]->isInside(x)) {
1994  return -1;
1995  }
1996  for (int j=0; j<nr[il]-1; j++) {
1997  if (cones[il][j]->isInside(x)) {
1998  return j;
1999  }
2000  }
2001  return nr[il]-1;
2002  }
2003 };
2004 
2005 #endif
Base geometry class. Every geometry class must be derived from EGS_BaseGeometry.
A set of cones with different opening angles but the same axis and apexes.
Definition: egs_cones.h:1061
A cone stack.
Definition: egs_cones.h:1463
A set of "parallel cones" (i.e. cones with the same axis and opening angles but different apexes)
Definition: egs_cones.h:741
A single cone that may be open (i.e. extends to infinity or closed by a plane perpendicular to the co...
Definition: egs_cones.h:126
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
EGS_BaseGeometry class header file.
Global egspp functions header file.
Attempts to fix broken math header files.
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
const EGS_I64 loopMax
The maximum number of iterations for near-infinite loops.
Definition: egs_functions.h:96
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.
const EGS_Float veryFar
A very large float.