From 5ecda193b1e51017c5a8bfeecf49a31e80627798 Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Wed, 27 May 2026 15:07:32 +0200 Subject: [PATCH 01/11] Remove XGrid.from_dataset --- src/parcels/_core/xgrid.py | 12 ------------ tests/test_field.py | 39 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/src/parcels/_core/xgrid.py b/src/parcels/_core/xgrid.py index 62234150b..d925cb03b 100644 --- a/src/parcels/_core/xgrid.py +++ b/src/parcels/_core/xgrid.py @@ -133,18 +133,6 @@ def __init__(self, grid: xgcm.Grid, mesh): ptyping.assert_valid_mesh(mesh) self._ds = ds - @classmethod - def from_dataset(cls, ds: xr.Dataset, mesh, xgcm_kwargs=None): - """WARNING: unstable API, subject to change in future versions.""" # TODO v4: make private or remove warning on v4 release - if xgcm_kwargs is None: - xgcm_kwargs = {} - - xgcm_kwargs = {**_DEFAULT_XGCM_KWARGS, **xgcm_kwargs} - - ds = _drop_field_data(ds) - grid = xgcm.Grid(ds, **xgcm_kwargs) - return cls(grid, mesh=mesh) - def __repr__(self): return xgrid_repr(self) diff --git a/tests/test_field.py b/tests/test_field.py index cc264beae..3b8a08a76 100644 --- a/tests/test_field.py +++ b/tests/test_field.py @@ -46,25 +46,26 @@ def test_field_init_param_types(): Field(name="test", data=data["data_g"], grid=123, interp_method=XLinear) -@pytest.mark.parametrize( - "data,grid", - [ - pytest.param( - ux.UxDataArray(), - XGrid.from_dataset(datasets_structured["ds_2d_left"], mesh="flat"), - id="uxdata-grid", - ), - pytest.param( - xr.DataArray(), - UxGrid( - datasets_unstructured["stommel_gyre_delaunay"].uxgrid, - z=datasets_unstructured["stommel_gyre_delaunay"].coords["zf"], - mesh="flat", - ), - id="xarray-uxgrid", - ), - ], -) +# @pytest.mark.parametrize( +# "data,grid", +# [ +# pytest.param( +# ux.UxDataArray(), +# XGrid.from_dataset(datasets_structured["ds_2d_left"], mesh="flat"), +# id="uxdata-grid", +# ), +# pytest.param( +# xr.DataArray(), +# UxGrid( +# datasets_unstructured["stommel_gyre_delaunay"].uxgrid, +# z=datasets_unstructured["stommel_gyre_delaunay"].coords["zf"], +# mesh="flat", +# ), +# id="xarray-uxgrid", +# ), +# ], +# ) +@pytest.mark.skip("Likely not relevant after refactoring from https://github.com/Parcels-code/Parcels/pull/2646") # TODO: Remove or replace def test_field_incompatible_combination(data, grid): with pytest.raises(ValueError, match="Incompatible data-grid combination."): Field( From d94a3c338ade0d0a626161ec8be353dd6d03eb1f Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Wed, 27 May 2026 15:16:35 +0200 Subject: [PATCH 02/11] Fixing test cases --- tests/test_field.py | 52 +++++++++++++++++++++++++-------------------- tests/test_xgrid.py | 3 ++- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/tests/test_field.py b/tests/test_field.py index 3b8a08a76..4f8ff9e0d 100644 --- a/tests/test_field.py +++ b/tests/test_field.py @@ -2,8 +2,6 @@ import numpy as np import pytest -import uxarray as ux -import xarray as xr from parcels import Field, UxGrid, VectorField, XGrid from parcels._datasets.structured.generic import T as T_structured @@ -65,7 +63,9 @@ def test_field_init_param_types(): # ), # ], # ) -@pytest.mark.skip("Likely not relevant after refactoring from https://github.com/Parcels-code/Parcels/pull/2646") # TODO: Remove or replace +@pytest.mark.skip( + "Likely not relevant after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_field_incompatible_combination(data, grid): with pytest.raises(ValueError, match="Incompatible data-grid combination."): Field( @@ -76,16 +76,19 @@ def test_field_incompatible_combination(data, grid): ) -@pytest.mark.parametrize( - "data,grid", - [ - pytest.param( - datasets_structured["ds_2d_left"]["data_g"], - XGrid.from_dataset(datasets_structured["ds_2d_left"], mesh="flat"), - id="ds_2d_left", - ), # TODO: Perhaps this test should be expanded to cover more datasets? - ], -) +# @pytest.mark.parametrize( +# "data,grid", +# [ +# pytest.param( +# datasets_structured["ds_2d_left"]["data_g"], +# XGrid.from_dataset(datasets_structured["ds_2d_left"], mesh="flat"), +# id="ds_2d_left", +# ), # TODO: Perhaps this test should be expanded to cover more datasets? +# ], +# ) +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_field_init_structured_grid(data, grid): """Test creating a field.""" field = Field( @@ -125,16 +128,19 @@ def test_field_init_fail_on_float_time_dim(): ) -@pytest.mark.parametrize( - "data,grid", - [ - pytest.param( - datasets_structured["ds_2d_left"]["data_g"], - XGrid.from_dataset(datasets_structured["ds_2d_left"], mesh="flat"), - id="ds_2d_left", - ), - ], -) +# @pytest.mark.parametrize( +# "data,grid", +# [ +# pytest.param( +# datasets_structured["ds_2d_left"]["data_g"], +# XGrid.from_dataset(datasets_structured["ds_2d_left"], mesh="flat"), +# id="ds_2d_left", +# ), +# ], +# ) +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_field_time_interval(data, grid): """Test creating a field.""" field = Field(name="test_field", data=data, grid=grid, interp_method=XLinear) diff --git a/tests/test_xgrid.py b/tests/test_xgrid.py index 53b0124e8..2d40d8250 100644 --- a/tests/test_xgrid.py +++ b/tests/test_xgrid.py @@ -207,7 +207,8 @@ def test_time1D_field(): ], ) # for key, ds in datasets.items()]) def test_xgrid_search_cpoints(ds): - grid = XGrid.from_dataset(ds, mesh="flat") + fieldset = FieldSet.from_sgrid_conventions(ds, mesh="flat") + grid = fieldset.U_A_grid.grid lat_array, lon_array = get_2d_fpoint_mesh(grid) lat_array, lon_array = corner_to_cell_center_points(lat_array, lon_array) From b3816822de52bc7f1d15b2a80481567ee2b53328 Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Wed, 27 May 2026 15:33:02 +0200 Subject: [PATCH 03/11] Fix test_vertical1D_field --- tests/test_xgrid.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/test_xgrid.py b/tests/test_xgrid.py index 2d40d8250..17daa3578 100644 --- a/tests/test_xgrid.py +++ b/tests/test_xgrid.py @@ -174,16 +174,17 @@ def test_dim_with_duplicate_axis(): FieldSet.from_sgrid_conventions(ds) -def test_vertical1D_field(): - nz = 11 - ds = xr.Dataset( - {"z1d": (["depth"], np.linspace(0, 10, nz))}, - coords={"depth": (["depth"], np.linspace(0, 1, nz), {"axis": "Z"})}, - ) - grid = XGrid.from_dataset(ds, mesh="flat") - field = Field("z1d", ds["z1d"], grid, XLinear) +@pytest.mark.parametrize("ds", [datasets["ds_2d_left"]]) +def test_vertical1D_field(ds): + ds = ds.drop(set(ds.data_vars) - {"grid"}) + ds["depth"] = (["ZG"], np.linspace(0, 1, ds["depth"].size), {"axis": "Z"}) + ds["z1d"] = xr.DataArray(np.linspace(0, 10, ds["depth"].size), dims=("ZG",)) + ds = ds.reset_coords("z1d") + + fieldset = FieldSet.from_sgrid_conventions(ds, mesh="flat") + field = fieldset.z1d - assert field.eval(np.timedelta64(0, "s"), 0.45, 0, 0) == 4.5 + np.testing.assert_almost_equal(field.eval(np.timedelta64(0, "s"), 0.45, 0, 0), np.array([4.5])) def test_time1D_field(): From 139d916861d24b87da25ad767e356affb11dea7a Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Thu, 28 May 2026 15:10:15 +0200 Subject: [PATCH 04/11] Create global fixture `fieldset` --- tests/conftest.py | 16 ++++++++++++++++ tests/test_fieldset.py | 17 ++--------------- tests/test_kernel.py | 14 -------------- tests/test_particlefile.py | 19 +------------------ tests/test_particleset.py | 14 -------------- tests/test_particleset_execute.py | 10 ---------- tests/test_particlesetview.py | 15 +-------------- 7 files changed, 20 insertions(+), 85 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 2c7878e01..71a3740e7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,8 @@ import pytest +from parcels import FieldSet +from parcels._datasets.structured.generic import datasets as datasets_structured + SKIP_BY_DEFAULT = {"validation", "flaky"} @@ -17,3 +20,16 @@ def pytest_collection_modifyitems(config, items): @pytest.fixture def tmp_parquet(tmp_path): return tmp_path / "tmp.parquet" + + +@pytest.fixture +def fieldset() -> FieldSet: + """FieldSet with U and V""" + ds = datasets_structured["ds_2d_left"].copy() + ds = ds[["U_A_grid", "V_A_grid", "grid"]].rename( + { + "U_A_grid": "U", + "V_A_grid": "V", + } + ) + return FieldSet.from_sgrid_conventions(ds, mesh="flat") diff --git a/tests/test_fieldset.py b/tests/test_fieldset.py index abcb08df4..887999e39 100644 --- a/tests/test_fieldset.py +++ b/tests/test_fieldset.py @@ -7,31 +7,18 @@ import pytest import xarray as xr -from parcels import Field, ParticleFile, ParticleSet, VectorField, XGrid, convert +from parcels import Field, ParticleFile, ParticleSet, XGrid, convert from parcels._core.fieldset import CalendarError, FieldSet, _datetime_to_msg from parcels._datasets.structured.generic import T as T_structured from parcels._datasets.structured.generic import datasets as datasets_structured from parcels._datasets.structured.generic import datasets_sgrid from parcels._datasets.unstructured.generic import datasets as datasets_unstructured -from parcels.interpolators import XLinear, XLinear_Velocity +from parcels.interpolators import XLinear from tests import utils ds = datasets_structured["ds_2d_left"] -@pytest.fixture -def fieldset() -> FieldSet: - """Fixture to create a FieldSet object for testing.""" - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U_A_grid"], grid, interp_method=XLinear) - V = Field("V", ds["V_A_grid"], grid, interp_method=XLinear) - UV = VectorField("UV", U, V, vector_interp_method=XLinear_Velocity) - - return FieldSet( - [U, V, UV], - ) - - def test_fieldset_init_wrong_types(): with pytest.raises(ValueError, match="Expected `field` to be a Field or VectorField object. Got .*"): FieldSet([1.0, 2.0, 3.0]) diff --git a/tests/test_kernel.py b/tests/test_kernel.py index b4a3de922..5a9ac0b31 100644 --- a/tests/test_kernel.py +++ b/tests/test_kernel.py @@ -2,27 +2,13 @@ import pytest from parcels import ( - Field, - FieldSet, ParticleSet, - XGrid, ) from parcels._core.kernel import Kernel -from parcels._datasets.structured.generic import datasets as datasets_structured -from parcels.interpolators import XLinear from parcels.kernels import AdvectionRK4, AdvectionRK45 from tests.common_kernels import MoveEast, MoveNorth -@pytest.fixture -def fieldset() -> FieldSet: - ds = datasets_structured["ds_2d_left"] - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U_A_grid"], grid, interp_method=XLinear) - V = Field("V", ds["V_A_grid"], grid, interp_method=XLinear) - return FieldSet([U, V]) - - def test_unknown_var_in_kernel(fieldset): pset = ParticleSet(fieldset, lon=[0.5], lat=[0.5]) diff --git a/tests/test_particlefile.py b/tests/test_particlefile.py index 6aa5a90ca..0aa6e8b8b 100755 --- a/tests/test_particlefile.py +++ b/tests/test_particlefile.py @@ -19,34 +19,17 @@ ParticleSetWarning, StatusCode, Variable, - VectorField, - XGrid, ) from parcels._core.particle import Particle, get_default_particle from parcels._core.particlefile import _get_schema from parcels._core.utils.time import TimeInterval, timedelta_to_float from parcels._datasets.structured.generated import peninsula_dataset -from parcels._datasets.structured.generic import datasets from parcels.convert import copernicusmarine_to_sgrid -from parcels.interpolators import XLinear, XLinear_Velocity +from parcels.interpolators import XLinear from parcels.kernels import AdvectionRK4 from tests.common_kernels import DoNothing -@pytest.fixture -def fieldset() -> FieldSet: # TODO v4: Move into a `conftest.py` file and remove duplicates - """Fixture to create a FieldSet object for testing.""" - ds = datasets["ds_2d_left"] - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U_A_grid"], grid, XLinear) - V = Field("V", ds["V_A_grid"], grid, XLinear) - UV = VectorField("UV", U, V, vector_interp_method=XLinear_Velocity) - - return FieldSet( - [U, V, UV], - ) - - def test_metadata(fieldset, tmp_parquet): pset = ParticleSet(fieldset, pclass=Particle, lon=0, lat=0) diff --git a/tests/test_particleset.py b/tests/test_particleset.py index 5c66341b5..f288f9654 100644 --- a/tests/test_particleset.py +++ b/tests/test_particleset.py @@ -7,29 +7,15 @@ import xarray as xr from parcels import ( - Field, - FieldSet, Particle, ParticleSet, ParticleSetWarning, Variable, - XGrid, ) -from parcels._datasets.structured.generic import datasets as datasets_structured -from parcels.interpolators import XLinear from tests.common_kernels import DoNothing from tests.utils import round_and_hash_float_array -@pytest.fixture -def fieldset() -> FieldSet: - ds = datasets_structured["ds_2d_left"] - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U_A_grid"], grid, interp_method=XLinear) - V = Field("V", ds["V_A_grid"], grid, interp_method=XLinear) - return FieldSet([U, V]) - - def test_pset_create_lon_lat(fieldset): npart = 100 lon = np.linspace(0, 1, npart, dtype=np.float32) diff --git a/tests/test_particleset_execute.py b/tests/test_particleset_execute.py index 87a6fabd1..2fe23d74a 100644 --- a/tests/test_particleset_execute.py +++ b/tests/test_particleset_execute.py @@ -35,16 +35,6 @@ from tests.utils import DEFAULT_PARTICLES -@pytest.fixture -def fieldset() -> FieldSet: - ds = datasets_structured["ds_2d_left"] - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U_A_grid"], grid, interp_method=XLinear) - V = Field("V", ds["V_A_grid"], grid, interp_method=XLinear) - UV = VectorField("UV", U, V, vector_interp_method=XLinear_Velocity) - return FieldSet([U, V, UV]) - - @pytest.fixture def fieldset_no_time_interval() -> FieldSet: # i.e., no time variation diff --git a/tests/test_particlesetview.py b/tests/test_particlesetview.py index 2f0532543..e7ad7ef2a 100644 --- a/tests/test_particlesetview.py +++ b/tests/test_particlesetview.py @@ -1,20 +1,7 @@ import numpy as np -import pytest -from parcels import Field, FieldSet, Particle, ParticleSet, Variable, VectorField, XGrid +from parcels import Particle, ParticleSet, Variable from parcels._core.statuscodes import StatusCode -from parcels._datasets.structured.generic import datasets as datasets_structured -from parcels.interpolators import XLinear, XLinear_Velocity - - -@pytest.fixture -def fieldset() -> FieldSet: - ds = datasets_structured["ds_2d_left"] - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U_A_grid"], grid, interp_method=XLinear) - V = Field("V", ds["V_A_grid"], grid, interp_method=XLinear) - UV = VectorField("UV", U, V, vector_interp_method=XLinear_Velocity) - return FieldSet([U, V, UV]) def test_execution_changing_particle_mask(fieldset): From 3007703c90df3aaf1c2f10e3ceaa11ca55e56d3c Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Thu, 28 May 2026 15:25:58 +0200 Subject: [PATCH 05/11] Fix test_interpolation.py --- tests/test_interpolation.py | 43 ++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/tests/test_interpolation.py b/tests/test_interpolation.py index b028b8f99..df6069e10 100644 --- a/tests/test_interpolation.py +++ b/tests/test_interpolation.py @@ -2,8 +2,8 @@ import pytest import xarray as xr +import parcels._sgrid as sgrid from parcels import ( - Field, FieldSet, Particle, ParticleFile, @@ -11,7 +11,6 @@ StatusCode, Variable, VectorField, - XGrid, ) from parcels._core.index_search import _search_time_index from parcels._datasets.structured.generated import simple_UV_dataset @@ -51,8 +50,24 @@ def field(): "x": (["x"], [0.5, 1.5, 2.5, 3.5], {"axis": "X"}), "y": (["y"], [0.5, 1.5, 2.5, 3.5], {"axis": "Y"}), }, + ).pipe( + sgrid._attach_sgrid_metadata, + sgrid.SGrid2DMetadata( + cf_role="grid_topology", + topology_dimension=2, + node_dimensions=("lon", "lat"), + face_dimensions=( + sgrid.FaceNodePadding("x", "lon", sgrid.Padding.LOW), + sgrid.FaceNodePadding("y", "lat", sgrid.Padding.LOW), + ), + node_coordinates=("lon", "lat"), + vertical_dimensions=(sgrid.FaceNodePadding("ZC", "depth", sgrid.Padding.HIGH),), + ), ) - return Field("U", ds["U"], XGrid.from_dataset(ds, mesh="flat"), interp_method=XLinear) + field = FieldSet.from_sgrid_conventions(ds, mesh="flat").U + assert field.interp_method == XLinear + + return field @pytest.mark.parametrize( @@ -218,13 +233,25 @@ def test_interp_regression_v3(interp_name): "lat": (["YG"], ds_input["lat"].values, {"axis": "Y", "c_grid_axis_shift": 0.5}), "lon": (["XG"], ds_input["lon"].values, {"axis": "X", "c_grid_axis_shift": -0.5}), }, + ).pipe( + sgrid._attach_sgrid_metadata, + sgrid.SGrid2DMetadata( + cf_role="grid_topology", + topology_dimension=2, + node_dimensions=("XG", "YG"), + face_dimensions=( + sgrid.FaceNodePadding("XC", "XG", sgrid.Padding.HIGH), + sgrid.FaceNodePadding("YC", "YG", sgrid.Padding.HIGH), + ), + node_coordinates=("lon", "lat"), + vertical_dimensions=(sgrid.FaceNodePadding("ZC", "depth", sgrid.Padding.HIGH),), + ), ) - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U"], grid, interp_method=interp_methods[interp_name]) - V = Field("V", ds["V"], grid, interp_method=interp_methods[interp_name]) - W = Field("W", ds["W"], grid, interp_method=interp_methods[interp_name]) - fieldset = FieldSet([U, V, W, VectorField("UVW", U, V, W)]) + fieldset = FieldSet.from_sgrid_conventions(ds, mesh="flat") + assert fieldset.U.interp_method == interp_methods[interp_name] + assert fieldset.V.interp_method == interp_methods[interp_name] + assert fieldset.W.interp_method == interp_methods[interp_name] x, y, z = np.meshgrid(np.linspace(0, 1, 7), np.linspace(0, 1, 13), np.linspace(0, 1, 5)) From 9026b392b588143a8993cb3a172cc7cbbef72964 Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Thu, 28 May 2026 15:49:04 +0200 Subject: [PATCH 06/11] Fix test_fieldset.py --- tests/test_fieldset.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/test_fieldset.py b/tests/test_fieldset.py index 887999e39..3663588fa 100644 --- a/tests/test_fieldset.py +++ b/tests/test_fieldset.py @@ -52,6 +52,9 @@ def test_fieldset_add_constant_field(fieldset): assert fieldset.test_constant_field[time, z, lat, lon] == 1.0 +@pytest.mark.skip( + "Likely not relevant after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_fieldset_add_field(fieldset): grid = XGrid.from_dataset(ds, mesh="flat") field = Field("test_field", ds["U_A_grid"], grid, interp_method=XLinear) @@ -59,12 +62,18 @@ def test_fieldset_add_field(fieldset): assert fieldset.test_field == field +@pytest.mark.skip( + "Likely not relevant after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_fieldset_add_field_wrong_type(fieldset): not_a_field = 1.0 with pytest.raises(ValueError, match="Expected `field` to be a Field or VectorField object. Got .*"): fieldset.add_field(not_a_field, "test_field") +@pytest.mark.skip( + "Likely not relevant after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_fieldset_add_field_already_exists(fieldset): grid = XGrid.from_dataset(ds, mesh="flat") field = Field("test_field", ds["U_A_grid"], grid, interp_method=XLinear) @@ -84,8 +93,7 @@ def test_fieldset_gridset(fieldset): def test_fieldset_no_UV(tmp_parquet): - grid = XGrid.from_dataset(ds, mesh="flat") - fieldset = FieldSet([Field("P", ds["U_A_grid"], grid, interp_method=XLinear)]) + fieldset = FieldSet.from_sgrid_conventions(ds[["U_A_grid", "grid"]].rename({"U_A_grid": "P"}), mesh="flat") def SampleP(particles, fieldset): particles.dlon += fieldset.P[particles] @@ -112,6 +120,9 @@ def test_fieldset_from_structured_generic_datasets(ds): def test_fieldset_gridset_multiple_grids(): ... +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_fieldset_time_interval(): grid1 = XGrid.from_dataset(ds, mesh="flat") field1 = Field("field1", ds["U_A_grid"], grid1, interp_method=XLinear) @@ -136,6 +147,9 @@ def test_fieldset_time_interval_constant_fields(): assert fieldset.time_interval is None +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_fieldset_init_incompatible_calendars(): ds1 = ds.copy() ds1["time"] = ( @@ -161,6 +175,9 @@ def test_fieldset_init_incompatible_calendars(): FieldSet([U, V, incompatible_calendar]) +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_fieldset_add_field_incompatible_calendars(fieldset): ds_test = ds.copy() ds_test["time"] = ( From f5626a0b0d71ea477965e7ffcaef8265f3ff79fe Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Thu, 28 May 2026 16:24:23 +0200 Subject: [PATCH 07/11] Fix test_xgrid.py --- tests/test_xgrid.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/tests/test_xgrid.py b/tests/test_xgrid.py index 17daa3578..17952e426 100644 --- a/tests/test_xgrid.py +++ b/tests/test_xgrid.py @@ -48,6 +48,9 @@ def assert_equal(actual, expected): assert_allclose(actual, expected) +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace @pytest.mark.parametrize("ds", [datasets["ds_2d_left"]]) def test_grid_init_param_types(ds): with pytest.raises(ValueError, match="Invalid value 'invalid'. Valid options are.*"): @@ -56,29 +59,25 @@ def test_grid_init_param_types(ds): @pytest.mark.parametrize("ds, attr, expected", test_cases) def test_xgrid_properties_ground_truth(ds, attr, expected): - grid = XGrid.from_dataset(ds, mesh="flat") + grid = FieldSet.from_sgrid_conventions(ds, mesh="flat").data_g.grid actual = getattr(grid, attr) assert_equal(actual, expected) -@pytest.mark.parametrize("ds", [pytest.param(ds, id=key) for key, ds in datasets.items()]) -def test_xgrid_from_dataset_on_generic_datasets(ds): - XGrid.from_dataset(ds, mesh="flat") - - -@pytest.mark.parametrize("ds", [datasets["ds_2d_left"]]) -def test_xgrid_axes(ds): - grid = XGrid.from_dataset(ds, mesh="flat") - assert grid.axes == ["Z", "Y", "X"] +def test_xgrid_axes(fieldset): + assert fieldset.U.grid.axes == ["Z", "Y", "X"] @pytest.mark.parametrize("ds", [datasets["ds_2d_left"]]) @pytest.mark.parametrize("mesh", ["flat", "spherical"]) def test_uxgrid_mesh(ds, mesh): - grid = XGrid.from_dataset(ds, mesh=mesh) + grid = FieldSet.from_sgrid_conventions(ds, mesh=mesh).data_g.grid assert grid._mesh == mesh +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace @pytest.mark.parametrize("ds", [datasets["ds_2d_left"]]) def test_transpose_xfield_data_to_tzyx(ds): da = ds["data_g"] @@ -93,9 +92,8 @@ def test_transpose_xfield_data_to_tzyx(ds): utils.assert_valid_field_data(da_test, grid) -@pytest.mark.parametrize("ds", [datasets["ds_2d_left"]]) -def test_xgrid_get_axis_dim(ds): - grid = XGrid.from_dataset(ds, mesh="flat") +def test_xgrid_get_axis_dim(fieldset): + grid = fieldset.U.grid assert grid.get_axis_dim("Z") == Z - 1 assert grid.get_axis_dim("Y") == Y - 1 assert grid.get_axis_dim("X") == X - 1 @@ -106,6 +104,9 @@ def test_invalid_xgrid_field_array(): ... +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_invalid_lon_lat(): """Stress test the grid initialiser by creating incompatible datasets that test the edge cases""" ds = datasets["ds_2d_left"].copy() @@ -136,6 +137,9 @@ def test_invalid_lon_lat(): XGrid.from_dataset(ds, mesh="flat") +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_invalid_depth(): ds = datasets["ds_2d_left"].copy() ds = ds.reindex({"ZG": ds.ZG[::-1]}) @@ -144,6 +148,9 @@ def test_invalid_depth(): XGrid.from_dataset(ds, mesh="flat") +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: axis checking no longer relies on these axis attributes being set (since we inspect the sgrid metadata directly) - I think this might be able to be removed entirely since sgrid metadata have quite informative error messaging. For planned future PR that deals with xgcm related cleanup def test_dim_without_axis(): ds = xr.Dataset({"z1d": (["depth"], [0])}, coords={"depth": [0]}) grid = XGrid.from_dataset(ds, mesh="flat") @@ -151,6 +158,9 @@ def test_dim_without_axis(): Field("z1d", ds["z1d"], grid, XLinear) +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: I think we can just rely on the SGRID metadata for this (which already has robust error messaging). How should discrepencies between SGRID and axis attr be handled? def test_dim_with_duplicate_axis(): ds = datasets_sgrid["ds_2d_padded_low"].copy() @@ -187,6 +197,9 @@ def test_vertical1D_field(ds): np.testing.assert_almost_equal(field.eval(np.timedelta64(0, "s"), 0.45, 0, 0), np.array([4.5])) +@pytest.mark.skip( + "Needs updating after refactoring from https://github.com/Parcels-code/Parcels/pull/2646" +) # TODO: Remove or replace def test_time1D_field(): timerange = xr.date_range("2000-01-01", "2000-01-20") ds = xr.Dataset( From cb1fd18f1604b2b1a091f96bf5445539b1af1b02 Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Thu, 28 May 2026 16:27:13 +0200 Subject: [PATCH 08/11] Fix test_spatialhash.py --- tests/test_spatialhash.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_spatialhash.py b/tests/test_spatialhash.py index 6beedfbf6..1f7d22ffa 100644 --- a/tests/test_spatialhash.py +++ b/tests/test_spatialhash.py @@ -3,17 +3,17 @@ from parcels import XGrid from parcels._datasets.structured.generic import datasets - +from parcels._core.fieldset import FieldSet def test_spatialhash_init(): ds = datasets["2d_left_rotated"] - grid = XGrid.from_dataset(ds, mesh="flat") + grid = FieldSet.from_sgrid_conventions(ds,mesh='flat').data_g.grid spatialhash = grid.get_spatial_hash() assert spatialhash is not None def test_invalid_positions(): ds = datasets["2d_left_rotated"] - grid = XGrid.from_dataset(ds, mesh="flat") + grid = FieldSet.from_sgrid_conventions(ds,mesh='flat').data_g.grid j, i, _ = grid.get_spatial_hash().query([np.nan, np.inf], [np.nan, np.inf]) assert np.all(j == -3) @@ -22,7 +22,7 @@ def test_invalid_positions(): def test_mixed_positions(): ds = datasets["2d_left_rotated"] - grid = XGrid.from_dataset(ds, mesh="flat") + grid = FieldSet.from_sgrid_conventions(ds,mesh='flat').data_g.grid lat = grid.lat.mean() lon = grid.lon.mean() y = [lat, np.nan] From ff1ac0bccd5745119f8124a9f0c27f4eba819f56 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 14:32:31 +0000 Subject: [PATCH 09/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_spatialhash.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_spatialhash.py b/tests/test_spatialhash.py index 1f7d22ffa..6233feb17 100644 --- a/tests/test_spatialhash.py +++ b/tests/test_spatialhash.py @@ -1,19 +1,19 @@ import numpy as np -from parcels import XGrid +from parcels._core.fieldset import FieldSet from parcels._datasets.structured.generic import datasets -from parcels._core.fieldset import FieldSet + def test_spatialhash_init(): ds = datasets["2d_left_rotated"] - grid = FieldSet.from_sgrid_conventions(ds,mesh='flat').data_g.grid + grid = FieldSet.from_sgrid_conventions(ds, mesh="flat").data_g.grid spatialhash = grid.get_spatial_hash() assert spatialhash is not None def test_invalid_positions(): ds = datasets["2d_left_rotated"] - grid = FieldSet.from_sgrid_conventions(ds,mesh='flat').data_g.grid + grid = FieldSet.from_sgrid_conventions(ds, mesh="flat").data_g.grid j, i, _ = grid.get_spatial_hash().query([np.nan, np.inf], [np.nan, np.inf]) assert np.all(j == -3) @@ -22,7 +22,7 @@ def test_invalid_positions(): def test_mixed_positions(): ds = datasets["2d_left_rotated"] - grid = FieldSet.from_sgrid_conventions(ds,mesh='flat').data_g.grid + grid = FieldSet.from_sgrid_conventions(ds, mesh="flat").data_g.grid lat = grid.lat.mean() lon = grid.lon.mean() y = [lat, np.nan] From b78de00b31346a0581df4c4f82f93963812af2af Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Fri, 29 May 2026 10:18:47 +0200 Subject: [PATCH 10/11] Re-enable SGRID convention setting --- src/parcels/_sgrid/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parcels/_sgrid/core.py b/src/parcels/_sgrid/core.py index 2dec2c3ec..05bec18ad 100644 --- a/src/parcels/_sgrid/core.py +++ b/src/parcels/_sgrid/core.py @@ -695,7 +695,7 @@ def _attach_sgrid_metadata(ds: xr.Dataset, grid: SGrid2DMetadata | SGrid3DMetada 0, grid.to_attrs(), ) - # ds.attrs["Conventions"] = "SGRID" # TODO: re-enable once XGrid.from_dataset is gone + ds.attrs["Conventions"] = "SGRID" return ds From e3a38d0001fe9b32545b0032208dae87b6459433 Mon Sep 17 00:00:00 2001 From: Vecko <36369090+VeckoTheGecko@users.noreply.github.com> Date: Fri, 29 May 2026 14:04:20 +0200 Subject: [PATCH 11/11] Fix remaining tests --- tests/test_index_search.py | 14 ++++---------- tests/test_particleset_execute.py | 15 +++++++-------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/tests/test_index_search.py b/tests/test_index_search.py index b85e6e3b6..44ef99ba5 100644 --- a/tests/test_index_search.py +++ b/tests/test_index_search.py @@ -3,23 +3,17 @@ import xgcm import parcels.tutorial -from parcels import Field, XGrid +from parcels import XGrid +from parcels._core.fieldset import FieldSet from parcels._core.index_search import _latlon_rad_to_xyz, _search_indices_curvilinear_2d from parcels._datasets.structured.generic import datasets -from parcels.interpolators import XLinear @pytest.fixture def field_cone(): ds = datasets["2d_left_unrolled_cone"] - grid = XGrid.from_dataset(ds, mesh="flat") - field = Field( - name="test_field", - data=ds["data_g"], - grid=grid, - interp_method=XLinear, - ) - return field + fieldset = FieldSet.from_sgrid_conventions(ds, mesh="flat") + return fieldset.data_g def test_grid_indexing_fpoints(field_cone): diff --git a/tests/test_particleset_execute.py b/tests/test_particleset_execute.py index 2fe23d74a..93042a799 100644 --- a/tests/test_particleset_execute.py +++ b/tests/test_particleset_execute.py @@ -17,7 +17,6 @@ UxGrid, Variable, VectorField, - XGrid, ) from parcels._core.utils.time import timedelta_to_float from parcels._datasets.structured.generated import simple_UV_dataset @@ -27,8 +26,6 @@ Ux_Velocity, UxConstantFaceConstantZC, UxLinearNodeLinearZF, - XLinear, - XLinear_Velocity, ) from parcels.kernels import AdvectionEE, AdvectionRK2, AdvectionRK4, AdvectionRK4_3D, AdvectionRK45 from tests.common_kernels import DoNothing @@ -40,11 +37,13 @@ def fieldset_no_time_interval() -> FieldSet: # i.e., no time variation ds = datasets_structured["ds_2d_left"].isel(time=0).drop_vars("time") - grid = XGrid.from_dataset(ds, mesh="flat") - U = Field("U", ds["U_A_grid"], grid, interp_method=XLinear) - V = Field("V", ds["V_A_grid"], grid, interp_method=XLinear) - UV = VectorField("UV", U, V, vector_interp_method=XLinear_Velocity) - return FieldSet([U, V, UV]) + ds = ds[["U_A_grid", "V_A_grid", "grid"]].rename( + { + "U_A_grid": "U", + "V_A_grid": "V", + } + ) + return FieldSet.from_sgrid_conventions(ds, mesh="flat") @pytest.fixture