Skip to content

Commit

Permalink
Merge pull request #645 from DHI/legacy-test
Browse files Browse the repository at this point in the history
Legacy-test
  • Loading branch information
ecomodeller authored Feb 9, 2024
2 parents f6037d9 + ada5963 commit 6ecbdf9
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 63 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/legacy_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Legacy test

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8

- name: Install older dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_min.txt
pip install pytest
- name: Install MIKE IO
run: |
pip install .[test]
- name: Test with pytest
run: |
pytest --ignore tests/notebooks/
16 changes: 12 additions & 4 deletions mikeio/_interpolation.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
from __future__ import annotations
from typing import Tuple, TYPE_CHECKING
import numpy as np
from numpy.typing import NDArray

if TYPE_CHECKING:
from .dataset import Dataset, DataArray

from .spatial import GeometryUndefined


def get_idw_interpolant(distances:NDArray[np.floating], p:float=2) -> NDArray[np.floating]:
def get_idw_interpolant(distances: np.ndarray, p: float = 2) -> np.ndarray:
"""IDW interpolant for 2d array of distances
https://pro.arcgis.com/en/pro-app/help/analysis/geostatistical-analyst/how-inverse-distance-weighted-interpolation-works.htm
Expand Down Expand Up @@ -45,7 +44,12 @@ def get_idw_interpolant(distances:NDArray[np.floating], p:float=2) -> NDArray[np
return weights


def interp2d(data: NDArray[np.floating] | Dataset | DataArray, elem_ids: NDArray[np.integer], weights:NDArray[np.floating] | None=None, shape:Tuple[int,...]|None=None) -> NDArray[np.floating] | Dataset:
def interp2d(
data: np.ndarray | Dataset | DataArray,
elem_ids: np.ndarray,
weights: np.ndarray | None = None,
shape: Tuple[int, ...] | None = None,
) -> np.ndarray | Dataset:
"""interp spatially in data (2d only)
Parameters
Expand Down Expand Up @@ -129,7 +133,11 @@ def interp2d(data: NDArray[np.floating] | Dataset | DataArray, elem_ids: NDArray
return idatitem


def _interp_itemstep(data: NDArray[np.floating], elem_ids: NDArray[np.integer], weights:NDArray[np.floating] | None =None) -> NDArray[np.floating]:
def _interp_itemstep(
data: np.ndarray,
elem_ids: np.ndarray,
weights: np.ndarray | None = None,
) -> np.ndarray:
"""Interpolate a single item and time step
Parameters
Expand Down
62 changes: 34 additions & 28 deletions mikeio/_spectral.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@
import numpy as np
from matplotlib.axes import Axes
from matplotlib.projections.polar import PolarAxes
from numpy.typing import NDArray


def plot_2dspectrum(
spectrum :NDArray[np.floating],
frequencies :NDArray[np.floating],
directions :NDArray[np.floating],
plot_type: str ="contourf",
title:str | None =None,
label:str | None=None,
cmap:str="Reds",
vmin:float | None =1e-5,
vmax:float | None =None,
r_as_periods:bool=True,
rmin:float | None=None,
rmax:float | None=None,
levels: int| Sequence[float] | None=None,
figsize: Tuple[float,float]=(7, 7),
add_colorbar:bool=True,
spectrum: np.ndarray,
frequencies: np.ndarray,
directions: np.ndarray,
plot_type: str = "contourf",
title: str | None = None,
label: str | None = None,
cmap: str = "Reds",
vmin: float | None = 1e-5,
vmax: float | None = None,
r_as_periods: bool = True,
rmin: float | None = None,
rmax: float | None = None,
levels: int | Sequence[float] | None = None,
figsize: Tuple[float, float] = (7, 7),
add_colorbar: bool = True,
) -> Axes:
"""
Plot spectrum in polar coordinates
Expand Down Expand Up @@ -88,14 +87,14 @@ def plot_2dspectrum(
spectrum = np.fliplr(spectrum)

fig = plt.figure(figsize=figsize)
ax = plt.subplot(111, polar=True) # type: ignore
ax = plt.subplot(111, polar=True) # type: ignore
assert isinstance(ax, PolarAxes)
ax.set_theta_direction(-1)
ax.set_theta_zero_location("N")

ddir = dirs[1] - dirs[0]

def is_circular(dir: NDArray[np.floating]) -> bool:
def is_circular(dir: np.ndarray) -> bool:
dir_diff = np.mod(dir[0], 2 * np.pi) - np.mod(dir[-1] + ddir, 2 * np.pi)
return np.abs(dir_diff) < 1e-6

Expand Down Expand Up @@ -124,7 +123,7 @@ def is_circular(dir: NDArray[np.floating]) -> bool:
n_levels = 10
if isinstance(levels, int):
n_levels = levels
levels = np.linspace(vmin, vmax, n_levels) # type: ignore
levels = np.linspace(vmin, vmax, n_levels) # type: ignore

if plot_type != "shaded":
spectrum[spectrum < vmin] = np.nan
Expand All @@ -134,17 +133,19 @@ def is_circular(dir: NDArray[np.floating]) -> bool:
dirs, freq, spectrum.T, levels=levels, cmap=cmap, vmin=vmin, vmax=vmax
)
elif plot_type == "contour":
colorax = ax.contour( # type: ignore
colorax = ax.contour( # type: ignore
dirs, freq, spectrum.T, levels=levels, cmap=cmap, vmin=vmin, vmax=vmax
)
# ax.clabel(colorax, fmt="%1.2f", inline=1, fontsize=9)
if label is not None:
ax.set_title(label)

elif plot_type in ("patch", "shaded", "box"):
shading: Literal['flat', 'nearest', 'gouraud', 'auto'] = "gouraud" if plot_type == "shaded" else "auto"
shading: Literal["flat", "nearest", "gouraud", "auto"] = (
"gouraud" if plot_type == "shaded" else "auto"
)
ax.grid(False) # Remove major grid
colorax = ax.pcolormesh( # type: ignore
colorax = ax.pcolormesh( # type: ignore
dirs,
freq,
spectrum.T,
Expand All @@ -153,14 +154,14 @@ def is_circular(dir: NDArray[np.floating]) -> bool:
vmin=vmin,
vmax=vmax,
)
ax.grid("on") # type: ignore
ax.grid("on") # type: ignore
else:
raise ValueError(
f"plot_type '{plot_type}' not supported (contour, contourf, patch, shaded)"
)

# TODO: optional
ax.set_thetagrids( # type: ignore
ax.set_thetagrids( # type: ignore
[0.0, 45, 90.0, 135, 180.0, 225, 270.0, 315],
labels=["N", "N-E", "E", "S-E", "S", "S-W", "W", "N-W"],
)
Expand All @@ -177,9 +178,9 @@ def is_circular(dir: NDArray[np.floating]) -> bool:
# ax.set_xticks(dfs.directions, minor=True);

if rmin is not None:
ax.set_rmin(rmin) # type: ignore
ax.set_rmin(rmin) # type: ignore
if rmax is not None:
ax.set_rmax(rmax) # type: ignore
ax.set_rmax(rmax) # type: ignore

if add_colorbar:
cbar = fig.colorbar(colorax)
Expand All @@ -194,7 +195,12 @@ def is_circular(dir: NDArray[np.floating]) -> bool:
return ax


def calc_m0_from_spectrum(spec: NDArray[np.floating], f: NDArray[np.floating] | None, dir: NDArray[np.floating] | None =None, tail:bool=True) -> NDArray[np.floating]:
def calc_m0_from_spectrum(
spec: np.ndarray,
f: np.ndarray | None,
dir: np.ndarray | None = None,
tail: bool = True,
) -> np.ndarray:
if f is None:
assert dir is not None
nd = len(dir)
Expand All @@ -215,7 +221,7 @@ def calc_m0_from_spectrum(spec: NDArray[np.floating], f: NDArray[np.floating] |
return m0


def _f_to_df(f: NDArray[np.floating]) -> NDArray[np.floating]:
def _f_to_df(f: np.ndarray) -> np.ndarray:
"""Frequency bins for equidistant or logrithmic frequency axis"""
if np.isclose(np.diff(f).min(), np.diff(f).max()):
# equidistant frequency bins
Expand Down
41 changes: 18 additions & 23 deletions mikeio/dataset/_dataarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@


import numpy as np
from numpy.typing import NDArray, ArrayLike, DTypeLike
import pandas as pd
from mikecore.DfsuFile import DfsuFileType

Expand Down Expand Up @@ -138,12 +137,12 @@ class DataArray:

def __init__(
self,
data: NDArray[np.floating],
data: np.ndarray,
*,
time: pd.DatetimeIndex | str | None = None,
item: ItemInfo | None = None,
geometry: GeometryType = GeometryUndefined(),
zn: NDArray[np.floating] | None = None,
zn: np.ndarray | None = None,
dims: Sequence[str] | None = None,
) -> None:
# TODO: add optional validation validate=True
Expand All @@ -160,14 +159,14 @@ def __init__(
self.plot = self._get_plotter_by_geometry()

@staticmethod
def _parse_data(data: ArrayLike) -> Any: # NDArray[np.floating] | float:
def _parse_data(data: Any) -> Any: # np.ndarray | float:
validation_errors = []
for p in ("shape", "ndim", "dtype"):
if not hasattr(data, p):
validation_errors.append(p)
if len(validation_errors) > 0:
raise TypeError(
"Data must be ArrayLike, e.g. numpy array, but it lacks properties: "
"Data must be np.ndarray, e.g. numpy array, but it lacks properties: "
+ ", ".join(validation_errors)
)
return data
Expand Down Expand Up @@ -307,8 +306,8 @@ def _parse_geometry(

@staticmethod
def _parse_zn(
zn: NDArray[np.floating] | None, geometry: GeometryType, n_timesteps: int
) -> NDArray[np.floating] | None:
zn: np.ndarray | None, geometry: GeometryType, n_timesteps: int
) -> np.ndarray | None:
if zn is not None:
if isinstance(geometry, _GeometryFMLayered):
# TODO: np.squeeze(zn) if n_timesteps=1 ?
Expand Down Expand Up @@ -460,17 +459,17 @@ def ndim(self) -> int:
return self.values.ndim

@property
def dtype(self) -> DTypeLike:
def dtype(self) -> Any:
"""Data-type of the array elements"""
return self.values.dtype

@property
def values(self) -> NDArray[np.floating]:
def values(self) -> np.ndarray:
"""Values as a np.ndarray (equivalent to to_numpy())"""
return self._values

@values.setter
def values(self, value: NDArray[np.floating] | float) -> None:
def values(self, value: np.ndarray | float) -> None:
if np.isscalar(self._values):
if not np.isscalar(value):
raise ValueError("Shape of new data is wrong (should be scalar)")
Expand All @@ -479,7 +478,7 @@ def values(self, value: NDArray[np.floating] | float) -> None:

self._values = value # type: ignore

def to_numpy(self) -> NDArray[np.floating]:
def to_numpy(self) -> np.ndarray:
"""Values as a np.ndarray (equivalent to values)"""
return self._values

Expand Down Expand Up @@ -602,7 +601,7 @@ def _getitem_parse_key(self, key: Any) -> Any:
)
return key

def __setitem__(self, key: Any, value: NDArray[np.floating]) -> None:
def __setitem__(self, key: Any, value: np.ndarray) -> None:
if self._is_boolean_mask(key):
mask = key if isinstance(key, np.ndarray) else key.values
return self._set_by_boolean_mask(self._values, mask, value)
Expand Down Expand Up @@ -1068,7 +1067,7 @@ def interp(

def __dataarray_read_item_time_func(
self, item: int, step: int
) -> Tuple[NDArray[np.floating], float]:
) -> Tuple[np.ndarray, float]:
"Used by _extract_track"
# Ignore item argument
data = self.isel(time=step).to_numpy()
Expand All @@ -1080,7 +1079,7 @@ def extract_track(
self,
track: pd.DataFrame,
method: Literal["nearest", "inverse_distance"] = "nearest",
dtype: DTypeLike = np.float32,
dtype: Any = np.float32,
) -> "Dataset":
"""
Extract data along a moving track
Expand Down Expand Up @@ -1423,7 +1422,7 @@ def ptp(self, axis: int | str = 0, **kwargs: Any) -> "DataArray":
return self.aggregate(axis=axis, func=np.ptp, **kwargs)

def average(
self, weights: NDArray[np.floating], axis: int | str = 0, **kwargs: Any
self, weights: np.ndarray, axis: int | str = 0, **kwargs: Any
) -> "DataArray":
"""Compute the weighted average along the specified axis.
Expand Down Expand Up @@ -1592,12 +1591,10 @@ def aggregate(
)

@overload
def quantile(self, q: float, **kwargs: Any) -> "DataArray":
...
def quantile(self, q: float, **kwargs: Any) -> "DataArray": ...

@overload
def quantile(self, q: Sequence[float], **kwargs: Any) -> "Dataset":
...
def quantile(self, q: Sequence[float], **kwargs: Any) -> "Dataset": ...

def quantile(
self, q: float | Sequence[float], *, axis: int | str = 0, **kwargs: Any
Expand Down Expand Up @@ -1632,12 +1629,10 @@ def quantile(
return self._quantile(q, axis=axis, func=np.quantile, **kwargs)

@overload
def nanquantile(self, q: float, **kwargs: Any) -> "DataArray":
...
def nanquantile(self, q: float, **kwargs: Any) -> "DataArray": ...

@overload
def nanquantile(self, q: Sequence[float], **kwargs: Any) -> "Dataset":
...
def nanquantile(self, q: Sequence[float], **kwargs: Any) -> "Dataset": ...

def nanquantile(
self, q: float | Sequence[float], *, axis: int | str = 0, **kwargs: Any
Expand Down
3 changes: 1 addition & 2 deletions mikeio/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from typing import Iterable, List, Sequence, Tuple, Union

import numpy as np
from numpy.typing import NDArray
import pandas as pd
from mikecore.DfsBuilder import DfsBuilder
from mikecore.DfsFile import (
Expand Down Expand Up @@ -908,7 +907,7 @@ def quantile(
dfs_o.Close()


def _read_item(dfs: DfsFile, item: int, timestep: int) -> NDArray[np.float64]:
def _read_item(dfs: DfsFile, item: int, timestep: int) -> np.ndarray:
"""Read item data from dfs file
Parameters
Expand Down
Loading

0 comments on commit 6ecbdf9

Please sign in to comment.