Skip to content

Clean up and vectorize the face_areas API (#1571)#1577

Open
rajeeja wants to merge 1 commit into
mainfrom
rajeeja/area_cleanup
Open

Clean up and vectorize the face_areas API (#1571)#1577
rajeeja wants to merge 1 commit into
mainfrom
rajeeja/area_cleanup

Conversation

@rajeeja

@rajeeja rajeeja commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Closes #1571.

  1. Un-deprecate compute_face_areas() as the public quadrature-capable entry point — returns areas by default, with return_jacobian and as_dataarray flags.
  2. Rename the internal worker to _compute_face_areas_and_jacobian and privatize the low-level area.py helpers.
  3. Document the quadrature_rule/order/latitude_adjusted_area options; note calculate_total_face_area is equivalent to compute_face_areas().sum().
  4. Vectorize get_all_face_area_from_coords with prange and hoist the per-face quadrature setup out of the loop — 6.3x faster with identical areas.

Un-deprecate compute_face_areas() as the public quadrature-capable entry
point (areas by default, return_jacobian and as_dataarray flags), rename
the internal worker to _compute_face_areas_and_jacobian, privatize the
low-level area.py helpers, and document the quadrature options. Vectorize
get_all_face_area_from_coords with prange and hoist the per-face
quadrature setup out of the loop for a 6.3x speedup with identical areas.
Closes #1571.
@review-notebook-app

Copy link
Copy Markdown

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@Sevans711 Sevans711 added documentation Improvements or additions to documentation redesign Content relating to the ongoing redesign improvement Improvements on existing features or infrastructure run-benchmark Run ASV benchmark workflow labels Jul 15, 2026
@Sevans711 Sevans711 self-requested a review July 15, 2026 23:16
@Sevans711

Copy link
Copy Markdown
Collaborator

Thank you for these changes! I added run-benchmarks label but then realized I'm not sure if there are any benchmarks which actually rely on face_areas. If there's no visible impact, then would you be able to add at least one face_areas benchmark? If the impact is visible already, I don't feel strongly about needing a face_areas specific benchmark.

I'll come back to this soon (probably tomorrow), and if the benchmark impact is visible then I will review the rest!

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

ASV Benchmarking

Benchmark Comparison Results

Benchmarks that have improved:

Change Before [736bc03] After [a9ef43d] Ratio Benchmark (Parameter)
- 579M 391M 0.68 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
- 699M 390M 0.56 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
- 498M 384M 0.77 mpas_ocean.Gradient.peakmem_gradient('480km')

Benchmarks that have stayed the same:

Change Before [736bc03] After [a9ef43d] Ratio Benchmark (Parameter)
9.88±0.04μs 9.93±0.03μs 1 bench_connectivity.Connectivity.time_edge_face('120km')
10.1±0.09μs 10.4±0.2μs 1.03 bench_connectivity.Connectivity.time_edge_face('480km')
9.83±0.05μs 10.2±0.04μs 1.04 bench_connectivity.Connectivity.time_edge_node('120km')
10.1±0.2μs 10.4±0.1μs 1.03 bench_connectivity.Connectivity.time_edge_node('480km')
10.2±0.2μs 10.1±0.1μs 0.99 bench_connectivity.Connectivity.time_face_edge('120km')
10.4±0.1μs 10.5±0.3μs 1.01 bench_connectivity.Connectivity.time_face_edge('480km')
10.0±0.06μs 10.2±0.03μs 1.02 bench_connectivity.Connectivity.time_face_face('120km')
10.1±0.08μs 10.4±0.1μs 1.03 bench_connectivity.Connectivity.time_face_face('480km')
19.9±0.1μs 20.9±0.2μs 1.05 bench_connectivity.Connectivity.time_face_node('120km')
20.5±0.1μs 20.4±0.2μs 1 bench_connectivity.Connectivity.time_face_node('480km')
9.86±0.04μs 9.93±0.1μs 1.01 bench_connectivity.Connectivity.time_node_edge('120km')
10.1±0.07μs 10.2±0.1μs 1.01 bench_connectivity.Connectivity.time_node_edge('480km')
10.0±0.06μs 10.0±0.07μs 1 bench_connectivity.Connectivity.time_node_face('120km')
10.4±0.3μs 10.2±0.08μs 0.98 bench_connectivity.Connectivity.time_node_face('480km')
389M 389M 1 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
419M 419M 1 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
13.2±0.1ms 12.8±0.02ms 0.97 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
3.44±0.08ms 3.35±0.1ms 0.97 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
17.3±0.1ms 17.4±0.09ms 1 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
1.99±0.03ms 1.99±0.04ms 1 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
980±5ms 978±1ms 1 import.Imports.timeraw_import_uxarray
868±8ns 865±2ns 1 mpas_ocean.CheckNorm.time_check_norm('120km')
858±20ns 870±6ns 1.01 mpas_ocean.CheckNorm.time_check_norm('480km')
716±3ms 715±1ms 1 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('120km')
44.8±0.2ms 44.7±0.09ms 1 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('480km')
616±9μs 619±6μs 1 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('120km')
550±10μs 543±10μs 0.99 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('480km')
5.02±0.03ms 5.04±0.02ms 1 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('120km')
3.51±0.03ms 3.49±0.03ms 0.99 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('480km')
3.31±0.01s 3.31±0s 1 mpas_ocean.ConstructFaceLatLon.time_welzl('120km')
212±0.8ms 214±1ms 1.01 mpas_ocean.ConstructFaceLatLon.time_welzl('480km')
20.2±0.5ms 19.7±0.02ms 0.98 mpas_ocean.ConstructTreeStructures.time_ball_tree('120km')
1.07±0.01ms 1.06±0.01ms 1 mpas_ocean.ConstructTreeStructures.time_ball_tree('480km')
10.6±0.01ms 10.6±0.01ms 1 mpas_ocean.ConstructTreeStructures.time_kd_tree('120km')
696±8μs 690±10μs 0.99 mpas_ocean.ConstructTreeStructures.time_kd_tree('480km')
647±2ms 648±3ms 1 mpas_ocean.CrossSections.time_const_lat('120km', 1)
329±0.8ms 331±0.8ms 1.01 mpas_ocean.CrossSections.time_const_lat('120km', 2)
169±0.6ms 170±0.6ms 1 mpas_ocean.CrossSections.time_const_lat('120km', 4)
485±1ms 486±4ms 1 mpas_ocean.CrossSections.time_const_lat('480km', 1)
248±0.7ms 245±1ms 0.99 mpas_ocean.CrossSections.time_const_lat('480km', 2)
125±0.4ms 127±0.5ms 1.01 mpas_ocean.CrossSections.time_const_lat('480km', 4)
21.9±0.04ms 21.9±0.02ms 1 mpas_ocean.DualMesh.time_dual_mesh_construction('120km')
2.56±0.01ms 2.57±0.02ms 1 mpas_ocean.DualMesh.time_dual_mesh_construction('480km')
843±10ms 843±10ms 1 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', False)
49.3±0.6ms 50.0±0.2ms 1.01 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', True)
72.1±0.7ms 72.5±0.2ms 1.01 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', False)
5.31±0.04ms 5.27±0.05ms 0.99 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', True)
404M 404M 1 mpas_ocean.Gradient.peakmem_gradient('120km')
164±0.3ms 170±3ms 1.03 mpas_ocean.Gradient.time_gradient('120km')
11.4±0.05ms 11.4±0.01ms 1 mpas_ocean.Gradient.time_gradient('480km')
191±1μs 191±2μs 1 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('120km')
88.2±0.9μs 87.7±0.6μs 0.99 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('480km')
351M 351M 1 mpas_ocean.Integrate.peakmem_integrate('120km')
330M 330M 1 mpas_ocean.Integrate.peakmem_integrate('480km')
179±0.9μs 177±2μs 0.99 mpas_ocean.Integrate.time_integrate('120km')
157±1μs 157±0.5μs 1 mpas_ocean.Integrate.time_integrate('480km')
186±1ms 186±0.9ms 1 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'exclude')
185±2ms 189±3ms 1.02 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'include')
187±2ms 188±1ms 1.01 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'split')
13.5±0.09ms 13.6±0.06ms 1 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'exclude')
13.6±0.04ms 13.7±0.1ms 1.01 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'include')
13.6±0.05ms 13.6±0.04ms 1 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'split')
284±1μs 284±2μs 1 mpas_ocean.PointInPolygon.time_face_search_lonlat('120km')
286±1μs 285±2μs 1 mpas_ocean.PointInPolygon.time_face_search_lonlat('480km')
266±0.4μs 264±1μs 0.99 mpas_ocean.PointInPolygon.time_face_search_xyz('120km')
267±0.8μs 270±1μs 1.01 mpas_ocean.PointInPolygon.time_face_search_xyz('480km')
210±0.5ms 213±4ms 1.02 mpas_ocean.RemapDownsample.time_bilinear_remapping
229±0.5ms 230±0.6ms 1 mpas_ocean.RemapDownsample.time_inverse_distance_weighted_remapping
4.23±0.01ms 4.26±0.02ms 1.01 mpas_ocean.RemapDownsample.time_nearest_neighbor_remapping
1.17±0s 1.17±0s 1 mpas_ocean.RemapUpsample.time_bilinear_remapping
36.0±0.5ms 35.0±0.2ms 0.97 mpas_ocean.RemapUpsample.time_inverse_distance_weighted_remapping
8.91±0.04ms 8.75±0.1ms 0.98 mpas_ocean.RemapUpsample.time_nearest_neighbor_remapping
28.2±0.5ms 27.7±1ms 0.98 mpas_ocean.ZonalAverage.time_zonal_average('120km')
6.00±0ms 5.98±0.04ms 1 mpas_ocean.ZonalAverage.time_zonal_average('480km')
326M 330M 1.01 quad_hexagon.QuadHexagon.peakmem_open_dataset
326M 326M 1 quad_hexagon.QuadHexagon.peakmem_open_grid
6.51±0.07ms 6.45±0.07ms 0.99 quad_hexagon.QuadHexagon.time_open_dataset
5.44±0.04ms 5.44±0.06ms 1 quad_hexagon.QuadHexagon.time_open_grid

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation improvement Improvements on existing features or infrastructure redesign Content relating to the ongoing redesign run-benchmark Run ASV benchmark workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clean up face_areas API

2 participants