Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dfs{2,3} local coordinates #668

Merged
merged 6 commits into from
Mar 19, 2024
Merged
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
4 changes: 3 additions & 1 deletion mikeio/dfs/_dfs2.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,13 @@ class Dfs2(_Dfs123):
def __init__(
self,
filename: str | Path,
type: Literal["horizontal", "spectral"] = "horizontal",
type: Literal["horizontal", "spectral", "vertical"] = "horizontal",
):
filename = str(filename)
super().__init__(filename)

is_spectral = type == "spectral"
is_vertical = type == "vertical"
dfs = DfsFileFactory.Dfs2FileOpen(str(filename))

x0 = dfs.SpatialAxis.X0 if is_spectral else 0.0
Expand All @@ -137,6 +138,7 @@ def __init__(
orientation=orientation,
origin=origin,
is_spectral=is_spectral,
is_vertical=is_vertical,
)
dfs.Close()
self._validate_no_orientation_in_geo()
Expand Down
16 changes: 15 additions & 1 deletion mikeio/spatial/_grid_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,11 +426,12 @@ def __init__(
dy: float | None = None,
ny: int | None = None,
bbox: Tuple[float, float, float, float] | None = None,
projection: str = "NON-UTM",
projection: str = "LONG/LAT",
origin: Tuple[float, float] | None = None,
orientation: float = 0.0,
axis_names: Tuple[str, str] = ("x", "y"),
is_spectral: bool = False,
is_vertical: bool = False,
):
"""Create equidistant 2D spatial geometry

Expand Down Expand Up @@ -464,6 +465,8 @@ def __init__(
names of x and y axes, by default ("x", "y")
is_spectral : bool, optional
if True, the grid is spectral, by default False
is_vertical : bool, optional
if True, the grid is vertical, by default False

Examples
--------
Expand Down Expand Up @@ -491,7 +494,13 @@ def __init__(
dy = self._dx if dy is None else dy
self._y0, self._dy, self._ny = _parse_grid_axis("y", y, y0, dy, ny)

if self.is_local_coordinates and not (is_spectral or is_vertical):
self._x0 = self._x0 + self._dx / 2
self._y0 = self._y0 + self._dy / 2
self._shift_origin_on_write = False

self.is_spectral = is_spectral
self.is_vertical = is_vertical

self.plot = _Grid2DPlotter(self)

Expand Down Expand Up @@ -945,6 +954,7 @@ def _index_to_Grid2D(
projection=self.projection,
orientation=self._orientation,
is_spectral=self.is_spectral,
is_vertical=self.is_vertical,
origin=origin,
)

Expand Down Expand Up @@ -1120,6 +1130,10 @@ def __init__(
self._origin = origin
self._orientation = orientation

if self.is_local_coordinates:
self._x0 = self._x0 + self._dx / 2
self._y0 = self._y0 + self._dy / 2

@property
def default_dims(self) -> Tuple[str, ...]:
return ("z", "y", "x")
Expand Down
46 changes: 34 additions & 12 deletions notebooks/Dfs2 - Various types.ipynb

Large diffs are not rendered by default.

544 changes: 524 additions & 20 deletions notebooks/Dfs3 - Basic.ipynb

Large diffs are not rendered by default.

51 changes: 48 additions & 3 deletions tests/test_dfs2.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def dfs2_pt_spectrum_linearf():
@pytest.fixture
def dfs2_vertical_nonutm():
filepath = Path("tests/testdata/hd_vertical_slice.dfs2")
return mikeio.open(filepath)
return mikeio.open(filepath, type="vertical")


@pytest.fixture
Expand All @@ -50,7 +50,7 @@ def dfs2_gebco():


def test_get_time_without_reading_data():
dfs = mikeio.open("tests/testdata/hd_vertical_slice.dfs2")
dfs = mikeio.open("tests/testdata/hd_vertical_slice.dfs2", type="vertical")

assert isinstance(dfs.time, pd.DatetimeIndex)
assert len(dfs.time) == 13
Expand Down Expand Up @@ -281,8 +281,10 @@ def test_properties_vertical_nonutm(dfs2_vertical_nonutm):

def test_isel_vertical_nonutm(dfs2_vertical_nonutm):
ds = dfs2_vertical_nonutm.read()
assert ds.geometry.is_vertical
dssel = ds.isel(y=slice(45, None))
g = dssel.geometry
assert g.is_vertical
assert g._x0 == 0
assert g._y0 == 0 # TODO: should this be 45?
assert g.x[0] == 0
Expand Down Expand Up @@ -346,7 +348,9 @@ def test_properties_pt_spectrum_linearf(dfs2_pt_spectrum_linearf):


def test_dir_wave_spectra_relative_time_axis():
ds = mikeio.read("tests/testdata/dir_wave_analysis_spectra.dfs2")
ds = mikeio.open(
"tests/testdata/dir_wave_analysis_spectra.dfs2", type="spectral"
).read()
assert ds.n_items == 1
assert ds.geometry.nx == 128
assert ds.geometry.ny == 37
Expand Down Expand Up @@ -819,3 +823,44 @@ def test_read_dfs2_static_dt_zero():

assert ds2.shape == (2, 2)
assert "time" not in ds2.dims


def test_write_read_local_coordinates(tmp_path):
da = mikeio.DataArray(
np.array([[1, 2, 3], [4, 5, 6]]),
geometry=mikeio.Grid2D(nx=3, ny=2, dx=0.5, projection="NON-UTM"),
)
fp = tmp_path / "local_coordinates.dfs2"
da.to_dfs(fp)

ds = mikeio.read(fp)
assert da.geometry == ds.geometry


def test_to_xarray():
# data is not important here
data = np.array([[1, 2, 3], [4, 5, 6]])

# geographic coordinates
dag = mikeio.DataArray(
data=data,
geometry=mikeio.Grid2D(nx=3, ny=2, dx=0.5, projection="LONG/LAT"),
)
assert dag.geometry.x[0] == pytest.approx(0.0)
assert dag.geometry.y[0] == pytest.approx(0.0)
xr_dag = dag.to_xarray()
assert xr_dag.x[0] == pytest.approx(0.0)
assert xr_dag.y[0] == pytest.approx(0.0)

# local coordinates
da = mikeio.DataArray(
data=data,
geometry=mikeio.Grid2D(nx=3, ny=2, dx=0.5, projection="NON-UTM"),
)
# local coordinates (=NON-UTM) have a different convention, geometry.x still refers to element centers
assert da.geometry.x[0] == pytest.approx(0.25)
assert da.geometry.y[0] == pytest.approx(0.25)

xr_da = da.to_xarray()
assert xr_da.x[0] == pytest.approx(0.25)
assert xr_da.y[0] == pytest.approx(0.25)
47 changes: 47 additions & 0 deletions tests/test_dfs3.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,50 @@ def test_MIKE_SHE_dfs3_output():
assert g2.x[0] == g.x[0] + 30 * g.dx
assert g2.y[0] == g.y[0] # + 35 * g.dy
assert g2.origin == pytest.approx((g2.x[0], g2.y[0]))


def test_write_read_local_coordinates(tmp_path):
da = mikeio.DataArray(
np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]),
geometry=mikeio.Grid3D(
nx=3, ny=2, nz=2, dx=0.5, dy=1, dz=1, projection="NON-UTM"
),
)
fp = tmp_path / "local_coordinates.dfs3"
da.to_dfs(fp)

ds = mikeio.read(fp)
assert da.geometry == ds.geometry


def test_to_xarray():
# data is not important here
data = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])

# geographic coordinates
dag = mikeio.DataArray(
data=data,
geometry=mikeio.Grid3D(
nx=3, ny=2, nz=2, dy=0.5, dz=1, dx=0.5, projection="LONG/LAT"
),
)
assert dag.geometry.x[0] == pytest.approx(0.0)
assert dag.geometry.y[0] == pytest.approx(0.0)
xr_dag = dag.to_xarray()
assert xr_dag.x[0] == pytest.approx(0.0)
assert xr_dag.y[0] == pytest.approx(0.0)

# local coordinates
da = mikeio.DataArray(
data=data,
geometry=mikeio.Grid3D(
nx=3, ny=2, nz=2, dy=0.5, dz=1, dx=0.5, projection="NON-UTM"
),
)
# local coordinates (=NON-UTM) have a different convention, geometry.x still refers to element centers
assert da.geometry.x[0] == pytest.approx(0.25)
assert da.geometry.y[0] == pytest.approx(0.25)

xr_da = da.to_xarray()
assert xr_da.x[0] == pytest.approx(0.25)
assert xr_da.y[0] == pytest.approx(0.25)
4 changes: 2 additions & 2 deletions tests/test_geometry_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def test_to_geometryFM():
assert isinstance(g, GeometryFM2D)
assert g.n_elements == nx * ny
assert g.n_nodes == (nx + 1) * (ny + 1)
assert g.projection_string == "NON-UTM"
assert g.projection_string == "LONG/LAT"

xe = g.element_coordinates[:, 0]
ye = g.element_coordinates[:, 1]
Expand Down Expand Up @@ -398,7 +398,7 @@ def test_grid2d_equality():

assert g1 == g2

g3 = Grid2D(dx=0.1, nx=2, dy=0.2, ny=4, projection="LONG/LAT")
g3 = Grid2D(dx=0.1, nx=2, dy=0.2, ny=4, projection="NON-UTM")
g4 = Grid2D(dx=0.1, nx=2, dy=0.2, ny=4)

assert g3 != g4
Expand Down
Loading