Skip to content

Commit 9868df2

Browse files
committed
Add input validation
1 parent 135c3c8 commit 9868df2

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

src/mdio/converters/numpy.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,42 @@ def _build_dataset(
4949
return mdio_ds
5050

5151

52+
def _validate_coordinates(
53+
index_coords: dict[str, NDArray],
54+
mdio_template: AbstractDatasetTemplate,
55+
array: NDArray,
56+
) -> None:
57+
"""Validate user-provided coordinates against template and array dimensions.
58+
59+
Args:
60+
index_coords: Dictionary mapping dimension names to coordinate arrays.
61+
mdio_template: The MDIO template defining expected dimensions.
62+
array: The numpy array being converted.
63+
64+
Raises:
65+
ValueError: If coordinate names or sizes don't match requirements.
66+
"""
67+
# Validate that coordinate names match template dimension names
68+
for coord_name in index_coords:
69+
if coord_name not in mdio_template.dimension_names:
70+
available_dims = sorted(mdio_template.dimension_names)
71+
err = (
72+
f"Coordinate name '{coord_name}' not found in template dimensions. "
73+
f"Available dimensions: {available_dims}"
74+
)
75+
raise ValueError(err)
76+
77+
# Validate coordinate array sizes match array dimensions
78+
for dim_name, coord_array in index_coords.items():
79+
expected_size = array.shape[mdio_template.dimension_names.index(dim_name)]
80+
if coord_array.size != expected_size:
81+
err = (
82+
f"Size of coordinate '{dim_name}' ({coord_array.size}) does not match "
83+
f"array dimension size ({expected_size})"
84+
)
85+
raise ValueError(err)
86+
87+
5288
def _populate_coordinates_and_write(
5389
xr_dataset: xr_Dataset,
5490
index_coords: dict[str, NDArray],
@@ -111,6 +147,10 @@ def numpy_to_mdio( # noqa: PLR0913
111147
err = f"Output location '{output_path.as_posix()}' exists. Set `overwrite=True` if intended."
112148
raise FileExistsError(err)
113149

150+
# Validate coordinates if provided
151+
if index_coords:
152+
_validate_coordinates(index_coords, mdio_template, array)
153+
114154
# Build dataset
115155
mdio_ds = _build_dataset(
116156
array=array,

tests/integration/test_import_numpy.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,50 @@ def test_npy_to_mdio_seismic_template(mock_array: NDArray) -> None:
118118
npt.assert_array_equal(ds[data_var].values, mock_array)
119119
assert list(ds.sizes.keys()) == ["inline", "crossline", "time"]
120120
assert ds[data_var].shape == mock_array.shape
121+
122+
123+
def test_npy_to_mdio_invalid_coordinate_names(mock_array: NDArray, mock_template: AbstractDatasetTemplate) -> None:
124+
"""Test NumPy to MDIO conversion with invalid coordinate names."""
125+
index_coords = {
126+
"inline": np.arange(15), # valid
127+
"crossline": np.arange(10), # valid
128+
"invalid_dim": np.arange(20), # invalid dimension name
129+
}
130+
131+
with pytest.raises(ValueError, match=r"Coordinate name 'invalid_dim' not found in template dimensions"):
132+
numpy_to_mdio(mock_array, mock_template, "memory://npy_invalid.mdio", index_coords=index_coords)
133+
134+
135+
def test_npy_to_mdio_invalid_coordinate_sizes(mock_array: NDArray, mock_template: AbstractDatasetTemplate) -> None:
136+
"""Test NumPy to MDIO conversion with invalid coordinate array sizes."""
137+
index_coords = {
138+
"inline": np.arange(10), # wrong size (should be 15)
139+
"crossline": np.arange(10), # valid
140+
"time": np.arange(20), # valid
141+
}
142+
143+
with pytest.raises(
144+
ValueError, match=r"Size of coordinate 'inline' \(10\) does not match array dimension size \(15\)"
145+
):
146+
numpy_to_mdio(mock_array, mock_template, "memory://npy_wrong_size.mdio", index_coords=index_coords)
147+
148+
149+
def test_npy_to_mdio_valid_custom_coordinates(mock_array: NDArray, mock_template: AbstractDatasetTemplate) -> None:
150+
"""Test NumPy to MDIO conversion with valid custom coordinates."""
151+
index_coords = {
152+
"inline": np.arange(100, 115), # valid size (15)
153+
"crossline": np.arange(200, 210), # valid size (10)
154+
"time": np.arange(0, 20), # valid size (20)
155+
}
156+
157+
numpy_to_mdio(mock_array, mock_template, "memory://npy_valid_custom.mdio", index_coords=index_coords)
158+
ds = open_mdio("memory://npy_valid_custom.mdio")
159+
160+
# Check data
161+
data_var = ds.attrs.get("defaultVariableName", "amplitude")
162+
npt.assert_array_equal(ds[data_var].values, mock_array)
163+
164+
# Check coordinates are correctly set
165+
npt.assert_array_equal(ds["inline"].values, index_coords["inline"])
166+
npt.assert_array_equal(ds["crossline"].values, index_coords["crossline"])
167+
npt.assert_array_equal(ds["time"].values, index_coords["time"])

0 commit comments

Comments
 (0)