Skip to content

Commit 811657b

Browse files
authored
Merge pull request #837 from DHI/feature/write_spectral_dfsu
Feature/write_spectral_dfsu
2 parents f76e85b + afdba23 commit 811657b

File tree

5 files changed

+121
-8
lines changed

5 files changed

+121
-8
lines changed

mikeio/dfsu/_dfsu.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def write_dfsu(filename: str | Path, data: Dataset) -> None:
5151
geometry = data.geometry
5252
dfsu_filetype = DfsuFileType.Dfsu2D
5353

54-
if geometry.is_layered:
54+
if geometry.is_layered or geometry.is_spectral:
5555
dfsu_filetype = geometry._type.value
5656

5757
xn = geometry.node_coordinates[:, 0]
@@ -61,9 +61,24 @@ def write_dfsu(filename: str | Path, data: Dataset) -> None:
6161
elem_table = [np.array(e) + 1 for e in geometry.element_table]
6262

6363
builder = DfsuBuilder.Create(dfsu_filetype)
64-
if dfsu_filetype != DfsuFileType.Dfsu2D:
64+
if dfsu_filetype in (
65+
DfsuFileType.Dfsu3DSigma,
66+
DfsuFileType.Dfsu3DSigmaZ,
67+
DfsuFileType.DfsuVerticalColumn,
68+
DfsuFileType.DfsuVerticalProfileSigma,
69+
DfsuFileType.DfsuVerticalProfileSigmaZ,
70+
):
6571
builder.SetNumberOfSigmaLayers(geometry.n_sigma_layers)
6672

73+
if dfsu_filetype in (
74+
DfsuFileType.DfsuSpectral0D,
75+
DfsuFileType.DfsuSpectral1D,
76+
DfsuFileType.DfsuSpectral2D,
77+
):
78+
builder.SetFrequencies(geometry.frequencies)
79+
# TODO should directions always be converted to radians
80+
builder.SetDirections(np.deg2rad(geometry.directions))
81+
6782
builder.SetNodes(xn, yn, zn, geometry.codes)
6883
builder.SetElements(elem_table)
6984

@@ -82,9 +97,6 @@ def write_dfsu(filename: str | Path, data: Dataset) -> None:
8297
builder.SetTemporalAxis(temporal_axis)
8398
builder.SetZUnit(eumUnit.eumUmeter)
8499

85-
if dfsu_filetype != DfsuFileType.Dfsu2D:
86-
builder.SetNumberOfSigmaLayers(geometry.n_sigma_layers)
87-
88100
for item in data.items:
89101
builder.AddDynamicItem(item.name, eumQuantity.Create(item.type, item.unit))
90102

@@ -117,7 +129,13 @@ def write_dfsu_data(dfs: DfsuFile, ds: Dataset, is_layered: bool) -> None:
117129
d = da.to_numpy()[i, :]
118130
else:
119131
d = da.to_numpy()
132+
d = d.copy() # to avoid modifying the input
120133
d[np.isnan(d)] = data.deletevalue
134+
135+
if d.ndim != 1:
136+
d = np.moveaxis(d, 0, -1)
137+
d = d.reshape(-1)
138+
121139
dfs.WriteItemTimeStepNext(t_rel[i], d.astype(np.float32))
122140
dfs.Close()
123141

mikeio/spatial/_FM_geometry_spectral.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ def n_frequencies(self) -> int:
105105
"""Number of frequencies."""
106106
return 0 if self.frequencies is None else len(self.frequencies)
107107

108+
@property
109+
def is_layered(self) -> bool:
110+
return False
111+
108112
@property
109113
def frequencies(self) -> np.ndarray | None:
110114
"""Frequency axis."""
@@ -182,6 +186,31 @@ def elements_to_geometry( # type: ignore
182186
class GeometryFMLineSpectrum(_GeometryFMSpectrum):
183187
"""Flexible mesh line spectrum geometry."""
184188

189+
@staticmethod
190+
def create_dummy_coordinates(
191+
n_nodes: int, frequencies: np.ndarray, directions: np.ndarray
192+
) -> GeometryFMLineSpectrum:
193+
# boogus x, y, z 1..n, 1..n, -10
194+
node_coordinates = np.zeros((n_nodes, 3), dtype=float)
195+
node_coordinates[:, 0] = np.arange(1, n_nodes + 1) # x
196+
node_coordinates[:, 1] = np.arange(1, n_nodes + 1) # y
197+
node_coordinates[:, 2] = -10.0 # z
198+
199+
# element table with nodes [0,1], [1,2], ..., [n-2,n-1]
200+
element_table = np.zeros((n_nodes - 1, 2), dtype=int)
201+
for i in range(n_nodes - 1):
202+
element_table[i, 0] = i
203+
element_table[i, 1] = i + 1
204+
return GeometryFMLineSpectrum(
205+
node_coordinates=node_coordinates,
206+
element_table=element_table,
207+
codes=np.zeros(n_nodes, dtype=int),
208+
dfsu_type=DfsuFileType.DfsuSpectral1D,
209+
projection="LONG/LAT",
210+
frequencies=frequencies,
211+
directions=directions,
212+
)
213+
185214
def isel( # type: ignore
186215
self, idx: Sequence[int], axis: str = "node"
187216
) -> GeometryFMPointSpectrum | GeometryFMLineSpectrum:

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ exclude = [
2020

2121
[project]
2222
name = "mikeio"
23-
version = "2.7.0"
23+
version = "3.0.0a0"
2424
dependencies = [
25-
"mikecore>=0.2.1",
25+
"mikecore>=0.3.0a0",
2626
"numpy>=1.22.0",
2727
"pandas>=1.3",
2828
"matplotlib>=3.6.0",

tests/test_dfsu_spectral.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
from pathlib import Path
12
import pytest
23
import numpy as np
34
import matplotlib.pyplot as plt
45
import mikeio
56
from mikecore.DfsuFile import DfsuFileType
67

7-
from mikeio import DfsuSpectral
8+
from mikeio import DfsuSpectral, EUMType, EUMUnit
89
from mikeio.spatial import GeometryFMPointSpectrum, GeometryFMAreaSpectrum
910
import mikeio._spectral as _spectral
11+
from mikeio.spatial._FM_geometry_spectral import GeometryFMLineSpectrum
1012

1113

1214
@pytest.fixture
@@ -439,3 +441,67 @@ def test_plot_da_spectrum(dfsu_pt: DfsuSpectral) -> None:
439441
# dfs.plot_spectrum(spec, title="pt", plot_type="shaded")
440442
# dfs.plot_spectrum(spec, r_as_periods=False, plot_type="contour")
441443
plt.close("all")
444+
445+
446+
def test_write_line_spectra(dfsu_line: DfsuSpectral, tmp_path: Path) -> None:
447+
ds = dfsu_line.read()
448+
449+
fp = tmp_path / "line.dfsu"
450+
451+
ds.to_dfs(fp)
452+
453+
ds2 = mikeio.read(fp)
454+
455+
456+
def test_write_line_spectra_energy(tmp_path: Path) -> None:
457+
ds = mikeio.read("tests/testdata/spectra/North_BC_2024_subset.dfsu")
458+
assert ds["Energy density"].type == EUMType.Wave_energy_density
459+
assert ds["Energy density"].unit == EUMUnit.meter_pow_2_sec_per_deg
460+
assert ds.geometry.directions[-1] == pytest.approx(350)
461+
assert ds.geometry.frequencies[0] == pytest.approx(0.035)
462+
assert ds.geometry.frequencies[-1] == pytest.approx(0.983586)
463+
assert ds["Energy density"].isel(time=-1).isel(node=0).isel(frequency=35).isel(
464+
direction=28
465+
).to_numpy() == pytest.approx(2.6239042e-05)
466+
467+
fp = tmp_path / "line_energy.dfsu"
468+
ds.to_dfs(fp)
469+
470+
ds2 = mikeio.read(fp)
471+
assert ds2["Energy density"].type == EUMType.Wave_energy_density
472+
assert ds2["Energy density"].unit == EUMUnit.meter_pow_2_sec_per_deg
473+
assert ds2.geometry.directions[-1] == pytest.approx(350)
474+
assert ds2.geometry.frequencies[0] == pytest.approx(0.035)
475+
assert ds2.geometry.frequencies[-1] == pytest.approx(0.983586)
476+
assert ds2["Energy density"].isel(time=-1).isel(node=0).isel(frequency=35).isel(
477+
direction=28
478+
).to_numpy() == pytest.approx(2.6239042e-05)
479+
assert np.all(ds2.to_numpy() == ds.to_numpy())
480+
481+
482+
def test_write_area_spectra(dfsu_area: DfsuSpectral, tmp_path: Path) -> None:
483+
ds = dfsu_area.read()
484+
fp = tmp_path / "area.dfsu"
485+
ds.to_dfs(fp)
486+
487+
dfs = mikeio.DfsuSpectral(fp)
488+
assert dfs.geometry.is_spectral
489+
assert dfs._type == DfsuFileType.DfsuSpectral2D
490+
assert dfs.geometry.n_frequencies == 25
491+
assert dfs.frequencies is not None
492+
assert len(dfs.frequencies) == 25
493+
assert dfs.geometry.n_directions == 16
494+
assert dfs.directions is not None
495+
assert len(dfs.directions) == 16
496+
497+
ds2 = dfs.read()
498+
assert np.all(ds2.to_numpy() == ds.to_numpy())
499+
500+
501+
def test_create_line_spectrum_dummy_coordinates() -> None:
502+
freq = np.array([0.035, 0.983586])
503+
dirs = np.arange(5.0, 360.0, step=10.0)
504+
geometry = GeometryFMLineSpectrum.create_dummy_coordinates(
505+
n_nodes=10, frequencies=freq, directions=dirs
506+
)
507+
assert geometry.n_nodes == 10
94.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)