File indexing completed on 2026-11-05 10:53:45
0001
0002
0003
0004
0005
0006 #ifndef VECGEOM_VOLUMES_KERNEL_SPHEREIMPLEMENTATION_H_
0007 #define VECGEOM_VOLUMES_KERNEL_SPHEREIMPLEMENTATION_H_
0008
0009 #include "VecGeom/base/Vector3D.h"
0010 #include "VecGeom/volumes/SphereStruct.h"
0011 #include "VecGeom/volumes/SurfaceHitView.h"
0012 #include "VecGeom/volumes/kernel/GenericKernels.h"
0013 #include <VecCore/VecCore>
0014 #include "VecGeom/volumes/kernel/OrbImplementation.h"
0015 #include "VecGeom/volumes/SphereUtilities.h"
0016
0017 #include <cstdio>
0018
0019 namespace vecgeom {
0020
0021 VECGEOM_DEVICE_FORWARD_DECLARE(struct SphereImplementation;);
0022 VECGEOM_DEVICE_DECLARE_CONV(struct, SphereImplementation);
0023
0024 inline namespace VECGEOM_IMPL_NAMESPACE {
0025
0026 class PlacedSphere;
0027 template <typename T>
0028 struct SphereStruct;
0029 class UnplacedSphere;
0030
0031
0032
0033
0034
0035 struct SphereImplementation {
0036
0037 using PlacedShape_t = PlacedSphere;
0038 using UnplacedStruct_t = SphereStruct<Precision>;
0039 using UnplacedVolume_t = UnplacedSphere;
0040
0041
0042 enum class ExitSurface : unsigned char { kNone, kOuter, kInner, kPhiStart, kPhiEnd, kThetaStart, kThetaEnd };
0043
0044
0045
0046 template <typename Real_v, bool Start>
0047 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static Real_v PhiDistanceToOut(UnplacedStruct_t const &sphere,
0048 Vector3D<Real_v> const &point,
0049 Vector3D<Real_v> const &direction)
0050 {
0051 Vector3D<Precision> const normal = Start ? sphere.fPhiWedge.GetNormal1() : sphere.fPhiWedge.GetNormal2();
0052 const Real_v motion = direction.x() * normal.x() + direction.y() * normal.y();
0053 if (motion >= Real_v(-1.e-12)) return Real_v(kInfLength);
0054
0055 const Real_v projection = point.x() * normal.x() + point.y() * normal.y();
0056 Vector3D<Precision> const along = Start ? sphere.fPhiWedge.GetAlong1() : sphere.fPhiWedge.GetAlong2();
0057 if (Abs(projection) < Real_v(kTolerance) && point.x() * along.x() + point.y() * along.y() >= Real_v(0.))
0058 return Real_v(0.);
0059
0060 Real_v distance = -projection / motion;
0061 if (distance > Real_v(-kTolerance) && distance < Real_v(0.)) distance = Real_v(0.);
0062 return distance >= Real_v(0.) ? distance : Real_v(kInfLength);
0063 }
0064
0065
0066 template <typename Real_v>
0067 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void PhiDistancesToOut(UnplacedStruct_t const &sphere,
0068 Vector3D<Real_v> const &point,
0069 Vector3D<Real_v> const &direction,
0070 Real_v &startDistance, Real_v &endDistance)
0071 {
0072 startDistance = PhiDistanceToOut<Real_v, true>(sphere, point, direction);
0073 endDistance = PhiDistanceToOut<Real_v, false>(sphere, point, direction);
0074 }
0075
0076
0077
0078
0079
0080 template <typename Real_v, bool Start>
0081 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void ThetaDistanceToOut(
0082 UnplacedStruct_t const &sphere, Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction,
0083 Real_v const &rho2, Real_v const &pDotV2d, Real_v const &dirRho2, Real_v &distance, bool &intersects)
0084 {
0085 const Precision theta = Start ? sphere.fSTheta : sphere.eTheta;
0086 distance = Real_v(kInfLength);
0087 intersects = false;
0088 if ((Start && theta == 0.) || (!Start && theta == kPi)) return;
0089
0090 if (Abs(theta - kHalfPi) <= 0.5 * sphere.kAngTolerance) {
0091 const bool movingOut = Start ? direction.z() > Real_v(0.) : direction.z() < Real_v(0.);
0092 if (!movingOut) return;
0093 distance = -point.z() / direction.z();
0094 if (distance > Real_v(-kTolerance) && distance < Real_v(0.)) distance = Real_v(0.);
0095 intersects = distance >= Real_v(0.);
0096 return;
0097 }
0098
0099 const Real_v sinTheta = Start ? Real_v(sphere.sinSTheta) : Real_v(sphere.sinETheta);
0100 const Real_v cosTheta = Start ? Real_v(sphere.cosSTheta) : Real_v(sphere.cosETheta);
0101 const Real_v sin2 = sinTheta * sinTheta;
0102 const Real_v cos2 = cosTheta * cosTheta;
0103 const Real_v a = cos2 * dirRho2 - sin2 * direction.z() * direction.z();
0104 const Real_v b = cos2 * pDotV2d - sin2 * point.z() * direction.z();
0105 const Real_v c = cos2 * rho2 - sin2 * point.z() * point.z();
0106 Real_v discriminant = b * b - a * c;
0107 if (discriminant < Real_v(0.) && Abs(discriminant) < Real_v(kHalfTolerance)) discriminant = Real_v(0.);
0108 if (discriminant < Real_v(0.)) return;
0109
0110 const Real_v root = Sqrt(discriminant);
0111
0112 const bool negativeRoot = (Start && theta < kHalfPi) || (!Start && theta > kHalfPi);
0113 if (negativeRoot) {
0114 if (b >= Real_v(0.) && a != Real_v(0.))
0115 distance = (-b - root) / a;
0116 else if (b < Real_v(0.))
0117 distance = c / NonZero(-b + root);
0118 } else {
0119 if (b > Real_v(0.))
0120 distance = c / NonZero(-b - root);
0121 else if (a != Real_v(0.))
0122 distance = (-b + root) / a;
0123 }
0124
0125 if (distance > Real_v(-kTolerance) && distance < Real_v(0.)) distance = Real_v(0.);
0126 if (distance < Real_v(0.) || distance >= Real_v(kInfLength)) {
0127 distance = Real_v(kInfLength);
0128 return;
0129 }
0130 const Real_v hitZ = point.z() + distance * direction.z();
0131 bool crossesBoundary = discriminant > Real_v(0.);
0132 if (!crossesBoundary) {
0133 const Real_v hitX = point.x() + distance * direction.x();
0134 const Real_v hitY = point.y() + distance * direction.y();
0135 const bool hitsApex = hitX * hitX + hitY * hitY + hitZ * hitZ <= Real_v(kTolerance * kTolerance);
0136 if (hitsApex)
0137 crossesBoundary = Start ? direction.z() > Real_v(sphere.cosSTheta) : direction.z() < Real_v(sphere.cosETheta);
0138 }
0139 intersects = crossesBoundary && (theta < kHalfPi ? hitZ > Real_v(-kHalfTolerance) : hitZ < Real_v(kHalfTolerance));
0140 }
0141
0142
0143 template <typename Real_v>
0144 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void ThetaDistancesToOut(
0145 UnplacedStruct_t const &sphere, Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction,
0146 Real_v &startDistance, Real_v &endDistance, bool &startIntersects, bool &endIntersects)
0147 {
0148 const Real_v rho2 = point.Perp2();
0149 const Real_v pDotV2d = point.x() * direction.x() + point.y() * direction.y();
0150 const Real_v dirRho2 = direction.Perp2();
0151 ThetaDistanceToOut<Real_v, true>(sphere, point, direction, rho2, pDotV2d, dirRho2, startDistance, startIntersects);
0152 ThetaDistanceToOut<Real_v, false>(sphere, point, direction, rho2, pDotV2d, dirRho2, endDistance, endIntersects);
0153 }
0154
0155
0156
0157
0158
0159 template <typename Real_v>
0160 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static ExitSurface SelectSurfaceExit(UnplacedStruct_t const &sphere,
0161 Vector3D<Real_v> const &point,
0162 Vector3D<Real_v> const &direction)
0163 {
0164 const Real_v rad2 = point.Mag2();
0165 if (SphereUtilities::IsPointOnRadialSurfaceAndMovingOut<Real_v, false, true>(sphere, point, direction) &&
0166 sphere.fPhiWedge.Contains<Real_v>(point) && sphere.fThetaCone.Contains<Real_v>(point))
0167 return ExitSurface::kOuter;
0168 if (sphere.fRmin &&
0169 SphereUtilities::IsPointOnRadialSurfaceAndMovingOut<Real_v, true, true>(sphere, point, direction) &&
0170 sphere.fPhiWedge.Contains<Real_v>(point) && sphere.fThetaCone.Contains<Real_v>(point))
0171 return ExitSurface::kInner;
0172 if (!sphere.fFullThetaSphere && rad2 >= sphere.fRmin * sphere.fRmin && rad2 <= sphere.fRmax * sphere.fRmax &&
0173 sphere.fPhiWedge.Contains<Real_v>(point)) {
0174 if (SphereUtilities::IsPointOnThetaSurfaceAndMovingOut<Real_v, true, true>(sphere, point, direction))
0175 return ExitSurface::kThetaStart;
0176 if (SphereUtilities::IsPointOnThetaSurfaceAndMovingOut<Real_v, false, true>(sphere, point, direction))
0177 return ExitSurface::kThetaEnd;
0178 }
0179 if (!sphere.fFullPhiSphere && rad2 >= sphere.fRmin * sphere.fRmin && rad2 <= sphere.fRmax * sphere.fRmax &&
0180 sphere.fThetaCone.Contains<Real_v>(point)) {
0181 if (sphere.fPhiWedge.IsPointOnSurfaceAndMovingOut<Real_v, true, true>(point, direction))
0182 return ExitSurface::kPhiStart;
0183 if (sphere.fPhiWedge.IsPointOnSurfaceAndMovingOut<Real_v, false, true>(point, direction))
0184 return ExitSurface::kPhiEnd;
0185 }
0186 return ExitSurface::kNone;
0187 }
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198 template <typename Real_v>
0199 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void SetExitHit(UnplacedStruct_t const &sphere,
0200 Vector3D<Real_v> const &point,
0201 Vector3D<Real_v> const &direction,
0202 Real_v const &distance, ExitSurface surface,
0203 SurfaceHitView<Real_v> *hitInfo)
0204 {
0205 bool leftBehind = false;
0206 switch (surface) {
0207 case ExitSurface::kOuter:
0208 leftBehind = true;
0209 if (hitInfo->WantsNormal()) {
0210 Vector3D<Real_v> normal = point + distance * direction;
0211 normal *= distance == Real_v(0.) ? Real_v(1.) / Sqrt(normal.Mag2()) : Real_v(1.) / sphere.fRmax;
0212 hitInfo->SetNormal(normal);
0213 }
0214 break;
0215 case ExitSurface::kPhiStart:
0216 case ExitSurface::kPhiEnd:
0217 leftBehind = sphere.fDPhi <= kPi;
0218 if (hitInfo->WantsNormal()) {
0219 const Vector3D<Precision> normal =
0220 surface == ExitSurface::kPhiStart ? sphere.fPhiWedge.GetNormal1() : sphere.fPhiWedge.GetNormal2();
0221 hitInfo->SetNormal(Vector3D<Real_v>(-normal.x(), -normal.y(), Real_v(0.)));
0222 }
0223 break;
0224 case ExitSurface::kThetaStart:
0225 case ExitSurface::kThetaEnd: {
0226 const bool start = surface == ExitSurface::kThetaStart;
0227 leftBehind = start ? sphere.fSTheta >= kHalfPi : sphere.eTheta <= kHalfPi;
0228 if (hitInfo->WantsNormal()) {
0229 const Precision theta = start ? sphere.fSTheta : sphere.eTheta;
0230 Vector3D<Real_v> normal;
0231 if (Abs(theta - kHalfPi) <= kTolerance) {
0232 normal.Set(Real_v(0.), Real_v(0.), start ? Real_v(1.) : Real_v(-1.));
0233 } else {
0234 Vector3D<Real_v> reference = point + distance * direction;
0235 Real_v transverse2 = reference.Perp2();
0236
0237
0238 if (transverse2 <= Real_v(kTolerance * kTolerance)) {
0239 reference = point.Perp2() > Real_v(kTolerance * kTolerance) ? point : -direction;
0240 transverse2 = reference.Perp2();
0241 }
0242 const Real_v invTransverse = transverse2 > Real_v(0.) ? Real_v(1.) / Sqrt(transverse2) : Real_v(0.);
0243 const Real_v sinTheta = start ? sphere.sinSTheta : sphere.sinETheta;
0244 const Real_v cosTheta = start ? sphere.cosSTheta : sphere.cosETheta;
0245 normal.Set(cosTheta * reference.x() * invTransverse, cosTheta * reference.y() * invTransverse, -sinTheta);
0246 if (transverse2 <= Real_v(0.)) normal.x() = cosTheta;
0247 if (start) normal = -normal;
0248 }
0249 hitInfo->SetNormal(normal);
0250 }
0251 break;
0252 }
0253 case ExitSurface::kInner:
0254
0255 if (hitInfo->WantsNormal()) {
0256 Vector3D<Real_v> normal = point + distance * direction;
0257 normal *= distance == Real_v(0.) ? Real_v(-1.) / Sqrt(normal.Mag2()) : Real_v(-1.) / sphere.fRmin;
0258 hitInfo->SetNormal(normal);
0259 }
0260 break;
0261 case ExitSurface::kNone:
0262 break;
0263 }
0264 hitInfo->SetLeftBehind(leftBehind);
0265 }
0266
0267
0268
0269
0270
0271 template <typename Real_v>
0272 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void Contains(UnplacedStruct_t const &sphere,
0273 Vector3D<Real_v> const &point, bool &inside)
0274 {
0275 bool unused = false, outside = false;
0276 GenericKernelForContainsAndInside<Real_v, false>(sphere, point, unused, outside);
0277 inside = !outside;
0278 }
0279
0280
0281
0282
0283
0284 template <typename Real_v>
0285 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void Inside(UnplacedStruct_t const &sphere,
0286 Vector3D<Real_v> const &point, Inside_t &inside)
0287 {
0288 bool completelyinside = false, completelyoutside = false;
0289 GenericKernelForContainsAndInside<Real_v, true, true>(sphere, point, completelyinside, completelyoutside);
0290 inside = EInside::kSurface;
0291 if (completelyoutside) inside = Inside_t(EInside::kOutside);
0292 if (completelyinside) inside = Inside_t(EInside::kInside);
0293 }
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304 template <typename Real_v, bool ForInside, bool EarlyReturn = false>
0305 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void GenericKernelForContainsAndInside(
0306 UnplacedStruct_t const &sphere, Vector3D<Real_v> const &localPoint, bool &completelyinside,
0307 bool &completelyoutside)
0308 {
0309 Real_v rad2 = localPoint.Mag2();
0310
0311
0312
0313 if (sphere.fRmin)
0314 completelyinside =
0315 rad2 <= MakeMinusTolerantSquare<true>(sphere.fRmax) && rad2 >= MakePlusTolerantSquare<true>(sphere.fRmin);
0316 else
0317 completelyinside = rad2 <= MakeMinusTolerantSquare<true>(sphere.fRmax);
0318
0319 if (sphere.fRmin)
0320 completelyoutside =
0321 rad2 >= MakePlusTolerantSquare<true>(sphere.fRmax) || rad2 <= MakeMinusTolerantSquare<true>(sphere.fRmin);
0322 else
0323 completelyoutside = rad2 >= MakePlusTolerantSquare<true>(sphere.fRmax);
0324
0325
0326 if (!sphere.fFullPhiSphere) {
0327
0328 if constexpr (EarlyReturn) {
0329 if (completelyoutside && !completelyinside) return;
0330 }
0331
0332 bool completelyoutsidephi = false;
0333 bool completelyinsidephi = false;
0334 sphere.fPhiWedge.GenericKernelForContainsAndInside<Real_v, ForInside>(localPoint, completelyinsidephi,
0335 completelyoutsidephi);
0336 completelyoutside |= completelyoutsidephi;
0337
0338 if (ForInside) completelyinside &= completelyinsidephi;
0339 }
0340
0341
0342
0343 if (!sphere.fFullThetaSphere) {
0344 if constexpr (EarlyReturn) {
0345 if (completelyoutside && !completelyinside) return;
0346 }
0347
0348 bool completelyoutsidetheta = false;
0349 bool completelyinsidetheta = false;
0350 sphere.fThetaCone.GenericKernelForContainsAndInside<Real_v, ForInside>(localPoint, completelyinsidetheta,
0351 completelyoutsidetheta);
0352 completelyoutside |= completelyoutsidetheta;
0353
0354 if (ForInside) completelyinside &= completelyinsidetheta;
0355 }
0356 return;
0357 }
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367 template <typename Real_v>
0368 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void DistanceToIn(UnplacedStruct_t const &sphere,
0369 Vector3D<Real_v> const &point,
0370 Vector3D<Real_v> const &direction,
0371 Real_v const & , Real_v &distance)
0372 {
0373 distance = kInfLength;
0374 bool done = false;
0375
0376 bool fullPhiSphere = sphere.fFullPhiSphere;
0377 bool fullThetaSphere = sphere.fFullThetaSphere;
0378
0379
0380 Real_v rad2 = point.Mag2();
0381 Real_v pDotV3d = point.Dot(direction);
0382
0383 Real_v c = rad2 - sphere.fRmax * sphere.fRmax;
0384
0385
0386 if (rad2 <= MakePlusTolerantSquare<true>(sphere.fRmax)) {
0387 if (SphereUtilities::IsCompletelyInside<Real_v>(sphere, point)) {
0388 distance = Real_v(-1.0);
0389 return;
0390 }
0391
0392 if (SphereUtilities::IsPointOnSurfaceAndMovingOut<Real_v, false>(sphere, point, direction)) {
0393 distance = Real_v(0.0);
0394 return;
0395 }
0396 }
0397
0398 Real_v sd1(kInfLength);
0399 Real_v sd2(kInfLength);
0400 Real_v d2 = (pDotV3d * pDotV3d - c);
0401 if (d2 < Real_v(0.) || ((c > Real_v(0.)) && (pDotV3d > Real_v(0.)))) return;
0402
0403 sd1 = -pDotV3d - Sqrt(d2);
0404
0405 Real_v outerDist(kInfLength);
0406 Real_v innerDist(kInfLength);
0407
0408 if (sphere.fFullSphere) {
0409 if (sd1 > Real_v(kTolerance)) outerDist = sd1;
0410 } else {
0411 if (sd1 > Real_v(kTolerance) && sd1 < Real_v(kInfLength)) {
0412 Vector3D<Real_v> tmpPt = point + sd1 * direction;
0413
0414 if ((fullPhiSphere || sphere.fPhiWedge.Inside<Real_v>(tmpPt) != EInside::kOutside) &&
0415 (fullThetaSphere || sphere.fThetaCone.Inside<Real_v>(tmpPt) != EInside::kOutside))
0416 outerDist = sd1;
0417 }
0418 }
0419
0420 if (sphere.fRmin) {
0421 c = rad2 - sphere.fRmin * sphere.fRmin;
0422 d2 = pDotV3d * pDotV3d - c;
0423 if (d2 >= Real_v(0.)) sd2 = -pDotV3d + Sqrt(d2);
0424 const bool innerTangentContinuation = (c <= Real_v(2. * kTolerance) * sphere.fRmin) &&
0425 (c >= -Real_v(2. * kTolerance) * sphere.fRmin) &&
0426 (pDotV3d >= -kToleranceDist<Real_v>);
0427
0428 if (sphere.fFullSphere) {
0429
0430
0431 if (sd2 > Real_v(kTolerance) || (innerTangentContinuation && sd2 >= -Real_v(kTolerance))) {
0432 innerDist = innerTangentContinuation ? Real_v(0.) : sd2;
0433 }
0434 } else {
0435 if (innerTangentContinuation || (sd2 > Real_v(kTolerance) && sd2 < Real_v(kInfLength))) {
0436 const Real_v innerCandidate = innerTangentContinuation ? Real_v(0.) : sd2;
0437 Vector3D<Real_v> tmpPt = point + innerCandidate * direction;
0438 if ((fullPhiSphere || sphere.fPhiWedge.Inside<Real_v>(tmpPt) != EInside::kOutside) &&
0439 (fullThetaSphere || sphere.fThetaCone.Inside<Real_v>(tmpPt) != EInside::kOutside))
0440 innerDist = innerCandidate;
0441 }
0442 }
0443 }
0444
0445 distance = Min(outerDist, innerDist);
0446
0447 if (!fullPhiSphere) {
0448 GetMinDistFromPhi<Real_v, true>(sphere, point, direction, done, distance);
0449 }
0450
0451 Real_v distThetaMin(kInfLength);
0452
0453 if (!fullThetaSphere) {
0454 bool intsect1 = false;
0455 bool intsect2 = false;
0456 Real_v distTheta1(kInfLength), distCone1(kInfLength);
0457 Real_v distTheta2(kInfLength), distCone2(kInfLength);
0458 Real_v rmin2 = sphere.fRmin * sphere.fRmin;
0459 Real_v rmax2 = sphere.fRmax * sphere.fRmax;
0460 Vector3D<Real_v> coneIntSecPt1;
0461 Vector3D<Real_v> coneIntSecPt2;
0462
0463 sphere.fThetaCone.DistanceToIn<Real_v>(point, direction, distTheta1, distTheta2, intsect1,
0464 intsect2);
0465 bool cone1MovesIn = false;
0466 bool cone1EntersApex = false;
0467 bool cone1ZeroRoot = intsect1 && (Abs(distTheta1) <= Real_v(kTolerance));
0468 bool candidateCone1 = intsect1 && ((distTheta1 > Real_v(kTolerance)) || cone1ZeroRoot);
0469 Real_v pDotV2d = point.x() * direction.x() + point.y() * direction.y();
0470 Real_v dirRho2 = direction.Perp2();
0471 Real_v pointZDir = point.z() * direction.z();
0472 Real_v dirZ2 = direction.z() * direction.z();
0473
0474 if (candidateCone1) {
0475 distCone1 = rad2 + distTheta1 * (Real_v(2.) * pDotV3d + distTheta1);
0476 candidateCone1 &= distCone1 >= rmin2 && distCone1 <= rmax2;
0477 if (candidateCone1) {
0478 Real_v motion1 = -direction.z();
0479 Real_v a = Real_v(0.);
0480 if (Abs(sphere.fSTheta - kHalfPi) > kTolerance) {
0481 Real_v tanTheta2 = Real_v(sphere.fThetaCone.GetTanSTheta2());
0482 a = dirRho2 - dirZ2 * tanTheta2;
0483 Real_v b = pDotV2d - pointZDir * tanTheta2;
0484 motion1 = b + distTheta1 * a;
0485 }
0486 cone1MovesIn = SphereUtilities::IsThetaConeMotion<Real_v, true, false>(sphere, motion1);
0487
0488
0489 if (cone1ZeroRoot && !cone1MovesIn && Abs(motion1) <= kToleranceDist<Real_v>) {
0490 cone1MovesIn = (sphere.fSTheta <= kHalfPi) ? (a > Real_v(0.)) : (a < Real_v(0.));
0491 }
0492 cone1EntersApex = (sphere.fRmin == 0.) && (distCone1 <= Real_v(kTolerance * kTolerance)) &&
0493 sphere.fThetaCone.Contains<Real_v>(direction);
0494 if (!fullPhiSphere) cone1EntersApex &= sphere.fPhiWedge.Contains<Real_v>(direction);
0495 }
0496 }
0497
0498 bool cone2MovesIn = false;
0499 bool cone2EntersApex = false;
0500 bool cone2ZeroRoot = intsect2 && (Abs(distTheta2) <= Real_v(kTolerance));
0501 bool candidateCone2 = intsect2 && ((distTheta2 > Real_v(kTolerance)) || cone2ZeroRoot);
0502 if (candidateCone2) {
0503 distCone2 = rad2 + distTheta2 * (Real_v(2.) * pDotV3d + distTheta2);
0504 candidateCone2 &= distCone2 >= rmin2 && distCone2 <= rmax2;
0505 if (candidateCone2) {
0506 Real_v motion2 = -direction.z();
0507 Real_v a = Real_v(0.);
0508 if (Abs(sphere.eTheta - kHalfPi) > kTolerance) {
0509 Real_v tanTheta2 = Real_v(sphere.fThetaCone.GetTanETheta2());
0510 a = dirRho2 - dirZ2 * tanTheta2;
0511 Real_v b = pDotV2d - pointZDir * tanTheta2;
0512 motion2 = b + distTheta2 * a;
0513 }
0514 cone2MovesIn = SphereUtilities::IsThetaConeMotion<Real_v, false, false>(sphere, motion2);
0515
0516 if (cone2ZeroRoot && !cone2MovesIn && Abs(motion2) <= kToleranceDist<Real_v>) {
0517 cone2MovesIn = (sphere.eTheta <= kHalfPi) ? (a < Real_v(0.)) : (a > Real_v(0.));
0518 }
0519 cone2EntersApex = (sphere.fRmin == 0.) && (distCone2 <= Real_v(kTolerance * kTolerance)) &&
0520 sphere.fThetaCone.Contains<Real_v>(direction);
0521 if (!fullPhiSphere) cone2EntersApex &= sphere.fPhiWedge.Contains<Real_v>(direction);
0522 }
0523 }
0524
0525 bool isValidCone1 = candidateCone1 && (cone1MovesIn || cone1EntersApex);
0526 bool isValidCone2 = candidateCone2 && (cone2MovesIn || cone2EntersApex);
0527
0528 if (!fullPhiSphere && isValidCone1 && !cone1EntersApex) {
0529 coneIntSecPt1 = point + distTheta1 * direction;
0530 isValidCone1 = sphere.fPhiWedge.Contains<Real_v>(coneIntSecPt1);
0531 }
0532 if (!fullPhiSphere && isValidCone2 && !cone2EntersApex) {
0533 coneIntSecPt2 = point + distTheta2 * direction;
0534 isValidCone2 = sphere.fPhiWedge.Contains<Real_v>(coneIntSecPt2);
0535 }
0536 if (isValidCone2 && !isValidCone1) distThetaMin = distTheta2;
0537 if (isValidCone1 && !isValidCone2) distThetaMin = distTheta1;
0538 if (isValidCone1 && isValidCone2) distThetaMin = Min(distTheta1, distTheta2);
0539
0540 bool isApexEntry = (sphere.fRmin == 0.) && (rad2 <= Real_v(kTolerance * kTolerance)) &&
0541 sphere.fThetaCone.Contains<Real_v>(direction);
0542 if (!fullPhiSphere) isApexEntry &= sphere.fPhiWedge.Contains<Real_v>(direction);
0543 if (isApexEntry) distThetaMin = Real_v(0.);
0544 }
0545
0546 distance = Min(distThetaMin, distance);
0547 }
0548
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558 template <typename Real_v, bool RecordHitInfo>
0559 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void DistanceToOutImpl(
0560 UnplacedStruct_t const &sphere, Vector3D<Real_v> const &point, Vector3D<Real_v> const &direction,
0561 Real_v const & , Real_v &distance, SurfaceHitView<Real_v> *hitInfo)
0562 {
0563
0564 distance = kInfLength;
0565 [[maybe_unused]] ExitSurface hitSurface = ExitSurface::kNone;
0566 if constexpr (RecordHitInfo) hitInfo->Clear();
0567
0568 Real_v snxt(kInfLength);
0569
0570 Real_v d2(0.);
0571
0572 Real_v pDotV3d = point.Dot(direction);
0573
0574 Real_v rad2 = point.Mag2();
0575 Real_v c = rad2 - sphere.fRmax * sphere.fRmax;
0576
0577 Real_v sd1(kInfLength);
0578 Real_v sd2(kInfLength);
0579
0580 const Real_v outerLimit = sphere.fRmax + Real_v(kTolerance);
0581 bool completelyOutside = rad2 > outerLimit * outerLimit;
0582 if (sphere.fRmin > kTolerance) {
0583 const Real_v innerLimit = sphere.fRmin - Real_v(kTolerance);
0584 completelyOutside |= rad2 < innerLimit * innerLimit;
0585 }
0586 if (!sphere.fFullPhiSphere) {
0587 bool unused = false, outsidePhi = false;
0588 sphere.fPhiWedge.GenericKernelForContainsAndInside<Real_v, false>(point, unused, outsidePhi);
0589 if (outsidePhi) {
0590 bool insidePhi = false;
0591 sphere.fPhiWedge.GenericKernelForContainsAndInside<Real_v, true>(point, insidePhi, outsidePhi);
0592 }
0593 completelyOutside |= outsidePhi;
0594 }
0595 bool thetaNeedsSurfaceCheck = false;
0596 if (!sphere.fFullThetaSphere) {
0597 const Real_v radius = Sqrt(rad2);
0598
0599 const Real_v margin = Real_v(20. * sphere.kAngTolerance);
0600 const bool clearlyInsideTheta =
0601 point.z() < radius * sphere.cosSTheta - margin && point.z() > radius * sphere.cosETheta + margin;
0602 thetaNeedsSurfaceCheck = !clearlyInsideTheta;
0603 if (thetaNeedsSurfaceCheck) completelyOutside |= sphere.fThetaCone.IsCompletelyOutside<Real_v>(point);
0604 }
0605 if (completelyOutside) {
0606 distance = Real_v(-1.0);
0607 return;
0608 }
0609
0610
0611 const bool outerSurfaceExit =
0612 rad2 >= MakeMinusTolerantSquare<true>(sphere.fRmax) && pDotV3d > Real_v(-kToleranceDist<Real_v>);
0613 const bool innerSurfaceExit =
0614 sphere.fRmin && rad2 <= MakePlusTolerantSquare<true>(sphere.fRmin) && pDotV3d < Real_v(-kToleranceDist<Real_v>);
0615 if ((outerSurfaceExit || innerSurfaceExit) && (sphere.fFullPhiSphere || sphere.fPhiWedge.Contains<Real_v>(point)) &&
0616 (sphere.fFullThetaSphere || sphere.fThetaCone.Contains<Real_v>(point))) {
0617 distance = Real_v(0.);
0618 if constexpr (RecordHitInfo)
0619 SetExitHit(sphere, point, direction, distance, outerSurfaceExit ? ExitSurface::kOuter : ExitSurface::kInner,
0620 hitInfo);
0621 return;
0622 }
0623
0624 d2 = (pDotV3d * pDotV3d - c);
0625 if (d2 >= Real_v(0.)) sd1 = -pDotV3d + Sqrt(d2);
0626
0627 if (sphere.fRmin) {
0628 c = rad2 - sphere.fRmin * sphere.fRmin;
0629 d2 = (pDotV3d * pDotV3d - c);
0630 if (d2 >= Real_v(0.) && pDotV3d < -kToleranceDist<Real_v>) sd2 = -pDotV3d - Sqrt(d2);
0631 }
0632
0633 if constexpr (RecordHitInfo) {
0634 if (sd1 < sd2) {
0635 snxt = sd1;
0636 hitSurface = ExitSurface::kOuter;
0637 } else {
0638 snxt = sd2;
0639 hitSurface = sd2 < Real_v(kInfLength) ? ExitSurface::kInner : ExitSurface::kNone;
0640 }
0641 } else {
0642 snxt = Min(sd1, sd2);
0643 }
0644
0645 const bool upperHemisphere = sphere.fSTheta == 0. && sphere.eTheta == kHalfPi;
0646 const bool lowerHemisphere = sphere.fSTheta == kHalfPi && sphere.eTheta == kPi;
0647 if (sphere.fFullPhiSphere && (upperHemisphere || lowerHemisphere)) {
0648 distance = snxt;
0649 const bool exitsPlane = upperHemisphere ? direction.z() < Real_v(0.) : direction.z() > Real_v(0.);
0650 if (exitsPlane) {
0651 Real_v planeDistance = -point.z() / direction.z();
0652 if (Abs(point.z()) < Real_v(kTolerance)) planeDistance = Real_v(0.);
0653 if (planeDistance < distance) {
0654 distance = planeDistance;
0655 if constexpr (RecordHitInfo) hitSurface = upperHemisphere ? ExitSurface::kThetaEnd : ExitSurface::kThetaStart;
0656 }
0657 }
0658 if constexpr (RecordHitInfo) SetExitHit(sphere, point, direction, distance, hitSurface, hitInfo);
0659 return;
0660 }
0661
0662 Real_v distThetaMin(kInfLength);
0663 Real_v distPhiMin(kInfLength);
0664 [[maybe_unused]] ExitSurface thetaSurface = ExitSurface::kNone;
0665
0666 if (!sphere.fFullThetaSphere) {
0667 bool intsect1 = false;
0668 bool intsect2 = false;
0669 Real_v distTheta1(kInfLength);
0670 Real_v distTheta2(kInfLength);
0671 ThetaDistancesToOut(sphere, point, direction, distTheta1, distTheta2, intsect1, intsect2);
0672 if (thetaNeedsSurfaceCheck) {
0673 if (SphereUtilities::IsPointOnThetaSurfaceAndMovingOut<Real_v, true, true>(sphere, point, direction)) {
0674 distTheta1 = Real_v(0.);
0675 intsect1 = true;
0676 }
0677 if (SphereUtilities::IsPointOnThetaSurfaceAndMovingOut<Real_v, false, true>(sphere, point, direction)) {
0678 distTheta2 = Real_v(0.);
0679 intsect2 = true;
0680 }
0681 }
0682
0683 intsect1 &= distTheta1 >= -Real_v(kTolerance);
0684 intsect2 &= distTheta2 >= -Real_v(kTolerance);
0685 if (intsect1 && Abs(distTheta1) <= Real_v(kTolerance)) {
0686 const bool apexExit = rad2 <= Real_v(kTolerance * kTolerance) && direction.z() > Real_v(sphere.cosSTheta);
0687 Real_v motion1 = -direction.z();
0688 Real_v a = Real_v(0.);
0689 if (Abs(sphere.fSTheta - kHalfPi) > kTolerance) {
0690 Real_v tanTheta2 = Real_v(sphere.fThetaCone.GetTanSTheta2());
0691 a = direction.Perp2() - direction.z() * direction.z() * tanTheta2;
0692 motion1 = point.x() * direction.x() + point.y() * direction.y() - point.z() * direction.z() * tanTheta2;
0693 }
0694
0695
0696 bool materialContinuation = SphereUtilities::IsThetaConeMotion<Real_v, true, false>(sphere, motion1);
0697 if (!materialContinuation && Abs(motion1) <= kToleranceDist<Real_v>) {
0698 materialContinuation = (sphere.fSTheta <= kHalfPi) ? (a > Real_v(0.)) : (a < Real_v(0.));
0699 }
0700 if (materialContinuation && !apexExit) intsect1 = false;
0701 }
0702 if (intsect2 && Abs(distTheta2) <= Real_v(kTolerance)) {
0703 const bool apexExit = rad2 <= Real_v(kTolerance * kTolerance) && direction.z() < Real_v(sphere.cosETheta);
0704 Real_v motion2 = -direction.z();
0705 Real_v a = Real_v(0.);
0706 if (Abs(sphere.eTheta - kHalfPi) > kTolerance) {
0707 Real_v tanTheta2 = Real_v(sphere.fThetaCone.GetTanETheta2());
0708 a = direction.Perp2() - direction.z() * direction.z() * tanTheta2;
0709 motion2 = point.x() * direction.x() + point.y() * direction.y() - point.z() * direction.z() * tanTheta2;
0710 }
0711
0712 bool materialContinuation = SphereUtilities::IsThetaConeMotion<Real_v, false, false>(sphere, motion2);
0713 if (!materialContinuation && Abs(motion2) <= kToleranceDist<Real_v>) {
0714 materialContinuation = (sphere.eTheta <= kHalfPi) ? (a < Real_v(0.)) : (a > Real_v(0.));
0715 }
0716 if (materialContinuation && !apexExit) intsect2 = false;
0717 }
0718 if (intsect2 && !intsect1) {
0719 distThetaMin = distTheta2;
0720 if constexpr (RecordHitInfo) thetaSurface = ExitSurface::kThetaEnd;
0721 }
0722 if (!intsect2 && intsect1) {
0723 distThetaMin = distTheta1;
0724 if constexpr (RecordHitInfo) thetaSurface = ExitSurface::kThetaStart;
0725 }
0726 if (intsect2 && intsect1) {
0727 distThetaMin = Min(distTheta1, distTheta2);
0728 if constexpr (RecordHitInfo)
0729 thetaSurface = distTheta1 < distTheta2 ? ExitSurface::kThetaStart : ExitSurface::kThetaEnd;
0730 }
0731 }
0732
0733 if constexpr (RecordHitInfo) {
0734 distance = snxt;
0735 if (distThetaMin < distance) {
0736 distance = distThetaMin;
0737 hitSurface = thetaSurface;
0738 }
0739 } else {
0740 distance = Min(distThetaMin, snxt);
0741 }
0742
0743 if (!sphere.fFullPhiSphere) {
0744 if (sphere.fDPhi <= kPi) {
0745 Real_v distPhi1;
0746 Real_v distPhi2;
0747 PhiDistancesToOut(sphere, point, direction, distPhi1, distPhi2);
0748 distPhiMin = Min(distPhi1, distPhi2);
0749 if constexpr (RecordHitInfo) {
0750 if (distPhiMin < distance) {
0751 distance = distPhiMin;
0752 hitSurface = distPhi1 < distPhi2 ? ExitSurface::kPhiStart : ExitSurface::kPhiEnd;
0753 }
0754 } else {
0755 distance = Min(distPhiMin, distance);
0756 }
0757 } else {
0758 GetMinDistToOutFromPhi<Real_v, RecordHitInfo>(sphere, point, direction, distance, &hitSurface);
0759 }
0760 }
0761
0762
0763 if (distance >= Real_v(kInfLength) &&
0764 SphereUtilities::IsPointOnSurfaceAndMovingOut<Real_v, true>(sphere, point, direction)) {
0765 distance = Real_v(0.);
0766 if constexpr (RecordHitInfo) hitSurface = SelectSurfaceExit(sphere, point, direction);
0767 }
0768 if constexpr (RecordHitInfo) SetExitHit(sphere, point, direction, distance, hitSurface, hitInfo);
0769 }
0770
0771 template <typename Real_v>
0772 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void DistanceToOutWithHit(UnplacedStruct_t const &sphere,
0773 Vector3D<Real_v> const &point,
0774 Vector3D<Real_v> const &direction,
0775 Real_v const &stepMax, Real_v &distance,
0776 SurfaceHitView<Real_v> *hitInfo)
0777 {
0778 DistanceToOutImpl<Real_v, true>(sphere, point, direction, stepMax, distance, hitInfo);
0779 }
0780
0781
0782
0783
0784
0785
0786
0787
0788
0789 template <typename Real_v>
0790 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void DistanceToOut(UnplacedStruct_t const &sphere,
0791 Vector3D<Real_v> const &point,
0792 Vector3D<Real_v> const &direction,
0793 Real_v const &stepMax, Real_v &distance,
0794 SurfaceHitView<Real_v> *hitInfo = nullptr)
0795 {
0796 if (hitInfo)
0797 DistanceToOutWithHit(sphere, point, direction, stepMax, distance, hitInfo);
0798 else
0799 DistanceToOutImpl<Real_v, false>(sphere, point, direction, stepMax, distance, nullptr);
0800 }
0801
0802
0803
0804
0805 template <typename Real_v, bool RecordHitInfo>
0806 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void GetMinDistToOutFromPhi(UnplacedStruct_t const &sphere,
0807 Vector3D<Real_v> const &localPoint,
0808 Vector3D<Real_v> const &localDir,
0809 Real_v &distance,
0810 ExitSurface *hitSurface)
0811 {
0812 Real_v distPhi1(kInfLength);
0813 Real_v distPhi2(kInfLength);
0814 PhiDistancesToOut(sphere, localPoint, localDir, distPhi1, distPhi2);
0815
0816 const Real_v nearest = Min(distPhi1, distPhi2);
0817 const Real_v farthest = Max(distPhi1, distPhi2);
0818 if (nearest < distance) {
0819 const Vector3D<Real_v> hit = localPoint + nearest * localDir;
0820 const bool onFace = (nearest == distPhi1) ? sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, true>(hit)
0821 : sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, false>(hit);
0822 if (onFace) {
0823
0824
0825 if (hit.Perp2() < Real_v(kTolerance * kTolerance)) {
0826 const auto other = nearest == distPhi1 ? sphere.fPhiWedge.GetNormal2() : sphere.fPhiWedge.GetNormal1();
0827 if (localDir.x() * other.x() + localDir.y() * other.y() > Real_v(1.e-12)) return;
0828 }
0829 distance = nearest;
0830 if constexpr (RecordHitInfo) *hitSurface = distPhi1 < distPhi2 ? ExitSurface::kPhiStart : ExitSurface::kPhiEnd;
0831 return;
0832 }
0833 }
0834 if (farthest < distance) {
0835 const Vector3D<Real_v> hit = localPoint + farthest * localDir;
0836 const bool onFace = (farthest == distPhi1) ? sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, true>(hit)
0837 : sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, false>(hit);
0838 if (onFace) {
0839 distance = farthest;
0840 if constexpr (RecordHitInfo) *hitSurface = distPhi1 > distPhi2 ? ExitSurface::kPhiStart : ExitSurface::kPhiEnd;
0841 }
0842 }
0843 }
0844
0845
0846
0847
0848
0849
0850
0851
0852
0853
0854
0855 template <typename Real_v, bool DistToIn, bool RecordHitInfo = false>
0856 VECCORE_ATT_HOST_DEVICE static void GetMinDistFromPhi(UnplacedStruct_t const &sphere,
0857 Vector3D<Real_v> const &localPoint,
0858 Vector3D<Real_v> const &localDir, bool done, Real_v &distance,
0859 ExitSurface *hitSurface = nullptr)
0860 {
0861 Real_v distPhi1(kInfLength);
0862 Real_v distPhi2(kInfLength);
0863 Real_v dist(kInfLength);
0864
0865 if (DistToIn)
0866 sphere.fPhiWedge.DistanceToIn<Real_v>(localPoint, localDir, distPhi1, distPhi2);
0867 else
0868 sphere.fPhiWedge.DistanceToOut<Real_v>(localPoint, localDir, distPhi1, distPhi2);
0869
0870 bool containsCond1 = false, containsCond2 = false;
0871 bool tempCond = false;
0872
0873 dist = Min(distPhi1, distPhi2);
0874 if (dist < kInfLength) {
0875 Vector3D<Real_v> tmpPt = localPoint + dist * localDir;
0876 Real_v rad2 = tmpPt.Mag2();
0877
0878 tempCond = ((dist == distPhi1) && sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, true>(tmpPt)) ||
0879 ((dist == distPhi2) && sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, false>(tmpPt));
0880
0881 containsCond1 = tempCond && (rad2 > sphere.fRmin * sphere.fRmin) && (rad2 < sphere.fRmax * sphere.fRmax) &&
0882 sphere.fThetaCone.Contains<Real_v>(tmpPt);
0883 }
0884
0885 if (!done && containsCond1) {
0886 if constexpr (RecordHitInfo) {
0887 if (dist < distance) {
0888 distance = dist;
0889 *hitSurface = distPhi1 < distPhi2 ? ExitSurface::kPhiStart : ExitSurface::kPhiEnd;
0890 }
0891 } else {
0892 distance = Min(dist, distance);
0893 }
0894 }
0895
0896
0897 dist = Max(distPhi1, distPhi2);
0898 if (dist < kInfLength) {
0899 Vector3D<Real_v> tmpPt = localPoint + dist * localDir;
0900
0901 Real_v rad2 = tmpPt.Mag2();
0902 tempCond = ((dist == distPhi1) && sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, true>(tmpPt)) ||
0903 ((dist == distPhi2) && sphere.fPhiWedge.IsOnSurfaceGeneric<Real_v, false>(tmpPt));
0904
0905 containsCond2 = tempCond && (rad2 > sphere.fRmin * sphere.fRmin) && (rad2 < sphere.fRmax * sphere.fRmax) &&
0906 sphere.fThetaCone.Contains<Real_v>(tmpPt);
0907 }
0908 if (!done && !containsCond1 && containsCond2) {
0909 if constexpr (RecordHitInfo) {
0910 if (dist < distance) {
0911 distance = dist;
0912 *hitSurface = distPhi1 > distPhi2 ? ExitSurface::kPhiStart : ExitSurface::kPhiEnd;
0913 }
0914 } else {
0915 distance = Min(dist, distance);
0916 }
0917 }
0918 }
0919
0920
0921
0922
0923
0924
0925
0926 template <typename Real_v>
0927 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void SafetyToIn(UnplacedStruct_t const &sphere,
0928 Vector3D<Real_v> const &point, Real_v &safety)
0929 {
0930
0931 Real_v rad = point.Mag();
0932
0933 Real_v safeRMin(0.);
0934 Real_v safeRMax(0.);
0935
0936 if (sphere.fFullThetaSphere) {
0937
0938
0939 if (sphere.fRmin) {
0940 safeRMin = sphere.fRmin - rad;
0941 safeRMax = rad - sphere.fRmax;
0942 safety = safeRMin > safeRMax ? safeRMin : safeRMax;
0943 } else {
0944 safety = rad - sphere.fRmax;
0945 }
0946
0947 if (!sphere.fFullPhiSphere) safety = Max(sphere.fPhiWedge.SafetyToIn<Real_v>(point), safety);
0948 if (safety > Real_v(kTolerance)) return;
0949 safety = safety < Real_v(-kTolerance) ? Real_v(-1.) : Real_v(0.);
0950 return;
0951 }
0952
0953 bool completelyinside = false;
0954 bool completelyoutside = false;
0955 GenericKernelForContainsAndInside<Real_v, true>(sphere, point, completelyinside, completelyoutside);
0956
0957 if (completelyinside) {
0958 safety = Real_v(-1.0);
0959 return;
0960 }
0961
0962 if (!completelyoutside) {
0963 safety = Real_v(0.0);
0964 return;
0965 }
0966
0967 if (sphere.fRmin) {
0968 safeRMin = sphere.fRmin - rad;
0969 safeRMax = rad - sphere.fRmax;
0970 safety = safeRMin > safeRMax ? safeRMin : safeRMax;
0971 } else {
0972 safety = rad - sphere.fRmax;
0973 }
0974
0975
0976
0977 if (!sphere.fFullPhiSphere) {
0978 Real_v safetyPhi = sphere.fPhiWedge.SafetyToIn<Real_v>(point);
0979 safety = Max(safetyPhi, safety);
0980 }
0981
0982
0983 if (!sphere.fFullThetaSphere) {
0984 Real_v safetyTheta = sphere.fThetaCone.SafetyToIn<Real_v>(point);
0985 safety = Max(safetyTheta, safety);
0986 }
0987 }
0988
0989
0990
0991
0992
0993
0994
0995 template <typename Real_v>
0996 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void SafetyToOut(UnplacedStruct_t const &sphere,
0997 Vector3D<Real_v> const &point, Real_v &safety)
0998 {
0999
1000 Real_v rad = point.Mag();
1001
1002 bool completelyinside = false;
1003 bool completelyoutside = false;
1004 GenericKernelForContainsAndInside<Real_v, true>(sphere, point, completelyinside, completelyoutside);
1005 if (completelyoutside) {
1006 safety = Real_v(-1.0);
1007 return;
1008 }
1009
1010 if (!completelyinside) {
1011 safety = Real_v(0.0);
1012 return;
1013 }
1014
1015
1016 if (sphere.fRmin) {
1017 Real_v safeRMin = (rad - sphere.fRmin);
1018 Real_v safeRMax = (sphere.fRmax - rad);
1019 safety = safeRMin < safeRMax ? safeRMin : safeRMax;
1020 } else {
1021 safety = sphere.fRmax - rad;
1022 }
1023
1024
1025 if (!sphere.fFullPhiSphere) {
1026 Real_v safetyPhi = sphere.fPhiWedge.SafetyToOut<Real_v>(point);
1027 safety = Min(safetyPhi, safety);
1028 }
1029
1030
1031 if (!sphere.fFullThetaSphere) {
1032 Real_v safeTheta = sphere.fThetaCone.SafetyToOut<Real_v>(point);
1033 safety = Min(safeTheta, safety);
1034 }
1035 }
1036
1037
1038
1039
1040
1041
1042 template <typename Real_v>
1043 VECCORE_ATT_HOST_DEVICE static Vector3D<Real_v> ApproxSurfaceNormalKernel(UnplacedStruct_t const &sphere,
1044 Vector3D<Real_v> const &point)
1045 {
1046 Vector3D<Real_v> norm(0., 0., 0.);
1047 Real_v radius = point.Mag();
1048 Real_v distRMax = Abs(radius - sphere.fRmax);
1049 Real_v distRMin = InfinityLength<Real_v>();
1050 Real_v distPhi1 = InfinityLength<Real_v>();
1051 Real_v distPhi2 = InfinityLength<Real_v>();
1052 Real_v distTheta1 = InfinityLength<Real_v>();
1053 Real_v distTheta2 = InfinityLength<Real_v>();
1054 Real_v distMin = distRMax;
1055
1056 if (sphere.fRmin > 0.) {
1057 distRMin = Abs(sphere.fRmin - radius);
1058 distMin = Min(distRMin, distRMax);
1059 }
1060
1061 if (!sphere.fFullPhiSphere) {
1062 distPhi1 = Abs(point.x() * sphere.fPhiWedge.GetNormal1().x() + point.y() * sphere.fPhiWedge.GetNormal1().y());
1063 distPhi2 = Abs(point.x() * sphere.fPhiWedge.GetNormal2().x() + point.y() * sphere.fPhiWedge.GetNormal2().y());
1064 distMin = Min(distMin, distPhi1, distPhi2);
1065 }
1066
1067 if (!sphere.fFullThetaSphere) {
1068 Real_v rho = point.Perp();
1069 distTheta1 = sphere.fThetaCone.DistanceToLine<Real_v>(sphere.fThetaCone.GetSlope1(), rho, point.z());
1070 distTheta2 = sphere.fThetaCone.DistanceToLine<Real_v>(sphere.fThetaCone.GetSlope2(), rho, point.z());
1071 distMin = Min(distMin, distTheta1, distTheta2);
1072 }
1073
1074 if (distMin == distRMax) norm = point.Unit();
1075 if (distMin == distRMin) norm = -point.Unit();
1076
1077 Vector3D<Real_v> normal1 = sphere.fPhiWedge.GetNormal1();
1078 Vector3D<Real_v> normal2 = sphere.fPhiWedge.GetNormal2();
1079 if (distMin == distPhi1) norm = -normal1;
1080 if (distMin == distPhi2) norm = -normal2;
1081
1082 if (distMin == distTheta1) norm += sphere.fThetaCone.GetNormal1<Real_v>(point);
1083 if (distMin == distTheta2) norm += sphere.fThetaCone.GetNormal2<Real_v>(point);
1084
1085 return norm;
1086 }
1087
1088
1089
1090
1091
1092
1093
1094
1095 template <typename Real_v>
1096 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static Vector3D<Real_v> Normal(UnplacedStruct_t const &sphere,
1097 Vector3D<Real_v> const &point,
1098 bool &valid)
1099 {
1100 Vector3D<Real_v> normal(0., 0., 0.);
1101 normal.Set(1e-30);
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129 bool isPointOutside = false;
1130
1131 bool isPointInside = false;
1132 GenericKernelForContainsAndInside<Real_v, true>(sphere, point, isPointInside, isPointOutside);
1133 if (isPointInside || isPointOutside) normal = ApproxSurfaceNormalKernel<Real_v>(sphere, point);
1134 bool needsNormalization = isPointInside || isPointOutside;
1135
1136 valid = false;
1137
1138 int noSurfaces = 0;
1139 bool isPointOnOuterRadius = SphereUtilities::IsPointOnOuterRadius<Real_v>(sphere, point);
1140
1141 if (isPointOnOuterRadius) ++noSurfaces;
1142 if (!isPointOutside && isPointOnOuterRadius) normal += point.Unit();
1143
1144 if (sphere.fRmin) {
1145 bool isPointOnInnerRadius = SphereUtilities::IsPointOnInnerRadius<Real_v>(sphere, point);
1146 if (isPointOnInnerRadius) ++noSurfaces;
1147 if (!isPointOutside && isPointOnInnerRadius) normal -= point.Unit();
1148 }
1149
1150 if (!sphere.fFullPhiSphere) {
1151 bool isPointOnStartPhi = SphereUtilities::IsPointOnStartPhi<Real_v>(sphere, point);
1152 bool isPointOnEndPhi = SphereUtilities::IsPointOnEndPhi<Real_v>(sphere, point);
1153 if (isPointOnStartPhi) ++noSurfaces;
1154 if (isPointOnEndPhi) ++noSurfaces;
1155 if (!isPointOutside && isPointOnStartPhi) normal -= sphere.fPhiWedge.GetNormal1();
1156 if (!isPointOutside && isPointOnEndPhi) normal -= sphere.fPhiWedge.GetNormal2();
1157 }
1158
1159 if (!sphere.fFullThetaSphere) {
1160 bool isPointOnStartTheta = SphereUtilities::IsPointOnStartTheta<Real_v>(sphere, point);
1161 bool isPointOnEndTheta = SphereUtilities::IsPointOnEndTheta<Real_v>(sphere, point);
1162 needsNormalization |= isPointOnStartTheta || isPointOnEndTheta;
1163
1164 if (isPointOnStartTheta) ++noSurfaces;
1165 if (!isPointOutside && isPointOnStartTheta) normal += sphere.fThetaCone.GetNormal1<Real_v>(point);
1166
1167 if (isPointOnEndTheta) ++noSurfaces;
1168 if (!isPointOutside && isPointOnEndTheta) normal += sphere.fThetaCone.GetNormal2<Real_v>(point);
1169
1170 Vector3D<Real_v> tempNormal(0., 0., -1.);
1171 if (!isPointOutside && isPointOnStartTheta && isPointOnEndTheta && (sphere.eTheta <= kPi / 2.))
1172 normal = tempNormal;
1173 Vector3D<Real_v> tempNormal2(0., 0., 1.);
1174 if (!isPointOutside && isPointOnStartTheta && isPointOnEndTheta && (sphere.fSTheta >= kPi / 2.))
1175 normal = tempNormal2;
1176 }
1177
1178
1179
1180 if (noSurfaces == 0 && !isPointInside && !isPointOutside) {
1181 normal = ApproxSurfaceNormalKernel<Real_v>(sphere, point);
1182 noSurfaces = 1;
1183 needsNormalization = true;
1184 }
1185
1186
1187 if (needsNormalization || noSurfaces != 1) normal.Normalize();
1188
1189 valid = (noSurfaces > 0);
1190
1191 return normal;
1192 }
1193 };
1194 }
1195 }
1196
1197 #endif