Skip to content

add numba_math.py; optimize coordinates.py and _small_angle_of_2_vectors - #1674

Merged
Sevans711 merged 15 commits into
mainfrom
sevans/add-numba_math.py--optimize-coordinates.py
Aug 18, 2026
Merged

add numba_math.py; optimize coordinates.py and _small_angle_of_2_vectors#1674
Sevans711 merged 15 commits into
mainfrom
sevans/add-numba_math.py--optimize-coordinates.py

Conversation

@Sevans711

@Sevans711 Sevans711 commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Closes #1680 and closes #1681 (both are sub-issues of #1648).

Overview

(For #1680) adds uxarray/utils/numba_math.py to provide numba routines primarily for 3-vectors. Applying these directly to tuples to avoid constructing lots of tiny numpy arrays inside numba routines can cause huge speedup in some cases.

(For #1681) Optimizes numba routines in uxarray/grid/coordinates.py, and the _small_angle_of_2_vectors function (which is called directly by some routines in coordinates.py). As per discussion in #1648, this PR does not yet attempt to make these optimizations more broadly, but instead just focuses on improving a single area of the code.

After these changes, locally I am seeing grid.construct_face_centers('welzl') time decrease from ~14 s to ~150 ms, for a grid with 157k faces. ASV benchmarks similarly show huge improvement (>=20x speedup) there.

Sidenote: attempted to make a cleverer / more convenient interface, such as def _numba_add3(*vectors) which could add multiple vectors together, instead of just def _numba_add3(u, v) but that leads to extremely confusing numba errors in case of any type mismatch between values in tuples. Seems like a better idea to keep this simple (yet maybe slightly less convenient) interface to avoid those confusing errors.

Tiny expansion of PR scope: also fixes type-hinting for _lonlat_rad_to_xyz and _xyz_to_lonlat_rad.

PR Checklist

General

  • An issue is created and linked
  • Added appropriate labels (if your uxarray repo permissions allow it)
  • Filled out Overview and Expected Usage (if applicable) sections

Testing & Benchmarking

  • Adequate tests are created if there is new functionality
  • Tests are not too basic (such as simply calling a function and nothing else)
  • Tests cover all major paths in your new functions
  • If this PR could affect performance, ran ASV benchmarks and confirmed they show expected behavior (add a new benchmark if necessary)

Documentation

  • Docstrings have been added to all new functions
  • Docstrings have been updated with any function changes
  • [N/A] User (public) functions have been added to docs/api.rst
  • Internal (private) function names start with an underscore (_)

AI Disclosure

AI Usage: GitHub Copilot's inline code suggestions; discussions with Claude Sonnet and ChatGPT to help understand and debug numba routines and confusing numba errors.

  • I take responsibility for all AI-generated content in my PR.
  • I have tested all AI-generated content in my PR.

E.g. locally seeing grid.construct_face_centers('welzl') time decrease from 14 s to ~150 ms, for a grid with 157k faces.
@Sevans711 Sevans711 added scalability Related to scalability & performance efforts run-benchmark Run ASV benchmark workflow labels Aug 13, 2026
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

ASV Benchmarking

Benchmark Comparison Results

Benchmarks that have improved:

Change Before [ab39109] After [07a04fc] Ratio Benchmark (Parameter)
- 1.49±0.03s 71.5±2ms 0.05 mpas_ocean.ConstructFaceLatLon.time_welzl('120km')
- 92.7±0.6ms 6.51±0.1ms 0.07 mpas_ocean.ConstructFaceLatLon.time_welzl('480km')

Benchmarks that have stayed the same:

Change Before [ab39109] After [07a04fc] Ratio Benchmark (Parameter)
122±10ms 113±1ms 0.92 bench_connectivity.Connectivity.time_edge_face('120km')
7.13±0.09ms 6.79±0.04ms 0.95 bench_connectivity.Connectivity.time_edge_face('480km')
115±3ms 114±2ms 1.00 bench_connectivity.Connectivity.time_edge_node('120km')
6.59±0.3ms 6.33±0.09ms 0.96 bench_connectivity.Connectivity.time_edge_node('480km')
116±1ms 113±1ms 0.98 bench_connectivity.Connectivity.time_face_edge('120km')
6.66±0.2ms 6.40±0.03ms 0.96 bench_connectivity.Connectivity.time_face_edge('480km')
468±20ms 455±4ms 0.97 bench_connectivity.Connectivity.time_face_face('120km')
28.0±1ms 27.2±0.2ms 0.97 bench_connectivity.Connectivity.time_face_face('480km')
48.8±7μs 43.4±2μs ~0.89 bench_connectivity.Connectivity.time_face_node('120km')
49.5±4μs 40.3±3μs ~0.81 bench_connectivity.Connectivity.time_face_node('480km')
286±40μs 258±5μs ~0.90 bench_connectivity.Connectivity.time_n_nodes_per_face('120km')
211±20μs 210±9μs 0.99 bench_connectivity.Connectivity.time_n_nodes_per_face('480km')
117±4ms 116±0.5ms 0.99 bench_connectivity.Connectivity.time_node_edge('120km')
7.08±0.5ms 6.77±0.3ms 0.96 bench_connectivity.Connectivity.time_node_edge('480km')
46.3±5ms 51.0±0.9ms ~1.10 bench_connectivity.Connectivity.time_node_face('120km')
2.81±0.2ms 2.58±0.01ms 0.92 bench_connectivity.Connectivity.time_node_face('480km')
5.17±1ms 4.81±0.1ms 0.93 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
1.69±0.3ms 1.55±0.05ms 0.92 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
6.51±7s 6.73±8ms ~0.00 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
1.12±0.03ms 1.16±0.02ms 1.04 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
57.3k 57.3k 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
12.3k 12.3k 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
123k 123k 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
128 128 1.00 face_bounds.FaceBounds.track_nbytes_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
1.27M 1.27M 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
50.1k 50.1k 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
1.48M 1.48M 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
712 712 1.00 face_bounds.FaceBounds.track_nbytes_grid_with_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
2.04M 2.03M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
2.04M 2.03M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
2.21M 2.2M 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
38.3k 38.3k 1.00 face_bounds.FaceBounds.track_peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
337M 336M 1.00 face_bounds.FaceBoundsColdStartRss.peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
367M 368M 1.00 face_bounds.FaceBoundsColdStartRss.peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
338M 338M 1.00 face_bounds.FaceBoundsColdStartRss.peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
337M 337M 1.00 face_bounds.FaceBoundsColdStartRss.peakmem_open_and_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
671±40ns 676±20ns 1.01 geometry_kernels.AccucrossKernels.time_accucross
1.38±0.02μs 1.43±0.01μs 1.03 geometry_kernels.AccucrossKernels.time_accucross_pair
256±10ns 245±4ns 0.96 geometry_kernels.EFTPrimitives.time_acc_sqrt_re
240±3ns 250±8ns 1.04 geometry_kernels.EFTPrimitives.time_diff_of_products
216±5ns 225±5ns 1.04 geometry_kernels.EFTPrimitives.time_two_prod
226±5ns 221±10ns 0.98 geometry_kernels.EFTPrimitives.time_two_sum
792±9ns 851±5ns 1.07 geometry_kernels.GCAConstLatIntersection.time_accux_constlat_kernel
576±30ns 611±20ns 1.06 geometry_kernels.GCAConstLatIntersection.time_gca_const_lat_intersection
1.00±0.02μs 1.05±0.02μs 1.05 geometry_kernels.GCAConstLatIntersection.time_try_gca_const_lat_intersection
866±30ns 876±10ns 1.01 geometry_kernels.GCAGCAIntersection.time_accux_gca_kernel
796±20ns 806±10ns 1.01 geometry_kernels.GCAGCAIntersection.time_gca_gca_intersection
1.16±0.02μs 1.14±0.02μs 0.98 geometry_kernels.GCAGCAIntersection.time_try_gca_gca_intersection
24.0±0.7μs 22.8±1μs 0.95 geometry_kernels.OrientPredicates.time_on_minor_arc
461±9ns 436±10ns 0.95 geometry_kernels.OrientPredicates.time_orient3d_on_sphere
1.61±0.04ms 1.57±0.04ms 0.98 geometry_samebody.SameBodyConstLat.time_accux_dispatch
681±10μs 681±5μs 1.00 geometry_samebody.SameBodyConstLat.time_accux_kernel
1.11±0.01ms 1.11±0.03ms 1.00 geometry_samebody.SameBodyConstLat.time_fp64_dispatch
90.2±3μs 86.9±2μs 0.96 geometry_samebody.SameBodyConstLat.time_fp64_kernel
25.5±0.3ms 25.4±0.7ms 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_dispatch
5.91±0.1ms 5.76±0.1ms 0.97 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_kernel
21.9±0.1ms 21.3±0.4ms 0.97 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_dispatch
3.59±0.1ms 3.52±0.03ms 0.98 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_kernel
555±20ms 532±20ms 0.96 import.Imports.timeraw_import_uxarray
293M 293M 1.00 import.Imports.track_peakmem_import_uxarray
1.45±0.03ms 1.48±0.02ms 1.02 mpas_ocean.CheckNorm.time_check_norm('120km')
1.15±0.02ms 1.12±0.02ms 0.98 mpas_ocean.CheckNorm.time_check_norm('480km')
419±20ms 406±10ms 0.97 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('120km')
25.1±1ms 25.2±0.7ms 1.01 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('480km')
400±9μs 402±10μs 1.00 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('120km')
346±10μs 330±6μs 0.95 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('480km')
2.62±0.01ms 2.68±0.08ms 1.02 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('120km')
2.19±0.03ms 2.13±0.07ms 0.98 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('480km')
13.2±0.2ms 12.9±0.3ms 0.97 mpas_ocean.ConstructTreeStructures.time_ball_tree('120km')
807±10μs 799±40μs 0.99 mpas_ocean.ConstructTreeStructures.time_ball_tree('480km')
6.84±0.06ms 6.64±0.06ms 0.97 mpas_ocean.ConstructTreeStructures.time_kd_tree('120km')
559±6μs 535±20μs 0.96 mpas_ocean.ConstructTreeStructures.time_kd_tree('480km')
322±10ms 334±9ms 1.04 mpas_ocean.CrossSections.time_const_lat('120km', 1)
166±3ms 173±6ms 1.04 mpas_ocean.CrossSections.time_const_lat('120km', 2)
87.8±1ms 86.7±5ms 0.99 mpas_ocean.CrossSections.time_const_lat('120km', 4)
276±7ms 276±7ms 1.00 mpas_ocean.CrossSections.time_const_lat('480km', 1)
139±2ms 144±2ms 1.03 mpas_ocean.CrossSections.time_const_lat('480km', 2)
71.5±2ms 74.2±0.5ms 1.04 mpas_ocean.CrossSections.time_const_lat('480km', 4)
357M 357M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('120km', 1)
356M 357M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('120km', 2)
357M 356M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('120km', 4)
340M 342M 1.01 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('480km', 1)
340M 339M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('480km', 2)
340M 340M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('480km', 4)
16.0±0.3ms 16.6±0.3ms 1.04 mpas_ocean.DualMesh.time_dual_mesh_construction('120km')
1.78±0.07ms 1.89±0.08ms 1.06 mpas_ocean.DualMesh.time_dual_mesh_construction('480km')
49.8±5ms 48.5±0.4ms 0.97 mpas_ocean.FaceAreas.time_face_areas('120km')
5.23±0.4ms 4.94±0.2ms 0.94 mpas_ocean.FaceAreas.time_face_areas('480km')
229k 229k 1.00 mpas_ocean.FaceAreas.track_nbytes_face_areas('120km')
14.3k 14.3k 1.00 mpas_ocean.FaceAreas.track_nbytes_face_areas('480km')
2.12M 2.12M 1.00 mpas_ocean.FaceAreas.track_peakmem_face_areas('120km')
836k 836k 1.00 mpas_ocean.FaceAreas.track_peakmem_face_areas('480km')
470±10ms 480±4ms 1.02 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', False)
29.2±1ms 31.8±4ms 1.09 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', True)
43.0±2ms 41.5±0.8ms 0.96 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', False)
4.09±0.1ms 3.89±0.05ms 0.95 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', True)
139±3ms 135±4ms 0.97 mpas_ocean.Gradient.time_gradient('120km')
9.14±0.4ms 9.37±0.5ms 1.03 mpas_ocean.Gradient.time_gradient('480km')
457k 457k 1.00 mpas_ocean.Gradient.track_nbytes_gradient('120km')
28.7k 28.7k 1.00 mpas_ocean.Gradient.track_nbytes_gradient('480km')
5.08M 5.08M 1.00 mpas_ocean.Gradient.track_peakmem_gradient('120km')
328k 328k 1.00 mpas_ocean.Gradient.track_peakmem_gradient('480km')
352M 352M 1.00 mpas_ocean.GradientColdStartRss.peakmem_gradient('120km')
331M 331M 1.00 mpas_ocean.GradientColdStartRss.peakmem_gradient('480km')
408±20μs 487±10μs ~1.20 mpas_ocean.Integrate.time_integrate('120km')
18.4M 18.4M 1.00 mpas_ocean.Integrate.track_nbytes_integrate('120km')
1.2M 1.2M 1.00 mpas_ocean.Integrate.track_nbytes_integrate('480km')
99.9±0.9ms 107±2ms 1.07 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'exclude')
101±1ms 105±2ms 1.04 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'include')
102±0.6ms 109±3ms 1.07 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'split')
8.18±0.2ms 8.75±0.2ms 1.07 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'split')
241±8μs 255±20μs 1.06 mpas_ocean.PointInPolygon.time_face_search_lonlat('120km')
203±1μs 222±8μs 1.09 mpas_ocean.PointInPolygon.time_face_search_lonlat('480km')
206±5μs 206±6μs 1.00 mpas_ocean.PointInPolygon.time_face_search_xyz('480km')
122±1ms 126±0.6ms 1.03 mpas_ocean.RemapDownsample.time_bilinear_remapping
121±2ms 124±0.5ms 1.02 mpas_ocean.RemapDownsample.time_inverse_distance_weighted_remapping
9.56±0.2ms 10.3±0.3ms 1.08 mpas_ocean.RemapDownsample.time_nearest_neighbor_remapping
620±8ms 615±8ms 0.99 mpas_ocean.RemapUpsample.time_bilinear_remapping
23.7±0.4ms 23.8±0.4ms 1.00 mpas_ocean.RemapUpsample.time_inverse_distance_weighted_remapping
7.30±0.3ms 7.73±0.5ms 1.06 mpas_ocean.RemapUpsample.time_nearest_neighbor_remapping
5.17±0.4ms 5.66±0.09ms 1.09 mpas_ocean.ZonalAverage.time_zonal_average('120km')
2.61±0.2ms 2.75±0.1ms 1.05 mpas_ocean.ZonalAverage.time_zonal_average('480km')
358M 358M 1.00 mpas_ocean.ZonalAveragePeakMem.peakmem_zonal_average('120km')
341M 341M 1.00 mpas_ocean.ZonalAveragePeakMem.peakmem_zonal_average('480km')
3.68±0.04ms 3.99±0.3ms 1.08 quad_hexagon.QuadHexagon.time_open_dataset
3.13±0.02ms 3.14±0.2ms 1.00 quad_hexagon.QuadHexagon.time_open_grid
408 408 1.00 quad_hexagon.QuadHexagon.track_nbytes_open_dataset
392 392 1.00 quad_hexagon.QuadHexagon.track_nbytes_open_grid
73.7k 73.5k 1.00 quad_hexagon.QuadHexagon.track_peakmem_open_dataset
73k 72.8k 1.00 quad_hexagon.QuadHexagon.track_peakmem_open_grid

Benchmarks that have got worse:

Change Before [ab39109] After [07a04fc] Ratio Benchmark (Parameter)
+ 258±9μs 285±7μs 1.1 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('120km')
+ 131±8μs 151±8μs 1.15 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('480km')
+ 307±7μs 348±20μs 1.13 mpas_ocean.Integrate.time_integrate('480km')
+ 7.90±0.05ms 9.08±0.4ms 1.15 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'exclude')
+ 7.85±0.2ms 8.92±0.1ms 1.14 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'include')
+ 209±7μs 318±50μs 1.52 mpas_ocean.PointInPolygon.time_face_search_xyz('120km')

@Sevans711
Sevans711 marked this pull request as ready for review August 14, 2026 15:25
@Sevans711 Sevans711 added run-benchmark Run ASV benchmark workflow and removed run-benchmark Run ASV benchmark workflow labels Aug 17, 2026
Comment thread uxarray/grid/coordinates.py Outdated
Comment thread uxarray/grid/coordinates.py Outdated
@cmdupuis3 cmdupuis3 added run-benchmark Run ASV benchmark workflow and removed run-benchmark Run ASV benchmark workflow labels Aug 17, 2026
@cmdupuis3

cmdupuis3 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Also, I think if we're going to go this route with numba_math, we should probably have a standard vector type for the whole repo, and then for these binops we can use operator overloads like @overload(operator.sub) so we don't have to explicitly call a long-named function when we just want to subtract things. Either way seems like a big change to the repo philosophy though.

@cmdupuis3

Copy link
Copy Markdown
Collaborator

Ope, wait, there's an issue with operator overloads. If you disable the numba jit, the decorator skips and Python goes back to concatenating with + instead of adding.

The joys of Python...

@Sevans711

Copy link
Copy Markdown
Collaborator Author

Thank you @cmdupuis3 for reviewing!

Also, I think if we're going to go this route with numba_math, we should probably have a standard vector type for the whole repo, and then for these binops we can use operator overloads like @overload(operator.sub) so we don't have to explicitly call a long-named function when we just want to subtract things. Either way seems like a big change to the repo philosophy though.

Ope, wait, there's an issue with operator overloads. If you disable the numba jit, the decorator skips and Python goes back to concatenating with + instead of adding.

The joys of Python...

Yeahhh, while Python objects would be happy to support a convenient interface with overloads like that, it seems to me like for performance-optimized numba it might be better to just keep the clunkier interface. Numba compilations make things harder to reason about, so keeping it simple seems to be the way to go here.

(E.g. I also originally tried def _numba_add3(*vectors, scalar=None) as a way to allow adding any number of 3-vectors plus maybe a scalar if you want, but this led to extremely confusing behavior, such as _numba_add3((1,2,3),scalar=10) crashing while _numba_add3((1,2,3),10) unexpectedly ran without complaint and returned (11,12,13); and _numba_add3((1,2,3),(4,5,6),(7,8,9)) working okay but _numba_add3((1,2,3),(4,5,6),(7,8,9.0)) crashing with an extremely confusing numba typing error)

To me it doesn't feel like a change to the whole repo philosophy, only a change to the philosophy of numba-compiled functions (wherein this directly causes huge speedup). Outside of njit-decorated functions, I believe uxarray should certainly still use numpy/dask/xarray arrays for dealing with vectors!

@erogluorhan erogluorhan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, so I approve it; just a single comment below though.

Comment thread uxarray/grid/coordinates.py Outdated
@Sevans711
Sevans711 merged commit b674956 into main Aug 18, 2026
13 checks passed
@Sevans711
Sevans711 deleted the sevans/add-numba_math.py--optimize-coordinates.py branch August 18, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-benchmark Run ASV benchmark workflow scalability Related to scalability & performance efforts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Avoid tiny numpy arrays in numba: optimize coordinates.py Avoid tiny numpy arrays in numba: create numba helper routines file

3 participants