diff --git a/mikeio/_interpolation.py b/mikeio/_interpolation.py index b8dc84343..1c073b6e3 100644 --- a/mikeio/_interpolation.py +++ b/mikeio/_interpolation.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Tuple, TYPE_CHECKING, overload +from typing import TYPE_CHECKING, overload import numpy as np if TYPE_CHECKING: @@ -49,7 +49,7 @@ def interp2d( data: np.ndarray | DataArray, elem_ids: np.ndarray, weights: np.ndarray | None = None, - shape: Tuple[int, ...] | None = None, + shape: tuple[int, ...] | None = None, ) -> np.ndarray: ... @@ -58,7 +58,7 @@ def interp2d( data: Dataset, elem_ids: np.ndarray, weights: np.ndarray | None = None, - shape: Tuple[int, ...] | None = None, + shape: tuple[int, ...] | None = None, ) -> Dataset: ... @@ -66,7 +66,7 @@ def interp2d( data: Dataset | DataArray | np.ndarray, elem_ids: np.ndarray, weights: np.ndarray | None = None, - shape: Tuple[int, ...] | None = None, + shape: tuple[int, ...] | None = None, ) -> Dataset | np.ndarray: """interp spatially in data (2d only) diff --git a/mikeio/_spectral.py b/mikeio/_spectral.py index c05da274e..b0a4681f1 100644 --- a/mikeio/_spectral.py +++ b/mikeio/_spectral.py @@ -1,6 +1,6 @@ from __future__ import annotations from collections.abc import Sequence -from typing import Literal, Tuple +from typing import Literal import numpy as np from matplotlib.axes import Axes from matplotlib.projections.polar import PolarAxes @@ -20,7 +20,7 @@ def plot_2dspectrum( rmin: float | None = None, rmax: float | None = None, levels: int | Sequence[float] | None = None, - figsize: Tuple[float, float] = (7, 7), + figsize: tuple[float, float] = (7, 7), add_colorbar: bool = True, ) -> Axes: """ diff --git a/mikeio/_time.py b/mikeio/_time.py index 244ce3a68..271b1e73c 100644 --- a/mikeio/_time.py +++ b/mikeio/_time.py @@ -1,7 +1,6 @@ from __future__ import annotations from datetime import datetime from dataclasses import dataclass -from typing import List from collections.abc import Iterable import pandas as pd @@ -18,7 +17,7 @@ def isel( x: ( int | Iterable[int] | str | datetime | pd.DatetimeIndex | slice | None ) = None, - ) -> List[int]: + ) -> list[int]: """Select time steps from a pandas DatetimeIndex Parameters @@ -28,7 +27,7 @@ def isel( Returns ------- - List[int] + list[int] List of indices in the range (0, len(index) Examples -------- diff --git a/mikeio/_track.py b/mikeio/_track.py index b0ab023c2..b38fc773d 100644 --- a/mikeio/_track.py +++ b/mikeio/_track.py @@ -1,7 +1,7 @@ from __future__ import annotations from pathlib import Path from collections.abc import Sequence -from typing import Any, Callable, Tuple +from typing import Any, Callable import numpy as np import pandas as pd @@ -26,7 +26,7 @@ def _extract_track( n_elements: int, method: str, dtype: Any, # TODO DTypeLike? - data_read_func: Callable[[int, int], Tuple[np.ndarray, float]], + data_read_func: Callable[[int, int], tuple[np.ndarray, float]], ) -> Dataset: if not isinstance(geometry, GeometryFM2D): raise NotImplementedError("Only implemented for 2d flexible mesh geometries") diff --git a/mikeio/dataset/_data_plot.py b/mikeio/dataset/_data_plot.py index 678d973b4..26a06b958 100644 --- a/mikeio/dataset/_data_plot.py +++ b/mikeio/dataset/_data_plot.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Any, Tuple, TYPE_CHECKING +from typing import Any, TYPE_CHECKING from matplotlib.figure import Figure import numpy as np @@ -23,7 +23,7 @@ def __init__(self, da: "DataArray") -> None: def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot DataArray according to geometry @@ -57,7 +57,7 @@ def __call__( @staticmethod def _get_ax( - ax: Axes | None = None, figsize: Tuple[float, float] | None = None + ax: Axes | None = None, figsize: tuple[float, float] | None = None ) -> Axes: if ax is None: _, ax = plt.subplots(figsize=figsize) @@ -65,8 +65,8 @@ def _get_ax( @staticmethod def _get_fig_ax( - ax: Axes | None = None, figsize: Tuple[float, float] | None = None - ) -> Tuple[Figure, Axes]: + ax: Axes | None = None, figsize: tuple[float, float] | None = None + ) -> tuple[Figure, Axes]: if ax is None: fig, ax = plt.subplots(figsize=figsize) else: @@ -76,7 +76,7 @@ def _get_fig_ax( def hist( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, title: str | None = None, **kwargs: Any, ) -> Axes: @@ -119,7 +119,7 @@ def _hist(self, ax: Axes, **kwargs: Any) -> Any: def line( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot data as lines (timeseries if time is present)""" @@ -176,7 +176,7 @@ class _DataArrayPlotterGrid1D(_DataArrayPlotter): def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: _, ax = self._get_fig_ax(ax, figsize) @@ -188,7 +188,7 @@ def __call__( def line( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot as spatial lines""" @@ -198,7 +198,7 @@ def line( def timeseries( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot as timeseries""" @@ -210,7 +210,7 @@ def timeseries( def imshow( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot as 2d""" @@ -226,7 +226,7 @@ def imshow( def pcolormesh( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, title: str | None = None, **kwargs: Any, ) -> Axes: @@ -279,7 +279,7 @@ class _DataArrayPlotterGrid2D(_DataArrayPlotter): def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: return self.pcolormesh(ax, figsize, **kwargs) @@ -287,7 +287,7 @@ def __call__( def contour( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, title: str | None = None, **kwargs: Any, ) -> Axes: @@ -316,7 +316,7 @@ def contour( def contourf( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, title: str | None = None, label: str | None = None, **kwargs: Any, @@ -347,7 +347,7 @@ def contourf( def pcolormesh( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, title: str | None = None, label: str | None = None, **kwargs: Any, @@ -375,12 +375,12 @@ def pcolormesh( ax.set_title(title) return ax - def _get_x_y(self) -> Tuple[np.ndarray, np.ndarray]: + def _get_x_y(self) -> tuple[np.ndarray, np.ndarray]: x = self.da.geometry.x y = self.da.geometry.y return x, y - def _get_xn_yn(self) -> Tuple[np.ndarray, np.ndarray]: + def _get_xn_yn(self) -> tuple[np.ndarray, np.ndarray]: xn = self.da.geometry._centers_to_nodes(self.da.geometry.x) yn = self.da.geometry._centers_to_nodes(self.da.geometry.y) return xn, yn @@ -427,7 +427,7 @@ class _DataArrayPlotterFM(_DataArrayPlotter): def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot data as coloured patches""" @@ -437,7 +437,7 @@ def __call__( def patch( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot data as coloured patches @@ -456,7 +456,7 @@ def patch( def contour( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot data as contour lines @@ -475,7 +475,7 @@ def contour( def contourf( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot data as filled contours @@ -494,7 +494,7 @@ def contourf( def mesh( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot mesh only @@ -511,7 +511,7 @@ def mesh( def outline( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot domain outline (using the boundary_polylines property) @@ -582,7 +582,7 @@ class _DataArrayPlotterFMVerticalColumn(_DataArrayPlotter): def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: ax = self._get_ax(ax, figsize) @@ -591,7 +591,7 @@ def __call__( def line( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, extrapolate: bool = True, **kwargs: Any, ) -> Axes: @@ -636,7 +636,7 @@ def _line( def pcolormesh( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, title: str | None = None, **kwargs: Any, ) -> Axes: @@ -679,7 +679,7 @@ class _DataArrayPlotterFMVerticalProfile(_DataArrayPlotter): def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: ax = self._get_ax(ax, figsize) @@ -712,7 +712,7 @@ class _DataArrayPlotterPointSpectrum(_DataArrayPlotter): def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: # ax = self._get_ax(ax, figsize) @@ -740,7 +740,7 @@ def contourf(self, **kwargs: Any) -> Axes: def _plot_freqspectrum( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: ax = self._plot_1dspectrum(self.da.frequencies, ax, figsize, **kwargs) # type: ignore @@ -751,7 +751,7 @@ def _plot_freqspectrum( def _plot_dirspectrum( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: ax = self._plot_1dspectrum(self.da.directions, ax, figsize, **kwargs) # type: ignore @@ -764,7 +764,7 @@ def _plot_1dspectrum( self, x_values: np.ndarray, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: ax = self._get_ax(ax, figsize) @@ -832,7 +832,7 @@ def __init__(self, ds: Dataset) -> None: self.ds = ds def __call__( - self, figsize: Tuple[float, float] | None = None, **kwargs: Any + self, figsize: tuple[float, float] | None = None, **kwargs: Any ) -> Axes: """Plot multiple DataArrays as time series (only possible dfs0-type data)""" if self.ds.dims == ("time",): @@ -845,8 +845,8 @@ def __call__( @staticmethod def _get_fig_ax( - ax: Axes | None = None, figsize: Tuple[float, float] | None = None - ) -> Tuple[Figure, Axes]: + ax: Axes | None = None, figsize: tuple[float, float] | None = None + ) -> tuple[Figure, Axes]: import matplotlib.pyplot as plt if ax is None: @@ -860,7 +860,7 @@ def scatter( x: str | int, y: str | int, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot data from two DataArrays against each other in a scatter plot diff --git a/mikeio/dataset/_data_utils.py b/mikeio/dataset/_data_utils.py index 939bcabe8..b04e2550d 100644 --- a/mikeio/dataset/_data_utils.py +++ b/mikeio/dataset/_data_utils.py @@ -1,7 +1,6 @@ from __future__ import annotations from datetime import datetime import re -from typing import List from collections.abc import Iterable, Sized import pandas as pd @@ -23,7 +22,7 @@ def _n_selected_timesteps(x: Sized, k: slice | Sized) -> int: def _get_time_idx_list( time: pd.DatetimeIndex, steps: int | Iterable[int] | str | datetime | pd.DatetimeIndex | slice, -) -> List[int] | slice: +) -> list[int] | slice: """Find list of idx in DatetimeIndex""" # indexing with a slice needs to be handled differently, since slicing returns a view diff --git a/mikeio/dataset/_dataarray.py b/mikeio/dataset/_dataarray.py index 597925aec..40e372d86 100644 --- a/mikeio/dataset/_dataarray.py +++ b/mikeio/dataset/_dataarray.py @@ -18,7 +18,6 @@ TYPE_CHECKING, overload, Callable, - Tuple, ) @@ -196,7 +195,7 @@ def _parse_data(data: Any) -> Any: # np.ndarray | float: def _parse_dims( self, dims: Sequence[str] | None, geometry: GeometryType - ) -> Tuple[str, ...]: + ) -> tuple[str, ...]: if dims is None: return self._guess_dims(self.ndim, self.shape, self.n_timesteps, geometry) else: @@ -212,8 +211,8 @@ def _parse_dims( @staticmethod def _guess_dims( - ndim: int, shape: Tuple[int, ...], n_timesteps: int, geometry: GeometryType - ) -> Tuple[str, ...]: + ndim: int, shape: tuple[int, ...], n_timesteps: int, geometry: GeometryType + ) -> tuple[str, ...]: # This is not very robust, but is probably a reasonable guess time_is_first = (n_timesteps > 1) or (shape[0] == 1 and n_timesteps == 1) dims = ["time"] if time_is_first else [] @@ -253,7 +252,7 @@ def _parse_item(item: ItemInfo | str | EUMType | None) -> ItemInfo: @staticmethod def _parse_geometry( - geometry: Any, dims: Tuple[str, ...], shape: Tuple[int, ...] + geometry: Any, dims: tuple[str, ...], shape: tuple[int, ...] ) -> Any: if len(dims) > 1 and ( geometry is None or isinstance(geometry, GeometryUndefined) @@ -881,7 +880,7 @@ def interp( y: float | None = None, z: float | None = None, n_nearest: int = 3, - interpolant: Tuple[Any, Any] | None = None, + interpolant: tuple[Any, Any] | None = None, **kwargs: Any, ) -> "DataArray": """Interpolate data in time and space @@ -997,7 +996,7 @@ def interp( def __dataarray_read_item_time_func( self, item: int, step: int - ) -> Tuple[np.ndarray, float]: + ) -> tuple[np.ndarray, float]: "Used by _extract_track" # Ignore item argument data = self.isel(time=step).to_numpy() @@ -1143,7 +1142,7 @@ def interp_na(self, axis: str = "time", **kwargs: Any) -> "DataArray": def interp_like( self, other: "DataArray" | Grid2D | GeometryFM2D | pd.DatetimeIndex, - interpolant: Tuple[Any, Any] | None = None, + interpolant: tuple[Any, Any] | None = None, **kwargs: Any, ) -> "DataArray": """Interpolate in space (and in time) to other geometry (and time axis) @@ -2005,10 +2004,10 @@ def _parse_time(time: Any) -> pd.DatetimeIndex: @staticmethod def _parse_axis( - data_shape: Tuple[int, ...], - dims: Tuple[str, ...], - axis: int | Tuple[int, ...] | str | None, - ) -> int | Tuple[int, ...]: + data_shape: tuple[int, ...], + dims: tuple[str, ...], + axis: int | tuple[int, ...] | str | None, + ) -> int | tuple[int, ...]: # TODO change to return tuple always # axis = 0 if axis == "time" else axis if (axis == "spatial") or (axis == "space"): diff --git a/mikeio/dataset/_dataset.py b/mikeio/dataset/_dataset.py index 7f00df684..45395e0bc 100644 --- a/mikeio/dataset/_dataset.py +++ b/mikeio/dataset/_dataset.py @@ -6,16 +6,13 @@ from typing import ( Iterable, Iterator, - List, Literal, Mapping, MutableMapping, Sequence, - Tuple, Any, overload, Hashable, - Set, TYPE_CHECKING, Callable, ) @@ -93,7 +90,7 @@ def __init__( items: Sequence[ItemInfo] | None = None, geometry: Any = None, zn: NDArray[np.floating] | None = None, - dims: Tuple[str, ...] | None = None, + dims: tuple[str, ...] | None = None, validate: bool = True, dt: float = 1.0, ): @@ -167,7 +164,7 @@ def _init_from_DataArrays( self._check_all_different_ids(list(data_vars.values())) # TODO is it necessary to keep track of item names? - self.__itemattr: Set[str] = set() + self.__itemattr: set[str] = set() for key, value in data_vars.items(): self._set_name_attr(key, value) @@ -180,7 +177,7 @@ def values(self) -> None: ) @staticmethod - def _modify_list(lst: Iterable[str]) -> List[str]: + def _modify_list(lst: Iterable[str]) -> list[str]: modified_list = [] count_dict = {} @@ -201,7 +198,7 @@ def _modify_list(lst: Iterable[str]) -> List[str]: @staticmethod def _parse_items( items: None | Sequence[ItemInfo | EUMType | str], n_items_data: int - ) -> List[ItemInfo]: + ) -> list[ItemInfo]: if items is None: # default Undefined items item_infos = [ItemInfo(f"Item_{j+1}") for j in range(n_items_data)] @@ -260,7 +257,7 @@ def _validate_item_names_and_keys( return data_map @staticmethod - def _unique_item_names(das: Sequence[DataArray]) -> List[str]: + def _unique_item_names(das: Sequence[DataArray]) -> list[str]: item_names = [da.name for da in das] if len(set(item_names)) != len(item_names): raise ValueError( @@ -374,7 +371,7 @@ def n_timesteps(self) -> int: return len(self.time) @property - def items(self) -> List[ItemInfo]: + def items(self) -> list[ItemInfo]: """ItemInfo for each of the DataArrays as a list""" return [x.item for x in self] @@ -384,7 +381,7 @@ def n_items(self) -> int: return len(self._data_vars) @property - def names(self) -> List[str]: + def names(self) -> list[str]: """Name of each of the DataArrays as a list""" return [da.name for da in self] @@ -397,7 +394,7 @@ def ndim(self) -> int: return self[0].ndim @property - def dims(self) -> Tuple[str, ...]: + def dims(self) -> tuple[str, ...]: """Named array dimensions of each DataArray""" return self[0].dims @@ -446,7 +443,7 @@ def dropna(self) -> "Dataset": if not self[0]._has_time_axis: # type: ignore raise ValueError("Not available if no time axis!") - all_index: List[int] = [] + all_index: list[int] = [] for i in range(self.n_items): x = self[i].to_numpy() @@ -502,7 +499,7 @@ def create_empty_data( n_items: int = 1, n_timesteps: int = 1, n_elements: int | None = None, - shape: Tuple[int, ...] | None = None, + shape: tuple[int, ...] | None = None, ) -> list: data = [] if shape is None: @@ -751,7 +748,7 @@ def _multi_indexing_attempted(self, key: Any) -> bool: # TODO change this to return a single type def _key_to_str(self, key: Any) -> Any: - """Translate item selection key to str (or List[str])""" + """Translate item selection key to str (or list[str])""" if isinstance(key, str): return key if isinstance(key, int): @@ -981,7 +978,7 @@ def interp( def __dataset_read_item_time_func( self, item: int, step: int - ) -> Tuple[np.ndarray, float]: + ) -> tuple[np.ndarray, float]: "Used by _extract_track" data = self[item].isel(time=step).to_numpy() @@ -1456,7 +1453,7 @@ def _quantile(self, q, *, axis=0, func=np.quantile, **kwargs) -> "Dataset": # t ) return Dataset([da], validate=False) else: - res: List[DataArray] = [] + res: list[DataArray] = [] for quantile in q: qd = self._quantile(q=quantile, axis=axis, func=func, **kwargs)[0] assert isinstance(qd, DataArray) @@ -2065,9 +2062,9 @@ def from_polars( def _parse_items( column_names: Sequence[str], items: Mapping[str, ItemInfo] | Sequence[ItemInfo] | ItemInfo | None = None, -) -> List[ItemInfo]: +) -> list[ItemInfo]: if items is None: - item_list: List[ItemInfo] = [ItemInfo(name) for name in column_names] + item_list: list[ItemInfo] = [ItemInfo(name) for name in column_names] elif isinstance(items, ItemInfo): eum_type = items.type eum_unit = items.unit diff --git a/mikeio/dfs/_dfs.py b/mikeio/dfs/_dfs.py index 28e6dc830..e59c8642a 100644 --- a/mikeio/dfs/_dfs.py +++ b/mikeio/dfs/_dfs.py @@ -4,7 +4,7 @@ from abc import abstractmethod from dataclasses import dataclass from datetime import datetime -from typing import Any, List, Tuple, Sequence +from typing import Any, Sequence import numpy as np import pandas as pd from tqdm import tqdm @@ -30,8 +30,8 @@ class DfsHeader: n_timesteps: int start_time: datetime dt: float - coordinates: Tuple[str, float, float, float] - items: List[ItemInfo] + coordinates: tuple[str, float, float, float] + items: list[ItemInfo] def _read_item_time_step( @@ -39,14 +39,14 @@ def _read_item_time_step( dfs: DfsFile, filename: str, time: pd.DatetimeIndex, - item_numbers: List[int], + item_numbers: list[int], deletevalue: float, - shape: Tuple[int, ...], + shape: tuple[int, ...], item: int, it: int, error_bad_data: bool = True, fill_bad_data_value: float = np.nan, -) -> Tuple[DfsFile, np.ndarray, float]: +) -> tuple[DfsFile, np.ndarray, float]: itemdata = dfs.ReadItemTimeStep(item_numbers[item] + 1, it) t = itemdata.Time if itemdata is not None: @@ -65,8 +65,8 @@ def _read_item_time_step( def _fuzzy_item_search( - *, dfsItemInfo: List[DfsDynamicItemInfo], search: str, start_idx: int = 0 -) -> List[int]: + *, dfsItemInfo: list[DfsDynamicItemInfo], search: str, start_idx: int = 0 +) -> list[int]: import fnmatch names = [info.Name for info in dfsItemInfo] @@ -79,10 +79,10 @@ def _fuzzy_item_search( def _valid_item_numbers( - dfsItemInfo: List[DfsDynamicItemInfo], + dfsItemInfo: list[DfsDynamicItemInfo], items: str | int | Sequence[int | str] | None = None, ignore_first: bool = False, -) -> List[int]: +) -> list[int]: start_idx = 1 if ignore_first else 0 n_items_file = len(dfsItemInfo) - start_idx if items is None: @@ -90,7 +90,7 @@ def _valid_item_numbers( # Handling scalar and sequences is a bit tricky - item_numbers: List[int] = [] + item_numbers: list[int] = [] # check if items is a scalar (int or str) if isinstance(items, (int, str)): @@ -126,7 +126,7 @@ def _valid_item_numbers( def _valid_timesteps( dfsFileInfo: DfsFileInfo, time_steps: int | Sequence[int] | str | slice | None -) -> Tuple[bool, List[int]]: +) -> tuple[bool, list[int]]: time_axis = dfsFileInfo.TimeAxis single_time_selected = False @@ -179,8 +179,8 @@ def _valid_timesteps( def _item_numbers_by_name( - dfsItemInfo: DfsDynamicItemInfo, item_names: List[str], ignore_first: bool = False -) -> List[int]: + dfsItemInfo: DfsDynamicItemInfo, item_names: list[str], ignore_first: bool = False +) -> list[int]: """Utility function to find item numbers Parameters @@ -213,15 +213,15 @@ def _item_numbers_by_name( def _get_item_info( - dfsItemInfo: List[DfsDynamicItemInfo], - item_numbers: List[int] | None = None, + dfsItemInfo: list[DfsDynamicItemInfo], + item_numbers: list[int] | None = None, ignore_first: bool = False, ) -> ItemInfoList: """Read DFS ItemInfo for specific item numbers Parameters ---------- - dfsItemInfo : List[DfsDynamicItemInfo] + dfsItemInfo : list[DfsDynamicItemInfo] item_numbers : list[int], optional Item numbers to read, by default all items are read ignore_first : bool, optional @@ -375,7 +375,7 @@ def read( single_time_selected, time_steps = _valid_timesteps(self._dfs.FileInfo, time) nt = len(time_steps) if not single_time_selected else 1 - shape: Tuple[int, ...] + shape: tuple[int, ...] if self._ndim == 1: shape = (nt, self.nx) # type: ignore @@ -387,7 +387,7 @@ def read( if single_time_selected and not keepdims: shape = shape[1:] - data_list: List[np.ndarray] = [ + data_list: list[np.ndarray] = [ np.ndarray(shape=shape, dtype=dtype) for _ in range(n_items) ] @@ -429,7 +429,7 @@ def read( def _open(self) -> None: raise NotImplementedError("Should be implemented by subclass") - def _get_item_info(self, item_numbers: Sequence[int]) -> List[ItemInfo]: + def _get_item_info(self, item_numbers: Sequence[int]) -> list[ItemInfo]: """Read DFS ItemInfo Parameters @@ -468,7 +468,7 @@ def n_items(self) -> int: return len(self.items) @property - def items(self) -> List[ItemInfo]: + def items(self) -> list[ItemInfo]: "List of items" return self._items @@ -531,7 +531,7 @@ def is_geo(self) -> bool: @property @abstractmethod - def shape(self) -> Tuple[int, ...]: + def shape(self) -> tuple[int, ...]: """Shape of the data array""" pass @@ -539,7 +539,7 @@ def _validate_no_orientation_in_geo(self) -> None: if self.is_geo and abs(self._orientation) > 1e-6: raise ValueError("Orientation is not supported for LONG/LAT coordinates") - def _origin_and_orientation_in_CRS(self) -> Tuple[Any, float]: + def _origin_and_orientation_in_CRS(self) -> tuple[Any, float]: """Project origin and orientation to projected CRS (if not LONG/LAT)""" if self.is_geo: origin = self._longitude, self._latitude diff --git a/mikeio/dfs/_dfs2.py b/mikeio/dfs/_dfs2.py index f656c3ec8..8c0174d52 100644 --- a/mikeio/dfs/_dfs2.py +++ b/mikeio/dfs/_dfs2.py @@ -1,7 +1,7 @@ from __future__ import annotations from copy import deepcopy from pathlib import Path -from typing import Any, List, Literal, Tuple +from typing import Any, Literal from collections.abc import Sequence import numpy as np @@ -148,7 +148,7 @@ def read( *, items: str | int | Sequence[str | int] | None = None, time: int | str | slice | None = None, - area: Tuple[float, float, float, float] | None = None, + area: tuple[float, float, float, float] | None = None, keepdims: bool = False, dtype: Any = np.float32, ) -> Dataset: @@ -183,7 +183,7 @@ def read( single_time_selected, time_steps = _valid_timesteps(self._dfs.FileInfo, time) nt = len(time_steps) if not single_time_selected else 1 - shape: Tuple[int, ...] + shape: tuple[int, ...] if area is not None: take_subset = True @@ -198,7 +198,7 @@ def read( if single_time_selected and not keepdims: shape = shape[1:] - data_list: List[np.ndarray] = [ + data_list: list[np.ndarray] = [ np.ndarray(shape=shape, dtype=dtype) for _ in range(n_items) ] @@ -226,7 +226,7 @@ def read( time = pd.to_datetime(t_seconds, unit="s", origin=self.start_time) - dims: Tuple[str, ...] + dims: tuple[str, ...] if single_time_selected and not keepdims: dims = ("y", "x") @@ -304,7 +304,7 @@ def dy(self) -> float: return self.geometry.dy @property - def shape(self) -> Tuple[int, ...]: + def shape(self) -> tuple[int, ...]: """Tuple with number of values in the t-, y-, x-direction""" return (self._n_timesteps, self.geometry.ny, self.geometry.nx) diff --git a/mikeio/dfs/_dfs3.py b/mikeio/dfs/_dfs3.py index bbf0a9979..90055a282 100644 --- a/mikeio/dfs/_dfs3.py +++ b/mikeio/dfs/_dfs3.py @@ -1,7 +1,7 @@ from __future__ import annotations from pathlib import Path from collections.abc import Sequence -from typing import Any, Tuple +from typing import Any import numpy as np import pandas as pd @@ -150,7 +150,7 @@ def read( *, items: str | int | Sequence[str | int] | None = None, time: int | str | slice | None = None, - area: Tuple[float, float, float, float] | None = None, + area: tuple[float, float, float, float] | None = None, layers: str | int | Sequence[int] | None = None, keepdims: bool = False, dtype: Any = np.float32, @@ -205,8 +205,8 @@ def read( layers = -1 layers = None if layers is None else np.atleast_1d(layers) - dims: Tuple[str, ...] - shape: Tuple[int, ...] + dims: tuple[str, ...] + shape: tuple[int, ...] nzl = nz if layers is None else len(layers) if nzl == 1 and (not keepdims): @@ -327,5 +327,5 @@ def dz(self) -> float: return self._dz @property - def shape(self) -> Tuple[int, int, int, int]: + def shape(self) -> tuple[int, int, int, int]: return (self._n_timesteps, self._nz, self._ny, self._nx) diff --git a/mikeio/dfsu/_dfsu.py b/mikeio/dfsu/_dfsu.py index e2fc3ec65..292399fa7 100644 --- a/mikeio/dfsu/_dfsu.py +++ b/mikeio/dfsu/_dfsu.py @@ -3,7 +3,7 @@ from datetime import datetime from pathlib import Path -from typing import Any, Literal, Sequence, Tuple +from typing import Any, Literal, Sequence import numpy as np import pandas as pd @@ -394,7 +394,7 @@ def read( items: str | int | Sequence[str | int] | None = None, time: int | str | slice | None = None, elements: Sequence[int] | np.ndarray | None = None, - area: Tuple[float, float, float, float] | None = None, + area: tuple[float, float, float, float] | None = None, x: float | None = None, y: float | None = None, keepdims: bool = False, @@ -461,7 +461,7 @@ def read( data_list = [] - shape: Tuple[int, ...] + shape: tuple[int, ...] t_rel = np.zeros(len(time_steps)) @@ -505,7 +505,7 @@ def read( dfs.Close() - dims: Tuple[str, ...] + dims: tuple[str, ...] dims = ("time", "element") @@ -555,7 +555,7 @@ def append(self, ds: Dataset, validate: bool = True) -> None: def _parse_geometry_sel( self, - area: Tuple[float, float, float, float] | None, + area: tuple[float, float, float, float] | None, x: float | None, y: float | None, ) -> np.ndarray | None: @@ -644,7 +644,7 @@ def get_overset_grid( def _dfs_read_item_time_func( self, item: int, step: int - ) -> Tuple[np.ndarray, pd.Timestamp]: + ) -> tuple[np.ndarray, pd.Timestamp]: dfs = DfsuFile.Open(self._filename) itemdata = dfs.ReadItemTimeStep(item + 1, step) diff --git a/mikeio/dfsu/_layered.py b/mikeio/dfsu/_layered.py index 3182829e8..face729a9 100644 --- a/mikeio/dfsu/_layered.py +++ b/mikeio/dfsu/_layered.py @@ -1,6 +1,6 @@ from __future__ import annotations from pathlib import Path -from typing import Any, Sequence, Tuple, TYPE_CHECKING +from typing import Any, Sequence, TYPE_CHECKING from matplotlib.axes import Axes import numpy as np @@ -203,7 +203,7 @@ def read( items: str | int | Sequence[str | int] | None = None, time: int | str | slice | None = None, elements: Sequence[int] | None = None, - area: Tuple[float, float, float, float] | None = None, + area: tuple[float, float, float, float] | None = None, x: float | None = None, y: float | None = None, z: float | None = None, diff --git a/mikeio/dfsu/_spectral.py b/mikeio/dfsu/_spectral.py index 08065dd4f..3a0279cc0 100644 --- a/mikeio/dfsu/_spectral.py +++ b/mikeio/dfsu/_spectral.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Sequence, Sized, Tuple, Any +from typing import Sequence, Sized, Any from pathlib import Path import numpy as np @@ -199,11 +199,11 @@ def directions(self) -> np.ndarray | None: def _get_spectral_data_shape( self, n_steps: int, elements: Sized | None, dfsu_type: DfsuFileType - ) -> Tuple[Tuple[int, ...], Tuple[int, ...], Tuple[str, ...]]: + ) -> tuple[tuple[int, ...], tuple[int, ...], tuple[str, ...]]: dims = [] if n_steps == 1 else ["time"] n_freq = self.geometry.n_frequencies n_dir = self.geometry.n_directions - shape: Tuple[int, ...] = (n_dir, n_freq) + shape: tuple[int, ...] = (n_dir, n_freq) if n_dir == 0: shape = (n_freq,) elif n_freq == 0: @@ -242,7 +242,7 @@ def read( time: int | str | slice | None = None, elements: Sequence[int] | np.ndarray | None = None, nodes: Sequence[int] | np.ndarray | None = None, - area: Tuple[float, float, float, float] | None = None, + area: tuple[float, float, float, float] | None = None, x: float | None = None, y: float | None = None, keepdims: bool = False, @@ -369,7 +369,7 @@ def read( def _parse_geometry_sel( self, - area: Tuple[float, float, float, float] | None, + area: tuple[float, float, float, float] | None, x: float | None, y: float | None, ) -> np.ndarray | None: @@ -423,7 +423,7 @@ def _parse_elements_nodes( self, elements: Sequence[int] | np.ndarray | None, nodes: Sequence[int] | np.ndarray | None, - ) -> Tuple[Any, Any]: + ) -> tuple[Any, Any]: if self._type == DfsuFileType.DfsuSpectral0D: if elements is not None or nodes is not None: raise ValueError( diff --git a/mikeio/generic.py b/mikeio/generic.py index 770047228..7cf0e29aa 100644 --- a/mikeio/generic.py +++ b/mikeio/generic.py @@ -6,7 +6,7 @@ from datetime import datetime, timedelta from shutil import copyfile from collections.abc import Iterable, Sequence -from typing import Union, List, Tuple +from typing import Union import numpy as np @@ -89,7 +89,7 @@ def chunk_end(self, start: int) -> int: @staticmethod def from_dfs( - dfs: DfsFile, item_numbers: List[int], buffer_size: float + dfs: DfsFile, item_numbers: list[int], buffer_size: float ) -> "_ChunkInfo": """Calculate chunk info based on # of elements in dfs file and selected buffer size""" @@ -225,7 +225,7 @@ def scale( value to add to all items, default 0.0 factor: float, optional value to multiply to all items, default 1.0 - items: List[str] or List[int], optional + items: list[str] or list[int], optional Process only selected items, by number (0-based) or name, by default: all """ infilename = str(infilename) @@ -275,7 +275,7 @@ def fill_corrupt( full path to the output file fill_value: float, optional value to use where data is corrupt, default delete value - items: List[str] or List[int], optional + items: list[str] or list[int], optional Process only selected items, by number (0-based) or name, by default: all """ dfs_i = DfsFileFactory.DfsGenericOpen(infilename) @@ -643,7 +643,7 @@ def _parse_start_end( time_axis: TimeAxis, start: int | float | str | datetime, end: int | float | str | datetime, -) -> Tuple[datetime | None, int, float, int, float]: # TODO better return type +) -> tuple[datetime | None, int, float, int, float]: # TODO better return type """Helper function for parsing start and end arguments""" n_time_steps = time_axis.NumberOfTimeSteps file_start_datetime = time_axis.StartDateTime @@ -820,7 +820,7 @@ def quantile( q: array_like of float Quantile or sequence of quantiles to compute, which must be between 0 and 1 inclusive. - items: List[str] or List[int], optional + items: list[str] or list[int], optional Process only selected items, by number (0-based) or name, by default: all skipna : bool, optional exclude NaN/delete values when computing the result, default True @@ -943,20 +943,20 @@ def _read_item(dfs: DfsFile, item: int, timestep: int) -> np.ndarray: def _get_repeated_items( - items_in: List[DfsDynamicItemInfo], prefixes: List[str] -) -> List[ItemInfo]: + items_in: list[DfsDynamicItemInfo], prefixes: list[str] +) -> list[ItemInfo]: """Create new items by repeating the items in items_in with the prefixes Parameters ---------- - items_in : List[DfsDynamicItemInfo] + items_in : list[DfsDynamicItemInfo] List of items to be repeated - prefixes : List[str] + prefixes : list[str] List of prefixes to be added to the items Returns ------- - List[ItemInfo] + list[ItemInfo] List of new items """ item_numbers = _valid_item_numbers(items_in) diff --git a/mikeio/pfs/__init__.py b/mikeio/pfs/__init__.py index bffd5458a..98f75939b 100644 --- a/mikeio/pfs/__init__.py +++ b/mikeio/pfs/__init__.py @@ -1,12 +1,12 @@ from __future__ import annotations from pathlib import Path -from typing import Dict, TextIO +from typing import TextIO from ._pfsdocument import PfsDocument from ._pfssection import PfsNonUniqueList, PfsSection def read_pfs( - filename: str | Path | TextIO | Dict | PfsSection, + filename: str | Path | TextIO | dict | PfsSection, encoding: str = "cp1252", unique_keywords: bool = False, ) -> PfsDocument: diff --git a/mikeio/pfs/_pfsdocument.py b/mikeio/pfs/_pfsdocument.py index b2b557ae1..75159ed5a 100644 --- a/mikeio/pfs/_pfsdocument.py +++ b/mikeio/pfs/_pfsdocument.py @@ -5,7 +5,7 @@ from collections.abc import Mapping, Sequence from datetime import datetime from pathlib import Path -from typing import Any, Callable, Dict, List, TextIO, Tuple +from typing import Any, Callable, TextIO import yaml @@ -128,15 +128,15 @@ def _to_nonunique_key_dict(keys: Any, vals: Any) -> dict[Any, Any]: data[key] = val return data - def keys(self) -> List[str]: # type: ignore + def keys(self) -> list[str]: # type: ignore """Return a list of the PfsDocument's keys (target names)""" return [k for k, _ in self.items()] - def values(self) -> List[PfsSection | PfsNonUniqueList]: # type: ignore + def values(self) -> list[PfsSection | PfsNonUniqueList]: # type: ignore """Return a list of the PfsDocument's values (targets).""" return [v for _, v in self.items()] - def items(self) -> List[Tuple[str, PfsSection | PfsNonUniqueList]]: # type: ignore + def items(self) -> list[tuple[str, PfsSection | PfsNonUniqueList]]: # type: ignore """Return a new view of the PfsDocument's items ((key, value) pairs)""" return [(k, v) for k, v in self.__dict__.items() if k not in self._ALIAS_LIST] @@ -147,7 +147,7 @@ def to_dict(self) -> dict: return d @staticmethod - def _unravel_items(items: Callable) -> Tuple[List, List]: + def _unravel_items(items: Callable) -> tuple[list, list]: rkeys = [] rvals = [] for k, v in items(): @@ -161,7 +161,7 @@ def _unravel_items(items: Callable) -> Tuple[List, List]: return rkeys, rvals @property - def targets(self) -> List[PfsSection]: + def targets(self) -> list[PfsSection]: """List of targets (root sections)""" _, rvals = self._unravel_items(self.items) return rvals @@ -177,7 +177,7 @@ def is_unique(self) -> bool: return len(self.keys()) == len(self.names) @property - def names(self) -> List[str]: + def names(self) -> list[str]: """Names of the targets (root sections) as a list""" rkeys, _ = self._unravel_items(self.items) return rkeys @@ -194,7 +194,7 @@ def _read_pfs_file( filename: str | Path | TextIO, encoding: str | None, unique_keywords: bool = False, - ) -> Tuple[List[str], List[PfsSection]]: + ) -> tuple[list[str], list[PfsSection]]: try: yml = self._pfs2yaml(filename, encoding) target_list = parse_yaml_preserving_duplicates(yml, unique_keywords) @@ -214,10 +214,10 @@ def _parse_non_file_input( Mapping[str | PfsSection, Any] | PfsSection | Sequence[PfsSection] - | Sequence[Dict] + | Sequence[dict] ), names: Sequence[str] | None = None, - ) -> Tuple[Sequence[str], List[PfsSection]]: + ) -> tuple[Sequence[str], list[PfsSection]]: """dict/PfsSection or lists of these can be parsed""" if names is None: assert isinstance(input, Mapping), "input must be a mapping" @@ -300,7 +300,7 @@ def _pfs2yaml( return "\n".join(output) - def _parse_line(self, line: str, level: int = 0) -> Tuple[str, int]: + def _parse_line(self, line: str, level: int = 0) -> tuple[str, int]: section_header = False s = line.strip() s = re.sub(r"\s*//.*", "", s) # remove comments @@ -360,7 +360,7 @@ def _parse_param(self, value: str) -> str: _COMMA_MATCHER = re.compile(r",(?=(?:[^\"']*[\"'][^\"']*[\"'])*[^\"']*$)") - def _split_line_by_comma(self, s: str) -> List[str]: + def _split_line_by_comma(self, s: str) -> list[str]: return self._COMMA_MATCHER.split(s) def _parse_token(self, token: str, context: str = "") -> str: diff --git a/mikeio/pfs/_pfssection.py b/mikeio/pfs/_pfssection.py index 9cdb0a709..ce717d0b8 100644 --- a/mikeio/pfs/_pfssection.py +++ b/mikeio/pfs/_pfssection.py @@ -5,9 +5,7 @@ from typing import ( Any, Callable, - Dict, ItemsView, - List, Mapping, MutableMapping, Sequence, @@ -16,7 +14,7 @@ import pandas as pd -def _merge_dict(a: Dict[str, Any], b: Mapping[str, Any]) -> Dict[str, Any]: +def _merge_dict(a: dict[str, Any], b: Mapping[str, Any]) -> dict[str, Any]: """merges dict b into dict a; handling non-unique keys""" for key in b: if key in a: @@ -241,7 +239,7 @@ def _find_patterns_generator( keypat: str | None = None, parampat: Any = None, secpat: str | None = None, - keylist: List[str] | None = None, + keylist: list[str] | None = None, case: bool = False, ) -> Any: """Look for patterns in either keys, params or sections""" @@ -295,8 +293,8 @@ def copy(self) -> "PfsSection": """Return a copy of the PfsSection.""" return PfsSection(self.to_dict()) - def _to_txt_lines(self) -> List[str]: - lines: List[str] = [] + def _to_txt_lines(self) -> list[str]: + lines: list[str] = [] self._write_with_func(lines.append, newline="") return lines @@ -321,7 +319,7 @@ def _write_with_func( func(f"{lvl_prefix * level}[{k}]{newline}") func(f"{lvl_prefix * level}EndSect // {k}{newline}{newline}") - elif isinstance(v, List) and any( + elif isinstance(v, list) and any( isinstance(subv, PfsSection) for subv in v ): # duplicate sections diff --git a/mikeio/spatial/_FM_geometry.py b/mikeio/spatial/_FM_geometry.py index a19972907..1e18bb503 100644 --- a/mikeio/spatial/_FM_geometry.py +++ b/mikeio/spatial/_FM_geometry.py @@ -3,12 +3,10 @@ from functools import cached_property from pathlib import Path from typing import ( - List, Any, Literal, Sequence, Sized, - Tuple, TYPE_CHECKING, ) @@ -61,7 +59,7 @@ def __init__(self, geometry: GeometryFM2D | GeometryFM3D) -> None: def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot bathymetry as coloured patches""" @@ -72,7 +70,7 @@ def __call__( def contour( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot bathymetry as contour lines""" @@ -83,7 +81,7 @@ def contour( def contourf( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: """Plot bathymetry as filled contours""" @@ -94,7 +92,7 @@ def contourf( @staticmethod def _get_ax( ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, ) -> Axes: import matplotlib.pyplot as plt @@ -125,7 +123,7 @@ def _plot_FM_map(self, ax: Axes, **kwargs: Any) -> Axes: def mesh( self, title: str = "Mesh", - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, ax: Axes | None = None, ) -> Axes: """Plot mesh only""" @@ -151,7 +149,7 @@ def mesh( def outline( self, title: str = "Outline", - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, ax: Axes | None = None, ) -> Axes: """Plot domain outline (using the boundary_polylines property)""" @@ -173,7 +171,7 @@ def outline( def boundary_nodes( self, boundary_names: Sequence[str] | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, ax: Axes | None = None, ) -> Axes: """Plot mesh boundary nodes and their code values""" @@ -228,7 +226,7 @@ def __init__( self, *, node_coordinates: np.ndarray, - element_table: np.ndarray | List[Sequence[int]] | List[np.ndarray], + element_table: np.ndarray | list[Sequence[int]] | list[np.ndarray], projection: str, codes: np.ndarray | None = None, dfsu_type: DfsuFileType, @@ -294,7 +292,7 @@ def _calc_element_coordinates(self, maxnodes: int = 4) -> np.ndarray: def _check_elements( self, - element_table: np.ndarray | List[Sequence[int]] | List[np.ndarray], + element_table: np.ndarray | list[Sequence[int]] | list[np.ndarray], element_ids: np.ndarray | None = None, validate: bool = True, ) -> tuple[Any, Any]: @@ -334,7 +332,7 @@ def _reindex(self) -> None: self._element_ids = new_element_ids @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: return ("element",) @property @@ -458,7 +456,7 @@ def _area_is_bbox(area: Sized) -> bool: return isinstance(area, Sized) and len(area) == 4 @staticmethod - def _area_is_polygon(area: Sequence[Tuple[float, float]] | Sequence[float]) -> bool: + def _area_is_polygon(area: Sequence[tuple[float, float]] | Sequence[float]) -> bool: polygon = np.array(area) return polygon.ndim == 2 and polygon.shape[1] == 2 @@ -618,8 +616,8 @@ def interp2d( data: np.ndarray, elem_ids: np.ndarray, weights: np.ndarray | None = None, - shape: Tuple[int, ...] | None = None, - ) -> np.ndarray | List[np.ndarray]: + shape: tuple[int, ...] | None = None, + ) -> np.ndarray | list[np.ndarray]: """interp spatially in data (2d only) Parameters @@ -881,7 +879,7 @@ def contains(self, points: np.ndarray) -> np.ndarray: def __contains__(self, pt: np.ndarray) -> bool: return self.contains(pt)[0] - def _get_boundary_polylines_uncategorized(self) -> List[List[np.int64]]: + def _get_boundary_polylines_uncategorized(self) -> list[list[np.int64]]: """Construct closed polylines for all boundary faces""" boundary_faces = self._get_boundary_faces() face_remains = boundary_faces.copy() @@ -991,7 +989,7 @@ def find_index( y: float | np.ndarray | None = None, coords: np.ndarray | None = None, area: ( - Tuple[float, float, float, float] | Sequence[Tuple[float, float]] | None + tuple[float, float, float, float] | Sequence[tuple[float, float]] | None ) = None, ) -> np.ndarray: """Find a *set* of element indicies for a number of points or within an area. @@ -1065,7 +1063,7 @@ def _inside_polygon(polygon: np.ndarray, xy: np.ndarray) -> np.ndarray: return mp.Path(polygon).contains_points(xy) def _elements_in_area( - self, area: Sequence[float] | Sequence[Tuple[float, float]] + self, area: Sequence[float] | Sequence[tuple[float, float]] ) -> np.ndarray: """Find 2d element ids of elements inside area""" if self._area_is_bbox(area): @@ -1086,7 +1084,7 @@ def elements_to_geometry( self, elements: int | Sequence[int], keepdims: bool = False ) -> "GeometryFM2D" | GeometryPoint2D: if isinstance(elements, (int, np.integer)): - sel_elements: List[int] = [elements] + sel_elements: list[int] = [elements] else: sel_elements = list(elements) if len(sel_elements) == 1 and not keepdims: @@ -1113,7 +1111,7 @@ def elements_to_geometry( ) def _get_nodes_and_table_for_elements( - self, elements: np.ndarray | List[int] + self, elements: np.ndarray | list[int] ) -> tuple[Any, Any]: """list of nodes and element table for a list of elements diff --git a/mikeio/spatial/_FM_geometry_layered.py b/mikeio/spatial/_FM_geometry_layered.py index e35844eba..f20ab7144 100644 --- a/mikeio/spatial/_FM_geometry_layered.py +++ b/mikeio/spatial/_FM_geometry_layered.py @@ -2,7 +2,7 @@ from functools import cached_property from pathlib import Path -from typing import Any, Iterable, Literal, Sequence, List, Tuple +from typing import Any, Iterable, Literal, Sequence from matplotlib.axes import Axes import numpy as np @@ -24,7 +24,7 @@ def __init__( self, *, node_coordinates: np.ndarray, - element_table: np.ndarray | List[Sequence[int]] | List[np.ndarray], + element_table: np.ndarray | list[Sequence[int]] | list[np.ndarray], codes: np.ndarray | None = None, projection: str = "LONG/LAT", dfsu_type: DfsuFileType = DfsuFileType.Dfsu3DSigma, @@ -81,7 +81,7 @@ def elements_to_geometry( node_layers: Layer = "all", keepdims: bool = False, ) -> GeometryFM3D | GeometryPoint3D | GeometryFM2D | GeometryFMVerticalColumn: - sel_elements: List[int] + sel_elements: list[int] if isinstance(elements, (int, np.integer)): sel_elements = [elements] @@ -182,7 +182,7 @@ def _get_nodes_and_table_for_elements( self, elements: Sequence[int] | np.ndarray, node_layers: Layer = "all", - ) -> Tuple[Any, Any]: + ) -> tuple[Any, Any]: """list of nodes and element table for a list of elements Parameters @@ -328,7 +328,7 @@ def top_elements(self) -> np.ndarray: return self._find_top_layer_elements(self.element_table) def _elements_in_area( - self, area: Sequence[Tuple[float, float]] | Sequence[float] + self, area: Sequence[tuple[float, float]] | Sequence[float] ) -> np.ndarray: """Find element ids of elements inside area""" idx2d = self.geometry2d._elements_in_area(area) @@ -480,8 +480,10 @@ def elem2d_ids(self) -> np.ndarray: self._layer_ids = res[2] return self._2d_ids - def _get_2d_to_3d_association(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: - e2_to_e3 = [] # for each 2d element: the corresponding 3d element ids from bot to top + def _get_2d_to_3d_association(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + e2_to_e3 = ( + [] + ) # for each 2d element: the corresponding 3d element ids from bot to top index2d = [] # for each 3d element: the associated 2d element id layerid = [] # for each 3d element: the associated layer number n2d = len(self.top_elements) @@ -619,7 +621,7 @@ def __init__( self, *, node_coordinates: np.ndarray, - element_table: np.ndarray | List[Sequence[int]] | List[np.ndarray], + element_table: np.ndarray | list[Sequence[int]] | list[np.ndarray], codes: np.ndarray | None = None, projection: str = "LONG/LAT", dfsu_type: DfsuFileType = DfsuFileType.Dfsu3DSigma, @@ -661,7 +663,7 @@ def find_index( y: float | None = None, z: float | None = None, coords: np.ndarray | None = None, - area: Tuple[float, float, float, float] | None = None, + area: tuple[float, float, float, float] | None = None, layers: int | Layer | Sequence[int] | None = None, ) -> np.ndarray: if layers is not None: @@ -706,7 +708,7 @@ class GeometryFMVerticalProfile(_GeometryFMLayered): def __init__( self, node_coordinates: np.ndarray, - element_table: np.ndarray | List[Sequence[int]] | List[np.ndarray], + element_table: np.ndarray | list[Sequence[int]] | list[np.ndarray], codes: np.ndarray | None = None, projection: str = "LONG/LAT", dfsu_type: DfsuFileType = DfsuFileType.Dfsu3DSigma, @@ -738,7 +740,7 @@ def relative_element_distance(self) -> np.ndarray: nc0 = self.node_coordinates[0, :2] return _relative_cumulative_distance(ec, nc0, is_geo=self.is_geo) - def get_nearest_relative_distance(self, coords: Tuple[float, float]) -> float: + def get_nearest_relative_distance(self, coords: tuple[float, float]) -> float: """For a point near a transect, find the nearest relative distance for showing position on transect plot. @@ -890,7 +892,7 @@ def __init__(self, geometry: "GeometryFMVerticalProfile") -> None: def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Axes: import matplotlib.pyplot as plt diff --git a/mikeio/spatial/_FM_geometry_spectral.py b/mikeio/spatial/_FM_geometry_spectral.py index 42e65fd44..7dd4066ce 100644 --- a/mikeio/spatial/_FM_geometry_spectral.py +++ b/mikeio/spatial/_FM_geometry_spectral.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Any, Sequence, Tuple +from typing import Any, Sequence import numpy as np @@ -31,7 +31,7 @@ def __init__( self.y = y @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: if self.directions is None: return ("frequency",) else: diff --git a/mikeio/spatial/_FM_utils.py b/mikeio/spatial/_FM_utils.py index 8e2437916..4d76c416e 100644 --- a/mikeio/spatial/_FM_utils.py +++ b/mikeio/spatial/_FM_utils.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Any, Literal, Sequence, Tuple +from typing import Any, Literal, Sequence from matplotlib.axes import Axes from matplotlib.cm import ScalarMappable from matplotlib.collections import PatchCollection @@ -40,7 +40,7 @@ def _plot_map( n_refinements: int = 0, show_mesh: bool = False, show_outline: bool = True, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, ax: Axes | None = None, add_colorbar: bool = True, ) -> Axes: @@ -235,7 +235,7 @@ def __set_colormap_levels( vmax: float | None, levels: int | Sequence[float] | np.ndarray | None, z: np.ndarray, -) -> Tuple[float, float, Colormap, Normalize, ScalarMappable, np.ndarray]: +) -> tuple[float, float, Colormap, Normalize, ScalarMappable, np.ndarray]: """Set colormap, levels, vmin, vmax, and cmap_norm Parameters @@ -430,7 +430,7 @@ def __get_tris( ec: np.ndarray, z: np.ndarray, n_refinements: int, -) -> Tuple[Triangulation, np.ndarray]: +) -> tuple[Triangulation, np.ndarray]: """get triangulation object and node-centered data Parameters @@ -702,7 +702,7 @@ def __create_tri_only_element_table( element_table: np.ndarray, element_coordinates: np.ndarray, data: np.ndarray, -) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Convert quad/tri mesh to pure tri-mesh""" if __is_tri_only(element_table): diff --git a/mikeio/spatial/_geometry.py b/mikeio/spatial/_geometry.py index 7fe0512c1..998ea3e4d 100644 --- a/mikeio/spatial/_geometry.py +++ b/mikeio/spatial/_geometry.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from collections import namedtuple -from typing import Any, Tuple +from typing import Any from mikecore.Projections import MapProjection @@ -42,7 +42,7 @@ def ndim(self) -> int: @property @abstractmethod - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: pass @@ -55,7 +55,7 @@ def ndim(self) -> int: raise NotImplementedError() @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: raise NotImplementedError() @@ -66,7 +66,7 @@ def __init__(self, x: float, y: float, projection: str = "LONG/LAT"): self.y = y @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: return () def __repr__(self) -> str: @@ -103,7 +103,7 @@ def __repr__(self) -> str: return f"GeometryPoint3D(x={self.x}, y={self.y}, z={self.z})" @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: return () @property diff --git a/mikeio/spatial/_grid_geometry.py b/mikeio/spatial/_grid_geometry.py index fd3c02f02..5189104fd 100644 --- a/mikeio/spatial/_grid_geometry.py +++ b/mikeio/spatial/_grid_geometry.py @@ -2,7 +2,7 @@ from functools import cached_property from pathlib import Path import warnings -from typing import Any, Sequence, Tuple, TYPE_CHECKING, List, overload +from typing import Any, Sequence, TYPE_CHECKING, overload from dataclasses import dataclass import numpy as np @@ -35,7 +35,7 @@ def _parse_grid_axis( x0: float = 0.0, dx: float | None = None, nx: int | None = None, -) -> Tuple[float, float, int]: +) -> tuple[float, float, int]: if x is not None: x = np.asarray(x) _check_equidistant(x) @@ -109,7 +109,7 @@ class Grid1D(_Geometry): _nx: int _x0: float _orientation: float - _origin: Tuple[float, float] + _origin: tuple[float, float] _projstr: str def __init__( @@ -120,7 +120,7 @@ def __init__( dx: float | None = None, nx: int | None = None, projection: str = "NON-UTM", - origin: Tuple[float, float] = (0.0, 0.0), + origin: tuple[float, float] = (0.0, 0.0), orientation: float = 0.0, node_coordinates: np.ndarray | None = None, axis_name: str = "x", @@ -143,7 +143,7 @@ def ndim(self) -> int: return 1 @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: return ("x",) def __repr__(self) -> str: @@ -178,8 +178,8 @@ def find_index(self, x: float, **kwargs: Any) -> int: return int(np.argmin(d)) def get_spatial_interpolant( - self, coords: Tuple[np.ndarray, np.ndarray], **kwargs: Any - ) -> Tuple[np.ndarray, np.ndarray]: + self, coords: tuple[np.ndarray, np.ndarray], **kwargs: Any + ) -> tuple[np.ndarray, np.ndarray]: x = coords[0][0] # TODO accept list of points assert self.nx > 1, "Interpolation not possible for Grid1D with one point" @@ -215,7 +215,7 @@ def nx(self) -> int: return self._nx @property - def origin(self) -> Tuple[float, float]: + def origin(self) -> tuple[float, float]: return self._origin @property @@ -296,7 +296,7 @@ def __init__(self, geometry: "Grid2D") -> None: def __call__( self, ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, **kwargs: Any, ) -> Any: """Plot bathymetry as coloured patches""" @@ -305,7 +305,7 @@ def __call__( @staticmethod def _get_ax( - ax: Axes | None = None, figsize: Tuple[float, float] | None = None + ax: Axes | None = None, figsize: tuple[float, float] | None = None ) -> Axes: import matplotlib.pyplot as plt @@ -341,7 +341,7 @@ def outline( self, title: str = "Outline", ax: Axes | None = None, - figsize: Tuple[float, float] | None = None, + figsize: tuple[float, float] | None = None, color: str = "0.4", linewidth: float = 1.2, **kwargs: Any, @@ -410,7 +410,7 @@ class Grid2D(_Geometry): _ny: int _y0: float _projstr: str - _origin: Tuple[float, float] + _origin: tuple[float, float] _orientation: float is_spectral: bool @@ -425,11 +425,11 @@ def __init__( y0: float = 0.0, dy: float | None = None, ny: int | None = None, - bbox: Tuple[float, float, float, float] | None = None, + bbox: tuple[float, float, float, float] | None = None, projection: str = "LONG/LAT", - origin: Tuple[float, float] | None = None, + origin: tuple[float, float] | None = None, orientation: float = 0.0, - axis_names: Tuple[str, str] = ("x", "y"), + axis_names: tuple[str, str] = ("x", "y"), is_spectral: bool = False, is_vertical: bool = False, ): @@ -500,7 +500,7 @@ def __init__( self.plot = _Grid2DPlotter(self) @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: return ("y", "x") @property @@ -513,8 +513,8 @@ def _is_rotated(self) -> Any: def _create_in_bbox( self, - bbox: Tuple[float, float, float, float], - dx: float | Tuple[float, float] | None = None, + bbox: tuple[float, float, float, float], + dx: float | tuple[float, float] | None = None, dy: float | None = None, nx: int | None = None, ny: int | None = None, @@ -564,8 +564,8 @@ def _create_in_bbox( @staticmethod def _parse_bbox( - bbox: Tuple[float, float, float, float], - ) -> Tuple[float, float, float, float]: + bbox: tuple[float, float, float, float] + ) -> tuple[float, float, float, float]: left = bbox[0] bottom = bbox[1] right = bbox[2] @@ -593,7 +593,7 @@ def _create_in_bbox_1d( right: float, dx: float | None = None, nx: int | None = None, - ) -> Tuple[float, float, int]: + ) -> tuple[float, float, int]: xr = right - left if dx is not None: nx = int(np.ceil(xr / dx)) @@ -704,7 +704,7 @@ def ny(self) -> int: return self._ny @property - def origin(self) -> Tuple[float, float]: + def origin(self) -> tuple[float, float]: """Coordinates of grid origo (in projection)""" return self._origin @@ -790,8 +790,8 @@ def find_index( x: float | None = None, y: float | None = None, coords: np.ndarray | None = None, - area: Tuple[float, float, float, float] | None = None, - ) -> Tuple[Any, Any]: + area: tuple[float, float, float, float] | None = None, + ) -> tuple[Any, Any]: """Find nearest index (i,j) of point(s) Parameters @@ -840,7 +840,7 @@ def find_index( else: raise ValueError("Provide x,y or coords") - def _xy_to_index(self, xy: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: + def _xy_to_index(self, xy: np.ndarray) -> tuple[np.ndarray, np.ndarray]: """Find specific points in this geometry""" xy = np.atleast_2d(xy) y = xy[:, 1] @@ -860,8 +860,8 @@ def _xy_to_index(self, xy: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: return ii, jj def _bbox_to_index( - self, bbox: Tuple[float, float, float, float] | BoundingBox - ) -> Tuple[range, range]: + self, bbox: tuple[float, float, float, float] | BoundingBox + ) -> tuple[range, range]: """Find subarea within this geometry""" if not (len(bbox) == 4): raise ValueError( @@ -963,7 +963,7 @@ def _index_to_Grid2D( origin=origin, ) - def _to_element_table(self, index_base: int = 0) -> List[List[int]]: + def _to_element_table(self, index_base: int = 0) -> list[list[int]]: elem_table = [] for elx in range(self.nx - 1): # each col @@ -1102,7 +1102,7 @@ class Grid3D(_Geometry): _nz: int _z0: float _projstr: str - _origin: Tuple[float, float] + _origin: tuple[float, float] _orientation: float def __init__( @@ -1121,7 +1121,7 @@ def __init__( dz: float | None = None, nz: int | None = None, projection: str = "NON-UTM", # TODO LONG/LAT - origin: Tuple[float, float] = (0.0, 0.0), + origin: tuple[float, float] = (0.0, 0.0), orientation: float = 0.0, ) -> None: super().__init__(projection=projection) @@ -1136,7 +1136,7 @@ def __init__( self._orientation = orientation @property - def default_dims(self) -> Tuple[str, ...]: + def default_dims(self) -> tuple[str, ...]: return ("z", "y", "x") @property @@ -1201,7 +1201,7 @@ def nz(self) -> int: return self._nz @property - def origin(self) -> Tuple[float, float]: + def origin(self) -> tuple[float, float]: """Coordinates of grid origo (in projection)""" return self._origin diff --git a/mikeio/spatial/_utils.py b/mikeio/spatial/_utils.py index a02a5fe16..f9f9e69dd 100644 --- a/mikeio/spatial/_utils.py +++ b/mikeio/spatial/_utils.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Tuple - import numpy as np from ._geometry import BoundingBox @@ -17,7 +15,7 @@ def xy_to_bbox(xy: np.ndarray, buffer: float = 0.0) -> BoundingBox: def dist_in_meters( - coords: np.ndarray, pt: Tuple[float, float], is_geo: bool = False + coords: np.ndarray, pt: tuple[float, float], is_geo: bool = False ) -> np.ndarray: """get distance between array of coordinates and point diff --git a/tests/notebooks/test_notebooks.py b/tests/notebooks/test_notebooks.py index 78c4c6db9..00281b014 100644 --- a/tests/notebooks/test_notebooks.py +++ b/tests/notebooks/test_notebooks.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import List import nbformat @@ -26,7 +25,7 @@ def _process_notebook(fp: Path): return -def _get_all_notebooks_in_repo() -> List[Path]: +def _get_all_notebooks_in_repo() -> list[Path]: ROOT_DIR = Path(__file__).parent.parent.parent NOTEBOOK_DIR = ROOT_DIR / "notebooks" @@ -36,6 +35,7 @@ def _get_all_notebooks_in_repo() -> List[Path]: def test_notebook(notebook): _process_notebook(notebook) + def pytest_generate_tests(metafunc): notebooks = _get_all_notebooks_in_repo() metafunc.parametrize("notebook", notebooks) @@ -44,4 +44,4 @@ def pytest_generate_tests(metafunc): if __name__ == "__main__": notebooks = _get_all_notebooks_in_repo() for notebook in notebooks: - print(notebook) \ No newline at end of file + print(notebook)