Skip to content

Commit

Permalink
Merge pull request #622 from DHI/simple
Browse files Browse the repository at this point in the history
Simplify Dfs{0,1}
  • Loading branch information
ecomodeller authored Nov 30, 2023
2 parents 886661f + 8c6af48 commit 5e84e3c
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 184 deletions.
5 changes: 2 additions & 3 deletions mikeio/dataset/_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -1859,10 +1859,9 @@ def _to_dfs3(self, filename):
write_dfs3(filename, self)

def _to_dfs1(self, filename):
from ..dfs._dfs1 import Dfs1
from ..dfs._dfs1 import write_dfs1

dfs = Dfs1()
dfs.write(filename, data=self, dx=self.geometry.dx, x0=self.geometry._x0)
write_dfs1(filename=filename,ds=self)

def _to_dfsu(self, filename):
from ..dfsu._dfsu import _write_dfsu
Expand Down
109 changes: 33 additions & 76 deletions mikeio/dfs/_dfs0.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations
from functools import cached_property
from pathlib import Path
import warnings
from datetime import datetime, timedelta
Expand All @@ -11,7 +13,7 @@
from mikecore.eum import eumQuantity # type: ignore

from .. import __dfs_version__
from ..dataset import Dataset
from ..dataset import Dataset, DataArray
from ._dfs import _get_item_info, _valid_item_numbers, _valid_timesteps
from ..eum import EUMType, EUMUnit, ItemInfo, TimeStepUnit

Expand Down Expand Up @@ -82,55 +84,22 @@ def _write_dfs0(filename, dataset: Dataset, title="", dtype=DfsSimpleType.Float)


class Dfs0:
def __init__(self, filename=None):
def __init__(self, filename:str | Path):
"""Create a Dfs0 object for reading, writing
Parameters
----------
filename: str or Path, optional
filename: str or Path
File name including full path to the dfs0 file.
"""

self._source = None
self._dfs = None
self._start_time = None
self._end_time = None
self._n_items = None
self._dt = None
self._is_equidistant = None
self._title = None
self._items = None
self._n_timesteps = None

self._filename = str(filename)

if filename:
self._read_header(Path(filename))

def __repr__(self):
out = ["<mikeio.Dfs0>"]

# TODO does this make sense:
if self._filename:
out.append(f"timeaxis: {repr(self._timeaxistype)}")

if self._n_items is not None:
if self._n_items < 10:
out.append("items:")
for i, item in enumerate(self.items):
out.append(f" {i}: {item}")
else:
out.append(f"number of items: {self._n_items}")

return str.join("\n", out)

def _read_header(self, path: Path):
path = Path(filename)
if not path.exists():
raise FileNotFoundError(path)

dfs = DfsFileFactory.DfsGenericOpen(str(path))
self._source = dfs
self._deletevalue = dfs.FileInfo.DeleteValueDouble # NOTE: changed in cutil

# Read items
self._n_items = len(dfs.ItemInfo)
Expand All @@ -151,6 +120,19 @@ def _read_header(self, path: Path):

dfs.Close()

def __repr__(self):
out = ["<mikeio.Dfs0>"]
out.append(f"timeaxis: {repr(self._timeaxistype)}")

if self._n_items < 10:
out.append("items:")
for i, item in enumerate(self.items):
out.append(f" {i}: {item}")
else:
out.append(f"number of items: {self._n_items}")

return str.join("\n", out)

def read(self, items=None, time=None, keepdims=False) -> Dataset:
"""
Read data from a dfs0 file.
Expand Down Expand Up @@ -487,19 +469,17 @@ def start_time(self):
"""File start time"""
return self._start_time

@property
@cached_property
def end_time(self):
if self._end_time is None:
if self._source.FileInfo.TimeAxis.IsEquidistant():
dt = self._source.FileInfo.TimeAxis.TimeStep
n_steps = self._source.FileInfo.TimeAxis.NumberOfTimeSteps
timespan = dt * (n_steps - 1)
else:
timespan = self._source.FileInfo.TimeAxis.TimeSpan

self._end_time = self.start_time + timedelta(seconds=timespan)

if self._source.FileInfo.TimeAxis.IsEquidistant():
dt = self._source.FileInfo.TimeAxis.TimeStep
n_steps = self._source.FileInfo.TimeAxis.NumberOfTimeSteps
timespan = dt * (n_steps - 1)
else:
timespan = self._source.FileInfo.TimeAxis.TimeSpan

return self._end_time
return self.start_time + timedelta(seconds=timespan)

@property
def n_timesteps(self):
Expand Down Expand Up @@ -571,20 +551,15 @@ def dataframe_to_dfs0(
dtype : np.dtype, optional
default np.float32
"""

if not isinstance(self.index, pd.DatetimeIndex):
raise ValueError(
"Dataframe index must be a DatetimeIndex. Hint: pd.read_csv(..., parse_dates=True)"
)

dfs = Dfs0()

data = []
for i in range(self.values.shape[1]):
data.append(self.values[:, i])
ncol = self.values.shape[1]
data = [self.values[:, i] for i in range(ncol)]

if items is None:

if itemtype is None:
items = [ItemInfo(name) for name in self.columns]
else:
Expand All @@ -593,27 +568,9 @@ def dataframe_to_dfs0(
else:
items = [ItemInfo(name, itemtype, unit) for name in self.columns]

if self.index.freq is None: # non-equidistant
dfs.write(
filename=filename,
data=data,
datetimes=self.index,
items=items,
title=title,
dtype=dtype,
)
else: # equidistant
dt = self.index.freq.delta.total_seconds()
start_time = self.index[0].to_pydatetime()
dfs.write(
filename=filename,
data=data,
start_time=start_time,
dt=dt,
items=items,
title=title,
dtype=dtype,
)
das = {item.name: DataArray(data=d, item=item, time=self.index) for d, item in zip(data, items)}
ds = Dataset(das)
_write_dfs0(filename=filename, dataset=ds, title=title, dtype=dtype)


# Monkey patching onto Pandas classes
Expand Down
174 changes: 88 additions & 86 deletions mikeio/dfs/_dfs1.py
Original file line number Diff line number Diff line change
@@ -1,113 +1,115 @@
from pathlib import Path

from mikecore.DfsBuilder import DfsBuilder # type: ignore
from mikecore.DfsFileFactory import DfsFileFactory # type: ignore
from mikecore.eum import eumUnit # type: ignore
from mikecore.DfsFactory import DfsBuilder, DfsFactory
from mikecore.DfsFile import DfsFile, DfsSimpleType
from mikecore.DfsFileFactory import DfsFileFactory
from mikecore.eum import eumQuantity, eumUnit

from .. import __dfs_version__
from ._dfs import _Dfs123
from ..dataset import Dataset
from ._dfs import (
_Dfs123,
_write_dfs_data,
)
from ..eum import TimeStepUnit
from ..spatial import Grid1D


class Dfs1(_Dfs123):
_ndim = 1
def write_dfs1(filename: str, ds: Dataset, title="") -> None:
dfs = _write_dfs1_header(filename, ds, title)
_write_dfs_data(dfs=dfs, ds=ds, n_spatial_dims=1)

def __init__(self, filename=None):
super().__init__(filename)

self._dx = None
self._nx = None
self._x0 = 0

if filename:
self._read_dfs1_header(path=Path(filename))
origin = self._longitude, self._latitude
self.geometry = Grid1D(
x0=self._x0,
dx=self._dx,
nx=self._nx,
projection=self._projstr,
origin=origin,
orientation=self._orientation,
)
def _write_dfs1_header(filename, ds: Dataset, title="") -> DfsFile:
builder = DfsBuilder.Create(title, "mikeio", __dfs_version__)
builder.SetDataType(0)

def __repr__(self):
out = ["<mikeio.Dfs1>"]
geometry: Grid1D = ds.geometry

# TODO does this make sense
if self._filename:
out.append(f"dx: {self.dx:.5f}")

if self._n_items is not None:
if self._n_items < 10:
out.append("items:")
for i, item in enumerate(self.items):
out.append(f" {i}: {item}")
else:
out.append(f"number of items: {self._n_items}")

if self._n_timesteps == 1:
out.append("time: time-invariant file (1 step)")
else:
out.append(f"time: {self._n_timesteps} steps")
out.append(f"start time: {self._start_time}")
factory = DfsFactory()
proj = factory.CreateProjectionGeoOrigin(ds.geometry.projection_string,ds.geometry.origin[0],ds.geometry.origin[1],ds.geometry.orientation)
builder.SetGeographicalProjection(proj)
builder.SetSpatialAxis(
factory.CreateAxisEqD1(
eumUnit.eumUmeter,
geometry._nx,
geometry._x0,
geometry._dx,
)
)

timestep_unit = TimeStepUnit.SECOND
dt = ds.timestep or 1.0 # It can not be None
time_axis = factory.CreateTemporalEqCalendarAxis(
timestep_unit, ds.time[0], 0, dt
)
builder.SetTemporalAxis(time_axis)

for item in ds.items:
builder.AddCreateDynamicItem(
item.name,
eumQuantity.Create(item.type, item.unit),
DfsSimpleType.Float,
item.data_value_type,
)

return str.join("\n", out)
try:
builder.CreateFile(filename)
except IOError:
print("cannot create dfs file: ", filename)

return builder.GetFile()


class Dfs1(_Dfs123):
_ndim = 1

def _read_dfs1_header(self, path: Path):
def __init__(self, filename):
super().__init__(filename)
path = Path(filename)
if not path.exists():
raise FileNotFoundError(path)

self._dfs = DfsFileFactory.Dfs1FileOpen(str(path))
self._dfs = DfsFileFactory.Dfs1FileOpen(str(filename))
self._x0 = self._dfs.SpatialAxis.X0
self._dx = self._dfs.SpatialAxis.Dx
self._nx = self._dfs.SpatialAxis.XCount

self._read_header()

def _open(self):
self._dfs = DfsFileFactory.Dfs1FileOpen(self._filename)
origin = self._longitude, self._latitude
self.geometry = Grid1D(
x0=self._x0,
dx=self._dx,
nx=self._nx,
projection=self._projstr,
origin=origin,
orientation=self._orientation,
)

def write(
self,
filename,
data,
dt=None,
dx=1,
x0=0,
title=None,
):
"""
Write a dfs1 file
Parameters
----------
filename: str
Location to write the dfs1 file
data: Dataset
list of matrices, one for each item. Matrix dimension: x, time
dt: float
The time step in seconds.
x0:
Lower right position
dx:
length of each grid in the x direction (meters)
title: str, optional
title of the dfs file (can be blank)
"""

self._x0 = x0
filename = str(filename)

if isinstance(data, list):
raise TypeError(
"supplying data as a list of numpy arrays is deprecated, please supply data in the form of a Dataset"
)
def __repr__(self):
out = ["<mikeio.Dfs1>"]

out.append(f"dx: {self.dx:.5f}")

if self._n_items < 10:
out.append("items:")
for i, item in enumerate(self.items):
out.append(f" {i}: {item}")
else:
out.append(f"number of items: {self._n_items}")

if self._n_timesteps == 1:
out.append("time: time-invariant file (1 step)")
else:
out.append(f"time: {self._n_timesteps} steps")
out.append(f"start time: {self._start_time}")

return str.join("\n", out)


self._builder = DfsBuilder.Create(title, "mikeio", __dfs_version__)
self._dx = dx
self._write(filename=filename, ds=data, dt=dt, title=title)
def _open(self):
self._dfs = DfsFileFactory.Dfs1FileOpen(self._filename)

def _set_spatial_axis(self):
self._builder.SetSpatialAxis(
Expand Down
Loading

0 comments on commit 5e84e3c

Please sign in to comment.