File indexing completed on 2025-01-18 10:14:10
0001
0002
0003
0004
0005
0006
0007 #ifndef VECGEOM_POLYHEDRONSTRUCT_H_
0008 #define VECGEOM_POLYHEDRONSTRUCT_H_
0009
0010 #include <ostream>
0011
0012 #include <VecCore/VecCore>
0013 #include "VecGeom/base/Vector3D.h"
0014 #include "VecGeom/volumes/Quadrilaterals.h"
0015 #include "VecGeom/volumes/Wedge_Evolution.h"
0016 #include "VecGeom/base/Array.h"
0017 #include "VecGeom/base/SOA3D.h"
0018 #include "VecGeom/volumes/TubeStruct.h"
0019
0020
0021
0022
0023 enum struct EInnerRadii { kFalse = -1, kGeneric = 0, kTrue = 1 };
0024 enum struct EPhiCutout { kFalse = -1, kGeneric = 0, kTrue = 1, kLarge = 2 };
0025
0026 namespace vecgeom {
0027
0028 VECGEOM_DEVICE_FORWARD_DECLARE(struct ZSegment;);
0029 VECGEOM_DEVICE_DECLARE_CONV(struct, ZSegment);
0030
0031
0032 namespace Polyhedron {
0033 using ::EInnerRadii;
0034 using ::EPhiCutout;
0035 }
0036
0037 inline namespace VECGEOM_IMPL_NAMESPACE {
0038
0039
0040
0041 struct ZSegment {
0042 Quadrilaterals outer;
0043 Quadrilaterals phi;
0044 Quadrilaterals inner;
0045
0046 VECCORE_ATT_HOST_DEVICE
0047 bool hasInnerRadius() const { return inner.size() > 0; }
0048 };
0049
0050
0051 template <typename T = double>
0052 struct PolyhedronStruct {
0053 int fSideCount;
0054 bool fHasInnerRadii;
0055 bool fHasPhiCutout;
0056 bool fHasLargePhiCutout;
0057 T fPhiStart;
0058 T fPhiDelta;
0059 evolution::Wedge fPhiWedge;
0060 Array<ZSegment> fZSegments;
0061 Array<T> fZPlanes;
0062 Array<T> fRMin;
0063 Array<T> fRMax;
0064 Array<bool> fSameZ;
0065 SOA3D<T> fPhiSections;
0066
0067
0068
0069 TubeStruct<T> fBoundingTube;
0070
0071
0072 T fBoundingTubeOffset;
0073
0074
0075
0076
0077 struct AreaStruct {
0078 Precision area = 0.;
0079 Precision top_area = 0.;
0080 Precision bottom_area = 0.;
0081 Precision *outer = nullptr;
0082 Precision *inner = nullptr;
0083 Precision *phi = nullptr;
0084
0085 AreaStruct(int nseg)
0086 {
0087 inner = new Precision[nseg];
0088 outer = new Precision[nseg];
0089 phi = new Precision[nseg];
0090 }
0091
0092 VECCORE_ATT_HOST_DEVICE
0093 ~AreaStruct()
0094 {
0095 delete[] inner;
0096 delete[] outer;
0097 delete[] phi;
0098 }
0099 };
0100
0101 mutable AreaStruct *fAreaStruct = nullptr;
0102 mutable Precision fCapacity = 0.;
0103
0104
0105 bool fContinuousInSlope;
0106 bool fConvexityPossible;
0107 bool fEqualRmax;
0108
0109 VECCORE_ATT_HOST_DEVICE
0110 PolyhedronStruct()
0111 : fSideCount(0), fHasInnerRadii(false), fHasPhiCutout(false), fHasLargePhiCutout(false), fPhiStart(0),
0112 fPhiDelta(0), fPhiWedge(0., 0.), fBoundingTube(0, 0, 0, 0, 0), fBoundingTubeOffset(0)
0113 {
0114 }
0115
0116 VECCORE_ATT_HOST_DEVICE
0117 PolyhedronStruct(Precision phiStart, Precision phiDelta, const int sideCount, const int zPlaneCount,
0118 Precision const zPlanes[], Precision const rMin[], Precision const rMax[])
0119 : fSideCount(sideCount), fHasInnerRadii(false), fHasPhiCutout(phiDelta < kTwoPi),
0120 fHasLargePhiCutout(phiDelta < kPi), fPhiStart(NormalizeAngle<kScalar>(phiStart)),
0121 fPhiDelta((phiDelta > kTwoPi) ? kTwoPi : phiDelta), fPhiWedge(fPhiDelta, fPhiStart),
0122 fZSegments(zPlaneCount - 1), fZPlanes(zPlaneCount), fRMin(zPlaneCount), fRMax(zPlaneCount),
0123 fPhiSections(sideCount + 1), fBoundingTube(0, 1, 1, fPhiStart, fPhiDelta), fContinuousInSlope(true),
0124 fConvexityPossible(true), fEqualRmax(true)
0125 {
0126
0127 Initialize(phiStart, phiDelta, sideCount, zPlaneCount, zPlanes, rMin, rMax);
0128 }
0129
0130 PolyhedronStruct(Precision phiStart, Precision phiDelta, const int sideCount, const int verticesCount,
0131 Precision const r[], Precision const z[])
0132 : fSideCount(sideCount), fHasInnerRadii(false), fHasPhiCutout(phiDelta < kTwoPi),
0133 fHasLargePhiCutout(phiDelta < kPi), fPhiStart(NormalizeAngle<kScalar>(phiStart)),
0134 fPhiDelta((phiDelta > kTwoPi) ? kTwoPi : phiDelta), fPhiWedge(fPhiDelta, fPhiStart), fZSegments(), fZPlanes(),
0135 fRMin(), fRMax(), fPhiSections(sideCount + 1), fBoundingTube(0, 1, 1, fPhiStart, fPhiDelta),
0136 fContinuousInSlope(true), fConvexityPossible(true), fEqualRmax(true)
0137 {
0138 if (verticesCount < 3) throw std::runtime_error("A Polyhedron needs at least 3 (rz) vertices");
0139
0140
0141
0142
0143
0144
0145 Precision area = 0;
0146 for (int i = 0; i < verticesCount; ++i) {
0147 int j = (i + 1) % verticesCount;
0148 area += r[i] * z[j] - r[j] * z[i];
0149 }
0150
0151 bool cw = (area < 0);
0152 int inc = cw ? -1 : 1;
0153 Precision zt = z[0];
0154 Precision zb = z[0];
0155
0156 for (int i = 0; i < verticesCount; ++i) {
0157 if (z[i] > zt) zt = z[i];
0158 if (z[i] < zb) zb = z[i];
0159 }
0160
0161
0162 Precision *rnew = new Precision[2 * verticesCount];
0163 Precision *znew = new Precision[2 * verticesCount];
0164 int verticesCount1 = 0;
0165 for (int i0 = 0; i0 < verticesCount; ++i0) {
0166 rnew[verticesCount1] = r[i0];
0167 znew[verticesCount1++] = z[i0];
0168
0169 if (vecCore::math::Abs(z[i0] - zt) < kTolerance || vecCore::math::Abs(z[i0] - zb) < kTolerance) {
0170 if (vecCore::math::Abs(z[i0] - z[(i0 + verticesCount - 1) % verticesCount]) > kTolerance &&
0171 vecCore::math::Abs(z[i0] - z[(i0 + 1) % verticesCount]) > kTolerance) {
0172 rnew[verticesCount1] = r[i0];
0173 znew[verticesCount1++] = z[i0];
0174 }
0175 }
0176 int i1 = (i0 + 1) % verticesCount;
0177 Precision dz = z[i1] - z[i0];
0178 if (vecCore::math::Abs(dz) < kTolerance) continue;
0179 Precision zmin = vecCore::math::Min(z[i0], z[i1]);
0180 Precision zmax = vecCore::math::Max(z[i0], z[i1]);
0181 for (int j = 0; j < verticesCount - 2; ++j) {
0182
0183 int k = (i0 - 1 - j + verticesCount) % verticesCount;
0184 if (z[k] > zmin + kTolerance && z[k] < zmax - kTolerance) {
0185
0186 Precision rp = r[i0] + (r[i1] - r[i0]) * (z[k] - z[i0]) / dz;
0187 assert(rp >= 0);
0188
0189 rnew[verticesCount1] = rp;
0190 znew[verticesCount1++] = z[k];
0191 }
0192 }
0193 }
0194
0195
0196 int i0 = -1;
0197 for (int i = 0; i < verticesCount1; ++i) {
0198 if (znew[i] == zb) {
0199 i0 = i;
0200 break;
0201 }
0202 }
0203 if (vecCore::math::Abs(zb - znew[(i0 + inc) % verticesCount1]) < kTolerance) i0 = (i0 + inc) % verticesCount1;
0204
0205 if (phiDelta > kTwoPi) phiDelta = kTwoPi;
0206 Precision sidePhi = phiDelta / sideCount;
0207 Precision cosHalfDeltaPhi = cos(0.5 * sidePhi);
0208
0209
0210
0211 int Nz = verticesCount1 / 2;
0212 Precision *rMin = new Precision[Nz];
0213 Precision *rMax = new Precision[Nz];
0214 Precision *zArg = new Precision[Nz];
0215
0216 for (int i = 0; i < Nz; ++i) {
0217
0218 int j = (i0 + verticesCount1 + inc * i) % verticesCount1;
0219 int jsim = (i0 + verticesCount1 + inc * (verticesCount1 - 1 - i)) % verticesCount1;
0220 assert(znew[j] == znew[jsim]);
0221 zArg[i] = znew[j];
0222 rMax[i] = rnew[j] * cosHalfDeltaPhi;
0223 rMin[i] = rnew[jsim] * cosHalfDeltaPhi;
0224 assert(rMax[i] >= rMin[i] &&
0225 "UnplPolycone ERROR: r[] provided has problems of the Rmax < Rmin type, please check!\n");
0226 }
0227
0228
0229 fZSegments.Allocate(Nz - 1);
0230 fZPlanes.Allocate(Nz);
0231 fRMin.Allocate(Nz);
0232 fRMax.Allocate(Nz);
0233
0234
0235 Initialize(phiStart, phiDelta, sideCount, Nz, zArg, rMin, rMax);
0236 delete[] rnew;
0237 delete[] znew;
0238 delete[] rMin;
0239 delete[] rMax;
0240 delete[] zArg;
0241 }
0242
0243 VECCORE_ATT_HOST_DEVICE
0244 ~PolyhedronStruct() { delete fAreaStruct; }
0245
0246 VECCORE_ATT_HOST_DEVICE
0247 bool CheckContinuityInSlope(const Precision rOuter[], const Precision zPlane[], const unsigned int nz)
0248 {
0249
0250 Precision prevSlope = kInfLength;
0251 for (unsigned int j = 0; j < nz - 1; ++j) {
0252 if (zPlane[j + 1] == zPlane[j]) {
0253 if (rOuter[j + 1] != rOuter[j]) return false;
0254 } else {
0255 Precision currentSlope = (rOuter[j + 1] - rOuter[j]) / (zPlane[j + 1] - zPlane[j]);
0256 if (currentSlope > prevSlope) return false;
0257 prevSlope = currentSlope;
0258 }
0259 }
0260 return true;
0261 }
0262
0263
0264
0265 VECCORE_ATT_HOST_DEVICE
0266 void Initialize(Precision phiStart, Precision phiDelta, const int sideCount, const int zPlaneCount,
0267 Precision const zPlanes[], Precision const rMin[], Precision const rMax[])
0268 {
0269 typedef Vector3D<Precision> Vec_t;
0270
0271
0272 assert(zPlaneCount > 1);
0273 assert(fSideCount > 0);
0274
0275 copy(zPlanes, zPlanes + zPlaneCount, &fZPlanes[0]);
0276 copy(rMin, rMin + zPlaneCount, &fRMin[0]);
0277 copy(rMax, rMax + zPlaneCount, &fRMax[0]);
0278 fSameZ.Allocate(zPlaneCount);
0279
0280 Precision startRmax = rMax[0];
0281 for (int i = 0; i < zPlaneCount; i++) {
0282 fConvexityPossible &= (rMin[i] == 0.);
0283 fEqualRmax &= (startRmax == rMax[i]);
0284 fSameZ[i] = false;
0285 if (i > 0 && i < zPlaneCount - 1 && fZPlanes[i] == fZPlanes[i + 1]) fSameZ[i] = true;
0286 }
0287 fContinuousInSlope = CheckContinuityInSlope(rMax, zPlanes, zPlaneCount);
0288
0289
0290
0291
0292 for (int i = 0; i < zPlaneCount - 1; ++i) {
0293
0294 assert(zPlanes[i] <= zPlanes[i + 1]);
0295
0296 bool hasInnerRadius = rMin[i] > 0 || rMin[i + 1] > 0;
0297
0298 int multiplier = (zPlanes[i] == zPlanes[i + 1] && rMax[i] == rMax[i + 1]) ? 0 : 1;
0299
0300
0301 new (&fZSegments[i].outer) Quadrilaterals(sideCount * multiplier);
0302
0303
0304 if (fHasPhiCutout) {
0305 multiplier = (zPlanes[i] == zPlanes[i + 1]) ? 0 : 1;
0306 new (&fZSegments[i].phi) Quadrilaterals(2 * multiplier, phiDelta <= kPi);
0307 }
0308
0309 multiplier = (zPlanes[i] == zPlanes[i + 1] && rMin[i] == rMin[i + 1]) ? 0 : 1;
0310
0311 if (hasInnerRadius && multiplier > 0) {
0312 new (&fZSegments[i].inner) Quadrilaterals(sideCount * multiplier);
0313 fHasInnerRadii = true;
0314 } else {
0315 new (&fZSegments[i].inner) Quadrilaterals(0);
0316 }
0317 }
0318
0319
0320 assert(phiDelta > 0);
0321 phiStart = NormalizeAngle<kScalar>(phiStart);
0322 if (phiDelta > kTwoPi) phiDelta = kTwoPi;
0323 Precision sidePhi = phiDelta / sideCount;
0324
0325 auto getPhi = [&](int side) {
0326 if (!fHasPhiCutout && side == sideCount) {
0327 side = 0;
0328 }
0329 return NormalizeAngle<kScalar>(phiStart + side * sidePhi);
0330 };
0331
0332 for (int i = 0, iMax = sideCount + 1; i < iMax; ++i) {
0333 Vector3D<Precision> cornerVector = Vec_t::FromCylindrical(1., getPhi(i), 0).Normalized().FixZeroes();
0334 fPhiSections.set(i, cornerVector.Normalized().Cross(Vector3D<Precision>(0, 0, -1)));
0335 }
0336
0337
0338
0339 Precision cosHalfDeltaPhi = cos(0.5 * sidePhi);
0340 Precision innerRadius = kInfLength, outerRadius = -kInfLength;
0341 for (int i = 0; i < zPlaneCount; ++i) {
0342
0343 if (rMin[i] < innerRadius) innerRadius = rMin[i];
0344
0345
0346 assert(rMin[i] >= 0 && rMax[i] >= 0);
0347
0348 if (rMax[i] > outerRadius) outerRadius = rMax[i];
0349 }
0350
0351
0352 outerRadius /= cosHalfDeltaPhi;
0353
0354
0355 Precision boundingTubeZ = 0.5 * (zPlanes[zPlaneCount - 1] - zPlanes[0]) + kTolerance;
0356
0357 const Precision kPhiTolerance = 100 * kTolerance;
0358
0359
0360
0361 Precision boundsPhiStart = !fHasPhiCutout ? 0 : phiStart - kPhiTolerance;
0362 Precision boundsPhiDelta = !fHasPhiCutout ? kTwoPi : phiDelta + 2 * kPhiTolerance;
0363
0364
0365
0366
0367 fBoundingTube = TubeStruct<Precision>(innerRadius - kHalfTolerance, outerRadius + kHalfTolerance, boundingTubeZ,
0368 boundsPhiStart, boundsPhiDelta);
0369
0370
0371 fBoundingTubeOffset = 0.5 * (zPlanes[0] + zPlanes[zPlaneCount - 1]);
0372
0373 auto getVertexImpl = [&](Precision const r[], int i, int j) {
0374 if (!fHasPhiCutout && j == sideCount) {
0375 j = 0;
0376 }
0377 return Vec_t::FromCylindrical(r[i] / cosHalfDeltaPhi, getPhi(j), zPlanes[i]).FixZeroes();
0378 };
0379
0380 auto getInnerVertex = [&](int i, int j) { return getVertexImpl(rMin, i, j); };
0381 auto getOuterVertex = [&](int i, int j) { return getVertexImpl(rMax, i, j); };
0382
0383
0384 for (int iPlane = 0; iPlane < zPlaneCount - 1; ++iPlane) {
0385
0386 auto WrongNormal = [](Vector3D<Precision> const &normal, Vector3D<Precision> const &corner) {
0387 return normal[0] * corner[0] + normal[1] * corner[1] < 0;
0388 };
0389
0390
0391 for (int iSide = 0; iSide < fZSegments[iPlane].outer.size(); ++iSide) {
0392 fZSegments[iPlane].outer.Set(iSide, getOuterVertex(iPlane, iSide), getOuterVertex(iPlane, iSide + 1),
0393 getOuterVertex(iPlane + 1, iSide + 1), getOuterVertex(iPlane + 1, iSide));
0394
0395 if (WrongNormal(fZSegments[iPlane].outer.GetNormal(iSide), getOuterVertex(iPlane, iSide))) {
0396 fZSegments[iPlane].outer.FlipSign(iSide);
0397 }
0398 }
0399 for (int iSide = 0; iSide < fZSegments[iPlane].inner.size(); ++iSide) {
0400 fZSegments[iPlane].inner.Set(iSide, getInnerVertex(iPlane, iSide), getInnerVertex(iPlane, iSide + 1),
0401 getInnerVertex(iPlane + 1, iSide + 1), getInnerVertex(iPlane + 1, iSide));
0402
0403 if (WrongNormal(fZSegments[iPlane].inner.GetNormal(iSide), getInnerVertex(iPlane, iSide))) {
0404 fZSegments[iPlane].inner.FlipSign(iSide);
0405 }
0406 }
0407
0408 if (fHasPhiCutout && fZSegments[iPlane].phi.size() == 2) {
0409
0410
0411
0412 fZSegments[iPlane].phi.Set(0, getInnerVertex(iPlane, 0), getInnerVertex(iPlane + 1, 0),
0413 getOuterVertex(iPlane + 1, 0), getOuterVertex(iPlane, 0));
0414
0415 if (fZSegments[iPlane].phi.GetNormal(0).Dot(fPhiSections[0]) > 0) {
0416 fZSegments[iPlane].phi.FlipSign(0);
0417 }
0418 fZSegments[iPlane].phi.Set(1, getOuterVertex(iPlane, sideCount), getOuterVertex(iPlane + 1, sideCount),
0419 getInnerVertex(iPlane + 1, sideCount), getInnerVertex(iPlane, sideCount));
0420
0421 if (fZSegments[iPlane].phi.GetNormal(1).Dot(fPhiSections[fSideCount]) < 0) {
0422 fZSegments[iPlane].phi.FlipSign(1);
0423 }
0424 }
0425
0426 }
0427 }
0428 };
0429 }
0430 }
0431
0432 #endif