diff --git a/src/parcels/_core/fieldset.py b/src/parcels/_core/fieldset.py index 940c98cf2..232ca2724 100644 --- a/src/parcels/_core/fieldset.py +++ b/src/parcels/_core/fieldset.py @@ -2,6 +2,7 @@ import functools import sys +import warnings from collections.abc import Iterable from typing import IO, TYPE_CHECKING @@ -21,6 +22,7 @@ from parcels._core.utils.string import _assert_str_and_python_varname from parcels._core.utils.time import get_datetime_type_calendar from parcels._core.utils.time import is_compatible as datetime_is_compatible +from parcels._core.warnings import FieldSetWarning from parcels._python import NOTSET, NotSetType from parcels._reprs import fieldset_describe from parcels.interpolators import ( @@ -73,6 +75,7 @@ def __init__(self, models: list[ModelData]): self._fields: dict[str, Field | VectorField] | None = None self.reconstruct_fields() self.context: dict[str, float] = {} + _warn_if_fields_use_different_meshes(self.fields.values()) def __setattr__(self, name, value): """Set field attribute by name. If context exists and name in context, raise error to prevent overwriting context variable.""" @@ -151,6 +154,7 @@ def add_field(self, field: Field, name: str | None = None): raise ValueError(f"FieldSet already has a Field with name '{name}'") self.fields[name] = field + _warn_if_fields_use_different_meshes(self.fields.values()) def to_windowed_arrays(self, *, max_levels: int | None = None): """Wrap dask-backed field data in rolling time-window caches. @@ -215,6 +219,7 @@ def add_constant_field(self, name: str, value, mesh: ptyping.Mesh = "spherical") self.reconstruct_fields() field = getattr(self, name) field.interp_method = XConstantField() + _warn_if_fields_use_different_meshes(self.fields.values()) def add_context(self, name, value): """Add context variable to the FieldSet. @@ -362,6 +367,28 @@ def assert_compatible_fieldsets(left: FieldSet, right: FieldSet) -> None: ) +def _warn_if_fields_use_different_meshes(fields: Iterable[Field | VectorField]): + """Warn if multiple fields use different meshes on the underlying grids. + + Parameters + ---------- + fields : Iterable[Field | VectorField] + The fields to check for conflicting meshes. + + Warns + ----- + FieldSetWarning + If the fields have different meshes on the underlying grids. + """ + meshes = {field.grid._mesh for field in fields} + if len(meshes) > 1: + warnings.warn( + f"FieldSet has multiple different meshes: {meshes}. This may lead to unexpected behavior during execution.", + category=FieldSetWarning, + stacklevel=3, + ) + + class CalendarError(Exception): # TODO: Move to a parcels errors module """Exception raised when the calendar of a field is not compatible with the rest of the Fields. The user should ensure that they only add fields to a FieldSet that have compatible CFtime calendars.""" diff --git a/tests/test_fieldset.py b/tests/test_fieldset.py index 5ec45ac0d..bba2738c7 100644 --- a/tests/test_fieldset.py +++ b/tests/test_fieldset.py @@ -31,7 +31,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) + fset2.add_constant_field("constant_field", 3.0, mesh="flat") return fset1 + fset2 @@ -68,7 +68,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) + fieldset.add_constant_field("test_constant_field", 1.0, mesh="flat") # Get a point in the domain time = ds["time"].mean() @@ -85,7 +85,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) + fieldset.add_constant_field("constant_field", 1.0, mesh="flat") assert len(fieldset.gridset) == 2 @@ -232,7 +232,7 @@ def test_fieldset_time_interval(): field2 = Field("field2", ds2["U_A_grid"], grid2, interp_method=XLinear) fieldset = FieldSet([field1, field2]) - fieldset.add_constant_field("constant_field", 1.0) + fieldset.add_constant_field("constant_field", 1.0, mesh="flat") assert fieldset.time_interval.left == np.datetime64("2000-01-02") assert fieldset.time_interval.right == np.datetime64("2001-01-01")