Skip to content

Commit

Permalink
Merge pull request #678 from DHI/dont-return-none
Browse files Browse the repository at this point in the history
Minimize passing and returning None and Dfsu refactor
  • Loading branch information
ecomodeller authored Mar 22, 2024
2 parents 4770472 + 9cfb29e commit 6cef6af
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 204 deletions.
12 changes: 10 additions & 2 deletions mikeio/dataset/_dataarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,8 +837,16 @@ def sel(
def _sel_with_slice(self, kwargs: Mapping[str, slice]) -> "DataArray":
for k, v in kwargs.items():
if isinstance(v, slice):
idx_start = self.geometry.find_index(**{k: v.start})
idx_stop = self.geometry.find_index(**{k: v.stop})
idx_start = (
self.geometry.find_index(**{k: v.start})
if v.start is not None
else None
)
idx_stop = (
self.geometry.find_index(**{k: v.stop})
if v.stop is not None
else None
)
pos = 0
if isinstance(idx_start, tuple):
if k == "x":
Expand Down
227 changes: 82 additions & 145 deletions mikeio/dfsu/_dfsu.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,55 @@ def end_time(self) -> pd.Timestamp:
def time(self) -> pd.DatetimeIndex:
return self._time

def _read(
def _validate_elements_and_geometry_sel(self, elements: Any, **kwargs: Any) -> None:
"""Check that only one of elements, area, x, y is selected"""
used_kwargs = [key for key, val in kwargs.items() if val is not None]

if elements is not None and len(used_kwargs) > 0:
raise ValueError(f"Cannot select both {used_kwargs} and elements!")

if "area" in used_kwargs and ("x" in used_kwargs or "y" in used_kwargs):
raise ValueError("Cannot select both x,y and area!")

def to_mesh(self, outfilename):
"""write object to mesh file
Parameters
----------
outfilename : str
path to file to be written
"""
self.geometry.geometry2d.to_mesh(outfilename)


class Dfsu2DH(_Dfsu):

def __init__(self, filename: str | Path) -> None:
super().__init__(filename)
self._geometry = self._read_geometry(self._filename)

@staticmethod
def _read_geometry(filename: str) -> GeometryFM2D:
dfs = DfsuFile.Open(filename)
dfsu_type = DfsuFileType(dfs.DfsuFileType)

node_table = get_nodes_from_source(dfs)
el_table = get_elements_from_source(dfs)

geometry = GeometryFM2D(
node_coordinates=node_table.coordinates,
element_table=el_table.connectivity,
codes=node_table.codes,
projection=dfs.Projection.WKTString,
dfsu_type=dfsu_type,
element_ids=el_table.ids,
node_ids=node_table.ids,
validate=False,
)
dfs.Close()
return geometry

def read(
self,
*,
items: str | int | Sequence[str | int] | None = None,
Expand All @@ -365,16 +413,42 @@ def _read(
error_bad_data: bool = True,
fill_bad_data_value: float = np.nan,
) -> Dataset:
"""
Read data from a dfsu file
Parameters
---------
items: list[int] or list[str], optional
Read only selected items, by number (0-based), or by name
time: int, str, datetime, pd.TimeStamp, sequence, slice or pd.DatetimeIndex, optional
Read only selected time steps, by default None (=all)
keepdims: bool, optional
When reading a single time step only, should the time-dimension be kept
in the returned Dataset? by default: False
area: list[float], optional
Read only data inside (horizontal) area given as a
bounding box (tuple with left, lower, right, upper)
or as list of coordinates for a polygon, by default None
x, y: float, optional
Read only data for elements containing the (x,y) points(s),
by default None
elements: list[int], optional
Read only selected element ids, by default None
error_bad_data: bool, optional
raise error if data is corrupt, by default True,
fill_bad_data_value:
fill value for to impute corrupt data, used in conjunction with error_bad_data=False
default np.nan
Returns
-------
Dataset
A Dataset with data dimensions [t,elements]
"""

if dtype not in [np.float32, np.float64]:
raise ValueError("Invalid data type. Choose np.float32 or np.float64")

# Open the dfs file for reading
# self._read_dfsu_header(self._filename)
dfs = DfsuFile.Open(self._filename)
# time may have changes since we read the header
# (if engine is continuously writing to this file)
# TODO: add more checks that this is actually still the same file
# (could have been replaced in the meantime)

single_time_selected, time_steps = _valid_timesteps(dfs, time)

Expand Down Expand Up @@ -459,45 +533,6 @@ def _read(
data_list, time, items, geometry=geometry, dims=dims, validate=False
)

def _validate_elements_and_geometry_sel(self, elements, **kwargs):
"""Check that only one of elements, area, x, y is selected
Parameters
----------
elements : list[int], optional
Read only selected element ids, by default None
area : list[float], optional
Read only data inside (horizontal) area given as a
bounding box (tuple with left, lower, right, upper)
or as list of coordinates for a polygon, by default None
x : float, optional
Read only data for elements containing the (x,y) points(s),
by default None
y : float, optional
Read only data for elements containing the (x,y) points(s),
by default None
Returns
-------
None
Raises
------
ValueError
If more than one of elements, area, x, y is selected
"""
used_kwargs = []
for kw, val in kwargs.items():
if val is not None:
used_kwargs.append(kw)

if elements is not None:
for kw in used_kwargs:
raise ValueError(f"Cannot select both {kw} and elements!")

if "area" in used_kwargs and ("x" in used_kwargs or "y" in used_kwargs):
raise ValueError("Cannot select both x,y and area!")

def _parse_geometry_sel(self, area, x, y):
"""Parse geometry selection
Expand Down Expand Up @@ -539,104 +574,6 @@ def _parse_geometry_sel(self, area, x, y):

return elements

def to_mesh(self, outfilename):
"""write object to mesh file
Parameters
----------
outfilename : str
path to file to be written
"""
self.geometry.geometry2d.to_mesh(outfilename)


class Dfsu2DH(_Dfsu):

def __init__(self, filename: str | Path) -> None:
super().__init__(filename)
self._geometry = self._read_geometry(self._filename)

@staticmethod
def _read_geometry(filename: str) -> GeometryFM2D:
dfs = DfsuFile.Open(filename)
dfsu_type = DfsuFileType(dfs.DfsuFileType)

node_table = get_nodes_from_source(dfs)
el_table = get_elements_from_source(dfs)

geometry = GeometryFM2D(
node_coordinates=node_table.coordinates,
element_table=el_table.connectivity,
codes=node_table.codes,
projection=dfs.Projection.WKTString,
dfsu_type=dfsu_type,
element_ids=el_table.ids,
node_ids=node_table.ids,
validate=False,
)
dfs.Close()
return geometry

def read(
self,
*,
items: str | int | Sequence[str | int] | None = None,
time: int | str | slice | None = None,
elements: Collection[int] | None = None,
area: Tuple[float, float, float, float] | None = None,
x: float | None = None,
y: float | None = None,
keepdims: bool = False,
dtype: Any = np.float32,
error_bad_data: bool = True,
fill_bad_data_value: float = np.nan,
) -> Dataset:
"""
Read data from a dfsu file
Parameters
---------
items: list[int] or list[str], optional
Read only selected items, by number (0-based), or by name
time: int, str, datetime, pd.TimeStamp, sequence, slice or pd.DatetimeIndex, optional
Read only selected time steps, by default None (=all)
keepdims: bool, optional
When reading a single time step only, should the time-dimension be kept
in the returned Dataset? by default: False
area: list[float], optional
Read only data inside (horizontal) area given as a
bounding box (tuple with left, lower, right, upper)
or as list of coordinates for a polygon, by default None
x, y: float, optional
Read only data for elements containing the (x,y) points(s),
by default None
elements: list[int], optional
Read only selected element ids, by default None
error_bad_data: bool, optional
raise error if data is corrupt, by default True,
fill_bad_data_value:
fill value for to impute corrupt data, used in conjunction with error_bad_data=False
default np.nan
Returns
-------
Dataset
A Dataset with data dimensions [t,elements]
"""

return self._read(
items=items,
time=time,
elements=elements,
area=area,
x=x,
y=y,
keepdims=keepdims,
dtype=dtype,
error_bad_data=error_bad_data,
fill_bad_data_value=fill_bad_data_value,
)

def get_overset_grid(self, dx=None, dy=None, nx=None, ny=None, buffer=0.0):
"""get a 2d grid that covers the domain by specifying spacing or shape
Expand Down
Loading

0 comments on commit 6cef6af

Please sign in to comment.