Skip to content

Commit fbeb39d

Browse files
committed
Refactor interpolation function checking
1 parent e31a2f7 commit fbeb39d

1 file changed

Lines changed: 38 additions & 58 deletions

File tree

parcels/field.py

Lines changed: 38 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,39 @@ def _deal_with_errors(error, key, vector_type: VectorType):
5959
return 0
6060

6161

62+
def ZeroInterpolator(
63+
field: Field,
64+
ti: int,
65+
position: dict[str, tuple[int, float | np.ndarray]],
66+
tau: np.float32 | np.float64,
67+
t: np.float32 | np.float64,
68+
z: np.float32 | np.float64,
69+
y: np.float32 | np.float64,
70+
x: np.float32 | np.float64,
71+
) -> np.float32 | np.float64:
72+
"""Template function used for the signature check of the lateral interpolation methods."""
73+
return 0.0
74+
75+
76+
def _assert_same_function_signature(f: Callable, *, ref: Callable) -> None:
77+
"""Ensures a function `f` has the same signature as the reference function `ref`."""
78+
sig_ref = inspect.signature(ref)
79+
sig = inspect.signature(f)
80+
81+
if len(sig_ref.parameters) != len(sig.parameters):
82+
raise ValueError(
83+
f"Interpolation function must have {len(sig_ref.parameters)} parameters, got {len(sig.parameters)}"
84+
)
85+
86+
for (_name1, param1), (_name2, param2) in zip(sig_ref.parameters.items(), sig.parameters.items(), strict=False):
87+
if param1.kind != param2.kind:
88+
raise ValueError(
89+
f"Parameter '{_name2}' has incorrect parameter kind. Expected {param1.kind}, got {param2.kind}"
90+
)
91+
if param1.name != param2.name:
92+
raise ValueError(f"Parameter '{_name2}' has incorrect name. Expected '{param1.name}', got '{param2.name}'")
93+
94+
6295
class Field:
6396
"""The Field class that holds scalar field data.
6497
The `Field` object is a wrapper around a xarray.DataArray or uxarray.UxDataArray object.
@@ -93,42 +126,6 @@ class Field:
93126
94127
"""
95128

96-
@staticmethod
97-
def _interp_template(
98-
self,
99-
ti: int,
100-
position: dict[str, tuple[int, float | np.ndarray]],
101-
tau: np.float32 | np.float64,
102-
t: np.float32 | np.float64,
103-
z: np.float32 | np.float64,
104-
y: np.float32 | np.float64,
105-
x: np.float32 | np.float64,
106-
) -> np.float32 | np.float64:
107-
"""Template function used for the signature check of the lateral interpolation methods."""
108-
return 0.0
109-
110-
def _validate_interp_function(self, func: Callable) -> None:
111-
"""Ensures that the function has the correct signature."""
112-
template_sig = inspect.signature(self._interp_template)
113-
func_sig = inspect.signature(func)
114-
115-
if len(template_sig.parameters) != len(func_sig.parameters):
116-
raise ValueError(
117-
f"Interpolation function must have {len(template_sig.parameters)} parameters, got {len(func_sig.parameters)}"
118-
)
119-
120-
for (_name1, param1), (_name2, param2) in zip(
121-
template_sig.parameters.items(), func_sig.parameters.items(), strict=False
122-
):
123-
if param1.kind != param2.kind:
124-
raise ValueError(
125-
f"Parameter '{_name2}' has incorrect parameter kind. Expected {param1.kind}, got {param2.kind}"
126-
)
127-
if param1.name != param2.name:
128-
raise ValueError(
129-
f"Parameter '{_name2}' has incorrect name. Expected '{param1.name}', got '{param2.name}'"
130-
)
131-
132129
def __init__(
133130
self,
134131
name: str,
@@ -176,9 +173,9 @@ def __init__(
176173

177174
# Setting the interpolation method dynamically
178175
if interp_method is None:
179-
self._interp_method = self._interp_template # Default to method that returns 0 always
176+
self._interp_method = ZeroInterpolator # Default to method that returns 0 always
180177
else:
181-
self._validate_interp_function(interp_method)
178+
_assert_same_function_signature(interp_method, ref=ZeroInterpolator)
182179
self._interp_method = interp_method
183180

184181
self.igrid = -1 # Default the grid index to -1
@@ -270,7 +267,7 @@ def interp_method(self):
270267

271268
@interp_method.setter
272269
def interp_method(self, method: Callable):
273-
self._validate_interp_function(method)
270+
_assert_same_function_signature(method, ref=ZeroInterpolator)
274271
self._interp_method = method
275272

276273
def _check_velocitysampling(self):
@@ -337,23 +334,6 @@ def __contains__(self, key: str):
337334
class VectorField:
338335
"""VectorField class that holds vector field data needed to execute particles."""
339336

340-
def _validate_vector_interp_function(self, func: Callable):
341-
"""Ensures that the function has the correct signature."""
342-
expected_params = ["ti", "ei", "bcoords", "t", "z", "y", "x"]
343-
expected_return_types = (np.float32, np.float64)
344-
345-
sig = inspect.signature(func)
346-
params = list(sig.parameters.keys())
347-
348-
# Check the parameter names and count
349-
if params != expected_params:
350-
raise TypeError(f"Function must have parameters {expected_params}, but got {params}")
351-
352-
# Check return annotation if present
353-
return_annotation = sig.return_annotation
354-
if return_annotation not in (inspect.Signature.empty, *expected_return_types):
355-
raise TypeError(f"Function must return a float, but got {return_annotation}")
356-
357337
def __init__(
358338
self, name: str, U: Field, V: Field, W: Field | None = None, vector_interp_method: Callable | None = None
359339
):
@@ -379,7 +359,7 @@ def __init__(
379359
if vector_interp_method is None:
380360
self._vector_interp_method = None
381361
else:
382-
self._validate_vector_interp_function(vector_interp_method)
362+
_assert_same_function_signature(vector_interp_method, ref=ZeroInterpolator)
383363
self._interp_method = vector_interp_method
384364

385365
def __repr__(self):
@@ -395,7 +375,7 @@ def vector_interp_method(self):
395375

396376
@vector_interp_method.setter
397377
def vector_interp_method(self, method: Callable):
398-
self._validate_vector_interp_function(method)
378+
_assert_same_function_signature(method, ref=ZeroInterpolator)
399379
self._vector_interp_method = method
400380

401381
# @staticmethod

0 commit comments

Comments
 (0)