Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/geometry/solids/specific/src/G4Hype.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  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 // Implementation of G4Hype
 27 //
 28 // Authors: 
 29 //      Ernesto Lamanna (Ernesto.Lamanna@roma1.infn.it) &
 30 //      Francesco Safai Tehrani (Francesco.SafaiTehrani@roma1.infn.it)
 31 //      Rome, INFN & University of Rome "La Sapienza",  9 June 1998.
 32 // --------------------------------------------------------------------
 33 
 34 #include "G4Hype.hh"
 35 
 36 #if !(defined(G4GEOM_USE_UHYPE) && defined(G4GEOM_USE_SYS_USOLIDS))
 37 
 38 #include "G4VoxelLimits.hh"
 39 #include "G4AffineTransform.hh"
 40 #include "G4BoundingEnvelope.hh"
 41 #include "G4ClippablePolygon.hh"
 42 
 43 #include "G4VPVParameterisation.hh"
 44 
 45 #include "meshdefs.hh"
 46 
 47 #include <cmath>
 48 
 49 #include "Randomize.hh"
 50 
 51 #include "G4VGraphicsScene.hh"
 52 #include "G4VisExtent.hh"
 53 
 54 #include "G4AutoLock.hh"
 55 
 56 namespace
 57 {
 58   G4Mutex polyhedronMutex = G4MUTEX_INITIALIZER;
 59 }
 60 
 61 using namespace CLHEP;
 62 
 63 // Constructor - check parameters, and fills protected data members
 64 //
 65 G4Hype::G4Hype(const G4String& pName,
 66                      G4double newInnerRadius,
 67                      G4double newOuterRadius,
 68                      G4double newInnerStereo,
 69                      G4double newOuterStereo,
 70                      G4double newHalfLenZ)
 71   : G4VSolid(pName)
 72 {
 73   fHalfTol = 0.5*kCarTolerance;
 74 
 75   // Check z-len
 76   //
 77   if (newHalfLenZ<=0)
 78   {
 79     std::ostringstream message;
 80     message << "Invalid Z half-length - " << GetName() << G4endl
 81             << "        Invalid Z half-length: "
 82             << newHalfLenZ/mm << " mm";
 83     G4Exception("G4Hype::G4Hype()", "GeomSolids0002",
 84                 FatalErrorInArgument, message);
 85   }
 86   halfLenZ=newHalfLenZ;   
 87 
 88   // Check radii
 89   //
 90   if (newInnerRadius<0 || newOuterRadius<0)
 91   {
 92     std::ostringstream message;
 93     message << "Invalid radii - " << GetName() << G4endl
 94             << "        Invalid radii !  Inner radius: "
 95             << newInnerRadius/mm << " mm" << G4endl
 96             << "                         Outer radius: "
 97             << newOuterRadius/mm << " mm";
 98     G4Exception("G4Hype::G4Hype()", "GeomSolids0002",
 99                 FatalErrorInArgument, message);
100   }
101   if (newInnerRadius >= newOuterRadius)
102   {
103     std::ostringstream message;
104     message << "Outer > inner radius - " << GetName() << G4endl
105             << "        Invalid radii !  Inner radius: "
106             << newInnerRadius/mm << " mm" << G4endl
107             << "                         Outer radius: "
108             << newOuterRadius/mm << " mm";
109     G4Exception("G4Hype::G4Hype()", "GeomSolids0002",
110                 FatalErrorInArgument, message);
111   }
112 
113   innerRadius=newInnerRadius;
114   outerRadius=newOuterRadius;
115 
116   innerRadius2=innerRadius*innerRadius;
117   outerRadius2=outerRadius*outerRadius;
118     
119   SetInnerStereo( newInnerStereo );
120   SetOuterStereo( newOuterStereo );
121 }
122 
123 // Fake default constructor - sets only member data and allocates memory
124 //                            for usage restricted to object persistency.
125 //
126 G4Hype::G4Hype( __void__& a  )
127   : G4VSolid(a), innerRadius(0.), outerRadius(0.), halfLenZ(0.), innerStereo(0.),
128     outerStereo(0.), tanInnerStereo(0.), tanOuterStereo(0.), tanInnerStereo2(0.),
129     tanOuterStereo2(0.), innerRadius2(0.), outerRadius2(0.), endInnerRadius2(0.),
130     endOuterRadius2(0.), endInnerRadius(0.), endOuterRadius(0.), fHalfTol(0.)
131 {
132 }
133 
134 // Destructor
135 //
136 G4Hype::~G4Hype()
137 {
138   delete fpPolyhedron; fpPolyhedron = nullptr;
139 }
140 
141 // Copy constructor
142 //
143 G4Hype::G4Hype(const G4Hype& rhs)
144   : G4VSolid(rhs), innerRadius(rhs.innerRadius),
145     outerRadius(rhs.outerRadius), halfLenZ(rhs.halfLenZ),
146     innerStereo(rhs.innerStereo), outerStereo(rhs.outerStereo),
147     tanInnerStereo(rhs.tanInnerStereo), tanOuterStereo(rhs.tanOuterStereo),
148     tanInnerStereo2(rhs.tanInnerStereo2), tanOuterStereo2(rhs.tanOuterStereo2),
149     innerRadius2(rhs.innerRadius2), outerRadius2(rhs.outerRadius2),
150     endInnerRadius2(rhs.endInnerRadius2), endOuterRadius2(rhs.endOuterRadius2),
151     endInnerRadius(rhs.endInnerRadius), endOuterRadius(rhs.endOuterRadius),
152     fCubicVolume(rhs.fCubicVolume), fSurfaceArea(rhs.fSurfaceArea),
153     fHalfTol(rhs.fHalfTol)
154 {
155 }
156 
157 // Assignment operator
158 //
159 G4Hype& G4Hype::operator = (const G4Hype& rhs) 
160 {
161    // Check assignment to self
162    //
163    if (this == &rhs)  { return *this; }
164 
165    // Copy base class data
166    //
167    G4VSolid::operator=(rhs);
168 
169    // Copy data
170    //
171    innerRadius = rhs.innerRadius; outerRadius = rhs.outerRadius;
172    halfLenZ = rhs.halfLenZ;
173    innerStereo = rhs.innerStereo; outerStereo = rhs.outerStereo;
174    tanInnerStereo = rhs.tanInnerStereo; tanOuterStereo = rhs.tanOuterStereo;
175    tanInnerStereo2 = rhs.tanInnerStereo2; tanOuterStereo2 = rhs.tanOuterStereo2;
176    innerRadius2 = rhs.innerRadius2; outerRadius2 = rhs.outerRadius2;
177    endInnerRadius2 = rhs.endInnerRadius2; endOuterRadius2 = rhs.endOuterRadius2;
178    endInnerRadius = rhs.endInnerRadius; endOuterRadius = rhs.endOuterRadius;
179    fCubicVolume = rhs.fCubicVolume; fSurfaceArea = rhs.fSurfaceArea;
180    fHalfTol = rhs.fHalfTol;
181    fRebuildPolyhedron = false;
182    delete fpPolyhedron; fpPolyhedron = nullptr;
183 
184    return *this;
185 }
186 
187 // Dispatch to parameterisation for replication mechanism dimension
188 // computation & modification.
189 //
190 void G4Hype::ComputeDimensions(G4VPVParameterisation* p,
191                               const G4int n,
192                               const G4VPhysicalVolume* pRep)
193 {
194   p->ComputeDimensions(*this,n,pRep);
195 }
196 
197 // Get bounding box
198 //
199 void G4Hype::BoundingLimits(G4ThreeVector& pMin, G4ThreeVector& pMax) const
200 {
201   pMin.set(-endOuterRadius,-endOuterRadius,-halfLenZ);
202   pMax.set( endOuterRadius, endOuterRadius, halfLenZ);
203 
204   // Check correctness of the bounding box
205   //
206   if (pMin.x() >= pMax.x() || pMin.y() >= pMax.y() || pMin.z() >= pMax.z())
207   {
208     std::ostringstream message;
209     message << "Bad bounding box (min >= max) for solid: "
210             << GetName() << " !"
211             << "\npMin = " << pMin
212             << "\npMax = " << pMax;
213     G4Exception("G4Hype::BoundingLimits()", "GeomMgt0001",
214                 JustWarning, message);
215     DumpInfo();
216   }
217 }
218 
219 // Calculate extent under transform and specified limit
220 //
221 G4bool G4Hype::CalculateExtent(const EAxis pAxis,
222                                const G4VoxelLimits& pVoxelLimit,
223                                const G4AffineTransform& pTransform,
224                                      G4double& pMin, G4double& pMax) const
225 {
226   G4ThreeVector bmin, bmax;
227 
228   // Get bounding box
229   BoundingLimits(bmin,bmax);
230 
231   // Find extent
232   G4BoundingEnvelope bbox(bmin,bmax);
233   return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
234 }
235 
236 // Decides whether point is inside, outside or on the surface
237 //
238 EInside G4Hype::Inside(const G4ThreeVector& p) const
239 {
240   //
241   // Check z extents: are we outside?
242   //
243   const G4double absZ(std::fabs(p.z()));
244   if (absZ > halfLenZ + fHalfTol) return kOutside;
245   
246   //
247   // Check outer radius
248   //
249   const G4double oRad2(HypeOuterRadius2(absZ));
250   const G4double xR2( p.x()*p.x()+p.y()*p.y() );
251   
252   if (xR2 > oRad2 + kCarTolerance*endOuterRadius) return kOutside;
253   
254   if (xR2 > oRad2 - kCarTolerance*endOuterRadius) return kSurface;
255   
256   if (InnerSurfaceExists())
257   {
258     //
259     // Check inner radius
260     //
261     const G4double iRad2(HypeInnerRadius2(absZ));
262     
263     if (xR2 < iRad2 - kCarTolerance*endInnerRadius) return kOutside;
264     
265     if (xR2 < iRad2 + kCarTolerance*endInnerRadius) return kSurface;
266   }
267   
268   //
269   // We are inside in radius, now check endplate surface
270   //
271   if (absZ > halfLenZ - fHalfTol) return kSurface;
272   
273   return kInside;
274 }
275 
276 // Returns the normal unit vector to the Hyperbolical Surface at a point 
277 // p on (or nearly on) the surface
278 //
279 G4ThreeVector G4Hype::SurfaceNormal( const G4ThreeVector& p ) const
280 {
281   //
282   // Which of the three or four surfaces are we closest to?
283   //
284   const G4double absZ(std::fabs(p.z()));
285   const G4double distZ(absZ - halfLenZ);
286   const G4double dist2Z(distZ*distZ);
287   
288   const G4double xR2( p.x()*p.x()+p.y()*p.y() );
289   const G4double dist2Outer( std::fabs(xR2 - HypeOuterRadius2(absZ)) );
290   
291   if (InnerSurfaceExists())
292   {
293     //
294     // Has inner surface: is this closest?
295     //
296     const G4double dist2Inner( std::fabs(xR2 - HypeInnerRadius2(absZ)) );
297     if (dist2Inner < dist2Z && dist2Inner < dist2Outer)
298       return G4ThreeVector( -p.x(), -p.y(), p.z()*tanInnerStereo2 ).unit();
299   }
300 
301   //
302   // Do the "endcaps" win?
303   //
304   if (dist2Z < dist2Outer) 
305     return { 0.0, 0.0, (G4double)(p.z() < 0 ? -1.0 : 1.0) };
306     
307     
308   //
309   // Outer surface wins
310   //
311   return G4ThreeVector( p.x(), p.y(), -p.z()*tanOuterStereo2 ).unit();
312 }
313 
314 // Calculates distance to shape from outside, along normalised vector
315 // - return kInfinity if no intersection,
316 //   or intersection distance <= tolerance
317 //
318 // Calculating the intersection of a line with the surfaces
319 // is fairly straight forward. The difficult problem is dealing
320 // with the intersections of the surfaces in a consistent manner, 
321 // and this accounts for the complicated logic.
322 //
323 G4double G4Hype::DistanceToIn( const G4ThreeVector& p,
324                                const G4ThreeVector& v ) const
325 {
326   //
327   // Quick test. Beware! This assumes v is a unit vector!
328   //
329   if (std::fabs(p.x()*v.y() - p.y()*v.x()) > endOuterRadius+kCarTolerance)
330     return kInfinity;
331   
332   //
333   // Take advantage of z symmetry, and reflect throught the
334   // z=0 plane so that pz is always positive
335   //
336   G4double pz(p.z()), vz(v.z());
337   if (pz < 0)
338   {
339     pz = -pz;
340     vz = -vz;
341   }
342 
343   //
344   // We must be very careful if we don't want to
345   // create subtle leaks at the edges where the
346   // hyperbolic surfaces connect to the endplate.
347   // The only reliable way to do so is to make sure
348   // that the decision as to when a track passes
349   // over the edge of one surface is exactly the
350   // same decision as to when a track passes into the
351   // other surface. By "exact", we don't mean algebraicly
352   // exact, but we mean the same machine instructions
353   // should be used.
354   //
355   G4bool couldMissOuter(true),
356          couldMissInner(true),
357          cantMissInnerCylinder(false);
358   
359   //
360   // Check endplate intersection
361   //
362   G4double sigz = pz-halfLenZ;
363   
364   if (sigz > -fHalfTol)    // equivalent to: if (pz > halfLenZ - fHalfTol)
365   {
366     //
367     // We start in front of the endplate (within roundoff)
368     // Correct direction to intersect endplate?
369     //
370     if (vz >= 0)
371     {
372       //
373       // Nope. As long as we are far enough away, we
374       // can't intersect anything
375       //
376       if (sigz > 0) return kInfinity;
377       
378       //
379       // Otherwise, we may still hit a hyperbolic surface
380       // if the point is on the hyperbolic surface (within tolerance)
381       //
382       G4double pr2 = p.x()*p.x() + p.y()*p.y();
383       if (pr2 > endOuterRadius2 + kCarTolerance*endOuterRadius)
384         return kInfinity;
385       
386       if (InnerSurfaceExists())
387       {
388         if (pr2 < endInnerRadius2 - kCarTolerance*endInnerRadius)
389           return kInfinity;
390         if ( (pr2 < endOuterRadius2 - kCarTolerance*endOuterRadius)
391           && (pr2 > endInnerRadius2 + kCarTolerance*endInnerRadius) )
392           return kInfinity;
393       }
394       else
395       {
396         if (pr2 < endOuterRadius2 - kCarTolerance*endOuterRadius)
397           return kInfinity;
398       }
399     }
400     else
401     {
402       //
403       // Where do we intersect at z = halfLenZ?
404       //
405       G4double q = -sigz/vz;
406       G4double xi = p.x() + q*v.x(),
407                yi = p.y() + q*v.y();
408          
409       //
410       // Is this on the endplate? If so, return s, unless
411       // we are on the tolerant surface, in which case return 0
412       //
413       G4double pr2 = xi*xi + yi*yi;
414       if (pr2 <= endOuterRadius2)
415       {
416         if (InnerSurfaceExists())
417         {
418           if (pr2 >= endInnerRadius2) return (sigz < fHalfTol) ? 0 : q;
419           //
420           // This test is sufficient to ensure that the
421           // trajectory cannot miss the inner hyperbolic surface
422           // for z > 0, if the normal is correct.
423           //
424           G4double dot1 = (xi*v.x() + yi*v.y())*endInnerRadius/std::sqrt(pr2);
425           couldMissInner = (dot1 - halfLenZ*tanInnerStereo2*vz <= 0);
426           
427           if (pr2 > endInnerRadius2*(1 - 2*DBL_EPSILON) )
428           {
429             //
430             // There is a potential leak if the inner
431             // surface is a cylinder
432             //
433             if ( (innerStereo < DBL_MIN)
434               && ((std::fabs(v.x()) > DBL_MIN) || (std::fabs(v.y()) > DBL_MIN)))
435               cantMissInnerCylinder = true;
436           }
437         }
438         else
439         {
440           return (sigz < fHalfTol) ? 0 : q;
441         }
442       }
443       else
444       {
445         G4double dotR( xi*v.x() + yi*v.y() );
446         if (dotR >= 0)
447         {
448           //
449           // Otherwise, if we are traveling outwards, we know
450           // we must miss the hyperbolic surfaces also, so
451           // we need not bother checking
452           //
453           return kInfinity;
454         }
455         else
456         {
457           //
458           // This test is sufficient to ensure that the
459           // trajectory cannot miss the outer hyperbolic surface
460           // for z > 0, if the normal is correct.
461           //
462           G4double dot1 = dotR*endOuterRadius/std::sqrt(pr2);
463           couldMissOuter = (dot1 - halfLenZ*tanOuterStereo2*vz>= 0);
464         }
465       }
466     }
467   }
468     
469   //
470   // Check intersection with outer hyperbolic surface, save
471   // distance to valid intersection into "best".
472   //    
473   G4double best = kInfinity;
474   
475   G4double q[2];
476   G4int n = IntersectHype( p, v, outerRadius2, tanOuterStereo2, q );
477   
478   if (n > 0)
479   {
480     //
481     // Potential intersection: is p on this surface?
482     //
483     if (pz < halfLenZ+fHalfTol)
484     {
485       G4double dr2 = p.x()*p.x() + p.y()*p.y() - HypeOuterRadius2(pz);
486       if (std::fabs(dr2) < kCarTolerance*endOuterRadius)
487       {
488         //
489         // Sure, but make sure we're traveling inwards at
490         // this point
491         //
492         if (p.x()*v.x() + p.y()*v.y() - pz*tanOuterStereo2*vz < 0)
493           return 0;
494       }
495     }
496     
497     //
498     // We are now certain that p is not on the tolerant surface.
499     // Accept only position distance q
500     //
501     for( G4int i=0; i<n; ++i )
502     {
503       if (q[i] >= 0)
504       {
505         //
506         // Check to make sure this intersection point is
507         // on the surface, but only do so if we haven't
508         // checked the endplate intersection already
509         //
510         G4double zi = pz + q[i]*vz;
511         
512         if (zi < -halfLenZ) continue;
513         if (zi > +halfLenZ && couldMissOuter) continue;
514         
515         //
516         // Check normal
517         //
518         G4double xi = p.x() + q[i]*v.x(),
519            yi = p.y() + q[i]*v.y();
520            
521         if (xi*v.x() + yi*v.y() - zi*tanOuterStereo2*vz > 0) continue;
522 
523         best = q[i];
524         break;
525       }
526     }
527   }
528   
529   if (!InnerSurfaceExists()) return best;    
530   
531   //
532   // Check intersection with inner hyperbolic surface
533   //
534   n = IntersectHype( p, v, innerRadius2, tanInnerStereo2, q );  
535   if (n == 0)
536   {
537     if (cantMissInnerCylinder) return (sigz < fHalfTol) ? 0 : -sigz/vz;
538         
539     return best;
540   }
541   
542   //
543   // P on this surface?
544   //
545   if (pz < halfLenZ+fHalfTol)
546   {
547     G4double dr2 = p.x()*p.x() + p.y()*p.y() - HypeInnerRadius2(pz);
548     if (std::fabs(dr2) < kCarTolerance*endInnerRadius)
549     {
550       //
551       // Sure, but make sure we're traveling outwards at
552       // this point
553       //
554       if (p.x()*v.x() + p.y()*v.y() - pz*tanInnerStereo2*vz > 0) return 0;
555     }
556   }
557   
558   //
559   // No, so only positive q is valid. Search for a valid intersection
560   // that is closer than the outer intersection (if it exists)
561   //
562   for( G4int i=0; i<n; ++i )
563   {
564     if (q[i] > best) break;
565     if (q[i] >= 0)
566     {
567       //
568       // Check to make sure this intersection point is
569       // on the surface, but only do so if we haven't
570       // checked the endplate intersection already
571       //
572       G4double zi = pz + q[i]*vz;
573 
574       if (zi < -halfLenZ) continue;
575       if (zi > +halfLenZ && couldMissInner) continue;
576 
577       //
578       // Check normal
579       //
580       G4double xi = p.x() + q[i]*v.x(),
581          yi = p.y() + q[i]*v.y();
582 
583       if (xi*v.x() + yi*v.y() - zi*tanOuterStereo2*vz < 0) continue;
584 
585       best = q[i];
586       break;
587     }
588   }
589     
590   //
591   // Done
592   //
593   return best;
594 }
595 
596 // Calculates distance to shape from outside, along perpendicular direction 
597 // (if one exists). May be an underestimate.
598 //
599 // There are five (r,z) regions:
600 //    1. a point that is beyond the endcap but within the
601 //       endcap radii
602 //    2. a point with r > outer endcap radius and with
603 //       a z position that is beyond the cone formed by the
604 //       normal of the outer hyperbolic surface at the 
605 //       edge at which it meets the endcap. 
606 //    3. a point that is outside the outer surface and not in (1 or 2)
607 //    4. a point that is inside the inner surface and not in (5)
608 //    5. a point with radius < inner endcap radius and
609 //       with a z position beyond the cone formed by the
610 //       normal of the inner hyperbolic surface at the
611 //       edge at which it meets the endcap.
612 // (regions 4 and 5 only exist if there is an inner surface)
613 //
614 G4double G4Hype::DistanceToIn(const G4ThreeVector& p) const
615 {
616   G4double absZ(std::fabs(p.z()));
617   
618   //
619   // Check region
620   //
621   G4double r2 = p.x()*p.x() + p.y()*p.y();
622   G4double r = std::sqrt(r2);
623   
624   G4double sigz = absZ - halfLenZ;
625   
626   if (r < endOuterRadius)
627   {
628     if (sigz > -fHalfTol)
629     {
630       if (InnerSurfaceExists())
631       {
632         if (r > endInnerRadius) 
633           return sigz < fHalfTol ? 0 : sigz;  // Region 1
634         
635         G4double dr = endInnerRadius - r;
636         if (sigz > dr*tanInnerStereo2)
637         {
638           //
639           // In region 5
640           //
641           G4double answer = std::sqrt( dr*dr + sigz*sigz );
642           return answer < fHalfTol ? 0 : answer;
643         }
644       }
645       else
646       {
647         //
648         // In region 1 (no inner surface)
649         //
650         return sigz < fHalfTol ? 0 : sigz;
651       }
652     }
653   }
654   else
655   {
656     G4double dr = r - endOuterRadius;
657     if (sigz > -dr*tanOuterStereo2)
658     {
659       //
660       // In region 2
661       //
662       G4double answer = std::sqrt( dr*dr + sigz*sigz );
663       return answer < fHalfTol ? 0 : answer;
664     }
665   }
666   
667   if (InnerSurfaceExists())
668   {
669     if (r2 < HypeInnerRadius2(absZ)+kCarTolerance*endInnerRadius)
670     {
671        //
672       // In region 4
673       //
674       G4double answer = ApproxDistInside( r,absZ,innerRadius,tanInnerStereo2 );
675       return answer < fHalfTol ? 0 : answer;
676     }
677   }
678   
679   //
680   // We are left by elimination with region 3
681   //
682   G4double answer = ApproxDistOutside( r, absZ, outerRadius, tanOuterStereo );
683   return answer < fHalfTol ? 0 : answer;
684 }
685 
686 // Calculates distance to surface of shape from 'inside', allowing for tolerance
687 //
688 // The situation here is much simplier than DistanceToIn(p,v). For
689 // example, there is no need to even check whether an intersection
690 // point is inside the boundary of a surface, as long as all surfaces 
691 // are checked and the smallest distance is used.
692 //
693 G4double G4Hype::DistanceToOut( const G4ThreeVector& p, const G4ThreeVector& v,
694                                 const G4bool calcNorm,
695                                 G4bool* validNorm, G4ThreeVector* norm ) const
696 {
697   static const G4ThreeVector normEnd1(0.0,0.0,+1.0);
698   static const G4ThreeVector normEnd2(0.0,0.0,-1.0);
699   
700   //
701   // Keep track of closest surface
702   //
703   G4double sBest;        // distance to
704   const G4ThreeVector* nBest;    // normal vector
705   G4bool vBest;        // whether "valid"
706 
707   //
708   // Check endplate, taking advantage of symmetry.
709   // Note that the endcap is the only surface which
710   // has a "valid" normal, i.e. is a surface of which
711   // the entire solid is behind.
712   //
713   G4double pz(p.z()), vz(v.z());
714   if (vz < 0)
715   {
716     pz = -pz;
717     vz = -vz;
718     nBest = &normEnd2;
719   }
720   else
721     nBest = &normEnd1;
722 
723   //
724   // Possible intercept. Are we on the surface?
725   //
726   if (pz > halfLenZ-fHalfTol)
727   {
728     if (calcNorm) { *norm = *nBest; *validNorm = true; }
729     return 0;
730   }
731 
732   //
733   // Nope. Get distance. Beware of zero vz.
734   //
735   sBest = (vz > DBL_MIN) ? (halfLenZ - pz)/vz : kInfinity;
736   vBest = true;
737   
738   //
739   // Check outer surface
740   //
741   G4double r2 = p.x()*p.x() + p.y()*p.y();
742   
743   G4double q[2];
744   G4int n = IntersectHype( p, v, outerRadius2, tanOuterStereo2, q );
745   
746   G4ThreeVector norm1, norm2;
747 
748   if (n > 0)
749   {
750     //
751     // We hit somewhere. Are we on the surface?
752     //  
753     G4double dr2 = r2 - HypeOuterRadius2(pz);
754     if (std::fabs(dr2) < endOuterRadius*kCarTolerance)
755     {
756       G4ThreeVector normHere( p.x(), p.y(), -p.z()*tanOuterStereo2 );
757       //
758       // Sure. But are we going the right way?
759       //
760       if (normHere.dot(v) > 0)
761       {
762         if (calcNorm) { *norm = normHere.unit(); *validNorm = false; }
763         return 0;
764       }
765     }
766     
767     //
768     // Nope. Check closest positive intercept.
769     //
770     for( G4int i=0; i<n; ++i )
771     {
772       if (q[i] > sBest) break;
773       if (q[i] > 0)
774       {
775         //
776         // Make sure normal is correct (that this
777         // solution is an outgoing solution)
778         //
779         G4ThreeVector pk(p+q[i]*v);
780         norm1 = G4ThreeVector( pk.x(), pk.y(), -pk.z()*tanOuterStereo2 );
781         if (norm1.dot(v) > 0)
782         {
783           sBest = q[i];
784           nBest = &norm1;
785           vBest = false;
786           break;
787         }
788       }
789     }
790   }
791   
792   if (InnerSurfaceExists())
793   {
794     //
795     // Check inner surface
796     //
797     n = IntersectHype( p, v, innerRadius2, tanInnerStereo2, q );
798     if (n > 0)
799     {
800       //
801       // On surface?
802       //
803       G4double dr2 = r2 - HypeInnerRadius2(pz);
804       if (std::fabs(dr2) < endInnerRadius*kCarTolerance)
805       {
806         G4ThreeVector normHere( -p.x(), -p.y(), p.z()*tanInnerStereo2 );
807         if (normHere.dot(v) > 0)
808         {
809           if (calcNorm)
810           {
811             *norm = normHere.unit();
812             *validNorm = false;
813           }
814           return 0;
815         }
816       }
817       
818       //
819       // Check closest positive
820       //
821       for( G4int i=0; i<n; ++i )
822       {
823         if (q[i] > sBest) break;
824         if (q[i] > 0)
825         {
826           G4ThreeVector pk(p+q[i]*v);
827           norm2 = G4ThreeVector( -pk.x(), -pk.y(), pk.z()*tanInnerStereo2 );
828           if (norm2.dot(v) > 0)
829           {
830             sBest = q[i];
831             nBest = &norm2;
832             vBest = false;
833             break;
834           }
835         }
836       }
837     }
838   }
839   
840   //
841   // Done!
842   //
843   if (calcNorm)
844   {
845     *validNorm = vBest;
846     
847     if (nBest == &norm1 || nBest == &norm2) 
848       *norm = nBest->unit();
849     else
850       *norm = *nBest;
851   }
852   
853   return sBest;
854 }
855 
856 // Calculates distance (<=actual) to closest surface of shape from inside
857 //
858 // May be an underestimate
859 //
860 G4double G4Hype::DistanceToOut(const G4ThreeVector& p) const
861 {
862   //
863   // Try each surface and remember the closest
864   //
865   G4double absZ(std::fabs(p.z()));
866   G4double r(p.perp());
867   
868   G4double sBest = halfLenZ - absZ;
869   
870   G4double tryOuter = ApproxDistInside( r, absZ, outerRadius, tanOuterStereo2 );
871   if (tryOuter < sBest)
872     sBest = tryOuter;
873   
874   if (InnerSurfaceExists())
875   {
876     G4double tryInner = ApproxDistOutside( r,absZ,innerRadius,tanInnerStereo );
877     if (tryInner < sBest) sBest = tryInner;
878   }
879   
880   return sBest < 0.5*kCarTolerance ? 0 : sBest;
881 }
882 
883 // IntersectHype (static)
884 //
885 // Decides if and where a line intersects with a hyperbolic
886 // surface (of infinite extent)
887 //
888 // Arguments:
889 //     p       - (in) Point on trajectory
890 //     v       - (in) Vector along trajectory
891 //     r2      - (in) Square of radius at z = 0
892 //     tan2phi - (in) std::tan(phi)**2
893 //     q       - (out) Up to two points of intersection, where the
894 //                     intersection point is p + q*v, and if there are
895 //                     two intersections, q[0] < q[1]. May be negative.
896 // Returns:
897 //     The number of intersections. If 0, the trajectory misses. 
898 //
899 //
900 // Equation of a line:
901 //
902 //       x = x0 + q*tx      y = y0 + q*ty      z = z0 + q*tz
903 //
904 // Equation of a hyperbolic surface:
905 //
906 //       x**2 + y**2 = r**2 + (z*tanPhi)**2
907 //
908 // Solution is quadratic:
909 //
910 //  a*q**2 + b*q + c = 0
911 //
912 // where:
913 //
914 //  a = tx**2 + ty**2 - (tz*tanPhi)**2
915 //
916 //  b = 2*( x0*tx + y0*ty - z0*tz*tanPhi**2 )
917 //
918 //  c = x0**2 + y0**2 - r**2 - (z0*tanPhi)**2
919 //
920 // 
921 G4int G4Hype::IntersectHype( const G4ThreeVector &p, const G4ThreeVector &v, 
922                              G4double r2, G4double tan2Phi, G4double ss[2] )
923 {
924   G4double x0 = p.x(), y0 = p.y(), z0 = p.z();
925   G4double tx = v.x(), ty = v.y(), tz = v.z();
926 
927   G4double a = tx*tx + ty*ty - tz*tz*tan2Phi;
928   G4double b = 2*( x0*tx + y0*ty - z0*tz*tan2Phi );
929   G4double c = x0*x0 + y0*y0 - r2 - z0*z0*tan2Phi;
930   
931   if (std::fabs(a) < DBL_MIN)
932   {
933     //
934     // The trajectory is parallel to the asympotic limit of
935     // the surface: single solution
936     //
937     if (std::fabs(b) < DBL_MIN) return 0;
938     // Unless we travel through exact center
939     
940     ss[0] = c/b;
941     return 1;
942   }
943 
944   G4double radical = b*b - 4*a*c;
945   
946   if (radical < -DBL_MIN) return 0;    // No solution
947   
948   if (radical < DBL_MIN)
949   {
950     //
951     // Grazes surface
952     //
953     ss[0] = -b/a/2.0;
954     return 1;
955   }
956   
957   radical = std::sqrt(radical);
958   
959   G4double q = -0.5*( b + (b < 0 ? -radical : +radical) );
960   G4double sa = q/a;
961   G4double sb = c/q;    
962   if (sa < sb) { ss[0] = sa; ss[1] = sb; } else { ss[0] = sb; ss[1] = sa; }
963   return 2;
964 }
965   
966 // ApproxDistOutside (static)
967 //
968 // Finds the approximate distance of a point outside
969 // (greater radius) of a hyperbolic surface. The distance
970 // must be an underestimate. It will also be nice (although
971 // not necesary) that the estimate is always finite no
972 // matter how close the point is.
973 //
974 // Our hyperbola approaches the asymptotic limit at z = +/- infinity
975 // to the lines r = z*tanPhi. We call these lines the 
976 // asymptotic limit line.
977 //
978 // We need the distance of the 2d point p(r,z) to the
979 // hyperbola r**2 = r0**2 + (z*tanPhi)**2. Find two
980 // points that bracket the true normal and use the 
981 // distance to the line that connects these two points.
982 // The first such point is z=p.z. The second point is
983 // the z position on the asymptotic limit line that
984 // contains the normal on the line through the point p.
985 //
986 G4double G4Hype::ApproxDistOutside( G4double pr, G4double pz,
987                                     G4double r0, G4double tanPhi )
988 {
989   if (tanPhi < DBL_MIN) return pr-r0;
990 
991   G4double tan2Phi = tanPhi*tanPhi;
992 
993   //
994   // First point
995   //
996   G4double z1 = pz;
997   G4double r1 = std::sqrt( r0*r0 + z1*z1*tan2Phi );
998   
999   //
1000   // Second point
1001   //
1002   G4double z2 = (pr*tanPhi + pz)/(1 + tan2Phi);
1003   G4double r2 = std::sqrt( r0*r0 + z2*z2*tan2Phi );
1004   
1005   //
1006   // Line between them
1007   //
1008   G4double dr = r2-r1;
1009   G4double dz = z2-z1;
1010   
1011   G4double len = std::sqrt(dr*dr + dz*dz);
1012   if (len < DBL_MIN)
1013   {
1014     //
1015     // The two points are the same?? I guess we
1016     // must have really bracketed the normal
1017     //
1018     dr = pr-r1;
1019     dz = pz-z1;
1020     return std::sqrt( dr*dr + dz*dz );
1021   }
1022   
1023   //
1024   // Distance
1025   //
1026   return std::fabs((pr-r1)*dz - (pz-z1)*dr)/len;
1027 }
1028 
1029 // ApproxDistInside (static)
1030 //
1031 // Finds the approximate distance of a point inside
1032 // of a hyperbolic surface. The distance
1033 // must be an underestimate. It will also be nice (although
1034 // not necesary) that the estimate is always finite no
1035 // matter how close the point is.
1036 //
1037 // This estimate uses the distance to a line tangent to
1038 // the hyperbolic function. The point of tangent is chosen
1039 // by the z position point
1040 //
1041 // Assumes pr and pz are positive
1042 //
1043 G4double G4Hype::ApproxDistInside( G4double pr, G4double pz,
1044                                    G4double r0, G4double tan2Phi )
1045 {
1046   if (tan2Phi < DBL_MIN) return r0 - pr;
1047 
1048   //
1049   // Corresponding position and normal on hyperbolic
1050   //
1051   G4double rh = std::sqrt( r0*r0 + pz*pz*tan2Phi );
1052   
1053   G4double dr = -rh;
1054   G4double dz = pz*tan2Phi;
1055   G4double len = std::sqrt(dr*dr + dz*dz);
1056   
1057   //
1058   // Answer
1059   //
1060   return std::fabs((pr-rh)*dr)/len;
1061 }
1062 
1063 // GetEntityType
1064 //
1065 G4GeometryType G4Hype::GetEntityType() const
1066 {
1067   return {"G4Hype"};
1068 }
1069 
1070 // Clone
1071 //
1072 G4VSolid* G4Hype::Clone() const
1073 {
1074   return new G4Hype(*this);
1075 }
1076 
1077 
1078 //
1079 // GetCubicVolume
1080 //
1081 G4double G4Hype::GetCubicVolume()
1082 {
1083   if (fCubicVolume == 0.)
1084   {
1085     fCubicVolume = CLHEP::twopi*halfLenZ*
1086       (2.*(outerRadius2 - innerRadius2) + endOuterRadius2 - endInnerRadius2)/3.;
1087   }
1088   return fCubicVolume;
1089 }
1090 
1091 // GetSurfaceArea
1092 //
1093 G4double G4Hype::GetSurfaceArea()
1094 {
1095   if (fSurfaceArea == 0.)
1096   {
1097     G4double h = halfLenZ;
1098     G4double innS = 2.*h*innerRadius;
1099     if (std::abs(endInnerRadius - innerRadius) > kCarTolerance)
1100     {
1101       G4double A  = innerRadius;
1102       G4double AA = innerRadius2;
1103       G4double RR = endInnerRadius2;
1104       G4double CC = AA*h*h/(RR - AA);
1105       G4double K  = std::sqrt(AA + CC)/CC;
1106       G4double Kh = K*h;
1107       innS = A*(h*std::sqrt(1. + Kh*Kh) + std::asinh(Kh)/K);
1108     }
1109     G4double outS = 2.*h*outerRadius;
1110     if (std::abs(endOuterRadius - outerRadius) > kCarTolerance)
1111     {
1112       G4double A  = outerRadius;
1113       G4double AA = outerRadius2;
1114       G4double RR = endOuterRadius2;
1115       G4double CC = AA*h*h/(RR - AA);
1116       G4double K  = std::sqrt(AA + CC)/CC;
1117       G4double Kh = K*h;
1118       outS  = A*(h*std::sqrt(1. + Kh*Kh) + std::asinh(Kh)/K);
1119     }
1120     fSurfaceArea = CLHEP::twopi*(endOuterRadius2 - endInnerRadius2 + innS + outS);
1121   }
1122   return fSurfaceArea;
1123 }
1124 
1125 // Streams object contents to an output stream
1126 //
1127 std::ostream& G4Hype::StreamInfo(std::ostream& os) const
1128 {
1129   G4long oldprc = os.precision(16);
1130   os << "-----------------------------------------------------------\n"
1131      << "    *** Dump for solid - " << GetName() << " ***\n"
1132      << "    ===================================================\n"
1133      << " Solid type: G4Hype\n"
1134      << " Parameters: \n"
1135      << "    half length Z: " << halfLenZ/mm << " mm \n"
1136      << "    inner radius : " << innerRadius/mm << " mm \n"
1137      << "    outer radius : " << outerRadius/mm << " mm \n"
1138      << "    inner stereo angle : " << innerStereo/degree << " degrees \n"
1139      << "    outer stereo angle : " << outerStereo/degree << " degrees \n"
1140      << "-----------------------------------------------------------\n";
1141   os.precision(oldprc);
1142 
1143   return os;
1144 }
1145 
1146 // GetPointOnSurface
1147 //
1148 G4ThreeVector G4Hype::GetPointOnSurface() const
1149 {
1150   G4double xRand, yRand, zRand, r2 , aOne, aTwo, aThree, chose, sinhu;
1151   G4double phi, cosphi, sinphi, rBar2Out, rBar2In, alpha, t, rOut, rIn2, rOut2;
1152 
1153   // we use the formula of the area of a surface of revolution to compute 
1154   // the areas, using the equation of the hyperbola:
1155   // x^2 + y^2 = (z*tanphi)^2 + r^2
1156 
1157   rBar2Out = outerRadius2;
1158   alpha = 2.*pi*rBar2Out*std::cos(outerStereo)/tanOuterStereo;
1159   t     = halfLenZ*tanOuterStereo/(outerRadius*std::cos(outerStereo));
1160   t     = std::log(t+std::sqrt(sqr(t)+1));
1161   aOne  = std::fabs(2.*alpha*(std::sinh(2.*t)/4.+t/2.));
1162 
1163 
1164   rBar2In = innerRadius2;
1165   alpha = 2.*pi*rBar2In*std::cos(innerStereo)/tanInnerStereo;
1166   t     = halfLenZ*tanInnerStereo/(innerRadius*std::cos(innerStereo));
1167   t     = std::log(t+std::sqrt(sqr(t)+1));
1168   aTwo  = std::fabs(2.*alpha*(std::sinh(2.*t)/4.+t/2.));
1169 
1170   aThree = pi*((outerRadius2+sqr(halfLenZ*tanOuterStereo)
1171               -(innerRadius2+sqr(halfLenZ*tanInnerStereo))));
1172   
1173   if(outerStereo == 0.) {aOne = std::fabs(2.*pi*outerRadius*2.*halfLenZ);}
1174   if(innerStereo == 0.) {aTwo = std::fabs(2.*pi*innerRadius*2.*halfLenZ);}
1175   
1176   phi = G4RandFlat::shoot(0.,2.*pi);
1177   cosphi = std::cos(phi);
1178   sinphi = std::sin(phi);
1179   sinhu = G4RandFlat::shoot(-1.*halfLenZ*tanOuterStereo/outerRadius,
1180                           halfLenZ*tanOuterStereo/outerRadius);
1181 
1182   chose = G4RandFlat::shoot(0.,aOne+aTwo+2.*aThree);
1183   if(chose>=0. && chose < aOne)
1184   {
1185     if(outerStereo != 0.)
1186     {
1187       zRand = outerRadius*sinhu/tanOuterStereo;
1188       xRand = std::sqrt(sqr(sinhu)+1)*outerRadius*cosphi;
1189       yRand = std::sqrt(sqr(sinhu)+1)*outerRadius*sinphi;
1190       return { xRand, yRand, zRand };
1191     }
1192     else
1193     {
1194       return { outerRadius*cosphi, outerRadius*sinphi,
1195                G4RandFlat::shoot(-halfLenZ,halfLenZ) };
1196     }
1197   }
1198   else if(chose>=aOne && chose<aOne+aTwo)
1199   {
1200     if(innerStereo != 0.)
1201     {
1202       sinhu = G4RandFlat::shoot(-1.*halfLenZ*tanInnerStereo/innerRadius,
1203                                 halfLenZ*tanInnerStereo/innerRadius);
1204       zRand = innerRadius*sinhu/tanInnerStereo;
1205       xRand = std::sqrt(sqr(sinhu)+1)*innerRadius*cosphi;
1206       yRand = std::sqrt(sqr(sinhu)+1)*innerRadius*sinphi;
1207       return { xRand, yRand, zRand };
1208     }
1209     else 
1210     {
1211       return { innerRadius*cosphi, innerRadius*sinphi,
1212                G4RandFlat::shoot(-1.*halfLenZ,halfLenZ) };
1213     }
1214   }
1215   else if(chose>=aOne+aTwo && chose<aOne+aTwo+aThree)
1216   {
1217     rIn2  = innerRadius2+tanInnerStereo2*halfLenZ*halfLenZ;
1218     rOut2 = outerRadius2+tanOuterStereo2*halfLenZ*halfLenZ;
1219     rOut  = std::sqrt(rOut2) ;
1220  
1221     do    // Loop checking, 13.08.2015, G.Cosmo
1222     {
1223       xRand = G4RandFlat::shoot(-rOut,rOut) ;
1224       yRand = G4RandFlat::shoot(-rOut,rOut) ;
1225       r2 = xRand*xRand + yRand*yRand ;
1226     } while ( r2 < rIn2 || r2 > rOut2 ) ;
1227 
1228     zRand = halfLenZ;
1229     return { xRand, yRand, zRand };
1230   }
1231   else
1232   {
1233     rIn2  = innerRadius2+tanInnerStereo2*halfLenZ*halfLenZ;
1234     rOut2 = outerRadius2+tanOuterStereo2*halfLenZ*halfLenZ;
1235     rOut  = std::sqrt(rOut2) ;
1236  
1237     do    // Loop checking, 13.08.2015, G.Cosmo
1238     {
1239       xRand = G4RandFlat::shoot(-rOut,rOut) ;
1240       yRand = G4RandFlat::shoot(-rOut,rOut) ;
1241       r2 = xRand*xRand + yRand*yRand ;
1242     } while ( r2 < rIn2 || r2 > rOut2 ) ;
1243 
1244     zRand = -1.*halfLenZ;
1245     return { xRand, yRand, zRand };
1246   }
1247 }
1248 
1249 // DescribeYourselfTo
1250 //
1251 void G4Hype::DescribeYourselfTo (G4VGraphicsScene& scene) const 
1252 {
1253   scene.AddSolid (*this);
1254 }
1255 
1256 // GetExtent
1257 //
1258 G4VisExtent G4Hype::GetExtent() const 
1259 {
1260   // Define the sides of the box into which the G4Tubs instance would fit.
1261   //
1262   return { -endOuterRadius, endOuterRadius, 
1263            -endOuterRadius, endOuterRadius, 
1264            -halfLenZ, halfLenZ };
1265 }
1266 
1267 // CreatePolyhedron
1268 //
1269 G4Polyhedron* G4Hype::CreatePolyhedron() const 
1270 {
1271    return new G4PolyhedronHype(innerRadius, outerRadius,
1272                                tanInnerStereo2, tanOuterStereo2, halfLenZ);
1273 }
1274 
1275 // GetPolyhedron
1276 //
1277 G4Polyhedron* G4Hype::GetPolyhedron () const
1278 {
1279   if (fpPolyhedron == nullptr ||
1280       fRebuildPolyhedron ||
1281       fpPolyhedron->GetNumberOfRotationStepsAtTimeOfCreation() !=
1282       fpPolyhedron->GetNumberOfRotationSteps())
1283     {
1284       G4AutoLock l(&polyhedronMutex);
1285       delete fpPolyhedron;
1286       fpPolyhedron = CreatePolyhedron();
1287       fRebuildPolyhedron = false;
1288       l.unlock();
1289     }
1290   return fpPolyhedron;
1291 }
1292 
1293 //  asinh
1294 //
1295 G4double G4Hype::asinh(G4double arg)
1296 {
1297   return std::log(arg+std::sqrt(sqr(arg)+1));
1298 }
1299 
1300 #endif // !defined(G4GEOM_USE_UHYPE) || !defined(G4GEOM_USE_SYS_USOLIDS)
1301