SpECTRE Documentation Coverage Report
Current view: top level - Domain/CoordinateMaps - Frustum.hpp Hit Total Coverage
Commit: 3c072f0ce967e2e56649d3fa12aa2a0e4fe2a42e Lines: 3 41 7.3 %
Date: 2024-04-23 20:50:18
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <array>
       7             : #include <cstddef>
       8             : #include <limits>
       9             : #include <optional>
      10             : 
      11             : #include "DataStructures/Tensor/TypeAliases.hpp"
      12             : #include "Domain/CoordinateMaps/Distribution.hpp"
      13             : #include "Domain/Structure/OrientationMap.hpp"
      14             : #include "Utilities/TypeTraits/RemoveReferenceWrapper.hpp"
      15             : 
      16             : /// \cond
      17             : namespace PUP {
      18             : class er;
      19             : }  // namespace PUP
      20             : /// \endcond
      21             : 
      22             : namespace domain {
      23             : namespace CoordinateMaps {
      24             : 
      25             : /*!
      26             :  * \ingroup CoordinateMapsGroup
      27             :  *
      28             :  * \brief A reorientable map from the cube to a frustum.
      29             :  * \image html frustum_table1.png "A frustum with rectangular bases."
      30             :  *
      31             :  * ### Description and Specifiable Parameters
      32             :  * A map from the logical cube to the volume determined by interpolating between
      33             :  * two parallel rectangles each perpendicular to the \f$z\f$-axis. A Frustum map
      34             :  * \f$\vec{x}(\xi,\eta,\zeta)\f$ is determined by specifying two
      35             :  * heights \f$z_1 = \f$ `lower_bound` and \f$z_2 = \f$ `upper_bound` for the
      36             :  * positions of the rectangular bases of the frustum along the \f$z-\f$axis,
      37             :  * and the sizes of the rectangles are determined by the eight values passed to
      38             :  * `face_vertices`:
      39             :  *
      40             :  * \f{align*}{
      41             :  * &\textrm{lower x upper base} : x^{+\zeta}_{-\xi,-\eta} = x(-1,-1,1)\\
      42             :  * &\textrm{lower x lower base} : x^{-\zeta}_{-\xi,-\eta} = x(-1,-1,-1)\\
      43             :  * &\textrm{upper x upper base} : x^{+\zeta}_{+\xi,+\eta} = x(1,1,1)\\
      44             :  * &\textrm{upper x lower base} : x^{-\zeta}_{+\xi,+\eta} = x(1,1,-1)\\
      45             :  * &\textrm{lower y upper base} : y^{+\zeta}_{-\xi,-\eta} = y(-1,-1,1)\\
      46             :  * &\textrm{lower y lower base} : y^{-\zeta}_{-\xi,-\eta} = y(-1,-1,-1)\\
      47             :  * &\textrm{upper y upper base} : y^{+\zeta}_{+\xi,+\eta} = y(1,1,1)\\
      48             :  * &\textrm{upper y lower base} : y^{-\zeta}_{+\xi,+\eta} = y(1,1,-1)\f}
      49             :  *
      50             :  * \note As an example, consider a frustum along the z-axis, with the lower base
      51             :  * starting at (x,y) = (-2.0,3.0) and extending to
      52             :  * (2.0,5.0), and with the upper base extending from (0.0,1.0) to (1.0,3.0).
      53             :  * The corresponding value for `face_vertices` is `{{{{-2.0,3.0}}, {{2.0,5.0}},
      54             :  * {{0.0,1.0}}, {{1.0,3.0}}}}`.
      55             :  *
      56             :  * In the case where the two rectangles are geometrically similar, the volume
      57             :  * formed is a geometric frustum. However, this coordinate map generalizes to
      58             :  * rectangles which need not be similar.
      59             :  * The user may reorient the frustum by passing an OrientationMap to the
      60             :  * constructor. If `with_equiangular_map` is true, then this coordinate map
      61             :  * applies a tangent function mapping to the logical \f$\xi\f$ and \f$\eta\f$
      62             :  * coordinates. We then refer to the generalized
      63             :  * logical coordinates as \f$\Xi\f$ and \f$\mathrm{H}\f$. If
      64             :  * `projective_scale_factor` is set to a quantity other than unity, then this
      65             :  * coordinate map applies a rational function mapping to the logical \f$\zeta\f$
      66             :  * coordinate. This generalized logical coordinate is referred to as
      67             :  * \f$\mathrm{Z}\f$. If `auto_projective_scale_factor` is `true`, the
      68             :  * user-specified `projective_scale_factor` is ignored and an appropriate value
      69             :  * for `projective_scale_factor` is computed based on the values passed to
      70             :  * `face_vertices`. See the
      71             :  * [page on redistributing gridpoints](@ref redistributing_gridpoints)
      72             :  * to see more detailed information on equiangular variables and projective
      73             :  * scaling.
      74             :  *
      75             :  * ### Coordinate Map and Jacobian
      76             :  * In terms of the `face_vertices` variables, we define the
      77             :  * following auxiliary variables:
      78             :  *
      79             :  * \f{align*}{\Sigma^x &= \frac{1}{4}(x^{+\zeta}_{-\xi,-\eta} +
      80             :  * x^{+\zeta}_{+\xi,+\eta} + x^{-\zeta}_{-\xi,-\eta} + x^{-\zeta}_{+\xi,+\eta})
      81             :  * \\
      82             :  * \Delta^x_{\zeta} &= \frac{1}{4}(x^{+\zeta}_{-\xi,-\eta} +
      83             :  * x^{+\zeta}_{+\xi,+\eta} - x^{-\zeta}_{-\xi,-\eta} - x^{-\zeta}_{+\xi,+\eta})
      84             :  * \\
      85             :  * \Delta^x_{\xi} &= \frac{1}{4}(x^{+\zeta}_{+\xi,+\eta} -
      86             :  * x^{+\zeta}_{-\xi,-\eta} + x^{-\zeta}_{+\xi,+\eta} - x^{-\zeta}_{-\xi,-\eta})
      87             :  * \\
      88             :  * \Delta^x_{\xi\zeta} &= \frac{1}{4}(x^{+\zeta}_{+\xi,+\eta} -
      89             :  * x^{+\zeta}_{-\xi,-\eta} - x^{-\zeta}_{+\xi,+\eta} + x^{-\zeta}_{-\xi,-\eta})
      90             :  * \\
      91             :  * \Sigma^y &= \frac{1}{4}(y^{+\zeta}_{-\xi,-\eta} +
      92             :  * y^{+\zeta}_{+\xi,+\eta} + y^{-\zeta}_{-\xi,-\eta} + y^{-\zeta}_{+\xi,+\eta})
      93             :  * \\
      94             :  * \Delta^y_{\zeta} &= \frac{1}{4}(y^{+\zeta}_{-\xi,-\eta} +
      95             :  * y^{+\zeta}_{+\xi,+\eta} - y^{-\zeta}_{-\xi,-\eta} - y^{-\zeta}_{+\xi,+\eta})
      96             :  * \\
      97             :  * \Delta^y_{\eta} &= \frac{1}{4}(y^{+\zeta}_{+\xi,+\eta} -
      98             :  * y^{+\zeta}_{-\xi,-\eta} + y^{-\zeta}_{+\xi,+\eta} - y^{-\zeta}_{-\xi,-\eta})
      99             :  * \\
     100             :  * \Delta^y_{\eta\zeta} &= \frac{1}{4}(y^{+\zeta}_{+\xi,+\eta} -
     101             :  * y^{+\zeta}_{-\xi,-\eta} - y^{-\zeta}_{+\xi,+\eta} + y^{-\zeta}_{-\xi,-\eta})
     102             :  * \\
     103             :  * \Sigma^z &= \frac{z_1 + z_2}{2}\\
     104             :  * \Delta^z &= \frac{z_2 - z_1}{2}
     105             :  * \f}
     106             :  *
     107             :  * The full map is then given by:
     108             :  * \f[\vec{x}(\xi,\eta,\zeta) = \begin{bmatrix}
     109             :  * \Sigma^x + \Delta^x_{\xi}\Xi + (\Delta^x_{\zeta} +
     110             :  * \Delta^x_{\xi\zeta}\Xi)\mathrm{Z}\\
     111             :  * \Sigma^y + \Delta^y_{\eta}\mathrm{H} + (\Delta^y_{\zeta} +
     112             :  * \Delta^y_{\eta\zeta}\mathrm{H})\mathrm{Z}\\
     113             :  * \Sigma^z + \Delta^z\mathrm{Z}\\
     114             :  * \end{bmatrix}\f]
     115             :  *
     116             :  * With Jacobian: \f[J =
     117             :  * \begin{bmatrix}
     118             :  * (\Delta^x_{\xi} + \Delta^x_{\xi\zeta}\mathrm{Z})\Xi' &
     119             :  * 0 & (\Delta^x_{\zeta}+ \Delta^x_{\xi\zeta}\Xi)\mathrm{Z}' \\
     120             :  * 0 & (\Delta^y_{\eta} + \Delta^y_{\eta\zeta}\mathrm{Z})\mathrm{H}' &
     121             :  * (\Delta^y_{\zeta} + \Delta^y_{\eta\zeta}\mathrm{H})\mathrm{Z}'\\
     122             :  * 0 & 0 & \Delta^z\mathrm{Z}' \\
     123             :  * \end{bmatrix}
     124             :  * \f]
     125             :  *
     126             :  * ### Suggested values for the projective scale factor
     127             :  * \note This section assumes familiarity with projective scaling as discussed
     128             :  * on the [page on redistributing gridpoints](@ref redistributing_gridpoints).
     129             :  *
     130             :  * When constructing a Frustum map, it is not immediately obvious what value of
     131             :  * \f$w_{\delta}\f$ to use in the projective map; here we present a choice of
     132             :  * \f$w_{\delta}\f$ that will produce minimal grid distortion. We cover two
     133             :  * cases: the first is the special case in which the two rectangular Frustum
     134             :  * bases are related by a simple scale factor; the second is the general case.
     135             :  *
     136             :  * \image html ProjectionOfCube.png "Trapezoids from squares. (Davide Cervone)"
     137             :  *
     138             :  * As seen in Cervone's [Cubes and Hypercubes Rotating]
     139             :  * (http://www.math.union.edu/~dpvc/math/4D/rotation/welcome.html), there is a
     140             :  * special case in which the inverse projection of a trapezoid is not another
     141             :  * trapezoid, but a rectangle where the bases are congruent.
     142             :  * Most often one will want to use the special value of \f$w_{\delta}\f$ where
     143             :  * this occurs. This value of \f$w_{\delta}\f$ corresponds to the projective
     144             :  * transformation mapping a rectangular prism in an ambient 4D space with
     145             :  * congruent faces at \f$w=1\f$ and \f$w=w_{\delta}\f$ to the frustum in the
     146             :  * plane \f$w=1\f$:
     147             :  *
     148             :  * \f[w_{\delta} = \frac{L_1}{L_2}\f]
     149             :  *
     150             :  * where \f$\frac{L_1}{L_2}\f$ is the ratio between any pair of
     151             :  * corresponding side lengths of the \f$z_1\f$ and \f$z_2\f$-bases of the
     152             :  * frustum, respectively.
     153             :  *
     154             :  * For the general case one will want to use the value:
     155             :  * \f[w_{\delta} =
     156             :  * \frac{\sqrt{(x^{-\zeta}_{+\xi,+\eta} - x^{-\zeta}_{-\xi,+\eta})
     157             :  *             (y^{-\zeta}_{+\xi,+\eta} - y^{-\zeta}_{+\xi,-\eta})}}
     158             :  * {\sqrt{(x^{+\zeta}_{+\xi,+\eta} - x^{+\zeta}_{-\xi,+\eta})
     159             :  *        (y^{+\zeta}_{+\xi,+\eta} - y^{+\zeta}_{+\xi,-\eta})}}\f]
     160             :  *
     161             :  * This is the value for \f$w_{\delta}\f$ used by this CoordinateMap when
     162             :  * `auto_projective_scale_factor` is `true`.
     163             :  *
     164             :  * ### Bulged Frustum Coordinate Map and Jacobian
     165             :  * Each of the frustum faces in the frustum map given above are flat, but the
     166             :  * upper +z face of the frustum can be bulged out by setting a non-zero value
     167             :  * for the `sphericity`, where a value of `0.0` corresponds to the usual flat-
     168             :  * face, and a value of `1.0` corresponds to a value of fully spherical. Using
     169             :  * OrientationMaps allows the user to create a set of frustums that fully cover
     170             :  * a spherical surface. The radius of the sphere is determined by the corner of
     171             :  * the frustum that is furthest from the origin.
     172             :  *
     173             :  * The full map is given by:
     174             :  * \f[\vec{x}(\xi,\eta,\zeta) = \begin{bmatrix}
     175             :  * \Sigma^x + \Delta^x_{\xi}\Xi + (\Delta^x_{\zeta} +
     176             :  * \Delta^x_{\xi\zeta}\Xi)\mathrm{Z}\\
     177             :  * \Sigma^y + \Delta^y_{\eta}\mathrm{H} + (\Delta^y_{\zeta} +
     178             :  * \Delta^y_{\eta\zeta}\mathrm{H})\mathrm{Z}\\
     179             :  * \Sigma^z + \Delta^z\mathrm{Z}\\
     180             :  * \end{bmatrix}
     181             :  * + s \frac{1 +
     182             :  * \mathrm{Z}}{2}\left(\frac{R}{|\vec{\sigma}_{\mathrm{+z}}|}-1\right)
     183             :  * \vec{\sigma}_{\mathrm{+z}}
     184             :  * \f]
     185             :  *
     186             :  * where \f$R\f$ is the radius, \f$s\f$ is the `sphericity`, and
     187             :  * \f$\vec{\sigma}_{\mathrm{+z}}\f$ is given by:
     188             :  * \f[
     189             :  * \vec{\sigma}_{\mathrm{+z}} =
     190             :  * \begin{bmatrix}
     191             :  * \Sigma^x + \Delta^x_{\xi}\Xi + \Delta^x_{\zeta} +
     192             :  * \Delta^x_{\xi\zeta}\Xi\\
     193             :  * \Sigma^y + \Delta^y_{\eta}\mathrm{H} + \Delta^y_{\zeta} +
     194             :  * \Delta^y_{\eta\zeta}\mathrm{H}\\
     195             :  * \Sigma^z + \Delta^z\\
     196             :  * \end{bmatrix}.
     197             :  * \f]
     198             :  *
     199             :  * The Jacobian is:
     200             :  * \f[J =
     201             :  * \begin{bmatrix}
     202             :  * (\Delta^x_{\xi} + \Delta^x_{\xi\zeta}\mathrm{Z})\Xi' &
     203             :  * 0 & (\Delta^x_{\zeta}+ \Delta^x_{\xi\zeta}\Xi)\mathrm{Z}' \\
     204             :  * 0 & (\Delta^y_{\eta} + \Delta^y_{\eta\zeta}\mathrm{Z})\mathrm{H}' &
     205             :  * (\Delta^y_{\zeta} + \Delta^y_{\eta\zeta}\mathrm{H})\mathrm{Z}'\\
     206             :  * 0 & 0 & \Delta^z\mathrm{Z}' \\
     207             :  * \end{bmatrix}
     208             :  * +
     209             :  * \frac{s}{2} \left\{ \left(\frac{R}{|\vec{\sigma}_{\mathrm{+z}}|}-1\right)
     210             :  * \vec{\sigma}_{\mathrm{+z}}\hat{\zeta}^{\intercal}\mathrm{Z}'+
     211             :  * (1+\mathrm{Z})\left(\frac{R}{|\vec{\sigma}_{\mathrm{+z}}|}\left(
     212             :  * \mathbb{1}-\frac{\vec{\sigma}_{\mathrm{+z}}
     213             :  * \vec{\sigma}_{\mathrm{+z}}^{\intercal}}
     214             :  * {|\vec{\sigma}_{\mathrm{+z}}|^2}\right)-\mathbb{1}\right)
     215             :  * J_{\sigma}\right\},
     216             :  * \f]
     217             :  *
     218             :  * where \f$\hat{\zeta}\f$ is the row vector \f$[0, 0, 1]\f$, and
     219             :  * \f$J_{\sigma}\f$ is the Jacobian of the upper +z surface, given by:
     220             :  * \f[
     221             :  * J_{\sigma} =
     222             :  * \begin{bmatrix}
     223             :  * (\Delta^x_{\xi} + \Delta^x_{\xi\zeta})\Xi' &
     224             :  * 0 & 0 \\
     225             :  * 0 & (\Delta^y_{\eta} + \Delta^y_{\eta\zeta})\mathrm{H}' &
     226             :  * 0 \\
     227             :  * 0 & 0 & 0 \\
     228             :  * \end{bmatrix}.
     229             :  * \f]
     230             :  *
     231             :  * ### Using the Frustum Transition between Equiangular Maps
     232             :  * Using the parameter \f$\phi\f$, the user can specify whether to use the
     233             :  * full equiangular map on the upper +z face, or whether to use only the upper
     234             :  * or lower half of the equiangular map on the upper +z face. This capability
     235             :  * is used in the BinaryCompactObject Domain, where two equiangular maps around
     236             :  * each spherical object must be transitioned into a single equiangular map at
     237             :  * the outer boundary. The transition region that interpolates between these
     238             :  * two regions is the layer of Blocks with the Frustum maps.
     239             :  *
     240             :  * The full map is obtained by setting
     241             :  * \f$\Xi = \frac{1 + \mathrm{Z}}{2}\Xi_{\mathrm{upper}}(\xi,\zeta) +
     242             :  *          \frac{1 - \mathrm{Z}}{2}\Xi_{0}(\xi)\f$
     243             :  *
     244             :  * for the volume map \f$\vec{x}(\xi,\eta,\zeta)\f$, and
     245             :  * \f$\Xi = \Xi_{\mathrm{upper}}\f$ for the surface map
     246             :  * \f$\vec{\sigma}(\xi,\eta)\f$, where \f$\Xi_{\mathrm{upper}}\f$ is the
     247             :  * \f$\phi\f$-dependent generalized equiangular map, which corresponds to the
     248             :  * lower half of the equiangular map when \f$\phi = -1\f$,
     249             :  * and to the upper half of the equiangular map when \f$\phi = 1 \f$.
     250             :  * It is given by:
     251             :  *
     252             :  * \f[\Xi_{\mathrm{upper}} = (1 +
     253             :  * \phi^2)\tan(\frac{\pi}{4}\frac{\xi+\phi}{1+\phi^2})-\phi\f]
     254             :  *
     255             :  * For \f$\phi=0\f$ this generalized map simplifies to the original equiangular
     256             :  * map \f$Xi_0 = \tan(\frac{\pi}{4}\xi)\f$.
     257             :  *
     258             :  * This function is compatible with the ability to bulge out the Frustum; the
     259             :  * map and Jacobian remain unchanged, modulo this substitution.
     260             :  */
     261           1 : class Frustum {
     262             :  public:
     263           0 :   static constexpr size_t dim = 3;
     264             :   /*!
     265             :    * Constructs a frustum.
     266             :    * \param face_vertices An array of four 2D vertices that define the (x, y)
     267             :    * coordinates of the upper and lower base of the frustum.
     268             :    * \param lower_bound z distance from the origin to the lower base of the
     269             :    * frustum.
     270             :    * \param upper_bound z distance from the origin to the upper base of the
     271             :    * frustum.
     272             :    * \param orientation_of_frustum The orientation of the frustum in 3D space.
     273             :    * \param with_equiangular_map Determines whether to apply a tangent function
     274             :    * mapping to the logical coordinates (true) or not (false).
     275             :    * \param zeta_distribution Whether to apply a linear, logarithmic, or
     276             :    * projective mapping to the logical zeta coordinate.
     277             :    * \param distribution_value In the case of a projective map, the projective
     278             :    * scale factor, otherwise unused.
     279             :    * \param sphericity Value between 0 and 1 which determines whether the
     280             :    * surface of the upper base of the Frustum is flat (value of 0), spherical
     281             :    * (value of 1), or somewhere in between.
     282             :    * \param transition_phi Determines whether the equiangular map used conforms
     283             :    * to a lower half wedge (value of -1), an upper half wedge (value of +1), or
     284             :    * a full wedge (value of 0).
     285             :    * \param opening_angle The opening angle of the wedge the frustum will
     286             :    * conform to (have conforming boundaries with).
     287             :    */
     288           1 :   Frustum(const std::array<std::array<double, 2>, 4>& face_vertices,
     289             :           double lower_bound, double upper_bound,
     290             :           OrientationMap<3> orientation_of_frustum,
     291             :           bool with_equiangular_map = false,
     292             :           Distribution zeta_distribution = Distribution::Linear,
     293             :           std::optional<double> distribution_value = std::nullopt,
     294             :           double sphericity = 0.0, double transition_phi = 0.0,
     295             :           double opening_angle = M_PI_2);
     296           0 :   Frustum() = default;
     297           0 :   ~Frustum() = default;
     298           0 :   Frustum(Frustum&&) = default;
     299           0 :   Frustum(const Frustum&) = default;
     300           0 :   Frustum& operator=(const Frustum&) = default;
     301           0 :   Frustum& operator=(Frustum&&) = default;
     302             : 
     303             :   template <typename T>
     304           0 :   std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(
     305             :       const std::array<T, 3>& source_coords) const;
     306             : 
     307             :   /// Returns std::nullopt if \f$z\f$ is at or beyond the \f$z\f$-coordinate of
     308             :   /// the apex of the pyramid, tetrahedron, or triangular prism that is
     309             :   /// formed by extending the `Frustum` (for a
     310             :   /// \f$z\f$-oriented `Frustum`).
     311             :   /// The inverse function is only callable with doubles because the inverse
     312             :   /// might fail if called for a point out of range, and it is unclear
     313             :   /// what should happen if the inverse were to succeed for some points in a
     314             :   /// DataVector but fail for other points.
     315           1 :   std::optional<std::array<double, 3>> inverse(
     316             :       const std::array<double, 3>& target_coords) const;
     317             : 
     318             :   template <typename T>
     319           0 :   tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(
     320             :       const std::array<T, 3>& source_coords) const;
     321             : 
     322             :   template <typename T>
     323           0 :   tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(
     324             :       const std::array<T, 3>& source_coords) const;
     325             : 
     326             :   // NOLINTNEXTLINE(google-runtime-references)
     327           0 :   void pup(PUP::er& p);
     328             : 
     329           0 :   bool is_identity() const { return is_identity_; }
     330             : 
     331             :  private:
     332           0 :   friend bool operator==(const Frustum& lhs, const Frustum& rhs);
     333             : 
     334           0 :   OrientationMap<3> orientation_of_frustum_{};
     335           0 :   bool with_equiangular_map_{false};
     336           0 :   bool is_identity_{false};
     337           0 :   Distribution zeta_distribution_ = Distribution::Linear;
     338           0 :   double zeta_distribution_value_{std::numeric_limits<double>::signaling_NaN()};
     339           0 :   double sigma_x_{std::numeric_limits<double>::signaling_NaN()};
     340           0 :   double delta_x_zeta_{std::numeric_limits<double>::signaling_NaN()};
     341           0 :   double delta_x_xi_{std::numeric_limits<double>::signaling_NaN()};
     342           0 :   double delta_x_xi_zeta_{std::numeric_limits<double>::signaling_NaN()};
     343           0 :   double sigma_y_{std::numeric_limits<double>::signaling_NaN()};
     344           0 :   double delta_y_zeta_{std::numeric_limits<double>::signaling_NaN()};
     345           0 :   double delta_y_eta_{std::numeric_limits<double>::signaling_NaN()};
     346           0 :   double delta_y_eta_zeta_{std::numeric_limits<double>::signaling_NaN()};
     347           0 :   double sigma_z_{std::numeric_limits<double>::signaling_NaN()};
     348           0 :   double delta_z_zeta_{std::numeric_limits<double>::signaling_NaN()};
     349           0 :   double w_plus_{std::numeric_limits<double>::signaling_NaN()};
     350           0 :   double w_minus_{std::numeric_limits<double>::signaling_NaN()};
     351           0 :   double sphericity_{std::numeric_limits<double>::signaling_NaN()};
     352           0 :   double radius_{std::numeric_limits<double>::signaling_NaN()};
     353           0 :   double phi_{std::numeric_limits<double>::signaling_NaN()};
     354           0 :   double half_opening_angle_{std::numeric_limits<double>::signaling_NaN()};
     355           0 :   double one_over_tan_half_opening_angle_{
     356             :       std::numeric_limits<double>::signaling_NaN()};
     357           0 :   double inner_radius_{std::numeric_limits<double>::signaling_NaN()};
     358             : };
     359             : 
     360           0 : bool operator!=(const Frustum& lhs, const Frustum& rhs);
     361             : }  // namespace CoordinateMaps
     362             : }  // namespace domain

Generated by: LCOV version 1.14