From 0f36fd8bce5fea0d594526d267a0c5f38fb61b4f Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 29 Apr 2025 09:02:58 -0400 Subject: [PATCH 1/4] Rename module to structured.generic --- .../_datasets/structured/{grid_datasets.py => generic.py} | 0 tests/v4/test_field.py | 5 +---- tests/v4/test_gridadapter.py | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) rename parcels/_datasets/structured/{grid_datasets.py => generic.py} (100%) diff --git a/parcels/_datasets/structured/grid_datasets.py b/parcels/_datasets/structured/generic.py similarity index 100% rename from parcels/_datasets/structured/grid_datasets.py rename to parcels/_datasets/structured/generic.py diff --git a/tests/v4/test_field.py b/tests/v4/test_field.py index be0f0b87e4..dd99b37537 100644 --- a/tests/v4/test_field.py +++ b/tests/v4/test_field.py @@ -3,7 +3,7 @@ import xarray as xr from parcels import Field -from parcels._datasets.structured.grid_datasets import datasets as structured_datasets +from parcels._datasets.structured.generic import datasets as structured_datasets from parcels.v4.grid import Grid @@ -36,9 +36,6 @@ def test_field_init_param_types(): xr.DataArray(), ux.UxDataArray().uxgrid, id="xarray-uxgrid", - marks=pytest.mark.xfail( - reason="Replace uxDataArray object with one that actually has a grid (once unstructured example datasets are in the codebase)." - ), ), ], ) diff --git a/tests/v4/test_gridadapter.py b/tests/v4/test_gridadapter.py index 9f1ead0ac7..8111764b3b 100644 --- a/tests/v4/test_gridadapter.py +++ b/tests/v4/test_gridadapter.py @@ -4,7 +4,7 @@ import pytest from numpy.testing import assert_allclose -from parcels._datasets.structured.grid_datasets import N, T, datasets +from parcels._datasets.structured.generic import N, T, datasets from parcels.grid import Grid as OldGrid from parcels.tools.converters import TimeConverter from parcels.v4.grid import Grid as NewGrid From 96361c35d7eda2abc7e3820d5469faa348f4ad7f Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 29 Apr 2025 09:06:32 -0400 Subject: [PATCH 2/4] Add stommel gyre unstructured grid dataset --- parcels/_datasets/unstructured/generic.py | 120 ++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 parcels/_datasets/unstructured/generic.py diff --git a/parcels/_datasets/unstructured/generic.py b/parcels/_datasets/unstructured/generic.py new file mode 100644 index 0000000000..c7a914c3ab --- /dev/null +++ b/parcels/_datasets/unstructured/generic.py @@ -0,0 +1,120 @@ + +import uxarray as ux +import numpy as np +import pandas as pd + +import math + +__all__ = ["datasets", "Nx"] + +Nx = 20 +vmax = 1.0 +delta = 0.1 + +def _stommel_gyre_delaunay(): + lon, lat = np.meshgrid(np.linspace(0, 60.0, Nx, dtype=np.float32), + np.linspace(0, 60.0, Nx, dtype=np.float32)) + lon_flat = lon.ravel() + lat_flat = lat.ravel() + + # mask any point on one of the boundaries + mask = ( + np.isclose(lon_flat, 0.0) | + np.isclose(lon_flat, 60.0) | + np.isclose(lat_flat, 0.0) | + np.isclose(lat_flat, 60.0) + ) + + boundary_points = np.flatnonzero(mask) + + uxgrid = ux.Grid.from_points( + (lon_flat, lat_flat), + method="regional_delaunay", + boundary_points=boundary_points, + ) + + # Define arrays U (zonal), V (meridional) and P (sea surface height) + U = np.zeros((1, 1, lat.size), dtype=np.float64) + V = np.zeros((1, 1, lat.size), dtype=np.float64) + P = np.zeros((1, 1, lat.size), dtype=np.float64) + + for i, (x, y) in enumerate(zip(lon_flat, lat_flat)): + xi = x / 60.0 + yi = y / 60.0 + + P[0, 0, i] = ( + -vmax*delta + * (1 - xi) + * (math.exp(-xi / delta) - 1) + * np.sin(math.pi * yi) + ) + U[0, 0, i] = ( + -vmax + * (1 - math.exp(-xi / delta) - xi) + * np.cos(math.pi * yi) + ) + V[0, 0, i] = ( + vmax + * ((2.0 - xi)*math.exp(-xi / delta) - 1) + * np.sin(math.pi * yi) + ) + + + u = ux.UxDataArray( + data=U, + name="U", + uxgrid=uxgrid, + dims=["time", "nz1", "n_node"], + coords=dict( + time=(["time"], pd.to_datetime(["2000-01-01"])), + nz1=(["nz1"], [0]), + ), + attrs=dict( + description="zonal velocity", + units="m/s", + location="node", + mesh="delaunay", + Conventions="UGRID-1.0" + ), + ) + v = ux.UxDataArray( + data=V, + name="V", + uxgrid=uxgrid, + dims=["time", "nz1", "n_node"], + coords=dict( + time=(["time"], pd.to_datetime(["2000-01-01"])), + nz1=(["nz1"], [0]), + ), + attrs=dict( + description="meridional velocity", + units="m/s", + location="node", + mesh="delaunay", + Conventions="UGRID-1.0" + ), + ) + p = ux.UxDataArray( + data=P, + name="p", + uxgrid=uxgrid, + dims=["time", "nz1", "n_node"], + coords=dict( + time=(["time"], pd.to_datetime(["2000-01-01"])), + nz1=(["nz1"], [0]), + ), + attrs=dict( + description="pressure", + units="N/m^2", + location="node", + mesh="delaunay", + Conventions="UGRID-1.0" + ), + ) + + return ux.UxDataset({"U": u, "V": v, "p": p}, uxgrid=uxgrid) + + +datasets = { + "stommel_gyre_delaunay": _stommel_gyre_delaunay(), +} \ No newline at end of file From cb492b954f3afbf79c7ae63b4db8954468af4c94 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 29 Apr 2025 09:10:18 -0400 Subject: [PATCH 3/4] Update test_field_incompatible_combination --- tests/v4/test_field.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/v4/test_field.py b/tests/v4/test_field.py index dd99b37537..912b1e66a2 100644 --- a/tests/v4/test_field.py +++ b/tests/v4/test_field.py @@ -5,6 +5,7 @@ from parcels import Field from parcels._datasets.structured.generic import datasets as structured_datasets from parcels.v4.grid import Grid +from parcels._datasets.unstructured.generic import datasets as unstructured_datasets def test_field_init_param_types(): @@ -34,7 +35,7 @@ def test_field_init_param_types(): pytest.param(ux.UxDataArray(), Grid(xr.Dataset()), id="uxdata-grid"), pytest.param( xr.DataArray(), - ux.UxDataArray().uxgrid, + unstructured_datasets["stommel_gyre_delaunay"].uxgrid, id="xarray-uxgrid", ), ], From c3f9f02a84ca77e589f1813ba47e5478da6d6a2f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:12:36 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- parcels/_datasets/unstructured/generic.py | 62 ++++++----------------- tests/v4/test_field.py | 2 +- 2 files changed, 16 insertions(+), 48 deletions(-) diff --git a/parcels/_datasets/unstructured/generic.py b/parcels/_datasets/unstructured/generic.py index c7a914c3ab..3870a9a341 100644 --- a/parcels/_datasets/unstructured/generic.py +++ b/parcels/_datasets/unstructured/generic.py @@ -1,28 +1,24 @@ +import math -import uxarray as ux import numpy as np import pandas as pd +import uxarray as ux -import math - -__all__ = ["datasets", "Nx"] +__all__ = ["Nx", "datasets"] Nx = 20 vmax = 1.0 delta = 0.1 + def _stommel_gyre_delaunay(): - lon, lat = np.meshgrid(np.linspace(0, 60.0, Nx, dtype=np.float32), - np.linspace(0, 60.0, Nx, dtype=np.float32)) + lon, lat = np.meshgrid(np.linspace(0, 60.0, Nx, dtype=np.float32), np.linspace(0, 60.0, Nx, dtype=np.float32)) lon_flat = lon.ravel() lat_flat = lat.ravel() # mask any point on one of the boundaries mask = ( - np.isclose(lon_flat, 0.0) | - np.isclose(lon_flat, 60.0) | - np.isclose(lat_flat, 0.0) | - np.isclose(lat_flat, 60.0) + np.isclose(lon_flat, 0.0) | np.isclose(lon_flat, 60.0) | np.isclose(lat_flat, 0.0) | np.isclose(lat_flat, 60.0) ) boundary_points = np.flatnonzero(mask) @@ -32,33 +28,19 @@ def _stommel_gyre_delaunay(): method="regional_delaunay", boundary_points=boundary_points, ) - + # Define arrays U (zonal), V (meridional) and P (sea surface height) U = np.zeros((1, 1, lat.size), dtype=np.float64) V = np.zeros((1, 1, lat.size), dtype=np.float64) P = np.zeros((1, 1, lat.size), dtype=np.float64) - for i, (x, y) in enumerate(zip(lon_flat, lat_flat)): + for i, (x, y) in enumerate(zip(lon_flat, lat_flat, strict=False)): xi = x / 60.0 yi = y / 60.0 - - P[0, 0, i] = ( - -vmax*delta - * (1 - xi) - * (math.exp(-xi / delta) - 1) - * np.sin(math.pi * yi) - ) - U[0, 0, i] = ( - -vmax - * (1 - math.exp(-xi / delta) - xi) - * np.cos(math.pi * yi) - ) - V[0, 0, i] = ( - vmax - * ((2.0 - xi)*math.exp(-xi / delta) - 1) - * np.sin(math.pi * yi) - ) + P[0, 0, i] = -vmax * delta * (1 - xi) * (math.exp(-xi / delta) - 1) * np.sin(math.pi * yi) + U[0, 0, i] = -vmax * (1 - math.exp(-xi / delta) - xi) * np.cos(math.pi * yi) + V[0, 0, i] = vmax * ((2.0 - xi) * math.exp(-xi / delta) - 1) * np.sin(math.pi * yi) u = ux.UxDataArray( data=U, @@ -70,11 +52,7 @@ def _stommel_gyre_delaunay(): nz1=(["nz1"], [0]), ), attrs=dict( - description="zonal velocity", - units="m/s", - location="node", - mesh="delaunay", - Conventions="UGRID-1.0" + description="zonal velocity", units="m/s", location="node", mesh="delaunay", Conventions="UGRID-1.0" ), ) v = ux.UxDataArray( @@ -87,11 +65,7 @@ def _stommel_gyre_delaunay(): nz1=(["nz1"], [0]), ), attrs=dict( - description="meridional velocity", - units="m/s", - location="node", - mesh="delaunay", - Conventions="UGRID-1.0" + description="meridional velocity", units="m/s", location="node", mesh="delaunay", Conventions="UGRID-1.0" ), ) p = ux.UxDataArray( @@ -103,13 +77,7 @@ def _stommel_gyre_delaunay(): time=(["time"], pd.to_datetime(["2000-01-01"])), nz1=(["nz1"], [0]), ), - attrs=dict( - description="pressure", - units="N/m^2", - location="node", - mesh="delaunay", - Conventions="UGRID-1.0" - ), + attrs=dict(description="pressure", units="N/m^2", location="node", mesh="delaunay", Conventions="UGRID-1.0"), ) return ux.UxDataset({"U": u, "V": v, "p": p}, uxgrid=uxgrid) @@ -117,4 +85,4 @@ def _stommel_gyre_delaunay(): datasets = { "stommel_gyre_delaunay": _stommel_gyre_delaunay(), -} \ No newline at end of file +} diff --git a/tests/v4/test_field.py b/tests/v4/test_field.py index 912b1e66a2..f0e0313009 100644 --- a/tests/v4/test_field.py +++ b/tests/v4/test_field.py @@ -4,8 +4,8 @@ from parcels import Field from parcels._datasets.structured.generic import datasets as structured_datasets -from parcels.v4.grid import Grid from parcels._datasets.unstructured.generic import datasets as unstructured_datasets +from parcels.v4.grid import Grid def test_field_init_param_types():