|
2 | 2 |
|
3 | 3 | import functools |
4 | 4 | import sys |
| 5 | +import warnings |
5 | 6 | from collections.abc import Iterable |
6 | 7 | from typing import IO, TYPE_CHECKING |
7 | 8 |
|
|
21 | 22 | from parcels._core.utils.string import _assert_str_and_python_varname |
22 | 23 | from parcels._core.utils.time import get_datetime_type_calendar |
23 | 24 | from parcels._core.utils.time import is_compatible as datetime_is_compatible |
| 25 | +from parcels._core.warnings import FieldSetWarning |
24 | 26 | from parcels._python import NOTSET, NotSetType |
25 | 27 | from parcels._reprs import fieldset_describe |
26 | 28 | from parcels.interpolators import ( |
@@ -73,6 +75,7 @@ def __init__(self, models: list[ModelData]): |
73 | 75 | self._fields: dict[str, Field | VectorField] | None = None |
74 | 76 | self.reconstruct_fields() |
75 | 77 | self.context: dict[str, float] = {} |
| 78 | + _warn_if_fields_use_different_meshes(self.fields.values()) |
76 | 79 |
|
77 | 80 | def __setattr__(self, name, value): |
78 | 81 | """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): |
151 | 154 | raise ValueError(f"FieldSet already has a Field with name '{name}'") |
152 | 155 |
|
153 | 156 | self.fields[name] = field |
| 157 | + _warn_if_fields_use_different_meshes(self.fields.values()) |
154 | 158 |
|
155 | 159 | def to_windowed_arrays(self, *, max_levels: int | None = None): |
156 | 160 | """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") |
215 | 219 | self.reconstruct_fields() |
216 | 220 | field = getattr(self, name) |
217 | 221 | field.interp_method = XConstantField() |
| 222 | + _warn_if_fields_use_different_meshes(self.fields.values()) |
218 | 223 |
|
219 | 224 | def add_context(self, name, value): |
220 | 225 | """Add context variable to the FieldSet. |
@@ -362,6 +367,28 @@ def assert_compatible_fieldsets(left: FieldSet, right: FieldSet) -> None: |
362 | 367 | ) |
363 | 368 |
|
364 | 369 |
|
| 370 | +def _warn_if_fields_use_different_meshes(fields: Iterable[Field | VectorField]): |
| 371 | + """Warn if multiple fields use different meshes on the underlying grids. |
| 372 | +
|
| 373 | + Parameters |
| 374 | + ---------- |
| 375 | + fields : Iterable[Field | VectorField] |
| 376 | + The fields to check for conflicting meshes. |
| 377 | +
|
| 378 | + Warns |
| 379 | + ----- |
| 380 | + FieldSetWarning |
| 381 | + If the fields have different meshes on the underlying grids. |
| 382 | + """ |
| 383 | + meshes = {field.grid._mesh for field in fields} |
| 384 | + if len(meshes) > 1: |
| 385 | + warnings.warn( |
| 386 | + f"FieldSet has multiple different meshes: {meshes}. This may lead to unexpected behavior during execution.", |
| 387 | + category=FieldSetWarning, |
| 388 | + stacklevel=3, |
| 389 | + ) |
| 390 | + |
| 391 | + |
365 | 392 | class CalendarError(Exception): # TODO: Move to a parcels errors module |
366 | 393 | """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.""" |
367 | 394 |
|
|
0 commit comments