Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute Grid Centerpoint using Welzl's algorithm #811

Merged
merged 60 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
60a2cbb
o #803 initial implementation of Welzl's algorithm to calculate grid …
rajeeja Jun 11, 2024
3c41af3
o Update grid class and add asserts for test
rajeeja Jun 18, 2024
5307806
Merge branch 'main' into rajeeja/welzl
aaronzedwick Jun 19, 2024
33c445f
o typo fix
rajeeja Jun 19, 2024
571986a
Merge branch 'rajeeja/welzl' of github.com:UXARRAY/uxarray into rajee…
rajeeja Jun 19, 2024
f926928
Merge branch 'main' into rajeeja/welzl
rajeeja Jun 25, 2024
9b3f066
o overhaul to not use tuples and go with numpy array, document and fi…
rajeeja Jun 26, 2024
cb5b380
Merge branch 'main' into rajeeja/welzl
rajeeja Jun 26, 2024
504bc08
o Conform to formatting standards
rajeeja Jun 26, 2024
9462e49
Merge branch 'main' into rajeeja/welzl
rajeeja Jun 27, 2024
c62d2b7
o fix bugs that reversed the coordinate ordering
rajeeja Jun 29, 2024
d117413
Merge branch 'main' into rajeeja/welzl
rajeeja Jul 2, 2024
d9a0823
Merge branch 'main' into rajeeja/welzl
philipc2 Jul 2, 2024
81710ba
Merge branch 'main' into rajeeja/welzl
philipc2 Jul 3, 2024
d09499c
o Move some fns from coordinates.py to utils as they caused circular …
rajeeja Jul 9, 2024
8ffbe93
Merge branch 'main' into rajeeja/welzl
rajeeja Jul 9, 2024
3962433
Merge branch 'rajeeja/welzl' of github.com:UXARRAY/uxarray into rajee…
rajeeja Jul 9, 2024
030f9f8
Merge branch 'main' into rajeeja/welzl
rajeeja Jul 17, 2024
cbb2c37
Merge branch 'main' into rajeeja/welzl
philipc2 Aug 1, 2024
478f99f
add benchmark for faceLatLon construction
philipc2 Aug 2, 2024
277b1ce
fix face nodes typo
philipc2 Aug 2, 2024
68edd31
fix face nodes typo
philipc2 Aug 2, 2024
c580465
o Remove new properties _ctrpt infavor of using regular face lat/lon …
rajeeja Aug 6, 2024
02472db
Merge branch 'main' into rajeeja/welzl
philipc2 Aug 6, 2024
d59248f
o Fix conflicts and use utils to avoid circular dependencies
rajeeja Aug 26, 2024
b73fb31
o precommit
rajeeja Aug 26, 2024
4f65cb8
o reduce some python-level loop overhead, use zip and some list compr…
rajeeja Aug 26, 2024
f5bd37b
o Fix structure, numba causes issues with recursive functions, but th…
rajeeja Aug 27, 2024
0be7cfd
o Modify the recursion logic for Numba compatibility, works 3-10x faster
rajeeja Aug 27, 2024
d77e726
o Try to get the codecov higher, add internal function tests
rajeeja Aug 27, 2024
f5d514d
Merge branch 'main' into rajeeja/welzl
philipc2 Aug 28, 2024
5baa0e0
Merge branch 'main' into rajeeja/welzl
rajeeja Aug 28, 2024
50cff16
Merge branch 'main' into rajeeja/welzl
philipc2 Aug 28, 2024
4d0bb17
Merge branch 'main' into rajeeja/welzl
rajeeja Aug 30, 2024
b263cb6
Merge branch 'main' into rajeeja/welzl
rajeeja Sep 2, 2024
80de6bd
Merge branch 'main' into rajeeja/welzl
philipc2 Sep 4, 2024
1a18530
o Try to include inside funcs
rajeeja Sep 4, 2024
38b559d
Merge branch 'main' into rajeeja/welzl
rajeeja Sep 10, 2024
67e3de3
o call cartesian average instead of average add more doc
rajeeja Sep 10, 2024
0b9f5b1
o Fix conflicts
rajeeja Sep 12, 2024
fcc22c1
o Add return doc
rajeeja Sep 12, 2024
ac8e099
o Add return test for doc
rajeeja Sep 13, 2024
6cac65b
Merge branch 'main' into rajeeja/welzl
erogluorhan Sep 15, 2024
dba53dc
o fix text
rajeeja Sep 16, 2024
ecc0fa0
o Introduce circular dependency issue
rajeeja Sep 16, 2024
3531d5c
Merge branch 'main' into rajeeja/welzl
rajeeja Sep 17, 2024
4d6014a
Merge branch 'main' into rajeeja/welzl
philipc2 Sep 18, 2024
cc330d4
Update benchmarks/mpas_ocean.py
rajeeja Sep 19, 2024
1744a30
Update benchmarks/mpas_ocean.py
rajeeja Sep 19, 2024
0fc7b3f
Merge branch 'main' into rajeeja/welzl
rajeeja Sep 19, 2024
57e3112
o Fix imports and benchmarks
rajeeja Sep 19, 2024
c48a632
Merge branch 'main' into rajeeja/welzl
philipc2 Sep 25, 2024
a45c8cf
Merge branch 'main' into rajeeja/welzl
rajeeja Sep 30, 2024
6a6897e
Merge branch 'main' into rajeeja/welzl
rajeeja Oct 7, 2024
83cdc67
resolve circuluar import
philipc2 Oct 7, 2024
fffa79b
Merge branch 'main' into rajeeja/welzl
philipc2 Oct 7, 2024
795e527
clean up quad hex benchmark
philipc2 Oct 7, 2024
cc7db2e
Merge branch 'rajeeja/welzl' of https://github.com/UXARRAY/uxarray in…
philipc2 Oct 7, 2024
b54dfca
update internal API
philipc2 Oct 7, 2024
f1058af
update benchmark
philipc2 Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions benchmarks/mpas_ocean.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ class HoleEdgeIndices(DatasetBenchmark):
def time_construct_hole_edge_indices(self, resolution):
ux.grid.geometry._construct_hole_edge_indices(self.uxds.uxgrid.edge_face_connectivity)

class ConstructFaceLatLon(GridBenchmark):
def time_welzl(self, resolution):
self.uxgrid.construct_face_centers(method='welzl')

def time_cartesian_averaging(self, resolution):
self.uxgrid.construct_face_centers(method='cartesian average')

class CheckNorm:
param_names = ['resolution']
params = ['480km', '120km']
Expand Down
19 changes: 14 additions & 5 deletions docs/internal_api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,26 @@ Coordinates
.. autosummary::
:toctree: generated/

grid.coordinates._lonlat_rad_to_xyz
grid.coordinates._xyz_to_lonlat_rad
grid.coordinates._xyz_to_lonlat_deg
grid.coordinates._normalize_xyz
grid.coordinates._populate_node_latlon
grid.coordinates._populate_node_xyz
grid.coordinates._populate_face_centroids
grid.coordinates._populate_edge_centroids
grid.coordinates._construct_face_centroids
grid.coordinates._construct_edge_centroids
grid.coordinates._set_desired_longitude_range
grid.coordinates._populate_face_centerpoints
grid.coordinates._circle_from_two_points
grid.coordinates._circle_from_three_points
grid.coordinates._is_inside_circle
grid.coordinates._welzl_recursive
grid.coordinates._smallest_enclosing_circle
grid.coordinates._construct_face_centerpoints
grid.coordinates._lonlat_rad_to_xyz
grid.coordinates._xyz_to_lonlat_rad
grid.coordinates._xyz_to_lonlat_rad_no_norm
grid.coordinates._xyz_to_lonlat_deg
grid.coordinates._normalize_xyz
grid.coordinates._normalize_xyz_scalar


Arcs
Expand All @@ -177,12 +186,12 @@ Utils

grid.utils._newton_raphson_solver_for_gca_constLat
grid.utils._inv_jacobian
grid.utils._angle_of_two_vectors
grid.utils._swap_first_fill_value_with_last
grid.utils._get_cartesiain_face_edge_nodes
grid.utils._get_lonlat_rad_face_edge_nodes



Validation
----------
.. autosummary::
Expand Down
1 change: 1 addition & 0 deletions docs/user_api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ Methods
Grid.get_kd_tree
Grid.copy
Grid.isel
Grid.construct_face_centers
Grid.chunk


Expand Down
63 changes: 61 additions & 2 deletions test/test_centroids.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import numpy.testing as nt
import uxarray as ux
from pathlib import Path
from uxarray.grid.coordinates import _populate_face_centroids, _populate_edge_centroids, _normalize_xyz
from uxarray.grid.coordinates import _populate_face_centroids, _populate_edge_centroids, _populate_face_centerpoints, _is_inside_circle, _circle_from_three_points, _circle_from_two_points
from uxarray.grid.coordinates import _normalize_xyz

current_path = Path(os.path.dirname(os.path.realpath(__file__)))

Expand Down Expand Up @@ -63,7 +64,8 @@ def test_centroids_from_mean_verts_scrip(self):
expected_face_x = uxgrid.face_lon.values
expected_face_y = uxgrid.face_lat.values

_populate_face_centroids(uxgrid, repopulate=True)
# _populate_face_centroids(uxgrid, repopulate=True)
uxgrid.construct_face_centers(method="cartesian average")

# computed_face_x = (uxgrid.face_lon.values + 180) % 360 - 180
computed_face_x = uxgrid.face_lon.values
Expand Down Expand Up @@ -107,3 +109,60 @@ def test_edge_centroids_from_mpas(self):

nt.assert_array_almost_equal(expected_edge_lon, computed_edge_lon)
nt.assert_array_almost_equal(expected_edge_lat, computed_edge_lat)

class TestCenterPoints(TestCase):

def test_circle_from_two_points(self):
"""Test creation of circle from 2 points."""
p1 = (0, 0)
p2 = (0, 90)
center, radius = _circle_from_two_points(p1, p2)

# The expected radius in radians should be half the angle between the two vectors
expected_center = (0.0, 45.0)
expected_radius = np.deg2rad(45.0)

assert np.allclose(center, expected_center), f"Expected center {expected_center}, but got {center}"
assert np.allclose(radius, expected_radius), f"Expected radius {expected_radius}, but got {radius}"

def test_circle_from_three_points(self):
"""Test creation of circle from 3 points."""
p1 = (0, 0)
p2 = (0, 90)
p3 = (90, 0)
center, radius = _circle_from_three_points(p1, p2, p3)
expected_radius = np.deg2rad(45.0)
expected_center = (30.0, 30.0)

assert np.allclose(center, expected_center), f"Expected center {expected_center}, but got {center}"
assert np.allclose(radius, expected_radius), f"Expected radius {expected_radius}, but got {radius}"

def test_is_inside_circle(self):
"""Test if a points is inside the circle."""
# Define the circle
circle = ((0.0, 0.0), 1) # Center at lon/lat with a radius in radians (angular measure of the radius)

# Define test points
point_inside = (30.0, 30.0) # Should be inside the circle
point_outside = (90.0, 0.0) # Should be outside the circle

# Test _is_inside_circle function
assert _is_inside_circle(circle, point_inside), f"Point {point_inside} should be inside the circle."
assert not _is_inside_circle(circle, point_outside), f"Point {point_outside} should be outside the circle."

def test_face_centerpoint(self):
"""Use points from an actual spherical face and get the centerpoint."""

points = np.array([(-35.26438968, -45.0), (-36.61769496, -42.0), (-33.78769181, -42.0), (-32.48416571, -45.0)])
uxgrid = ux.open_grid(points, latlon=True)

# Uses the @property from get face_lon/lat - default is average or centroid
ctr_lon = uxgrid.face_lon.values[0]
ctr_lat = uxgrid.face_lat.values[0]

# now explicitly get the centerpoints stored to face_lon/lat using welzl's centerpoint algorithm
uxgrid.construct_face_centers(method = "welzl")

# Test the values of the calculated centerpoint, giving high tolerance of two decimal place
nt.assert_array_almost_equal(ctr_lon, uxgrid.face_lon.values[0], decimal=2)
nt.assert_array_almost_equal(ctr_lat, uxgrid.face_lat.values[0], decimal=2)
28 changes: 3 additions & 25 deletions uxarray/grid/arcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
_xyz_to_lonlat_rad_scalar,
_normalize_xyz_scalar,
)

from uxarray.grid.utils import _angle_of_2_vectors

from uxarray.constants import ERROR_TOLERANCE, MACHINE_EPSILON

from uxarray.utils.computing import isclose, cross, dot, allclose
Expand Down Expand Up @@ -303,31 +306,6 @@ def _decide_pole_latitude(lat1, lat2):
return closest_pole


@njit
def _angle_of_2_vectors(u, v):
"""Calculate the angle between two 3D vectors u and v in radians. Can be
used to calcualte the span of a GCR.

Parameters
----------
u : numpy.ndarray (float)
The first 3D vector.
v : numpy.ndarray (float)
The second 3D vector.

Returns
-------
float
The angle between u and v in radians.
"""
v_norm_times_u = np.linalg.norm(v) * u
u_norm_times_v = np.linalg.norm(u) * v
vec_minus = v_norm_times_u - u_norm_times_v
vec_sum = v_norm_times_u + u_norm_times_v
angle_u_v_rad = 2 * np.arctan2(np.linalg.norm(vec_minus), np.linalg.norm(vec_sum))
return angle_u_v_rad


def extreme_gca_latitude(gca_cart, extreme_type):
"""Calculate the maximum or minimum latitude of a great circle arc defined
by two 3D points.
Expand Down
Loading
Loading