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