@@ -103,7 +103,7 @@ def _unique_points(points, tolerance=ERROR_TOLERANCE):
103103
104104@njit (cache = True )
105105def _pad_closed_face_nodes (
106- face_node_connectivity , n_face , n_max_face_nodes , n_nodes_per_face
106+ face_node_connectivity , n_face , n_max_face_nodes , n_nodes_per_face
107107):
108108 """Pads a closed array of face nodes by inserting the first element at any
109109 point a fill value is encountered.
@@ -123,14 +123,14 @@ def _pad_closed_face_nodes(
123123
124124
125125def _build_polygon_shells (
126- node_lon ,
127- node_lat ,
128- face_node_connectivity ,
129- n_face ,
130- n_max_face_nodes ,
131- n_nodes_per_face ,
132- projection = None ,
133- central_longitude = 0.0 ,
126+ node_lon ,
127+ node_lat ,
128+ face_node_connectivity ,
129+ n_face ,
130+ n_max_face_nodes ,
131+ n_nodes_per_face ,
132+ projection = None ,
133+ central_longitude = 0.0 ,
134134):
135135 """Builds an array of polygon shells, which can be used with Shapely to
136136 construct polygons."""
@@ -266,7 +266,7 @@ def _grid_to_polygon_geodataframe(grid, periodic_elements, projection, project,
266266
267267
268268def _build_geodataframe_without_antimeridian (
269- polygon_shells , projected_polygon_shells , antimeridian_face_indices , engine
269+ polygon_shells , projected_polygon_shells , antimeridian_face_indices , engine
270270):
271271 """Builds a ``spatialpandas.GeoDataFrame`` or
272272 ``geopandas.GeoDataFrame``excluding any faces that cross the
@@ -295,10 +295,10 @@ def _build_geodataframe_without_antimeridian(
295295
296296
297297def _build_geodataframe_with_antimeridian (
298- polygon_shells ,
299- projected_polygon_shells ,
300- antimeridian_face_indices ,
301- engine ,
298+ polygon_shells ,
299+ projected_polygon_shells ,
300+ antimeridian_face_indices ,
301+ engine ,
302302):
303303 """Builds a ``spatialpandas.GeoDataFrame`` or ``geopandas.GeoDataFrame``
304304 including any faces that cross the antimeridian."""
@@ -317,9 +317,9 @@ def _build_geodataframe_with_antimeridian(
317317
318318
319319def _build_corrected_shapely_polygons (
320- polygon_shells ,
321- projected_polygon_shells ,
322- antimeridian_face_indices ,
320+ polygon_shells ,
321+ projected_polygon_shells ,
322+ antimeridian_face_indices ,
323323):
324324 if projected_polygon_shells is not None :
325325 # use projected shells if a projection is applied
@@ -438,7 +438,7 @@ def _build_corrected_polygon_shells(polygon_shells):
438438
439439
440440def _grid_to_matplotlib_polycollection (
441- grid , periodic_elements , projection = None , ** kwargs
441+ grid , periodic_elements , projection = None , ** kwargs
442442):
443443 """Constructs and returns a ``matplotlib.collections.PolyCollection``"""
444444
@@ -642,7 +642,7 @@ def _get_polygons(grid, periodic_elements, projection=None, apply_projection=Tru
642642
643643
644644def _grid_to_matplotlib_linecollection (
645- grid , periodic_elements , projection = None , ** kwargs
645+ grid , periodic_elements , projection = None , ** kwargs
646646):
647647 """Constructs and returns a ``matplotlib.collections.LineCollection``"""
648648
@@ -913,7 +913,7 @@ def _check_intersection(ref_edge_xyz, edges_xyz):
913913 for i in range (n_edges ):
914914 edge_xyz = edges_xyz [i ]
915915 if allclose (
916- intersection_point , edge_xyz [0 ], atol = ERROR_TOLERANCE
916+ intersection_point , edge_xyz [0 ], atol = ERROR_TOLERANCE
917917 ) or allclose (intersection_point , edge_xyz [1 ], atol = ERROR_TOLERANCE ):
918918 return 0
919919
@@ -1140,10 +1140,10 @@ def insert_pt_in_latlonbox(old_box, new_pt, is_lon_periodic=True):
11401140
11411141@njit (cache = True )
11421142def _populate_face_latlon_bound (
1143- face_edges_xyz ,
1144- face_edges_lonlat ,
1145- is_latlonface = False ,
1146- is_GCA_list = None ,
1143+ face_edges_xyz ,
1144+ face_edges_lonlat ,
1145+ is_latlonface = False ,
1146+ is_GCA_list = None ,
11471147):
11481148 # Check if face_edges contains pole points
11491149 has_north_pole = pole_point_inside_polygon (1 , face_edges_xyz , face_edges_lonlat )
@@ -1198,9 +1198,9 @@ def _populate_face_latlon_bound(
11981198 # Check if the node matches the pole point or if the pole point is within the edge
11991199 max_abs_diff = np .max (np .abs (n1_cart - pole_point_xyz ))
12001200 if max_abs_diff <= ERROR_TOLERANCE or point_within_gca (
1201- pole_point_xyz ,
1202- n1_cart ,
1203- n2_cart ,
1201+ pole_point_xyz ,
1202+ n1_cart ,
1203+ n2_cart ,
12041204 ):
12051205 is_center_pole = False
12061206 face_latlon_array = insert_pt_in_latlonbox (
@@ -1292,15 +1292,15 @@ def _populate_face_latlon_bound(
12921292
12931293 # Insert extreme latitude points into the latlonbox
12941294 if (
1295- abs (node1_lat_rad - lat_max ) > ERROR_TOLERANCE
1296- and abs (node2_lat_rad - lat_max ) > ERROR_TOLERANCE
1295+ abs (node1_lat_rad - lat_max ) > ERROR_TOLERANCE
1296+ and abs (node2_lat_rad - lat_max ) > ERROR_TOLERANCE
12971297 ):
12981298 face_latlon_array = insert_pt_in_latlonbox (
12991299 face_latlon_array , np .array ([lat_max , node1_lon_rad ])
13001300 )
13011301 elif (
1302- abs (node1_lat_rad - lat_min ) > ERROR_TOLERANCE
1303- and abs (node2_lat_rad - lat_min ) > ERROR_TOLERANCE
1302+ abs (node1_lat_rad - lat_min ) > ERROR_TOLERANCE
1303+ and abs (node2_lat_rad - lat_min ) > ERROR_TOLERANCE
13041304 ):
13051305 face_latlon_array = insert_pt_in_latlonbox (
13061306 face_latlon_array , np .array ([lat_min , node1_lon_rad ])
@@ -1315,23 +1315,23 @@ def _populate_face_latlon_bound(
13151315
13161316@njit (cache = True , parallel = True )
13171317def compute_temp_latlon_array (
1318- face_node_connectivity ,
1319- faces_edges_cartesian ,
1320- faces_edges_lonlat_rad ,
1321- n_nodes_per_face ,
1322- is_latlonface ,
1323- is_face_GCA_list ,
1324- INT_FILL_VALUE ,
1318+ face_node_connectivity ,
1319+ faces_edges_cartesian ,
1320+ faces_edges_lonlat_rad ,
1321+ n_nodes_per_face ,
1322+ is_latlonface ,
1323+ is_face_GCA_list ,
1324+ INT_FILL_VALUE ,
13251325):
13261326 n_face = face_node_connectivity .shape [0 ]
13271327 temp_latlon_array = np .full ((n_face , 2 , 2 ), INT_FILL_VALUE , dtype = np .float64 )
13281328 for face_idx in prange (n_face ):
13291329 cur_face_edges_cartesian = faces_edges_cartesian [
1330- face_idx , 0 : n_nodes_per_face [face_idx ]
1331- ]
1330+ face_idx , 0 : n_nodes_per_face [face_idx ]
1331+ ]
13321332 cur_faces_edges_lonlat_rad = faces_edges_lonlat_rad [
1333- face_idx , 0 : n_nodes_per_face [face_idx ]
1334- ]
1333+ face_idx , 0 : n_nodes_per_face [face_idx ]
1334+ ]
13351335 if is_face_GCA_list is not None :
13361336 is_GCA_list = is_face_GCA_list [face_idx ]
13371337 else :
@@ -1347,7 +1347,12 @@ def compute_temp_latlon_array(
13471347
13481348
13491349def _populate_bounds (
1350- grid , is_latlonface : bool = False , is_face_GCA_list = None , return_array = False
1350+ grid ,
1351+ faces_edges_cartesian : np .ndarray = None ,
1352+ faces_edges_lonlat_rad : np .ndarray = None ,
1353+ is_latlonface : bool = False ,
1354+ is_face_GCA_list = None ,
1355+ return_array = False
13511356):
13521357 """Populates the bounds of the grid based on the geometry of its faces,
13531358 taking into account special conditions such as faces crossing the
@@ -1357,6 +1362,21 @@ def _populate_bounds(
13571362
13581363 Parameters
13591364 ----------
1365+ grid : object
1366+ The grid object whose internal representation will be updated with the computed
1367+ face bounds.
1368+
1369+ faces_edges_cartesian : np.ndarray, optional
1370+ An array of shape (n_face, n_max_face_edges, 2, 3) containing the Cartesian coordinates
1371+ of each face's edges. This array may include dummy values for grids with holes.
1372+ Default is None.
1373+
1374+ faces_edges_lonlat_rad : np.ndarray, optional
1375+ An array of shape (n_face, n_max_face_edges, 2, 2) containing the longitude and latitude
1376+ (in radians) coordinates of each face's edges. This array may include dummy values for
1377+ grids with holes.
1378+ Default is None.
1379+
13601380 is_latlonface : bool, optional
13611381 A global flag that indicates if faces are latlon faces. If True, all faces
13621382 are treated as latlon faces, meaning that all edges are either longitude or
@@ -1407,25 +1427,29 @@ def _populate_bounds(
14071427 """
14081428
14091429 # Ensure grid's cartesian coordinates are normalized
1430+ # TODO: Is it possible to have a more flexible normalization? (Avoid duplicated normalizations)
14101431 grid .normalize_cartesian_coordinates ()
14111432
14121433 # Prepare data for Numba functions
1413- faces_edges_cartesian = _get_cartesian_face_edge_nodes (
1414- grid .face_node_connectivity .values ,
1415- grid .n_face ,
1416- grid .n_max_face_edges ,
1417- grid .node_x .values ,
1418- grid .node_y .values ,
1419- grid .node_z .values ,
1420- )
1434+ # TODO: remove the duplicate call for this function in both zonal average and bounds
1435+ if faces_edges_cartesian is None :
1436+ faces_edges_cartesian = _get_cartesian_face_edge_nodes (
1437+ grid .face_node_connectivity .values ,
1438+ grid .n_face ,
1439+ grid .n_max_face_edges ,
1440+ grid .node_x .values ,
1441+ grid .node_y .values ,
1442+ grid .node_z .values ,
1443+ )
14211444
1422- faces_edges_lonlat_rad = _get_lonlat_rad_face_edge_nodes (
1423- grid .face_node_connectivity .values ,
1424- grid .n_face ,
1425- grid .n_max_face_edges ,
1426- grid .node_lon .values ,
1427- grid .node_lat .values ,
1428- )
1445+ if faces_edges_lonlat_rad is None :
1446+ faces_edges_lonlat_rad = _get_lonlat_rad_face_edge_nodes (
1447+ grid .face_node_connectivity .values ,
1448+ grid .n_face ,
1449+ grid .n_max_face_edges ,
1450+ grid .node_lon .values ,
1451+ grid .node_lat .values ,
1452+ )
14291453
14301454 n_nodes_per_face = grid .n_nodes_per_face .values
14311455
@@ -1512,16 +1536,16 @@ def stereographic_projection(lon, lat, central_lon, central_lat):
15121536
15131537 # Calculate constant used for calculation
15141538 k = 2.0 / (
1515- 1.0
1516- + np .sin (central_lat ) * np .sin (lat )
1517- + np .cos (central_lat ) * np .cos (lat ) * np .cos (lon - central_lon )
1539+ 1.0
1540+ + np .sin (central_lat ) * np .sin (lat )
1541+ + np .cos (central_lat ) * np .cos (lat ) * np .cos (lon - central_lon )
15181542 )
15191543
15201544 # Calculate the x and y coordinates
15211545 x = k * np .cos (lat ) * np .sin (lon - central_lon )
15221546 y = k * (
1523- np .cos (central_lat ) * np .sin (lat )
1524- - np .sin (central_lat ) * np .cos (lat ) * np .cos (lon - central_lon )
1547+ np .cos (central_lat ) * np .sin (lat )
1548+ - np .sin (central_lat ) * np .cos (lat ) * np .cos (lon - central_lon )
15251549 )
15261550
15271551 return x , y
@@ -1557,7 +1581,7 @@ def inverse_stereographic_projection(x, y, central_lon, central_lat):
15571581 central_lat = np .deg2rad (central_lat )
15581582
15591583 # Calculate constants used for calculation
1560- p = np .sqrt (x ** 2 + y ** 2 )
1584+ p = np .sqrt (x ** 2 + y ** 2 )
15611585
15621586 c = 2 * np .arctan (p / 2 )
15631587
@@ -1576,9 +1600,9 @@ def inverse_stereographic_projection(x, y, central_lon, central_lat):
15761600
15771601@njit (cache = True )
15781602def point_in_face (
1579- edges_xyz ,
1580- point_xyz ,
1581- inclusive = True ,
1603+ edges_xyz ,
1604+ point_xyz ,
1605+ inclusive = True ,
15821606):
15831607 """Determines if a point lies inside a face.
15841608
@@ -1628,9 +1652,9 @@ def point_in_face(
16281652 for ind in range (len (edges_xyz )):
16291653 # If the point lies on an edge, return True if inclusive
16301654 if point_within_gca (
1631- point_xyz ,
1632- edges_xyz [ind ][0 ],
1633- edges_xyz [ind ][1 ],
1655+ point_xyz ,
1656+ edges_xyz [ind ][0 ],
1657+ edges_xyz [ind ][1 ],
16341658 ):
16351659 if inclusive :
16361660 return True
@@ -1723,7 +1747,7 @@ def _populate_max_face_radius(self):
17231747
17241748@njit (cache = True )
17251749def calculate_max_face_radius (
1726- face_node_connectivity , node_lats_rad , node_lons_rad , face_lats_rad , face_lons_rad
1750+ face_node_connectivity , node_lats_rad , node_lons_rad , face_lats_rad , face_lons_rad
17271751):
17281752 """Finds the max face radius in the mesh.
17291753 Parameters
@@ -1797,7 +1821,7 @@ def haversine_distance(lon_a, lat_a, lon_b, lat_b):
17971821
17981822 # Haversine formula
17991823 equation_in_sqrt = (np .sin (dlat / 2 ) ** 2 ) + np .cos (lat_a ) * np .cos (lat_b ) * (
1800- np .sin (dlon / 2 ) ** 2
1824+ np .sin (dlon / 2 ) ** 2
18011825 )
18021826 distance = 2 * np .arcsin (np .sqrt (equation_in_sqrt ))
18031827
0 commit comments