From 99573b8edfc2dc2fc329e91d395da4d907b42e4a Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Wed, 25 Oct 2023 16:32:54 -0700 Subject: [PATCH 01/10] Initial Draft Algorithm --- uxarray/grid/utils.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/uxarray/grid/utils.py b/uxarray/grid/utils.py index 6bfeb38b0..f4a45713a 100644 --- a/uxarray/grid/utils.py +++ b/uxarray/grid/utils.py @@ -229,3 +229,34 @@ def _newton_raphson_solver_for_gca_constLat(init_cart, _iter += 1 return np.append(y_new, constZ) + +def get_face_edge_connectivity_cartesian(Mesh2_face_nodes_i, Mesh2_face_edges_i, Mesh2_edge_nodes): + """Get the face-edge connectivity for Cartesian grid. + Mesh2_face_nodes_i: The ith entry of Grid.Mesh2_face_nodes + Mesh2_face_edges_i: The ith entry of Grid.Mesh2_face_edges + Mesh2_edge_nodes: The entire Grid.Mesh2_edge_nodes + """ + face_edges = np.zeros((len(Mesh2_face_edges_i), 2), dtype=INT_DTYPE) + face_edges = face_edges.astype(INT_DTYPE) + for iter in range(0, len(Mesh2_face_edges_i)): + edge_idx = Mesh2_face_edges_i[iter] + if edge_idx == INT_FILL_VALUE: + edge_nodes = [INT_FILL_VALUE, INT_FILL_VALUE] + else: + edge_nodes = Mesh2_edge_nodes.values[edge_idx] + face_edges[iter] = edge_nodes + # sort edge nodes in counter-clockwise order + starting_two_nodes_index = [Mesh2_face_nodes_i[0], Mesh2_face_nodes_i[1]] + face_edges[0] = starting_two_nodes_index + for idx in range(1, len(face_edges)): + if face_edges[idx][0] == face_edges[idx - 1][1]: + continue + else: + # Swap the node index in this edge + temp = face_edges[idx][0] + face_edges[idx][0] = face_edges[idx][1] + face_edges[idx][1] = temp + + return face_edges + + From 2bb2b93ed4dc238ae6af5aa18d7f7fd287b19d1d Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Wed, 25 Oct 2023 16:34:20 -0700 Subject: [PATCH 02/10] Revert "Initial Draft Algorithm" This reverts commit 99573b8edfc2dc2fc329e91d395da4d907b42e4a. --- uxarray/grid/utils.py | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/uxarray/grid/utils.py b/uxarray/grid/utils.py index f4a45713a..6bfeb38b0 100644 --- a/uxarray/grid/utils.py +++ b/uxarray/grid/utils.py @@ -229,34 +229,3 @@ def _newton_raphson_solver_for_gca_constLat(init_cart, _iter += 1 return np.append(y_new, constZ) - -def get_face_edge_connectivity_cartesian(Mesh2_face_nodes_i, Mesh2_face_edges_i, Mesh2_edge_nodes): - """Get the face-edge connectivity for Cartesian grid. - Mesh2_face_nodes_i: The ith entry of Grid.Mesh2_face_nodes - Mesh2_face_edges_i: The ith entry of Grid.Mesh2_face_edges - Mesh2_edge_nodes: The entire Grid.Mesh2_edge_nodes - """ - face_edges = np.zeros((len(Mesh2_face_edges_i), 2), dtype=INT_DTYPE) - face_edges = face_edges.astype(INT_DTYPE) - for iter in range(0, len(Mesh2_face_edges_i)): - edge_idx = Mesh2_face_edges_i[iter] - if edge_idx == INT_FILL_VALUE: - edge_nodes = [INT_FILL_VALUE, INT_FILL_VALUE] - else: - edge_nodes = Mesh2_edge_nodes.values[edge_idx] - face_edges[iter] = edge_nodes - # sort edge nodes in counter-clockwise order - starting_two_nodes_index = [Mesh2_face_nodes_i[0], Mesh2_face_nodes_i[1]] - face_edges[0] = starting_two_nodes_index - for idx in range(1, len(face_edges)): - if face_edges[idx][0] == face_edges[idx - 1][1]: - continue - else: - # Swap the node index in this edge - temp = face_edges[idx][0] - face_edges[idx][0] = face_edges[idx][1] - face_edges[idx][1] = temp - - return face_edges - - From 1ba68804f2f8f6fd062c9eeb63ef0b600549c9f0 Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Mon, 12 Feb 2024 17:31:34 -0800 Subject: [PATCH 03/10] Initial commit --- test/test_arcs.py | 130 +++++++++++++++++++++++++++++++++++++++++++ test/test_helpers.py | 108 ----------------------------------- 2 files changed, 130 insertions(+), 108 deletions(-) create mode 100644 test/test_arcs.py diff --git a/test/test_arcs.py b/test/test_arcs.py new file mode 100644 index 000000000..cacd36934 --- /dev/null +++ b/test/test_arcs.py @@ -0,0 +1,130 @@ +import os +import numpy as np +import numpy.testing as nt +import random +import xarray as xr + +from unittest import TestCase +from pathlib import Path + +import uxarray as ux + + +from uxarray.grid.coordinates import node_lonlat_rad_to_xyz +from uxarray.grid.arcs import point_within_gca, in_between + +try: + import constants +except ImportError: + from . import constants + +# Data files +current_path = Path(os.path.dirname(os.path.realpath(__file__))) + +class TestArcs(TestCase): + + def test_pt_within_gcr(self): + # The GCR that's eexactly 180 degrees will have Value Error raised + gcr_180degree_cart = [ + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]), + ux.grid.coordinates.node_lonlat_rad_to_xyz([np.pi, 0.0]) + ] + pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) + with self.assertRaises(ValueError): + point_within_gca(pt_same_lon_in, gcr_180degree_cart) + + gcr_180degree_cart = [ + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, np.pi / 2.0]), + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -np.pi / 2.0]) + ] + + pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) + with self.assertRaises(ValueError): + point_within_gca(pt_same_lon_in, gcr_180degree_cart) + + # Test when the point and the GCA all have the same longitude + gcr_same_lon_cart = [ + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 1.5]), + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -1.5]) + ] + pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) + self.assertTrue(point_within_gca(pt_same_lon_in, gcr_same_lon_cart)) + + pt_same_lon_out = ux.grid.coordinates.node_lonlat_rad_to_xyz( + [0.0, 1.500000000000001]) + res = point_within_gca(pt_same_lon_out, gcr_same_lon_cart) + self.assertFalse(res) + + pt_same_lon_out_2 = ux.grid.coordinates.node_lonlat_rad_to_xyz( + [0.1, 1.0]) + res = point_within_gca(pt_same_lon_out_2, gcr_same_lon_cart) + self.assertFalse(res) + + # And if we increase the digital place by one, it should be true again + pt_same_lon_out_add_one_place = ux.grid.coordinates.node_lonlat_rad_to_xyz( + [0.0, 1.5000000000000001]) + res = point_within_gca(pt_same_lon_out_add_one_place, gcr_same_lon_cart) + self.assertTrue(res) + + # Normal case + # GCR vertex0 in radian : [1.3003315590159483, -0.007004587172323237], + # GCR vertex1 in radian : [3.5997458123873827, -1.4893379576608758] + # Point in radian : [1.3005410084914981, -0.010444274637648326] + gcr_cart_2 = np.array([[0.267, 0.963, -0.007], [-0.073, -0.036, + -0.997]]) + pt_cart_within = np.array( + [0.25616109352676675, 0.9246590335292105, -0.010021496695000144]) + self.assertTrue(point_within_gca(pt_cart_within, gcr_cart_2, True)) + + # Test other more complicate cases : The anti-meridian case + + # GCR vertex0 in radian : [5.163808182822441, 0.6351384888657234], + # GCR vertex1 in radian : [0.8280410325693055, 0.42237025187091526] + # Point in radian : [0.12574759138415173, 0.770098701904903] + gcr_cart = np.array([[0.351, -0.724, 0.593], [0.617, 0.672, 0.410]]) + pt_cart = np.array( + [0.9438777657502077, 0.1193199333436068, 0.922714737029319]) + self.assertTrue(point_within_gca(pt_cart, gcr_cart, is_directed=True)) + # If we swap the gcr, it should throw a value error since it's larger than 180 degree + gcr_cart_flip = np.array([[0.617, 0.672, 0.410], [0.351, -0.724, + 0.593]]) + with self.assertRaises(ValueError): + point_within_gca(pt_cart, gcr_cart_flip, is_directed=True) + + # If we flip the gcr in the undirected mode, it should still work + self.assertTrue( + point_within_gca(pt_cart, gcr_cart_flip, is_directed=False)) + + # 2nd anti-meridian case + # GCR vertex0 in radian : [4.104711496596806, 0.5352983676533828], + # GCR vertex1 in radian : [2.4269979227622533, -0.007003212877856825] + # Point in radian : [0.43400375562899113, -0.49554509841586936] + gcr_cart_1 = np.array([[-0.491, -0.706, 0.510], [-0.755, 0.655, + -0.007]]) + pt_cart_within = np.array( + [0.6136726305712109, 0.28442243941920053, -0.365605190899831]) + self.assertFalse( + point_within_gca(pt_cart_within, gcr_cart_1, is_directed=True)) + self.assertFalse( + point_within_gca(pt_cart_within, gcr_cart_1, is_directed=False)) + + # The first case should not work and the second should work + v1_rad = [0.1, 0.0] + v2_rad = [2 * np.pi - 0.1, 0.0] + v1_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v1_rad) + v2_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v2_rad) + gcr_cart = np.array([v1_cart, v2_cart]) + pt_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.01, 0.0]) + with self.assertRaises(ValueError): + point_within_gca(pt_cart, gcr_cart, is_directed=True) + gcr_car_flipped = np.array([v2_cart, v1_cart]) + self.assertTrue( + point_within_gca(pt_cart, gcr_car_flipped, is_directed=True)) + + +class TestOperators(TestCase): + + def test_in_between(self): + # Test the in_between operator + self.assertTrue(in_between(0, 1, 2)) + self.assertTrue(in_between(-1, -1.5, -2)) \ No newline at end of file diff --git a/test/test_helpers.py b/test/test_helpers.py index 549ae95c5..7d04ba680 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -220,114 +220,6 @@ def test_convert_face_node_conn_to_sparse_matrix(self): nt.assert_array_equal(nodes_indices, expected_nodes_indices) -class TestIntersectionPoint(TestCase): - - def test_pt_within_gcr(self): - # The GCR that's eexactly 180 degrees will have Value Error raised - gcr_180degree_cart = [ - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]), - ux.grid.coordinates.node_lonlat_rad_to_xyz([np.pi, 0.0]) - ] - pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) - with self.assertRaises(ValueError): - point_within_gca(pt_same_lon_in, gcr_180degree_cart) - - gcr_180degree_cart = [ - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, np.pi / 2.0]), - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -np.pi / 2.0]) - ] - - pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) - with self.assertRaises(ValueError): - point_within_gca(pt_same_lon_in, gcr_180degree_cart) - - # Test when the point and the GCA all have the same longitude - gcr_same_lon_cart = [ - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 1.5]), - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -1.5]) - ] - pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) - self.assertTrue(point_within_gca(pt_same_lon_in, gcr_same_lon_cart)) - - pt_same_lon_out = ux.grid.coordinates.node_lonlat_rad_to_xyz( - [0.0, 1.500000000000001]) - res = point_within_gca(pt_same_lon_out, gcr_same_lon_cart) - self.assertFalse(res) - - pt_same_lon_out_2 = ux.grid.coordinates.node_lonlat_rad_to_xyz( - [0.1, 1.0]) - res = point_within_gca(pt_same_lon_out_2, gcr_same_lon_cart) - self.assertFalse(res) - - # And if we increase the digital place by one, it should be true again - pt_same_lon_out_add_one_place = ux.grid.coordinates.node_lonlat_rad_to_xyz( - [0.0, 1.5000000000000001]) - res = point_within_gca(pt_same_lon_out_add_one_place, gcr_same_lon_cart) - self.assertTrue(res) - - # Normal case - # GCR vertex0 in radian : [1.3003315590159483, -0.007004587172323237], - # GCR vertex1 in radian : [3.5997458123873827, -1.4893379576608758] - # Point in radian : [1.3005410084914981, -0.010444274637648326] - gcr_cart_2 = np.array([[0.267, 0.963, -0.007], [-0.073, -0.036, - -0.997]]) - pt_cart_within = np.array( - [0.25616109352676675, 0.9246590335292105, -0.010021496695000144]) - self.assertTrue(point_within_gca(pt_cart_within, gcr_cart_2, True)) - - # Test other more complicate cases : The anti-meridian case - - # GCR vertex0 in radian : [5.163808182822441, 0.6351384888657234], - # GCR vertex1 in radian : [0.8280410325693055, 0.42237025187091526] - # Point in radian : [0.12574759138415173, 0.770098701904903] - gcr_cart = np.array([[0.351, -0.724, 0.593], [0.617, 0.672, 0.410]]) - pt_cart = np.array( - [0.9438777657502077, 0.1193199333436068, 0.922714737029319]) - self.assertTrue(point_within_gca(pt_cart, gcr_cart, is_directed=True)) - # If we swap the gcr, it should throw a value error since it's larger than 180 degree - gcr_cart_flip = np.array([[0.617, 0.672, 0.410], [0.351, -0.724, - 0.593]]) - with self.assertRaises(ValueError): - point_within_gca(pt_cart, gcr_cart_flip, is_directed=True) - - # If we flip the gcr in the undirected mode, it should still work - self.assertTrue( - point_within_gca(pt_cart, gcr_cart_flip, is_directed=False)) - - # 2nd anti-meridian case - # GCR vertex0 in radian : [4.104711496596806, 0.5352983676533828], - # GCR vertex1 in radian : [2.4269979227622533, -0.007003212877856825] - # Point in radian : [0.43400375562899113, -0.49554509841586936] - gcr_cart_1 = np.array([[-0.491, -0.706, 0.510], [-0.755, 0.655, - -0.007]]) - pt_cart_within = np.array( - [0.6136726305712109, 0.28442243941920053, -0.365605190899831]) - self.assertFalse( - point_within_gca(pt_cart_within, gcr_cart_1, is_directed=True)) - self.assertFalse( - point_within_gca(pt_cart_within, gcr_cart_1, is_directed=False)) - - # The first case should not work and the second should work - v1_rad = [0.1, 0.0] - v2_rad = [2 * np.pi - 0.1, 0.0] - v1_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v1_rad) - v2_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v2_rad) - gcr_cart = np.array([v1_cart, v2_cart]) - pt_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.01, 0.0]) - with self.assertRaises(ValueError): - point_within_gca(pt_cart, gcr_cart, is_directed=True) - gcr_car_flipped = np.array([v2_cart, v1_cart]) - self.assertTrue( - point_within_gca(pt_cart, gcr_car_flipped, is_directed=True)) - - -class TestOperators(TestCase): - - def test_in_between(self): - # Test the in_between operator - self.assertTrue(in_between(0, 1, 2)) - self.assertTrue(in_between(-1, -1.5, -2)) - class TestVectorsAngel(TestCase): From dfdb43bba13b2219d9c2225fe2464ca68a6627b3 Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Mon, 12 Feb 2024 17:34:25 -0800 Subject: [PATCH 04/10] Revert "Initial commit" This reverts commit 1ba68804f2f8f6fd062c9eeb63ef0b600549c9f0. --- test/test_arcs.py | 130 ------------------------------------------- test/test_helpers.py | 108 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 130 deletions(-) delete mode 100644 test/test_arcs.py diff --git a/test/test_arcs.py b/test/test_arcs.py deleted file mode 100644 index cacd36934..000000000 --- a/test/test_arcs.py +++ /dev/null @@ -1,130 +0,0 @@ -import os -import numpy as np -import numpy.testing as nt -import random -import xarray as xr - -from unittest import TestCase -from pathlib import Path - -import uxarray as ux - - -from uxarray.grid.coordinates import node_lonlat_rad_to_xyz -from uxarray.grid.arcs import point_within_gca, in_between - -try: - import constants -except ImportError: - from . import constants - -# Data files -current_path = Path(os.path.dirname(os.path.realpath(__file__))) - -class TestArcs(TestCase): - - def test_pt_within_gcr(self): - # The GCR that's eexactly 180 degrees will have Value Error raised - gcr_180degree_cart = [ - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]), - ux.grid.coordinates.node_lonlat_rad_to_xyz([np.pi, 0.0]) - ] - pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) - with self.assertRaises(ValueError): - point_within_gca(pt_same_lon_in, gcr_180degree_cart) - - gcr_180degree_cart = [ - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, np.pi / 2.0]), - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -np.pi / 2.0]) - ] - - pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) - with self.assertRaises(ValueError): - point_within_gca(pt_same_lon_in, gcr_180degree_cart) - - # Test when the point and the GCA all have the same longitude - gcr_same_lon_cart = [ - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 1.5]), - ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -1.5]) - ] - pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) - self.assertTrue(point_within_gca(pt_same_lon_in, gcr_same_lon_cart)) - - pt_same_lon_out = ux.grid.coordinates.node_lonlat_rad_to_xyz( - [0.0, 1.500000000000001]) - res = point_within_gca(pt_same_lon_out, gcr_same_lon_cart) - self.assertFalse(res) - - pt_same_lon_out_2 = ux.grid.coordinates.node_lonlat_rad_to_xyz( - [0.1, 1.0]) - res = point_within_gca(pt_same_lon_out_2, gcr_same_lon_cart) - self.assertFalse(res) - - # And if we increase the digital place by one, it should be true again - pt_same_lon_out_add_one_place = ux.grid.coordinates.node_lonlat_rad_to_xyz( - [0.0, 1.5000000000000001]) - res = point_within_gca(pt_same_lon_out_add_one_place, gcr_same_lon_cart) - self.assertTrue(res) - - # Normal case - # GCR vertex0 in radian : [1.3003315590159483, -0.007004587172323237], - # GCR vertex1 in radian : [3.5997458123873827, -1.4893379576608758] - # Point in radian : [1.3005410084914981, -0.010444274637648326] - gcr_cart_2 = np.array([[0.267, 0.963, -0.007], [-0.073, -0.036, - -0.997]]) - pt_cart_within = np.array( - [0.25616109352676675, 0.9246590335292105, -0.010021496695000144]) - self.assertTrue(point_within_gca(pt_cart_within, gcr_cart_2, True)) - - # Test other more complicate cases : The anti-meridian case - - # GCR vertex0 in radian : [5.163808182822441, 0.6351384888657234], - # GCR vertex1 in radian : [0.8280410325693055, 0.42237025187091526] - # Point in radian : [0.12574759138415173, 0.770098701904903] - gcr_cart = np.array([[0.351, -0.724, 0.593], [0.617, 0.672, 0.410]]) - pt_cart = np.array( - [0.9438777657502077, 0.1193199333436068, 0.922714737029319]) - self.assertTrue(point_within_gca(pt_cart, gcr_cart, is_directed=True)) - # If we swap the gcr, it should throw a value error since it's larger than 180 degree - gcr_cart_flip = np.array([[0.617, 0.672, 0.410], [0.351, -0.724, - 0.593]]) - with self.assertRaises(ValueError): - point_within_gca(pt_cart, gcr_cart_flip, is_directed=True) - - # If we flip the gcr in the undirected mode, it should still work - self.assertTrue( - point_within_gca(pt_cart, gcr_cart_flip, is_directed=False)) - - # 2nd anti-meridian case - # GCR vertex0 in radian : [4.104711496596806, 0.5352983676533828], - # GCR vertex1 in radian : [2.4269979227622533, -0.007003212877856825] - # Point in radian : [0.43400375562899113, -0.49554509841586936] - gcr_cart_1 = np.array([[-0.491, -0.706, 0.510], [-0.755, 0.655, - -0.007]]) - pt_cart_within = np.array( - [0.6136726305712109, 0.28442243941920053, -0.365605190899831]) - self.assertFalse( - point_within_gca(pt_cart_within, gcr_cart_1, is_directed=True)) - self.assertFalse( - point_within_gca(pt_cart_within, gcr_cart_1, is_directed=False)) - - # The first case should not work and the second should work - v1_rad = [0.1, 0.0] - v2_rad = [2 * np.pi - 0.1, 0.0] - v1_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v1_rad) - v2_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v2_rad) - gcr_cart = np.array([v1_cart, v2_cart]) - pt_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.01, 0.0]) - with self.assertRaises(ValueError): - point_within_gca(pt_cart, gcr_cart, is_directed=True) - gcr_car_flipped = np.array([v2_cart, v1_cart]) - self.assertTrue( - point_within_gca(pt_cart, gcr_car_flipped, is_directed=True)) - - -class TestOperators(TestCase): - - def test_in_between(self): - # Test the in_between operator - self.assertTrue(in_between(0, 1, 2)) - self.assertTrue(in_between(-1, -1.5, -2)) \ No newline at end of file diff --git a/test/test_helpers.py b/test/test_helpers.py index 7d04ba680..549ae95c5 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -220,6 +220,114 @@ def test_convert_face_node_conn_to_sparse_matrix(self): nt.assert_array_equal(nodes_indices, expected_nodes_indices) +class TestIntersectionPoint(TestCase): + + def test_pt_within_gcr(self): + # The GCR that's eexactly 180 degrees will have Value Error raised + gcr_180degree_cart = [ + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]), + ux.grid.coordinates.node_lonlat_rad_to_xyz([np.pi, 0.0]) + ] + pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) + with self.assertRaises(ValueError): + point_within_gca(pt_same_lon_in, gcr_180degree_cart) + + gcr_180degree_cart = [ + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, np.pi / 2.0]), + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -np.pi / 2.0]) + ] + + pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) + with self.assertRaises(ValueError): + point_within_gca(pt_same_lon_in, gcr_180degree_cart) + + # Test when the point and the GCA all have the same longitude + gcr_same_lon_cart = [ + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 1.5]), + ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, -1.5]) + ] + pt_same_lon_in = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.0, 0.0]) + self.assertTrue(point_within_gca(pt_same_lon_in, gcr_same_lon_cart)) + + pt_same_lon_out = ux.grid.coordinates.node_lonlat_rad_to_xyz( + [0.0, 1.500000000000001]) + res = point_within_gca(pt_same_lon_out, gcr_same_lon_cart) + self.assertFalse(res) + + pt_same_lon_out_2 = ux.grid.coordinates.node_lonlat_rad_to_xyz( + [0.1, 1.0]) + res = point_within_gca(pt_same_lon_out_2, gcr_same_lon_cart) + self.assertFalse(res) + + # And if we increase the digital place by one, it should be true again + pt_same_lon_out_add_one_place = ux.grid.coordinates.node_lonlat_rad_to_xyz( + [0.0, 1.5000000000000001]) + res = point_within_gca(pt_same_lon_out_add_one_place, gcr_same_lon_cart) + self.assertTrue(res) + + # Normal case + # GCR vertex0 in radian : [1.3003315590159483, -0.007004587172323237], + # GCR vertex1 in radian : [3.5997458123873827, -1.4893379576608758] + # Point in radian : [1.3005410084914981, -0.010444274637648326] + gcr_cart_2 = np.array([[0.267, 0.963, -0.007], [-0.073, -0.036, + -0.997]]) + pt_cart_within = np.array( + [0.25616109352676675, 0.9246590335292105, -0.010021496695000144]) + self.assertTrue(point_within_gca(pt_cart_within, gcr_cart_2, True)) + + # Test other more complicate cases : The anti-meridian case + + # GCR vertex0 in radian : [5.163808182822441, 0.6351384888657234], + # GCR vertex1 in radian : [0.8280410325693055, 0.42237025187091526] + # Point in radian : [0.12574759138415173, 0.770098701904903] + gcr_cart = np.array([[0.351, -0.724, 0.593], [0.617, 0.672, 0.410]]) + pt_cart = np.array( + [0.9438777657502077, 0.1193199333436068, 0.922714737029319]) + self.assertTrue(point_within_gca(pt_cart, gcr_cart, is_directed=True)) + # If we swap the gcr, it should throw a value error since it's larger than 180 degree + gcr_cart_flip = np.array([[0.617, 0.672, 0.410], [0.351, -0.724, + 0.593]]) + with self.assertRaises(ValueError): + point_within_gca(pt_cart, gcr_cart_flip, is_directed=True) + + # If we flip the gcr in the undirected mode, it should still work + self.assertTrue( + point_within_gca(pt_cart, gcr_cart_flip, is_directed=False)) + + # 2nd anti-meridian case + # GCR vertex0 in radian : [4.104711496596806, 0.5352983676533828], + # GCR vertex1 in radian : [2.4269979227622533, -0.007003212877856825] + # Point in radian : [0.43400375562899113, -0.49554509841586936] + gcr_cart_1 = np.array([[-0.491, -0.706, 0.510], [-0.755, 0.655, + -0.007]]) + pt_cart_within = np.array( + [0.6136726305712109, 0.28442243941920053, -0.365605190899831]) + self.assertFalse( + point_within_gca(pt_cart_within, gcr_cart_1, is_directed=True)) + self.assertFalse( + point_within_gca(pt_cart_within, gcr_cart_1, is_directed=False)) + + # The first case should not work and the second should work + v1_rad = [0.1, 0.0] + v2_rad = [2 * np.pi - 0.1, 0.0] + v1_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v1_rad) + v2_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz(v2_rad) + gcr_cart = np.array([v1_cart, v2_cart]) + pt_cart = ux.grid.coordinates.node_lonlat_rad_to_xyz([0.01, 0.0]) + with self.assertRaises(ValueError): + point_within_gca(pt_cart, gcr_cart, is_directed=True) + gcr_car_flipped = np.array([v2_cart, v1_cart]) + self.assertTrue( + point_within_gca(pt_cart, gcr_car_flipped, is_directed=True)) + + +class TestOperators(TestCase): + + def test_in_between(self): + # Test the in_between operator + self.assertTrue(in_between(0, 1, 2)) + self.assertTrue(in_between(-1, -1.5, -2)) + class TestVectorsAngel(TestCase): From ba9bc9fcdf0d2108b3c0185e197cfbd0b6d10a70 Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Fri, 31 Jul 2026 13:40:18 -0700 Subject: [PATCH 05/10] Add algorithm-level citations for spherical geometry implementations Adds a docs/references.bib with full BibTeX entries for the two Chen et al. UXarray papers plus the supporting numerical-methods references (Shewchuk, Knuth, Dekker, Higham, Jeannerod et al., Rump), extends citation.rst with an algorithm-to-publication mapping table alongside the existing Zenodo citation, and adds References sections to the docstrings of the specific APIs called out in the mapping: Grid.bounds and face_bounds_lon/lat, zonal_mean, the intersection APIs, the arcs predicates, and the compensated-arithmetic primitives in utils/computing.py. Resolves UXARRAY/uxarray#1631 Co-Authored-By: Claude Sonnet 5 --- docs/citation.rst | 90 +++++++++++++++++++++++++++++++++++ docs/references.bib | 86 +++++++++++++++++++++++++++++++++ uxarray/core/dataarray.py | 17 ++++++- uxarray/grid/arcs.py | 19 ++++++++ uxarray/grid/grid.py | 27 ++++++++++- uxarray/grid/intersections.py | 33 +++++++++++++ uxarray/utils/computing.py | 49 +++++++++++++++++++ 7 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 docs/references.bib diff --git a/docs/citation.rst b/docs/citation.rst index 4796688e9..23b55d874 100644 --- a/docs/citation.rst +++ b/docs/citation.rst @@ -27,3 +27,93 @@ For example: **UXarray Organization. (2021). UXarray (version 2025.06.0) [Software]. Project Raijin & Project SEATS. doi:10.5281/zenodo.15757812.** + +.. _algorithm-citations: + +Algorithm-Level Citations +========================== + +In addition to the package-level Zenodo citation above, several of the spherical +geometry and regridding algorithms implemented in UXarray are associated with +peer-reviewed methodological publications. If your work makes use of the APIs +listed below, please also cite the corresponding publication(s) alongside the +UXarray software citation. + +The definitions and geometric conventions for nodes, edges, and faces used +throughout UXarray are based on: + + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). "Accurate and Robust Geometric + Algorithms for Regridding on the Sphere." *Geoscientific Model + Development*, 19(14), 6545-6570. + `doi:10.5194/gmd-19-6545-2026 `_ + +Several of the intersection and remapping algorithms are additionally based on: + + Chen, H., Ullrich, P. A., and Panetta, J. (2026). "Fast and Accurate + Intersections on a Sphere." *SIAM Journal on Scientific Computing*, + 48(2), B208-B232. + `doi:10.1137/25M1737614 `_ + +Complete BibTeX entries for these and the supporting numerical-methods +references below are maintained in +`docs/references.bib `_. + +Algorithm-to-Publication Mapping +--------------------------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 30 50 + + * - Documentation section + - API or implementation + - Required citation(s) + * - Grid bounds + - :py:attr:`~uxarray.Grid.bounds` + - Chen et al. (2026), *GMD* + * - Grid bounds + - :py:attr:`~uxarray.Grid.face_bounds_lon` + - Chen et al. (2026), *GMD* + * - Grid bounds + - :py:attr:`~uxarray.Grid.face_bounds_lat` + - Chen et al. (2026), *GMD* + * - `Zonal Average `__ + - All zonal-average remapping implementations (e.g. :py:meth:`~uxarray.UxDataArray.zonal_average`) + - Chen, Ullrich & Panetta (2026), *SIAM J. Sci. Comput.* + * - `Spherical Geometry: Intersections `__ + - All spherical-intersection APIs in this section (:py:func:`~uxarray.grid.intersections.gca_gca_intersection`, :py:func:`~uxarray.grid.intersections.gca_const_lat_intersection`, :py:func:`~uxarray.grid.intersections.get_number_of_intersections`) + - Cite both: Chen et al. (2026), *GMD*; Chen, Ullrich & Panetta (2026), *SIAM J. Sci. Comput.* + * - `Spherical Geometry: Arcs `__ + - :py:func:`~uxarray.grid.arcs.extreme_gca_latitude` + - Chen et al. (2026), *GMD* + * - `Spherical Geometry: Arcs `__ + - :py:func:`~uxarray.grid.arcs.orient3d_on_sphere` + - Shewchuk (1997) + * - `Spherical Geometry: Arcs `__ + - :py:func:`~uxarray.grid.arcs.on_minor_arc` + - Shewchuk (1997) + * - `Spherical Geometry: Arcs `__ + - :py:func:`~uxarray.grid.arcs.in_between` + - No new citation required. Expected to be removed in a future release. + * - `Spherical Geometry: Arcs `__ + - :py:func:`~uxarray.grid.arcs.point_within_gca` + - No new citation required. Expected to be removed in a future release. + * - `Compensated Arithmetic `__ + - :py:func:`~uxarray.utils.computing.two_sum` + - Knuth (1997), *TAOCP Vol. 2*, Sec. 4.2.2, Theorem B + * - `Compensated Arithmetic `__ + - :py:func:`~uxarray.utils.computing.two_prod` + - Dekker (1971) + * - `Compensated Arithmetic `__ + - :py:func:`~uxarray.utils.computing.diff_of_products` + - Cite both: Higham (2002); Jeannerod, Louvet & Muller (2013) + * - `Compensated Arithmetic `__ + - :py:func:`~uxarray.utils.computing.accucross` + - Chen et al. (2026), *GMD* + * - `Compensated Arithmetic `__ + - :py:func:`~uxarray.utils.computing.accucross_pair` + - Chen et al. (2026), *GMD* + * - `Compensated Arithmetic `__ + - :py:func:`~uxarray.utils.computing.acc_sqrt_re` + - Rump (2023) diff --git a/docs/references.bib b/docs/references.bib new file mode 100644 index 000000000..3a5daacb1 --- /dev/null +++ b/docs/references.bib @@ -0,0 +1,86 @@ +% Algorithm-level references for UXarray's spherical geometry and +% regridding implementations, in addition to the package-level Zenodo +% citation described in citation.rst. See the "Algorithm-Level Citations" +% section of citation.rst for the human-readable mapping between these +% references and specific UXarray APIs. + +@Article{gmd-19-6545-2026, + author = {Chen, H. and Ullrich, P. A. and Panetta, J. and Marsico, D. and Hanke, M. and Jain, R. and Zhang, C. and Jacob, R. L.}, + title = {Accurate and robust geometric algorithms for regridding on the sphere}, + journal = {Geoscientific Model Development}, + volume = {19}, + year = {2026}, + number = {14}, + pages = {6545--6570}, + url = {https://gmd.copernicus.org/articles/19/6545/2026/}, + doi = {10.5194/gmd-19-6545-2026} +} + +@Article{doi:10.1137/25M1737614, + author = {Chen, Hongyu and Ullrich, Paul A. and Panetta, Julian}, + title = {Fast and Accurate Intersections on a Sphere}, + journal = {SIAM Journal on Scientific Computing}, + volume = {48}, + number = {2}, + pages = {B208--B232}, + year = {2026}, + doi = {10.1137/25M1737614}, + url = {https://doi.org/10.1137/25M1737614} +} + +@Article{shewchuk1997, + author = {Shewchuk, J. R.}, + title = {Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates}, + journal = {Discrete \& Computational Geometry}, + volume = {18}, + year = {1997}, + pages = {305--363}, + doi = {10.1007/PL00009321} +} + +@Book{knuth1997, + author = {Knuth, D. E.}, + title = {The Art of Computer Programming, Volume 2: Seminumerical Algorithms}, + edition = {3rd}, + publisher = {Addison-Wesley}, + year = {1997}, + note = {Section 4.2.2, Theorem B} +} + +@Article{dekker1971, + author = {Dekker, T. J.}, + title = {A Floating-Point Technique for Extending the Available Precision}, + journal = {Numerische Mathematik}, + volume = {18}, + year = {1971}, + pages = {224--242}, + doi = {10.1007/BF01397083} +} + +@Book{higham2002, + author = {Higham, N. J.}, + title = {Accuracy and Stability of Numerical Algorithms}, + edition = {2nd}, + publisher = {Society for Industrial and Applied Mathematics}, + year = {2002}, + doi = {10.1137/1.9780898718027} +} + +@Article{jeannerod2013, + author = {Jeannerod, C.-P. and Louvet, N. and Muller, J.-M.}, + title = {Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 x 2 Determinants}, + journal = {Mathematics of Computation}, + volume = {82}, + year = {2013}, + pages = {2245--2264}, + doi = {10.1090/S0025-5718-2013-02679-8} +} + +@Article{rump2023, + author = {Rump, S. M.}, + title = {Fast and Accurate Computation of the Euclidean Norm of a Vector}, + journal = {Japan Journal of Industrial and Applied Mathematics}, + volume = {40}, + year = {2023}, + doi = {10.1007/s13160-023-00593-8} +} diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 8f41a1627..86e0a5a00 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -690,6 +690,12 @@ def zonal_mean(self, lat=(-90, 90, 10), conservative: bool = False, **kwargs): Conservative averaging preserves integral quantities and is recommended for physical analysis. Non-conservative averaging samples at latitude lines. + + References + ---------- + Chen, H., Ullrich, P. A., and Panetta, J. (2026). Fast and accurate + intersections on a sphere. SIAM Journal on Scientific Computing, 48(2), + B208-B232. https://doi.org/10.1137/25M1737614 """ if not self._face_centered(): raise DataCenteringError( @@ -807,7 +813,12 @@ def zonal_mean(self, lat=(-90, 90, 10), conservative: bool = False, **kwargs): ) def zonal_average(self, lat=(-90, 90, 10), conservative: bool = False, **kwargs): - """Alias of zonal_mean; prefer `zonal_mean` for primary API.""" + """Alias of zonal_mean; prefer `zonal_mean` for primary API. + + See Also + -------- + zonal_mean : Full docstring, including algorithm references. + """ return self.zonal_mean(lat=lat, conservative=conservative, **kwargs) def zonal_anomaly(self, lat=(-90, 90, 10), conservative: bool = False): @@ -838,6 +849,10 @@ def zonal_anomaly(self, lat=(-90, 90, 10), conservative: bool = False): -------- >>> uxds["var"].zonal_anomaly() >>> uxds["var"].zonal_anomaly(lat=(-60, 60, 5), conservative=True) + + See Also + -------- + zonal_mean : Underlying zonal averaging algorithm and references. """ if not self._face_centered(): raise DataCenteringError( diff --git a/uxarray/grid/arcs.py b/uxarray/grid/arcs.py index 2edc2c8a8..6cf76b036 100644 --- a/uxarray/grid/arcs.py +++ b/uxarray/grid/arcs.py @@ -214,6 +214,13 @@ def extreme_gca_latitude(gca_cart, gca_lonlat, extreme_type): ------ ValueError If `extreme_type` is not 'max' or 'min'. + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 """ # Validate extreme_type if (extreme_type != "max") and (extreme_type != "min"): @@ -465,6 +472,12 @@ def orient3d_on_sphere(a, b, q, tol=_PREDICATE_ZERO_TOL): int +1 if q is to the left of a->b, -1 if to the right, 0 if collinear within ``tol``. + + References + ---------- + Shewchuk, J. R. (1997). Adaptive precision floating-point arithmetic and + fast robust geometric predicates. Discrete & Computational Geometry, 18, + 305-363. https://doi.org/10.1007/PL00009321 """ v = _orient3d_on_sphere_value(a, b, q) if v > tol: @@ -500,6 +513,12 @@ def on_minor_arc(q, a, b, tol=_ON_MINOR_ARC_TOL): mask (not bool) so callers can multiply it into validity products. An attempt to implement a similar Python function that provides the same functionality as AccuSphGeom's ``on_minor_arc_tol_ptr``. + + References + ---------- + Shewchuk, J. R. (1997). Adaptive precision floating-point arithmetic and + fast robust geometric predicates. Discrete & Computational Geometry, 18, + 305-363. https://doi.org/10.1007/PL00009321 """ return _on_minor_arc_xyz(q[0], q[1], q[2], a[0], a[1], a[2], b[0], b[1], b[2], tol) diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 27c65e183..e2c620986 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -1540,6 +1540,13 @@ def bounds(self) -> xr.DataArray: ------- bounds: :py:class:`xr.DataArray` An array of shape (:py:attr:`~uxarray.Grid.n_face`, `two`, `two`) + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 """ if "bounds" not in self._ds: _populate_face_bounds(self) @@ -1549,7 +1556,15 @@ def bounds(self) -> xr.DataArray: @property def face_bounds_lon(self): - """Longitude bounds for each face in degrees.""" + """Longitude bounds for each face in degrees. + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 + """ if "face_bounds_lon" not in self._ds: bounds = self.bounds.values @@ -1569,7 +1584,15 @@ def face_bounds_lon(self): @property def face_bounds_lat(self): - """Latitude bounds for each face in degrees.""" + """Latitude bounds for each face in degrees. + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 + """ if "face_bounds_lat" not in self._ds: bounds = self.bounds.values diff --git a/uxarray/grid/intersections.py b/uxarray/grid/intersections.py index c413e6686..e2269b219 100644 --- a/uxarray/grid/intersections.py +++ b/uxarray/grid/intersections.py @@ -432,6 +432,17 @@ def gca_gca_intersection(gca_a_xyz, gca_b_xyz): numpy.ndarray Intersection points, shape ``(2, 3)``, with unused rows filled with NaN (0, 1, or 2 valid rows). + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 + + Chen, H., Ullrich, P. A., and Panetta, J. (2026). Fast and accurate + intersections on a sphere. SIAM Journal on Scientific Computing, 48(2), + B208-B232. https://doi.org/10.1137/25M1737614 """ if gca_a_xyz.shape[1] != 3 or gca_b_xyz.shape[1] != 3: raise DimensionError("The two GCAs must be in the cartesian [x, y, z] format") @@ -664,6 +675,17 @@ def gca_const_lat_intersection(gca_cart, const_z): numpy.ndarray Intersection points, shape ``(2, 3)``, with unused rows filled with NaN (0, 1, or 2 valid rows). + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 + + Chen, H., Ullrich, P. A., and Panetta, J. (2026). Fast and accurate + intersections on a sphere. SIAM Journal on Scientific Computing, 48(2), + B208-B232. https://doi.org/10.1137/25M1737614 """ res = np.empty((2, 3)) res.fill(np.nan) @@ -727,6 +749,17 @@ def get_number_of_intersections(arr): ------- int Number of non-NaN intersection points (0, 1, or 2). + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 + + Chen, H., Ullrich, P. A., and Panetta, J. (2026). Fast and accurate + intersections on a sphere. SIAM Journal on Scientific Computing, 48(2), + B208-B232. https://doi.org/10.1137/25M1737614 """ row1_is_nan = np.all(np.isnan(arr[0])) row2_is_nan = np.all(np.isnan(arr[1])) diff --git a/uxarray/utils/computing.py b/uxarray/utils/computing.py index 80b661cad..1f60bd72a 100644 --- a/uxarray/utils/computing.py +++ b/uxarray/utils/computing.py @@ -66,6 +66,12 @@ def two_sum(a, b): Rounded sum fl(a + b). e : float Rounding error term; s + e = a + b exactly. + + References + ---------- + Knuth, D. E. (1997). The Art of Computer Programming, Volume 2: + Seminumerical Algorithms (3rd ed.). Addison-Wesley, Section 4.2.2, + Theorem B. """ s = a + b bp = s - a @@ -151,6 +157,12 @@ def two_prod(a, b): Rounded product fl(a * b). e : float Rounding error term; p + e = a * b exactly. + + References + ---------- + Dekker, T. J. (1971). A floating-point technique for extending the + available precision. Numerische Mathematik, 18, 224-242. + https://doi.org/10.1007/BF01397083 """ return _two_prod_fma(a, b) @@ -174,6 +186,12 @@ def two_prod(a, b): Rounded product fl(a * b). e : float Rounding error term; p + e = a * b exactly. + + References + ---------- + Dekker, T. J. (1971). A floating-point technique for extending the + available precision. Numerische Mathematik, 18, 224-242. + https://doi.org/10.1007/BF01397083 """ return _two_prod_veltkamp(a, b) @@ -204,6 +222,17 @@ def diff_of_products(a, b, c, d): High-order part of the accurate result. lo : float Low-order correction term; hi + lo equals the accurate value. + + References + ---------- + Higham, N. J. (2002). Accuracy and Stability of Numerical Algorithms + (2nd ed.). Society for Industrial and Applied Mathematics. + https://doi.org/10.1137/1.9780898718027 + + Jeannerod, C.-P., Louvet, N., and Muller, J.-M. (2013). Further analysis + of Kahan's algorithm for the accurate computation of 2 x 2 determinants. + Mathematics of Computation, 82, 2245-2264. + https://doi.org/10.1090/S0025-5718-2013-02679-8 """ w, e_w = two_prod(c, d) x, e_x = two_prod(a, b) @@ -235,6 +264,13 @@ def accucross(a0, a1, a2, b0, b1, b2): ------- x_hi, y_hi, z_hi, x_lo, y_lo, z_lo : float High and low parts of each cross-product component. + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 """ x_hi, x_lo = diff_of_products(a1, b2, a2, b1) y_hi, y_lo = diff_of_products(a2, b0, a0, b2) @@ -336,6 +372,13 @@ def accucross_pair( ------- x_hi, y_hi, z_hi, x_lo, y_lo, z_lo : float Compensated cross-product components. + + References + ---------- + Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). Accurate and robust geometric + algorithms for regridding on the sphere. Geoscientific Model + Development, 19(14), 6545-6570. https://doi.org/10.5194/gmd-19-6545-2026 """ # x = (ay*bz) - (az*by), expanded over all four hi/lo cross-terms x_hi, x_lo = _cdp8( @@ -507,6 +550,12 @@ def acc_sqrt_re(value, error=0.0): Rounded sqrt, fl(sqrt(value)). correction : float Additive correction; root + correction ≈ sqrt(value + error) to ~1 ulp. + + References + ---------- + Rump, S. M. (2023). Fast and accurate computation of the Euclidean norm + of a vector. Japan Journal of Industrial and Applied Mathematics, 40. + https://doi.org/10.1007/s13160-023-00593-8 """ # Branch-free, matching AccuSphGeom acc_sqrt_re exactly. Negative value # yields nan via math.sqrt and root==0 yields nan via the 0/0 correction, From f990bd44451db58f7f1d2e8725cabb1d3399756c Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Fri, 31 Jul 2026 14:02:28 -0700 Subject: [PATCH 06/10] Fix citation.rst heading level, links, and citation format per review - Demote "Algorithm-Level Citations" from a page-title-weight heading to a proper second-level section, and wrap the call-to-action intro in a note admonition instead of plain body text. - Replace short in-text citations ("Chen et al. (2026), GMD") in the mapping table with the full citation (title, journal, DOI) so readers don't have to cross-reference elsewhere to know what they're citing. - Drop the single hand-maintained docs/references.bib (and its now-dead link to a file that doesn't exist on main yet) in favor of one small .bib file per publication under docs/_static/citations/, downloadable directly from next to each citation via Sphinx's :download: role. - Fix "Documentation section" links: every row pointed at api.html#remapping regardless of section; now each points at its real anchor (descriptors, zonal-average, intersections, arcs, compensated-arithmetic), verified against the built HTML. Co-Authored-By: Claude Sonnet 5 --- docs/_static/citations/chen2026-gmd.bib | 11 ++ docs/_static/citations/chen2026-siam.bib | 11 ++ docs/_static/citations/dekker1971.bib | 9 ++ docs/_static/citations/higham2002.bib | 8 ++ docs/_static/citations/jeannerod2013.bib | 9 ++ docs/_static/citations/knuth1997.bib | 8 ++ docs/_static/citations/rump2023.bib | 8 ++ docs/_static/citations/shewchuk1997.bib | 9 ++ docs/citation.rst | 142 +++++++++++++++-------- docs/references.bib | 86 -------------- 10 files changed, 164 insertions(+), 137 deletions(-) create mode 100644 docs/_static/citations/chen2026-gmd.bib create mode 100644 docs/_static/citations/chen2026-siam.bib create mode 100644 docs/_static/citations/dekker1971.bib create mode 100644 docs/_static/citations/higham2002.bib create mode 100644 docs/_static/citations/jeannerod2013.bib create mode 100644 docs/_static/citations/knuth1997.bib create mode 100644 docs/_static/citations/rump2023.bib create mode 100644 docs/_static/citations/shewchuk1997.bib delete mode 100644 docs/references.bib diff --git a/docs/_static/citations/chen2026-gmd.bib b/docs/_static/citations/chen2026-gmd.bib new file mode 100644 index 000000000..01d5c7451 --- /dev/null +++ b/docs/_static/citations/chen2026-gmd.bib @@ -0,0 +1,11 @@ +@Article{gmd-19-6545-2026, + author = {Chen, H. and Ullrich, P. A. and Panetta, J. and Marsico, D. and Hanke, M. and Jain, R. and Zhang, C. and Jacob, R. L.}, + title = {Accurate and robust geometric algorithms for regridding on the sphere}, + journal = {Geoscientific Model Development}, + volume = {19}, + year = {2026}, + number = {14}, + pages = {6545--6570}, + url = {https://gmd.copernicus.org/articles/19/6545/2026/}, + doi = {10.5194/gmd-19-6545-2026} +} diff --git a/docs/_static/citations/chen2026-siam.bib b/docs/_static/citations/chen2026-siam.bib new file mode 100644 index 000000000..e78d1dcec --- /dev/null +++ b/docs/_static/citations/chen2026-siam.bib @@ -0,0 +1,11 @@ +@Article{doi:10.1137/25M1737614, + author = {Chen, Hongyu and Ullrich, Paul A. and Panetta, Julian}, + title = {Fast and Accurate Intersections on a Sphere}, + journal = {SIAM Journal on Scientific Computing}, + volume = {48}, + number = {2}, + pages = {B208--B232}, + year = {2026}, + doi = {10.1137/25M1737614}, + url = {https://doi.org/10.1137/25M1737614} +} diff --git a/docs/_static/citations/dekker1971.bib b/docs/_static/citations/dekker1971.bib new file mode 100644 index 000000000..03c59f0c5 --- /dev/null +++ b/docs/_static/citations/dekker1971.bib @@ -0,0 +1,9 @@ +@Article{dekker1971, + author = {Dekker, T. J.}, + title = {A Floating-Point Technique for Extending the Available Precision}, + journal = {Numerische Mathematik}, + volume = {18}, + year = {1971}, + pages = {224--242}, + doi = {10.1007/BF01397083} +} diff --git a/docs/_static/citations/higham2002.bib b/docs/_static/citations/higham2002.bib new file mode 100644 index 000000000..4b92def38 --- /dev/null +++ b/docs/_static/citations/higham2002.bib @@ -0,0 +1,8 @@ +@Book{higham2002, + author = {Higham, N. J.}, + title = {Accuracy and Stability of Numerical Algorithms}, + edition = {2nd}, + publisher = {Society for Industrial and Applied Mathematics}, + year = {2002}, + doi = {10.1137/1.9780898718027} +} diff --git a/docs/_static/citations/jeannerod2013.bib b/docs/_static/citations/jeannerod2013.bib new file mode 100644 index 000000000..d3594f50b --- /dev/null +++ b/docs/_static/citations/jeannerod2013.bib @@ -0,0 +1,9 @@ +@Article{jeannerod2013, + author = {Jeannerod, C.-P. and Louvet, N. and Muller, J.-M.}, + title = {Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 x 2 Determinants}, + journal = {Mathematics of Computation}, + volume = {82}, + year = {2013}, + pages = {2245--2264}, + doi = {10.1090/S0025-5718-2013-02679-8} +} diff --git a/docs/_static/citations/knuth1997.bib b/docs/_static/citations/knuth1997.bib new file mode 100644 index 000000000..5c37334c8 --- /dev/null +++ b/docs/_static/citations/knuth1997.bib @@ -0,0 +1,8 @@ +@Book{knuth1997, + author = {Knuth, D. E.}, + title = {The Art of Computer Programming, Volume 2: Seminumerical Algorithms}, + edition = {3rd}, + publisher = {Addison-Wesley}, + year = {1997}, + note = {Section 4.2.2, Theorem B} +} diff --git a/docs/_static/citations/rump2023.bib b/docs/_static/citations/rump2023.bib new file mode 100644 index 000000000..d18e82fa6 --- /dev/null +++ b/docs/_static/citations/rump2023.bib @@ -0,0 +1,8 @@ +@Article{rump2023, + author = {Rump, S. M.}, + title = {Fast and Accurate Computation of the Euclidean Norm of a Vector}, + journal = {Japan Journal of Industrial and Applied Mathematics}, + volume = {40}, + year = {2023}, + doi = {10.1007/s13160-023-00593-8} +} diff --git a/docs/_static/citations/shewchuk1997.bib b/docs/_static/citations/shewchuk1997.bib new file mode 100644 index 000000000..2debb4c89 --- /dev/null +++ b/docs/_static/citations/shewchuk1997.bib @@ -0,0 +1,9 @@ +@Article{shewchuk1997, + author = {Shewchuk, J. R.}, + title = {Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates}, + journal = {Discrete \& Computational Geometry}, + volume = {18}, + year = {1997}, + pages = {305--363}, + doi = {10.1007/PL00009321} +} diff --git a/docs/citation.rst b/docs/citation.rst index 23b55d874..321299e1a 100644 --- a/docs/citation.rst +++ b/docs/citation.rst @@ -31,89 +31,129 @@ Project Raijin & Project SEATS. doi:10.5281/zenodo.15757812.** .. _algorithm-citations: Algorithm-Level Citations -========================== +-------------------------- -In addition to the package-level Zenodo citation above, several of the spherical -geometry and regridding algorithms implemented in UXarray are associated with -peer-reviewed methodological publications. If your work makes use of the APIs -listed below, please also cite the corresponding publication(s) alongside the -UXarray software citation. +.. note:: + + In addition to the package-level Zenodo citation above, several of the spherical + geometry and regridding algorithms implemented in UXarray are associated with + peer-reviewed methodological publications. If your work makes use of the APIs + listed in the table below, please also cite the corresponding publication(s) + alongside the UXarray software citation. The definitions and geometric conventions for nodes, edges, and faces used throughout UXarray are based on: - Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., - Zhang, C., and Jacob, R. L. (2026). "Accurate and Robust Geometric - Algorithms for Regridding on the Sphere." *Geoscientific Model - Development*, 19(14), 6545-6570. - `doi:10.5194/gmd-19-6545-2026 `_ + |cite-gmd| Several of the intersection and remapping algorithms are additionally based on: - Chen, H., Ullrich, P. A., and Panetta, J. (2026). "Fast and Accurate - Intersections on a Sphere." *SIAM Journal on Scientific Computing*, - 48(2), B208-B232. - `doi:10.1137/25M1737614 `_ - -Complete BibTeX entries for these and the supporting numerical-methods -references below are maintained in -`docs/references.bib `_. + |cite-siam| + +.. |cite-gmd| replace:: Chen, H., Ullrich, P. A., Panetta, J., Marsico, D., Hanke, M., Jain, R., + Zhang, C., and Jacob, R. L. (2026). "Accurate and Robust Geometric Algorithms for + Regridding on the Sphere." *Geoscientific Model Development*, 19(14), 6545-6570. + `doi:10.5194/gmd-19-6545-2026 `__ + (:download:`BibTeX <_static/citations/chen2026-gmd.bib>`) + +.. |cite-siam| replace:: Chen, H., Ullrich, P. A., and Panetta, J. (2026). "Fast and Accurate + Intersections on a Sphere." *SIAM Journal on Scientific Computing*, 48(2), B208-B232. + `doi:10.1137/25M1737614 `__ + (:download:`BibTeX <_static/citations/chen2026-siam.bib>`) + +.. |cite-shewchuk| replace:: Shewchuk, J. R. (1997). "Adaptive Precision Floating-Point + Arithmetic and Fast Robust Geometric Predicates." *Discrete & Computational Geometry*, + 18, 305-363. `doi:10.1007/PL00009321 `__ + (:download:`BibTeX <_static/citations/shewchuk1997.bib>`) + +.. |cite-knuth| replace:: Knuth, D. E. (1997). *The Art of Computer Programming, Volume 2: + Seminumerical Algorithms* (3rd ed.). Addison-Wesley, Section 4.2.2, Theorem B. + (:download:`BibTeX <_static/citations/knuth1997.bib>`) + +.. |cite-dekker| replace:: Dekker, T. J. (1971). "A Floating-Point Technique for Extending + the Available Precision." *Numerische Mathematik*, 18, 224-242. + `doi:10.1007/BF01397083 `__ + (:download:`BibTeX <_static/citations/dekker1971.bib>`) + +.. |cite-higham| replace:: Higham, N. J. (2002). *Accuracy and Stability of Numerical + Algorithms* (2nd ed.). Society for Industrial and Applied Mathematics. + `doi:10.1137/1.9780898718027 `__ + (:download:`BibTeX <_static/citations/higham2002.bib>`) + +.. |cite-jeannerod| replace:: Jeannerod, C.-P., Louvet, N., and Muller, J.-M. (2013). + "Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 x 2 + Determinants." *Mathematics of Computation*, 82, 2245-2264. + `doi:10.1090/S0025-5718-2013-02679-8 `__ + (:download:`BibTeX <_static/citations/jeannerod2013.bib>`) + +.. |cite-rump| replace:: Rump, S. M. (2023). "Fast and Accurate Computation of the + Euclidean Norm of a Vector." *Japan Journal of Industrial and Applied Mathematics*, 40. + `doi:10.1007/s13160-023-00593-8 `__ + (:download:`BibTeX <_static/citations/rump2023.bib>`) Algorithm-to-Publication Mapping ---------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 - :widths: 20 30 50 + :widths: 15 20 65 * - Documentation section - API or implementation - Required citation(s) - * - Grid bounds + * - `Descriptors `__ - :py:attr:`~uxarray.Grid.bounds` - - Chen et al. (2026), *GMD* - * - Grid bounds + - |cite-gmd| + * - `Descriptors `__ - :py:attr:`~uxarray.Grid.face_bounds_lon` - - Chen et al. (2026), *GMD* - * - Grid bounds + - |cite-gmd| + * - `Descriptors `__ - :py:attr:`~uxarray.Grid.face_bounds_lat` - - Chen et al. (2026), *GMD* - * - `Zonal Average `__ + - |cite-gmd| + * - `Zonal Average `__ - All zonal-average remapping implementations (e.g. :py:meth:`~uxarray.UxDataArray.zonal_average`) - - Chen, Ullrich & Panetta (2026), *SIAM J. Sci. Comput.* - * - `Spherical Geometry: Intersections `__ + - |cite-siam| + * - `Spherical Geometry: Intersections `__ - All spherical-intersection APIs in this section (:py:func:`~uxarray.grid.intersections.gca_gca_intersection`, :py:func:`~uxarray.grid.intersections.gca_const_lat_intersection`, :py:func:`~uxarray.grid.intersections.get_number_of_intersections`) - - Cite both: Chen et al. (2026), *GMD*; Chen, Ullrich & Panetta (2026), *SIAM J. Sci. Comput.* - * - `Spherical Geometry: Arcs `__ + - **Cite both:** + + |cite-gmd| + + |cite-siam| + * - `Spherical Geometry: Arcs `__ - :py:func:`~uxarray.grid.arcs.extreme_gca_latitude` - - Chen et al. (2026), *GMD* - * - `Spherical Geometry: Arcs `__ + - |cite-gmd| + * - `Spherical Geometry: Arcs `__ - :py:func:`~uxarray.grid.arcs.orient3d_on_sphere` - - Shewchuk (1997) - * - `Spherical Geometry: Arcs `__ + - |cite-shewchuk| + * - `Spherical Geometry: Arcs `__ - :py:func:`~uxarray.grid.arcs.on_minor_arc` - - Shewchuk (1997) - * - `Spherical Geometry: Arcs `__ + - |cite-shewchuk| + * - `Spherical Geometry: Arcs `__ - :py:func:`~uxarray.grid.arcs.in_between` - No new citation required. Expected to be removed in a future release. - * - `Spherical Geometry: Arcs `__ + * - `Spherical Geometry: Arcs `__ - :py:func:`~uxarray.grid.arcs.point_within_gca` - No new citation required. Expected to be removed in a future release. - * - `Compensated Arithmetic `__ + * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.two_sum` - - Knuth (1997), *TAOCP Vol. 2*, Sec. 4.2.2, Theorem B - * - `Compensated Arithmetic `__ + - |cite-knuth| + * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.two_prod` - - Dekker (1971) - * - `Compensated Arithmetic `__ + - |cite-dekker| + * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.diff_of_products` - - Cite both: Higham (2002); Jeannerod, Louvet & Muller (2013) - * - `Compensated Arithmetic `__ + - **Cite both:** + + |cite-higham| + + |cite-jeannerod| + * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.accucross` - - Chen et al. (2026), *GMD* - * - `Compensated Arithmetic `__ + - |cite-gmd| + * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.accucross_pair` - - Chen et al. (2026), *GMD* - * - `Compensated Arithmetic `__ + - |cite-gmd| + * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.acc_sqrt_re` - - Rump (2023) + - |cite-rump| diff --git a/docs/references.bib b/docs/references.bib deleted file mode 100644 index 3a5daacb1..000000000 --- a/docs/references.bib +++ /dev/null @@ -1,86 +0,0 @@ -% Algorithm-level references for UXarray's spherical geometry and -% regridding implementations, in addition to the package-level Zenodo -% citation described in citation.rst. See the "Algorithm-Level Citations" -% section of citation.rst for the human-readable mapping between these -% references and specific UXarray APIs. - -@Article{gmd-19-6545-2026, - author = {Chen, H. and Ullrich, P. A. and Panetta, J. and Marsico, D. and Hanke, M. and Jain, R. and Zhang, C. and Jacob, R. L.}, - title = {Accurate and robust geometric algorithms for regridding on the sphere}, - journal = {Geoscientific Model Development}, - volume = {19}, - year = {2026}, - number = {14}, - pages = {6545--6570}, - url = {https://gmd.copernicus.org/articles/19/6545/2026/}, - doi = {10.5194/gmd-19-6545-2026} -} - -@Article{doi:10.1137/25M1737614, - author = {Chen, Hongyu and Ullrich, Paul A. and Panetta, Julian}, - title = {Fast and Accurate Intersections on a Sphere}, - journal = {SIAM Journal on Scientific Computing}, - volume = {48}, - number = {2}, - pages = {B208--B232}, - year = {2026}, - doi = {10.1137/25M1737614}, - url = {https://doi.org/10.1137/25M1737614} -} - -@Article{shewchuk1997, - author = {Shewchuk, J. R.}, - title = {Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates}, - journal = {Discrete \& Computational Geometry}, - volume = {18}, - year = {1997}, - pages = {305--363}, - doi = {10.1007/PL00009321} -} - -@Book{knuth1997, - author = {Knuth, D. E.}, - title = {The Art of Computer Programming, Volume 2: Seminumerical Algorithms}, - edition = {3rd}, - publisher = {Addison-Wesley}, - year = {1997}, - note = {Section 4.2.2, Theorem B} -} - -@Article{dekker1971, - author = {Dekker, T. J.}, - title = {A Floating-Point Technique for Extending the Available Precision}, - journal = {Numerische Mathematik}, - volume = {18}, - year = {1971}, - pages = {224--242}, - doi = {10.1007/BF01397083} -} - -@Book{higham2002, - author = {Higham, N. J.}, - title = {Accuracy and Stability of Numerical Algorithms}, - edition = {2nd}, - publisher = {Society for Industrial and Applied Mathematics}, - year = {2002}, - doi = {10.1137/1.9780898718027} -} - -@Article{jeannerod2013, - author = {Jeannerod, C.-P. and Louvet, N. and Muller, J.-M.}, - title = {Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 x 2 Determinants}, - journal = {Mathematics of Computation}, - volume = {82}, - year = {2013}, - pages = {2245--2264}, - doi = {10.1090/S0025-5718-2013-02679-8} -} - -@Article{rump2023, - author = {Rump, S. M.}, - title = {Fast and Accurate Computation of the Euclidean Norm of a Vector}, - journal = {Japan Journal of Industrial and Applied Mathematics}, - volume = {40}, - year = {2023}, - doi = {10.1007/s13160-023-00593-8} -} From faf255f6004c854686f9b85e85b49f17fa8b1059 Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Fri, 31 Jul 2026 14:12:51 -0700 Subject: [PATCH 07/10] Reword algorithm-citations note per author-provided wording Replace the generic "please also cite the corresponding publication(s)" note with more precise guidance on when citation is expected: results reported in an academic work that depend on one of these algorithms (e.g. computed face areas or regridding weights used as analysis input), as opposed to incidental use in tutorials/internal tools/other software that merely depends on UXarray. Co-Authored-By: Claude Sonnet 5 --- docs/citation.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/citation.rst b/docs/citation.rst index 321299e1a..4c2402a9d 100644 --- a/docs/citation.rst +++ b/docs/citation.rst @@ -35,11 +35,15 @@ Algorithm-Level Citations .. note:: - In addition to the package-level Zenodo citation above, several of the spherical - geometry and regridding algorithms implemented in UXarray are associated with - peer-reviewed methodological publications. If your work makes use of the APIs - listed in the table below, please also cite the corresponding publication(s) - alongside the UXarray software citation. + In addition to the package-level Zenodo citation, several spherical geometry and + regridding algorithms in UXarray implement methods from peer-reviewed publications. + If a result you report in an academic work (paper, thesis, preprint, technical + report) depends on one of these algorithms — e.g., you use computed face areas or + regridding weights as an input to your analysis — please also cite the + corresponding publication listed below, in addition to the UXarray software + citation. This does not apply to incidental use of these APIs in code that isn't + producing a citable scientific result (e.g., tutorials, internal tools, or + software that merely depends on UXarray). The definitions and geometric conventions for nodes, edges, and faces used throughout UXarray are based on: From 7d67794f6ea95479b8a54668a0906b2bdb34dd05 Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Sun, 2 Aug 2026 11:55:50 -0700 Subject: [PATCH 08/10] Reword note wording per author feedback Replace em dash punctuation with parentheses (no em dashes unless explicitly requested), swap the citation-trigger examples for concrete UXarray operations (latlon bounds, zonal-mean, conservative remapping, computed face area), and correct "intersection and remapping algorithms" to "intersection and geometry operators" in the SIAM paper's intro sentence. --- docs/citation.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/citation.rst b/docs/citation.rst index 4c2402a9d..43f10d632 100644 --- a/docs/citation.rst +++ b/docs/citation.rst @@ -38,19 +38,19 @@ Algorithm-Level Citations In addition to the package-level Zenodo citation, several spherical geometry and regridding algorithms in UXarray implement methods from peer-reviewed publications. If a result you report in an academic work (paper, thesis, preprint, technical - report) depends on one of these algorithms — e.g., you use computed face areas or - regridding weights as an input to your analysis — please also cite the - corresponding publication listed below, in addition to the UXarray software - citation. This does not apply to incidental use of these APIs in code that isn't - producing a citable scientific result (e.g., tutorials, internal tools, or - software that merely depends on UXarray). + report) depends on one of these algorithms (e.g., latlon bounds, zonal-mean, + conservative remapping, computed face area), please also cite the corresponding + publication listed below, in addition to the UXarray software citation. This does + not apply to incidental use of these APIs in code that isn't producing a citable + scientific result (e.g., tutorials, internal tools, or software that merely + depends on UXarray). The definitions and geometric conventions for nodes, edges, and faces used throughout UXarray are based on: |cite-gmd| -Several of the intersection and remapping algorithms are additionally based on: +Several of the intersection and geometry operators are additionally based on: |cite-siam| From eed89651682fe5ba9de86d17db1662b3d8836602 Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Sun, 2 Aug 2026 12:05:02 -0700 Subject: [PATCH 09/10] Merge mapping table rows and sort by API reference order Merge table rows that share the same documentation section and required citation (Descriptors bounds/face_bounds_lon/face_bounds_lat; Arcs orient3d_on_sphere/on_minor_arc; Arcs in_between/point_within_gca; Compensated Arithmetic accucross/accucross_pair) into single rows listing each API on its own line, instead of repeating the citation per row. Also reorder the Arcs rows to match the order APIs actually appear in api.rst (in_between, point_within_gca, extreme_gca_latitude, orient3d_on_sphere, on_minor_arc); the other sections already matched. --- docs/citation.rst | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/docs/citation.rst b/docs/citation.rst index 43f10d632..728584cc5 100644 --- a/docs/citation.rst +++ b/docs/citation.rst @@ -107,12 +107,10 @@ Algorithm-to-Publication Mapping - Required citation(s) * - `Descriptors `__ - :py:attr:`~uxarray.Grid.bounds` - - |cite-gmd| - * - `Descriptors `__ - - :py:attr:`~uxarray.Grid.face_bounds_lon` - - |cite-gmd| - * - `Descriptors `__ - - :py:attr:`~uxarray.Grid.face_bounds_lat` + + :py:attr:`~uxarray.Grid.face_bounds_lon` + + :py:attr:`~uxarray.Grid.face_bounds_lat` - |cite-gmd| * - `Zonal Average `__ - All zonal-average remapping implementations (e.g. :py:meth:`~uxarray.UxDataArray.zonal_average`) @@ -124,21 +122,19 @@ Algorithm-to-Publication Mapping |cite-gmd| |cite-siam| + * - `Spherical Geometry: Arcs `__ + - :py:func:`~uxarray.grid.arcs.in_between` + + :py:func:`~uxarray.grid.arcs.point_within_gca` + - No new citation required. Expected to be removed in a future release. * - `Spherical Geometry: Arcs `__ - :py:func:`~uxarray.grid.arcs.extreme_gca_latitude` - |cite-gmd| * - `Spherical Geometry: Arcs `__ - :py:func:`~uxarray.grid.arcs.orient3d_on_sphere` + + :py:func:`~uxarray.grid.arcs.on_minor_arc` - |cite-shewchuk| - * - `Spherical Geometry: Arcs `__ - - :py:func:`~uxarray.grid.arcs.on_minor_arc` - - |cite-shewchuk| - * - `Spherical Geometry: Arcs `__ - - :py:func:`~uxarray.grid.arcs.in_between` - - No new citation required. Expected to be removed in a future release. - * - `Spherical Geometry: Arcs `__ - - :py:func:`~uxarray.grid.arcs.point_within_gca` - - No new citation required. Expected to be removed in a future release. * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.two_sum` - |cite-knuth| @@ -154,9 +150,8 @@ Algorithm-to-Publication Mapping |cite-jeannerod| * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.accucross` - - |cite-gmd| - * - `Compensated Arithmetic `__ - - :py:func:`~uxarray.utils.computing.accucross_pair` + + :py:func:`~uxarray.utils.computing.accucross_pair` - |cite-gmd| * - `Compensated Arithmetic `__ - :py:func:`~uxarray.utils.computing.acc_sqrt_re` From 7ef5a825d72a784a328256cd75d32d8234f0a925 Mon Sep 17 00:00:00 2001 From: Hongyu Chen Date: Sun, 2 Aug 2026 12:17:03 -0700 Subject: [PATCH 10/10] =?UTF-8?q?Fix=20Jeannerod=20et=20al.=20title:=20use?= =?UTF-8?q?=20=C3=97=20not=20x=20for=20"2=20=C3=97=202=20Determinants"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Independent citation audit against the DOI landing page found the published title uses the multiplication sign (2 × 2), not the letter x, in citation.rst, the jeannerod2013.bib entry, and the diff_of_products docstring. The .bib uses the LaTeX $\times$ form for portability across BibTeX toolchains; the RST/docstring prose uses the Unicode × character directly. --- docs/_static/citations/jeannerod2013.bib | 2 +- docs/citation.rst | 2 +- uxarray/utils/computing.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/_static/citations/jeannerod2013.bib b/docs/_static/citations/jeannerod2013.bib index d3594f50b..38cb44ad5 100644 --- a/docs/_static/citations/jeannerod2013.bib +++ b/docs/_static/citations/jeannerod2013.bib @@ -1,6 +1,6 @@ @Article{jeannerod2013, author = {Jeannerod, C.-P. and Louvet, N. and Muller, J.-M.}, - title = {Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 x 2 Determinants}, + title = {Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 $\times$ 2 Determinants}, journal = {Mathematics of Computation}, volume = {82}, year = {2013}, diff --git a/docs/citation.rst b/docs/citation.rst index 728584cc5..9e2ce6929 100644 --- a/docs/citation.rst +++ b/docs/citation.rst @@ -85,7 +85,7 @@ Several of the intersection and geometry operators are additionally based on: (:download:`BibTeX <_static/citations/higham2002.bib>`) .. |cite-jeannerod| replace:: Jeannerod, C.-P., Louvet, N., and Muller, J.-M. (2013). - "Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 x 2 + "Further Analysis of Kahan's Algorithm for the Accurate Computation of 2 × 2 Determinants." *Mathematics of Computation*, 82, 2245-2264. `doi:10.1090/S0025-5718-2013-02679-8 `__ (:download:`BibTeX <_static/citations/jeannerod2013.bib>`) diff --git a/uxarray/utils/computing.py b/uxarray/utils/computing.py index 1f60bd72a..aff800372 100644 --- a/uxarray/utils/computing.py +++ b/uxarray/utils/computing.py @@ -230,7 +230,7 @@ def diff_of_products(a, b, c, d): https://doi.org/10.1137/1.9780898718027 Jeannerod, C.-P., Louvet, N., and Muller, J.-M. (2013). Further analysis - of Kahan's algorithm for the accurate computation of 2 x 2 determinants. + of Kahan's algorithm for the accurate computation of 2 × 2 determinants. Mathematics of Computation, 82, 2245-2264. https://doi.org/10.1090/S0025-5718-2013-02679-8 """