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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ version = "1.4.0a3"
dependencies = [
"numpy > 1.24.4",
"pandas >= 1.4, < 3.0", # TODO remove upper limit
"mikeio >= 1.2",
"mikeio >= 3.2",
"matplotlib",
"xarray",
"netCDF4",
Expand Down
3 changes: 0 additions & 3 deletions requirements_min.txt

This file was deleted.

15 changes: 6 additions & 9 deletions src/modelskill/model/dfsu.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from __future__ import annotations
import inspect
from pathlib import Path
from typing import Literal, get_args, cast

Expand Down Expand Up @@ -195,9 +194,7 @@ def _extract_vertical(
if isinstance(self.data, mikeio.Dataset):
ds_column = self.data.sel(x=x, y=y)
elif isinstance(self.data, mikeio.dfsu.Dfsu3D):
# FIXME: open with specific element instead...make sure mikeio fix is in place before
# ds_column = self.data.read(elements=elemids) # when bug is fixed in mikeio
ds_column = self.data.read(items=self.sel_items.all).sel(x=x, y=y)
ds_column = self.data.read(elements=elemids, items=self.sel_items.all)
else:
raise ValueError(
"Unsupported data type for vertical profile extraction."
Expand All @@ -207,8 +204,8 @@ def _extract_vertical(
"Only spatial_method='contained' is currently implemented for vertical profile extraction from DfsuModelResult. "
)

# get layer depth info
layer_boundaries = ds_column.geometry.calc_ze(ds_column._zn)
# z-coordinates at element centers, one row per timestep
element_depths = ds_column.z.elements

item_name = self.sel_items.values

Expand All @@ -218,7 +215,7 @@ def _extract_vertical(

# Create flattened arrays # Repeat each timestamp n_layers times
time_flat = np.repeat(ds_column.time, n_layers)
z_flat = layer_boundaries.flatten() # Flatten z-coordinates
z_flat = element_depths.flatten() # Flatten z-coordinates
item_values_1d = ds_column[item_name].to_numpy().flatten() # Flatten item?
# aux_items_flat = {aux_item: ds_column[aux_item].to_numpy().flatten() for aux_item in self.sel_items.aux}

Expand Down Expand Up @@ -261,8 +258,8 @@ def _extract_point(
)

if method == "contained":
signature = inspect.signature(self.data.geometry.find_index)
if "z" in signature.parameters and z is not None:
# only layered geometries can be indexed by z
if z is not None and hasattr(self.data.geometry, "n_layers"):
elemids = self.data.geometry.find_index(x=x, y=y, z=z)
else:
elemids = self.data.geometry.find_index(x=x, y=y)
Expand Down
36 changes: 35 additions & 1 deletion tests/model/test_vertical.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def test_extract_from_dfsu_correct_layers(self, dfsu_ds):
# ===

# expected element depths from dfsu geometry
element_depths_expected = dfsu_col.geometry.calc_ze(dfsu_col._zn)
element_depths_expected = dfsu_col.z.elements

# element depths at first timestep from VerticalModelResult
element_depths_t0 = vmr.data.sel(time=vmr.data.time.values[0]).z.values
Expand All @@ -239,6 +239,40 @@ def test_extract_from_dfsu_correct_layers(self, dfsu_ds):
assert np.allclose(element_depths_t0, element_depths_expected[0, :])
assert np.allclose(element_depths_tend, element_depths_expected[-1, :])

def test_extract_from_dfsu_file_matches_dataset(self, dfsu_fpath, dfsu_ds):
# A dfsu file takes the Dfsu3D branch, which reads only the observation
# column; a Dataset takes the sel(x, y) branch. Both must give the same
# z and values.
dfsu_mr = ms.DfsuModelResult(dfsu_fpath, item=0, name="test")
assert isinstance(dfsu_mr.data, mikeio.dfsu.Dfsu3D)

dummy_obs = pd.DataFrame(
{"z": [-5.0, -4.0, -3.0], "salt": [30.0, 31.0, 32.0]},
index=pd.to_datetime(["2022-06-14 00:00:00"] * 3),
)
XPOS = 6.575e5
YPOS = 6.55e6
vo = ms.VerticalObservation(dummy_obs, x=XPOS, y=YPOS, item="salt", z_item="z")

vmr = dfsu_mr.extract(vo, spatial_method="contained")
vmr_from_ds = ms.DfsuModelResult(dfsu_ds, item=0, name="test").extract(
vo, spatial_method="contained"
)

assert np.allclose(vmr.data.z.values, vmr_from_ds.data.z.values)
assert np.allclose(vmr.data["test"].values, vmr_from_ds.data["test"].values)

# ...and against the dfsu column itself, so both branches breaking the
# same way is still a failure
dfsu_col = dfsu_ds.sel(x=XPOS, y=YPOS)
item_name = dfsu_ds.items[0].name
assert np.allclose(vmr.data.z.values, dfsu_col.z.elements.flatten())
assert np.allclose(
vmr.data["test"].values, dfsu_col[item_name].to_numpy().flatten()
)
assert vmr.x == pytest.approx(dfsu_col.geometry.element_coordinates[0, 0])
assert vmr.y == pytest.approx(dfsu_col.geometry.element_coordinates[0, 1])

@pytest.mark.parametrize("spatial_method", ["nearest", "inverse_distance"])
def test_extract_from_dfsu_unsupported_spatial_methods_raise(
self, dfsu_ds, spatial_method
Expand Down
Loading