Skip to content

Commit 5936bb9

Browse files
committed
updated method for finding barycentric weights
1 parent c545f47 commit 5936bb9

3 files changed

Lines changed: 92 additions & 36 deletions

File tree

benchmarks/mpas_ocean.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ def time_nearest_neighbor_remapping(self):
138138
def time_inverse_distance_weighted_remapping(self):
139139
self.uxds_480["bottomDepth"].remap.inverse_distance_weighted(self.uxds_120.uxgrid)
140140

141+
def time_bilinear_remapping(self):
142+
self.uxds_480["bottomDepth"].remap.bilinear(self.uxds_120.uxgrid)
143+
141144

142145
class HoleEdgeIndices(DatasetBenchmark):
143146
def time_construct_hole_edge_indices(self, resolution):

uxarray/grid/geometry.py

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from uxarray.grid.intersections import (
2525
gca_gca_intersection,
2626
)
27+
from uxarray.grid.utils import _get_cartesian_face_edge_nodes
2728
from uxarray.utils.computing import allclose
2829

2930
POLE_POINTS_XYZ = {
@@ -1251,7 +1252,7 @@ def barycentric_coordinates_cartesian(polygon, point):
12511252
Parameters
12521253
----------
12531254
polygon: np.array
1254-
Cartesian coordinates of the polygons' nodes
1255+
Cartesian coordinates of the polygon nodes
12551256
point: np.array
12561257
Cartesian coordinates of the point.
12571258
@@ -1262,7 +1263,7 @@ def barycentric_coordinates_cartesian(polygon, point):
12621263
>>> point_xyz = np.array([0.0, 0.0, 1.0], dtype=np.float64)
12631264
12641265
Define a cartesian polygon:
1265-
>>> point_xyz = np.array(
1266+
>>> polygon = np.array(
12661267
... [[0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0]], dtype=np.float64
12671268
... )
12681269
@@ -1272,11 +1273,13 @@ def barycentric_coordinates_cartesian(polygon, point):
12721273
Returns
12731274
-------
12741275
weights: np.ndarray
1275-
Barycentric coordinates corresponding to each vertex in the polygon.
1276+
Barycentric coordinates corresponding to each vertex in the triangle.
1277+
nodes: np.ndarray
1278+
Indices of which nodes of the polygon form the triangle containing the point
12761279
"""
1280+
12771281
n = len(polygon)
1278-
weights = np.zeros(n)
1279-
total_area = 0.0
1282+
weights = np.zeros(3)
12801283

12811284
# Use the first vertex as a reference point to form triangles
12821285
p0 = polygon[0]
@@ -1285,41 +1288,64 @@ def barycentric_coordinates_cartesian(polygon, point):
12851288
# Get the two other points of the triangle
12861289
p1, p2 = polygon[i], polygon[i + 1]
12871290

1288-
# Find the inside portion of the equation: p - p0 =(p1 - p0)b + (p2 - p0)c
1289-
v1, v2 = p1 - p0, p2 - p0
1291+
# Get the faces in terms of their edges for the point_in_face check
1292+
face_edge = _get_cartesian_face_edge_nodes(
1293+
face_idx=0,
1294+
face_node_connectivity=np.array([[0, 1, 2]]),
1295+
n_edges_per_face=np.array([3]),
1296+
node_x=np.array([p0[0], p1[0], p2[0]], dtype=np.float64),
1297+
node_y=np.array([p0[1], p1[1], p2[1]], dtype=np.float64),
1298+
node_z=np.array([p0[2], p1[2], p2[2]], dtype=np.float64),
1299+
)
1300+
1301+
# Check to see if the point lies within the current triangle
1302+
contains_point = point_in_face(
1303+
face_edge,
1304+
point,
1305+
inclusive=True,
1306+
)
1307+
1308+
# If the point is in the current triangle, get the weights for that triangle
1309+
if contains_point:
1310+
# Find the inside portion of the equation: p - p0 =(p1 - p0)b + (p2 - p0)c
1311+
v1, v2 = p1 - p0, p2 - p0
1312+
1313+
# Solve the left side of the equation
1314+
vp = point - p0
12901315

1291-
# Solve the left side of the equation
1292-
vp = point - p0
1316+
# Find the largest coordinate, we will ignore this one and use the other two
1317+
abs_det = np.abs(np.cross(v1, v2))
1318+
max_coord = np.argmax(abs_det)
12931319

1294-
# Find the largest coordinate, we will ignore this one and use the other two
1295-
abs_det = np.abs(np.cross(v1, v2))
1296-
max_coord = np.argmax(abs_det)
1320+
# Select the two coordinates to find corresponding weights for
1321+
idx = [j for j in range(3) if j != max_coord]
1322+
array = np.array([v1[idx[0]], v1[idx[1]]])
1323+
array2 = np.array([v2[idx[0]], v2[idx[1]]])
12971324

1298-
# Select the two coordinates to find corresponding weights for
1299-
idx = [j for j in range(3) if j != max_coord]
1300-
array = np.array([v1[idx[0]], v1[idx[1]]])
1301-
array2 = np.array([v2[idx[0]], v2[idx[1]]])
1325+
A = np.column_stack((array, array2)) # 2x2 matrix
1326+
b = np.array([vp[idx[0]], vp[idx[1]]]) # Right-hand side vector
13021327

1303-
A = np.column_stack((array, array2)) # 2x2 matrix
1304-
b = np.array([vp[idx[0]], vp[idx[1]]]) # Right-hand side vector
1328+
# Solve for weights b, c
1329+
b_c = np.linalg.solve(A, b)
13051330

1306-
# Solve for weights b, c
1307-
b_c = np.linalg.solve(A, b)
1331+
# Solve for a
1332+
a = 1 - b_c[0] - b_c[1]
13081333

1309-
# Solve for a
1310-
a = 1 - b_c[0] - b_c[1]
1334+
# Compute triangle area
1335+
triangle_area = 0.5 * np.linalg.norm(np.cross(v1, v2))
13111336

1312-
# Compute triangle area
1313-
triangle_area = 0.5 * np.linalg.norm(np.cross(v1, v2))
1314-
total_area += triangle_area
1337+
# Calculate weights
1338+
weights[0] += a * triangle_area
1339+
weights[1] += b_c[0] * triangle_area
1340+
weights[2] += b_c[1] * triangle_area
13151341

1316-
# Accumulate weights
1317-
weights[0] += a * triangle_area
1318-
weights[i] += b_c[0] * triangle_area
1319-
weights[i + 1] += b_c[1] * triangle_area
1342+
# Normalize weights so they sum to 1
1343+
if triangle_area > 0:
1344+
weights /= triangle_area
13201345

1321-
# Normalize weights so they sum to 1
1322-
if total_area > 0:
1323-
weights /= total_area
1346+
# The nodes of the triangle, so it can be known which values to apply the weights to
1347+
nodes = np.array([0, i, i + 1])
1348+
return weights, nodes
13241349

1325-
return weights
1350+
# If the point doesn't reside in the polygon, raise an error
1351+
raise ValueError("Point does not reside in polygon")

uxarray/remap/bilinear.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
from uxarray.core.dataset import UxDataset
1010

1111
import numpy as np
12+
from numba import njit
1213

1314
import uxarray.core.dataarray
1415
import uxarray.core.dataset
15-
from uxarray.constants import INT_FILL_VALUE
16+
from uxarray.constants import ERROR_TOLERANCE, INT_FILL_VALUE
1617
from uxarray.grid import Grid
1718

1819

@@ -239,7 +240,33 @@ def _get_values(point_xyz, point_lonlat, dual, source_data, data_size, source_gr
239240
# Create the data array on the polygon
240241
data[ind] = source_data[node]
241242

242-
weights = barycentric_coordinates_cartesian(polygon, point_xyz[i])
243-
values[i] = np.sum(weights * data, axis=-1)
243+
# Check to see if the point lies on a node of the polygon
244+
matching_indices = _find_matching_node_index(
245+
polygon, point_xyz[i], tolerance=ERROR_TOLERANCE
246+
)
247+
248+
# If the point lies on a node, assign it a weight of 1 and return the node index
249+
if matching_indices[0] != -1:
250+
weights, nodes = np.array([1]), matching_indices
251+
else:
252+
weights, nodes = barycentric_coordinates_cartesian(
253+
polygon, point_xyz[i]
254+
)
255+
256+
values[i] = np.sum(weights * data[nodes], axis=-1)
244257

245258
return values
259+
260+
261+
@njit(cache=True)
262+
def _find_matching_node_index(nodes, point, tolerance=ERROR_TOLERANCE):
263+
for i in range(nodes.shape[0]):
264+
match = True
265+
for j in range(3): # Compare each coordinate
266+
diff = abs(nodes[i, j] - point[j])
267+
if diff > tolerance + tolerance * abs(point[j]):
268+
match = False
269+
break
270+
if match:
271+
return np.array([i]) # Return first matching index
272+
return np.array([-1]) # Not found

0 commit comments

Comments
 (0)