wavespectra 3.18.0__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wavespectra-3.18.0/wavespectra.egg-info → wavespectra-4.0.0}/PKG-INFO +8 -8
- {wavespectra-3.18.0 → wavespectra-4.0.0}/pyproject.toml +39 -8
- wavespectra-4.0.0/setup.py +16 -0
- wavespectra-4.0.0/tests/test_cli.py +103 -0
- wavespectra-4.0.0/tests/test_cookiecutter_template.py +37 -0
- wavespectra-4.0.0/tests/test_partition.py +397 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/tests/test_specarray.py +32 -16
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/__init__.py +11 -7
- wavespectra-4.0.0/wavespectra/cli.py +196 -0
- wavespectra-4.0.0/wavespectra/construct/__init__.py +190 -0
- wavespectra-4.0.0/wavespectra/construct/direction.py +104 -0
- wavespectra-4.0.0/wavespectra/construct/frequency.py +210 -0
- wavespectra-4.0.0/wavespectra/core/__init__.py +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/core/attributes.py +9 -7
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/core/attributes.yml +42 -1
- wavespectra-4.0.0/wavespectra/core/fitting.py +140 -0
- wavespectra-4.0.0/wavespectra/core/npstats.py +254 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/core/select.py +16 -7
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/core/swan.py +5 -5
- wavespectra-4.0.0/wavespectra/core/utils.py +459 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/core/xrstats.py +98 -14
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/dataset.py +6 -5
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/era5.py +23 -1
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/funwave.py +24 -4
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/json.py +20 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/ncswan.py +30 -7
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/ndbc.py +49 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/ndbc_ascii.py +28 -28
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/netcdf.py +36 -1
- wavespectra-4.0.0/wavespectra/input/obscape.py +267 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/octopus.py +25 -5
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/spotter.py +27 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/swan.py +29 -2
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/triaxys.py +33 -2
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/wavespectra.py +22 -2
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/ww3.py +27 -3
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/ww3_station.py +21 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/wwm.py +22 -1
- wavespectra-4.0.0/wavespectra/input/xwaves.py +67 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/funwave.py +2 -2
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/netcdf.py +3 -1
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/octopus.py +23 -22
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/orcaflex.py +16 -13
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/ww3.py +10 -3
- wavespectra-4.0.0/wavespectra/partition/__init__.py +0 -0
- wavespectra-4.0.0/wavespectra/partition/hanson_and_phillips_2001.py +295 -0
- wavespectra-4.0.0/wavespectra/partition/partition.py +1288 -0
- wavespectra-4.0.0/wavespectra/partition/specpart/specpartc.c +475 -0
- wavespectra-4.0.0/wavespectra/partition/specpart/specpartc_wrap.c +61 -0
- wavespectra-4.0.0/wavespectra/partition/tracking.py +356 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/plot.py +13 -8
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/specarray.py +523 -234
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/specdataset.py +2 -2
- {wavespectra-3.18.0 → wavespectra-4.0.0/wavespectra.egg-info}/PKG-INFO +8 -8
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra.egg-info/SOURCES.txt +13 -6
- wavespectra-4.0.0/wavespectra.egg-info/entry_points.txt +20 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra.egg-info/requires.txt +5 -6
- wavespectra-3.18.0/setup.py +0 -15
- wavespectra-3.18.0/tests/test_cookiecutter_template.py +0 -37
- wavespectra-3.18.0/wavespectra/cli.py +0 -17
- wavespectra-3.18.0/wavespectra/construct/__init__.py +0 -4
- wavespectra-3.18.0/wavespectra/construct/helpers.py +0 -101
- wavespectra-3.18.0/wavespectra/construct/jonswap.py +0 -50
- wavespectra-3.18.0/wavespectra/construct/ochihubble.py +0 -57
- wavespectra-3.18.0/wavespectra/core/__init__.py +0 -4
- wavespectra-3.18.0/wavespectra/core/npstats.py +0 -147
- wavespectra-3.18.0/wavespectra/core/utils.py +0 -283
- wavespectra-3.18.0/wavespectra/core/watershed.py +0 -188
- wavespectra-3.18.0/wavespectra/specpart/specpart.f90 +0 -499
- wavespectra-3.18.0/wavespectra/specpart/specpart.pyf +0 -20
- wavespectra-3.18.0/wavespectra.egg-info/entry_points.txt +0 -2
- {wavespectra-3.18.0 → wavespectra-4.0.0}/AUTHORS.rst +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/LICENSE.txt +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/README.rst +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/setup.cfg +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/input/__init__.py +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/__init__.py +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/json.py +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/swan.py +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra/output/ww3.yml +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra.egg-info/dependency_links.txt +0 -0
- {wavespectra-3.18.0 → wavespectra-4.0.0}/wavespectra.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wavespectra
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: Library for ocean wave spectra
|
|
5
5
|
Author-email: Wavespectra Developers <r.guedes@oceanum.science>
|
|
6
6
|
Maintainer-email: Rafael Guedes <r.guedes@oceanum.science>, Tom Durrant <t.durrant@oceanum.science>
|
|
@@ -41,22 +41,18 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
41
41
|
Classifier: Programming Language :: Python :: 3.9
|
|
42
42
|
Classifier: Programming Language :: Python :: 3.10
|
|
43
43
|
Classifier: Programming Language :: Python :: 3.11
|
|
44
|
-
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
45
|
+
Requires-Python: >=3.8
|
|
45
46
|
Description-Content-Type: text/x-rst
|
|
46
47
|
License-File: LICENSE.txt
|
|
47
48
|
License-File: AUTHORS.rst
|
|
48
49
|
Requires-Dist: click
|
|
49
|
-
Requires-Dist: cmocean
|
|
50
50
|
Requires-Dist: dask
|
|
51
51
|
Requires-Dist: matplotlib
|
|
52
|
-
Requires-Dist: numba
|
|
53
|
-
Requires-Dist: pandas
|
|
54
52
|
Requires-Dist: python-dateutil
|
|
55
53
|
Requires-Dist: pyyaml
|
|
56
54
|
Requires-Dist: sortedcontainers
|
|
57
55
|
Requires-Dist: scipy
|
|
58
|
-
Requires-Dist: toolz
|
|
59
|
-
Requires-Dist: typing_extensions
|
|
60
56
|
Requires-Dist: xarray
|
|
61
57
|
Provides-Extra: test
|
|
62
58
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -65,13 +61,17 @@ Requires-Dist: ruff; extra == "test"
|
|
|
65
61
|
Provides-Extra: extra
|
|
66
62
|
Requires-Dist: fsspec; extra == "extra"
|
|
67
63
|
Requires-Dist: gcsfs; extra == "extra"
|
|
64
|
+
Requires-Dist: h5netcdf; extra == "extra"
|
|
68
65
|
Requires-Dist: netCDF4; extra == "extra"
|
|
69
66
|
Requires-Dist: zarr; extra == "extra"
|
|
70
|
-
Requires-Dist: h5netcdf; extra == "extra"
|
|
71
67
|
Provides-Extra: docs
|
|
72
68
|
Requires-Dist: autodoc; extra == "docs"
|
|
69
|
+
Requires-Dist: cmocean; extra == "docs"
|
|
73
70
|
Requires-Dist: ipython; extra == "docs"
|
|
71
|
+
Requires-Dist: pickleshare; extra == "docs"
|
|
74
72
|
Requires-Dist: pydata_sphinx_theme; extra == "docs"
|
|
73
|
+
Requires-Dist: sphinxcontrib-programoutput; extra == "docs"
|
|
74
|
+
Requires-Dist: sphinx-book-theme; extra == "docs"
|
|
75
75
|
Requires-Dist: sphinx; extra == "docs"
|
|
76
76
|
Requires-Dist: sphinx-autobuild; extra == "docs"
|
|
77
77
|
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
@@ -29,21 +29,17 @@ classifiers = [
|
|
|
29
29
|
"Programming Language :: Python :: 3.9",
|
|
30
30
|
"Programming Language :: Python :: 3.10",
|
|
31
31
|
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
32
33
|
]
|
|
33
|
-
requires-python = ">=3.8
|
|
34
|
+
requires-python = ">=3.8"
|
|
34
35
|
dependencies = [
|
|
35
36
|
"click",
|
|
36
|
-
"cmocean",
|
|
37
37
|
"dask",
|
|
38
38
|
"matplotlib",
|
|
39
|
-
"numba",
|
|
40
|
-
"pandas",
|
|
41
39
|
"python-dateutil",
|
|
42
40
|
"pyyaml",
|
|
43
41
|
"sortedcontainers",
|
|
44
42
|
"scipy",
|
|
45
|
-
"toolz",
|
|
46
|
-
"typing_extensions",
|
|
47
43
|
"xarray",
|
|
48
44
|
]
|
|
49
45
|
dynamic = ["version"]
|
|
@@ -57,19 +53,41 @@ dynamic = ["version"]
|
|
|
57
53
|
[project.scripts]
|
|
58
54
|
wavespectra = "wavespectra.cli:main"
|
|
59
55
|
|
|
56
|
+
[project.entry-points."xarray.backends"]
|
|
57
|
+
era5 = "wavespectra.input.era5:ERA5BackendEntrypoint"
|
|
58
|
+
funwave = "wavespectra.input.funwave:FunwaveBackendEntrypoint"
|
|
59
|
+
json = "wavespectra.input.json:JsonBackendEntrypoint"
|
|
60
|
+
ncswan = "wavespectra.input.ncswan:NCSwanBackendEntrypoint"
|
|
61
|
+
ndbc = "wavespectra.input.ndbc:NDBCBackendEntrypoint"
|
|
62
|
+
ndbc_ascii = "wavespectra.input.ndbc_ascii:NDBCASCIIBackendEntrypoint"
|
|
63
|
+
netcdf = "wavespectra.input.netcdf:NetCDFBackendEntrypoint"
|
|
64
|
+
octopus = "wavespectra.input.octopus:OctopusBackendEntrypoint"
|
|
65
|
+
spotter = "wavespectra.input.spotter:SpotterBackendEntrypoint"
|
|
66
|
+
swan = "wavespectra.input.swan:SWANBackendEntrypoint"
|
|
67
|
+
triaxys = "wavespectra.input.triaxys:TRIAXYSBackendEntrypoint"
|
|
68
|
+
wavespectra = "wavespectra.input.wavespectra:WavespectraBackendEntrypoint"
|
|
69
|
+
wwm = "wavespectra.input.wwm:WWMBackendEntrypoint"
|
|
70
|
+
ww3 = "wavespectra.input.ww3:WW3BackendEntrypoint"
|
|
71
|
+
ww3_station = "wavespectra.input.ww3_station:WW3StationBackendEntrypoint"
|
|
72
|
+
xwaves = "wavespectra.input.xwaves:XWavesBackendEntrypoint"
|
|
73
|
+
|
|
60
74
|
[project.optional-dependencies]
|
|
61
75
|
test = ["pytest", "tox", "ruff"]
|
|
62
76
|
extra = [
|
|
63
77
|
"fsspec",
|
|
64
78
|
"gcsfs",
|
|
79
|
+
"h5netcdf",
|
|
65
80
|
"netCDF4",
|
|
66
81
|
"zarr",
|
|
67
|
-
"h5netcdf",
|
|
68
82
|
]
|
|
69
83
|
docs = [
|
|
70
84
|
"autodoc",
|
|
85
|
+
"cmocean",
|
|
71
86
|
"ipython",
|
|
87
|
+
"pickleshare",
|
|
72
88
|
"pydata_sphinx_theme",
|
|
89
|
+
"sphinxcontrib-programoutput",
|
|
90
|
+
"sphinx-book-theme",
|
|
73
91
|
"sphinx",
|
|
74
92
|
"sphinx-autobuild",
|
|
75
93
|
"sphinx-autodoc-typehints",
|
|
@@ -79,11 +97,14 @@ docs = [
|
|
|
79
97
|
[tool.setuptools.package-data]
|
|
80
98
|
"wavespectra.core" = ["*.yml"]
|
|
81
99
|
"wavespectra.output" = ["*.yml"]
|
|
82
|
-
"wavespectra.
|
|
100
|
+
"wavespectra.specpartc" = ["specpartc.c", "specpartc.h", "specpartc_wrap.c"]
|
|
83
101
|
|
|
84
102
|
[tool.setuptools.dynamic]
|
|
85
103
|
version = {attr = "wavespectra.__version__"}
|
|
86
104
|
|
|
105
|
+
[tool.ruff]
|
|
106
|
+
line-length = 88
|
|
107
|
+
|
|
87
108
|
[tool.ruff.extend-per-file-ignores]
|
|
88
109
|
"wavespectra/__init__.py" = ["F401"]
|
|
89
110
|
"wavespectra/specdataset.py" = ["F401"]
|
|
@@ -95,6 +116,16 @@ version = {attr = "wavespectra.__version__"}
|
|
|
95
116
|
"wavespectra/input/spotter.py" = ["F401"]
|
|
96
117
|
"wavespectra/input/dataset.py" = ["F401"]
|
|
97
118
|
"wavespectra/input/swan.py" = ["F401"]
|
|
119
|
+
"wavespectra/construct.py" = ["F401"]
|
|
120
|
+
"wavespectra/directional.py" = ["F401"]
|
|
121
|
+
"wavespectra/fit/pierson_moskowitz.py" = ["F401"]
|
|
122
|
+
"wavespectra/fit/tma.py" = ["F401"]
|
|
123
|
+
"wavespectra/input/funwave.py" = ["F401"]
|
|
124
|
+
"wavespectra/input/ndbc.py" = ["F401"]
|
|
125
|
+
"wavespectra/input/ndbc_ascii.py" = ["F401"]
|
|
126
|
+
"wavespectra/input/octopus.py" = ["F401"]
|
|
127
|
+
"wavespectra/input/wavespectra.py" = ["F401"]
|
|
128
|
+
"wavespectra/input/xwaves.py" = ["F401"]
|
|
98
129
|
|
|
99
130
|
[tool.tox]
|
|
100
131
|
legacy_tox_ini = """
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from setuptools import setup, Extension
|
|
2
|
+
from numpy import get_include as get_numpy_include
|
|
3
|
+
|
|
4
|
+
setup(
|
|
5
|
+
name="wavespectra",
|
|
6
|
+
ext_modules=[
|
|
7
|
+
Extension(
|
|
8
|
+
name="wavespectra.partition.specpartc",
|
|
9
|
+
sources=[
|
|
10
|
+
"wavespectra/partition/specpart/specpartc_wrap.c",
|
|
11
|
+
"wavespectra/partition/specpart/specpartc.c",
|
|
12
|
+
],
|
|
13
|
+
include_dirs=[get_numpy_include()],
|
|
14
|
+
)
|
|
15
|
+
],
|
|
16
|
+
)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Test model wrapper."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import pytest
|
|
4
|
+
import xarray as xr
|
|
5
|
+
from click.testing import CliRunner
|
|
6
|
+
|
|
7
|
+
from wavespectra import cli
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DATADIR = Path(__file__).parent / "sample_files"
|
|
11
|
+
INFILE = str(DATADIR / "ww3file.nc")
|
|
12
|
+
ENGINE = "ww3"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture(scope="module")
|
|
16
|
+
def runner():
|
|
17
|
+
instance = CliRunner()
|
|
18
|
+
yield instance
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_main(runner):
|
|
22
|
+
result = runner.invoke(cli.main)
|
|
23
|
+
assert result.exit_code == 0
|
|
24
|
+
assert "main" in result.output
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_main_convert(runner):
|
|
28
|
+
result = runner.invoke(cli.main, "convert")
|
|
29
|
+
assert result.exit_code == 0
|
|
30
|
+
assert "convert" in result.output
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_main_convert_format(runner, tmpdir):
|
|
34
|
+
OUTFILE = str(tmpdir / "outspec.nc")
|
|
35
|
+
|
|
36
|
+
result = runner.invoke(
|
|
37
|
+
cli.main, ["convert", "format", INFILE, ENGINE, OUTFILE, "swan"]
|
|
38
|
+
)
|
|
39
|
+
assert result.exit_code == 0
|
|
40
|
+
|
|
41
|
+
xr.open_dataset(OUTFILE, engine="swan")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_main_convert_stats(runner, tmpdir):
|
|
45
|
+
OUTFILE = str(tmpdir / "outstats.nc")
|
|
46
|
+
|
|
47
|
+
result = runner.invoke(
|
|
48
|
+
cli.main, ["convert", "stats", INFILE, ENGINE, OUTFILE, "-p", "hs", "-p", "tp"]
|
|
49
|
+
)
|
|
50
|
+
assert result.exit_code == 0
|
|
51
|
+
|
|
52
|
+
dset = xr.open_dataset(OUTFILE)
|
|
53
|
+
assert "hs" in dset.data_vars and "tp" in dset.data_vars
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_main_reconstruct(runner):
|
|
57
|
+
result = runner.invoke(cli.main, "reconstruct")
|
|
58
|
+
assert result.exit_code == 0
|
|
59
|
+
assert "reconstruct" in result.output
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_main_reconstruct_spectra(runner, tmpdir):
|
|
63
|
+
OUTFILE = str(tmpdir / "outspec.nc")
|
|
64
|
+
|
|
65
|
+
result = runner.invoke(
|
|
66
|
+
cli.main, ["reconstruct", "spectra", INFILE, ENGINE, OUTFILE]
|
|
67
|
+
)
|
|
68
|
+
assert result.exit_code == 0
|
|
69
|
+
|
|
70
|
+
result = runner.invoke(cli.main, ["reconstruct", "spectra", INFILE, ENGINE, INFILE])
|
|
71
|
+
assert isinstance(result.exception, ValueError)
|
|
72
|
+
|
|
73
|
+
result = runner.invoke(
|
|
74
|
+
cli.main,
|
|
75
|
+
[
|
|
76
|
+
"reconstruct",
|
|
77
|
+
"spectra",
|
|
78
|
+
INFILE,
|
|
79
|
+
ENGINE,
|
|
80
|
+
OUTFILE,
|
|
81
|
+
"-p",
|
|
82
|
+
"3",
|
|
83
|
+
"-d",
|
|
84
|
+
"cartwright,cartwright,cartwright",
|
|
85
|
+
],
|
|
86
|
+
)
|
|
87
|
+
assert result.exit_code == 0
|
|
88
|
+
|
|
89
|
+
result = runner.invoke(
|
|
90
|
+
cli.main,
|
|
91
|
+
[
|
|
92
|
+
"reconstruct",
|
|
93
|
+
"spectra",
|
|
94
|
+
INFILE,
|
|
95
|
+
ENGINE,
|
|
96
|
+
OUTFILE,
|
|
97
|
+
"-p",
|
|
98
|
+
"3",
|
|
99
|
+
"-f",
|
|
100
|
+
"jonswap,jonswap,jonswap",
|
|
101
|
+
],
|
|
102
|
+
)
|
|
103
|
+
assert result.exit_code == 0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""Tests for `wavespectra` package."""
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from click.testing import CliRunner
|
|
9
|
+
|
|
10
|
+
from wavespectra import cli
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# @pytest.fixture
|
|
14
|
+
# def response():
|
|
15
|
+
# """Sample pytest fixture.
|
|
16
|
+
|
|
17
|
+
# See more at: http://doc.pytest.org/en/latest/fixture.html
|
|
18
|
+
# """
|
|
19
|
+
# # import requests
|
|
20
|
+
# # return requests.get('https://github.com/audreyr/cookiecutter-pypackage')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# def test_content(response):
|
|
24
|
+
# """Sample pytest test function with the pytest fixture as an argument."""
|
|
25
|
+
# # from bs4 import BeautifulSoup
|
|
26
|
+
# # assert 'GitHub' in BeautifulSoup(response.content).title.string
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# def test_command_line_interface():
|
|
30
|
+
# """Test the CLI."""
|
|
31
|
+
# runner = CliRunner()
|
|
32
|
+
# result = runner.invoke(cli.main)
|
|
33
|
+
# assert result.exit_code == 0
|
|
34
|
+
# assert "wavespectra.cli.main" in result.output
|
|
35
|
+
# help_result = runner.invoke(cli.main, ["--help"])
|
|
36
|
+
# assert help_result.exit_code == 0
|
|
37
|
+
# assert "--help Show this message and exit." in help_result.output
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import numpy as np
|
|
4
|
+
import xarray as xr
|
|
5
|
+
|
|
6
|
+
from wavespectra import read_ww3
|
|
7
|
+
from wavespectra.partition.partition import (
|
|
8
|
+
Partition,
|
|
9
|
+
np_ptm1,
|
|
10
|
+
np_ptm2,
|
|
11
|
+
np_ptm3,
|
|
12
|
+
np_hp01,
|
|
13
|
+
)
|
|
14
|
+
from wavespectra.core.utils import waveage
|
|
15
|
+
from wavespectra.core import npstats
|
|
16
|
+
from wavespectra.partition.tracking import track_partitions
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
HERE = Path(__file__).parent
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BasePTM:
|
|
23
|
+
def setup_class(self):
|
|
24
|
+
self.dset = read_ww3(HERE / "sample_files/ww3file.nc")
|
|
25
|
+
self.efth = self.dset.isel(time=0, site=0).efth.values
|
|
26
|
+
self.freq = self.dset.freq.values
|
|
27
|
+
self.dir = self.dset.dir.values
|
|
28
|
+
self.pt = Partition(self.dset)
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def hs_full(self):
|
|
32
|
+
return npstats.hs(self.efth, self.freq, self.dir)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def hs_from_partitions(self):
|
|
36
|
+
hs_partitions = [npstats.hs(efth, self.freq, self.dir) for efth in self.out]
|
|
37
|
+
return np.sqrt(np.sum(np.power(hs_partitions, 2)))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestPTM1(BasePTM):
|
|
41
|
+
def setup_class(self):
|
|
42
|
+
super().setup_class(self)
|
|
43
|
+
self.wspd = self.dset.isel(time=0, site=0).wspd.values
|
|
44
|
+
self.wdir = self.dset.isel(time=0, site=0).wdir.values
|
|
45
|
+
self.dpt = self.dset.isel(time=0, site=0).dpt.values
|
|
46
|
+
self.agefac = 1.7
|
|
47
|
+
self.wscut = 0.3333
|
|
48
|
+
self.swells = 3
|
|
49
|
+
|
|
50
|
+
def _exec(self, **kwargs):
|
|
51
|
+
out = np_ptm1(
|
|
52
|
+
spectrum=self.efth,
|
|
53
|
+
spectrum_smooth=self.efth,
|
|
54
|
+
freq=self.freq,
|
|
55
|
+
dir=self.dir,
|
|
56
|
+
wspd=self.wspd,
|
|
57
|
+
wdir=self.wdir,
|
|
58
|
+
dpt=self.dpt,
|
|
59
|
+
agefac=kwargs.get("agefac", self.agefac),
|
|
60
|
+
wscut=kwargs.get("wscut", self.wscut),
|
|
61
|
+
swells=kwargs.get("swells", self.swells),
|
|
62
|
+
)
|
|
63
|
+
return out
|
|
64
|
+
|
|
65
|
+
def test_defaults(self):
|
|
66
|
+
self.out = self._exec()
|
|
67
|
+
assert self.out.shape[0] == self.swells + 1
|
|
68
|
+
assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
69
|
+
|
|
70
|
+
def test_request_all_partitions(self):
|
|
71
|
+
self.out = self._exec(swells=None)
|
|
72
|
+
assert self.out.shape[0] == 3
|
|
73
|
+
assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
74
|
+
|
|
75
|
+
def test_request_less_partitions(self):
|
|
76
|
+
swells = 2
|
|
77
|
+
self.out = self._exec(swells=swells)
|
|
78
|
+
assert self.hs_from_partitions < self.hs_full
|
|
79
|
+
assert len(self.out) == swells + 1
|
|
80
|
+
|
|
81
|
+
def test_partition_class(self):
|
|
82
|
+
swells = 2
|
|
83
|
+
self.out = self._exec(swells=swells)
|
|
84
|
+
dspart = self.pt.ptm1(
|
|
85
|
+
wspd=self.dset.wspd,
|
|
86
|
+
wdir=self.dset.wdir,
|
|
87
|
+
dpt=self.dset.dpt,
|
|
88
|
+
swells=swells,
|
|
89
|
+
)
|
|
90
|
+
hs_dspart = np.sqrt(np.sum(dspart.isel(time=0, site=0).spec.hs().values ** 2))
|
|
91
|
+
assert hs_dspart == pytest.approx(self.hs_from_partitions, rel=1e-2)
|
|
92
|
+
|
|
93
|
+
def test_smoothing(self):
|
|
94
|
+
swells = 2
|
|
95
|
+
ds_nosmoothing = self.pt.ptm1(
|
|
96
|
+
wspd=self.dset.wspd,
|
|
97
|
+
wdir=self.dset.wdir,
|
|
98
|
+
dpt=self.dset.dpt,
|
|
99
|
+
swells=swells,
|
|
100
|
+
)
|
|
101
|
+
ds_smoothing = self.pt.ptm1(
|
|
102
|
+
wspd=self.dset.wspd,
|
|
103
|
+
wdir=self.dset.wdir,
|
|
104
|
+
dpt=self.dset.dpt,
|
|
105
|
+
swells=swells,
|
|
106
|
+
smooth=True,
|
|
107
|
+
)
|
|
108
|
+
assert not ds_nosmoothing.spec.hs().equals(ds_smoothing.spec.hs())
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class TestPTM2(BasePTM):
|
|
112
|
+
def setup_class(self):
|
|
113
|
+
super().setup_class(self)
|
|
114
|
+
self.wspd = self.dset.isel(time=0, site=0).wspd.values
|
|
115
|
+
self.wdir = self.dset.isel(time=0, site=0).wdir.values
|
|
116
|
+
self.dpt = self.dset.isel(time=0, site=0).dpt.values
|
|
117
|
+
self.agefac = 1.7
|
|
118
|
+
self.wscut = 0.3333
|
|
119
|
+
self.swells = 3
|
|
120
|
+
|
|
121
|
+
def _exec(self, **kwargs):
|
|
122
|
+
out = np_ptm2(
|
|
123
|
+
spectrum=self.efth,
|
|
124
|
+
spectrum_smooth=self.efth,
|
|
125
|
+
freq=self.freq,
|
|
126
|
+
dir=self.dir,
|
|
127
|
+
wspd=self.wspd,
|
|
128
|
+
wdir=self.wdir,
|
|
129
|
+
dpt=self.dpt,
|
|
130
|
+
agefac=kwargs.get("agefac", self.agefac),
|
|
131
|
+
wscut=kwargs.get("wscut", self.wscut),
|
|
132
|
+
swells=kwargs.get("swells", self.swells),
|
|
133
|
+
)
|
|
134
|
+
return out
|
|
135
|
+
|
|
136
|
+
def test_defaults(self):
|
|
137
|
+
self.out = self._exec()
|
|
138
|
+
assert self.out.shape[0] == self.swells + 2
|
|
139
|
+
assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
140
|
+
|
|
141
|
+
def test_request_all_partitions(self):
|
|
142
|
+
self.out = self._exec(swells=None)
|
|
143
|
+
assert self.out.shape[0] == 4
|
|
144
|
+
assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
145
|
+
|
|
146
|
+
def test_request_less_partitions(self):
|
|
147
|
+
swells = 2
|
|
148
|
+
self.out = self._exec(swells=swells)
|
|
149
|
+
assert self.hs_from_partitions < self.hs_full
|
|
150
|
+
assert len(self.out) == swells + 2
|
|
151
|
+
|
|
152
|
+
def test_less_partitions_combined(self):
|
|
153
|
+
swells = 2
|
|
154
|
+
self.out = self._exec(swells=swells)
|
|
155
|
+
assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
156
|
+
assert len(self.out) == swells + 2
|
|
157
|
+
|
|
158
|
+
def test_partition_class(self):
|
|
159
|
+
swells = 2
|
|
160
|
+
combine = False
|
|
161
|
+
self.out = self._exec(swells=swells)
|
|
162
|
+
dspart = self.pt.ptm2(
|
|
163
|
+
wspd=self.dset.wspd,
|
|
164
|
+
wdir=self.dset.wdir,
|
|
165
|
+
dpt=self.dset.dpt,
|
|
166
|
+
swells=swells,
|
|
167
|
+
)
|
|
168
|
+
hs_dspart = np.sqrt(np.sum(dspart.isel(time=0, site=0).spec.hs().values ** 2))
|
|
169
|
+
assert hs_dspart == pytest.approx(self.hs_from_partitions, rel=1e-2)
|
|
170
|
+
|
|
171
|
+
def test_smoothing(self):
|
|
172
|
+
swells = 2
|
|
173
|
+
ds_nosmoothing = self.pt.ptm1(
|
|
174
|
+
wspd=self.dset.wspd,
|
|
175
|
+
wdir=self.dset.wdir,
|
|
176
|
+
dpt=self.dset.dpt,
|
|
177
|
+
swells=swells,
|
|
178
|
+
)
|
|
179
|
+
ds_smoothing = self.pt.ptm1(
|
|
180
|
+
wspd=self.dset.wspd,
|
|
181
|
+
wdir=self.dset.wdir,
|
|
182
|
+
dpt=self.dset.dpt,
|
|
183
|
+
swells=swells,
|
|
184
|
+
smooth=True,
|
|
185
|
+
)
|
|
186
|
+
assert not ds_nosmoothing.spec.hs().equals(ds_smoothing.spec.hs())
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class TestPTM3(BasePTM):
|
|
190
|
+
def _exec(self, parts):
|
|
191
|
+
out = np_ptm3(
|
|
192
|
+
spectrum=self.dset.efth.isel(time=0, site=0).values,
|
|
193
|
+
spectrum_smooth=self.dset.efth.isel(time=0, site=0).values,
|
|
194
|
+
freq=self.dset.freq.values,
|
|
195
|
+
dir=self.dset.dir.values,
|
|
196
|
+
parts=parts,
|
|
197
|
+
)
|
|
198
|
+
return out
|
|
199
|
+
|
|
200
|
+
def test_request_all_partitions(self):
|
|
201
|
+
self.out = self._exec(parts=None)
|
|
202
|
+
assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
203
|
+
|
|
204
|
+
def test_request_less_partitions(self):
|
|
205
|
+
parts = 2
|
|
206
|
+
self.out = self._exec(parts=parts)
|
|
207
|
+
assert self.hs_from_partitions < self.hs_full
|
|
208
|
+
assert len(self.out) == parts
|
|
209
|
+
|
|
210
|
+
def test_more_partitions(self):
|
|
211
|
+
parts = 5
|
|
212
|
+
self.out = self._exec(parts=parts)
|
|
213
|
+
assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
214
|
+
assert len(self.out) == parts
|
|
215
|
+
assert self.out[3:4].sum() == 0
|
|
216
|
+
|
|
217
|
+
def test_partition_class(self):
|
|
218
|
+
parts = 2
|
|
219
|
+
self.out = self._exec(parts=parts)
|
|
220
|
+
dspart = self.pt.ptm3(parts=parts, smooth=False)
|
|
221
|
+
hs_dspart = np.sqrt(np.sum(dspart.isel(time=0, site=0).spec.hs().values ** 2))
|
|
222
|
+
assert hs_dspart == pytest.approx(self.hs_from_partitions)
|
|
223
|
+
|
|
224
|
+
def test_partition_class_smoothing(self):
|
|
225
|
+
parts = 3
|
|
226
|
+
ds_nosmoothing = self.pt.ptm3(parts=parts)
|
|
227
|
+
ds_smoothing = self.pt.ptm3(parts=parts, smooth=True)
|
|
228
|
+
assert not ds_nosmoothing.spec.hs().equals(ds_smoothing.spec.hs())
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class TestPTM4(BasePTM):
|
|
232
|
+
def test_agefac(self):
|
|
233
|
+
ds_agefac15 = self.pt.ptm4(
|
|
234
|
+
wspd=self.dset.wspd, wdir=self.dset.wdir, dpt=self.dset.dpt, agefac=1.5
|
|
235
|
+
)
|
|
236
|
+
ds_agefac17 = self.pt.ptm4(
|
|
237
|
+
wspd=self.dset.wspd,
|
|
238
|
+
wdir=self.dset.wdir,
|
|
239
|
+
dpt=self.dset.dpt,
|
|
240
|
+
)
|
|
241
|
+
sea_agefac15 = ds_agefac15.isel(part=0).spec.hs()
|
|
242
|
+
sea_agefac17 = ds_agefac17.isel(part=0).spec.hs()
|
|
243
|
+
assert np.all((sea_agefac17.values - sea_agefac15.values) > 0)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class TestPTM5(BasePTM):
|
|
247
|
+
def test_default(self):
|
|
248
|
+
fcut = 0.1
|
|
249
|
+
ds = self.pt.ptm5(fcut=0.1)
|
|
250
|
+
assert fcut in ds.freq
|
|
251
|
+
assert ds.isel(part=0).sel(freq=slice(None, fcut - 0.001)).spec.hs().max() == 0
|
|
252
|
+
assert ds.isel(part=1).sel(freq=slice(fcut + 0.001, None)).spec.hs().max() == 0
|
|
253
|
+
|
|
254
|
+
def test_not_interpolate(self):
|
|
255
|
+
fcut = 0.1
|
|
256
|
+
ds = self.pt.ptm5(fcut=0.1, interpolate=False)
|
|
257
|
+
assert fcut not in ds.freq
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class TestHP01(BasePTM):
|
|
261
|
+
def setup_class(self):
|
|
262
|
+
super().setup_class(self)
|
|
263
|
+
self.agefac = 1.7
|
|
264
|
+
self.wscut = 0.3333
|
|
265
|
+
self.swells = 3
|
|
266
|
+
wspd = self.dset.wspd.isel(time=0, site=0)
|
|
267
|
+
wdir = self.dset.wdir.isel(time=0, site=0)
|
|
268
|
+
dpt = self.dset.dpt.isel(time=0, site=0)
|
|
269
|
+
self.windseamask = waveage(
|
|
270
|
+
self.dset.freq, self.dset.dir, wspd, wdir, dpt, self.agefac
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
def _exec(self, **kwargs):
|
|
274
|
+
out = np_hp01(
|
|
275
|
+
spectrum=self.efth,
|
|
276
|
+
spectrum_smooth=self.efth,
|
|
277
|
+
windseamask=self.windseamask,
|
|
278
|
+
freq=self.freq,
|
|
279
|
+
dir=self.dir,
|
|
280
|
+
wscut=kwargs.get("wscut", self.wscut),
|
|
281
|
+
swells=kwargs.get("swells", self.swells),
|
|
282
|
+
)
|
|
283
|
+
return out
|
|
284
|
+
|
|
285
|
+
def test_defaults(self):
|
|
286
|
+
self.out = self._exec()
|
|
287
|
+
# assert self.out.shape[0] == self.swells + 1
|
|
288
|
+
# assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
289
|
+
|
|
290
|
+
# def test_request_all_partitions(self):
|
|
291
|
+
# self.out = self._exec(swells=None)
|
|
292
|
+
# assert self.out.shape[0] == 3
|
|
293
|
+
# assert self.hs_full == pytest.approx(self.hs_from_partitions)
|
|
294
|
+
|
|
295
|
+
# def test_request_less_partitions(self):
|
|
296
|
+
# swells = 2
|
|
297
|
+
# self.out = self._exec(swells=swells)
|
|
298
|
+
# assert self.hs_from_partitions < self.hs_full
|
|
299
|
+
# assert len(self.out) == swells + 1
|
|
300
|
+
|
|
301
|
+
def test_partition_class(self):
|
|
302
|
+
swells = 2
|
|
303
|
+
self.out = self._exec(swells=swells)
|
|
304
|
+
dspart = self.pt.hp01(
|
|
305
|
+
wspd=self.dset.wspd,
|
|
306
|
+
wdir=self.dset.wdir,
|
|
307
|
+
dpt=self.dset.dpt,
|
|
308
|
+
agefac=self.agefac,
|
|
309
|
+
swells=swells,
|
|
310
|
+
)
|
|
311
|
+
hs_dspart = np.sqrt(np.sum(dspart.isel(time=0, site=0).spec.hs().values ** 2))
|
|
312
|
+
# assert hs_dspart == pytest.approx(self.hs_from_partitions, rel=1e-2)
|
|
313
|
+
|
|
314
|
+
# def test_smoothing(self):
|
|
315
|
+
# swells = 2
|
|
316
|
+
# ds_nosmoothing = self.pt.ptm1(
|
|
317
|
+
# wspd=self.dset.wspd,
|
|
318
|
+
# wdir=self.dset.wdir,
|
|
319
|
+
# dpt=self.dset.dpt,
|
|
320
|
+
# swells=swells,
|
|
321
|
+
# )
|
|
322
|
+
# ds_smoothing = self.pt.ptm1(
|
|
323
|
+
# wspd=self.dset.wspd,
|
|
324
|
+
# wdir=self.dset.wdir,
|
|
325
|
+
# dpt=self.dset.dpt,
|
|
326
|
+
# swells=swells,
|
|
327
|
+
# smooth=True,
|
|
328
|
+
# )
|
|
329
|
+
# assert not ds_nosmoothing.spec.hs().equals(ds_smoothing.spec.hs())
|
|
330
|
+
|
|
331
|
+
# def test_compare_legacy(self):
|
|
332
|
+
# kwargs = {"agefac": self.agefac, "wscut": self.wscut, "swells": self.swells}
|
|
333
|
+
# old = self.dset.spec.partition_deprecated(
|
|
334
|
+
# wsp_darr=self.dset.wspd,
|
|
335
|
+
# wdir_darr=self.dset.wdir,
|
|
336
|
+
# dep_darr=self.dset.dpt,
|
|
337
|
+
# **kwargs,
|
|
338
|
+
# )
|
|
339
|
+
# new = self.pt.ptm1(
|
|
340
|
+
# wspd=self.dset.wspd, wdir=self.dset.wdir, dpt=self.dset.dpt, **kwargs,
|
|
341
|
+
# )
|
|
342
|
+
# assert np.array_equal(old.spec.hs().values, new.spec.hs().values)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class TestBbox(BasePTM):
|
|
346
|
+
def test_default(self):
|
|
347
|
+
bboxes = [
|
|
348
|
+
dict(fmin=0.1, fmax=0.2, dmin=None, dmax=None),
|
|
349
|
+
dict(fmin=0.2, fmax=0.3, dmin=None, dmax=None),
|
|
350
|
+
]
|
|
351
|
+
ds = self.pt.bbox(bboxes=bboxes)
|
|
352
|
+
assert ds.part.size == 3
|
|
353
|
+
|
|
354
|
+
def test_ignore_dlimits(self):
|
|
355
|
+
bboxes = [dict(fmin=0.1, fmax=0.2), dict(fmin=0.2, fmax=0.3)]
|
|
356
|
+
ds = self.pt.bbox(bboxes=bboxes)
|
|
357
|
+
assert ds.part.size == 3
|
|
358
|
+
|
|
359
|
+
def test_overlap(self):
|
|
360
|
+
bboxes = [
|
|
361
|
+
dict(fmin=0.1, fmax=0.2, dmin=None, dmax=None),
|
|
362
|
+
dict(fmin=0.15, fmax=0.3, dmin=None, dmax=None),
|
|
363
|
+
]
|
|
364
|
+
with pytest.raises(ValueError):
|
|
365
|
+
ds = self.pt.bbox(bboxes=bboxes)
|
|
366
|
+
|
|
367
|
+
def test_freq_increasing(self):
|
|
368
|
+
bboxes = [dict(fmin=0.2, fmax=0.1, dmin=None, dmax=None)]
|
|
369
|
+
with pytest.raises(ValueError):
|
|
370
|
+
ds = self.pt.bbox(bboxes=bboxes)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class TestPartitionAndTrack(BasePTM):
|
|
374
|
+
def setup_class(self):
|
|
375
|
+
super().setup_class(self)
|
|
376
|
+
|
|
377
|
+
def test_partition_class(self):
|
|
378
|
+
swells = 2
|
|
379
|
+
dspart = self.pt.ptm1(
|
|
380
|
+
wspd=self.dset.wspd,
|
|
381
|
+
wdir=self.dset.wdir,
|
|
382
|
+
dpt=self.dset.dpt,
|
|
383
|
+
swells=swells,
|
|
384
|
+
)
|
|
385
|
+
stats = dspart.spec.stats(["fp", "dpm"]).load()
|
|
386
|
+
part_ids = track_partitions(stats, wspd=self.dset.wspd)
|
|
387
|
+
|
|
388
|
+
def test_class(self):
|
|
389
|
+
swells = 2
|
|
390
|
+
dspart = self.pt.ptm1_track(
|
|
391
|
+
wspd=self.dset.wspd,
|
|
392
|
+
wdir=self.dset.wdir,
|
|
393
|
+
dpt=self.dset.dpt,
|
|
394
|
+
swells=swells,
|
|
395
|
+
)
|
|
396
|
+
assert "part_id" in dspart
|
|
397
|
+
assert "npart_id" in dspart
|