// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // // // $Id: G4Torus.cc,v 1.63 2007/10/02 09:34:17 gcosmo Exp $ // GEANT4 tag $Name: geant4-09-02-ref-02 $ // // // class G4Torus // // Implementation // // 02.10.07 T.Nikitina: Bug fixed in SolveNumericJT(), b.969:segmentation fault. // rootsrefined is used only if the number of refined roots // is the same as for primary roots. // 02.10.07 T.Nikitina: Bug fixed in CalculateExtent() for case of non-rotated // full-phi torus:protect against negative value for sqrt, // correct formula for delta. // 20.11.05 V.Grichine: Bug fixed in Inside(p) for phi sections, b.810 // 25.08.05 O.Link: new methods for DistanceToIn/Out using JTPolynomialSolver // 07.06.05 V.Grichine: SurfaceNormal(p) for rho=0, Constructor as G4Cons // 03.05.05 V.Grichine: SurfaceNormal(p) according to J. Apostolakis proposal // 18.03.04 V.Grichine: bug fixed in DistanceToIn(p) // 11.01.01 E.Medernach: Use G4PolynomialSolver to find roots // 03.10.00 E.Medernach: SafeNewton added // 31.08.00 E.Medernach: numerical computation of roots wuth bounding // volume technique // 26.05.00 V.Grichine: new fuctions developed by O.Cremonesi were added // 06.03.00 V.Grichine: modifications in Distance ToOut(p,v,...) // 19.11.99 V.Grichine: side = kNull in Distance ToOut(p,v,...) // 09.10.98 V.Grichine: modifications in Distance ToOut(p,v,...) // 30.10.96 V.Grichine: first implementation with G4Tubs elements in Fs // #include "G4Torus.hh" #include "G4VoxelLimits.hh" #include "G4AffineTransform.hh" #include "G4GeometryTolerance.hh" #include "G4JTPolynomialSolver.hh" #include "G4VPVParameterisation.hh" #include "meshdefs.hh" #include "Randomize.hh" #include "G4VGraphicsScene.hh" #include "G4Polyhedron.hh" #include "G4NURBS.hh" #include "G4NURBStube.hh" #include "G4NURBScylinder.hh" #include "G4NURBStubesector.hh" using namespace CLHEP; /////////////////////////////////////////////////////////////// // // Constructor - check parameters, convert angles so 02PI then reset to 2PI G4Torus::G4Torus( const G4String &pName, G4double pRmin, G4double pRmax, G4double pRtor, G4double pSPhi, G4double pDPhi) : G4CSGSolid(pName) { SetAllParameters(pRmin, pRmax, pRtor, pSPhi, pDPhi); } //////////////////////////////////////////////////////////////////////////// // // void G4Torus::SetAllParameters( G4double pRmin, G4double pRmax, G4double pRtor, G4double pSPhi, G4double pDPhi ) { fCubicVolume = 0.; fSurfaceArea = 0.; fpPolyhedron = 0; kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance(); kAngTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance(); if ( pRtor >= pRmax+1.e3*kCarTolerance ) // Check swept radius, as in G4Cons { fRtor = pRtor ; } else { G4cerr << "ERROR - G4Torus()::SetAllParameters(): " << GetName() << G4endl << " Invalid swept radius !" << G4endl << "pRtor = " << pRtor << ", pRmax = " << pRmax << G4endl; G4Exception("G4Torus::SetAllParameters()", "InvalidSetup", FatalException, "Invalid swept radius."); } // Check radii, as in G4Cons // if ( pRmin < pRmax - 1.e2*kCarTolerance && pRmin >= 0 ) { if (pRmin >= 1.e2*kCarTolerance) { fRmin = pRmin ; } else { fRmin = 0.0 ; } fRmax = pRmax ; } else { G4cerr << "ERROR - G4Torus()::SetAllParameters(): " << GetName() << G4endl << " Invalid values for radii !" << G4endl << " pRmin = " << pRmin << ", pRmax = " << pRmax << G4endl; G4Exception("G4Torus::SetAllParameters()", "InvalidSetup", FatalException, "Invalid radii."); } // Check angles // if ( pDPhi >= twopi ) { fDPhi = twopi ; } else { if (pDPhi > 0) { fDPhi = pDPhi ; } else { G4cerr << "ERROR - G4Torus::SetAllParameters(): " << GetName() << G4endl << " Negative Z delta-Phi ! - " << pDPhi << G4endl; G4Exception("G4Torus::SetAllParameters()", "InvalidSetup", FatalException, "Invalid dphi."); } } // Ensure psphi in 0-2PI or -2PI-0 range if shape crosses 0 // fSPhi = pSPhi; if (fSPhi < 0) { fSPhi = twopi-std::fmod(std::fabs(fSPhi),twopi) ; } else { fSPhi = std::fmod(fSPhi,twopi) ; } if (fSPhi+fDPhi > twopi) { fSPhi-=twopi ; } } /////////////////////////////////////////////////////////////////////// // // Fake default constructor - sets only member data and allocates memory // for usage restricted to object persistency. // G4Torus::G4Torus( __void__& a ) : G4CSGSolid(a) { } ////////////////////////////////////////////////////////////////////// // // Destructor G4Torus::~G4Torus() {} ////////////////////////////////////////////////////////////////////// // // Dispatch to parameterisation for replication mechanism dimension // computation & modification. void G4Torus::ComputeDimensions( G4VPVParameterisation* p, const G4int n, const G4VPhysicalVolume* pRep ) { p->ComputeDimensions(*this,n,pRep); } //////////////////////////////////////////////////////////////////////////////// // // Calculate the real roots to torus surface. // Returns negative solutions as well. std::vector G4Torus::TorusRootsJT( const G4ThreeVector& p, const G4ThreeVector& v, G4double r ) const { G4int i, num ; G4double c[5], sr[4], si[4] ; std::vector roots ; G4double Rtor2 = fRtor*fRtor, r2 = r*r ; G4double pDotV = p.x()*v.x() + p.y()*v.y() + p.z()*v.z() ; G4double pRad2 = p.x()*p.x() + p.y()*p.y() + p.z()*p.z() ; c[0] = 1.0 ; c[1] = 4*pDotV ; c[2] = 2*(pRad2 + 2*pDotV*pDotV - Rtor2 - r2 + 2*Rtor2*v.z()*v.z()) ; c[3] = 4*(pDotV*(pRad2 - Rtor2 - r2) + 2*Rtor2*p.z()*v.z()) ; c[4] = pRad2*pRad2 - 2*pRad2*(Rtor2+r2) + 4*Rtor2*p.z()*p.z() + (Rtor2-r2)*(Rtor2-r2) ; G4JTPolynomialSolver torusEq; num = torusEq.FindRoots( c, 4, sr, si ); for ( i = 0; i < num; i++ ) { if( si[i] == 0. ) { roots.push_back(sr[i]) ; } // store real roots } std::sort(roots.begin() , roots.end() ) ; // sorting with < return roots; } ////////////////////////////////////////////////////////////////////////////// // // Interface for DistanceToIn and DistanceToOut. // Calls TorusRootsJT and returns the smalles possible distance to // the surface. // Attention: Difference in DistanceToIn/Out for points p on the surface. G4double G4Torus::SolveNumericJT( const G4ThreeVector& p, const G4ThreeVector& v, G4double r, G4bool IsDistanceToIn ) const { G4double bigdist = 10*mm ; G4double tmin = kInfinity ; G4double t, scal ; // calculate the distances to the intersections with the Torus // from a given point p and direction v. // std::vector roots ; std::vector rootsrefined ; roots = TorusRootsJT(p,v,r) ; G4ThreeVector ptmp ; // determine the smallest non-negative solution // for ( size_t k = 0 ; k bigdist && t= - kAngTolerance*0.5) && (theta - (fSPhi + fDPhi) <= kAngTolerance*0.5) ) { // check if P is on the surface, and called from DistanceToIn // DistanceToIn has to return 0.0 if particle is going inside the solid if ( IsDistanceToIn == true ) { if (std::fabs(t) < 0.5*kCarTolerance ) { // compute scalar product at position p : v.n // ( n taken from SurfaceNormal, not normalized ) scal = v* G4ThreeVector( p.x()*(1-fRtor/std::sqrt(p.x()*p.x() + p.y()*p.y())), p.y()*(1-fRtor/std::sqrt(p.x()*p.x() + p.y()*p.y())), p.z() ); // change sign in case of inner radius // if ( r == GetRmin() ) { scal = -scal ; } if ( scal < 0 ) { return 0.0 ; } } } // check if P is on the surface, and called from DistanceToOut // DistanceToIn has to return 0.0 if particle is leaving the solid if ( IsDistanceToIn == false ) { if (std::fabs(t) < 0.5*kCarTolerance ) { // compute scalar product at position p : v.n // scal = v* G4ThreeVector( p.x()*(1-fRtor/std::sqrt(p.x()*p.x() + p.y()*p.y())), p.y()*(1-fRtor/std::sqrt(p.x()*p.x() + p.y()*p.y())), p.z() ); // change sign in case of inner radius // if ( r == GetRmin() ) { scal = -scal ; } if ( scal > 0 ) { return 0.0 ; } } } // check if distance is larger than 1/2 kCarTolerance // if( t > 0.5*kCarTolerance ) { tmin = t ; return tmin ; } } } return tmin; } ///////////////////////////////////////////////////////////////////////////// // // Calculate extent under transform and specified limit G4bool G4Torus::CalculateExtent( const EAxis pAxis, const G4VoxelLimits& pVoxelLimit, const G4AffineTransform& pTransform, G4double& pMin, G4double& pMax) const { if ((!pTransform.IsRotated()) && (fDPhi==twopi) && (fRmin==0)) { // Special case handling for unrotated solid torus // Compute x/y/z mins and maxs for bounding box respecting limits, // with early returns if outside limits. Then switch() on pAxis, // and compute exact x and y limit for x/y case G4double xoffset,xMin,xMax; G4double yoffset,yMin,yMax; G4double zoffset,zMin,zMax; G4double RTorus,delta,diff1,diff2,maxDiff,newMin,newMax; G4double xoff1,xoff2,yoff1,yoff2; xoffset = pTransform.NetTranslation().x(); xMin = xoffset - fRmax - fRtor ; xMax = xoffset + fRmax + fRtor ; if (pVoxelLimit.IsXLimited()) { if ( (xMin > pVoxelLimit.GetMaxXExtent()+kCarTolerance) || (xMax < pVoxelLimit.GetMinXExtent()-kCarTolerance) ) return false ; else { if (xMin < pVoxelLimit.GetMinXExtent()) { xMin = pVoxelLimit.GetMinXExtent() ; } if (xMax > pVoxelLimit.GetMaxXExtent()) { xMax = pVoxelLimit.GetMaxXExtent() ; } } } yoffset = pTransform.NetTranslation().y(); yMin = yoffset - fRmax - fRtor ; yMax = yoffset + fRmax + fRtor ; if (pVoxelLimit.IsYLimited()) { if ( (yMin > pVoxelLimit.GetMaxYExtent()+kCarTolerance) || (yMax < pVoxelLimit.GetMinYExtent()-kCarTolerance) ) { return false ; } else { if (yMin < pVoxelLimit.GetMinYExtent() ) { yMin = pVoxelLimit.GetMinYExtent() ; } if (yMax > pVoxelLimit.GetMaxYExtent() ) { yMax = pVoxelLimit.GetMaxYExtent() ; } } } zoffset = pTransform.NetTranslation().z() ; zMin = zoffset - fRmax ; zMax = zoffset + fRmax ; if (pVoxelLimit.IsZLimited()) { if ( (zMin > pVoxelLimit.GetMaxZExtent()+kCarTolerance) || (zMax < pVoxelLimit.GetMinZExtent()-kCarTolerance) ) { return false ; } else { if (zMin < pVoxelLimit.GetMinZExtent() ) { zMin = pVoxelLimit.GetMinZExtent() ; } if (zMax > pVoxelLimit.GetMaxZExtent() ) { zMax = pVoxelLimit.GetMaxZExtent() ; } } } // Known to cut cylinder switch (pAxis) { case kXAxis: yoff1=yoffset-yMin; yoff2=yMax-yoffset; if ( yoff1 >= 0 && yoff2 >= 0 ) { // Y limits cross max/min x => no change // pMin = xMin ; pMax = xMax ; } else { // Y limits don't cross max/min x => compute max delta x, // hence new mins/maxs // RTorus=fRmax+fRtor; delta = RTorus*RTorus - yoff1*yoff1; diff1 = (delta>0.) ? std::sqrt(delta) : 0.; delta = RTorus*RTorus - yoff2*yoff2; diff2 = (delta>0.) ? std::sqrt(delta) : 0.; maxDiff = (diff1 > diff2) ? diff1:diff2 ; newMin = xoffset - maxDiff ; newMax = xoffset + maxDiff ; pMin = (newMin < xMin) ? xMin : newMin ; pMax = (newMax > xMax) ? xMax : newMax ; } break; case kYAxis: xoff1 = xoffset - xMin ; xoff2 = xMax - xoffset ; if (xoff1 >= 0 && xoff2 >= 0 ) { // X limits cross max/min y => no change // pMin = yMin ; pMax = yMax ; } else { // X limits don't cross max/min y => compute max delta y, // hence new mins/maxs // RTorus=fRmax+fRtor; delta = RTorus*RTorus - xoff1*xoff1; diff1 = (delta>0.) ? std::sqrt(delta) : 0.; delta = RTorus*RTorus - xoff2*xoff2; diff2 = (delta>0.) ? std::sqrt(delta) : 0.; maxDiff = (diff1 > diff2) ? diff1 : diff2 ; newMin = yoffset - maxDiff ; newMax = yoffset + maxDiff ; pMin = (newMin < yMin) ? yMin : newMin ; pMax = (newMax > yMax) ? yMax : newMax ; } break; case kZAxis: pMin=zMin; pMax=zMax; break; default: break; } pMin -= kCarTolerance ; pMax += kCarTolerance ; return true; } else { G4int i, noEntries, noBetweenSections4 ; G4bool existsAfterClip = false ; // Calculate rotated vertex coordinates G4ThreeVectorList *vertices ; G4int noPolygonVertices ; // will be 4 vertices = CreateRotatedVertices(pTransform,noPolygonVertices) ; pMin = +kInfinity ; pMax = -kInfinity ; noEntries = vertices->size() ; noBetweenSections4 = noEntries - noPolygonVertices ; for (i=0;i= tolRMin*tolRMin && pt2 <= tolRMax*tolRMax ) { if ( fDPhi == twopi || pt2 == 0 ) // on torus swept axis { in = kInside ; } else { // Try inner tolerant phi boundaries (=>inside) // if not inside, try outer tolerant phi boundaries pPhi = std::atan2(p.y(),p.x()) ; if ( pPhi < -kAngTolerance*0.5 ) { pPhi += twopi ; } // 0<=pPhi<2pi if ( fSPhi >= 0 ) { if ( (std::abs(pPhi) < kAngTolerance*0.5) && (std::abs(fSPhi + fDPhi - twopi) < kAngTolerance*0.5) ) { pPhi += twopi ; // 0 <= pPhi < 2pi } if ( (pPhi >= fSPhi + kAngTolerance*0.5) && (pPhi <= fSPhi + fDPhi - kAngTolerance*0.5) ) { in = kInside ; } else if ( (pPhi >= fSPhi - kAngTolerance*0.5) && (pPhi <= fSPhi + fDPhi + kAngTolerance*0.5) ) { in = kSurface ; } } else // fSPhi < 0 { if ( (pPhi <= fSPhi + twopi - kAngTolerance*0.5) && (pPhi >= fSPhi + fDPhi + kAngTolerance*0.5) ) {;} else { in = kSurface ; } } } } else // Try generous boundaries { tolRMin = fRmin - kRadTolerance*0.5 ; tolRMax = fRmax + kRadTolerance*0.5 ; if (tolRMin < 0 ) { tolRMin = 0 ; } if ( (pt2 >= tolRMin*tolRMin) && (pt2 <= tolRMax*tolRMax) ) { if ( (fDPhi == twopi) || (pt2 == 0) ) // Continuous in phi or on z-axis { in = kSurface ; } else // Try outer tolerant phi boundaries only { pPhi = std::atan2(p.y(),p.x()) ; if ( pPhi < -kAngTolerance*0.5 ) { pPhi += twopi ; } // 0<=pPhi<2pi if ( fSPhi >= 0 ) { if ( (std::abs(pPhi) < kAngTolerance*0.5) && (std::abs(fSPhi + fDPhi - twopi) < kAngTolerance*0.5) ) { pPhi += twopi ; // 0 <= pPhi < 2pi } if ( (pPhi >= fSPhi - kAngTolerance*0.5) && (pPhi <= fSPhi + fDPhi + kAngTolerance*0.5) ) { in = kSurface; } } else // fSPhi < 0 { if ( (pPhi <= fSPhi + twopi - kAngTolerance*0.5) && (pPhi >= fSPhi + fDPhi + kAngTolerance*0.5) ) {;} else { in = kSurface ; } } } } } return in ; } ///////////////////////////////////////////////////////////////////////////// // // Return unit normal of surface closest to p // - note if point on z axis, ignore phi divided sides // - unsafe if point close to z axis a rmin=0 - no explicit checks G4ThreeVector G4Torus::SurfaceNormal( const G4ThreeVector& p ) const { G4int noSurfaces = 0; G4double rho2, rho, pt2, pt, pPhi; G4double distRMin = kInfinity; G4double distSPhi = kInfinity, distEPhi = kInfinity; G4double delta = 0.5*kCarTolerance, dAngle = 0.5*kAngTolerance; G4ThreeVector nR, nPs, nPe; G4ThreeVector norm, sumnorm(0.,0.,0.); rho2 = p.x()*p.x() + p.y()*p.y(); rho = std::sqrt(rho2); pt2 = std::fabs(rho2+p.z()*p.z() +fRtor*fRtor - 2*fRtor*rho); pt = std::sqrt(pt2) ; G4double distRMax = std::fabs(pt - fRmax); if(fRmin) distRMin = std::fabs(pt - fRmin); if( rho > delta ) { nR = G4ThreeVector( p.x()*(1-fRtor/rho)/pt, p.y()*(1-fRtor/rho)/pt, p.z()/pt ); } if ( fDPhi < twopi ) // && rho ) // old limitation against (0,0,z) { if ( rho ) { pPhi = std::atan2(p.y(),p.x()); if(pPhi < fSPhi-delta) { pPhi += twopi; } else if(pPhi > fSPhi+fDPhi+delta) { pPhi -= twopi; } distSPhi = std::fabs( pPhi - fSPhi ); distEPhi = std::fabs(pPhi-fSPhi-fDPhi); } nPs = G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0); nPe = G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0); } if( distRMax <= delta ) { noSurfaces ++; sumnorm += nR; } if( fRmin && distRMin <= delta ) { noSurfaces ++; sumnorm -= nR; } if( fDPhi < twopi ) { if (distSPhi <= dAngle) { noSurfaces ++; sumnorm += nPs; } if (distEPhi <= dAngle) { noSurfaces ++; sumnorm += nPe; } } if ( noSurfaces == 0 ) { #ifdef G4CSGDEBUG G4Exception("G4Torus::SurfaceNormal(p)", "Notification", JustWarning, "Point p is not on surface !?" ); #endif norm = ApproxSurfaceNormal(p); } else if ( noSurfaces == 1 ) { norm = sumnorm; } else { norm = sumnorm.unit(); } return norm ; } ////////////////////////////////////////////////////////////////////////////// // // Algorithm for SurfaceNormal() following the original specification // for points not on the surface G4ThreeVector G4Torus::ApproxSurfaceNormal( const G4ThreeVector& p ) const { ENorm side ; G4ThreeVector norm; G4double rho2,rho,pt2,pt,phi; G4double distRMin,distRMax,distSPhi,distEPhi,distMin; rho2 = p.x()*p.x() + p.y()*p.y(); rho = std::sqrt(rho2) ; pt2 = std::fabs(rho2+p.z()*p.z() +fRtor*fRtor - 2*fRtor*rho) ; pt = std::sqrt(pt2) ; distRMax = std::fabs(pt - fRmax) ; if(fRmin) // First minimum radius { distRMin = std::fabs(pt - fRmin) ; if (distRMin < distRMax) { distMin = distRMin ; side = kNRMin ; } else { distMin = distRMax ; side = kNRMax ; } } else { distMin = distRMax ; side = kNRMax ; } if ( (fDPhi < twopi) && rho ) { phi = std::atan2(p.y(),p.x()) ; // Protected against (0,0,z) (above rho!=0) if (phi < 0) { phi += twopi ; } if (fSPhi < 0 ) { distSPhi = std::fabs(phi-(fSPhi+twopi))*rho ; } else { distSPhi = std::fabs(phi-fSPhi)*rho ; } distEPhi = std::fabs(phi - fSPhi - fDPhi)*rho ; if (distSPhi < distEPhi) // Find new minimum { if (distSPhi If point is outer outer radius, compute intersection with rmax // - if at valid phi,z return // // -> Compute intersection with inner radius, taking largest +ve root // - if valid (phi), save intersction // // -> If phi segmented, compute intersections with phi half planes // - return smallest of valid phi intersections and // inner radius intersection // // NOTE: // - Precalculations for phi trigonometry are Done `just in time' // - `if valid' implies tolerant checking of intersection points G4double G4Torus::DistanceToIn( const G4ThreeVector& p, const G4ThreeVector& v ) const { G4double snxt=kInfinity, sphi=kInfinity; // snxt = default return value G4double s[4] ; // Precalculated trig for phi intersections - used by r,z intersections to // check validity G4bool seg; // true if segmented G4double hDPhi,hDPhiOT,hDPhiIT,cosHDPhiOT=0.,cosHDPhiIT=0.; // half dphi + outer tolerance G4double cPhi,sinCPhi=0.,cosCPhi=0.; // central phi G4double tolORMin2,tolIRMin2; // `generous' radii squared G4double tolORMax2,tolIRMax2 ; G4double Dist,xi,yi,zi,rhoi2,it2; // Intersection point variables G4double Comp; G4double cosSPhi,sinSPhi; // Trig for phi start intersect G4double ePhi,cosEPhi,sinEPhi; // for phi end intersect // Set phi divided flag and precalcs // if ( fDPhi < twopi ) { seg = true ; hDPhi = 0.5*fDPhi ; // half delta phi cPhi = fSPhi + hDPhi ; hDPhiOT = hDPhi+0.5*kAngTolerance ; // outers tol' half delta phi hDPhiIT = hDPhi - 0.5*kAngTolerance ; sinCPhi = std::sin(cPhi) ; cosCPhi = std::cos(cPhi) ; cosHDPhiOT = std::cos(hDPhiOT) ; cosHDPhiIT = std::cos(hDPhiIT) ; } else { seg = false ; } if (fRmin > kRadTolerance) // Calculate tolerant rmin and rmax { tolORMin2 = (fRmin - 0.5*kRadTolerance)*(fRmin - 0.5*kRadTolerance) ; tolIRMin2 = (fRmin + 0.5*kRadTolerance)*(fRmin + 0.5*kRadTolerance) ; } else { tolORMin2 = 0 ; tolIRMin2 = 0 ; } tolORMax2 = (fRmax + 0.5*kRadTolerance)*(fRmax + 0.5*kRadTolerance) ; tolIRMax2 = (fRmax - kRadTolerance*0.5)*(fRmax - kRadTolerance*0.5) ; // Intersection with Rmax (possible return) and Rmin (must also check phi) G4double Rtor2 = fRtor*fRtor ; snxt = SolveNumericJT(p,v,fRmax,true); if (fRmin) // Possible Rmin intersection { s[0] = SolveNumericJT(p,v,fRmin,true); if ( s[0] < snxt ) { snxt = s[0] ; } } // // Phi segment intersection // // o Tolerant of points inside phi planes by up to kCarTolerance*0.5 // // o NOTE: Large duplication of code between sphi & ephi checks // -> only diffs: sphi -> ephi, Comp -> -Comp and half-plane // intersection check <=0 -> >=0 // -> use some form of loop Construct ? if (seg) { sinSPhi = std::sin(fSPhi) ; // First phi surface (`S'tarting phi) cosSPhi = std::cos(fSPhi) ; Comp = v.x()*sinSPhi - v.y()*cosSPhi ; // Component in outwards // normal direction if (Comp < 0 ) { Dist = (p.y()*cosSPhi - p.x()*sinSPhi) ; if (Dist < kCarTolerance*0.5) { sphi = Dist/Comp ; if (sphi < snxt) { if ( sphi < 0 ) { sphi = 0 ; } xi = p.x() + sphi*v.x() ; yi = p.y() + sphi*v.y() ; zi = p.z() + sphi*v.z() ; rhoi2 = xi*xi + yi*yi ; it2 = std::fabs(rhoi2 + zi*zi + Rtor2 - 2*fRtor*std::sqrt(rhoi2)) ; if ( it2 >= tolORMin2 && it2 <= tolORMax2 ) { // r intersection is good - check intersecting // with correct half-plane // if ((yi*cosCPhi-xi*sinCPhi)<=0) { snxt=sphi; } } } } } ePhi=fSPhi+fDPhi; // Second phi surface (`E'nding phi) sinEPhi=std::sin(ePhi); cosEPhi=std::cos(ePhi); Comp=-(v.x()*sinEPhi-v.y()*cosEPhi); if ( Comp < 0 ) // Component in outwards normal dirn { Dist = -(p.y()*cosEPhi - p.x()*sinEPhi) ; if (Dist < kCarTolerance*0.5 ) { sphi = Dist/Comp ; if (sphi < snxt ) { if (sphi < 0 ) { sphi = 0 ; } xi = p.x() + sphi*v.x() ; yi = p.y() + sphi*v.y() ; zi = p.z() + sphi*v.z() ; rhoi2 = xi*xi + yi*yi ; it2 = std::fabs(rhoi2 + zi*zi + Rtor2 - 2*fRtor*std::sqrt(rhoi2)) ; if (it2 >= tolORMin2 && it2 <= tolORMax2) { // z and r intersections good - check intersecting // with correct half-plane // if ((yi*cosCPhi-xi*sinCPhi)>=0) { snxt=sphi; } } } } } } if(snxt < 0.5*kCarTolerance) { snxt = 0.0 ; } return snxt ; } ///////////////////////////////////////////////////////////////////////////// // // Calculate distance (<= actual) to closest surface of shape from outside // - Calculate distance to z, radial planes // - Only to phi planes if outside phi extent // - Return 0 if point inside G4double G4Torus::DistanceToIn( const G4ThreeVector& p ) const { G4double safe=0.0, safe1, safe2 ; G4double phiC, cosPhiC, sinPhiC, safePhi, ePhi, cosPsi ; G4double rho2, rho, pt2, pt ; rho2 = p.x()*p.x() + p.y()*p.y() ; rho = std::sqrt(rho2) ; pt2 = std::fabs(rho2 + p.z()*p.z() + fRtor*fRtor - 2*fRtor*rho) ; pt = std::sqrt(pt2) ; safe1 = fRmin - pt ; safe2 = pt - fRmax ; if (safe1 > safe2) { safe = safe1; } else { safe = safe2; } if ( fDPhi < twopi && rho ) { phiC = fSPhi + fDPhi*0.5 ; cosPhiC = std::cos(phiC) ; sinPhiC = std::sin(phiC) ; cosPsi = (p.x()*cosPhiC + p.y()*sinPhiC)/rho ; if (cosPsi < std::cos(fDPhi*0.5) ) // Psi=angle from central phi to point { // Point lies outside phi range if ((p.y()*cosPhiC - p.x()*sinPhiC) <= 0 ) { safePhi = std::fabs(p.x()*std::sin(fSPhi) - p.y()*std::cos(fSPhi)) ; } else { ePhi = fSPhi + fDPhi ; safePhi = std::fabs(p.x()*std::sin(ePhi) - p.y()*std::cos(ePhi)) ; } if (safePhi > safe) { safe = safePhi ; } } } if (safe < 0 ) { safe = 0 ; } return safe; } /////////////////////////////////////////////////////////////////////////// // // Calculate distance to surface of shape from `inside', allowing for tolerance // - Only Calc rmax intersection if no valid rmin intersection // G4double G4Torus::DistanceToOut( const G4ThreeVector& p, const G4ThreeVector& v, const G4bool calcNorm, G4bool *validNorm, G4ThreeVector *n ) const { ESide side = kNull, sidephi = kNull ; G4double snxt = kInfinity, sphi, s[4] ; // Vars for phi intersection // G4double sinSPhi, cosSPhi, ePhi, sinEPhi, cosEPhi; G4double cPhi, sinCPhi, cosCPhi ; G4double pDistS, compS, pDistE, compE, sphi2, xi, yi, zi, vphi ; // Radial Intersections Defenitions & General Precals //////////////////////// new calculation ////////////////////// #if 1 // This is the version with the calculation of CalcNorm = true // To be done: Check the precision of this calculation. // If you want return always validNorm = false, then take the version below G4double Rtor2 = fRtor*fRtor ; G4double rho2 = p.x()*p.x()+p.y()*p.y(); G4double rho = std::sqrt(rho2) ; G4double pt2 = std::fabs(rho2 + p.z()*p.z() + Rtor2 - 2*fRtor*rho) ; G4double pt = std::sqrt(pt2) ; G4double pDotV = p.x()*v.x() + p.y()*v.y() + p.z()*v.z() ; G4double tolRMax = fRmax - kRadTolerance*0.5 ; G4double vDotNmax = pDotV - fRtor*(v.x()*p.x() + v.y()*p.y())/rho ; G4double pDotxyNmax = (1 - fRtor/rho) ; if( (pt2 > tolRMax*tolRMax) && (vDotNmax >= 0) ) { // On tolerant boundary & heading outwards (or perpendicular to) outer // radial surface -> leaving immediately with *n for really convex part // only if ( calcNorm && (pDotxyNmax >= -kRadTolerance) ) { *n = G4ThreeVector( p.x()*(1 - fRtor/rho)/pt, p.y()*(1 - fRtor/rho)/pt, p.z()/pt ) ; *validNorm = true ; } return snxt = 0 ; // Leaving by Rmax immediately } snxt = SolveNumericJT(p,v,fRmax,false); side = kRMax ; // rmin if ( fRmin ) { G4double tolRMin = fRmin + kRadTolerance*0.5 ; if ( (pt2 < tolRMin*tolRMin) && (vDotNmax < 0) ) { if (calcNorm) { *validNorm = false ; } // Concave surface of the torus return snxt = 0 ; // Leaving by Rmin immediately } s[0] = SolveNumericJT(p,v,fRmin,false); if ( s[0] < snxt ) { snxt = s[0] ; side = kRMin ; } } #else // this is the "conservative" version which return always validnorm = false // NOTE: using this version the unit test testG4Torus will break snxt = SolveNumericJT(p,v,fRmax,false); side = kRMax ; if ( fRmin ) { s[0] = SolveNumericJT(p,v,fRmin,false); if ( s[0] < snxt ) { snxt = s[0] ; side = kRMin ; } } if ( calcNorm && (snxt == 0.0) ) { *validNorm = false ; // Leaving solid, but possible re-intersection return snxt ; } #endif if (fDPhi < twopi) // Phi Intersections { sinSPhi = std::sin(fSPhi) ; cosSPhi = std::cos(fSPhi) ; ePhi = fSPhi + fDPhi ; sinEPhi = std::sin(ePhi) ; cosEPhi = std::cos(ePhi) ; cPhi = fSPhi + fDPhi*0.5 ; sinCPhi = std::sin(cPhi) ; cosCPhi = std::cos(cPhi) ; if ( p.x() || p.y() ) // Check if on z axis (rho not needed later) { pDistS = p.x()*sinSPhi - p.y()*cosSPhi ; // pDist -ve when inside pDistE = -p.x()*sinEPhi + p.y()*cosEPhi ; // Comp -ve when in direction of outwards normal // compS = -sinSPhi*v.x() + cosSPhi*v.y() ; compE = sinEPhi*v.x() - cosEPhi*v.y() ; sidephi = kNull ; if ( (pDistS <= 0) && (pDistE <= 0) ) { // Inside both phi *full* planes if (compS<0) { sphi=pDistS/compS; xi=p.x()+sphi*v.x(); yi=p.y()+sphi*v.y(); // Check intersecting with correct half-plane // (if not -> no intersect) // if ((yi*cosCPhi-xi*sinCPhi)>=0) { sphi=kInfinity; } else { sidephi=kSPhi; if (pDistS>-kCarTolerance*0.5) { sphi=0; } // Leave by sphi // immediately } } else { sphi=kInfinity; } if (compE<0) { sphi2=pDistE/compE; // Only check further if < starting phi intersection // if (sphi2=0) { // Leaving via ending phi // sidephi=kEPhi; if (pDistE<=-kCarTolerance*0.5) { sphi=sphi2; } else { sphi=0; } } } } } else if ( (pDistS>=0) && (pDistE>=0) ) { // Outside both *full* phi planes if (pDistS <= pDistE) { sidephi = kSPhi ; } else { sidephi = kEPhi ; } if (fDPhi>pi) { if ( (compS<0) && (compE<0) ) { sphi=0; } else { sphi=kInfinity; } } else { // if towards both >=0 then once inside (after error) // will remain inside // if ( (compS>=0) && (compE>=0) ) { sphi=kInfinity; } else { sphi=0; } } } else if ( (pDistS>0) && (pDistE<0) ) { // Outside full starting plane, inside full ending plane if (fDPhi>pi) { if (compE<0) { sphi=pDistE/compE; xi=p.x()+sphi*v.x(); yi=p.y()+sphi*v.y(); // Check intersection in correct half-plane // (if not -> not leaving phi extent) // if ((yi*cosCPhi-xi*sinCPhi)<=0) { sphi=kInfinity; } else { // Leaving via Ending phi // sidephi = kEPhi ; if (pDistE>-kCarTolerance*0.5) { sphi=0; } } } else { sphi=kInfinity; } } else { if (compS>=0) { if (compE<0) { sphi=pDistE/compE; xi=p.x()+sphi*v.x(); yi=p.y()+sphi*v.y(); // Check intersection in correct half-plane // (if not -> remain in extent) // if ((yi*cosCPhi-xi*sinCPhi)<=0) { sphi=kInfinity; } else { // otherwise leaving via Ending phi // sidephi=kEPhi; } } else { sphi=kInfinity; } } else { // leaving immediately by starting phi // sidephi=kSPhi; sphi=0; } } } else { // Must be pDistS<0&&pDistE>0 // Inside full starting plane, outside full ending plane if (fDPhi>pi) { if (compS<0) { sphi=pDistS/compS; xi=p.x()+sphi*v.x(); yi=p.y()+sphi*v.y(); // Check intersection in correct half-plane // (if not -> not leaving phi extent) // if ((yi*cosCPhi-xi*sinCPhi)>=0) { sphi=kInfinity; } else { // Leaving via Starting phi // sidephi = kSPhi ; if (pDistS>-kCarTolerance*0.5) { sphi=0; } } } else { sphi=kInfinity; } } else { if (compE>=0) { if (compS<0) { sphi=pDistS/compS; xi=p.x()+sphi*v.x(); yi=p.y()+sphi*v.y(); // Check intersection in correct half-plane // (if not -> remain in extent) // if ((yi*cosCPhi-xi*sinCPhi)>=0) { sphi=kInfinity; } else { // otherwise leaving via Starting phi // sidephi=kSPhi; } } else { sphi=kInfinity; } } else { // leaving immediately by ending // sidephi=kEPhi; sphi=0; } } } } else { // On z axis + travel not || to z axis -> if phi of vector direction // within phi of shape, Step limited by rmax, else Step =0 vphi=std::atan2(v.y(),v.x()); if ( (fSPhi= -kRadTolerance) // really convex part of Rmax { *n = G4ThreeVector( xi*(1-fRtor/rhoi)/it, yi*(1-fRtor/rhoi)/it, zi/it ) ; *validNorm = true ; } else { *validNorm = false ; // concave-convex part of Rmax } break ; case kRMin: *validNorm = false ; // Rmin is concave or concave-convex break; case kSPhi: if (fDPhi <= pi ) { *n=G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0); *validNorm=true; } else { *validNorm = false ; } break ; case kEPhi: if (fDPhi <= pi) { *n=G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0); *validNorm=true; } else { *validNorm = false ; } break; default: // It seems we go here from time to time ... G4cout.precision(16); G4cout << G4endl; DumpInfo(); G4cout << "Position:" << G4endl << G4endl; G4cout << "p.x() = " << p.x()/mm << " mm" << G4endl; G4cout << "p.y() = " << p.y()/mm << " mm" << G4endl; G4cout << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl; G4cout << "Direction:" << G4endl << G4endl; G4cout << "v.x() = " << v.x() << G4endl; G4cout << "v.y() = " << v.y() << G4endl; G4cout << "v.z() = " << v.z() << G4endl << G4endl; G4cout << "Proposed distance :" << G4endl << G4endl; G4cout << "snxt = " << snxt/mm << " mm" << G4endl << G4endl; G4Exception("G4Torus::DistanceToOut(p,v,..)", "Notification",JustWarning, "Undefined side for valid surface normal to solid."); break; } } return snxt; } ///////////////////////////////////////////////////////////////////////// // // Calculate distance (<=actual) to closest surface of shape from inside G4double G4Torus::DistanceToOut( const G4ThreeVector& p ) const { G4double safe=0.0,safeR1,safeR2; G4double rho2,rho,pt2,pt ; G4double safePhi,phiC,cosPhiC,sinPhiC,ePhi; rho2 = p.x()*p.x() + p.y()*p.y() ; rho = std::sqrt(rho2) ; pt2 = std::fabs(rho2 + p.z()*p.z() + fRtor*fRtor - 2*fRtor*rho) ; pt = std::sqrt(pt2) ; #ifdef G4CSGDEBUG if( Inside(p) == kOutside ) { G4cout.precision(16) ; G4cout << G4endl ; DumpInfo(); G4cout << "Position:" << G4endl << G4endl ; G4cout << "p.x() = " << p.x()/mm << " mm" << G4endl ; G4cout << "p.y() = " << p.y()/mm << " mm" << G4endl ; G4cout << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl ; G4Exception("G4Torus::DistanceToOut(p)", "Notification", JustWarning, "Point p is outside !?" ); } #endif if (fRmin) { safeR1 = pt - fRmin ; safeR2 = fRmax - pt ; if (safeR1 < safeR2) { safe = safeR1 ; } else { safe = safeR2 ; } } else { safe = fRmax - pt ; } // Check if phi divided, Calc distances closest phi plane // if (fDPhikMaxMeshSections) { noCrossSections=kMaxMeshSections; } meshAngle = fDPhi/(noCrossSections - 1) ; meshRMax = (fRtor + fRmax)/std::cos(meshAngle*0.5) ; // If complete in phi, set start angle such that mesh will be at fRmax // on the x axis. Will give better extent calculations when not rotated if ( (fDPhi == pi*2.0) && (fSPhi == 0) ) { sAngle = -meshAngle*0.5 ; } else { sAngle = fSPhi ; } vertices = new G4ThreeVectorList(); vertices->reserve(noCrossSections*4) ; if (vertices) { for (crossSection=0;crossSectionpush_back(pTransform.TransformPoint(vertex0)); vertices->push_back(pTransform.TransformPoint(vertex1)); vertices->push_back(pTransform.TransformPoint(vertex2)); vertices->push_back(pTransform.TransformPoint(vertex3)); } noPolygonVertices = 4 ; } else { DumpInfo(); G4Exception("G4Torus::CreateRotatedVertices()", "FatalError", FatalException, "Error in allocation of vertices. Out of memory !"); } return vertices; } ////////////////////////////////////////////////////////////////////////// // // Stream object contents to an output stream G4GeometryType G4Torus::GetEntityType() const { return G4String("G4Torus"); } ////////////////////////////////////////////////////////////////////////// // // Stream object contents to an output stream std::ostream& G4Torus::StreamInfo( std::ostream& os ) const { os << "-----------------------------------------------------------\n" << " *** Dump for solid - " << GetName() << " ***\n" << " ===================================================\n" << " Solid type: G4Torus\n" << " Parameters: \n" << " inner radius: " << fRmin/mm << " mm \n" << " outer radius: " << fRmax/mm << " mm \n" << " swept radius: " << fRtor/mm << " mm \n" << " starting phi: " << fSPhi/degree << " degrees \n" << " delta phi : " << fDPhi/degree << " degrees \n" << "-----------------------------------------------------------\n"; return os; } //////////////////////////////////////////////////////////////////////////// // // GetPointOnSurface G4ThreeVector G4Torus::GetPointOnSurface() const { G4double cosu, sinu,cosv, sinv, aOut, aIn, aSide, chose, phi, theta, rRand; phi = RandFlat::shoot(fSPhi,fSPhi+fDPhi); theta = RandFlat::shoot(0.,2.*pi); cosu = std::cos(phi); sinu = std::sin(phi); cosv = std::cos(theta); sinv = std::sin(theta); // compute the areas aOut = (fDPhi)*2.*pi*fRtor*fRmax; aIn = (fDPhi)*2.*pi*fRtor*fRmin; aSide = pi*(fRmax*fRmax-fRmin*fRmin); if(fSPhi == 0 && fDPhi == twopi){ aSide = 0; } chose = RandFlat::shoot(0.,aOut + aIn + 2.*aSide); if(chose < aOut) { return G4ThreeVector ((fRtor+fRmax*cosv)*cosu, (fRtor+fRmax*cosv)*sinu, fRmax*sinv); } else if( (chose >= aOut) && (chose < aOut + aIn) ) { return G4ThreeVector ((fRtor+fRmin*cosv)*cosu, (fRtor+fRmin*cosv)*sinu, fRmin*sinv); } else if( (chose >= aOut + aIn) && (chose < aOut + aIn + aSide) ) { rRand = RandFlat::shoot(fRmin,fRmax); return G4ThreeVector ((fRtor+rRand*cosv)*std::cos(fSPhi), (fRtor+rRand*cosv)*std::sin(fSPhi), rRand*sinv); } else { rRand = RandFlat::shoot(fRmin,fRmax); return G4ThreeVector ((fRtor+rRand*cosv)*std::cos(fSPhi+fDPhi), (fRtor+rRand*cosv)*std::sin(fSPhi+fDPhi), rRand*sinv); } } /////////////////////////////////////////////////////////////////////// // // Visualisation Functions void G4Torus::DescribeYourselfTo ( G4VGraphicsScene& scene ) const { scene.AddSolid (*this); } G4Polyhedron* G4Torus::CreatePolyhedron () const { return new G4PolyhedronTorus (fRmin, fRmax, fRtor, fSPhi, fDPhi); } G4NURBS* G4Torus::CreateNURBS () const { G4NURBS* pNURBS; if (fRmin != 0) { if (fDPhi >= 2.0 * pi) { pNURBS = new G4NURBStube(fRmin, fRmax, fRtor); } else { pNURBS = new G4NURBStubesector(fRmin, fRmax, fRtor, fSPhi, fSPhi + fDPhi); } } else { if (fDPhi >= 2.0 * pi) { pNURBS = new G4NURBScylinder (fRmax, fRtor); } else { const G4double epsilon = 1.e-4; // Cylinder sector not yet available! pNURBS = new G4NURBStubesector (epsilon, fRmax, fRtor, fSPhi, fSPhi + fDPhi); } } return pNURBS; }