Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions test/test_remap.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,45 @@ def test_b_quadrilateral(gridpath, datasetpath):
out = uxds['var2'].remap.bilinear(destination_grid=dest)

assert out.size > 0

def test_b_coords_remap_to_faces(gridpath):
"""Bilinear remap should change the array when remap_to != source."""
mesh_path = gridpath("mpas", "QU", "mesh.QU.1920km.151026.nc")
uxds = ux.open_dataset(mesh_path, mesh_path)
dest = ux.open_grid(gridpath("ugrid", "geoflow-small", "grid.nc"))

uxda_with_coords = ux.core.UxDataArray(
data=uxds["latCell"],
uxgrid=uxds.uxgrid,
coords={"Mesh2_face_lat": uxds.uxgrid.face_lat,
"Mesh_Face_lon": uxds.uxgrid.face_lon,
}
)

da_remap_b = uxda_with_coords.remap.bilinear(
destination_grid=dest, remap_to="faces"
)

assert (da_remap_b.Mesh_Face_lon.size == dest.face_lon.size)
assert np.array_equal(da_remap_b.Mesh_Face_lon.values, dest.face_lon.values)

def test_b_coords_remap_to_nodes(gridpath):
"""Bilinear remap should change the array when remap_to != source."""
mesh_path = gridpath("mpas", "QU", "mesh.QU.1920km.151026.nc")
uxds = ux.open_dataset(mesh_path, mesh_path)
dest = ux.open_grid(gridpath("ugrid", "geoflow-small", "grid.nc"))

uxda_with_coords = ux.core.UxDataArray(
data=uxds["latCell"],
uxgrid=uxds.uxgrid,
coords={"Mesh2_face_lat": uxds.uxgrid.face_lat,
"Mesh_Face_lon": uxds.uxgrid.face_lon,
}
)

da_remap_b = uxda_with_coords.remap.bilinear(
destination_grid=dest, remap_to="nodes"
)

assert (da_remap_b.Mesh_Node_lon.size == dest.node_lon.size)
assert np.array_equal(da_remap_b.Mesh_Node_lon.values, dest.node_lon.values)
6 changes: 6 additions & 0 deletions uxarray/conventions/ugrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,14 @@
"standard_name": "x",
"long name": "Cartesian x location of the corner nodes of each face",
"units": "meters",
"axis": "X",
}

NODE_Y_ATTRS = {
"standard_name": "y",
"long name": "Cartesian y location of the corner nodes of each face",
"units": "meters",
"axis": "Y",
}

NODE_Z_ATTRS = {
Expand All @@ -104,12 +106,14 @@
"standard_name": "x",
"long name": "Cartesian x location of the center of each edge",
"units": "meters",
"axis": "X",
}

EDGE_Y_ATTRS = {
"standard_name": "y",
"long name": "Cartesian y location of the center of each edge",
"units": "meters",
"axis": "Y",
}

EDGE_Z_ATTRS = {
Expand All @@ -124,12 +128,14 @@
"standard_name": "x",
"long name": "Cartesian x location of the center of each face",
"units": "meters",
"axis": "X",
}

FACE_Y_ATTRS = {
"standard_name": "y",
"long name": "Cartesian y location of the center of each face",
"units": "meters",
"axis": "Y",
}

FACE_Z_ATTRS = {
Expand Down
18 changes: 9 additions & 9 deletions uxarray/remap/bilinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
def _bilinear(
source: UxDataArray | UxDataset,
destination_grid: Grid,
destination_dim: str = "n_face",
remap_to: str = "faces",
) -> np.ndarray:
"""Bilinear Remapping between two grids, mapping data that resides on the
corner nodes, edge centers, or face centers on the source grid to the
Expand All @@ -39,8 +39,8 @@ def _bilinear(
---------
source_uxda : UxDataArray
Source UxDataArray
remap_to : str, default="nodes"
Location of where to map data, either "nodes", "edge centers", or "face centers"
remap_to : str, default="faces"
Which grid element receives the remapped values, either "nodes", "edges", or "faces"

Returns
-------
Expand All @@ -49,7 +49,7 @@ def _bilinear(
"""

# ensure array is a np.ndarray
_assert_dimension(destination_dim)
_assert_dimension(remap_to)

# Ensure the destination grid is normalized
destination_grid.normalize_cartesian_coordinates()
Expand All @@ -70,12 +70,12 @@ def _bilinear(
dual = source.uxgrid.get_dual()

# get destination coordinate pairs
point_xyz = _prepare_points(destination_grid, destination_dim)
point_xyz = _prepare_points(destination_grid, remap_to)

weights, indices = _barycentric_weights(
point_xyz=point_xyz,
dual=dual,
data_size=getattr(destination_grid, f"n_{KDTREE_DIM_MAP[destination_dim]}"),
data_size=getattr(destination_grid, f"n_{KDTREE_DIM_MAP[remap_to]}"),
source_grid=ds.uxgrid,
)

Expand All @@ -87,8 +87,8 @@ def _bilinear(
inds, w = indices, weights

# pack indices & weights into tiny DataArrays:
indexer = xr.DataArray(inds, dims=[LABEL_TO_COORD[destination_dim], "k"])
weight_da = xr.DataArray(w, dims=[LABEL_TO_COORD[destination_dim], "k"])
indexer = xr.DataArray(inds, dims=[LABEL_TO_COORD[remap_to], "k"])
weight_da = xr.DataArray(w, dims=[LABEL_TO_COORD[remap_to], "k"])

# gather the k neighbor values:
da_k = da.isel({source_dim: indexer}, ignore_grid=True)
Expand All @@ -103,7 +103,7 @@ def _bilinear(
remapped_vars[name] = da

ds_remapped = _construct_remapped_ds(
source, remapped_vars, destination_grid, destination_dim
source, remapped_vars, destination_grid, remap_to
)

return ds_remapped[name] if is_da else ds_remapped
Expand Down
18 changes: 9 additions & 9 deletions uxarray/remap/inverse_distance_weighted.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def _idw_weights(distances, power):
def _inverse_distance_weighted_remap(
source: UxDataArray | UxDataset,
destination_grid: Grid,
destination_dim: str = "n_face",
remap_to: str = "faces",
power: int = 2,
k: int = 8,
):
Expand All @@ -68,8 +68,8 @@ def _inverse_distance_weighted_remap(
The data to be remapped.
destination_grid : Grid
The UXarray grid instance on which to interpolate data.
destination_dim : {'n_node', 'n_edge', 'n_face'}, default='n_face'
The spatial dimension on `destination_grid` to receive interpolated values.
remap_to : {'nodes', 'edges', 'faces'}, default='faces'
Which grid element receives the remapped values, either "nodes", "edges", or "faces"
power : int, default=2
Exponent in the inverse-distance weighting function. Larger values
emphasize closer neighbors.
Expand All @@ -88,9 +88,9 @@ def _inverse_distance_weighted_remap(
"""
# Fall back onto nearest neighbor
if k == 1:
return _nearest_neighbor_remap(source, destination_grid, destination_dim)
return _nearest_neighbor_remap(source, destination_grid, remap_to)

_assert_dimension(destination_dim)
_assert_dimension(remap_to)

# Perform remapping on a UxDataset
ds, is_da, name = _to_dataset(source)
Expand All @@ -106,7 +106,7 @@ def _inverse_distance_weighted_remap(
ds.uxgrid,
destination_grid,
src_dim,
destination_dim,
remap_to,
k=k,
return_distances=True,
)
Expand All @@ -123,8 +123,8 @@ def _inverse_distance_weighted_remap(
inds, w = indices_weights_map[source_dim]

# pack indices & weights into tiny DataArrays:
indexer = xr.DataArray(inds, dims=[LABEL_TO_COORD[destination_dim], "k"])
weight_da = xr.DataArray(w, dims=[LABEL_TO_COORD[destination_dim], "k"])
indexer = xr.DataArray(inds, dims=[LABEL_TO_COORD[remap_to], "k"])
weight_da = xr.DataArray(w, dims=[LABEL_TO_COORD[remap_to], "k"])

# gather the k neighbor values:
da_k = da.isel({source_dim: indexer}, ignore_grid=True)
Expand All @@ -139,7 +139,7 @@ def _inverse_distance_weighted_remap(
remapped_vars[name] = da

ds_remapped = _construct_remapped_ds(
source, remapped_vars, destination_grid, destination_dim
source, remapped_vars, destination_grid, remap_to
)

return ds_remapped[name] if is_da else ds_remapped
16 changes: 7 additions & 9 deletions uxarray/remap/nearest_neighbor.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def _nearest_neighbor_query(
def _nearest_neighbor_remap(
source: UxDataArray | UxDataset,
destination_grid: Grid,
destination_dim: str = "n_face",
remap_to: str = "faces",
):
"""
Apply nearest-neighbor remapping from a UXarray object onto another grid.
Expand All @@ -88,15 +88,15 @@ def _nearest_neighbor_remap(
The data array or dataset to be remapped.
destination_grid : Grid
The UXarray Grid instance to which data will be remapped.
destination_dim : str, default='n_face'
The spatial dimension on the destination grid ('n_node', 'n_edge', 'n_face').
remap_to : str, default='faces'
Which grid element receives the remapped values, either 'nodes', 'edges', 'faces').

Returns
-------
UxDataArray or UxDataset
A new UXarray object with data values assigned to `destination_grid`.
"""
_assert_dimension(destination_dim)
_assert_dimension(remap_to)

# Perform remapping on a UxDataset
ds, is_da, name = _to_dataset(source)
Expand All @@ -106,9 +106,7 @@ def _nearest_neighbor_remap(

# Build Nearest Neighbor Index Arrays
indices_map: dict[str, np.ndarray] = {
src_dim: _nearest_neighbor_query(
ds.uxgrid, destination_grid, src_dim, destination_dim
)
src_dim: _nearest_neighbor_query(ds.uxgrid, destination_grid, src_dim, remap_to)
for src_dim in dims_to_remap
}
remapped_vars = {}
Expand All @@ -122,7 +120,7 @@ def _nearest_neighbor_remap(
indexer = xr.DataArray(
indices,
dims=[
LABEL_TO_COORD[destination_dim],
LABEL_TO_COORD[remap_to],
],
)

Expand All @@ -134,7 +132,7 @@ def _nearest_neighbor_remap(
remapped_vars[name] = da

ds_remapped = _construct_remapped_ds(
source, remapped_vars, destination_grid, destination_dim
source, remapped_vars, destination_grid, remap_to
)

return ds_remapped[name] if is_da else ds_remapped
Loading
Loading