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
27 changes: 27 additions & 0 deletions src/parcels/_core/fieldset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import functools
import sys
import warnings
from collections.abc import Iterable
from typing import IO, TYPE_CHECKING

Expand All @@ -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 (
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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."""

Expand Down
8 changes: 4 additions & 4 deletions tests/test_fieldset.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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()
Expand All @@ -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


Expand Down Expand Up @@ -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")
Expand Down