File indexing completed on 2025-01-18 10:14:00
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #ifndef VECGEOM_VOLUMES_KERNEL_CONEIMPLEMENTATION_H_
0014 #define VECGEOM_VOLUMES_KERNEL_CONEIMPLEMENTATION_H_
0015
0016 #include "VecGeom/base/Vector3D.h"
0017 #include "VecGeom/volumes/kernel/GenericKernels.h"
0018 #include "VecGeom/volumes/kernel/shapetypes/ConeTypes.h"
0019 #include "VecGeom/volumes/ConeStruct.h"
0020 #include <cstdio>
0021 #include "VecGeom/volumes/ConeUtilities.h"
0022
0023 namespace vecgeom {
0024
0025 VECGEOM_DEVICE_DECLARE_CONV_TEMPLATE(struct, ConeImplementation, typename);
0026
0027 inline namespace VECGEOM_IMPL_NAMESPACE {
0028
0029 template <typename T>
0030 class SPlacedCone;
0031 template <typename T>
0032 class SUnplacedCone;
0033
0034 template <typename coneTypeT>
0035 struct ConeImplementation {
0036
0037 using UnplacedStruct_t = ConeStruct<Precision>;
0038 using UnplacedVolume_t = SUnplacedCone<coneTypeT>;
0039 using PlacedShape_t = SPlacedCone<UnplacedVolume_t>;
0040
0041 VECCORE_ATT_HOST_DEVICE
0042 static void PrintType() {}
0043
0044 template <typename Stream>
0045 static void PrintType(Stream &s, int transCodeT = translation::kGeneric, int rotCodeT = rotation::kGeneric)
0046 {
0047 s << "SpecializedCone<" << transCodeT << "," << rotCodeT << ">";
0048 }
0049
0050 template <typename Stream>
0051 static void PrintImplementationType(Stream & )
0052 {
0053 }
0054
0055 template <typename Stream>
0056 static void PrintUnplacedType(Stream & )
0057 {
0058 }
0059
0060
0061
0062
0063
0064
0065 template <typename Real_v, bool ForInnerSurface, bool ForLowerZ>
0066 VECGEOM_FORCE_INLINE
0067 VECCORE_ATT_HOST_DEVICE
0068 static typename vecCore::Mask_v<Real_v> IsOnRing(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point)
0069 {
0070 using Bool_v = typename vecCore::Mask_v<Real_v>;
0071
0072 Real_v rad2 = point.Perp2();
0073 Bool_v onRing(false);
0074
0075 if (ForLowerZ) {
0076 if (ForInnerSurface) {
0077 onRing = (rad2 <= MakePlusTolerantSquare<true>(cone.fRmin1)) &&
0078 (rad2 >= MakeMinusTolerantSquare<true>(cone.fRmin1));
0079 } else {
0080 onRing = (rad2 <= MakePlusTolerantSquare<true>(cone.fRmax1)) &&
0081 (rad2 >= MakeMinusTolerantSquare<true>(cone.fRmax1));
0082 }
0083 } else {
0084 if (ForInnerSurface) {
0085 onRing = (rad2 <= MakePlusTolerantSquare<true>(cone.fRmin2)) &&
0086 (rad2 >= MakeMinusTolerantSquare<true>(cone.fRmin2));
0087 } else {
0088 onRing = (rad2 <= MakePlusTolerantSquare<true>(cone.fRmax2)) &&
0089 (rad2 >= MakeMinusTolerantSquare<true>(cone.fRmax2));
0090 }
0091 }
0092
0093 return onRing;
0094 }
0095
0096 template <typename Real_v>
0097 VECGEOM_FORCE_INLINE
0098 VECCORE_ATT_HOST_DEVICE
0099 static void Contains(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point,
0100 typename vecCore::Mask_v<Real_v> &inside)
0101 {
0102 typedef typename vecCore::Mask_v<Real_v> Bool_v;
0103 Bool_v unused(false);
0104 Bool_v outside(false);
0105 ConeHelpers<Real_v, coneTypeT>::template GenericKernelForContainsAndInside<false>(cone, point, unused, outside);
0106 inside = !outside;
0107 }
0108
0109 template <typename Real_v, typename Inside_v>
0110 VECGEOM_FORCE_INLINE
0111 VECCORE_ATT_HOST_DEVICE
0112 static void Inside(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point, Inside_v &inside)
0113 {
0114 ConeHelpers<Real_v, coneTypeT>::template Inside<Inside_v>(cone, point, inside);
0115 }
0116
0117 template <typename Real_v>
0118 VECGEOM_FORCE_INLINE
0119 VECCORE_ATT_HOST_DEVICE
0120 static void DistanceToIn(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point, Vector3D<Real_v> const &dir,
0121 Real_v const & , Real_v &distance)
0122 {
0123 using namespace ConeUtilities;
0124 using namespace ConeTypes;
0125 typedef Real_v Float_t;
0126 typedef typename vecCore::Mask_v<Real_v> Bool_t;
0127
0128 Bool_t done(false);
0129 const Real_v zero(0.);
0130
0131
0132 distance = kInfLength;
0133
0134
0135 Float_t distz = Abs(point.z()) - cone.fDz;
0136 Bool_t outZAndGoingOut = (distz > kConeTolerance && (point.z() * dir.z()) >= zero) ||
0137 (Abs(distz) < kConeTolerance && (point.z() * dir.z()) > zero);
0138 done |= outZAndGoingOut;
0139 if (vecCore::MaskFull(done)) return;
0140
0141
0142 Float_t outerRad = GetRadiusOfConeAtPoint<Real_v, false>(cone, point.z());
0143 Float_t rsq = point.Perp2();
0144 done |= (rsq > MakeMinusTolerantSquare<true>(outerRad, cone.fOuterTolerance))
0145 && (dir.Dot(GetNormal<Real_v, false>(cone, point)) >= zero);
0146 if (vecCore::MaskFull(done)) return;
0147
0148
0149 vecCore__MaskedAssignFunc(distance, !done, Float_t(-1.0));
0150
0151
0152 Bool_t inside = distz < -kConeTolerance;
0153
0154 inside &= rsq < MakeMinusTolerantSquare<true>(outerRad, cone.fOuterTolerance);
0155
0156 if (checkRminTreatment<coneTypeT>(cone)) {
0157 Float_t innerRad = GetRadiusOfConeAtPoint<Real_v, true>(cone, point.z());
0158 inside &= rsq > MakePlusTolerantSquare<true>(innerRad, cone.fInnerTolerance);
0159 }
0160 if (checkPhiTreatment<coneTypeT>(cone)) {
0161 Bool_t insector;
0162 PointInCyclicalSector<Real_v, coneTypeT, false, false>(cone, point.x(), point.y(), insector);
0163 inside &= insector;
0164 }
0165 done |= inside;
0166 if (vecCore::MaskFull(done)) return;
0167
0168
0169
0170 vecCore__MaskedAssignFunc(distance, !done, Float_t(kInfLength));
0171
0172 distz /= NonZero(Abs(dir.z()));
0173
0174
0175 #ifdef EDGE_POINTS
0176 Bool_t onZsurf = (Abs(point.z()) - cone.fDz) < Real_v(kConeTolerance);
0177 Bool_t onLoZSrf = onZsurf && point.z() < zero;
0178 Bool_t onHiZSrf = onZsurf && point.z() > zero;
0179 Bool_t loZcond = onLoZSrf && (IsOnRing<Real_v, false, true>(cone, point));
0180 Bool_t hiZcond = onHiZSrf && (IsOnRing<Real_v, false,false>(cone, point));
0181 if (checkRminTreatment<coneTypeT>(cone)) {
0182 loZcond |= onLoZSrf && IsOnRing<Real_v, true, true>(cone, point);
0183 hiZcond |= onHiZSrf && IsOnRing<Real_v, true,false>(cone, point);
0184 }
0185 vecCore::MaskedAssign(distz, loZcond || hiZcond, zero);
0186 #endif
0187
0188
0189 Float_t hitx = point.x() + distz * dir.x();
0190 Float_t hity = point.y() + distz * dir.y();
0191
0192 Float_t r2 = (hitx * hitx) + (hity * hity);
0193
0194 Precision innerZTol = cone.fTolIz;
0195 Bool_t isHittingTopPlane = (point.z() >= innerZTol) && (r2 <= cone.fSqRmax2 + kTolerance);
0196 Bool_t isHittingBottomPlane = (point.z() <= -innerZTol) && (r2 <= cone.fSqRmax1 + kTolerance);
0197 Bool_t okz = (isHittingTopPlane || isHittingBottomPlane);
0198
0199 if (checkRminTreatment<coneTypeT>(cone)) {
0200 isHittingTopPlane &= (r2 >= cone.fSqRmin2 - kTolerance);
0201 isHittingBottomPlane &= (r2 >= cone.fSqRmin1 - kTolerance);
0202 okz &= ((isHittingTopPlane || isHittingBottomPlane));
0203 }
0204
0205 if (checkPhiTreatment<coneTypeT>(cone)) {
0206 Bool_t insector;
0207 PointInCyclicalSector<Real_v, coneTypeT, false>(cone, hitx, hity, insector);
0208 okz &= insector;
0209 }
0210 vecCore::MaskedAssign(distance, !done && okz, distz);
0211 done |= okz;
0212 if (vecCore::MaskFull(done)) return;
0213
0214 Float_t dist_rOuter(kInfLength);
0215 Bool_t ok_outerCone =
0216 ConeHelpers<Real_v, coneTypeT>::template DetectIntersectionAndCalculateDistanceToConicalSurface<true, false>(
0217 cone, point, dir, dist_rOuter);
0218 ok_outerCone &= dist_rOuter < distance;
0219 vecCore::MaskedAssign(distance, !done && ok_outerCone, dist_rOuter);
0220 done |= ok_outerCone;
0221 if (vecCore::MaskFull(done)) return;
0222
0223 Float_t dist_rInner(kInfLength);
0224 if (checkRminTreatment<coneTypeT>(cone)) {
0225
0226 Bool_t ok_innerCone =
0227 ConeHelpers<Real_v, coneTypeT>::template DetectIntersectionAndCalculateDistanceToConicalSurface<true, true>(
0228 cone, point, dir, dist_rInner);
0229 ok_innerCone &= dist_rInner < distance;
0230 vecCore::MaskedAssign(distance, !done && ok_innerCone, dist_rInner);
0231 }
0232
0233 if (checkPhiTreatment<coneTypeT>(cone)) {
0234
0235 evolution::Wedge const &w = cone.fPhiWedge;
0236
0237 Float_t dist_phi;
0238 Bool_t ok_phi;
0239 PhiPlaneTrajectoryIntersection<Real_v, coneTypeT, SectorType<coneTypeT>::value != kOnePi, true>(
0240 cone.fAlongPhi1x, cone.fAlongPhi1y, w.GetNormal1().x(), w.GetNormal1().y(), cone, point, dir, dist_phi,
0241 ok_phi);
0242 ok_phi &= dist_phi < distance;
0243 vecCore::MaskedAssign(distance, !done && ok_phi, dist_phi);
0244 done |= ok_phi;
0245
0246 if (SectorType<coneTypeT>::value != kOnePi) {
0247
0248 PhiPlaneTrajectoryIntersection<Real_v, coneTypeT, true, true>(cone.fAlongPhi2x, cone.fAlongPhi2y,
0249 w.GetNormal2().x(), w.GetNormal2().y(), cone,
0250 point, dir, dist_phi, ok_phi);
0251
0252 vecCore::MaskedAssign(distance, ok_phi && dist_phi < distance, dist_phi);
0253 }
0254 }
0255 }
0256
0257 template <typename Real_v>
0258 VECGEOM_FORCE_INLINE
0259 VECCORE_ATT_HOST_DEVICE
0260 static void DistanceToOut(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point,
0261 Vector3D<Real_v> const &direction, Real_v const & , Real_v &distance)
0262 {
0263
0264 distance = kInfLength;
0265 using namespace ConeUtilities;
0266 using namespace ConeTypes;
0267
0268 typedef typename vecCore::Mask_v<Real_v> Bool_t;
0269
0270 Bool_t done(false);
0271 const Real_v zero(0.0);
0272
0273
0274 Real_v distz = Abs(point.z()) - cone.fDz;
0275
0276
0277 vecCore__MaskedAssignFunc(distance, !done, Real_v(-1.0));
0278
0279 Bool_t outside = distz > Real_v(kConeTolerance);
0280
0281 Real_v rsq = point.Perp2();
0282 Real_v outerRad = ConeUtilities::GetRadiusOfConeAtPoint<Real_v, false>(cone, point.z());
0283 outside |= rsq > MakePlusTolerantSquare<true>(outerRad, cone.fOuterTolerance);
0284 done |= outside;
0285 if (vecCore::MaskFull(done)) return;
0286
0287 if (checkRminTreatment<coneTypeT>(cone) && !vecCore::MaskFull(outside)) {
0288 Real_v innerRad = ConeUtilities::GetRadiusOfConeAtPoint<Real_v, true>(cone, point.z());
0289 outside |= rsq < MakeMinusTolerantSquare<true>(innerRad, cone.fInnerTolerance);
0290 done |= outside;
0291 if (vecCore::MaskFull(done)) return;
0292 }
0293 if (checkPhiTreatment<coneTypeT>(cone) && !vecCore::MaskEmpty(outside)) {
0294 Bool_t insector;
0295 ConeUtilities::PointInCyclicalSector<Real_v, coneTypeT, false, false>(cone, point.x(), point.y(), insector);
0296 outside |= !insector;
0297 }
0298 done |= outside;
0299 if (vecCore::MaskFull(done)) return;
0300
0301
0302 Bool_t isGoingUp = direction.z() > zero;
0303 Bool_t isGoingDown = direction.z() < zero;
0304 Bool_t isOnZPlaneAndMovingOutside(false);
0305 isOnZPlaneAndMovingOutside = !outside && ((isGoingUp && point.z() > zero && Abs(distz) < kConeTolerance) ||
0306 (isGoingDown && point.z() < zero && Abs(distz) < kConeTolerance));
0307 vecCore__MaskedAssignFunc(distance, !done && isOnZPlaneAndMovingOutside, distz);
0308 done |= isOnZPlaneAndMovingOutside;
0309 if (vecCore::MaskFull(done)) return;
0310
0311
0312
0313
0314 vecCore__MaskedAssignFunc(distance, !done, Real_v(kInfLength));
0315
0316 Precision fDz = cone.fDz;
0317 Real_v dirZInv = Real_v(1.) / NonZero(direction.z());
0318 vecCore__MaskedAssignFunc(distance, isGoingUp, (fDz - point.z()) * dirZInv);
0319 vecCore__MaskedAssignFunc(distance, isGoingDown, (-fDz - point.z()) * dirZInv);
0320
0321 Real_v dist_rOuter(kInfLength);
0322 Bool_t ok_outerCone =
0323 ConeHelpers<Real_v, coneTypeT>::template DetectIntersectionAndCalculateDistanceToConicalSurface<false, false>(
0324 cone, point, direction, dist_rOuter);
0325
0326 vecCore::MaskedAssign(distance, !done && ok_outerCone && dist_rOuter < distance, dist_rOuter);
0327
0328 Real_v dist_rInner(kInfLength);
0329 if (checkRminTreatment<coneTypeT>(cone)) {
0330 Bool_t ok_innerCone =
0331 ConeHelpers<Real_v, coneTypeT>::template DetectIntersectionAndCalculateDistanceToConicalSurface<false, true>(
0332 cone, point, direction, dist_rInner);
0333 vecCore::MaskedAssign(distance, !done && ok_innerCone && dist_rInner < distance, dist_rInner);
0334 }
0335
0336 if (checkPhiTreatment<coneTypeT>(cone)) {
0337
0338 Bool_t isOnStartPhi = ConeUtilities::IsOnStartPhi<Real_v>(cone, point);
0339 Bool_t isOnEndPhi = ConeUtilities::IsOnEndPhi<Real_v>(cone, point);
0340 Vector3D<Real_v> normal1 = cone.fPhiWedge.GetNormal1();
0341 Vector3D<Real_v> normal2 = cone.fPhiWedge.GetNormal2();
0342 Bool_t cond = (isOnStartPhi && direction.Dot(-normal1) > zero) ||
0343 (isOnEndPhi && direction.Dot(-normal2) > zero);
0344 vecCore__MaskedAssignFunc(distance, !done && cond, zero);
0345 done |= cond;
0346 if (vecCore::MaskFull(done)) return;
0347
0348 Real_v dist_phi;
0349 Bool_t ok_phi;
0350 evolution::Wedge const &w = cone.fPhiWedge;
0351 PhiPlaneTrajectoryIntersection<Real_v, coneTypeT, SectorType<coneTypeT>::value != kOnePi, false>(
0352 cone.fAlongPhi1x, cone.fAlongPhi1y, w.GetNormal1().x(), w.GetNormal1().y(), cone, point, direction, dist_phi,
0353 ok_phi);
0354 ok_phi &= dist_phi < distance;
0355 vecCore::MaskedAssign(distance, !done && ok_phi, dist_phi);
0356 done |= ok_phi;
0357
0358 if (SectorType<coneTypeT>::value != kOnePi) {
0359 ConeUtilities::PhiPlaneTrajectoryIntersection<Real_v, coneTypeT, true, false>(
0360 cone.fAlongPhi2x, cone.fAlongPhi2y, w.GetNormal2().x(), w.GetNormal2().y(), cone, point, direction,
0361 dist_phi, ok_phi);
0362 vecCore::MaskedAssign(distance, ok_phi && dist_phi < distance, dist_phi);
0363 }
0364 }
0365 vecCore__MaskedAssignFunc(distance, distance < zero && Abs(distance) < kTolerance, zero);
0366 }
0367
0368 template <typename Real_v>
0369 VECGEOM_FORCE_INLINE
0370 VECCORE_ATT_HOST_DEVICE
0371 static void SafetyToIn(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point, Real_v &safety)
0372 {
0373 using namespace ConeUtilities;
0374 using namespace ConeTypes;
0375 safety = -kInfLength;
0376 typedef typename vecCore::Mask_v<Real_v> Bool_t;
0377 typedef Real_v Float_t;
0378
0379 Bool_t done(false);
0380 Precision fDz = cone.fDz;
0381 Float_t distz = Abs(point.z()) - fDz;
0382
0383
0384
0385 vecCore__MaskedAssignFunc(safety, !done, Float_t(-1.0));
0386
0387
0388 Bool_t inside = distz < -kConeTolerance;
0389
0390
0391
0392 Float_t outerRad = GetRadiusOfConeAtPoint<Real_v, false>(cone, point.z());
0393 Float_t rsq = point.Perp2();
0394 inside &= rsq < MakeMinusTolerantSquare<true>(outerRad, cone.fOuterTolerance);
0395
0396 if (checkRminTreatment<coneTypeT>(cone)) {
0397 Float_t innerRad = GetRadiusOfConeAtPoint<Real_v, true>(cone, point.z());
0398 inside &= rsq > MakePlusTolerantSquare<true>(innerRad, cone.fInnerTolerance);
0399 }
0400 if (checkPhiTreatment<coneTypeT>(cone) && !vecCore::MaskEmpty(inside)) {
0401 Bool_t insector;
0402 PointInCyclicalSector<Real_v, coneTypeT, false, false>(cone, point.x(), point.y(), insector);
0403 inside &= insector;
0404 }
0405 done |= inside;
0406 if (vecCore::MaskFull(done)) return;
0407
0408
0409
0410
0411 vecCore__MaskedAssignFunc(safety, !done, Float_t(0.));
0412
0413
0414
0415
0416 Float_t safeZ = Abs(point.z()) - fDz;
0417 Float_t safeDistOuterSurface = -SafeDistanceToConicalSurface<Real_v, false>(cone, point);
0418
0419 Float_t safeDistInnerSurface(-kInfLength);
0420 if (checkRminTreatment<coneTypeT>(cone)) {
0421 safeDistInnerSurface = -SafeDistanceToConicalSurface<Real_v, true>(cone, point);
0422 }
0423
0424 vecCore__MaskedAssignFunc(safety, !done, Max(safeZ, Max(safeDistOuterSurface, safeDistInnerSurface)));
0425
0426 if (checkPhiTreatment<coneTypeT>(cone)) {
0427 Float_t safetyPhi = cone.fPhiWedge.SafetyToIn<Real_v>(point);
0428 vecCore__MaskedAssignFunc(safety, !done, Max(safetyPhi, safety));
0429 }
0430
0431 vecCore__MaskedAssignFunc(safety, vecCore::math::Abs(safety) < kTolerance, Float_t(0.));
0432 }
0433
0434 template <typename Real_v>
0435 VECGEOM_FORCE_INLINE
0436 VECCORE_ATT_HOST_DEVICE
0437 static void SafetyToOut(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point, Real_v &safety)
0438 {
0439
0440 using namespace ConeUtilities;
0441 using namespace ConeTypes;
0442 safety = kInfLength;
0443 typedef typename vecCore::Mask_v<Real_v> Bool_t;
0444 typedef Real_v Float_t;
0445
0446 Bool_t done(false);
0447
0448 Float_t distz = Abs(point.z()) - cone.fDz;
0449 Float_t rsq = point.Perp2();
0450
0451
0452 vecCore__MaskedAssignFunc(safety, !done, Float_t(-1.0));
0453
0454
0455
0456 Bool_t outside = distz > Real_v(kConeTolerance);
0457
0458 Float_t outerRad = GetRadiusOfConeAtPoint<Real_v, false>(cone, point.z());
0459 outside |= rsq > MakePlusTolerantSquare<true>(outerRad, cone.fOuterTolerance);
0460
0461 if (checkRminTreatment<coneTypeT>(cone)) {
0462 Float_t innerRad = GetRadiusOfConeAtPoint<Real_v, true>(cone, point.z());
0463 outside |= rsq < MakeMinusTolerantSquare<true>(innerRad, cone.fInnerTolerance);
0464 }
0465
0466 if (checkPhiTreatment<coneTypeT>(cone) && !vecCore::MaskEmpty(outside)) {
0467
0468 Bool_t insector;
0469 ConeUtilities::PointInCyclicalSector<Real_v, coneTypeT, false, false>(cone, point.x(), point.y(), insector);
0470 outside |= !insector;
0471 }
0472 done |= outside;
0473 if (vecCore::MaskFull(done)) return;
0474
0475
0476
0477
0478 vecCore__MaskedAssignFunc(safety, !done, Float_t(0.));
0479
0480
0481
0482
0483 Precision fDz = cone.fDz;
0484 Float_t safeZ = fDz - Abs(point.z());
0485
0486 Float_t safeDistOuterSurface = SafeDistanceToConicalSurface<Real_v, false>(cone, point);
0487 Float_t safeDistInnerSurface(kInfLength);
0488 if (checkRminTreatment<coneTypeT>(cone)) {
0489 safeDistInnerSurface = SafeDistanceToConicalSurface<Real_v, true>(cone, point);
0490 }
0491
0492 vecCore__MaskedAssignFunc(safety, !done, Min(safeZ, Min(safeDistOuterSurface, safeDistInnerSurface)));
0493
0494 if (checkPhiTreatment<coneTypeT>(cone)) {
0495 Float_t safetyPhi = cone.fPhiWedge.SafetyToOut<Real_v>(point);
0496 vecCore__MaskedAssignFunc(safety, !done, Min(safetyPhi, safety));
0497 }
0498 vecCore__MaskedAssignFunc(safety, vecCore::math::Abs(safety) < kTolerance, Float_t(0.));
0499 }
0500
0501 template <typename Real_v, bool ForInnerSurface>
0502 VECGEOM_FORCE_INLINE
0503 VECCORE_ATT_HOST_DEVICE
0504 static Real_v SafeDistanceToConicalSurface(UnplacedStruct_t const &cone, Vector3D<Real_v> const &point)
0505 {
0506
0507 typedef Real_v Float_t;
0508 Float_t rho = point.Perp();
0509 if (ForInnerSurface) {
0510 Float_t pRMin = cone.fTanRMin * point.z() + (cone.fRmin1 + cone.fRmin2) * Float_t(0.5);
0511 return (rho - pRMin) * cone.fInvSecRMin;
0512 } else {
0513 Float_t pRMax = cone.fTanRMax * point.z() + (cone.fRmax1 + cone.fRmax2) * Float_t(0.5);
0514 return (pRMax - rho) * cone.fInvSecRMax;
0515 }
0516 }
0517 };
0518 }
0519 }
0520
0521 #endif