wavespectra 4.4.3__tar.gz → 4.5.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.
Files changed (72) hide show
  1. {wavespectra-4.4.3/wavespectra.egg-info → wavespectra-4.5.0}/PKG-INFO +1 -1
  2. {wavespectra-4.4.3 → wavespectra-4.5.0}/pyproject.toml +1 -0
  3. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/__init__.py +1 -1
  4. wavespectra-4.5.0/wavespectra/input/funwave_new.py +104 -0
  5. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/funwave.py +111 -2
  6. {wavespectra-4.4.3 → wavespectra-4.5.0/wavespectra.egg-info}/PKG-INFO +1 -1
  7. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra.egg-info/SOURCES.txt +1 -0
  8. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra.egg-info/entry_points.txt +1 -0
  9. {wavespectra-4.4.3 → wavespectra-4.5.0}/AUTHORS.rst +0 -0
  10. {wavespectra-4.4.3 → wavespectra-4.5.0}/LICENSE.txt +0 -0
  11. {wavespectra-4.4.3 → wavespectra-4.5.0}/README.rst +0 -0
  12. {wavespectra-4.4.3 → wavespectra-4.5.0}/setup.cfg +0 -0
  13. {wavespectra-4.4.3 → wavespectra-4.5.0}/setup.py +0 -0
  14. {wavespectra-4.4.3 → wavespectra-4.5.0}/tests/test_cli.py +0 -0
  15. {wavespectra-4.4.3 → wavespectra-4.5.0}/tests/test_cookiecutter_template.py +0 -0
  16. {wavespectra-4.4.3 → wavespectra-4.5.0}/tests/test_partition.py +0 -0
  17. {wavespectra-4.4.3 → wavespectra-4.5.0}/tests/test_specarray.py +0 -0
  18. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/cli.py +0 -0
  19. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/construct/__init__.py +0 -0
  20. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/construct/direction.py +0 -0
  21. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/construct/frequency.py +0 -0
  22. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/__init__.py +0 -0
  23. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/attributes.py +0 -0
  24. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/attributes.yml +0 -0
  25. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/fitting.py +0 -0
  26. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/npstats.py +0 -0
  27. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/select.py +0 -0
  28. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/swan.py +0 -0
  29. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/utils.py +0 -0
  30. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/core/xrstats.py +0 -0
  31. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/__init__.py +0 -0
  32. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/awac.py +0 -0
  33. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/dataset.py +0 -0
  34. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/datawell.py +0 -0
  35. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/era5.py +0 -0
  36. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/funwave.py +0 -0
  37. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/json.py +0 -0
  38. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/ncswan.py +0 -0
  39. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/ndbc.py +0 -0
  40. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/ndbc_ascii.py +0 -0
  41. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/netcdf.py +0 -0
  42. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/obscape.py +0 -0
  43. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/octopus.py +0 -0
  44. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/spotter.py +0 -0
  45. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/swan.py +0 -0
  46. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/triaxys.py +0 -0
  47. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/wavespectra.py +0 -0
  48. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/ww3.py +0 -0
  49. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/ww3_station.py +0 -0
  50. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/wwm.py +0 -0
  51. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/input/xwaves.py +0 -0
  52. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/__init__.py +0 -0
  53. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/json.py +0 -0
  54. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/netcdf.py +0 -0
  55. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/octopus.py +0 -0
  56. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/orcaflex.py +0 -0
  57. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/swan.py +0 -0
  58. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/ww3.py +0 -0
  59. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/output/ww3.yml +0 -0
  60. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/partition/__init__.py +0 -0
  61. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/partition/hanson_and_phillips_2001.py +0 -0
  62. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/partition/partition.py +0 -0
  63. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/partition/specpart/specpart.c +0 -0
  64. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/partition/specpart/specpart.h +0 -0
  65. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/partition/specpart/specpart_wrap.c +0 -0
  66. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/partition/tracking.py +0 -0
  67. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/plot.py +0 -0
  68. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/specarray.py +0 -0
  69. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra/specdataset.py +0 -0
  70. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra.egg-info/dependency_links.txt +0 -0
  71. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra.egg-info/requires.txt +0 -0
  72. {wavespectra-4.4.3 → wavespectra-4.5.0}/wavespectra.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavespectra
3
- Version: 4.4.3
3
+ Version: 4.5.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>
@@ -56,6 +56,7 @@ wavespectra = "wavespectra.cli:main"
56
56
  [project.entry-points."xarray.backends"]
57
57
  era5 = "wavespectra.input.era5:ERA5BackendEntrypoint"
58
58
  funwave = "wavespectra.input.funwave:FunwaveBackendEntrypoint"
59
+ funwave_new = "wavespectra.input.funwave_new:FunwaveNewBackendEntrypoint"
59
60
  json = "wavespectra.input.json:JsonBackendEntrypoint"
60
61
  ncswan = "wavespectra.input.ncswan:NCSwanBackendEntrypoint"
61
62
  ndbc = "wavespectra.input.ndbc:NDBCBackendEntrypoint"
@@ -14,7 +14,7 @@ except ImportError:
14
14
  warnings.warn("Cannot import accessors at the main module level")
15
15
 
16
16
 
17
- __version__ = "4.4.3"
17
+ __version__ = "4.5.0"
18
18
 
19
19
 
20
20
  def _import_functions(pkgname="input", prefix="read"):
@@ -0,0 +1,104 @@
1
+ """Read Funwave WK_NEW_DATA2D Wave Maker files"""
2
+
3
+ from xarray.backends import BackendEntrypoint
4
+ import numpy as np
5
+ import xarray as xr
6
+
7
+ from wavespectra import SpecArray
8
+ from wavespectra.input import read_ascii_or_binary
9
+ from wavespectra.core.attributes import attrs, set_spec_attributes
10
+
11
+
12
+ def read_funwave_new(filename_or_obj):
13
+ """Read Spectra in Funwave WK_NEW_DATA2D wavemaker format.
14
+
15
+ Args:
16
+ - filename_or_obj (str, filelike): Funwave WaveCompFile to read.
17
+
18
+ Returns:
19
+ - dset (SpecDataset): spectra dataset object read from funwave file.
20
+
21
+ Note:
22
+ - Format description: https://fengyanshi.github.io/build/html/wavemaker_coherence.html.
23
+ - The file describes individual wave components, the gridded spectrum is
24
+ reconstructed by summing component energies :math:`a^2/2` falling in each
25
+ frequency-direction bin defined by the unique frequencies and directions.
26
+ - Directions converted from Cartesian (0E, CCW, to) to wavespectra (0N, CW, from).
27
+ - A 1D :math:`E(f)` spectrum is returned if all directions are equal.
28
+ - Phases are ignored if present.
29
+
30
+ """
31
+ data = read_ascii_or_binary(filename_or_obj, mode="r")
32
+
33
+ # Remove any empty rows
34
+ data = [row for row in data if row != "\n"]
35
+
36
+ # Number of wave components
37
+ nc = int(data.pop(0).split()[0])
38
+
39
+ # Tp
40
+ tp = float(data.pop(0).split()[0])
41
+
42
+ # Wave component blocks
43
+ values = [float(row.split()[0]) for row in data]
44
+ freq = np.array(values[:nc])
45
+ dir = np.array(values[nc : 2 * nc])
46
+ amp = np.array(values[2 * nc : 3 * nc])
47
+
48
+ # Bin component energies onto the spectral grid
49
+ freqs = np.unique(freq)
50
+ dirs = np.unique(dir)
51
+ ifreq = np.searchsorted(freqs, freq)
52
+ idir = np.searchsorted(dirs, dir)
53
+ energy = np.zeros((freqs.size, dirs.size))
54
+ np.add.at(energy, (ifreq, idir), amp**2 / 2)
55
+
56
+ if dirs.size == 1:
57
+ coords = {attrs.FREQNAME: freqs}
58
+ dims = (attrs.FREQNAME,)
59
+ energy = energy.squeeze(axis=1)
60
+ else:
61
+ # Convert dir from cartesian to wavespectra convention
62
+ dirs = (270 - dirs) % 360
63
+
64
+ # Turn zero dir into 360 for continuity
65
+ i0 = np.where(dirs == 0)[0]
66
+ if i0.size > 0:
67
+ dirs[i0] = 360.0
68
+
69
+ coords = {attrs.FREQNAME: freqs, attrs.DIRNAME: dirs}
70
+ dims = (attrs.FREQNAME, attrs.DIRNAME)
71
+ darr = xr.DataArray(data=energy, coords=coords, dims=dims)
72
+
73
+ # Energy density spectrum
74
+ darr = darr / (darr.spec.df * darr.spec.dd)
75
+
76
+ # Define output dataset
77
+ dset = darr.to_dataset(name=attrs.SPECNAME)
78
+ if dirs.size > 1:
79
+ dset = dset.sortby(attrs.DIRNAME)
80
+ dset["tp"] = tp
81
+ set_spec_attributes(dset)
82
+ return dset
83
+
84
+
85
+ class FunwaveNewBackendEntrypoint(BackendEntrypoint):
86
+ """Funwave WK_NEW_DATA2D backend engine."""
87
+
88
+ def open_dataset(
89
+ self,
90
+ filename_or_obj,
91
+ *,
92
+ drop_variables=None,
93
+ ):
94
+ return read_funwave_new(filename_or_obj)
95
+
96
+ def guess_can_open(self, filename_or_obj):
97
+ return False
98
+
99
+ description = (
100
+ "Open Funwave WK_NEW_DATA2D ASCII wave component files as a wavespectra "
101
+ "dataset."
102
+ )
103
+
104
+ url = "https://github.com/wavespectra/wavespectra"
@@ -33,6 +33,46 @@ def to_funwave(
33
33
  zip archive defined by replacing the extension of `filename` by ".zip".
34
34
 
35
35
  """
36
+ _write_funwave(self, filename, clip, funwave_spectrum)
37
+
38
+
39
+ def to_funwave_new(
40
+ self,
41
+ filename,
42
+ clip=True,
43
+ phases=True,
44
+ ):
45
+ """Write spectra in Funwave WK_NEW_DATA2D wavemaker format.
46
+
47
+ Args:
48
+ - filename (str): Name for output Funwave file.
49
+ - clip (bool): Clip directions outside [-90, 90] range in cartesian convention.
50
+ - phases (bool): Write random phases for each wave component, if False the
51
+ phase block is omitted and Funwave generates random phases itself.
52
+
53
+ Note:
54
+ - Format description: https://fengyanshi.github.io/build/html/wavemaker_coherence.html.
55
+ - Unlike the gridded WK_DATA2D format written by `to_funwave`, this format
56
+ describes individual wave components, one per spectral bin.
57
+ - Directions converted from wavespectra (0N, CW, from) to Cartesian (0E, CCW, to).
58
+ - Funwave only deals with directions in the [-90, 90] range in cartesian
59
+ convention (components outside it are discarded by the model), use
60
+ clip=True to clip spectra outside that range.
61
+ - Both 2D :math:`E(f,d)` and 1d :math:`E(f)` spectra are supported.
62
+ - If the SpecArray has more than one spectrum, multiple files are created in a
63
+ zip archive defined by replacing the extension of `filename` by ".zip".
64
+
65
+ """
66
+ _write_funwave(
67
+ self,
68
+ filename,
69
+ clip,
70
+ lambda darr, fname: funwave_new_spectrum(darr, fname, phases=phases),
71
+ )
72
+
73
+
74
+ def _write_funwave(self, filename, clip, spectrum_writer):
75
+ """Write one or multiple spectra using the given single-spectrum writer."""
36
76
  darr = self.efth.copy(deep=True)
37
77
 
38
78
  # Convert directions to Cartesian, going-to convention
@@ -50,7 +90,7 @@ def to_funwave(
50
90
 
51
91
  if not stack_dims or dimsizes == {1}:
52
92
  # Single spectrum in object, write directly to txt file
53
- funwave_spectrum(darr, filename)
93
+ spectrum_writer(darr, filename)
54
94
 
55
95
  else:
56
96
  # Multiple spectra in object, write each txt file in a zip archive
@@ -71,7 +111,7 @@ def to_funwave(
71
111
  # Write spectrum to zip archive
72
112
  fname = os.path.basename(fpath) + "-" + prefix + fext
73
113
  logger.debug(f"Write {fname} to {zipname}")
74
- spectrum = funwave_spectrum(darr, None).getvalue()
114
+ spectrum = spectrum_writer(darr, None).getvalue()
75
115
  zstream.writestr(fname, spectrum)
76
116
 
77
117
 
@@ -120,3 +160,72 @@ def funwave_spectrum(darr, filename):
120
160
  fid.write(s.getvalue())
121
161
 
122
162
  return s
163
+
164
+
165
+ def funwave_new_spectrum(darr, filename, phases=True):
166
+ """Funwave WK_NEW_DATA2D spectrum memory buffer.
167
+
168
+ Args:
169
+ darr (SpecArray): Spectrum to write (only `freq`, `dir` dims are allowed).
170
+ filename (str): Name of file to save spectrum to, choose `None` if you don't
171
+ want to save it to file but only return the memory buffer.
172
+ phases (bool): Write random phases for each wave component, if False the
173
+ phase block is omitted and Funwave generates random phases itself.
174
+
175
+ Returns:
176
+ StringIO memory buffer with spectrum object.
177
+
178
+ Note:
179
+ The file describes one wave component per spectral bin, with blocks of
180
+ frequency (Hz), direction (degrees, cartesian) and amplitude (m) values,
181
+ optionally followed by a block of phases (degrees):
182
+
183
+ .. code-block:: none
184
+
185
+ NumFreq
186
+ PeakPeriod
187
+ Freq(1..NumFreq)
188
+ Dire(1..NumFreq)
189
+ Amp(1..NumFreq)
190
+ Phase(1..NumFreq)
191
+
192
+ """
193
+ # Ensure direction dim exists for 1D spectrum and has value 0
194
+ if attrs.DIRNAME not in darr.dims:
195
+ darr = darr.expand_dims({attrs.DIRNAME: [0]}, axis=-1)
196
+ elif darr[attrs.DIRNAME].size == 1:
197
+ darr = darr.assign_coords({attrs.DIRNAME: [0]})
198
+
199
+ # Component amplitudes from spectral bins
200
+ amp = np.sqrt(darr * darr.spec.df * darr.spec.dd * 8) / 2
201
+ amp = amp.transpose(attrs.FREQNAME, attrs.DIRNAME)
202
+
203
+ # Unpack bins into individual wave components
204
+ freq, dire = np.meshgrid(
205
+ darr[attrs.FREQNAME].values, darr[attrs.DIRNAME].values, indexing="ij"
206
+ )
207
+ freq = freq.ravel()
208
+ dire = dire.ravel()
209
+ amp = amp.values.ravel()
210
+ nc = freq.size
211
+
212
+ # Peak wave period
213
+ tp = float(darr.spec.tp())
214
+
215
+ # Create spectrum in memory buffer
216
+ s = StringIO()
217
+ s.write(f"{nc:>5d} - NumFreq\n")
218
+ s.write(f"{tp:>10.3f} - PeakPeriod\n")
219
+ s.write(nc * "%10.5f - Freq\n" % tuple(freq))
220
+ s.write(nc * "%10.3f - Dire\n" % tuple(dire))
221
+ s.write(nc * "%12.8f - Amp\n" % tuple(amp))
222
+ if phases:
223
+ phi = np.random.uniform(0, 360.0, nc)
224
+ s.write(nc * "%12.3f - Phase\n" % tuple(phi))
225
+
226
+ # Save to file
227
+ if filename is not None:
228
+ with open(filename, "w") as fid:
229
+ fid.write(s.getvalue())
230
+
231
+ return s
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavespectra
3
- Version: 4.4.3
3
+ Version: 4.5.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>
@@ -37,6 +37,7 @@ wavespectra/input/dataset.py
37
37
  wavespectra/input/datawell.py
38
38
  wavespectra/input/era5.py
39
39
  wavespectra/input/funwave.py
40
+ wavespectra/input/funwave_new.py
40
41
  wavespectra/input/json.py
41
42
  wavespectra/input/ncswan.py
42
43
  wavespectra/input/ndbc.py
@@ -4,6 +4,7 @@ wavespectra = wavespectra.cli:main
4
4
  [xarray.backends]
5
5
  era5 = wavespectra.input.era5:ERA5BackendEntrypoint
6
6
  funwave = wavespectra.input.funwave:FunwaveBackendEntrypoint
7
+ funwave_new = wavespectra.input.funwave_new:FunwaveNewBackendEntrypoint
7
8
  json = wavespectra.input.json:JsonBackendEntrypoint
8
9
  ncswan = wavespectra.input.ncswan:NCSwanBackendEntrypoint
9
10
  ndbc = wavespectra.input.ndbc:NDBCBackendEntrypoint
File without changes
File without changes
File without changes
File without changes
File without changes