Repository: UXARRAY/uxarray

Issue:
Constant latitude cross sections using bounding box
Compute the cross section and candidate faces using the minimum and maximum of each face's bounding box.

Discussion:

Solution patch:
diff --git a/uxarray/__init__.py b/uxarray/__init__.py
index f14c9961d..857c65f6c 100644
--- a/uxarray/__init__.py
+++ b/uxarray/__init__.py
@@ -33,6 +33,9 @@ def disable_fma():
     uxarray.constants.ENABLE_FMA = False
 
 
+disable_fma()
+
+
 __all__ = (
     "open_grid",
     "open_dataset",
diff --git a/uxarray/cross_sections/dataarray_accessor.py b/uxarray/cross_sections/dataarray_accessor.py
index 299451d01..de35739ab 100644
--- a/uxarray/cross_sections/dataarray_accessor.py
+++ b/uxarray/cross_sections/dataarray_accessor.py
@@ -21,7 +21,7 @@ def __repr__(self):
 
         return prefix + methods_heading
 
-    def constant_latitude(self, lat: float, method="fast"):
+    def constant_latitude(self, lat: float, use_spherical_bounding_box=False):
         """Extracts a cross-section of the data array at a specified constant
         latitude.
 
@@ -29,12 +29,8 @@ def constant_latitude(self, lat: float, method="fast"):
         ----------
         lat : float
             The latitude at which to extract the cross-section, in degrees.
-        method : str, optional
-            The internal method to use when identifying faces at the constant latitude.
-            Options are:
-            - 'fast': Uses a faster but potentially less accurate method for face identification.
-            - 'accurate': Uses a slower but more accurate method.
-            Default is 'fast'.
+        use_spherical_bounding_box : bool, optional
+            If True, uses a spherical bounding box for intersection calculations.
 
         Raises
         ------
@@ -44,17 +40,14 @@ def constant_latitude(self, lat: float, method="fast"):
         Examples
         --------
         >>> uxda.constant_latitude_cross_section(lat=-15.5)
-
-        Notes
-        -----
-        The accuracy and performance of the function can be controlled using the `method` parameter.
-        For higher precision requreiments, consider using method='acurate'.
         """
-        faces = self.uxda.uxgrid.get_faces_at_constant_latitude(lat, method)
+        faces = self.uxda.uxgrid.get_faces_at_constant_latitude(
+            lat, use_spherical_bounding_box
+        )
 
         return self.uxda.isel(n_face=faces)
 
-    def constant_longitude(self, lon: float, method="fast"):
+    def constant_longitude(self, lon: float, use_spherical_bounding_box=False):
         """Extracts a cross-section of the data array at a specified constant
         longitude.
 
@@ -62,12 +55,8 @@ def constant_longitude(self, lon: float, method="fast"):
         ----------
         lon : float
             The longitude at which to extract the cross-section, in degrees.
-        method : str, optional
-            The internal method to use when identifying faces at the constant longitude.
-            Options are:
-            - 'fast': Uses a faster but potentially less accurate method for face identification.
-            - 'accurate': Uses a slower but more accurate method.
-            Default is 'fast'.
+        use_spherical_bounding_box : bool, optional
+            If True, uses a spherical bounding box for intersection calculations.
 
         Raises
         ------
@@ -83,7 +72,9 @@ def constant_longitude(self, lon: float, method="fast"):
         The accuracy and performance of the function can be controlled using the `method` parameter.
         For higher precision requreiments, consider using method='acurate'.
         """
-        faces = self.uxda.uxgrid.get_faces_at_constant_longitude(lon, method)
+        faces = self.uxda.uxgrid.get_faces_at_constant_longitude(
+            lon, use_spherical_bounding_box
+        )
 
         return self.uxda.isel(n_face=faces)
 
diff --git a/uxarray/cross_sections/grid_accessor.py b/uxarray/cross_sections/grid_accessor.py
index a193ec8e0..5b9055166 100644
--- a/uxarray/cross_sections/grid_accessor.py
+++ b/uxarray/cross_sections/grid_accessor.py
@@ -20,15 +20,12 @@ def __repr__(self):
         methods_heading += "  * constant_latitude(lat, )\n"
         return prefix + methods_heading
 
-    def constant_latitude(self, lat: float, return_face_indices=False, method="fast"):
+    def constant_latitude(
+        self, lat: float, return_face_indices=False, use_spherical_bounding_box=False
+    ):
         """Extracts a cross-section of the grid at a specified constant
         latitude.
 
-        This method identifies and returns all faces (or grid elements) that intersect
-        with a given latitude. The returned cross-section can include either just the grid
-        or both the grid elements and the corresponding face indices, depending
-        on the `return_face_indices` parameter.
-
         Parameters
         ----------
         lat : float
@@ -37,12 +34,9 @@ def constant_latitude(self, lat: float, return_face_indices=False, method="fast"
             If True, returns both the grid at the specified latitude and the indices
             of the intersecting faces. If False, only the grid is returned.
             Default is False.
-        method : str, optional
-            The internal method to use when identifying faces at the constant latitude.
-            Options are:
-            - 'fast': Uses a faster but potentially less accurate method for face identification.
-            - 'accurate': Uses a slower but more accurate method.
-            Default is 'fast'.
+        use_spherical_bounding_box : bool, optional
+            If True, uses a spherical bounding box for intersection calculations.
+
 
         Returns
         -------
@@ -69,7 +63,9 @@ def constant_latitude(self, lat: float, return_face_indices=False, method="fast"
         The accuracy and performance of the function can be controlled using the `method` parameter.
         For higher precision requreiments, consider using method='acurate'.
         """
-        faces = self.uxgrid.get_faces_at_constant_latitude(lat, method)
+        faces = self.uxgrid.get_faces_at_constant_latitude(
+            lat, use_spherical_bounding_box
+        )
 
         if len(faces) == 0:
             raise ValueError(f"No intersections found at lat={lat}.")
@@ -81,29 +77,22 @@ def constant_latitude(self, lat: float, return_face_indices=False, method="fast"
         else:
             return grid_at_constant_lat
 
-    def constant_longitude(self, lon: float, return_face_indices=False, method="fast"):
+    def constant_longitude(
+        self, lon: float, use_spherical_bounding_box=False, return_face_indices=False
+    ):
         """Extracts a cross-section of the grid at a specified constant
         longitude.
 
-        This method identifies and returns all faces (or grid elements) that intersect
-        with a given longitude. The returned cross-section can include either just the grid
-        or both the grid elements and the corresponding face indices, depending
-        on the `return_face_indices` parameter.
-
         Parameters
         ----------
         lon : float
             The longitude at which to extract the cross-section, in degrees.
+        use_spherical_bounding_box : bool, optional
+            If True, uses a spherical bounding box for intersection calculations.
         return_face_indices : bool, optional
             If True, returns both the grid at the specified longitude and the indices
             of the intersecting faces. If False, only the grid is returned.
             Default is False.
-        method : str, optional
-            The internal method to use when identifying faces at the constant longitude.
-            Options are:
-            - 'fast': Uses a faster but potentially less accurate method for face identification.
-            - 'accurate': Uses a slower but more accurate method.
-            Default is 'fast'.
 
         Returns
         -------
@@ -130,10 +119,12 @@ def constant_longitude(self, lon: float, return_face_indices=False, method="fast
         The accuracy and performance of the function can be controlled using the `method` parameter.
         For higher precision requreiments, consider using method='acurate'.
         """
-        faces = self.uxgrid.get_faces_at_constant_longitude(lon, method)
+        faces = self.uxgrid.get_faces_at_constant_longitude(
+            lon, use_spherical_bounding_box
+        )
 
         if len(faces) == 0:
-            raise ValueError(f"No intersections found at lon={lon}.")
+            raise ValueError(f"No intersections found at lon={lon}")
 
         grid_at_constant_lon = self.uxgrid.isel(n_face=faces)
 
diff --git a/uxarray/grid/geometry.py b/uxarray/grid/geometry.py
index 9a6566f67..1ae95d99e 100644
--- a/uxarray/grid/geometry.py
+++ b/uxarray/grid/geometry.py
@@ -34,8 +34,7 @@
 
 
 def _unique_points(points, tolerance=ERROR_TOLERANCE):
-    """Identify unique intersection points from a list of points, considering
-    floating point precision errors.
+    """Identify unique intersection points from a list of points, considering floating point precision errors.
 
     Parameters
     ----------
diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py
index d81fa5f51..e7007af26 100644
--- a/uxarray/grid/grid.py
+++ b/uxarray/grid/grid.py
@@ -69,8 +69,9 @@
 )
 
 from uxarray.grid.intersections import (
-    fast_constant_lat_intersections,
-    fast_constant_lon_intersections,
+    constant_lat_intersections_no_extreme,
+    constant_lon_intersections_no_extreme,
+    constant_lat_intersections_face_bounds,
 )
 
 from spatialpandas import GeoDataFrame
@@ -1359,7 +1360,8 @@ def bounds(self):
         """
         if "bounds" not in self._ds:
             warn(
-                "Constructing of `Grid.bounds` has not been optimized, which may lead to a long execution time."
+                "Computing 'Grid.bounds' for the first time. This may take some time...",
+                UserWarning,
             )
             _populate_bounds(self)
 
@@ -2181,132 +2183,153 @@ def isel(self, **dim_kwargs):
                 "Indexing must be along a grid dimension: ('n_node', 'n_edge', 'n_face')"
             )
 
-    def get_edges_at_constant_latitude(self, lat, method="fast"):
-        """Identifies the edges of the grid that intersect with a specified
-        constant latitude.
-
-        This method computes the intersection of grid edges with a given latitude and
-        returns a collection of edges that cross or are aligned with that latitude.
-        The method used for identifying these edges can be controlled by the `method`
-        parameter.
+    def get_edges_at_constant_latitude(self, lat, use_spherical_bounding_box=False):
+        """Identifies the indices of edges that intersect with a line of constant latitude.
 
         Parameters
         ----------
-        lat : float
+        lon : float
             The latitude at which to identify intersecting edges, in degrees.
-        method : str, optional
-            The computational method used to determine edge intersections. Options are:
-            - 'fast': Uses a faster but potentially less accurate method for determining intersections.
-            - 'accurate': Uses a slower but more precise method.
-            Default is 'fast'.
+                use_spherical_bounding_box : bool, optional
+            If `True`,
+            computes the bounding box for each face using great circle arcs for edges
+            and considers extreme minimums or maximums to increase accuracy.
+            Defaults to `False`.
 
         Returns
         -------
-        edges : array
-            A squeezed array of edges that intersect the specified constant latitude.
+        faces : numpy.ndarray
+            An array of edge indices that intersect with the specified latitude.
         """
-        if method == "fast":
-            edges = fast_constant_lat_intersections(
-                lat, self.edge_node_z.values, self.n_edge
+
+        if lat > 90.0 or lat < -90.0:
+            raise ValueError(
+                f"Latitude must be between -90 and 90 degrees. Received {lat}"
+            )
+
+        if use_spherical_bounding_box:
+            raise NotImplementedError(
+                "Computing the intersection using the spherical bounding box"
+                "is not yet supported."
             )
-        elif method == "accurate":
-            raise NotImplementedError("Accurate method not yet implemented.")
         else:
-            raise ValueError(f"Invalid method: {method}.")
+            edges = constant_lat_intersections_no_extreme(
+                lat, self.edge_node_z.values, self.n_edge
+            )
+
         return edges.squeeze()
 
-    def get_faces_at_constant_latitude(self, lat, method="fast"):
-        """Identifies the faces of the grid that intersect with a specified
-        constant latitude.
+    def get_faces_at_constant_latitude(self, lat, use_spherical_bounding_box=False):
+        """
+        Identifies the indices of faces that intersect with a line of constant latitude.
 
-        This method finds the faces (or cells) of the grid that intersect a given latitude
-        by first identifying the intersecting edges and then determining the faces connected
-        to these edges. The method used for identifying edges can be adjusted with the `method`
-        parameter.
+        When `use_spherical_bounding_box` is set to `True`,
+        the bounding box for each face is computed by representing each edge as a great circle arc.
+        This approach takes into account the extreme minimums or maximums along the arcs.
 
         Parameters
         ----------
         lat : float
             The latitude at which to identify intersecting faces, in degrees.
-        method : str, optional
-            The computational method used to determine intersecting edges. Options are:
-            - 'fast': Uses a faster but potentially less accurate method for determining intersections.
-            - 'accurate': Uses a slower but more precise method.
-            Default is 'fast'.
+        use_spherical_bounding_box : bool, optional
+            If `True`,
+            computes the bounding box for each face using great circle arcs for edges
+            and considers extreme minimums or maximums to increase accuracy.
+            Defaults to `False`.
 
         Returns
         -------
-        faces : array
-            An array of unique face indices that intersect the specified latitude.
-            Faces that are invalid or missing (e.g., with a fill value) are excluded
-            from the result.
+        faces : numpy.ndarray
+            An array of face indices that intersect with the specified latitude.
         """
-        edges = self.get_edges_at_constant_latitude(lat, method)
-        faces = np.unique(self.edge_face_connectivity[edges].data.ravel())
 
-        return faces[faces != INT_FILL_VALUE]
+        if lat > 90.0 or lat < -90.0:
+            raise ValueError(
+                f"Latitude must be between -90 and 90 degrees. Received {lat}"
+            )
+
+        if use_spherical_bounding_box:
+            faces = constant_lat_intersections_face_bounds(
+                lat=lat,
+                face_min_lat_rad=self.bounds.values[:, 0, 0],
+                face_max_lat_rad=self.bounds.values[:, 0, 1],
+            )
+            return faces
+        else:
+            edges = self.get_edges_at_constant_latitude(lat, use_spherical_bounding_box)
+            faces = np.unique(self.edge_face_connectivity[edges].data.ravel())
 
-    def get_edges_at_constant_longitude(self, lon, method="fast"):
-        """Identifies the edges of the grid that intersect with a specified
-        constant longitude.
+            return faces[faces != INT_FILL_VALUE]
 
-        This method computes the intersection of grid edges with a given longitude and
-        returns a collection of edges that cross or are aligned with that longitude.
-        The method used for identifying these edges can be controlled by the `method`
-        parameter.
+    def get_edges_at_constant_longitude(self, lon, use_spherical_bounding_box=False):
+        """
+        Identifies the indices of edges that intersect with a line of constant longitude.
 
         Parameters
         ----------
         lon : float
             The longitude at which to identify intersecting edges, in degrees.
-        method : str, optional
-            The computational method used to determine edge intersections. Options are:
-            - 'fast': Uses a faster but potentially less accurate method for determining intersections.
-            - 'accurate': Uses a slower but more precise method.
-            Default is 'fast'.
+        use_spherical_bounding_box : bool, optional
+            If `True`,
+            computes the bounding box for each face using great circle arcs for edges
+            and considers extreme minimums or maximums to increase accuracy.
+            Defaults to `False`.
 
         Returns
         -------
-        edges : array
-            A squeezed array of edges that intersect the specified constant longitude.
+        faces : numpy.ndarray
+            An array of edge indices that intersect with the specified longitude.
         """
-        if method == "fast":
-            edges = fast_constant_lon_intersections(
-                lon, self.edge_node_x.values, self.edge_node_y.values, self.n_edge
+
+        if lon > 180.0 or lon < -180.0:
+            raise ValueError(
+                f"Longitude must be between -180 and 180 degrees. Received {lon}"
+            )
+
+        if use_spherical_bounding_box:
+            raise NotImplementedError(
+                "Computing the intersection using the spherical bounding box"
+                "is not yet supported."
             )
-        elif method == "accurate":
-            raise NotImplementedError("Accurate method not yet implemented.")
         else:
-            raise ValueError(f"Invalid method: {method}.")
-        return edges.squeeze()
+            edges = constant_lon_intersections_no_extreme(
+                lon, self.edge_node_x.values, self.edge_node_y.values, self.n_edge
+            )
+            return edges.squeeze()
 
-    def get_faces_at_constant_longitude(self, lon, method="fast"):
-        """Identifies the faces of the grid that intersect with a specified
-        constant longitude.
+    def get_faces_at_constant_longitude(self, lon, use_spherical_bounding_box=False):
+        """
+        Identifies the indices of faces that intersect with a line of constant longitude.
 
-        This method finds the faces (or cells) of the grid that intersect a given longitude
-        by first identifying the intersecting edges and then determining the faces connected
-        to these edges. The method used for identifying edges can be adjusted with the `method`
-        parameter.
+        When `use_spherical_bounding_box` is set to `True`,
+        the bounding box for each face is computed by representing each edge as a great circle arc.
+        This approach takes into account the extreme minimums or maximums along the arcs.
 
         Parameters
         ----------
         lon : float
             The longitude at which to identify intersecting faces, in degrees.
-        method : str, optional
-            The computational method used to determine intersecting edges. Options are:
-            - 'fast': Uses a faster but potentially less accurate method for determining intersections.
-            - 'accurate': Uses a slower but more precise method.
-            Default is 'fast'.
+        use_spherical_bounding_box : bool, optional
+            If `True`,
+            computes the bounding box for each face using great circle arcs for edges
+            and considers extreme minimums or maximums to increase accuracy.
+            Defaults to `False`.
 
         Returns
         -------
-        faces : array
-            An array of unique face indices that intersect the specified longitude.
-            Faces that are invalid or missing (e.g., with a fill value) are excluded
-            from the result.
+        faces : numpy.ndarray
+            An array of face indices that intersect with the specified longitude.
         """
-        edges = self.get_edges_at_constant_longitude(lon, method)
-        faces = np.unique(self.edge_face_connectivity[edges].data.ravel())
 
-        return faces[faces != INT_FILL_VALUE]
+        if use_spherical_bounding_box:
+            raise NotImplementedError(
+                "Computing the intersection using the spherical bounding box is not"
+                "yet supported."
+            )
+        else:
+            edges = self.get_edges_at_constant_longitude(
+                lon, use_spherical_bounding_box
+            )
+            faces = np.unique(self.edge_face_connectivity[edges].data.ravel())
+
+            return faces[faces != INT_FILL_VALUE]
diff --git a/uxarray/grid/intersections.py b/uxarray/grid/intersections.py
index 3d3e9d96e..147a4b156 100644
--- a/uxarray/grid/intersections.py
+++ b/uxarray/grid/intersections.py
@@ -11,16 +11,19 @@
 
 
 @njit(parallel=True, nogil=True, cache=True)
-def fast_constant_lat_intersections(lat, edge_node_z, n_edge):
-    """Determine which edges intersect a constant line of latitude on a sphere,
-    including edges that lie exactly along the latitude.
+def constant_lat_intersections_no_extreme(lat, edge_node_z, n_edge):
+    """Determine which edges intersect a constant line of latitude on a
+    sphere, without wrapping to the opposite longitude, with extremes
+    along each great circle arc not considered.
 
     Parameters
     ----------
     lat:
         Constant latitude value in degrees.
-    edge_node_z:
-        Array of shape (n_edge, 2) containing z-coordinates of the edge nodes.
+    edge_node_x:
+        Array of shape (n_edge, 2) containing x-coordinates of the edge nodes.
+    edge_node_y:
+        Array of shape (n_edge, 2) containing y-coordinates of the edge nodes.
     n_edge:
         Total number of edges to check.
 
@@ -38,15 +41,8 @@ def fast_constant_lat_intersections(lat, edge_node_z, n_edge):
 
     # Iterate through each edge and check for intersections
     for i in prange(n_edge):
-        # Get the z-coordinates of the edge's nodes
-        z0 = edge_node_z[i, 0]
-        z1 = edge_node_z[i, 1]
-
         # Check if the edge crosses the constant latitude or lies exactly on it
-        if (z0 - z_constant) * (z1 - z_constant) < 0.0 or (
-            abs(z0 - z_constant) < ERROR_TOLERANCE
-            and abs(z1 - z_constant) < ERROR_TOLERANCE
-        ):
+        if edge_intersects_constant_lat_no_extreme(edge_node_z[i], z_constant):
             intersecting_edges_mask[i] = 1
 
     intersecting_edges = np.argwhere(intersecting_edges_mask)
@@ -54,10 +50,28 @@ def fast_constant_lat_intersections(lat, edge_node_z, n_edge):
     return np.unique(intersecting_edges)
 
 
+@njit(cache=True, nogil=True)
+def edge_intersects_constant_lat_no_extreme(edge_node_z, z_constant):
+    """Helper to compute whether an edge intersects a line of constant latitude."""
+
+    # z coordinate of edge nodes
+    z0 = edge_node_z[0]
+    z1 = edge_node_z[1]
+
+    if (z0 - z_constant) * (z1 - z_constant) < 0.0 or (
+        abs(z0 - z_constant) < ERROR_TOLERANCE
+        and abs(z1 - z_constant) < ERROR_TOLERANCE
+    ):
+        return True
+    else:
+        return False
+
+
 @njit(parallel=True, nogil=True, cache=True)
-def fast_constant_lon_intersections(lon, edge_node_x, edge_node_y, n_edge):
+def constant_lon_intersections_no_extreme(lon, edge_node_x, edge_node_y, n_edge):
     """Determine which edges intersect a constant line of longitude on a
-    sphere, without wrapping to the opposite longitude.
+    sphere, without wrapping to the opposite longitude, with extremes
+    along each great circle arc not considered.
 
     Parameters
     ----------
@@ -108,6 +122,70 @@ def fast_constant_lon_intersections(lon, edge_node_x, edge_node_y, n_edge):
     return np.unique(intersecting_edges)
 
 
+@njit
+def constant_lat_intersections_face_bounds(lat, face_min_lat_rad, face_max_lat_rad):
+    """Identifies the candidate faces on a grid that intersect with a given
+    constant latitude.
+
+    This function checks whether the specified latitude, `lat`, in degrees lies within
+    the latitude bounds of grid faces, defined by `face_min_lat_rad` and `face_max_lat_rad`,
+    which are given in radians. The function returns the indices of the faces where the
+    latitude is within these bounds.
+
+    Parameters
+    ----------
+    lat : float
+        The latitude in degrees for which to find intersecting faces.
+    face_min_lat_rad : numpy.ndarray
+        A 1D array containing the minimum latitude bounds (in radians) of each face.
+    face_max_lat_rad : numpy.ndarray
+        A 1D array containing the maximum latitude bounds (in radians) of each face.
+
+    Returns
+    -------
+    candidate_faces : numpy.ndarray
+        A 1D array containing the indices of the faces that intersect with the given latitude.
+    """
+    lat = np.deg2rad(lat)
+    within_bounds = (face_min_lat_rad <= lat) & (face_max_lat_rad >= lat)
+    candidate_faces = np.where(within_bounds)[0]
+    return candidate_faces
+
+
+@njit(cache=True)
+def constant_lon_intersections_face_bounds(lon, face_min_lon_rad, face_max_lon_rad):
+    """Identifies the candidate faces on a grid that intersect with a given
+    constant longitude.
+
+    This function checks whether the specified longitude, `lon`, in degrees lies within
+    the longitude bounds of grid faces, defined by `face_min_lon_rad` and `face_max_lon_rad`,
+    which are given in radians. The function returns the indices of the faces where the
+    longitude is within these bounds.
+
+    Parameters
+    ----------
+    lon : float
+        The longitude in degrees for which to find intersecting faces.
+    face_min_lon_rad : numpy.ndarray
+        A 1D array containing the minimum longitude bounds (in radians) of each face.
+    face_max_lon_rad : numpy.ndarray
+        A 1D array containing the maximum longitude bounds (in radians) of each face.
+
+    Returns
+    -------
+    candidate_faces : numpy.ndarray
+        A 1D array containing the indices of the faces that intersect with the given longitude.
+    """
+
+    # lon = np.deg2rad(lon)
+    # lon = (lon + 2 * np.pi) % (2 * np.pi)
+    # within_bounds = (face_min_lon_rad <= lon) & (face_max_lon_rad >= lon)
+    # candidate_faces = np.where(within_bounds)[0]
+    # return candidate_faces
+
+    raise NotImplementedError
+
+
 def gca_gca_intersection(gca1_cart, gca2_cart, fma_disabled=True):
     """Calculate the intersection point(s) of two Great Circle Arcs (GCAs) in a
     Cartesian coordinate system.


Test patch:
diff --git a/test/test_cross_sections.py b/test/test_cross_sections.py
index 55068b6c5..1847e69b3 100644
--- a/test/test_cross_sections.py
+++ b/test/test_cross_sections.py
@@ -1,5 +1,6 @@
 import uxarray as ux
 import pytest
+import numpy as np
 from pathlib import Path
 import os
 
@@ -10,7 +11,9 @@
 quad_hex_grid_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / 'grid.nc'
 quad_hex_data_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / 'data.nc'
 
-cube_sphere_grid = current_path / "meshfiles" / "geos-cs" / "c12" / "test-c12.native.nc4"
+cube_sphere_grid = current_path / "meshfiles" / "ugrid" / "outCSne30" / "outCSne30.ug"
+
+from uxarray.grid.intersections import constant_lat_intersections_face_bounds
 
 
 
@@ -32,33 +35,38 @@ class TestQuadHex:
     All four faces intersect a constant latitude of 0.0
     """
 
-    def test_constant_lat_cross_section_grid(self):
+    @pytest.mark.parametrize("use_spherical_bounding_box", [True, False])
+    def test_constant_lat_cross_section_grid(self, use_spherical_bounding_box):
+
+
+
         uxgrid = ux.open_grid(quad_hex_grid_path)
 
-        grid_top_two = uxgrid.cross_section.constant_latitude(lat=0.1)
+        grid_top_two = uxgrid.cross_section.constant_latitude(lat=0.1, use_spherical_bounding_box=use_spherical_bounding_box)
 
         assert grid_top_two.n_face == 2
 
-        grid_bottom_two = uxgrid.cross_section.constant_latitude(lat=-0.1)
+        grid_bottom_two = uxgrid.cross_section.constant_latitude(lat=-0.1, use_spherical_bounding_box=use_spherical_bounding_box)
 
         assert grid_bottom_two.n_face == 2
 
-        grid_all_four = uxgrid.cross_section.constant_latitude(lat=0.0)
+        grid_all_four = uxgrid.cross_section.constant_latitude(lat=0.0, use_spherical_bounding_box=use_spherical_bounding_box)
 
         assert grid_all_four.n_face == 4
 
         with pytest.raises(ValueError):
             # no intersections found at this line
-            uxgrid.cross_section.constant_latitude(lat=10.0)
+            uxgrid.cross_section.constant_latitude(lat=10.0, use_spherical_bounding_box=use_spherical_bounding_box)
 
-    def test_constant_lon_cross_section_grid(self):
+    @pytest.mark.parametrize("use_spherical_bounding_box", [False])
+    def test_constant_lon_cross_section_grid(self, use_spherical_bounding_box):
         uxgrid = ux.open_grid(quad_hex_grid_path)
 
-        grid_left_two = uxgrid.cross_section.constant_longitude(lon=-0.1)
+        grid_left_two = uxgrid.cross_section.constant_longitude(lon=-0.1, use_spherical_bounding_box=use_spherical_bounding_box)
 
         assert grid_left_two.n_face == 2
 
-        grid_right_two = uxgrid.cross_section.constant_longitude(lon=0.2)
+        grid_right_two = uxgrid.cross_section.constant_longitude(lon=0.2, use_spherical_bounding_box=use_spherical_bounding_box)
 
         assert grid_right_two.n_face == 2
 
@@ -66,59 +74,110 @@ def test_constant_lon_cross_section_grid(self):
             # no intersections found at this line
             uxgrid.cross_section.constant_longitude(lon=10.0)
 
-
-    def test_constant_lat_cross_section_uxds(self):
+    @pytest.mark.parametrize("use_spherical_bounding_box", [False])
+    def test_constant_lat_cross_section_uxds(self, use_spherical_bounding_box):
         uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path)
+        uxds.uxgrid.normalize_cartesian_coordinates()
 
-        da_top_two = uxds['t2m'].cross_section.constant_latitude(lat=0.1)
+        da_top_two = uxds['t2m'].cross_section.constant_latitude(lat=0.1, use_spherical_bounding_box=use_spherical_bounding_box)
 
         nt.assert_array_equal(da_top_two.data, uxds['t2m'].isel(n_face=[1, 2]).data)
 
-        da_bottom_two = uxds['t2m'].cross_section.constant_latitude(lat=-0.1)
+        da_bottom_two = uxds['t2m'].cross_section.constant_latitude(lat=-0.1, use_spherical_bounding_box=use_spherical_bounding_box)
 
         nt.assert_array_equal(da_bottom_two.data, uxds['t2m'].isel(n_face=[0, 3]).data)
 
-        da_all_four = uxds['t2m'].cross_section.constant_latitude(lat=0.0)
+        da_all_four = uxds['t2m'].cross_section.constant_latitude(lat=0.0, use_spherical_bounding_box=use_spherical_bounding_box)
 
         nt.assert_array_equal(da_all_four.data , uxds['t2m'].data)
 
         with pytest.raises(ValueError):
             # no intersections found at this line
-            uxds['t2m'].cross_section.constant_latitude(lat=10.0)
+            uxds['t2m'].cross_section.constant_latitude(lat=10.0, use_spherical_bounding_box=use_spherical_bounding_box)
 
-    def test_constant_lon_cross_section_uxds(self):
+    @pytest.mark.parametrize("use_spherical_bounding_box", [False])
+    def test_constant_lon_cross_section_uxds(self, use_spherical_bounding_box):
         uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path)
+        uxds.uxgrid.normalize_cartesian_coordinates()
 
-        da_left_two = uxds['t2m'].cross_section.constant_longitude(lon=-0.1)
+        da_left_two = uxds['t2m'].cross_section.constant_longitude(lon=-0.1, use_spherical_bounding_box=use_spherical_bounding_box)
 
         nt.assert_array_equal(da_left_two.data, uxds['t2m'].isel(n_face=[0, 2]).data)
 
-        da_right_two = uxds['t2m'].cross_section.constant_longitude(lon=0.2)
+        da_right_two = uxds['t2m'].cross_section.constant_longitude(lon=0.2, use_spherical_bounding_box=use_spherical_bounding_box)
 
         nt.assert_array_equal(da_right_two.data, uxds['t2m'].isel(n_face=[1, 3]).data)
 
         with pytest.raises(ValueError):
             # no intersections found at this line
-            uxds['t2m'].cross_section.constant_longitude(lon=10.0)
+            uxds['t2m'].cross_section.constant_longitude(lon=10.0, use_spherical_bounding_box=use_spherical_bounding_box)
 
 
-class TestGeosCubeSphere:
-    def test_north_pole(self):
+class TestCubeSphere:
+    @pytest.mark.parametrize("use_spherical_bounding_box", [True, False])
+    def test_north_pole(self, use_spherical_bounding_box):
         uxgrid = ux.open_grid(cube_sphere_grid)
 
         lats = [89.85, 89.9, 89.95, 89.99]
 
         for lat in lats:
-            cross_grid = uxgrid.cross_section.constant_latitude(lat=lat)
+            cross_grid = uxgrid.cross_section.constant_latitude(lat=lat, use_spherical_bounding_box=use_spherical_bounding_box)
             # Cube sphere grid should have 4 faces centered around the pole
             assert cross_grid.n_face == 4
-
-    def test_south_pole(self):
+    @pytest.mark.parametrize("use_spherical_bounding_box", [True, False])
+    def test_south_pole(self, use_spherical_bounding_box):
         uxgrid = ux.open_grid(cube_sphere_grid)
 
         lats = [-89.85, -89.9, -89.95, -89.99]
 
         for lat in lats:
-            cross_grid = uxgrid.cross_section.constant_latitude(lat=lat)
+            cross_grid = uxgrid.cross_section.constant_latitude(lat=lat, use_spherical_bounding_box=use_spherical_bounding_box)
             # Cube sphere grid should have 4 faces centered around the pole
             assert cross_grid.n_face == 4
+
+
+
+class TestCandidateFacesUsingBounds:
+
+    def test_constant_lat(self):
+        bounds = np.array([
+            [[-45, 45], [0, 360]],
+            [[-90, -45], [0, 360]],
+            [[45, 90], [0, 360]],
+        ])
+
+        bounds_rad = np.deg2rad(bounds)
+
+        const_lat = 0
+
+        candidate_faces = constant_lat_intersections_face_bounds(
+            lat=const_lat,
+            face_min_lat_rad=bounds_rad[:, 0, 0],
+            face_max_lat_rad=bounds_rad[:, 0, 1],
+        )
+
+        # Expected output
+        expected_faces = np.array([0])
+
+        # Test the function output
+        nt.assert_array_equal(candidate_faces, expected_faces)
+
+    def test_constant_lat_out_of_bounds(self):
+
+        bounds = np.array([
+            [[-45, 45], [0, 360]],
+            [[-90, -45], [0, 360]],
+            [[45, 90], [0, 360]],
+        ])
+
+        bounds_rad = np.deg2rad(bounds)
+
+        const_lat = 100
+
+        candidate_faces = constant_lat_intersections_face_bounds(
+            lat=const_lat,
+            face_min_lat_rad=bounds_rad[:, 0, 0],
+            face_max_lat_rad=bounds_rad[:, 0, 1],
+        )
+
+        assert len(candidate_faces) == 0