diff --git a/docs/user_guide/examples/tutorial_diffusion.ipynb b/docs/user_guide/examples/tutorial_diffusion.ipynb index 12d1389a47..8f52efcf54 100644 --- a/docs/user_guide/examples/tutorial_diffusion.ipynb +++ b/docs/user_guide/examples/tutorial_diffusion.ipynb @@ -84,7 +84,7 @@ "\n", "The advection component of these kernels is similar to that of the Explicit Euler advection kernel (`AdvectionEE`). In the special case where diffusivity is constant over the entire domain, the diffusion-only kernel {py:func}`parcels.kernels.DiffusionUniformKh` can be used in combination with an advection kernel of choice. Since the diffusivity here is space-independent, gradients are not calculated, increasing efficiency. The diffusion-step can in this case be computed after or before advection, thus allowing you to chain kernels in a list.\n", "\n", - "Just like velocities, diffusivities are passed to Parcels in the form of {py:func}`parcels.Field` objects. When using {py:func}`parcels.kernels.DiffusionUniformKh`, they should be added to the {py:func}`parcels.FieldSet` object as constant fields, e.g. `fieldset.add_constant_field(\"Kh_zonal\", 1, mesh=\"flat\")`.\n", + "Just like velocities, diffusivities are passed to Parcels in the form of {py:func}`parcels.Field` objects. When using {py:func}`parcels.kernels.DiffusionUniformKh`, they should be added to the {py:func}`parcels.FieldSet` object as constant fields, e.g. `fieldset.add_constant_field(\"Kh_zonal\", 1)`.\n", "\n", "To make a central difference approximation for computing the gradient in diffusivity, a resolution for this approximation `dres` is needed: _Parcels_ approximates the gradients in diffusivities by using their values at the particle's location ± `dres` (in both $x$ and $y$). A value of `dres` must be specified and added to the FieldSet by the user (e.g. `fieldset.add_context(\"dres\", 0.01)`). Currently, it is unclear what the best value of `dres` is. From experience, the size of `dres` should be smaller than the spatial resolution of the data, but within reasonable limits of machine precision to avoid numerical errors. We are working on a method to compute gradients differently so that specifying `dres` is not necessary anymore.\n", "\n", @@ -203,7 +203,7 @@ "outputs": [], "source": [ "fieldset = parcels.FieldSet.from_sgrid_conventions(ds, mesh=\"flat\")\n", - "fieldset.add_constant_field(\"Kh_zonal\", 1, mesh=\"flat\")\n", + "fieldset.add_constant_field(\"Kh_zonal\", 1)\n", "fieldset.add_context(\"dres\", 0.00005)" ] }, diff --git a/docs/user_guide/examples/tutorial_interaction.ipynb b/docs/user_guide/examples/tutorial_interaction.ipynb index 7d42fa9d94..713ee6d862 100644 --- a/docs/user_guide/examples/tutorial_interaction.ipynb +++ b/docs/user_guide/examples/tutorial_interaction.ipynb @@ -101,8 +101,8 @@ " \"\"\"Define a fieldset with only diffusion\"\"\"\n", " ds = simple_UV_dataset(dims=(1, 1, 1, 1), mesh=\"flat\")\n", " fieldset = parcels.FieldSet.from_sgrid_conventions(ds, mesh=\"flat\")\n", - " fieldset.add_constant_field(\"Kh_zonal\", 0.0005, mesh=\"flat\")\n", - " fieldset.add_constant_field(\"Kh_meridional\", 0.0005, mesh=\"flat\")\n", + " fieldset.add_constant_field(\"Kh_zonal\", 0.0005)\n", + " fieldset.add_constant_field(\"Kh_meridional\", 0.0005)\n", " return fieldset" ] }, @@ -361,7 +361,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Parcels:docs (3.14.6)", + "display_name": "default", "language": "python", "name": "python3" }, @@ -375,7 +375,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.14.6" + "version": "3.14.7" } }, "nbformat": 4, diff --git a/docs/user_guide/examples/tutorial_stuck_particles.ipynb b/docs/user_guide/examples/tutorial_stuck_particles.ipynb index 3b379a2fcc..cbc92f9d65 100644 --- a/docs/user_guide/examples/tutorial_stuck_particles.ipynb +++ b/docs/user_guide/examples/tutorial_stuck_particles.ipynb @@ -821,8 +821,8 @@ "fieldset = parcels.FieldSet.from_sgrid_conventions(ds_fset)\n", "fieldset = fieldset.to_windowed_arrays()\n", "\n", - "fieldset.add_constant_field(\"Kh_zonal\", 5, mesh=\"spherical\")\n", - "fieldset.add_constant_field(\"Kh_meridional\", 5, mesh=\"spherical\")\n", + "fieldset.add_constant_field(\"Kh_zonal\", 5)\n", + "fieldset.add_constant_field(\"Kh_meridional\", 5)\n", "fieldset.add_context(\"dres\", 0.083)\n", "\n", "npart = 10 # number of particles to be released\n", diff --git a/src/parcels/_core/fieldset.py b/src/parcels/_core/fieldset.py index 5276b7b460..10d1da6806 100644 --- a/src/parcels/_core/fieldset.py +++ b/src/parcels/_core/fieldset.py @@ -191,7 +191,7 @@ def to_windowed_arrays(self, *, max_levels: int | None = None): model.to_windowed_arrays(max_levels=max_levels) return self - def add_constant_field(self, name: str, value, mesh: ptyping.TMesh = "spherical"): + def add_constant_field(self, name: str, value): """Wrapper function to add a Field that is constant in space, useful e.g. when using constant horizontal diffusivity @@ -201,18 +201,9 @@ def add_constant_field(self, name: str, value, mesh: ptyping.TMesh = "spherical" Name of the :class:`parcels.field.Field` object to be added value : Value of the constant field - mesh : str - String indicating the type of mesh coordinates, - - 1. spherical (default): Lat and lon in degree, with a - correction for zonal velocity U near the poles. - 2. flat: No conversion, lat/lon are assumed to be in m. """ - if mesh not in ("flat", "spherical"): - raise ValueError(f"mesh must be one of ['flat', 'spherical']. Got {mesh!r}.") - if self.constant_model is None: - self.constant_model = create_empty_constant_field_model(mesh) + self.constant_model = create_empty_constant_field_model(self.mesh) self.constant_model.data[name] = (["time", "depth", "lat", "lon"], np.full((1, 1, 1, 1), value)) diff --git a/src/parcels/_core/model.py b/src/parcels/_core/model.py index 2934257346..00e9a6300c 100644 --- a/src/parcels/_core/model.py +++ b/src/parcels/_core/model.py @@ -296,8 +296,9 @@ def assert_vector_field_components_in_dataset(ds: xr.Dataset, vector_fields: pty return -def create_empty_constant_field_model(mesh: ptyping.TMesh) -> StructuredModelData: +def create_empty_constant_field_model(mesh: SphericalMesh | FlatMesh) -> StructuredModelData: """Create a empty model for constant fields with the given mesh type.""" + mesh_: ptyping.TMesh = "flat" if isinstance(mesh, FlatMesh) else mesh return StructuredModelData.from_sgrid_conventions( xr.Dataset( {}, @@ -319,7 +320,7 @@ def create_empty_constant_field_model(mesh: ptyping.TMesh) -> StructuredModelDat ), ), ), - mesh=mesh, + mesh=mesh_, vector_fields={}, ) diff --git a/src/parcels/kernels/_advectiondiffusion.py b/src/parcels/kernels/_advectiondiffusion.py index b5d327de93..1cf9f0f055 100644 --- a/src/parcels/kernels/_advectiondiffusion.py +++ b/src/parcels/kernels/_advectiondiffusion.py @@ -122,9 +122,9 @@ def DiffusionUniformKh(particles, fieldset): # pragma: no cover Assumes that fieldset has constant fields `Kh_zonal` and `Kh_meridional`. These can be added via e.g. - `fieldset.add_constant_field("Kh_zonal", kh_zonal, mesh=mesh)` + `fieldset.add_constant_field("Kh_zonal", kh_zonal)` or - `fieldset.add_constant_field("Kh_meridional", kh_meridional, mesh=mesh)` + `fieldset.add_constant_field("Kh_meridional", kh_meridional)` where mesh is either 'flat' or 'spherical' This kernel assumes diffusivity gradients are zero and is therefore more efficient. diff --git a/tests/test_diffusion.py b/tests/test_diffusion.py index 985ca1731f..d199a7ae0f 100644 --- a/tests/test_diffusion.py +++ b/tests/test_diffusion.py @@ -26,8 +26,8 @@ def test_fieldKh_Brownian(mesh): ds["lon"].data = np.array([-1e6, 1e6]) ds["lat"].data = np.array([-1e6, 1e6]) fieldset = FieldSet.from_sgrid_conventions(ds, mesh=mesh) - fieldset.add_constant_field("Kh_zonal", kh_zonal, mesh=mesh) - fieldset.add_constant_field("Kh_meridional", kh_meridional, mesh=mesh) + fieldset.add_constant_field("Kh_zonal", kh_zonal) + fieldset.add_constant_field("Kh_meridional", kh_meridional) npart = 100 runtime = np.timedelta64(2, "h") diff --git a/tests/test_fieldset.py b/tests/test_fieldset.py index 48f503a89b..18cf73cc6f 100644 --- a/tests/test_fieldset.py +++ b/tests/test_fieldset.py @@ -33,7 +33,7 @@ def fieldset_two_models(): fset2 = FieldSet.from_sgrid_conventions(ds2, mesh="flat", vector_fields={"UV_wind": ("U_wind", "V_wind")}) fset2.add_context("my_value", 2.0) fset2.add_context("my_list", [1, 2, "hello"]) - fset2.add_constant_field("constant_field", 3.0, mesh="flat") + fset2.add_constant_field("constant_field", 3.0) return fset1 + fset2 @@ -74,7 +74,7 @@ def test_fieldset_add_context_invalid_name(fieldset, name): def test_fieldset_add_constant_field(fieldset): - fieldset.add_constant_field("test_constant_field", 1.0, mesh="flat") + fieldset.add_constant_field("test_constant_field", 1.0) # Get a point in the domain time = ds["time"].mean() @@ -91,7 +91,7 @@ def test_fieldset_gridset(fieldset): assert fieldset.fields["UV"].grid in fieldset.gridset assert len(fieldset.gridset) == 1 - fieldset.add_constant_field("constant_field", 1.0, mesh="flat") + fieldset.add_constant_field("constant_field", 1.0) assert len(fieldset.gridset) == 2 @@ -238,7 +238,7 @@ def test_multi_model_time_interval(): ds3["time"] = (ds3["time"].dims, ds3["time"].data + np.timedelta64(timedelta(days=2)), ds3["time"].attrs) fieldset += FieldSet.from_sgrid_conventions(ds3, mesh="flat") - fieldset.add_constant_field("constant_field", 1.0, mesh="flat") + fieldset.add_constant_field("constant_field", 1.0) assert len(fieldset.models) == 3 assert fieldset.constant_model is not None @@ -258,7 +258,7 @@ def test_multi_model_nonoverlapping_time_interval(): ds3["time"] = (ds3["time"].dims, ds3["time"].data + np.timedelta64(timedelta(days=2000)), ds3["time"].attrs) fieldset += FieldSet.from_sgrid_conventions(ds3, mesh="flat") - fieldset.add_constant_field("constant_field", 1.0, mesh="flat") + fieldset.add_constant_field("constant_field", 1.0) assert len(fieldset.models) == 3 assert fieldset.constant_model is not None