Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/geometry/navigation/src/G4ReplicaNavigation.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 // * License and Disclaimer                                           *
  3 // *                                                                  *
  4 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  5 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  6 // * conditions of the Geant4 Software License,  included in the file *
  7 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  8 // * include a list of copyright holders.                             *
  9 // *                                                                  *
 10 // * Neither the authors of this software system, nor their employing *
 11 // * institutes,nor the agencies providing financial support for this *
 12 // * work  make  any representation or  warranty, express or implied, *
 13 // * regarding  this  software system or assume any liability for its *
 14 // * use.  Please see the license in the file  LICENSE  and URL above *
 15 // * for the full disclaimer and the limitation of liability.         *
 16 // *                                                                  *
 17 // * This  code  implementation is the result of  the  scientific and *
 18 // * technical work of the GEANT4 collaboration.                      *
 19 // * By using,  copying,  modifying or  distributing the software (or *
 20 // * any work based  on the software)  you  agree  to acknowledge its *
 21 // * use  in  resulting  scientific  publications,  and indicate your *
 22 // * acceptance of all terms of the Geant4 Software license.          *
 23 // ********************************************************************
 24 //
 25 // class G4ReplicaNavigation Implementation
 26 //
 27 // Author: P.Kent, 1996
 28 // --------------------------------------------------------------------
 29 
 30 #include "G4ReplicaNavigation.hh"
 31 
 32 #include "G4AffineTransform.hh"
 33 #include "G4SmartVoxelProxy.hh"
 34 #include "G4SmartVoxelNode.hh"
 35 #include "G4VSolid.hh"
 36 #include "G4GeometryTolerance.hh"
 37 
 38 namespace
 39 {
 40   const G4ThreeVector VecCartAxes[3]=
 41   { G4ThreeVector(1.,0.,0.), G4ThreeVector(0.,1.,0.), G4ThreeVector(0.,0.,1.) };
 42   const G4ExitNormal::ESide SideCartAxesPlus[3]=
 43   { G4ExitNormal::kPX, G4ExitNormal::kPY, G4ExitNormal::kPZ };
 44   const G4ExitNormal::ESide SideCartAxesMinus[3]=
 45   { G4ExitNormal::kMX, G4ExitNormal::kMX, G4ExitNormal::kMX };
 46 }
 47 
 48 // ********************************************************************
 49 // Constructor
 50 // ********************************************************************
 51 //
 52 G4ReplicaNavigation::G4ReplicaNavigation()
 53 {
 54   kCarTolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance();
 55   kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
 56   kAngTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance();
 57   halfkCarTolerance = kCarTolerance*0.5;
 58   halfkRadTolerance = kRadTolerance*0.5;
 59   halfkAngTolerance = kAngTolerance*0.5;
 60   fMinStep = 0.05*kCarTolerance;
 61 }
 62 
 63 // ********************************************************************
 64 // Inside
 65 // ********************************************************************
 66 //
 67 EInside
 68 G4ReplicaNavigation::Inside(const G4VPhysicalVolume* pVol,
 69                             const G4int replicaNo,
 70                             const G4ThreeVector& localPoint) const
 71 {
 72   EInside in = kOutside;
 73   
 74   // Replication data
 75   //
 76   EAxis axis;
 77   G4int nReplicas;
 78   G4double width, offset;
 79   G4bool consuming;
 80   
 81   G4double coord, rad2, rmin, tolRMax2, rmax, tolRMin2;
 82 
 83   pVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
 84 
 85   switch (axis)
 86   {
 87     case kXAxis:
 88     case kYAxis:
 89     case kZAxis:
 90       coord = std::fabs(localPoint(axis))-width*0.5;
 91       if ( coord<=-halfkCarTolerance )
 92       {
 93         in = kInside;
 94       }
 95       else if ( coord<=halfkCarTolerance )
 96       {
 97         in = kSurface;
 98       }
 99       break;
100     case kPhi:
101       if ( (localPoint.y() != 0.0)||(localPoint.x() != 0.0) )
102       {
103         coord = std::fabs(std::atan2(localPoint.y(),localPoint.x()))-width*0.5;
104         if ( coord<=-halfkAngTolerance )
105         {
106           in = kInside;
107         }
108         else if ( coord<=halfkAngTolerance )
109         {
110           in = kSurface;
111         }
112       }
113       else
114       {
115         in = kSurface;
116       }
117       break;
118     case kRho:
119       rad2 = localPoint.perp2();
120       rmax = (replicaNo+1)*width+offset;
121       tolRMax2  = rmax-halfkRadTolerance;
122       tolRMax2 *= tolRMax2;
123       if ( rad2>tolRMax2 )
124       {
125         tolRMax2 = rmax+halfkRadTolerance;
126         tolRMax2 *= tolRMax2;
127         if ( rad2<=tolRMax2 )
128         {
129           in = kSurface;
130         }
131       }
132       else
133       {
134         // Known to be inside outer radius
135         //
136         if ( (replicaNo != 0)||(offset != 0.0) )
137         {
138           rmin = rmax-width;
139           tolRMin2 = rmin-halfkRadTolerance;
140           tolRMin2 *= tolRMin2;
141           if ( rad2>tolRMin2 )
142           {
143             tolRMin2 = rmin+halfkRadTolerance;
144             tolRMin2 *= tolRMin2;
145             if ( rad2>=tolRMin2 )
146             {
147               in = kInside;
148             }
149             else
150             {
151               in = kSurface;
152             }
153           }
154         }
155         else
156         {
157           in = kInside;
158         }
159       }
160       break;
161     default:
162       G4Exception("G4ReplicaNavigation::Inside()", "GeomNav0002",
163                   FatalException, "Unknown axis!");
164       break;
165   }
166   return in;
167 }
168 
169 // ********************************************************************
170 // DistanceToOut
171 // ********************************************************************
172 //
173 G4double
174 G4ReplicaNavigation::DistanceToOut(const G4VPhysicalVolume* pVol,
175                                    const G4int replicaNo,
176                                    const G4ThreeVector& localPoint) const
177 {
178   // Replication data
179   //
180   EAxis axis;
181   G4int nReplicas;
182   G4double width,offset;
183   G4bool consuming;
184   
185   G4double safety = 0.;
186   G4double safe1,safe2;
187   G4double coord, rho, rmin, rmax;
188 
189   pVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
190   switch(axis)
191   {
192     case kXAxis:
193     case kYAxis:
194     case kZAxis:
195        coord = localPoint(axis);
196        safe1 = width*0.5-coord;
197        safe2 = width*0.5+coord;
198        safety = (safe1<=safe2) ? safe1 : safe2;
199        break;
200     case kPhi:
201       if ( localPoint.y()<=0 )
202       {
203         safety = localPoint.x()*std::sin(width*0.5)
204                + localPoint.y()*std::cos(width*0.5);
205       }
206       else
207       {
208         safety = localPoint.x()*std::sin(width*0.5)
209                - localPoint.y()*std::cos(width*0.5);
210       }
211       break;
212     case kRho:
213       rho = localPoint.perp();
214       rmax = width*(replicaNo+1)+offset;
215       if ( (replicaNo != 0)||(offset != 0.0) )
216       {
217         rmin  = rmax-width;
218         safe1 = rho-rmin;
219         safe2 = rmax-rho;
220         safety = (safe1<=safe2) ? safe1 : safe2;
221       }
222       else
223       {
224         safety = rmax-rho;
225       }
226       break;
227     default:
228      G4Exception("G4ReplicaNavigation::DistanceToOut()", "GeomNav0002",
229                  FatalException, "Unknown axis!");
230      break;
231   }
232   return (safety >= halfkCarTolerance) ? safety : 0;
233 }
234 
235 // ********************************************************************
236 // DistanceToOut
237 // ********************************************************************
238 //
239 G4double
240 G4ReplicaNavigation::DistanceToOut(const G4VPhysicalVolume* pVol,
241                                    const G4int replicaNo,
242                                    const G4ThreeVector& localPoint,
243                                    const G4ThreeVector& localDirection,
244                                    G4ExitNormal& arExitNormal ) const
245 {
246   // Replication data
247   //
248   EAxis axis;
249   G4int nReplicas;
250   G4double width, offset;
251   G4bool consuming;
252 
253   G4double Dist=kInfinity;
254   G4double coord, Comp, lindist;
255   G4double signC = 0.0;
256   G4ExitNormal candidateNormal; 
257    
258   pVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
259   switch(axis)
260   {
261     case kXAxis:
262     case kYAxis:
263     case kZAxis:
264       coord = localPoint(axis);
265       Comp = localDirection(axis);
266       if ( Comp>0 )
267       {
268         lindist = width*0.5-coord;
269         Dist = (lindist>0) ? lindist/Comp : 0;
270         signC= 1.0;
271       }
272       else if ( Comp<0 )
273       {
274         lindist = width*0.5+coord;
275         Dist = (lindist>0) ? -lindist/Comp : 0;
276         signC= -1.0;
277       }
278       else
279       {
280         Dist = kInfinity;
281       }
282       // signC = sign<G4double>(Comp)
283       candidateNormal.exitNormal = ( signC * VecCartAxes[axis]);
284       candidateNormal.calculated = true;
285       candidateNormal.validConvex = true;
286       candidateNormal.exitSide =
287         (Comp>0) ? SideCartAxesPlus[axis] : SideCartAxesMinus[axis];
288       break;
289     case kPhi:
290       Dist = DistanceToOutPhi(localPoint,localDirection,width,candidateNormal);
291         // candidateNormal set in call
292       break;
293     case kRho:
294       Dist = DistanceToOutRad(localPoint,localDirection,width,offset,
295                               replicaNo,candidateNormal);
296         // candidateNormal set in call
297       break;
298     default:
299      G4Exception("G4ReplicaNavigation::DistanceToOut()", "GeomNav0002",
300                  FatalException, "Unknown axis!");
301      break;
302   }
303 
304   arExitNormal= candidateNormal; // .exitNormal;
305 
306   return Dist;
307 }
308 
309 // ********************************************************************
310 // DistanceToOutPhi
311 // ********************************************************************
312 //
313 G4double
314 G4ReplicaNavigation::DistanceToOutPhi(const G4ThreeVector& localPoint,
315                                       const G4ThreeVector& localDirection,
316                                       const G4double width,
317                                       G4ExitNormal& foundNormal ) const
318 {
319   // Phi Intersection
320   // NOTE: width<=pi by definition
321   //
322   G4double sinSPhi = -2.0, cosSPhi = -2.0;
323   G4double pDistS, pDistE, compS, compE, Dist, dist2, yi;
324   G4ExitNormal::ESide sidePhi = G4ExitNormal::kNull;
325   G4ThreeVector  candidateNormal;
326 
327   if ( (localPoint.x()!=0.0) || (localPoint.y()!=0.0) )
328   {
329     sinSPhi = std::sin(-width*0.5);  // SIN of starting phi plane
330     cosSPhi = std::cos(width*0.5);   // COS of starting phi plane
331 
332     // pDist -ve when inside
333     //
334     pDistS = localPoint.x()*sinSPhi-localPoint.y()*cosSPhi;
335      // Start plane at phi= -S
336     pDistE = localPoint.x()*sinSPhi+localPoint.y()*cosSPhi;
337      // End   plane at phi= +S
338 
339     // Comp -ve when in direction of outwards normal
340     //
341     compS = -sinSPhi*localDirection.x()+cosSPhi*localDirection.y();
342     compE = -sinSPhi*localDirection.x()-cosSPhi*localDirection.y();
343 
344     if ( (pDistS<=halfkCarTolerance)&&(pDistE<=halfkCarTolerance) )
345     {
346       // Inside both phi *full* planes
347       //
348       if ( compS<0 )
349       {
350         dist2 = pDistS/compS;
351         yi = localPoint.y()+dist2*localDirection.y();
352 
353         // Check intersecting with correct half-plane (no -> no intersect)
354         //
355         if ( yi<=0 )
356         {
357           Dist = (pDistS<=-halfkCarTolerance) ? dist2 : 0;
358           sidePhi= G4ExitNormal::kSPhi; // tbc
359         }
360         else
361         {
362           Dist = kInfinity;
363         }
364       }
365       else
366       {
367         Dist = kInfinity;
368       }
369       if ( compE<0 )
370       {
371         dist2 = pDistE/compE;
372         
373         // Only check further if < starting phi intersection
374         //
375         if ( dist2<Dist )
376         {
377           yi = localPoint.y()+dist2*localDirection.y();
378 
379           // Check intersecting with correct half-plane
380           //
381           if ( yi>=0 )
382           {
383             // Leaving via ending phi
384             //
385             Dist = (pDistE<=-halfkCarTolerance) ? dist2 : 0;
386             sidePhi = G4ExitNormal::kEPhi;
387           }
388         }
389       }
390     }
391     else if ( (pDistS>halfkCarTolerance)&&(pDistE>halfkCarTolerance) )
392     {
393       // Outside both *full* phi planes
394       // if towards both >=0 then once inside will remain inside
395       //
396       Dist = ((compS>=0)&&(compE>=0)) ? kInfinity : 0;
397     }
398     else if ( (pDistS>halfkCarTolerance)&&(pDistE<=halfkCarTolerance) )
399     {
400       // Outside full starting plane, inside full ending plane
401       //
402       if ( compE<0 )
403       {      
404         dist2 = pDistE/compE;
405         yi = localPoint.y()+dist2*localDirection.y();
406 
407         // Check intersection in correct half-plane
408         // (if not -> remain in extent)
409         //
410         Dist = (yi>0) ? dist2 : kInfinity;
411         if( yi> 0 ) { sidePhi = G4ExitNormal::kEPhi; }
412       }
413       else  // Leaving immediately by starting phi
414       {
415         Dist = kInfinity;
416       }
417     }
418     else
419     {
420       // Must be (pDistS<=halfkCarTolerance)&&(pDistE>halfkCarTolerance)
421       // Inside full starting plane, outside full ending plane
422       //
423       if ( compE>=0 )
424       {
425         if ( compS<0 )
426         {
427           dist2 = pDistS/compS;
428           yi = localPoint.y()+dist2*localDirection.y();
429 
430           // Check intersection in correct half-plane
431           // (if not -> remain in extent)
432           //
433           Dist = (yi<0) ? dist2 : kInfinity;
434           if(yi<0)  { sidePhi = G4ExitNormal::kSPhi; }
435         }
436         else
437         {
438           Dist = kInfinity;
439         }
440       }
441       else
442       {
443         // Leaving immediately by ending phi
444         //
445         Dist = 0.;
446         sidePhi = G4ExitNormal::kEPhi;
447       }
448     }
449   }
450   else
451   {
452     // On z axis + travel not || to z axis -> use direction vector
453     //
454     if( (std::fabs(localDirection.phi())<=width*0.5) )
455     {
456        Dist = kInfinity;
457     }
458     else
459     {
460        Dist = 0.;
461        sidePhi = G4ExitNormal::kMY;
462     }
463   }
464 
465   if(sidePhi == G4ExitNormal::kSPhi )
466   {
467     candidateNormal = G4ThreeVector(sinSPhi,-cosSPhi,0.) ;
468   }
469   else if (sidePhi == G4ExitNormal::kEPhi)
470   {
471     candidateNormal = G4ThreeVector(sinSPhi,cosSPhi,0.) ;
472   }
473   else if (sidePhi == G4ExitNormal::kMY )
474   {
475     candidateNormal = G4ThreeVector(0., -1.0, 0.); // Split -S and +S 'phi'
476   }
477   foundNormal.calculated= (sidePhi != G4ExitNormal::kNull );
478   foundNormal.exitNormal= candidateNormal;
479    
480   return Dist;
481 }
482 
483 // ********************************************************************
484 // DistanceToOutRad
485 // ********************************************************************
486 //
487 G4double
488 G4ReplicaNavigation::DistanceToOutRad(const G4ThreeVector& localPoint,
489                                       const G4ThreeVector& localDirection,
490                                       const G4double width,
491                                       const G4double offset,
492                                       const G4int replicaNo,
493                                       G4ExitNormal& foundNormal ) const
494 {
495   G4double rmin, rmax, t1, t2, t3, deltaR;
496   G4double b, c, d2, srd;
497   G4ExitNormal::ESide  sideR= G4ExitNormal::kNull;
498 
499   //
500   // Radial Intersections
501   //
502   
503   // Find intersction with cylinders at rmax/rmin
504   // Intersection point (xi,yi,zi) on line
505   // x=localPoint.x+t*localDirection.x etc.
506   //
507   // Intersects with x^2+y^2=R^2
508   //
509   // Hence (localDirection.x^2+localDirection.y^2)t^2+
510   //     2t(localPoint.x*localDirection.x+localPoint.y*localDirection.y)+
511   //        localPoint.x^2+localPoint.y^2-R^2=0
512   //
513   //            t1                t2                    t3
514 
515   rmin = replicaNo*width+offset;
516   rmax = (replicaNo+1)*width+offset;
517 
518   t1 = 1.0-localDirection.z()*localDirection.z();   // since v normalised
519   t2 = localPoint.x()*localDirection.x()+localPoint.y()*localDirection.y();
520   t3 = localPoint.x()*localPoint.x()+localPoint.y()*localPoint.y();
521   
522   if ( t1>0 )        // Check not parallel
523   {
524     // Calculate srd, r exit distance
525     //
526     if ( t2>=0 )
527     {
528       // Delta r not negative => leaving via rmax
529       //
530       deltaR = t3-rmax*rmax;
531     
532       // NOTE: Should use
533       // rho-rmax<-halfkRadTolerance - [no sqrts for efficiency]
534       //
535       if ( deltaR<-halfkRadTolerance )
536       {
537         b  = t2/t1;
538         c  = deltaR/t1;
539         srd = -b+std::sqrt(b*b-c);
540         sideR = G4ExitNormal::kRMax;
541       }
542       else
543       {
544         // On tolerant boundary & heading outwards (or locally
545         // perpendicular to) outer radial surface -> leaving immediately
546         //
547         srd = 0;
548         sideR = G4ExitNormal::kRMax;
549       }
550     }
551     else
552     {
553       // Possible rmin intersection
554       //
555       if (rmin != 0.0)
556       {
557         deltaR = t3-rmin*rmin;
558         b  = t2/t1;
559         c  = deltaR/t1;
560         d2 = b*b-c;
561         if ( d2>=0 )
562         {
563           // Leaving via rmin
564           // NOTE: Should use
565           // rho-rmin>halfkRadTolerance - [no sqrts for efficiency]
566           //
567           srd = (deltaR>halfkRadTolerance) ? -b-std::sqrt(d2) : 0.0;
568           // Is the following more accurate ?
569           // srd = (deltaR>halfkRadTolerance) ? c/( -b - std::sqrt(d2)) : 0.0;
570           sideR = G4ExitNormal::kRMin;
571         }
572         else
573         {
574           // No rmin intersect -> must be rmax intersect
575           //
576           deltaR = t3-rmax*rmax;
577           c  = deltaR/t1;
578           d2 = b*b-c;
579           srd = (d2 < 0.) ? 0.0 : -b+std::sqrt(d2);
580           sideR = G4ExitNormal::kRMax;
581         }
582       }
583       else
584       {
585         // No rmin intersect -> must be rmax intersect
586         //
587         deltaR = t3-rmax*rmax;
588         b  = t2/t1;
589         c  = deltaR/t1;
590         d2 = b*b-c;
591         srd = (d2 < 0.) ? 0.0 : -b+std::sqrt(d2);
592         sideR= G4ExitNormal::kRMax;
593       }
594     }
595   }
596   else
597   {
598     srd =kInfinity;
599     sideR = G4ExitNormal::kNull;
600   }
601    
602   if( sideR != G4ExitNormal::kNull ) // if ((side == kRMax) || (side==kRMin))
603   {
604     // Note: returned vector not explicitly normalised
605     // (divided by fRMax for unit vector)
606 
607     G4double xi, yi;
608     xi = localPoint.x() + srd*localDirection.x();
609     yi = localPoint.y() + srd*localDirection.y();
610     G4ThreeVector normalR = G4ThreeVector(xi,yi,0.0);
611      
612     if( sideR == G4ExitNormal::kRMax )
613     {
614       normalR *= 1.0/rmax;
615     }
616     else
617     {
618       normalR *= (-1.0)/rmin;
619     }
620     foundNormal.exitNormal= normalR;
621     foundNormal.calculated= true;
622     foundNormal.validConvex = (sideR == G4ExitNormal::kRMax);
623     foundNormal.exitSide = sideR;
624   }
625   else
626   {
627     foundNormal.calculated = false;
628   }
629    
630   return srd;
631 }
632 
633 // ********************************************************************
634 // ComputeTransformation
635 //
636 // Setup transformation and transform point into local system
637 // ********************************************************************
638 //
639 void
640 G4ReplicaNavigation::ComputeTransformation(const G4int replicaNo,
641                                                  G4VPhysicalVolume* pVol,
642                                                  G4ThreeVector& point) const
643 {
644   G4double val,cosv,sinv,tmpx,tmpy;
645 
646   // Replication data
647   //
648   EAxis axis;
649   G4int nReplicas;
650   G4double width,offset;
651   G4bool consuming;
652 
653   pVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
654 
655   switch (axis)
656   {
657     case kXAxis:
658       val = -width*0.5*(nReplicas-1)+width*replicaNo;
659       pVol->SetTranslation(G4ThreeVector(val,0,0));
660       point.setX(point.x()-val);
661       break;
662     case kYAxis:
663       val = -width*0.5*(nReplicas-1)+width*replicaNo;
664       pVol->SetTranslation(G4ThreeVector(0,val,0));
665       point.setY(point.y()-val);
666       break;
667     case kZAxis:
668       val = -width*0.5*(nReplicas-1)+width*replicaNo;
669       pVol->SetTranslation(G4ThreeVector(0,0,val));
670       point.setZ(point.z()-val);
671       break;
672     case kPhi:
673       val = -(offset+width*(replicaNo+0.5));
674       SetPhiTransformation(val,pVol);
675       cosv = std::cos(val);
676       sinv = std::sin(val);
677       tmpx = point.x()*cosv-point.y()*sinv;
678       tmpy = point.x()*sinv+point.y()*cosv;
679       point.setY(tmpy);
680       point.setX(tmpx);
681       break;
682     case kRho:
683       // No setup required for radial case
684     default:
685       break;
686   }
687 }
688 
689 // ********************************************************************
690 // ComputeTransformation
691 //
692 // Setup transformation into local system
693 // ********************************************************************
694 //
695 void
696 G4ReplicaNavigation::ComputeTransformation(const G4int replicaNo,
697                                                  G4VPhysicalVolume* pVol) const
698 {
699   G4double val;
700 
701   // Replication data
702   //
703   EAxis axis;
704   G4int nReplicas;
705   G4double width, offset;
706   G4bool consuming;
707 
708   pVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
709 
710   switch (axis)
711   {
712     case kXAxis:
713       val = -width*0.5*(nReplicas-1)+width*replicaNo;
714       pVol->SetTranslation(G4ThreeVector(val,0,0));
715       break;
716     case kYAxis:
717       val = -width*0.5*(nReplicas-1)+width*replicaNo;
718       pVol->SetTranslation(G4ThreeVector(0,val,0));
719       break;
720     case kZAxis:
721       val = -width*0.5*(nReplicas-1)+width*replicaNo;
722       pVol->SetTranslation(G4ThreeVector(0,0,val));
723       break;
724     case kPhi:
725       val = -(offset+width*(replicaNo+0.5));
726       SetPhiTransformation(val,pVol);
727       break;
728     case kRho:
729       // No setup required for radial case
730     default:
731       break;
732   }
733 }
734 
735 // ********************************************************************
736 // ComputeStep
737 // ********************************************************************
738 //
739 G4double
740 G4ReplicaNavigation::ComputeStep(const G4ThreeVector& globalPoint,
741                                  const G4ThreeVector& globalDirection,
742                                  const G4ThreeVector& localPoint,
743                                  const G4ThreeVector& localDirection,
744                                  const G4double currentProposedStepLength,
745                                        G4double& newSafety,
746                                        G4NavigationHistory &history,
747                                  // std::pair<G4bool,G4bool>& validAndCalculated
748                                        G4bool& validExitNormal,
749                                        G4bool& calculatedExitNormal, 
750                                        G4ThreeVector& exitNormalVector,
751                                        G4bool& exiting,
752                                        G4bool& entering,
753                                        G4VPhysicalVolume* (*pBlockedPhysical),
754                                        G4int& blockedReplicaNo )
755 {
756   G4VPhysicalVolume *repPhysical, *motherPhysical;
757   G4VPhysicalVolume *samplePhysical, *blockedExitedVol = nullptr;
758   G4LogicalVolume *repLogical;
759   G4VSolid *motherSolid;
760   G4ThreeVector repPoint, repDirection, sampleDirection;
761   G4double ourStep=currentProposedStepLength;
762   G4double ourSafety=kInfinity;
763   G4double sampleStep, sampleSafety, motherStep, motherSafety;
764   G4long localNoDaughters, sampleNo;
765   G4int depth;
766   G4ExitNormal exitNormalStc;
767   // G4int depthDeterminingStep= -1; // Useful only for debugging - for now
768 
769   calculatedExitNormal= false;
770   
771   // Exiting normal optimisation
772   //
773   if ( exiting&&validExitNormal )
774   {
775     if ( localDirection.dot(exitNormalVector)>=kMinExitingNormalCosine )
776     {
777       // Block exited daughter volume
778       //
779       blockedExitedVol = *pBlockedPhysical;
780       ourSafety = 0;
781     }
782   }
783   exiting  = false;
784   entering = false;
785 
786   repPhysical = history.GetTopVolume();
787   repLogical  = repPhysical->GetLogicalVolume();
788 
789   //
790   // Compute intersection with replica boundaries & replica safety
791   //
792 
793   sampleSafety = DistanceToOut(repPhysical,
794                                history.GetTopReplicaNo(),
795                                localPoint);
796   G4ExitNormal normalOutStc;
797   const auto  topDepth= (G4int)history.GetDepth();
798 
799   ourSafety = std::min( ourSafety, sampleSafety);
800 
801   if ( sampleSafety<ourStep )
802   {
803 
804     sampleStep = DistanceToOut(repPhysical,
805                                history.GetTopReplicaNo(),
806                                localPoint,
807                                localDirection,
808                                normalOutStc);
809     if ( sampleStep<ourStep )
810     {
811       ourStep = sampleStep;
812       exiting = true;
813       validExitNormal = normalOutStc.validConvex; // false; -> Old,Conservative
814 
815       exitNormalStc = normalOutStc;
816       exitNormalStc.exitNormal =
817         history.GetTopTransform().InverseTransformAxis(normalOutStc.exitNormal);
818       calculatedExitNormal = true;
819     }
820   }
821   const G4int secondDepth = topDepth;
822   depth = secondDepth;  
823 
824   // Loop checking, 07.10.2016, JA -- Need to add: assert(depth>0)
825   while ( history.GetVolumeType(depth)==kReplica )  
826   {
827     const G4AffineTransform& GlobalToLocal = history.GetTransform(depth);
828     repPoint = GlobalToLocal.TransformPoint(globalPoint);
829     // repPoint = history.GetTransform(depth).TransformPoint(globalPoint);
830  
831     sampleSafety = DistanceToOut(history.GetVolume(depth),
832                                  history.GetReplicaNo(depth),
833                                  repPoint);
834     if ( sampleSafety < ourSafety )
835     {
836       ourSafety = sampleSafety;
837     }
838     if ( sampleSafety < ourStep )
839     {
840       G4ThreeVector newLocalDirection =
841           GlobalToLocal.TransformAxis(globalDirection);
842       sampleStep = DistanceToOut(history.GetVolume(depth),
843                                  history.GetReplicaNo(depth),
844                                  repPoint,
845                                  newLocalDirection,
846                                  normalOutStc);
847       if ( sampleStep < ourStep )
848       {
849         ourStep = sampleStep;
850         exiting = true;
851        
852         // As step is limited by this level, must set Exit Normal
853         //
854         G4ThreeVector localExitNorm = normalOutStc.exitNormal;
855         G4ThreeVector globalExitNorm =
856           GlobalToLocal.InverseTransformAxis(localExitNorm);
857 
858         exitNormalStc = normalOutStc; // Normal, convex, calculated, side
859         exitNormalStc.exitNormal = globalExitNorm;
860         calculatedExitNormal = true;
861       }
862     }
863     depth--;
864   }
865  
866   // Compute mother safety & intersection
867   //
868   G4ThreeVector exitVectorMother;
869   G4bool exitConvex = false; // Value obtained in DistanceToOut(p,v) call
870   G4ExitNormal motherNormalStc;
871 
872   repPoint = history.GetTransform(depth).TransformPoint(globalPoint);
873   motherPhysical = history.GetVolume(depth);
874   motherSolid = motherPhysical->GetLogicalVolume()->GetSolid();
875   motherSafety = motherSolid->DistanceToOut(repPoint);
876   repDirection = history.GetTransform(depth).TransformAxis(globalDirection);
877 
878   motherStep = motherSolid->DistanceToOut(repPoint,repDirection,true,
879                                           &exitConvex,&exitVectorMother);
880   if( exitConvex )
881   {
882      motherNormalStc = G4ExitNormal( exitVectorMother, true, false,
883                                      G4ExitNormal::kMother);
884      calculatedExitNormal = true;
885   }
886   const G4AffineTransform& globalToLocalTop = history.GetTopTransform();
887 
888   G4bool motherDeterminedStep = (motherStep<ourStep);
889 
890   if( (!exitConvex) && motherDeterminedStep )
891   {
892      exitVectorMother = motherSolid->SurfaceNormal( repPoint );
893      motherNormalStc = G4ExitNormal( exitVectorMother, true, false,
894                                      G4ExitNormal::kMother);
895      // CalculatedExitNormal -> true;
896      // Convex               -> false: do not know value
897      // ExitSide             -> kMother (or kNull)
898  
899      calculatedExitNormal = true;
900   }
901   if( motherDeterminedStep )
902   {
903      G4ThreeVector globalExitNormalTop =
904        globalToLocalTop.InverseTransformAxis(exitVectorMother);
905      
906      exitNormalStc = motherNormalStc;
907      exitNormalStc.exitNormal = globalExitNormalTop;
908   }
909 
910   // Push in principle no longer necessary. G4Navigator now takes care of ...
911   // Removing this however may cause additional almost-zero steps and generate
912   // warnings for pushed particles from G4Navigator, particularly for the case
913   // of 3D replicas (Cartesian or combined Radial/Phi cases).
914   // Requires further investigation and eventually reimplementation of
915   // LevelLocate() to take into account point and direction ...
916   //
917   if( ourStep<fMinStep )
918   {
919     ourStep = 2*kCarTolerance;
920   }
921 
922   if ( motherSafety<ourSafety )
923   {
924     ourSafety = motherSafety;
925   }
926 
927 #ifdef G4VERBOSE
928   if ( fCheck )
929   {
930     if( motherSolid->Inside(localPoint)==kOutside )
931     {
932       std::ostringstream message;
933       message << "Point outside volume !" << G4endl
934               << "          Point " << localPoint
935               << " is outside current volume " << motherPhysical->GetName()
936               << G4endl;
937       G4double estDistToSolid= motherSolid->DistanceToIn(localPoint); 
938       message << "          Estimated isotropic distance to solid (distToIn)= " 
939               << estDistToSolid << G4endl;
940       if( estDistToSolid > 100.0 * kCarTolerance )
941       {
942         motherSolid->DumpInfo();
943         G4Exception("G4ReplicaNavigation::ComputeStep()",
944                     "GeomNav0003", FatalException, message,
945                     "Point is far outside Current Volume !" ); 
946       }
947       else
948       {
949         G4Exception("G4ReplicaNavigation::ComputeStep()",
950                     "GeomNav1002", JustWarning, message,
951                     "Point is a little outside Current Volume."); 
952       }
953     }
954   }
955 #endif
956 
957   // Comparison of steps may need precision protection
958   //
959 #if 1
960   if( motherDeterminedStep )
961   {
962     ourStep = motherStep;
963     exiting = true;
964   }
965 
966   // Transform it to the Grand-Mother Reference Frame (current convention)
967   //
968   if ( calculatedExitNormal )
969   {
970     if ( motherDeterminedStep )
971     {
972       exitNormalVector = motherNormalStc.exitNormal;
973     }
974     else
975     {
976       G4ThreeVector exitNormalGlobal = exitNormalStc.exitNormal;
977       exitNormalVector = globalToLocalTop.TransformAxis(exitNormalGlobal);
978       // exitNormalVector= globalToLocal2nd.TransformAxis(exitNormalGlobal);
979       // Alt Make it in one go to Grand-Mother, avoiding transform below
980     }
981     // Transform to Grand-mother reference frame
982     const G4RotationMatrix* rot = motherPhysical->GetRotation();
983     if ( rot != nullptr )
984     {
985       exitNormalVector *= rot->inverse();
986     }
987 
988   }
989   else
990   {
991     validExitNormal = false;
992   }
993 
994 #else
995   if ( motherSafety<=ourStep )
996   {
997     if ( motherStep<=ourStep )
998     {
999       ourStep = motherStep;
1000       exiting = true;
1001       if ( validExitNormal )
1002       {
1003         const G4RotationMatrix* rot = motherPhysical->GetRotation();
1004         if ( rot )
1005         {
1006           exitNormal *= rot->inverse();
1007         }
1008       }
1009     }
1010     else
1011     {
1012       validExitNormal = false;
1013       // calculatedExitNormal= false;
1014     }
1015   }
1016 #endif
1017 
1018 
1019   G4bool daughterDeterminedStep = false;
1020   G4ThreeVector daughtNormRepCrd;
1021      // Exit normal of daughter transformed to
1022      // the coordinate system of Replica (i.e. last depth)
1023 
1024   //
1025   // Compute daughter safeties & intersections
1026   //
1027   localNoDaughters = repLogical->GetNoDaughters();
1028   for ( sampleNo=localNoDaughters-1; sampleNo>=0; sampleNo-- )
1029   {
1030     samplePhysical = repLogical->GetDaughter((G4int)sampleNo);
1031     if ( samplePhysical!=blockedExitedVol )
1032     {
1033       G4ThreeVector localExitNorm;
1034       G4ThreeVector normReplicaCoord;
1035 
1036       G4AffineTransform sampleTf(samplePhysical->GetRotation(),
1037                                  samplePhysical->GetTranslation());
1038       sampleTf.Invert();
1039       const G4ThreeVector samplePoint =
1040                         sampleTf.TransformPoint(localPoint);
1041       const G4VSolid* sampleSolid =
1042                         samplePhysical->GetLogicalVolume()->GetSolid();
1043       const G4double sampleSafetyDistance =
1044                         sampleSolid->DistanceToIn(samplePoint);
1045       if ( sampleSafetyDistance<ourSafety )
1046       {
1047         ourSafety = sampleSafetyDistance;
1048       }
1049       if ( sampleSafetyDistance<=ourStep )
1050       {
1051         sampleDirection = sampleTf.TransformAxis(localDirection);
1052         const G4double sampleStepDistance =
1053                         sampleSolid->DistanceToIn(samplePoint,sampleDirection);
1054         if ( sampleStepDistance<=ourStep )
1055         {
1056           daughterDeterminedStep = true;
1057 
1058           ourStep  = sampleStepDistance;
1059           entering = true;
1060           exiting  = false;
1061           *pBlockedPhysical = samplePhysical;
1062           blockedReplicaNo  = (G4int)sampleNo;
1063 
1064 #ifdef DAUGHTER_NORMAL_ALSO
1065           // This norm can be calculated later, if needed daughter is available
1066           localExitNorm = sampleSolid->SurfaceNormal(samplePoint);
1067           daughtNormRepCrd = sampleTf.InverseTransformAxis(localExitNorm);
1068 #endif
1069           
1070 #ifdef G4VERBOSE
1071           // Check to see that the resulting point is indeed in/on volume.
1072           // This check could eventually be made only for successful candidate.
1073 
1074           if ( ( fCheck ) && ( sampleStepDistance < kInfinity ) )
1075           {
1076             G4ThreeVector intersectionPoint;
1077             intersectionPoint = samplePoint
1078                               + sampleStepDistance * sampleDirection;
1079             EInside insideIntPt = sampleSolid->Inside(intersectionPoint); 
1080             if ( insideIntPt != kSurface )
1081             {
1082               G4long oldcoutPrec = G4cout.precision(16); 
1083               std::ostringstream message;
1084               message << "Navigator gets conflicting response from Solid."
1085                       << G4endl
1086                       << "          Inaccurate DistanceToIn for solid "
1087                       << sampleSolid->GetName() << G4endl
1088                       << "          Solid gave DistanceToIn = "
1089                       << sampleStepDistance << " yet returns " ;
1090               if ( insideIntPt == kInside ) {
1091                 message << "-kInside-"; 
1092               } else if ( insideIntPt == kOutside ) {
1093                 message << "-kOutside-";
1094               } else {
1095                 message << "-kSurface-"; 
1096               }
1097               message << " for this point !" << G4endl
1098                       << "          Point = " << intersectionPoint << G4endl;
1099               if ( insideIntPt != kInside ) {
1100                 message << "        DistanceToIn(p) = " 
1101                        << sampleSolid->DistanceToIn(intersectionPoint)
1102                        << G4endl;
1103 }
1104               if ( insideIntPt != kOutside ) { 
1105                 message << "        DistanceToOut(p) = " 
1106                        << sampleSolid->DistanceToOut(intersectionPoint);
1107 }
1108               G4Exception("G4ReplicaNavigation::ComputeStep()", 
1109                           "GeomNav1002", JustWarning, message); 
1110               G4cout.precision(oldcoutPrec);
1111             }
1112           }
1113 #endif
1114         }
1115       }
1116     }
1117   }
1118 
1119   calculatedExitNormal &= (!daughterDeterminedStep);
1120 
1121 #ifdef DAUGHTER_NORMAL_ALSO
1122   if( daughterDeterminedStep )
1123   {
1124     // G4ThreeVector daughtNormGlobal =
1125     //   GlobalToLastDepth.Inverse().TransformAxis(daughtNormRepCrd);
1126     // ==> Can calculate it, but have no way to transmit it to caller (for now)
1127 
1128     exitNormalVector = globalToLocalTop.InverseTransformAxis(daughtNormGlobal);
1129     validExitNormal = false; // Entering daughter - never convex for parent
1130 
1131     calculatedExitNormal = true;
1132   }
1133   // calculatedExitNormal= true;  // Force it to true -- dubious
1134 #endif
1135 
1136   newSafety = ourSafety;
1137   return ourStep;
1138 }
1139 
1140 // ********************************************************************
1141 // ComputeSafety
1142 //
1143 // Compute the isotropic distance to current volume's boundaries
1144 // and to daughter volumes.
1145 // ********************************************************************
1146 //
1147 G4double
1148 G4ReplicaNavigation::ComputeSafety(const G4ThreeVector& globalPoint,
1149                                    const G4ThreeVector& localPoint,
1150                                    const G4NavigationHistory& history,
1151                                    const G4double ) const
1152 {
1153   G4VPhysicalVolume *repPhysical, *motherPhysical;
1154   G4VPhysicalVolume *samplePhysical, *blockedExitedVol = nullptr;
1155   G4LogicalVolume *repLogical;
1156   G4VSolid *motherSolid;
1157   G4ThreeVector repPoint;
1158   G4double ourSafety = kInfinity;
1159   G4double sampleSafety;
1160   G4long localNoDaughters, sampleNo;
1161   G4int depth;
1162 
1163   repPhysical = history.GetTopVolume();
1164   repLogical  = repPhysical->GetLogicalVolume();
1165 
1166   //
1167   // Compute intersection with replica boundaries & replica safety
1168   //
1169 
1170   sampleSafety = DistanceToOut(history.GetTopVolume(),
1171                                history.GetTopReplicaNo(),
1172                                localPoint);
1173   if ( sampleSafety<ourSafety )
1174   {
1175     ourSafety = sampleSafety;
1176   }
1177 
1178   depth = (G4int)history.GetDepth()-1;
1179 
1180   // Loop checking, 07.10.2016, JA -- need to add: assert(depth>0)
1181   while ( history.GetVolumeType(depth)==kReplica )
1182   {      
1183     repPoint = history.GetTransform(depth).TransformPoint(globalPoint);
1184     sampleSafety = DistanceToOut(history.GetVolume(depth),
1185                                  history.GetReplicaNo(depth),
1186                                  repPoint);
1187     if ( sampleSafety<ourSafety )
1188     {
1189       ourSafety = sampleSafety;
1190     }
1191     depth--;
1192   }
1193 
1194   // Compute mother safety & intersection
1195   //
1196   repPoint = history.GetTransform(depth).TransformPoint(globalPoint);
1197   motherPhysical = history.GetVolume(depth);
1198   motherSolid = motherPhysical->GetLogicalVolume()->GetSolid();
1199   sampleSafety = motherSolid->DistanceToOut(repPoint);
1200 
1201   if ( sampleSafety<ourSafety )
1202   {
1203     ourSafety = sampleSafety;
1204   }
1205 
1206   // Compute daughter safeties & intersections
1207   //
1208   localNoDaughters = repLogical->GetNoDaughters();
1209   for ( sampleNo=localNoDaughters-1; sampleNo>=0; sampleNo-- )
1210   {
1211     samplePhysical = repLogical->GetDaughter((G4int)sampleNo);
1212     if ( samplePhysical!=blockedExitedVol )
1213     {
1214       G4AffineTransform sampleTf(samplePhysical->GetRotation(),
1215                                  samplePhysical->GetTranslation());
1216       sampleTf.Invert();
1217       const G4ThreeVector samplePoint =
1218                             sampleTf.TransformPoint(localPoint);
1219       const G4VSolid *sampleSolid =
1220                             samplePhysical->GetLogicalVolume()->GetSolid();
1221       const G4double sampleSafetyDistance =
1222                             sampleSolid->DistanceToIn(samplePoint);
1223       if ( sampleSafetyDistance<ourSafety )
1224       {
1225         ourSafety = sampleSafetyDistance;
1226       }
1227     }
1228   }
1229   return ourSafety;
1230 }
1231 
1232 // ********************************************************************
1233 // BackLocate
1234 // ********************************************************************
1235 //
1236 EInside
1237 G4ReplicaNavigation::BackLocate(G4NavigationHistory& history,
1238                           const G4ThreeVector& globalPoint,
1239                                 G4ThreeVector& localPoint,
1240                           const G4bool& exiting,
1241                                 G4bool& notKnownInside ) const
1242 {
1243   G4VPhysicalVolume *pNRMother = nullptr;
1244   G4VSolid *motherSolid;
1245   G4ThreeVector repPoint, goodPoint;
1246   G4int mdepth, depth, cdepth;
1247   EInside insideCode;
1248 
1249   cdepth = (G4int)history.GetDepth();
1250   
1251   // Find non replicated mother
1252   //
1253   for ( mdepth=cdepth-1; mdepth>=0; mdepth-- )
1254   {
1255     if ( history.GetVolumeType(mdepth)!=kReplica )
1256     {
1257       pNRMother = history.GetVolume(mdepth);
1258       break;
1259     }
1260   }
1261 
1262   if( pNRMother == nullptr ) 
1263   {
1264     // All the tree of mother volumes were Replicas. 
1265     // This is an error, as the World volume must be a Placement
1266     //
1267     G4Exception("G4ReplicaNavigation::BackLocate()", "GeomNav0002",
1268                 FatalException, "The World volume must be a Placement!");
1269     return kInside;
1270   }
1271 
1272   motherSolid = pNRMother->GetLogicalVolume()->GetSolid();
1273   goodPoint = history.GetTransform(mdepth).TransformPoint(globalPoint);
1274   insideCode = motherSolid->Inside(goodPoint);
1275   if ( (insideCode==kOutside)||((insideCode==kSurface)&&exiting) )
1276   {
1277     // Outside mother -> back up to mother level
1278     // Locate.. in Navigator will back up one more level
1279     // localPoint not required
1280     //
1281     history.BackLevel(cdepth-mdepth);
1282     //      localPoint = goodPoint;
1283   }
1284   else
1285   {
1286     notKnownInside = false;
1287 
1288     // Still within replications
1289     // Check down: if on outside stop at this level
1290     //
1291     for ( depth=mdepth+1; depth<cdepth; ++depth)
1292     {
1293       repPoint = history.GetTransform(depth).TransformPoint(globalPoint);
1294       insideCode = Inside(history.GetVolume(depth),
1295                           history.GetReplicaNo(depth),
1296                           repPoint);
1297       if ( (insideCode==kOutside)||((insideCode==kSurface)&&exiting) )
1298       {
1299         localPoint = goodPoint;
1300         history.BackLevel(cdepth-depth);
1301         return insideCode;
1302       }
1303       goodPoint = repPoint;
1304     }
1305     localPoint = history.GetTransform(depth).TransformPoint(globalPoint);
1306     insideCode = Inside(history.GetVolume(depth),
1307                         history.GetReplicaNo(depth),
1308                         localPoint);
1309     // If outside level, set localPoint = coordinates in reference system
1310     // of *previous* level - location code in navigator will back up one
1311     // level [And also manage blocking]
1312     //
1313     if ( (insideCode==kOutside)||((insideCode==kSurface)&&exiting) )
1314     {
1315       localPoint = goodPoint;
1316     }
1317   }
1318   return insideCode;
1319 }
1320