File indexing completed on 2026-06-26 08:37:07
0001
0002
0003
0004 #ifndef VECGEOM_VOLUMES_QUADRILATERALS_H_
0005 #define VECGEOM_VOLUMES_QUADRILATERALS_H_
0006
0007 #include "VecGeom/base/Global.h"
0008
0009 #include "VecGeom/base/Array.h"
0010 #include "VecGeom/base/AOS3D.h"
0011 #include "VecGeom/base/SOA3D.h"
0012 #include "VecGeom/base/Vector3D.h"
0013 #include "VecGeom/base/RNG.h"
0014 #include "VecGeom/volumes/kernel/GenericKernels.h"
0015 #include "VecGeom/volumes/Planes.h"
0016
0017
0018
0019
0020 namespace vecgeom {
0021
0022 VECGEOM_DEVICE_FORWARD_DECLARE(class Quadrilaterals;);
0023 VECGEOM_DEVICE_DECLARE_CONV(class, Quadrilaterals);
0024
0025 inline namespace VECGEOM_IMPL_NAMESPACE {
0026
0027 class Quadrilaterals {
0028
0029 private:
0030 Planes fPlanes;
0031 Planes fSideVectors[4];
0032
0033
0034
0035 AOS3D<Precision> fCorners[4];
0036
0037
0038 public:
0039 typedef Planes Sides_t[4];
0040 typedef AOS3D<Precision> Corners_t[4];
0041
0042 Quadrilaterals() = default;
0043
0044 VECCORE_ATT_HOST_DEVICE
0045 Quadrilaterals(int size, bool convex = true);
0046
0047
0048
0049
0050
0051
0052 VECCORE_ATT_HOST_DEVICE
0053 Quadrilaterals(int size, AlignedAllocator &a, bool convex = true);
0054
0055 VECCORE_ATT_HOST_DEVICE
0056 ~Quadrilaterals();
0057
0058 VECCORE_ATT_HOST_DEVICE
0059 Quadrilaterals(Quadrilaterals const &other);
0060
0061 VECCORE_ATT_HOST_DEVICE
0062 Quadrilaterals &operator=(Quadrilaterals const &other);
0063
0064
0065 VECCORE_ATT_HOST_DEVICE
0066 VECGEOM_FORCE_INLINE
0067 int size() const;
0068
0069 VECCORE_ATT_HOST_DEVICE
0070 VECGEOM_FORCE_INLINE
0071 static size_t aligned_sizeof_data(const size_t initSize);
0072
0073 VECCORE_ATT_HOST_DEVICE
0074 VECGEOM_FORCE_INLINE
0075 Planes const &GetPlanes() const;
0076
0077 VECCORE_ATT_HOST_DEVICE
0078 VECGEOM_FORCE_INLINE
0079 SOA3D<Precision> const &GetNormals() const;
0080
0081 VECCORE_ATT_HOST_DEVICE
0082 VECGEOM_FORCE_INLINE
0083 Vector3D<Precision> GetNormal(int i) const;
0084
0085 VECCORE_ATT_HOST_DEVICE
0086 VECGEOM_FORCE_INLINE
0087 Array<Precision> const &GetDistances() const;
0088
0089 VECCORE_ATT_HOST_DEVICE
0090 VECGEOM_FORCE_INLINE
0091 Precision GetDistance(int i) const;
0092
0093 VECCORE_ATT_HOST_DEVICE
0094 VECGEOM_FORCE_INLINE
0095 Sides_t const &GetSideVectors() const;
0096
0097 VECCORE_ATT_HOST_DEVICE
0098 VECGEOM_FORCE_INLINE
0099 Corners_t const &GetCorners() const;
0100
0101 VECCORE_ATT_HOST_DEVICE
0102 VECGEOM_FORCE_INLINE
0103 Precision GetTriangleArea(int index, int iCorner1, int iCorner2) const;
0104
0105 VECCORE_ATT_HOST_DEVICE
0106 VECGEOM_FORCE_INLINE
0107 Precision GetQuadrilateralArea(int index) const;
0108
0109 inline Vector3D<Precision> GetPointOnTriangle(int index, int iCorner0, int iCorner1, int iCorner2) const;
0110
0111 inline Vector3D<Precision> GetPointOnFace(int index) const;
0112
0113 VECCORE_ATT_HOST_DEVICE
0114 VECGEOM_FORCE_INLINE
0115 bool RayHitsQuadrilateral(int index, Vector3D<Precision> const &intersection) const
0116 {
0117 bool valid = true;
0118 for (int j = 0; j < 4; ++j) {
0119 valid &=
0120 intersection.Dot(fSideVectors[j].GetNormal(index)) + fSideVectors[j].GetDistances()[index] >= -kTolerance;
0121 if (vecCore::MaskEmpty(valid)) break;
0122 }
0123 return valid;
0124 }
0125
0126
0127
0128
0129
0130
0131 VECCORE_ATT_HOST_DEVICE
0132 void Set(int index, Vector3D<Precision> const &corner0, Vector3D<Precision> const &corner1,
0133 Vector3D<Precision> const &corner2, Vector3D<Precision> const &corner3);
0134
0135
0136 VECCORE_ATT_HOST_DEVICE
0137 void FlipSign(int index);
0138
0139 template <typename Real_v>
0140 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE vecCore::Mask_v<Real_v> Contains(Vector3D<Real_v> const &point) const;
0141
0142 template <typename Real_v, typename Inside_v>
0143 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Inside_v Inside(Vector3D<Real_v> const &point) const;
0144
0145 template <typename Real_v, typename Inside_v>
0146 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Inside_v Inside(Vector3D<Real_v> const &point, int i) const;
0147
0148 template <typename Real_v, bool behindPlanesT>
0149 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Real_v DistanceToIn(Vector3D<Real_v> const &point,
0150 Vector3D<Real_v> const &direction) const;
0151
0152 template <typename Real_v>
0153 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Real_v DistanceToOut(Vector3D<Real_v> const &point,
0154 Vector3D<Real_v> const &direction, Precision zMin,
0155 Precision zMax) const;
0156
0157 template <typename Real_v>
0158 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE Real_v DistanceToOut(Vector3D<Real_v> const &point,
0159 Vector3D<Real_v> const &direction) const;
0160
0161
0162 VECGEOM_FORCE_INLINE
0163 VECCORE_ATT_HOST_DEVICE
0164 Precision ScalarDistanceSquared(int index, Vector3D<Precision> const &point) const;
0165
0166 VECCORE_ATT_HOST_DEVICE
0167 void Print() const;
0168
0169 };
0170
0171 VECCORE_ATT_HOST_DEVICE
0172 VECGEOM_FORCE_INLINE
0173 int Quadrilaterals::size() const { return fPlanes.size(); }
0174
0175 VECCORE_ATT_HOST_DEVICE
0176 VECGEOM_FORCE_INLINE
0177 size_t Quadrilaterals::aligned_sizeof_data(const size_t initSize)
0178 {
0179 return (5 * Planes::aligned_sizeof_data(initSize) + 4 * AOS3D<Precision>::aligned_sizeof_data(initSize));
0180 }
0181
0182 VECCORE_ATT_HOST_DEVICE VECGEOM_FORCE_INLINE Planes const &Quadrilaterals::GetPlanes() const { return fPlanes; }
0183
0184 VECCORE_ATT_HOST_DEVICE
0185 SOA3D<Precision> const &Quadrilaterals::GetNormals() const { return fPlanes.GetNormals(); }
0186
0187 VECCORE_ATT_HOST_DEVICE
0188 Vector3D<Precision> Quadrilaterals::GetNormal(int i) const { return fPlanes.GetNormal(i); }
0189
0190 VECCORE_ATT_HOST_DEVICE
0191 Array<Precision> const &Quadrilaterals::GetDistances() const { return fPlanes.GetDistances(); }
0192
0193 VECCORE_ATT_HOST_DEVICE
0194 Precision Quadrilaterals::GetDistance(int i) const { return fPlanes.GetDistance(i); }
0195
0196 VECCORE_ATT_HOST_DEVICE
0197 Quadrilaterals::Sides_t const &Quadrilaterals::GetSideVectors() const { return fSideVectors; }
0198
0199 VECCORE_ATT_HOST_DEVICE
0200 Quadrilaterals::Corners_t const &Quadrilaterals::GetCorners() const { return fCorners; }
0201
0202 VECCORE_ATT_HOST_DEVICE
0203 Precision Quadrilaterals::GetTriangleArea(int index, int iCorner1, int iCorner2) const
0204 {
0205 Precision fArea = 0.;
0206 Vector3D<Precision> vec1 = fCorners[iCorner1][index] - fCorners[0][index];
0207 Vector3D<Precision> vec2 = fCorners[iCorner2][index] - fCorners[0][index];
0208
0209 fArea = 0.5 * (vec1.Cross(vec2)).Mag();
0210 return fArea;
0211 }
0212
0213 VECCORE_ATT_HOST_DEVICE
0214 Precision Quadrilaterals::GetQuadrilateralArea(int index) const
0215 {
0216 Precision fArea = 0.;
0217
0218 fArea = GetTriangleArea(index, 1, 2) + GetTriangleArea(index, 2, 3);
0219 return fArea;
0220 }
0221
0222 Vector3D<Precision> Quadrilaterals::GetPointOnTriangle(int index, int iCorner0, int iCorner1, int iCorner2) const
0223 {
0224 Precision r1 = RNG::Instance().uniform(0.0, 1.0);
0225 Precision r2 = RNG::Instance().uniform(0.0, 1.0);
0226 if (r1 + r2 > 1.) {
0227 r1 = 1. - r1;
0228 r2 = 1. - r2;
0229 }
0230 Vector3D<Precision> vec1 = fCorners[iCorner1][index] - fCorners[iCorner0][index];
0231 Vector3D<Precision> vec2 = fCorners[iCorner2][index] - fCorners[iCorner0][index];
0232 return fCorners[iCorner0][index] + r1 * vec1 + r2 * vec2;
0233 }
0234
0235 Vector3D<Precision> Quadrilaterals::GetPointOnFace(int index) const
0236 {
0237
0238
0239 int nvert = 0;
0240 int iCorners[4];
0241 for (int i = 0; i < 4; ++i) {
0242 if ((fCorners[(i + 1) % 4][index] - fCorners[i % 4][index]).Mag2() > kTolerance) {
0243 iCorners[nvert++] = i;
0244 }
0245 }
0246 if (nvert == 1) return fCorners[0][index];
0247 if (nvert == 2) return GetPointOnTriangle(index, iCorners[0], iCorners[0], iCorners[1]);
0248 if (nvert == 3) return GetPointOnTriangle(index, iCorners[0], iCorners[1], iCorners[2]);
0249 Precision choice = RNG::Instance().uniform(0, 1.0);
0250 if (choice < 0.5) {
0251 return GetPointOnTriangle(index, 0, 1, 2);
0252 } else {
0253 return GetPointOnTriangle(index, 0, 2, 3);
0254 }
0255 }
0256
0257 template <typename Real_v>
0258 VECCORE_ATT_HOST_DEVICE vecCore::Mask_v<Real_v> Quadrilaterals::Contains(Vector3D<Real_v> const &point) const
0259 {
0260 return fPlanes.Contains<Real_v>(point);
0261 }
0262
0263 template <typename Real_v, typename Inside_v>
0264 VECCORE_ATT_HOST_DEVICE Inside_v Quadrilaterals::Inside(Vector3D<Real_v> const &point) const
0265 {
0266 return fPlanes.Inside<Real_v, Inside_v>(point);
0267 }
0268
0269 template <typename Real_v, typename Inside_v>
0270 VECCORE_ATT_HOST_DEVICE Inside_v Quadrilaterals::Inside(Vector3D<Real_v> const &point, int i) const
0271 {
0272 return fPlanes.Inside<Real_v, Inside_v>(point, i);
0273 }
0274
0275 namespace {
0276
0277 template <class Real_v>
0278 struct AcceleratedDistanceToIn {
0279 template <bool behindPlanesT>
0280 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void VectorLoop(
0281 int & , const int , Planes const & , Planes const (& )[4],
0282 Vector3D<Real_v> const & , Vector3D<Real_v> const & , Real_v & )
0283 {
0284
0285 return;
0286 }
0287 };
0288
0289 #if defined(VECGEOM_VC) && defined(VECGEOM_QUADRILATERAL_ACCELERATION)
0290 template <>
0291 struct AcceleratedDistanceToIn<Precision> {
0292
0293 template <bool behindPlanesT>
0294 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE static void VectorLoop(int &i, const int n, Planes const &planes,
0295 Planes const (&sideVectors)[4],
0296 Vector3D<Precision> const &point,
0297 Vector3D<Precision> const &direction,
0298 Precision &distance)
0299 {
0300
0301
0302 for (; i <= n - kVectorSize; i += kVectorSize) {
0303 Vector3D<VcPrecision> plane(VcPrecision(planes.GetNormals().x() + i), VcPrecision(planes.GetNormals().y() + i),
0304 VcPrecision(planes.GetNormals().z() + i));
0305 VcPrecision dPlane(&planes.GetDistances()[0] + i);
0306 VcPrecision distanceTest = plane.Dot(point) + dPlane;
0307
0308
0309 VcBool valid = Flip<behindPlanesT>::FlipSign(distanceTest) > -kTolerance;
0310 if (vecCore::MaskEmpty(valid)) continue;
0311
0312 VcPrecision directionProjection = plane.Dot(direction);
0313 valid &= Flip<!behindPlanesT>::FlipSign(directionProjection) > 0;
0314 if (vecCore::MaskEmpty(valid)) continue;
0315 VcPrecision tiny = Vc::copysign(VcPrecision(1E-20), directionProjection);
0316 distanceTest /= -(directionProjection + tiny);
0317 Vector3D<VcPrecision> intersection = Vector3D<VcPrecision>(direction) * distanceTest + point;
0318
0319 for (int j = 0; j < 4; ++j) {
0320 Vector3D<VcPrecision> sideVector(VcPrecision(sideVectors[j].GetNormals().x() + i),
0321 VcPrecision(sideVectors[j].GetNormals().y() + i),
0322 VcPrecision(sideVectors[j].GetNormals().z() + i));
0323 VcPrecision dSide(&sideVectors[j].GetDistances()[i]);
0324 valid &= sideVector.Dot(intersection) + dSide >= -kTolerance;
0325
0326 if (vecCore::MaskEmpty(valid)) goto distanceToInVcContinueOuter;
0327 }
0328
0329
0330 distanceTest(!valid) = InfinityLength<Precision>();
0331 distance = Max(distanceTest.min(), Precision(0.));
0332 i = n;
0333 return;
0334
0335 distanceToInVcContinueOuter:;
0336 }
0337 return;
0338 }
0339 };
0340 #endif
0341
0342 }
0343
0344 template <typename Real_v, bool behindPlanesT>
0345 VECCORE_ATT_HOST_DEVICE Real_v Quadrilaterals::DistanceToIn(Vector3D<Real_v> const &point,
0346 Vector3D<Real_v> const &direction) const
0347 {
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361 using Bool_v = vecCore::Mask_v<Real_v>;
0362
0363 Real_v bestDistance = InfinityLength<Real_v>();
0364
0365 int i = 0;
0366 const int n = size();
0367 AcceleratedDistanceToIn<Real_v>::template VectorLoop<behindPlanesT>(i, n, fPlanes, fSideVectors, point, direction,
0368 bestDistance);
0369
0370
0371 for (; i < n; ++i) {
0372 Vector3D<Precision> normal = fPlanes.GetNormal(i);
0373 Real_v distance = point.Dot(normal) + fPlanes.GetDistance(i);
0374
0375
0376 Bool_v valid = Flip<behindPlanesT>::FlipSign(distance) > -kTolerance;
0377 if (vecCore::MaskEmpty(valid)) continue;
0378 Real_v directionProjection = direction.Dot(normal);
0379 valid &= Flip<!behindPlanesT>::FlipSign(directionProjection) > kTolerance;
0380 if (vecCore::MaskEmpty(valid)) continue;
0381 distance /= -(directionProjection + CopySign(Real_v(1E-20), directionProjection));
0382 Vector3D<Real_v> intersection = point + direction * distance;
0383 for (int j = 0; j < 4; ++j) {
0384 valid &= intersection.Dot(fSideVectors[j].GetNormal(i)) + fSideVectors[j].GetDistances()[i] >= -kTolerance;
0385 if (vecCore::MaskEmpty(valid)) break;
0386 }
0387 vecCore::MaskedAssign(bestDistance, valid, distance);
0388
0389
0390 if (vecCore::MaskFull(bestDistance < InfinityLength<Real_v>())) break;
0391 }
0392
0393 return Max(Real_v(0.), bestDistance);
0394 }
0395
0396 namespace {
0397
0398 template <typename Real_v>
0399 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE void AcceleratedDistanceToOut(
0400 int & , const int , Planes const & , Planes const (& )[4],
0401 const Precision , const Precision , Vector3D<Real_v> const & ,
0402 Vector3D<Real_v> const & , Real_v & )
0403 {
0404
0405 return;
0406 }
0407
0408 #if defined(VECGEOM_VC) && defined(VECGEOM_QUADRILATERAL_ACCELERATION)
0409 template <>
0410 VECGEOM_FORCE_INLINE VECCORE_ATT_HOST_DEVICE void AcceleratedDistanceToOut<Precision>(
0411 int &i, const int n, Planes const &planes, Planes const (&sideVectors)[4], const Precision zMin,
0412 const Precision zMax, Vector3D<Precision> const &point, Vector3D<Precision> const &direction, Precision &distance)
0413 {
0414
0415
0416 for (; i <= n - kVectorSize; i += kVectorSize) {
0417 Vector3D<VcPrecision> plane(VcPrecision(planes.GetNormals().x() + i), VcPrecision(planes.GetNormals().y() + i),
0418 VcPrecision(planes.GetNormals().z() + i));
0419 VcPrecision dPlane(&planes.GetDistances()[0] + i);
0420 VcPrecision distanceTest = plane.Dot(point) + dPlane;
0421
0422 VcBool valid = distanceTest < kTolerance;
0423 if (vecCore::MaskEmpty(valid)) continue;
0424 VcPrecision directionProjection = plane.Dot(direction);
0425
0426
0427 valid &= directionProjection > 0;
0428 if (vecCore::MaskEmpty(valid)) continue;
0429 distanceTest /= -NonZero(directionProjection);
0430 valid &= distanceTest < distance;
0431 if (vecCore::MaskEmpty(valid)) continue;
0432
0433 if (zMin == zMax) {
0434
0435 Vector3D<VcPrecision> intersection = Vector3D<VcPrecision>(direction) * distanceTest + point;
0436 for (int j = 0; j < 4; ++j) {
0437 Vector3D<VcPrecision> sideVector(VcPrecision(sideVectors[j].GetNormals().x() + i),
0438 VcPrecision(sideVectors[j].GetNormals().y() + i),
0439 VcPrecision(sideVectors[j].GetNormals().z() + i));
0440 VcPrecision dSide(&sideVectors[j].GetDistances()[i]);
0441 valid &= sideVector.Dot(intersection) + dSide >= -kTolerance;
0442
0443 if (vecCore::MaskEmpty(valid)) goto distanceToOutVcContinueOuter;
0444 }
0445 } else {
0446 VcPrecision zProjection = distanceTest * direction[2] + point[2];
0447 valid &= zProjection >= zMin && zProjection < zMax;
0448 }
0449 distanceToOutVcContinueOuter:
0450 if (vecCore::MaskEmpty(valid)) continue;
0451 distanceTest(!valid) = InfinityLength<Precision>();
0452 distance = distanceTest.min();
0453 }
0454 distance = Max(Precision(0.), distance);
0455 return;
0456 }
0457 #endif
0458
0459 }
0460
0461 template <typename Real_v>
0462 VECCORE_ATT_HOST_DEVICE Real_v Quadrilaterals::DistanceToOut(Vector3D<Real_v> const &point,
0463 Vector3D<Real_v> const &direction, Precision zMin,
0464 Precision zMax) const
0465 {
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475 using Bool_v = vecCore::Mask_v<Real_v>;
0476
0477 Real_v bestDistance = InfinityLength<Real_v>();
0478
0479 int i = 0;
0480 const int n = size();
0481 AcceleratedDistanceToOut<Real_v>(i, n, fPlanes, fSideVectors, zMin, zMax, point, direction, bestDistance);
0482
0483 for (; i < n; ++i) {
0484 Vector3D<Precision> normal = fPlanes.GetNormal(i);
0485 Real_v distanceTest = point.Dot(normal) + fPlanes.GetDistance(i);
0486
0487 Bool_v valid = distanceTest < kTolerance;
0488 if (vecCore::MaskEmpty(valid)) continue;
0489 Real_v directionProjection = direction.Dot(normal);
0490
0491
0492 valid &= directionProjection > kTolerance;
0493 if (vecCore::MaskEmpty(valid)) continue;
0494 distanceTest /= -directionProjection;
0495 valid &= distanceTest < bestDistance && distanceTest > -kTolerance / directionProjection;
0496 if (vecCore::MaskEmpty(valid)) continue;
0497
0498
0499 if (zMin == zMax) {
0500
0501
0502 Vector3D<Real_v> intersection = point + distanceTest * direction;
0503
0504 valid = RayHitsQuadrilateral(i, intersection);
0505
0506 } else {
0507 Real_v zProjection = point[2] + distanceTest * direction[2];
0508 valid &= (zProjection >= zMin - kTolerance) && (zProjection < zMax + kTolerance);
0509 }
0510 if (vecCore::MaskEmpty(valid)) continue;
0511 vecCore::MaskedAssign(bestDistance, valid, distanceTest);
0512 }
0513
0514 if (bestDistance > -kTolerance) bestDistance = Max(bestDistance, Precision(0.));
0515 return bestDistance;
0516 }
0517
0518 template <typename Real_v>
0519 VECCORE_ATT_HOST_DEVICE Real_v Quadrilaterals::DistanceToOut(Vector3D<Real_v> const &point,
0520 Vector3D<Real_v> const &direction) const
0521 {
0522 return DistanceToOut<Real_v>(point, direction, -InfinityLength<Real_v>(), InfinityLength<Real_v>());
0523 }
0524
0525 VECCORE_ATT_HOST_DEVICE
0526 Precision Quadrilaterals::ScalarDistanceSquared(int i, Vector3D<Precision> const &point) const
0527 {
0528
0529
0530
0531
0532
0533
0534 VECGEOM_ASSERT(i < size());
0535
0536 Vector3D<Precision> planeNormal = fPlanes.GetNormal(i);
0537 Precision distance = point.Dot(planeNormal) + fPlanes.GetDistance(i);
0538
0539
0540
0541 Vector3D<Precision> intersection = point - distance * planeNormal;
0542
0543 bool withinBound[4];
0544 for (int j = 0; j < 4; ++j) {
0545
0546
0547 withinBound[j] = intersection[0] * fSideVectors[j].GetNormals().x(i) +
0548 intersection[1] * fSideVectors[j].GetNormals().y(i) +
0549 intersection[2] * fSideVectors[j].GetNormals().z(i) + fSideVectors[j].GetDistances()[i] >=
0550 0;
0551 }
0552 if (withinBound[0] && withinBound[1] && withinBound[2] && withinBound[3]) {
0553 return distance * distance;
0554 }
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586 Precision distancesq = InfinityLength<Precision>();
0587 for (int j = 0; j < 4; ++j) {
0588 if (!withinBound[j]) {
0589 distance = DistanceToLineSegmentSquared1<Precision>(fCorners[j][i], fCorners[(j + 1) % 4][i], point);
0590 if (distance < distancesq) distancesq = distance;
0591 }
0592 }
0593 return distancesq;
0594 }
0595
0596 std::ostream &operator<<(std::ostream &os, Quadrilaterals const &quads);
0597
0598 }
0599
0600 }
0601
0602 #endif