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 44 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
31 changes: 31 additions & 0 deletions benchmarks/mpas_ocean.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,34 @@ def teardown(self, resolution):

def time_construct_hole_edge_indices(self, resolution):
ux.grid.geometry._construct_hole_edge_indices(self.uxds.uxgrid.edge_face_connectivity)


class ConstructFaceLatLon:
rajeeja marked this conversation as resolved.
Show resolved Hide resolved

param_names = ['resolution']
params = ['480km', '120km']

def setup(self, resolution):
self.uxgrid = ux.open_grid(file_path_dict[resolution][0])

def teardown(self, resolution):
del self.uxgrid
rajeeja marked this conversation as resolved.
Show resolved Hide resolved

def time_welzl(self, resolution):
from uxarray.grid.coordinates import _construct_face_centerpoints


_construct_face_centerpoints(self.uxgrid.node_lon.values,
self.uxgrid.node_lat.values,
self.uxgrid.face_node_connectivity.values,
self.uxgrid.n_nodes_per_face.values)


def time_cartesian_averaging(self, resolution):
from uxarray.grid.coordinates import _construct_face_centroids

_construct_face_centroids(self.uxgrid.node_x.values,
self.uxgrid.node_y.values,
self.uxgrid.node_z.values,
self.uxgrid.face_node_connectivity.values,
self.uxgrid.n_nodes_per_face.values)
36 changes: 36 additions & 0 deletions benchmarks/quad_hexagon.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,39 @@ def time_open_dataset(self):
def peakmem_open_dataset(self):
"""Peak memory usage of a `UxDataset`"""
uxds = ux.open_dataset(grid_path, data_path)


# from uxarray.grid.coordinates import _construct_face_centerpoints, _construct_face_centroids

class ConstructFaceLatLon:


def time_welzl(self):
uxgrid = ux.open_grid("/Users/mbook/cp_uxarray/benchmarks/oQU480.grid.nc")

ux.grid.coordinates._construct_face_centerpoints(uxgrid.node_lon.values,
uxgrid.node_lat.values,
uxgrid.face_node_connectivity.values,
uxgrid.n_nodes_per_face.values)





class QuadHexagon2:
def time_open_grid(self):
"""Time to open a `Grid`"""
ux.open_grid(grid_path)

# def mem_open_grid(self):
# """Memory Occupied by a `Grid`"""
# return ux.open_grid(grid_path)

def peakmem_open_grid2(self):
"""Peak memory usage of a `Grid`"""
uxgrid = ux.open_grid(grid_path)


def time_open_dataset(self):
"""Time to open a `UxDataset`"""
ux.open_dataset(grid_path, data_path)
rajeeja marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 13 additions & 5 deletions docs/internal_api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,20 @@ 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


Arcs
Expand All @@ -180,7 +183,12 @@ Utils
grid.utils._swap_first_fill_value_with_last
grid.utils._get_cartesiain_face_edge_nodes
grid.utils._get_lonlat_rad_face_edge_nodes

grid.utils._lonlat_rad_to_xyz
grid.utils._xyz_to_lonlat_rad
grid.utils._xyz_to_lonlat_rad_no_norm
grid.utils._xyz_to_lonlat_deg
grid.utils._normalize_xyz
grid.utils._normalize_xyz_scalar


Validation
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.utils 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)
2 changes: 1 addition & 1 deletion test/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from uxarray.grid.connectivity import _replace_fill_values
from uxarray.constants import INT_DTYPE, INT_FILL_VALUE

from uxarray.grid.coordinates import _lonlat_rad_to_xyz, _normalize_xyz, _xyz_to_lonlat_rad
from uxarray.grid.utils import _lonlat_rad_to_xyz, _normalize_xyz, _xyz_to_lonlat_rad
from uxarray.grid.arcs import point_within_gca, _angle_of_2_vectors, in_between
from uxarray.grid.utils import _get_cartesian_face_edge_nodes, _get_lonlat_rad_face_edge_nodes
from uxarray.grid.geometry import _pole_point_inside_polygon
Expand Down
2 changes: 1 addition & 1 deletion uxarray/grid/arcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# from uxarray.grid.coordinates import node_xyz_to_lonlat_rad, normalize_in_place

from uxarray.grid.coordinates import _xyz_to_lonlat_rad_no_norm, _normalize_xyz_scalar
from uxarray.grid.utils import _xyz_to_lonlat_rad_no_norm, _normalize_xyz_scalar
from uxarray.constants import ERROR_TOLERANCE

from uxarray.utils.computing import isclose, cross, dot
Expand Down
2 changes: 1 addition & 1 deletion uxarray/grid/area.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np

from uxarray.grid.coordinates import _lonlat_rad_to_xyz
from uxarray.grid.utils import _lonlat_rad_to_xyz

from numba import njit, config
from uxarray.constants import ENABLE_JIT_CACHE, ENABLE_JIT
Expand Down
Loading
Loading