Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 09:03:16

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file orange/orangeinp/IntersectRegion.hh
0006 //! \brief Contains IntersectRegionInterface and concrete daughters
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include "corecel/Macros.hh"
0011 #include "corecel/cont/Array.hh"
0012 #include "corecel/math/Turn.hh"
0013 #include "orange/OrangeTypes.hh"
0014 
0015 namespace celeritas
0016 {
0017 struct JsonPimpl;
0018 
0019 namespace orangeinp
0020 {
0021 class IntersectSurfaceBuilder;
0022 
0023 //---------------------------------------------------------------------------//
0024 /*!
0025  * Interface class for building non-reentrant spatial regions.
0026  *
0027  * This is a building block for constructing more complex objects out of
0028  * smaller spatial regions. A \c shape object will have a single intersect
0029  * region, and a \c solid object region may have multiple adjacent intersect
0030  * regions.
0031  *
0032  * Convex regions should be as minimal as possible and rely on transformations
0033  * to change axes, displacement, etc. As a general rule, the exterior bounding
0034  * box of a intersect region should be <em>centered on the origin</em>, and
0035  * objects should be aligned along the \em z axis.
0036  *
0037  * When implementing this class, prefer to build simpler surfaces (planes)
0038  * before complex ones (cones) in case we implement short-circuiting logic,
0039  * since expressions are currently sorted.
0040  *
0041  * \note Additional methods such as volume calculation may be added here later.
0042  */
0043 class IntersectRegionInterface
0044 {
0045   public:
0046     //! Construct surfaces that are AND-ed into this region
0047     virtual void build(IntersectSurfaceBuilder&) const = 0;
0048 
0049     //! Write the region to a JSON object
0050     virtual void output(JsonPimpl*) const = 0;
0051 
0052     virtual ~IntersectRegionInterface() = default;
0053 
0054   protected:
0055     //!@{
0056     //! Allow construction and assignment only through daughter classes
0057     IntersectRegionInterface() = default;
0058     CELER_DEFAULT_COPY_MOVE(IntersectRegionInterface);
0059     //!@}
0060 };
0061 
0062 //---------------------------------------------------------------------------//
0063 /*!
0064  * A rectangular parallelepiped/cuboid centered on the origin.
0065  *
0066  * The box is constructed with half-widths.
0067  */
0068 class Box final : public IntersectRegionInterface
0069 {
0070   public:
0071     // Construct with half-widths
0072     explicit Box(Real3 const& halfwidths);
0073 
0074     // Build surfaces
0075     void build(IntersectSurfaceBuilder&) const final;
0076 
0077     // Output to JSON
0078     void output(JsonPimpl*) const final;
0079 
0080     //// ACCESSORS ////
0081 
0082     //! Half-width for each axis
0083     Real3 const& halfwidths() const { return hw_; }
0084 
0085   private:
0086     Real3 hw_;
0087 };
0088 
0089 //---------------------------------------------------------------------------//
0090 /*!
0091  * A closed truncated cone along the *z* axis centered on the origin.
0092  *
0093  * A quadric cone technically defines two opposing cones that touch at a single
0094  * vanishing point, but this cone is required to be truncated so that the
0095  * vanishing point is on our outside the cone.
0096  *
0097  * The midpoint along the \em z axis of the cone is the origin. A cone is \em
0098  * not allowed to have equal radii: for that, use a cylinder. However, it \em
0099  * may have a single radius of zero, which puts the vanishing point on one end
0100  * of the cone.
0101  *
0102  * This intersect region, along with the Cylinder, is a base component of the
0103  * G4Polycone (PCON).
0104  */
0105 class Cone final : public IntersectRegionInterface
0106 {
0107   public:
0108     // Construct with Z half-height and lo, hi radii
0109     Cone(Real2 const& radii, real_type halfheight);
0110 
0111     //// INTERFACE ////
0112 
0113     // Build surfaces
0114     void build(IntersectSurfaceBuilder&) const final;
0115 
0116     // Output to JSON
0117     void output(JsonPimpl*) const final;
0118 
0119     //// TEMPLATE INTERFACE ////
0120 
0121     // Whether this encloses another cone
0122     bool encloses(Cone const& other) const;
0123 
0124     //// ACCESSORS ////
0125 
0126     //! Lower and upper radii
0127     Real2 const& radii() const { return radii_; }
0128 
0129     //! Half-height along Z
0130     real_type halfheight() const { return hh_; }
0131 
0132   private:
0133     Real2 radii_;
0134     real_type hh_;
0135 };
0136 
0137 //---------------------------------------------------------------------------//
0138 /*!
0139  * A *z*-aligned cylinder centered on the origin.
0140  *
0141  * The cylinder is defined with a radius and half-height.
0142  */
0143 class Cylinder final : public IntersectRegionInterface
0144 {
0145   public:
0146     // Construct with radius
0147     Cylinder(real_type radius, real_type halfheight);
0148 
0149     // Build surfaces
0150     void build(IntersectSurfaceBuilder&) const final;
0151 
0152     // Output to JSON
0153     void output(JsonPimpl*) const final;
0154 
0155     //// TEMPLATE INTERFACE ////
0156 
0157     // Whether this encloses another cylinder
0158     bool encloses(Cylinder const& other) const;
0159 
0160     //// ACCESSORS ////
0161 
0162     //! Radius
0163     real_type radius() const { return radius_; }
0164 
0165     //! Half-height along Z
0166     real_type halfheight() const { return hh_; }
0167 
0168   private:
0169     real_type radius_;
0170     real_type hh_;
0171 };
0172 
0173 //---------------------------------------------------------------------------//
0174 /*!
0175  * An axis-alligned ellipsoid centered at the origin.
0176  *
0177  * The ellipsoid is constructed with the three radial lengths.
0178  */
0179 class Ellipsoid final : public IntersectRegionInterface
0180 {
0181   public:
0182     // Construct with radius
0183     explicit Ellipsoid(Real3 const& radii);
0184 
0185     // Build surfaces
0186     void build(IntersectSurfaceBuilder&) const final;
0187 
0188     // Output to JSON
0189     void output(JsonPimpl*) const final;
0190 
0191     //// TEMPLATE INTERFACE ////
0192 
0193     // Whether this encloses another ellipsoid
0194     bool encloses(Ellipsoid const& other) const;
0195 
0196     //// ACCESSORS ////
0197 
0198     //! Radius along each axis
0199     Real3 const& radii() const { return radii_; }
0200 
0201   private:
0202     Real3 radii_;
0203 };
0204 
0205 //---------------------------------------------------------------------------//
0206 /*!
0207  * A *z*-aligned cylinder with an elliptical cross section.
0208  *
0209  * The elliptical cylinder is defined with a two radii and a half-height,
0210  * such that the centroid of the bounding box is origin.
0211  */
0212 class EllipticalCylinder final : public IntersectRegionInterface
0213 {
0214   public:
0215     // Construct with x- and y-radii and half-height in z
0216     EllipticalCylinder(Real2 const& radii, real_type halfheight);
0217 
0218     // Build surfaces
0219     void build(IntersectSurfaceBuilder&) const final;
0220 
0221     // Output to JSON
0222     void output(JsonPimpl*) const final;
0223 
0224     //// TEMPLATE INTERFACE ////
0225 
0226     // Whether this encloses another ellipsoid
0227     bool encloses(EllipticalCylinder const& other) const;
0228 
0229     //// ACCESSORS ////
0230 
0231     //! Radius along each axis
0232     Real2 const& radii() const { return radii_; }
0233 
0234     //! Half-height along Z
0235     real_type halfheight() const { return hh_; }
0236 
0237   private:
0238     Real2 radii_;
0239     real_type hh_;
0240 };
0241 
0242 //---------------------------------------------------------------------------//
0243 /*!
0244  * A finite *z*-aligned cone with an elliptical cross section.
0245  *
0246  * The elliptical cone is defined in an analogous fashion to the regular
0247  * (i.e., circular) cone. A half-height (hh) defines the z-extents, such
0248  * that the centroid of the outer bounding box is the origin. The lower radii
0249  * are the x- and y-radii at the plane z = -hh. The upper radii are the x- and
0250  * y-radii at the plane z = hh. There are several restrictions on these radii:
0251  *
0252  * 1) Either the lower or upper radii may be (0, 0); this is the only permitted
0253  *    way for the elliptical cone to include the vertex.
0254  * 2) The aspect ratio of the elliptical cross sections is constant. Thus, the
0255  *    aspect ratio at z = -hh must equal the aspect ratio at z = hh.
0256  * 3) Degenerate elliptical cones with lower_radii == upper_radii (i.e.,
0257  *    elliptical cylinders) are not permitted.
0258  * 4) Degenerate elliptical cones where lower or upper radii are equal to
0259  *    (0, x) or (x, 0), where x is non-zero, are not permitted.
0260  *
0261  * The elliptical surface can be expressed as:
0262  *
0263  * \f[
0264    (x/r_x)^2 + (y/r_y)^2 = (v-z)^2,
0265  * \f]
0266  *
0267  * which can be converted to SimpleQuadric form:
0268  * \verbatim
0269    (1/r_x)^2 x^2  + (1/r_y)^2 y^2 + (-1) z^2 + (2v) z + (-v^2) = 0.
0270       |                |              |         |          |
0271       a                b              c         d          e
0272  * \endverbatim
0273  *
0274  * where v is the location of the vertex. The r_x, r_y, and v can be calculated
0275  * from the lower and upper radii as given by \c G4EllipticalCone:
0276  * \verbatim
0277    r_x = (lower_radii[X] - upper_radii[X])/(2 hh),
0278    r_y = (lower_radii[Y] - upper_radii[Y])/(2 hh),
0279      v = hh (lower_radii[X] + upper_radii[X])/(lower_radii[X] -
0280  upper_radii[X]).
0281  * \endverbatim
0282  */
0283 class EllipticalCone final : public IntersectRegionInterface
0284 {
0285   public:
0286     // Construct with x- and y-radii and half-height in z
0287     EllipticalCone(Real2 const& lower_radii,
0288                    Real2 const& upper_radii,
0289                    real_type halfheight);
0290 
0291     // Build surfaces
0292     void build(IntersectSurfaceBuilder&) const final;
0293 
0294     // Output to JSON
0295     void output(JsonPimpl*) const final;
0296 
0297     //// TEMPLATE INTERFACE ////
0298 
0299     // Whether this encloses another elliptical cone
0300     bool encloses(EllipticalCone const& other) const;
0301 
0302     //// ACCESSORS ////
0303 
0304     //! Radii along the x- and y-axes at z=-hh
0305     Real2 const& lower_radii() const { return lower_radii_; }
0306 
0307     //! Radii along the x- and y-axes at z=-hh
0308     Real2 const& upper_radii() const { return upper_radii_; }
0309 
0310     //! Half-height along Z
0311     real_type halfheight() const { return hh_; }
0312 
0313   private:
0314     Real2 lower_radii_;
0315     Real2 upper_radii_;
0316     real_type hh_;
0317 };
0318 
0319 //---------------------------------------------------------------------------//
0320 /*!
0321  * A generalized polygon with parallel flat faces along the *z* axis.
0322  *
0323  * A GenPrism, like VecGeom's GenTrap, ROOT's Arb8, and Geant4's
0324  * G4GenericTrap, represents a generalized volume with polyhedral faces on two
0325  * parallel planes perpendicular to the \em z axis. Unlike those other codes,
0326  * the number of faces can be arbitrary in number.
0327  *
0328  * The faces have an orientation and ordering so that \em twisted faces can be
0329  * constructed by joining corresponding points using straight-line "vertical"
0330  * edges, directly matching the G4GenericTrap definition, but directly
0331  * generating a hyperbolic paraboloid for each twisted face.
0332  *
0333  * Trapezoids constructed from the helper functions will have sides that are
0334  * same ordering as a prism: the rightward face is first (normal is along the
0335  * \em +x axis), then the others follow counterclockwise.
0336  */
0337 class GenPrism final : public IntersectRegionInterface
0338 {
0339   public:
0340     //!@{
0341     //! \name Type aliases
0342     using VecReal2 = std::vector<Real2>;
0343     //!@}
0344 
0345     //! Regular trapezoidal top/bottom face
0346     struct TrapFace
0347     {
0348         //! Half the vertical distance between horizontal edges
0349         real_type hy{};
0350         //! Top horizontal edge half-length
0351         real_type hx_lo{};
0352         //! Bottom horizontal edge half-length
0353         real_type hx_hi{};
0354         //! Shear angle between horizontal line centers and Y axis
0355         Turn alpha;
0356     };
0357 
0358   public:
0359     // Helper function to construct a Trd shape from hz and two rectangles,
0360     // one for each z-face
0361     static GenPrism from_trd(real_type halfz, Real2 const& lo, Real2 const& hi);
0362 
0363     // Helper function to construct a general trap from its half-height and
0364     // the two trapezoids defining its lower and upper faces
0365     static GenPrism from_trap(real_type hz,
0366                               Turn theta,
0367                               Turn phi,
0368                               TrapFace const& lo,
0369                               TrapFace const& hi);
0370 
0371     // Construct from half Z height and 4 vertices for top and bottom planes
0372     GenPrism(real_type halfz, VecReal2 const& lo, VecReal2 const& hi);
0373 
0374     // Build surfaces
0375     void build(IntersectSurfaceBuilder&) const final;
0376 
0377     // Output to JSON
0378     void output(JsonPimpl*) const final;
0379 
0380     //// ACCESSORS ////
0381 
0382     //! Half-height along Z
0383     real_type halfheight() const { return hz_; }
0384 
0385     //! Polygon on -z face
0386     VecReal2 const& lower() const { return lo_; }
0387 
0388     //! Polygon on +z face
0389     VecReal2 const& upper() const { return hi_; }
0390 
0391     //! Number of sides (points on the Z face)
0392     size_type num_sides() const { return lo_.size(); }
0393 
0394     // Calculate the cosine of the twist angle for a given side
0395     real_type calc_twist_cosine(size_type size_idx) const;
0396 
0397   private:
0398     enum class Degenerate
0399     {
0400         none,
0401         lo,
0402         hi
0403     };
0404 
0405     real_type hz_;  //!< half-height
0406     VecReal2 lo_;  //!< corners of the -z face
0407     VecReal2 hi_;  //!< corners of the +z face
0408     Degenerate degen_{Degenerate::none};  //!< no plane on this z axis
0409 };
0410 
0411 //---------------------------------------------------------------------------//
0412 /*!
0413  * An infinite slab bound by lower and upper z-planes.
0414  */
0415 class InfSlab final : public IntersectRegionInterface
0416 {
0417   public:
0418     // Construct from lower and upper z-planes
0419     InfSlab(real_type lower, real_type upper);
0420 
0421     // Build surfaces
0422     void build(IntersectSurfaceBuilder&) const final;
0423 
0424     // Write output to the given JSON object
0425     void output(JsonPimpl*) const final;
0426 
0427     //// ACCESSORS ////
0428 
0429     //! Lower z-plane
0430     real_type lower() const { return lower_; }
0431 
0432     //! Upper z-plane
0433     real_type upper() const { return upper_; }
0434 
0435   private:
0436     real_type lower_;
0437     real_type upper_;
0438 };
0439 
0440 //---------------------------------------------------------------------------//
0441 /*!
0442  * An open wedge shape from the *z* axis.
0443  *
0444  * The wedge is defined by an interior angle that \em must be less than or
0445  * equal to 180 degrees (half a turn) and \em must be more than zero. It can be
0446  * subtracted, or its negation can be subtracted. The start angle is mapped
0447  * onto \f$[0, 1)\f$ on construction.
0448  */
0449 class InfWedge final : public IntersectRegionInterface
0450 {
0451   public:
0452     // Construct from a starting angle and interior angle
0453     InfWedge(Turn start, Turn interior);
0454 
0455     // Build surfaces
0456     void build(IntersectSurfaceBuilder&) const final;
0457 
0458     // Output to JSON
0459     void output(JsonPimpl*) const final;
0460 
0461     //// ACCESSORS ////
0462 
0463     //! Starting angle
0464     Turn start() const { return start_; }
0465 
0466     //! Interior angle
0467     Turn interior() const { return interior_; }
0468 
0469   private:
0470     Turn start_;
0471     Turn interior_;
0472 };
0473 
0474 //---------------------------------------------------------------------------//
0475 /*!
0476  * An involute "blade" centered on the origin.
0477  *
0478  * This is the intersection of two parallel involutes with a cylindrical shell.
0479  * The three radii, which must be in ascending order, are that of the involute,
0480  * the inner cylinder, and the outer cylinder.
0481  *
0482  * The "chirality" of the involute is viewed from the \em +z axis looking down:
0483  * whether it spirals to the right or left.
0484  */
0485 class Involute final : public IntersectRegionInterface
0486 {
0487   public:
0488     // Construct with radius
0489     explicit Involute(Real3 const& radii,
0490                       Real2 const& displacement,
0491                       Chirality chirality,
0492                       real_type halfheight);
0493 
0494     // Build surfaces
0495     void build(IntersectSurfaceBuilder&) const final;
0496 
0497     // Output to JSON
0498     void output(JsonPimpl*) const final;
0499 
0500     //// ACCESSORS ////
0501 
0502     //! Radii: Rdius of involute, minimum radius, maximum radius
0503     Real3 const& radii() const { return radii_; }
0504     //! Displacement angle
0505     Real2 const& displacement_angle() const { return a_; }
0506     //!  Angular bounds of involute
0507     Real2 const& t_bounds() const { return t_bounds_; }
0508     //! Chirality of involute: turning left or right
0509     Chirality chirality() const { return sign_; }
0510     //! Halfheight
0511     real_type halfheight() const { return hh_; }
0512 
0513   private:
0514     Real3 radii_;
0515     Real2 a_;
0516     Real2 t_bounds_;
0517     Chirality sign_;
0518     real_type hh_;
0519 };
0520 
0521 //---------------------------------------------------------------------------//
0522 /*!
0523  * A general parallelepiped centered on the origin.
0524  *
0525  * A parallelepiped is a shape having 3 pairs of parallel faces out of
0526  * which one is parallel with the \em x-y plane (\em z faces). All faces are
0527  * parallelograms in the general case. The \em z faces have 2 edges parallel
0528  * with the \em x axis. Note that all angle parameters are expressed in terms
0529  * of fractions of a 360-degree turn.
0530  *
0531  * The shape has the center in the origin and it is defined by:
0532  *
0533  *   - \c halfedges: a 3-vector (dY, dY, dZ) with half-lengths of the
0534  *     projections of the edges on X, Y, Z. The lower Z face is positioned at
0535  *     `-dZ`, and the upper one at `+dZ`.
0536  *   - \c alpha angle between the segment defined by the centers of the
0537  *     X-parallel edges and Y axis. Validity range is `(-1/4, 1/4)`;
0538  *   - \c theta polar angle of the shape's main axis, e.g. the segment defined
0539  *     by the centers of the Z faces. Validity range is `[0, 1/4)`;
0540  *   - \c phi azimuthal angle of the shape's main axis (as explained above).
0541  *     Validity range is `[0, 1)`.
0542  */
0543 class Parallelepiped final : public IntersectRegionInterface
0544 {
0545   public:
0546     // Construct with half widths and 3 angles
0547     Parallelepiped(Real3 const& halfedges, Turn alpha, Turn theta, Turn phi);
0548 
0549     // Build surfaces
0550     void build(IntersectSurfaceBuilder&) const final;
0551 
0552     // Output to JSON
0553     void output(JsonPimpl*) const final;
0554 
0555     //// ACCESSORS ////
0556 
0557     //! Half-lengths of edge projections along each axis
0558     Real3 const& halfedges() const { return hpr_; }
0559     //! Angle between slanted y-edges and the y-axis (in turns)
0560     Turn alpha() const { return alpha_; }
0561     //! Polar angle of main axis (in turns)
0562     Turn theta() const { return theta_; }
0563     //! Azimuthal angle of main axis (in turns)
0564     Turn phi() const { return phi_; }
0565 
0566   private:
0567     // half-lengths
0568     Real3 hpr_;
0569     // angle between slanted y-edges and the y-axis
0570     Turn alpha_;
0571     // polar angle of main axis
0572     Turn theta_;
0573     // azimuthal angle of main axis
0574     Turn phi_;
0575 };
0576 
0577 //---------------------------------------------------------------------------//
0578 /*!
0579  * A regular, z-extruded polygon centered on the origin.
0580  *
0581  * This is the base component of a G4Polyhedra (PGON). The default rotation is
0582  * to put a y-aligned plane on the bottom of the shape, so looking at an x-y
0583  * slice given an apothem \em a, every shape has a surface at \f$ y = -a \f$:
0584  * - n=3 is a triangle with a flat bottom, point up
0585  * - n=4 is a square with axis-aligned sides
0586  * - n=6 is a flat-top hexagon
0587  *
0588  * The "orientation" parameter is a scaled counterclockwise rotation on
0589  * \f$[0, 1)\f$, where zero preserves the orientation described above, and
0590  * unity replicates the original shape but with the "p0" face being where the
0591  * "p1" originally was. With a value of 0.5:
0592  * - n=3 is a downward-pointing triangle
0593  * - n=4 is a diamond
0594  * - n=6 is a pointy-top hexagon
0595  */
0596 class Prism final : public IntersectRegionInterface
0597 {
0598   public:
0599     // Construct with inner radius (apothem), half height, and orientation
0600     Prism(int num_sides,
0601           real_type apothem,
0602           real_type halfheight,
0603           real_type orientation);
0604 
0605     // Build surfaces
0606     void build(IntersectSurfaceBuilder&) const final;
0607 
0608     // Output to JSON
0609     void output(JsonPimpl*) const final;
0610 
0611     //// TEMPLATE INTERFACE ////
0612 
0613     // Whether this encloses another cylinder
0614     bool encloses(Prism const& other) const;
0615 
0616     //// ACCESSORS ////
0617 
0618     //! Number of sides
0619     int num_sides() const { return num_sides_; }
0620     //! Inner radius
0621     real_type apothem() const { return apothem_; }
0622     //! Half the Z height
0623     real_type halfheight() const { return hh_; }
0624     //! Rotation factor
0625     real_type orientation() const { return orientation_; }
0626 
0627   private:
0628     // Number of sides
0629     int num_sides_;
0630 
0631     // Distance from center to midpoint of its side
0632     real_type apothem_;
0633 
0634     // Half the z height
0635     real_type hh_;
0636 
0637     // Rotational offset (0 has bottom face at -Y, 1 is congruent)
0638     real_type orientation_;
0639 };
0640 
0641 //---------------------------------------------------------------------------//
0642 /*!
0643  * A sphere centered on the origin.
0644  *
0645  * \note Be aware there's also a sphere *surface* at orange/surf/Sphere.hh in a
0646  * different namespace.
0647  */
0648 class Sphere final : public IntersectRegionInterface
0649 {
0650   public:
0651     // Construct with radius
0652     explicit Sphere(real_type radius);
0653 
0654     // Build surfaces
0655     void build(IntersectSurfaceBuilder&) const final;
0656 
0657     // Output to JSON
0658     void output(JsonPimpl*) const final;
0659 
0660     //// TEMPLATE INTERFACE ////
0661 
0662     // Whether this encloses another sphere
0663     bool encloses(Sphere const& other) const;
0664 
0665     //// ACCESSORS ////
0666 
0667     //! Radius
0668     real_type radius() const { return radius_; }
0669 
0670   private:
0671     real_type radius_;
0672 };
0673 
0674 //---------------------------------------------------------------------------//
0675 }  // namespace orangeinp
0676 }  // namespace celeritas