Geant4  10.03
G4Torus.cc
Go to the documentation of this file.
1 //
2 // ********************************************************************
3 // * License and Disclaimer *
4 // * *
5 // * The Geant4 software is copyright of the Copyright Holders of *
6 // * the Geant4 Collaboration. It is provided under the terms and *
7 // * conditions of the Geant4 Software License, included in the file *
8 // * LICENSE and available at http://cern.ch/geant4/license . These *
9 // * include a list of copyright holders. *
10 // * *
11 // * Neither the authors of this software system, nor their employing *
12 // * institutes,nor the agencies providing financial support for this *
13 // * work make any representation or warranty, express or implied, *
14 // * regarding this software system or assume any liability for its *
15 // * use. Please see the license in the file LICENSE and URL above *
16 // * for the full disclaimer and the limitation of liability. *
17 // * *
18 // * This code implementation is the result of the scientific and *
19 // * technical work of the GEANT4 collaboration. *
20 // * By using, copying, modifying or distributing the software (or *
21 // * any work based on the software) you agree to acknowledge its *
22 // * use in resulting scientific publications, and indicate your *
23 // * acceptance of all terms of the Geant4 Software license. *
24 // ********************************************************************
25 //
26 //
27 // $Id: G4Torus.cc 101121 2016-11-07 09:18:01Z gcosmo $
28 //
29 //
30 // class G4Torus
31 //
32 // Implementation
33 //
34 // 28.10.16 E.Tcherniaev: reimplemented CalculateExtent(),
35 // added Extent(), removed CreateRotatedVertices()
36 // 05.04.12 M.Kelsey: Use sqrt(r) in GetPointOnSurface() for uniform points
37 // 02.10.07 T.Nikitina: Bug fixed in SolveNumericJT(), b.969:segmentation fault.
38 // rootsrefined is used only if the number of refined roots
39 // is the same as for primary roots.
40 // 02.10.07 T.Nikitina: Bug fixed in CalculateExtent() for case of non-rotated
41 // full-phi torus:protect against negative value for sqrt,
42 // correct formula for delta.
43 // 20.11.05 V.Grichine: Bug fixed in Inside(p) for phi sections, b.810
44 // 25.08.05 O.Link: new methods for DistanceToIn/Out using JTPolynomialSolver
45 // 07.06.05 V.Grichine: SurfaceNormal(p) for rho=0, Constructor as G4Cons
46 // 03.05.05 V.Grichine: SurfaceNormal(p) according to J. Apostolakis proposal
47 // 18.03.04 V.Grichine: bug fixed in DistanceToIn(p)
48 // 11.01.01 E.Medernach: Use G4PolynomialSolver to find roots
49 // 03.10.00 E.Medernach: SafeNewton added
50 // 31.08.00 E.Medernach: numerical computation of roots wuth bounding
51 // volume technique
52 // 26.05.00 V.Grichine: new fuctions developed by O.Cremonesi were added
53 // 06.03.00 V.Grichine: modifications in Distance ToOut(p,v,...)
54 // 19.11.99 V.Grichine: side = kNull in Distance ToOut(p,v,...)
55 // 09.10.98 V.Grichine: modifications in Distance ToOut(p,v,...)
56 // 30.10.96 V.Grichine: first implementation with G4Tubs elements in Fs
57 //
58 
59 #include "G4Torus.hh"
60 
61 #if !(defined(G4GEOM_USE_UTORUS) && defined(G4GEOM_USE_SYS_USOLIDS))
62 
63 #include "G4GeomTools.hh"
64 #include "G4VoxelLimits.hh"
65 #include "G4AffineTransform.hh"
66 #include "G4BoundingEnvelope.hh"
67 #include "G4GeometryTolerance.hh"
68 #include "G4JTPolynomialSolver.hh"
69 
70 #include "G4VPVParameterisation.hh"
71 
72 #include "meshdefs.hh"
73 
74 #include "Randomize.hh"
75 
76 #include "G4VGraphicsScene.hh"
77 #include "G4Polyhedron.hh"
78 
79 using namespace CLHEP;
80 
82 //
83 // Constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
84 // - note if pdphi>2PI then reset to 2PI
85 
86 G4Torus::G4Torus( const G4String &pName,
87  G4double pRmin,
88  G4double pRmax,
89  G4double pRtor,
90  G4double pSPhi,
91  G4double pDPhi)
92  : G4CSGSolid(pName)
93 {
94  SetAllParameters(pRmin, pRmax, pRtor, pSPhi, pDPhi);
95 }
96 
98 //
99 //
100 
101 void
103  G4double pRmax,
104  G4double pRtor,
105  G4double pSPhi,
106  G4double pDPhi )
107 {
108  const G4double fEpsilon = 4.e-11; // relative tolerance of radii
109 
110  fCubicVolume = 0.;
111  fSurfaceArea = 0.;
112  fRebuildPolyhedron = true;
113 
116 
119 
120  if ( pRtor >= pRmax+1.e3*kCarTolerance ) // Check swept radius, as in G4Cons
121  {
122  fRtor = pRtor ;
123  }
124  else
125  {
126  std::ostringstream message;
127  message << "Invalid swept radius for Solid: " << GetName() << G4endl
128  << " pRtor = " << pRtor << ", pRmax = " << pRmax;
129  G4Exception("G4Torus::SetAllParameters()",
130  "GeomSolids0002", FatalException, message);
131  }
132 
133  // Check radii, as in G4Cons
134  //
135  if ( pRmin < pRmax - 1.e2*kCarTolerance && pRmin >= 0 )
136  {
137  if (pRmin >= 1.e2*kCarTolerance) { fRmin = pRmin ; }
138  else { fRmin = 0.0 ; }
139  fRmax = pRmax ;
140  }
141  else
142  {
143  std::ostringstream message;
144  message << "Invalid values of radii for Solid: " << GetName() << G4endl
145  << " pRmin = " << pRmin << ", pRmax = " << pRmax;
146  G4Exception("G4Torus::SetAllParameters()",
147  "GeomSolids0002", FatalException, message);
148  }
149 
150  // Relative tolerances
151  //
153  ? 0.5*std::max( kRadTolerance, fEpsilon*(fRtor-fRmin )) : 0;
154  fRmaxTolerance = 0.5*std::max( kRadTolerance, fEpsilon*(fRtor+fRmax) );
155 
156  // Check angles
157  //
158  if ( pDPhi >= twopi ) { fDPhi = twopi ; }
159  else
160  {
161  if (pDPhi > 0) { fDPhi = pDPhi ; }
162  else
163  {
164  std::ostringstream message;
165  message << "Invalid Z delta-Phi for Solid: " << GetName() << G4endl
166  << " pDPhi = " << pDPhi;
167  G4Exception("G4Torus::SetAllParameters()",
168  "GeomSolids0002", FatalException, message);
169  }
170  }
171 
172  // Ensure psphi in 0-2PI or -2PI-0 range if shape crosses 0
173  //
174  fSPhi = pSPhi;
175 
176  if (fSPhi < 0) { fSPhi = twopi-std::fmod(std::fabs(fSPhi),twopi) ; }
177  else { fSPhi = std::fmod(fSPhi,twopi) ; }
178 
179  if (fSPhi+fDPhi > twopi) { fSPhi-=twopi ; }
180 }
181 
183 //
184 // Fake default constructor - sets only member data and allocates memory
185 // for usage restricted to object persistency.
186 //
187 G4Torus::G4Torus( __void__& a )
188  : G4CSGSolid(a), fRmin(0.), fRmax(0.), fRtor(0.), fSPhi(0.),
189  fDPhi(0.), fRminTolerance(0.), fRmaxTolerance(0. ),
190  kRadTolerance(0.), kAngTolerance(0.),
191  halfCarTolerance(0.), halfAngTolerance(0.)
192 {
193 }
194 
196 //
197 // Destructor
198 
200 {}
201 
203 //
204 // Copy constructor
205 
207  : G4CSGSolid(rhs), fRmin(rhs.fRmin),fRmax(rhs.fRmax),
208  fRtor(rhs.fRtor),fSPhi(rhs.fSPhi),fDPhi(rhs.fDPhi),
209  fRminTolerance(rhs.fRminTolerance), fRmaxTolerance(rhs.fRmaxTolerance),
210  kRadTolerance(rhs.kRadTolerance), kAngTolerance(rhs.kAngTolerance),
211  halfCarTolerance(rhs.halfCarTolerance),
212  halfAngTolerance(rhs.halfAngTolerance)
213 {
214 }
215 
217 //
218 // Assignment operator
219 
221 {
222  // Check assignment to self
223  //
224  if (this == &rhs) { return *this; }
225 
226  // Copy base class data
227  //
229 
230  // Copy data
231  //
232  fRmin = rhs.fRmin; fRmax = rhs.fRmax;
233  fRtor = rhs.fRtor; fSPhi = rhs.fSPhi; fDPhi = rhs.fDPhi;
238 
239  return *this;
240 }
241 
243 //
244 // Dispatch to parameterisation for replication mechanism dimension
245 // computation & modification.
246 
248  const G4int n,
249  const G4VPhysicalVolume* pRep )
250 {
251  p->ComputeDimensions(*this,n,pRep);
252 }
253 
254 
255 
257 //
258 // Calculate the real roots to torus surface.
259 // Returns negative solutions as well.
260 
262  const G4ThreeVector& v,
263  G4double r,
264  std::vector<G4double>& roots ) const
265 {
266 
267  G4int i, num ;
268  G4double c[5], srd[4], si[4] ;
269 
270  G4double Rtor2 = fRtor*fRtor, r2 = r*r ;
271 
272  G4double pDotV = p.x()*v.x() + p.y()*v.y() + p.z()*v.z() ;
273  G4double pRad2 = p.x()*p.x() + p.y()*p.y() + p.z()*p.z() ;
274 
275  c[0] = 1.0 ;
276  c[1] = 4*pDotV ;
277  c[2] = 2*(pRad2 + 2*pDotV*pDotV - Rtor2 - r2 + 2*Rtor2*v.z()*v.z()) ;
278  c[3] = 4*(pDotV*(pRad2 - Rtor2 - r2) + 2*Rtor2*p.z()*v.z()) ;
279  c[4] = pRad2*pRad2 - 2*pRad2*(Rtor2+r2)
280  + 4*Rtor2*p.z()*p.z() + (Rtor2-r2)*(Rtor2-r2) ;
281 
282  G4JTPolynomialSolver torusEq;
283 
284  num = torusEq.FindRoots( c, 4, srd, si );
285 
286  for ( i = 0; i < num; i++ )
287  {
288  if( si[i] == 0. ) { roots.push_back(srd[i]) ; } // store real roots
289  }
290 
291  std::sort(roots.begin() , roots.end() ) ; // sorting with <
292 }
293 
295 //
296 // Interface for DistanceToIn and DistanceToOut.
297 // Calls TorusRootsJT and returns the smalles possible distance to
298 // the surface.
299 // Attention: Difference in DistanceToIn/Out for points p on the surface.
300 
302  const G4ThreeVector& v,
303  G4double r,
304  G4bool IsDistanceToIn ) const
305 {
306  G4double bigdist = 10*mm ;
307  G4double tmin = kInfinity ;
308  G4double t, scal ;
309 
310  // calculate the distances to the intersections with the Torus
311  // from a given point p and direction v.
312  //
313  std::vector<G4double> roots ;
314  std::vector<G4double> rootsrefined ;
315  TorusRootsJT(p,v,r,roots) ;
316 
317  G4ThreeVector ptmp ;
318 
319  // determine the smallest non-negative solution
320  //
321  for ( size_t k = 0 ; k<roots.size() ; k++ )
322  {
323  t = roots[k] ;
324 
325  if ( t < -halfCarTolerance ) { continue ; } // skip negative roots
326 
327  if ( t > bigdist && t<kInfinity ) // problem with big distances
328  {
329  ptmp = p + t*v ;
330  TorusRootsJT(ptmp,v,r,rootsrefined) ;
331  if ( rootsrefined.size()==roots.size() )
332  {
333  t = t + rootsrefined[k] ;
334  }
335  }
336 
337  ptmp = p + t*v ; // calculate the position of the proposed intersection
338 
339  G4double theta = std::atan2(ptmp.y(),ptmp.x());
340 
341  if ( fSPhi >= 0 )
342  {
343  if ( theta < - halfAngTolerance ) { theta += twopi; }
344  if ( (std::fabs(theta) < halfAngTolerance)
345  && (std::fabs(fSPhi + fDPhi - twopi) < halfAngTolerance) )
346  {
347  theta += twopi ; // 0 <= theta < 2pi
348  }
349  }
350  if ((fSPhi <= -pi )&&(theta>halfAngTolerance)) { theta = theta-twopi; }
351 
352  // We have to verify if this root is inside the region between
353  // fSPhi and fSPhi + fDPhi
354  //
355  if ( (theta - fSPhi >= - halfAngTolerance)
356  && (theta - (fSPhi + fDPhi) <= halfAngTolerance) )
357  {
358  // check if P is on the surface, and called from DistanceToIn
359  // DistanceToIn has to return 0.0 if particle is going inside the solid
360 
361  if ( IsDistanceToIn == true )
362  {
363  if (std::fabs(t) < halfCarTolerance )
364  {
365  // compute scalar product at position p : v.n
366  // ( n taken from SurfaceNormal, not normalized )
367 
368  scal = v* G4ThreeVector( p.x()*(1-fRtor/std::sqrt(p.x()*p.x()
369  + p.y()*p.y())),
370  p.y()*(1-fRtor/std::sqrt(p.x()*p.x()
371  + p.y()*p.y())),
372  p.z() );
373 
374  // change sign in case of inner radius
375  //
376  if ( r == GetRmin() ) { scal = -scal ; }
377  if ( scal < 0 ) { return 0.0 ; }
378  }
379  }
380 
381  // check if P is on the surface, and called from DistanceToOut
382  // DistanceToIn has to return 0.0 if particle is leaving the solid
383 
384  if ( IsDistanceToIn == false )
385  {
386  if (std::fabs(t) < halfCarTolerance )
387  {
388  // compute scalar product at position p : v.n
389  //
390  scal = v* G4ThreeVector( p.x()*(1-fRtor/std::sqrt(p.x()*p.x()
391  + p.y()*p.y())),
392  p.y()*(1-fRtor/std::sqrt(p.x()*p.x()
393  + p.y()*p.y())),
394  p.z() );
395 
396  // change sign in case of inner radius
397  //
398  if ( r == GetRmin() ) { scal = -scal ; }
399  if ( scal > 0 ) { return 0.0 ; }
400  }
401  }
402 
403  // check if distance is larger than 1/2 kCarTolerance
404  //
405  if( t > halfCarTolerance )
406  {
407  tmin = t ;
408  return tmin ;
409  }
410  }
411  }
412 
413  return tmin;
414 }
415 
417 //
418 // Get bounding box
419 
421 {
422  G4double rmax = GetRmax();
423  G4double rtor = GetRtor();
424  G4double rint = rtor - rmax;
425  G4double rext = rtor + rmax;
426  G4double dz = rmax;
427 
428  // Find bounding box
429  //
430  if (GetDPhi() >= twopi)
431  {
432  pMin.set(-rext,-rext,-dz);
433  pMax.set( rext, rext, dz);
434  }
435  else
436  {
437  G4TwoVector vmin,vmax;
438  G4GeomTools::DiskExtent(rint,rext,
441  vmin,vmax);
442  pMin.set(vmin.x(),vmin.y(),-dz);
443  pMax.set(vmax.x(),vmax.y(), dz);
444  }
445 
446  // Check correctness of the bounding box
447  //
448  if (pMin.x() >= pMax.x() || pMin.y() >= pMax.y() || pMin.z() >= pMax.z())
449  {
450  std::ostringstream message;
451  message << "Bad bounding box (min >= max) for solid: "
452  << GetName() << " !"
453  << "\npMin = " << pMin
454  << "\npMax = " << pMax;
455  G4Exception("G4Torus::Extent()", "GeomMgt0001", JustWarning, message);
456  DumpInfo();
457  }
458 }
459 
461 //
462 // Calculate extent under transform and specified limit
463 
465  const G4VoxelLimits& pVoxelLimit,
466  const G4AffineTransform& pTransform,
467  G4double& pMin, G4double& pMax) const
468 {
469  G4ThreeVector bmin, bmax;
470  G4bool exist;
471 
472  // Get bounding box
473  Extent(bmin,bmax);
474 
475  // Check bounding box
476  G4BoundingEnvelope bbox(bmin,bmax);
477 #ifdef G4BBOX_EXTENT
478  if (true) return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
479 #endif
480  if (bbox.BoundingBoxVsVoxelLimits(pAxis,pVoxelLimit,pTransform,pMin,pMax))
481  {
482  return exist = (pMin < pMax) ? true : false;
483  }
484 
485  // Get parameters of the solid
486  G4double rmin = GetRmin();
487  G4double rmax = GetRmax();
488  G4double rtor = GetRtor();
489  G4double dphi = GetDPhi();
490  G4double sinStart = GetSinStartPhi();
491  G4double cosStart = GetCosStartPhi();
492  G4double sinEnd = GetSinEndPhi();
493  G4double cosEnd = GetCosEndPhi();
494  G4double rint = rtor - rmax;
495  G4double rext = rtor + rmax;
496 
497  // Find bounding envelope and calculate extent
498  //
499  static const G4int NPHI = 24; // number of steps for whole torus
500  static const G4int NDISK = 16; // number of steps for disk
501  static const G4double sinHalfDisk = std::sin(pi/NDISK);
502  static const G4double cosHalfDisk = std::cos(pi/NDISK);
503  static const G4double sinStepDisk = 2.*sinHalfDisk*cosHalfDisk;
504  static const G4double cosStepDisk = 1. - 2.*sinHalfDisk*sinHalfDisk;
505 
506  G4double astep = (360/NPHI)*deg; // max angle for one slice in phi
507  G4int kphi = (dphi <= astep) ? 1 : (G4int)((dphi-deg)/astep) + 1;
508  G4double ang = dphi/kphi;
509 
510  G4double sinHalf = std::sin(0.5*ang);
511  G4double cosHalf = std::cos(0.5*ang);
512  G4double sinStep = 2.*sinHalf*cosHalf;
513  G4double cosStep = 1. - 2.*sinHalf*sinHalf;
514 
515  // define vectors for bounding envelope
516  G4ThreeVectorList pols[NDISK+1];
517  for (G4int k=0; k<NDISK+1; ++k) pols[k].resize(4);
518 
519  std::vector<const G4ThreeVectorList *> polygons;
520  polygons.resize(NDISK+1);
521  for (G4int k=0; k<NDISK+1; ++k) polygons[k] = &pols[k];
522 
523  // set internal and external reference circles
524  G4TwoVector rzmin[NDISK];
525  G4TwoVector rzmax[NDISK];
526 
527  if ((rtor-rmin*sinHalfDisk)/cosHalf > (rtor+rmin*sinHalfDisk)) rmin = 0;
528  rmax /= cosHalfDisk;
529  G4double sinCurDisk = sinHalfDisk;
530  G4double cosCurDisk = cosHalfDisk;
531  for (G4int k=0; k<NDISK; ++k)
532  {
533  G4double rmincur = rtor + rmin*cosCurDisk;
534  if (cosCurDisk < 0 && rmin > 0) rmincur /= cosHalf;
535  rzmin[k].set(rmincur,rmin*sinCurDisk);
536 
537  G4double rmaxcur = rtor + rmax*cosCurDisk;
538  if (cosCurDisk > 0) rmaxcur /= cosHalf;
539  rzmax[k].set(rmaxcur,rmax*sinCurDisk);
540 
541  G4double sinTmpDisk = sinCurDisk;
542  sinCurDisk = sinCurDisk*cosStepDisk + cosCurDisk*sinStepDisk;
543  cosCurDisk = cosCurDisk*cosStepDisk - sinTmpDisk*sinStepDisk;
544  }
545 
546  // Loop along slices in Phi. The extent is calculated as cumulative
547  // extent of the slices
548  pMin = kInfinity;
549  pMax = -kInfinity;
550  G4double eminlim = pVoxelLimit.GetMinExtent(pAxis);
551  G4double emaxlim = pVoxelLimit.GetMaxExtent(pAxis);
552  G4double sinCur1 = 0, cosCur1 = 0, sinCur2 = 0, cosCur2 = 0;
553  for (G4int i=0; i<kphi+1; ++i)
554  {
555  if (i == 0)
556  {
557  sinCur1 = sinStart;
558  cosCur1 = cosStart;
559  sinCur2 = sinCur1*cosHalf + cosCur1*sinHalf;
560  cosCur2 = cosCur1*cosHalf - sinCur1*sinHalf;
561  }
562  else
563  {
564  sinCur1 = sinCur2;
565  cosCur1 = cosCur2;
566  sinCur2 = (i == kphi) ? sinEnd : sinCur1*cosStep + cosCur1*sinStep;
567  cosCur2 = (i == kphi) ? cosEnd : cosCur1*cosStep - sinCur1*sinStep;
568  }
569  for (G4int k=0; k<NDISK; ++k)
570  {
571  G4double r1 = rzmin[k].x(), r2 = rzmax[k].x();
572  G4double z1 = rzmin[k].y(), z2 = rzmax[k].y();
573  pols[k][0].set(r1*cosCur1,r1*sinCur1,z1);
574  pols[k][1].set(r2*cosCur1,r2*sinCur1,z2);
575  pols[k][2].set(r2*cosCur2,r2*sinCur2,z2);
576  pols[k][3].set(r1*cosCur2,r1*sinCur2,z1);
577  }
578  pols[NDISK] = pols[0];
579 
580  // get bounding box of current slice
581  G4TwoVector vmin,vmax;
583  DiskExtent(rint,rext,sinCur1,cosCur1,sinCur2,cosCur2,vmin,vmax);
584  bmin.setX(vmin.x()); bmin.setY(vmin.y());
585  bmax.setX(vmax.x()); bmax.setY(vmax.y());
586 
587  // set bounding envelope for current slice and adjust extent
588  G4double emin,emax;
589  G4BoundingEnvelope benv(bmin,bmax,polygons);
590  if (!benv.CalculateExtent(pAxis,pVoxelLimit,pTransform,emin,emax)) continue;
591  if (emin < pMin) pMin = emin;
592  if (emax > pMax) pMax = emax;
593  if (eminlim > pMin && emaxlim < pMax) break; // max possible extent
594  }
595  return (pMin < pMax);
596 }
597 
599 //
600 // Return whether point inside/outside/on surface
601 
603 {
604  G4double r2, pt2, pPhi, tolRMin, tolRMax ;
605 
606  EInside in = kOutside ;
607 
608  // General precals
609  //
610  r2 = p.x()*p.x() + p.y()*p.y() ;
611  pt2 = r2 + p.z()*p.z() + fRtor*fRtor - 2*fRtor*std::sqrt(r2) ;
612 
613  if (fRmin) tolRMin = fRmin + fRminTolerance ;
614  else tolRMin = 0 ;
615 
616  tolRMax = fRmax - fRmaxTolerance;
617 
618  if (pt2 >= tolRMin*tolRMin && pt2 <= tolRMax*tolRMax )
619  {
620  if ( fDPhi == twopi || pt2 == 0 ) // on torus swept axis
621  {
622  in = kInside ;
623  }
624  else
625  {
626  // Try inner tolerant phi boundaries (=>inside)
627  // if not inside, try outer tolerant phi boundaries
628 
629  pPhi = std::atan2(p.y(),p.x()) ;
630 
631  if ( pPhi < -halfAngTolerance ) { pPhi += twopi ; } // 0<=pPhi<2pi
632  if ( fSPhi >= 0 )
633  {
634  if ( (std::fabs(pPhi) < halfAngTolerance)
635  && (std::fabs(fSPhi + fDPhi - twopi) < halfAngTolerance) )
636  {
637  pPhi += twopi ; // 0 <= pPhi < 2pi
638  }
639  if ( (pPhi >= fSPhi + halfAngTolerance)
640  && (pPhi <= fSPhi + fDPhi - halfAngTolerance) )
641  {
642  in = kInside ;
643  }
644  else if ( (pPhi >= fSPhi - halfAngTolerance)
645  && (pPhi <= fSPhi + fDPhi + halfAngTolerance) )
646  {
647  in = kSurface ;
648  }
649  }
650  else // fSPhi < 0
651  {
652  if ( (pPhi <= fSPhi + twopi - halfAngTolerance)
653  && (pPhi >= fSPhi + fDPhi + halfAngTolerance) ) {;}
654  else
655  {
656  in = kSurface ;
657  }
658  }
659  }
660  }
661  else // Try generous boundaries
662  {
663  tolRMin = fRmin - fRminTolerance ;
664  tolRMax = fRmax + fRmaxTolerance ;
665 
666  if (tolRMin < 0 ) { tolRMin = 0 ; }
667 
668  if ( (pt2 >= tolRMin*tolRMin) && (pt2 <= tolRMax*tolRMax) )
669  {
670  if ( (fDPhi == twopi) || (pt2 == 0) ) // Continuous in phi or on z-axis
671  {
672  in = kSurface ;
673  }
674  else // Try outer tolerant phi boundaries only
675  {
676  pPhi = std::atan2(p.y(),p.x()) ;
677 
678  if ( pPhi < -halfAngTolerance ) { pPhi += twopi ; } // 0<=pPhi<2pi
679  if ( fSPhi >= 0 )
680  {
681  if ( (std::fabs(pPhi) < halfAngTolerance)
682  && (std::fabs(fSPhi + fDPhi - twopi) < halfAngTolerance) )
683  {
684  pPhi += twopi ; // 0 <= pPhi < 2pi
685  }
686  if ( (pPhi >= fSPhi - halfAngTolerance)
687  && (pPhi <= fSPhi + fDPhi + halfAngTolerance) )
688  {
689  in = kSurface;
690  }
691  }
692  else // fSPhi < 0
693  {
694  if ( (pPhi <= fSPhi + twopi - halfAngTolerance)
695  && (pPhi >= fSPhi + fDPhi + halfAngTolerance) ) {;}
696  else
697  {
698  in = kSurface ;
699  }
700  }
701  }
702  }
703  }
704  return in ;
705 }
706 
708 //
709 // Return unit normal of surface closest to p
710 // - note if point on z axis, ignore phi divided sides
711 // - unsafe if point close to z axis a rmin=0 - no explicit checks
712 
714 {
715  G4int noSurfaces = 0;
716  G4double rho2, rho, pt2, pt, pPhi;
717  G4double distRMin = kInfinity;
718  G4double distSPhi = kInfinity, distEPhi = kInfinity;
719 
720  // To cope with precision loss
721  //
722  const G4double delta = std::max(10.0*kCarTolerance,
723  1.0e-8*(fRtor+fRmax));
724  const G4double dAngle = 10.0*kAngTolerance;
725 
726  G4ThreeVector nR, nPs, nPe;
727  G4ThreeVector norm, sumnorm(0.,0.,0.);
728 
729  rho2 = p.x()*p.x() + p.y()*p.y();
730  rho = std::sqrt(rho2);
731  pt2 = rho2+p.z()*p.z() +fRtor * (fRtor-2*rho);
732  pt2 = std::max(pt2, 0.0); // std::fabs(pt2);
733  pt = std::sqrt(pt2) ;
734 
735  G4double distRMax = std::fabs(pt - fRmax);
736  if(fRmin) distRMin = std::fabs(pt - fRmin);
737 
738  if( rho > delta && pt != 0.0 )
739  {
740  G4double redFactor= (rho-fRtor)/rho;
741  nR = G4ThreeVector( p.x()*redFactor, // p.x()*(1.-fRtor/rho),
742  p.y()*redFactor, // p.y()*(1.-fRtor/rho),
743  p.z() );
744  nR *= 1.0/pt;
745  }
746 
747  if ( fDPhi < twopi ) // && rho ) // old limitation against (0,0,z)
748  {
749  if ( rho )
750  {
751  pPhi = std::atan2(p.y(),p.x());
752 
753  if(pPhi < fSPhi-delta) { pPhi += twopi; }
754  else if(pPhi > fSPhi+fDPhi+delta) { pPhi -= twopi; }
755 
756  distSPhi = std::fabs( pPhi - fSPhi );
757  distEPhi = std::fabs(pPhi-fSPhi-fDPhi);
758  }
759  nPs = G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
760  nPe = G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
761  }
762  if( distRMax <= delta )
763  {
764  noSurfaces ++;
765  sumnorm += nR;
766  }
767  else if( fRmin && (distRMin <= delta) ) // Must not be on both Outer and Inner
768  {
769  noSurfaces ++;
770  sumnorm -= nR;
771  }
772 
773  // To be on one of the 'phi' surfaces,
774  // it must be within the 'tube' - with tolerance
775 
776  if( (fDPhi < twopi) && (fRmin-delta <= pt) && (pt <= (fRmax+delta)) )
777  {
778  if (distSPhi <= dAngle)
779  {
780  noSurfaces ++;
781  sumnorm += nPs;
782  }
783  if (distEPhi <= dAngle)
784  {
785  noSurfaces ++;
786  sumnorm += nPe;
787  }
788  }
789  if ( noSurfaces == 0 )
790  {
791 #ifdef G4CSGDEBUG
793  ed.precision(16);
794 
795  EInside inIt= Inside( p );
796 
797  if( inIt != kSurface )
798  {
799  ed << " ERROR> Surface Normal was called for Torus,"
800  << " with point not on surface." << G4endl;
801  }
802  else
803  {
804  ed << " ERROR> Surface Normal has not found a surface, "
805  << " despite the point being on the surface. " <<G4endl;
806  }
807 
808  if( inIt != kInside)
809  {
810  ed << " Safety (Dist To In) = " << DistanceToIn(p) << G4endl;
811  }
812  if( inIt != kOutside)
813  {
814  ed << " Safety (Dist to Out) = " << DistanceToOut(p) << G4endl;
815  }
816  ed << " Coordinates of point : " << p << G4endl;
817  ed << " Parameters of solid : " << G4endl << *this << G4endl;
818 
819  if( inIt == kSurface )
820  {
821  G4Exception("G4Torus::SurfaceNormal(p)", "GeomSolids1002",
822  JustWarning, ed,
823  "Failing to find normal, even though point is on surface!");
824  }
825  else
826  {
827  static const char* NameInside[3]= { "Inside", "Surface", "Outside" };
828  ed << " The point is " << NameInside[inIt] << " the solid. "<< G4endl;
829  G4Exception("G4Torus::SurfaceNormal(p)", "GeomSolids1002",
830  JustWarning, ed, "Point p is not on surface !?" );
831  }
832 #endif
833  norm = ApproxSurfaceNormal(p);
834  }
835  else if ( noSurfaces == 1 ) { norm = sumnorm; }
836  else { norm = sumnorm.unit(); }
837 
838  return norm ;
839 }
840 
842 //
843 // Algorithm for SurfaceNormal() following the original specification
844 // for points not on the surface
845 
847 {
848  ENorm side ;
849  G4ThreeVector norm;
850  G4double rho2,rho,pt2,pt,phi;
851  G4double distRMin,distRMax,distSPhi,distEPhi,distMin;
852 
853  rho2 = p.x()*p.x() + p.y()*p.y();
854  rho = std::sqrt(rho2) ;
855  pt2 = std::fabs(rho2+p.z()*p.z() +fRtor*fRtor - 2*fRtor*rho) ;
856  pt = std::sqrt(pt2) ;
857 
858 #ifdef G4CSGDEBUG
859  G4cout << " G4Torus::ApproximateSurfaceNormal called for point " << p
860  << G4endl;
861 #endif
862 
863  distRMax = std::fabs(pt - fRmax) ;
864 
865  if(fRmin) // First minimum radius
866  {
867  distRMin = std::fabs(pt - fRmin) ;
868 
869  if (distRMin < distRMax)
870  {
871  distMin = distRMin ;
872  side = kNRMin ;
873  }
874  else
875  {
876  distMin = distRMax ;
877  side = kNRMax ;
878  }
879  }
880  else
881  {
882  distMin = distRMax ;
883  side = kNRMax ;
884  }
885  if ( (fDPhi < twopi) && rho )
886  {
887  phi = std::atan2(p.y(),p.x()) ; // Protected against (0,0,z) (above rho!=0)
888 
889  if (phi < 0) { phi += twopi ; }
890 
891  if (fSPhi < 0 ) { distSPhi = std::fabs(phi-(fSPhi+twopi))*rho ; }
892  else { distSPhi = std::fabs(phi-fSPhi)*rho ; }
893 
894  distEPhi = std::fabs(phi - fSPhi - fDPhi)*rho ;
895 
896  if (distSPhi < distEPhi) // Find new minimum
897  {
898  if (distSPhi<distMin) side = kNSPhi ;
899  }
900  else
901  {
902  if (distEPhi < distMin) { side = kNEPhi ; }
903  }
904  }
905  switch (side)
906  {
907  case kNRMin: // Inner radius
908  norm = G4ThreeVector( -p.x()*(1-fRtor/rho)/pt,
909  -p.y()*(1-fRtor/rho)/pt,
910  -p.z()/pt ) ;
911  break ;
912  case kNRMax: // Outer radius
913  norm = G4ThreeVector( p.x()*(1-fRtor/rho)/pt,
914  p.y()*(1-fRtor/rho)/pt,
915  p.z()/pt ) ;
916  break;
917  case kNSPhi:
918  norm = G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0) ;
919  break;
920  case kNEPhi:
921  norm = G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0) ;
922  break;
923  default: // Should never reach this case ...
924  DumpInfo();
925  G4Exception("G4Torus::ApproxSurfaceNormal()",
926  "GeomSolids1002", JustWarning,
927  "Undefined side for valid surface normal to solid.");
928  break ;
929  }
930  return norm ;
931 }
932 
934 //
935 // Calculate distance to shape from outside, along normalised vector
936 // - return kInfinity if no intersection, or intersection distance <= tolerance
937 //
938 // - Compute the intersection with the z planes
939 // - if at valid r, phi, return
940 //
941 // -> If point is outer outer radius, compute intersection with rmax
942 // - if at valid phi,z return
943 //
944 // -> Compute intersection with inner radius, taking largest +ve root
945 // - if valid (phi), save intersction
946 //
947 // -> If phi segmented, compute intersections with phi half planes
948 // - return smallest of valid phi intersections and
949 // inner radius intersection
950 //
951 // NOTE:
952 // - Precalculations for phi trigonometry are Done `just in time'
953 // - `if valid' implies tolerant checking of intersection points
954 
956  const G4ThreeVector& v ) const
957 {
958 
959  G4double snxt=kInfinity, sphi=kInfinity; // snxt = default return value
960 
961  G4double sd[4] ;
962 
963  // Precalculated trig for phi intersections - used by r,z intersections to
964  // check validity
965 
966  G4bool seg; // true if segmented
967  G4double hDPhi; // half dphi
968  G4double cPhi,sinCPhi=0.,cosCPhi=0.; // central phi
969 
970  G4double tolORMin2; // `generous' radii squared
971  G4double tolORMax2;
972 
973  G4double Dist,xi,yi,zi,rhoi2,it2; // Intersection point variables
974 
975  G4double Comp;
976  G4double cosSPhi,sinSPhi; // Trig for phi start intersect
977  G4double ePhi,cosEPhi,sinEPhi; // for phi end intersect
978 
979  // Set phi divided flag and precalcs
980  //
981  if ( fDPhi < twopi )
982  {
983  seg = true ;
984  hDPhi = 0.5*fDPhi ; // half delta phi
985  cPhi = fSPhi + hDPhi ;
986  sinCPhi = std::sin(cPhi) ;
987  cosCPhi = std::cos(cPhi) ;
988  }
989  else
990  {
991  seg = false ;
992  }
993 
994  if (fRmin > fRminTolerance) // Calculate tolerant rmin and rmax
995  {
996  tolORMin2 = (fRmin - fRminTolerance)*(fRmin - fRminTolerance) ;
997  }
998  else
999  {
1000  tolORMin2 = 0 ;
1001  }
1002  tolORMax2 = (fRmax + fRmaxTolerance)*(fRmax + fRmaxTolerance) ;
1003 
1004  // Intersection with Rmax (possible return) and Rmin (must also check phi)
1005 
1006  G4double Rtor2 = fRtor*fRtor ;
1007 
1008  snxt = SolveNumericJT(p,v,fRmax,true);
1009 
1010  if (fRmin) // Possible Rmin intersection
1011  {
1012  sd[0] = SolveNumericJT(p,v,fRmin,true);
1013  if ( sd[0] < snxt ) { snxt = sd[0] ; }
1014  }
1015 
1016  //
1017  // Phi segment intersection
1018  //
1019  // o Tolerant of points inside phi planes by up to kCarTolerance*0.5
1020  //
1021  // o NOTE: Large duplication of code between sphi & ephi checks
1022  // -> only diffs: sphi -> ephi, Comp -> -Comp and half-plane
1023  // intersection check <=0 -> >=0
1024  // -> use some form of loop Construct ?
1025 
1026  if (seg)
1027  {
1028  sinSPhi = std::sin(fSPhi) ; // First phi surface ('S'tarting phi)
1029  cosSPhi = std::cos(fSPhi) ;
1030  Comp = v.x()*sinSPhi - v.y()*cosSPhi ; // Component in outwards
1031  // normal direction
1032  if (Comp < 0 )
1033  {
1034  Dist = (p.y()*cosSPhi - p.x()*sinSPhi) ;
1035 
1036  if (Dist < halfCarTolerance)
1037  {
1038  sphi = Dist/Comp ;
1039  if (sphi < snxt)
1040  {
1041  if ( sphi < 0 ) { sphi = 0 ; }
1042 
1043  xi = p.x() + sphi*v.x() ;
1044  yi = p.y() + sphi*v.y() ;
1045  zi = p.z() + sphi*v.z() ;
1046  rhoi2 = xi*xi + yi*yi ;
1047  it2 = std::fabs(rhoi2 + zi*zi + Rtor2 - 2*fRtor*std::sqrt(rhoi2)) ;
1048 
1049  if ( it2 >= tolORMin2 && it2 <= tolORMax2 )
1050  {
1051  // r intersection is good - check intersecting
1052  // with correct half-plane
1053  //
1054  if ((yi*cosCPhi-xi*sinCPhi)<=0) { snxt=sphi; }
1055  }
1056  }
1057  }
1058  }
1059  ePhi=fSPhi+fDPhi; // Second phi surface ('E'nding phi)
1060  sinEPhi=std::sin(ePhi);
1061  cosEPhi=std::cos(ePhi);
1062  Comp=-(v.x()*sinEPhi-v.y()*cosEPhi);
1063 
1064  if ( Comp < 0 ) // Component in outwards normal dirn
1065  {
1066  Dist = -(p.y()*cosEPhi - p.x()*sinEPhi) ;
1067 
1068  if (Dist < halfCarTolerance )
1069  {
1070  sphi = Dist/Comp ;
1071 
1072  if (sphi < snxt )
1073  {
1074  if (sphi < 0 ) { sphi = 0 ; }
1075 
1076  xi = p.x() + sphi*v.x() ;
1077  yi = p.y() + sphi*v.y() ;
1078  zi = p.z() + sphi*v.z() ;
1079  rhoi2 = xi*xi + yi*yi ;
1080  it2 = std::fabs(rhoi2 + zi*zi + Rtor2 - 2*fRtor*std::sqrt(rhoi2)) ;
1081 
1082  if (it2 >= tolORMin2 && it2 <= tolORMax2)
1083  {
1084  // z and r intersections good - check intersecting
1085  // with correct half-plane
1086  //
1087  if ((yi*cosCPhi-xi*sinCPhi)>=0) { snxt=sphi; }
1088  }
1089  }
1090  }
1091  }
1092  }
1093  if(snxt < halfCarTolerance) { snxt = 0.0 ; }
1094 
1095  return snxt ;
1096 }
1097 
1099 //
1100 // Calculate distance (<= actual) to closest surface of shape from outside
1101 // - Calculate distance to z, radial planes
1102 // - Only to phi planes if outside phi extent
1103 // - Return 0 if point inside
1104 
1106 {
1107  G4double safe=0.0, safe1, safe2 ;
1108  G4double phiC, cosPhiC, sinPhiC, safePhi, ePhi, cosPsi ;
1109  G4double rho2, rho, pt2, pt ;
1110 
1111  rho2 = p.x()*p.x() + p.y()*p.y() ;
1112  rho = std::sqrt(rho2) ;
1113  pt2 = std::fabs(rho2 + p.z()*p.z() + fRtor*fRtor - 2*fRtor*rho) ;
1114  pt = std::sqrt(pt2) ;
1115 
1116  safe1 = fRmin - pt ;
1117  safe2 = pt - fRmax ;
1118 
1119  if (safe1 > safe2) { safe = safe1; }
1120  else { safe = safe2; }
1121 
1122  if ( fDPhi < twopi && rho )
1123  {
1124  phiC = fSPhi + fDPhi*0.5 ;
1125  cosPhiC = std::cos(phiC) ;
1126  sinPhiC = std::sin(phiC) ;
1127  cosPsi = (p.x()*cosPhiC + p.y()*sinPhiC)/rho ;
1128 
1129  if (cosPsi < std::cos(fDPhi*0.5) ) // Psi=angle from central phi to point
1130  { // Point lies outside phi range
1131  if ((p.y()*cosPhiC - p.x()*sinPhiC) <= 0 )
1132  {
1133  safePhi = std::fabs(p.x()*std::sin(fSPhi) - p.y()*std::cos(fSPhi)) ;
1134  }
1135  else
1136  {
1137  ePhi = fSPhi + fDPhi ;
1138  safePhi = std::fabs(p.x()*std::sin(ePhi) - p.y()*std::cos(ePhi)) ;
1139  }
1140  if (safePhi > safe) { safe = safePhi ; }
1141  }
1142  }
1143  if (safe < 0 ) { safe = 0 ; }
1144  return safe;
1145 }
1146 
1148 //
1149 // Calculate distance to surface of shape from `inside', allowing for tolerance
1150 // - Only Calc rmax intersection if no valid rmin intersection
1151 //
1152 
1154  const G4ThreeVector& v,
1155  const G4bool calcNorm,
1156  G4bool *validNorm,
1157  G4ThreeVector *n ) const
1158 {
1159  ESide side = kNull, sidephi = kNull ;
1160  G4double snxt = kInfinity, sphi, sd[4] ;
1161 
1162  // Vars for phi intersection
1163  //
1164  G4double sinSPhi, cosSPhi, ePhi, sinEPhi, cosEPhi;
1165  G4double cPhi, sinCPhi, cosCPhi ;
1166  G4double pDistS, compS, pDistE, compE, sphi2, xi, yi, zi, vphi ;
1167 
1168  // Radial Intersections Defenitions & General Precals
1169 
1171 
1172 #if 1
1173 
1174  // This is the version with the calculation of CalcNorm = true
1175  // To be done: Check the precision of this calculation.
1176  // If you want return always validNorm = false, then take the version below
1177 
1178  G4double rho2 = p.x()*p.x()+p.y()*p.y();
1179  G4double rho = std::sqrt(rho2) ;
1180 
1181  G4double pt2 = rho2 + p.z()*p.z() + fRtor * (fRtor - 2.0*rho);
1182  // Regroup for slightly better FP accuracy
1183 
1184  if( pt2 < 0.0)
1185  {
1186  pt2= std::fabs( pt2 );
1187  }
1188 
1189  G4double pt = std::sqrt(pt2) ;
1190 
1191  G4double pDotV = p.x()*v.x() + p.y()*v.y() + p.z()*v.z() ;
1192 
1193  G4double tolRMax = fRmax - fRmaxTolerance ;
1194 
1195  G4double vDotNmax = pDotV - fRtor*(v.x()*p.x() + v.y()*p.y())/rho ;
1196  G4double pDotxyNmax = (1 - fRtor/rho) ;
1197 
1198  if( (pt2 > tolRMax*tolRMax) && (vDotNmax >= 0) )
1199  {
1200  // On tolerant boundary & heading outwards (or perpendicular to) outer
1201  // radial surface -> leaving immediately with *n for really convex part
1202  // only
1203 
1204  if ( calcNorm && (pDotxyNmax >= -2.*fRmaxTolerance) )
1205  {
1206  *n = G4ThreeVector( p.x()*(1 - fRtor/rho)/pt,
1207  p.y()*(1 - fRtor/rho)/pt,
1208  p.z()/pt ) ;
1209  *validNorm = true ;
1210  }
1211 
1212  return snxt = 0 ; // Leaving by Rmax immediately
1213  }
1214 
1215  snxt = SolveNumericJT(p,v,fRmax,false);
1216  side = kRMax ;
1217 
1218  // rmin
1219 
1220  if ( fRmin )
1221  {
1222  G4double tolRMin = fRmin + fRminTolerance ;
1223 
1224  if ( (pt2 < tolRMin*tolRMin) && (vDotNmax < 0) )
1225  {
1226  if (calcNorm) { *validNorm = false ; } // Concave surface of the torus
1227  return snxt = 0 ; // Leaving by Rmin immediately
1228  }
1229 
1230  sd[0] = SolveNumericJT(p,v,fRmin,false);
1231  if ( sd[0] < snxt )
1232  {
1233  snxt = sd[0] ;
1234  side = kRMin ;
1235  }
1236  }
1237 
1238 #else
1239 
1240  // this is the "conservative" version which return always validnorm = false
1241  // NOTE: using this version the unit test testG4Torus will break
1242 
1243  snxt = SolveNumericJT(p,v,fRmax,false);
1244  side = kRMax ;
1245 
1246  if ( fRmin )
1247  {
1248  sd[0] = SolveNumericJT(p,v,fRmin,false);
1249  if ( sd[0] < snxt )
1250  {
1251  snxt = sd[0] ;
1252  side = kRMin ;
1253  }
1254  }
1255 
1256  if ( calcNorm && (snxt == 0.0) )
1257  {
1258  *validNorm = false ; // Leaving solid, but possible re-intersection
1259  return snxt ;
1260  }
1261 
1262 #endif
1263 
1264  if (fDPhi < twopi) // Phi Intersections
1265  {
1266  sinSPhi = std::sin(fSPhi) ;
1267  cosSPhi = std::cos(fSPhi) ;
1268  ePhi = fSPhi + fDPhi ;
1269  sinEPhi = std::sin(ePhi) ;
1270  cosEPhi = std::cos(ePhi) ;
1271  cPhi = fSPhi + fDPhi*0.5 ;
1272  sinCPhi = std::sin(cPhi) ;
1273  cosCPhi = std::cos(cPhi) ;
1274 
1275  // angle calculation with correction
1276  // of difference in domain of atan2 and Sphi
1277  //
1278  vphi = std::atan2(v.y(),v.x()) ;
1279 
1280  if ( vphi < fSPhi - halfAngTolerance ) { vphi += twopi; }
1281  else if ( vphi > ePhi + halfAngTolerance ) { vphi -= twopi; }
1282 
1283  if ( p.x() || p.y() ) // Check if on z axis (rho not needed later)
1284  {
1285  pDistS = p.x()*sinSPhi - p.y()*cosSPhi ; // pDist -ve when inside
1286  pDistE = -p.x()*sinEPhi + p.y()*cosEPhi ;
1287 
1288  // Comp -ve when in direction of outwards normal
1289  //
1290  compS = -sinSPhi*v.x() + cosSPhi*v.y() ;
1291  compE = sinEPhi*v.x() - cosEPhi*v.y() ;
1292  sidephi = kNull ;
1293 
1294  if( ( (fDPhi <= pi) && ( (pDistS <= halfCarTolerance)
1295  && (pDistE <= halfCarTolerance) ) )
1296  || ( (fDPhi > pi) && !((pDistS > halfCarTolerance)
1297  && (pDistE > halfCarTolerance) ) ) )
1298  {
1299  // Inside both phi *full* planes
1300 
1301  if ( compS < 0 )
1302  {
1303  sphi = pDistS/compS ;
1304 
1305  if (sphi >= -halfCarTolerance)
1306  {
1307  xi = p.x() + sphi*v.x() ;
1308  yi = p.y() + sphi*v.y() ;
1309 
1310  // Check intersecting with correct half-plane
1311  // (if not -> no intersect)
1312  //
1313  if ( (std::fabs(xi)<=kCarTolerance)
1314  && (std::fabs(yi)<=kCarTolerance) )
1315  {
1316  sidephi = kSPhi;
1317  if ( ((fSPhi-halfAngTolerance)<=vphi)
1318  && ((ePhi+halfAngTolerance)>=vphi) )
1319  {
1320  sphi = kInfinity;
1321  }
1322  }
1323  else if ( yi*cosCPhi-xi*sinCPhi >=0 )
1324  {
1325  sphi = kInfinity ;
1326  }
1327  else
1328  {
1329  sidephi = kSPhi ;
1330  }
1331  }
1332  else
1333  {
1334  sphi = kInfinity ;
1335  }
1336  }
1337  else
1338  {
1339  sphi = kInfinity ;
1340  }
1341 
1342  if ( compE < 0 )
1343  {
1344  sphi2 = pDistE/compE ;
1345 
1346  // Only check further if < starting phi intersection
1347  //
1348  if ( (sphi2 > -kCarTolerance) && (sphi2 < sphi) )
1349  {
1350  xi = p.x() + sphi2*v.x() ;
1351  yi = p.y() + sphi2*v.y() ;
1352 
1353  if ( (std::fabs(xi)<=kCarTolerance)
1354  && (std::fabs(yi)<=kCarTolerance) )
1355  {
1356  // Leaving via ending phi
1357  //
1358  if( !( (fSPhi-halfAngTolerance <= vphi)
1359  && (ePhi+halfAngTolerance >= vphi) ) )
1360  {
1361  sidephi = kEPhi ;
1362  sphi = sphi2;
1363  }
1364  }
1365  else // Check intersecting with correct half-plane
1366  {
1367  if ( (yi*cosCPhi-xi*sinCPhi) >= 0)
1368  {
1369  // Leaving via ending phi
1370  //
1371  sidephi = kEPhi ;
1372  sphi = sphi2;
1373 
1374  }
1375  }
1376  }
1377  }
1378  }
1379  else
1380  {
1381  sphi = kInfinity ;
1382  }
1383  }
1384  else
1385  {
1386  // On z axis + travel not || to z axis -> if phi of vector direction
1387  // within phi of shape, Step limited by rmax, else Step =0
1388 
1389  vphi = std::atan2(v.y(),v.x());
1390 
1391  if ( ( fSPhi-halfAngTolerance <= vphi ) &&
1392  ( vphi <= ( ePhi+halfAngTolerance ) ) )
1393  {
1394  sphi = kInfinity;
1395  }
1396  else
1397  {
1398  sidephi = kSPhi ; // arbitrary
1399  sphi=0;
1400  }
1401  }
1402 
1403  // Order intersections
1404 
1405  if (sphi<snxt)
1406  {
1407  snxt=sphi;
1408  side=sidephi;
1409  }
1410  }
1411 
1412  G4double rhoi2,rhoi,it2,it,iDotxyNmax ;
1413  // Note: by numerical computation we know where the ray hits the torus
1414  // So I propose to return the side where the ray hits
1415 
1416  if (calcNorm)
1417  {
1418  switch(side)
1419  {
1420  case kRMax: // n is unit vector
1421  xi = p.x() + snxt*v.x() ;
1422  yi =p.y() + snxt*v.y() ;
1423  zi = p.z() + snxt*v.z() ;
1424  rhoi2 = xi*xi + yi*yi ;
1425  rhoi = std::sqrt(rhoi2) ;
1426  it2 = std::fabs(rhoi2 + zi*zi + fRtor*fRtor - 2*fRtor*rhoi) ;
1427  it = std::sqrt(it2) ;
1428  iDotxyNmax = (1-fRtor/rhoi) ;
1429  if(iDotxyNmax >= -2.*fRmaxTolerance) // really convex part of Rmax
1430  {
1431  *n = G4ThreeVector( xi*(1-fRtor/rhoi)/it,
1432  yi*(1-fRtor/rhoi)/it,
1433  zi/it ) ;
1434  *validNorm = true ;
1435  }
1436  else
1437  {
1438  *validNorm = false ; // concave-convex part of Rmax
1439  }
1440  break ;
1441 
1442  case kRMin:
1443  *validNorm = false ; // Rmin is concave or concave-convex
1444  break;
1445 
1446  case kSPhi:
1447  if (fDPhi <= pi )
1448  {
1449  *n=G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
1450  *validNorm=true;
1451  }
1452  else
1453  {
1454  *validNorm = false ;
1455  }
1456  break ;
1457 
1458  case kEPhi:
1459  if (fDPhi <= pi)
1460  {
1461  *n=G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
1462  *validNorm=true;
1463  }
1464  else
1465  {
1466  *validNorm = false ;
1467  }
1468  break;
1469 
1470  default:
1471 
1472  // It seems we go here from time to time ...
1473 
1474  G4cout << G4endl;
1475  DumpInfo();
1476  std::ostringstream message;
1477  G4int oldprc = message.precision(16);
1478  message << "Undefined side for valid surface normal to solid."
1479  << G4endl
1480  << "Position:" << G4endl << G4endl
1481  << "p.x() = " << p.x()/mm << " mm" << G4endl
1482  << "p.y() = " << p.y()/mm << " mm" << G4endl
1483  << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl
1484  << "Direction:" << G4endl << G4endl
1485  << "v.x() = " << v.x() << G4endl
1486  << "v.y() = " << v.y() << G4endl
1487  << "v.z() = " << v.z() << G4endl << G4endl
1488  << "Proposed distance :" << G4endl << G4endl
1489  << "snxt = " << snxt/mm << " mm" << G4endl;
1490  message.precision(oldprc);
1491  G4Exception("G4Torus::DistanceToOut(p,v,..)",
1492  "GeomSolids1002",JustWarning, message);
1493  break;
1494  }
1495  }
1496  if ( snxt<halfCarTolerance ) { snxt=0 ; }
1497 
1498  return snxt;
1499 }
1500 
1502 //
1503 // Calculate distance (<=actual) to closest surface of shape from inside
1504 
1506 {
1507  G4double safe=0.0,safeR1,safeR2;
1508  G4double rho2,rho,pt2,pt ;
1509  G4double safePhi,phiC,cosPhiC,sinPhiC,ePhi;
1510  rho2 = p.x()*p.x() + p.y()*p.y() ;
1511  rho = std::sqrt(rho2) ;
1512  pt2 = std::fabs(rho2 + p.z()*p.z() + fRtor*fRtor - 2*fRtor*rho) ;
1513  pt = std::sqrt(pt2) ;
1514 
1515 #ifdef G4CSGDEBUG
1516  if( Inside(p) == kOutside )
1517  {
1518  G4int oldprc = G4cout.precision(16) ;
1519  G4cout << G4endl ;
1520  DumpInfo();
1521  G4cout << "Position:" << G4endl << G4endl ;
1522  G4cout << "p.x() = " << p.x()/mm << " mm" << G4endl ;
1523  G4cout << "p.y() = " << p.y()/mm << " mm" << G4endl ;
1524  G4cout << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl ;
1525  G4cout.precision(oldprc);
1526  G4Exception("G4Torus::DistanceToOut(p)", "GeomSolids1002",
1527  JustWarning, "Point p is outside !?" );
1528  }
1529 #endif
1530 
1531  if (fRmin)
1532  {
1533  safeR1 = pt - fRmin ;
1534  safeR2 = fRmax - pt ;
1535 
1536  if (safeR1 < safeR2) { safe = safeR1 ; }
1537  else { safe = safeR2 ; }
1538  }
1539  else
1540  {
1541  safe = fRmax - pt ;
1542  }
1543 
1544  // Check if phi divided, Calc distances closest phi plane
1545  //
1546  if (fDPhi<twopi) // Above/below central phi of Torus?
1547  {
1548  phiC = fSPhi + fDPhi*0.5 ;
1549  cosPhiC = std::cos(phiC) ;
1550  sinPhiC = std::sin(phiC) ;
1551 
1552  if ((p.y()*cosPhiC-p.x()*sinPhiC)<=0)
1553  {
1554  safePhi = -(p.x()*std::sin(fSPhi) - p.y()*std::cos(fSPhi)) ;
1555  }
1556  else
1557  {
1558  ePhi = fSPhi + fDPhi ;
1559  safePhi = (p.x()*std::sin(ePhi) - p.y()*std::cos(ePhi)) ;
1560  }
1561  if (safePhi < safe) { safe = safePhi ; }
1562  }
1563  if (safe < 0) { safe = 0 ; }
1564  return safe ;
1565 }
1566 
1568 //
1569 // Stream object contents to an output stream
1570 
1572 {
1573  return G4String("G4Torus");
1574 }
1575 
1577 //
1578 // Make a clone of the object
1579 //
1581 {
1582  return new G4Torus(*this);
1583 }
1584 
1586 //
1587 // Stream object contents to an output stream
1588 
1589 std::ostream& G4Torus::StreamInfo( std::ostream& os ) const
1590 {
1591  G4int oldprc = os.precision(16);
1592  os << "-----------------------------------------------------------\n"
1593  << " *** Dump for solid - " << GetName() << " ***\n"
1594  << " ===================================================\n"
1595  << " Solid type: G4Torus\n"
1596  << " Parameters: \n"
1597  << " inner radius: " << fRmin/mm << " mm \n"
1598  << " outer radius: " << fRmax/mm << " mm \n"
1599  << " swept radius: " << fRtor/mm << " mm \n"
1600  << " starting phi: " << fSPhi/degree << " degrees \n"
1601  << " delta phi : " << fDPhi/degree << " degrees \n"
1602  << "-----------------------------------------------------------\n";
1603  os.precision(oldprc);
1604 
1605  return os;
1606 }
1607 
1609 //
1610 // GetPointOnSurface
1611 
1613 {
1614  G4double cosu, sinu,cosv, sinv, aOut, aIn, aSide, chose, phi, theta, rRand;
1615 
1617  theta = G4RandFlat::shoot(0.,twopi);
1618 
1619  cosu = std::cos(phi); sinu = std::sin(phi);
1620  cosv = std::cos(theta); sinv = std::sin(theta);
1621 
1622  // compute the areas
1623 
1624  aOut = (fDPhi)*twopi*fRtor*fRmax;
1625  aIn = (fDPhi)*twopi*fRtor*fRmin;
1626  aSide = pi*(fRmax*fRmax-fRmin*fRmin);
1627 
1628  if ((fSPhi == 0) && (fDPhi == twopi)){ aSide = 0; }
1629  chose = G4RandFlat::shoot(0.,aOut + aIn + 2.*aSide);
1630 
1631  if(chose < aOut)
1632  {
1633  return G4ThreeVector ((fRtor+fRmax*cosv)*cosu,
1634  (fRtor+fRmax*cosv)*sinu, fRmax*sinv);
1635  }
1636  else if( (chose >= aOut) && (chose < aOut + aIn) )
1637  {
1638  return G4ThreeVector ((fRtor+fRmin*cosv)*cosu,
1639  (fRtor+fRmin*cosv)*sinu, fRmin*sinv);
1640  }
1641  else if( (chose >= aOut + aIn) && (chose < aOut + aIn + aSide) )
1642  {
1643  rRand = GetRadiusInRing(fRmin,fRmax);
1644  return G4ThreeVector ((fRtor+rRand*cosv)*std::cos(fSPhi),
1645  (fRtor+rRand*cosv)*std::sin(fSPhi), rRand*sinv);
1646  }
1647  else
1648  {
1649  rRand = GetRadiusInRing(fRmin,fRmax);
1650  return G4ThreeVector ((fRtor+rRand*cosv)*std::cos(fSPhi+fDPhi),
1651  (fRtor+rRand*cosv)*std::sin(fSPhi+fDPhi),
1652  rRand*sinv);
1653  }
1654 }
1655 
1657 //
1658 // Visualisation Functions
1659 
1661 {
1662  scene.AddSolid (*this);
1663 }
1664 
1666 {
1667  return new G4PolyhedronTorus (fRmin, fRmax, fRtor, fSPhi, fDPhi);
1668 }
1669 
1670 #endif // !defined(G4GEOM_USE_TORUS) || !defined(G4GEOM_USE_SYS_USOLIDS)
G4String GetName() const
EInside Inside(const G4ThreeVector &p) const
Definition: G4Torus.cc:602
ThreeVector shoot(const G4int Ap, const G4int Af)
G4double fRminTolerance
Definition: G4Torus.hh:203
G4double fRmin
Definition: G4Torus.hh:195
G4Polyhedron * CreatePolyhedron() const
Definition: G4Torus.cc:1665
static constexpr double mm
Definition: G4SIunits.hh:115
static const G4double kInfinity
Definition: geomdefs.hh:42
std::ostringstream G4ExceptionDescription
Definition: globals.hh:76
CLHEP::Hep3Vector G4ThreeVector
G4bool fRebuildPolyhedron
Definition: G4CSGSolid.hh:80
G4Torus & operator=(const G4Torus &rhs)
Definition: G4Torus.cc:220
std::ostream & StreamInfo(std::ostream &os) const
Definition: G4Torus.cc:1589
std::vector< ExP01TrackerHit * > a
Definition: ExP01Classes.hh:33
G4double GetRadiusInRing(G4double rmin, G4double rmax) const
Definition: G4CSGSolid.cc:111
G4double fRmax
Definition: G4Torus.hh:195
G4double DistanceToIn(const G4ThreeVector &p, const G4ThreeVector &v) const
Definition: G4Torus.cc:955
G4double SolveNumericJT(const G4ThreeVector &p, const G4ThreeVector &v, G4double r, G4bool IsDistanceToIn) const
Definition: G4Torus.cc:301
G4double GetRmax() const
G4double fCubicVolume
Definition: G4CSGSolid.hh:78
~G4Torus()
Definition: G4Torus.cc:199
G4double GetSinStartPhi() const
virtual void AddSolid(const G4Box &)=0
G4bool CalculateExtent(const EAxis pAxis, const G4VoxelLimits &pVoxelLimit, const G4AffineTransform &pTransform, G4double &pmin, G4double &pmax) const
Definition: G4Torus.cc:464
int G4int
Definition: G4Types.hh:78
void DumpInfo() const
G4double GetRtor() const
static constexpr double twopi
Definition: G4SIunits.hh:76
G4double GetSinEndPhi() const
void Extent(G4ThreeVector &pMin, G4ThreeVector &pMax) const
Definition: G4Torus.cc:420
G4double fSurfaceArea
Definition: G4CSGSolid.hh:79
G4GLOB_DLL std::ostream G4cout
G4double kRadTolerance
Definition: G4Torus.hh:203
G4double GetRmin() const
static constexpr double degree
Definition: G4SIunits.hh:144
G4double GetCosEndPhi() const
G4double GetRadialTolerance() const
bool G4bool
Definition: G4Types.hh:79
G4bool CalculateExtent(const EAxis pAxis, const G4VoxelLimits &pVoxelLimits, const G4Transform3D &pTransform3D, G4double &pMin, G4double &pMax) const
G4bool BoundingBoxVsVoxelLimits(const EAxis pAxis, const G4VoxelLimits &pVoxelLimits, const G4Transform3D &pTransform3D, G4double &pMin, G4double &pMax) const
G4Torus(const G4String &pName, G4double pRmin, G4double pRmax, G4double pRtor, G4double pSPhi, G4double pDPhi)
Definition: G4Torus.cc:86
G4ThreeVector SurfaceNormal(const G4ThreeVector &p) const
Definition: G4Torus.cc:713
const G4int n
G4double GetDPhi() const
std::vector< G4ThreeVector > G4ThreeVectorList
G4VSolid * Clone() const
Definition: G4Torus.cc:1580
G4ThreeVector GetPointOnSurface() const
Definition: G4Torus.cc:1612
void G4Exception(const char *originOfException, const char *exceptionCode, G4ExceptionSeverity severity, const char *comments)
Definition: G4Exception.cc:41
static const G4double emax
virtual void ComputeDimensions(G4Box &, const G4int, const G4VPhysicalVolume *) const
G4double fSPhi
Definition: G4Torus.hh:195
G4double GetCosStartPhi() const
void ComputeDimensions(G4VPVParameterisation *p, const G4int n, const G4VPhysicalVolume *pRep)
Definition: G4Torus.cc:247
G4double DistanceToOut(const G4ThreeVector &p, const G4ThreeVector &v, const G4bool calcNorm=G4bool(false), G4bool *validNorm=0, G4ThreeVector *n=0) const
Definition: G4Torus.cc:1153
G4double halfCarTolerance
Definition: G4Torus.hh:206
G4double kAngTolerance
Definition: G4Torus.hh:203
T max(const T t1, const T t2)
brief Return the largest of the two arguments
EInside
Definition: geomdefs.hh:58
G4double fDPhi
Definition: G4Torus.hh:195
EAxis
Definition: geomdefs.hh:54
G4double halfAngTolerance
Definition: G4Torus.hh:206
CLHEP::Hep2Vector G4TwoVector
Definition: G4TwoVector.hh:42
G4GeometryType GetEntityType() const
Definition: G4Torus.cc:1571
#define G4endl
Definition: G4ios.hh:61
void TorusRootsJT(const G4ThreeVector &p, const G4ThreeVector &v, G4double r, std::vector< G4double > &roots) const
Definition: G4Torus.cc:261
G4double fRtor
Definition: G4Torus.hh:195
G4double kCarTolerance
Definition: G4VSolid.hh:307
static constexpr double pi
Definition: G4SIunits.hh:75
void SetAllParameters(G4double pRmin, G4double pRmax, G4double pRtor, G4double pSPhi, G4double pDPhi)
Definition: G4Torus.cc:102
double G4double
Definition: G4Types.hh:76
G4double fRmaxTolerance
Definition: G4Torus.hh:203
static constexpr double deg
Definition: G4SIunits.hh:152
G4double GetMaxExtent(const EAxis pAxis) const
void DescribeYourselfTo(G4VGraphicsScene &scene) const
Definition: G4Torus.cc:1660
G4CSGSolid & operator=(const G4CSGSolid &rhs)
Definition: G4CSGSolid.cc:91
G4int FindRoots(G4double *op, G4int degree, G4double *zeror, G4double *zeroi)
G4double GetAngularTolerance() const
G4double GetMinExtent(const EAxis pAxis) const
static G4bool DiskExtent(G4double rmin, G4double rmax, G4double startPhi, G4double delPhi, G4TwoVector &pmin, G4TwoVector &pmax)
Definition: G4GeomTools.cc:378
static G4GeometryTolerance * GetInstance()
G4ThreeVector ApproxSurfaceNormal(const G4ThreeVector &p) const
Definition: G4Torus.cc:846