Skip to content

Commit d7ab907

Browse files
feat: add WaveBinSpectrum class (#77)
1 parent d675095 commit d7ab907

3 files changed

Lines changed: 386 additions & 0 deletions

File tree

src/waveresponse/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
DirectionalBinSpectrum,
77
DirectionalSpectrum,
88
Grid,
9+
WaveBinSpectrum,
910
WaveSpectrum,
1011
calculate_response,
1112
complex_to_polar,
@@ -53,5 +54,6 @@
5354
"rigid_transform_surge",
5455
"rigid_transform_sway",
5556
"Torsethaugen",
57+
"WaveBinSpectrum",
5658
"WaveSpectrum",
5759
]

src/waveresponse/_core.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,149 @@ def dirm(self, degrees=None):
21302130
return dirm
21312131

21322132

2133+
class WaveBinSpectrum(DisableComplexMixin, DirectionalBinSpectrum):
2134+
"""
2135+
Binned wave spectrum.
2136+
2137+
The ``WaveSpectrum`` class extends the :class:`~waveresponse.DirectionalBinSpectrum`
2138+
class, and is a two-dimentional frequency/(wave)direction grid. The spectrum values
2139+
represents spectrum density as a function of frequency, binned by direction.
2140+
2141+
Proper scaling is applied to ensure that the total "energy" remains constant at all times.
2142+
2143+
Parameters
2144+
----------
2145+
freq : array-like
2146+
1-D array of grid frequency coordinates. Positive and monotonically increasing.
2147+
dirs : array-like
2148+
1-D array of grid direction coordinates. Positive and monotonically increasing.
2149+
Must cover the directional range [0, 360) degrees (or [0, 2 * numpy.pi) radians).
2150+
vals : array-like (N, M)
2151+
Spectrum density values associated with the grid. Should be a 2-D array
2152+
of shape (N, M), such that ``N=len(freq)`` and ``M=len(dirs)``.
2153+
freq_hz : bool
2154+
If frequency is given in 'Hz'. If ``False``, 'rad/s' is assumed.
2155+
degrees : bool
2156+
If direction is given in 'degrees'. If ``False``, 'radians' is assumed.
2157+
clockwise : bool
2158+
If positive directions are defined to be 'clockwise' (``True``) or 'counterclockwise'
2159+
(``False``). Clockwise means that the directions follow the right-hand rule
2160+
with an axis pointing downwards.
2161+
waves_coming_from : bool
2162+
If waves are 'coming from' the given directions. If ``False``, 'going towards'
2163+
convention is assumed.
2164+
"""
2165+
2166+
def __init__(self, *args, **kwargs):
2167+
super().__init__(*args, **kwargs)
2168+
2169+
if np.any(np.iscomplex(self._vals)):
2170+
raise ValueError("Spectrum values can not be complex.")
2171+
elif np.any(self._vals < 0.0):
2172+
raise ValueError("Spectrum values must be positive.")
2173+
2174+
def __repr__(self):
2175+
return "WaveBinSpectrum"
2176+
2177+
@property
2178+
def hs(self):
2179+
"""
2180+
Significan wave height, Hs.
2181+
2182+
Calculated from the zeroth-order spectral moment according to:
2183+
2184+
``hs = 4.0 * sqrt(m0)``
2185+
2186+
Notes
2187+
-----
2188+
The significant wave height is calculated according to equation (2.26) in
2189+
reference [1].
2190+
2191+
References
2192+
----------
2193+
[1] 0. M. Faltinsen, (1990), "Sea loads on ships and offshore structures",
2194+
Cambridge University Press.
2195+
"""
2196+
m0 = self.moment(0)
2197+
return 4.0 * np.sqrt(m0)
2198+
2199+
@property
2200+
def tp(self):
2201+
"""
2202+
Wave peak period in 'seconds'.
2203+
2204+
The period at which the 'non-directional' wave spectrum, ``S(f)``, has its maximum
2205+
value.
2206+
"""
2207+
f, S = self.spectrum1d(axis=1, freq_hz=True)
2208+
fp = f[np.argmax(S)]
2209+
return 1.0 / fp
2210+
2211+
@staticmethod
2212+
def _mean_direction(dirs, spectrum):
2213+
"""
2214+
Mean spectrum direction.
2215+
2216+
Parameters
2217+
----------
2218+
dirs : array-like
2219+
Directions in 'radians'.
2220+
spectrum : array-like
2221+
1-D spectrum directional distribution.
2222+
"""
2223+
sin = np.sum(np.sin(dirs) * spectrum) / len(dirs)
2224+
cos = np.sum(np.cos(dirs) * spectrum) / len(dirs)
2225+
return _robust_modulus(np.arctan2(sin, cos), 2.0 * np.pi)
2226+
2227+
def dirp(self, degrees=None):
2228+
"""
2229+
Wave peak direction.
2230+
2231+
Defined as the mean wave direction along the frequency corresponding to
2232+
the maximum value of the 'non-directional' spectrum.
2233+
2234+
Parameters
2235+
----------
2236+
degrees : bool
2237+
If wave peak direction should be returned in 'degrees'. If ``False``,
2238+
the direction is returned in 'radians'. Defaults to original unit used
2239+
during initialization.
2240+
"""
2241+
2242+
if degrees is None:
2243+
degrees = self._degrees
2244+
2245+
_, spectrum1d = self.spectrum1d(axis=1, freq_hz=False)
2246+
2247+
spectrum_peakfreq = self._vals[np.argmax(spectrum1d), :]
2248+
2249+
dirp = self._mean_direction(self._dirs, spectrum_peakfreq)
2250+
2251+
if degrees:
2252+
dirp = (180.0 / np.pi) * dirp
2253+
2254+
return dirp
2255+
2256+
def dirm(self, degrees=None):
2257+
"""
2258+
Mean wave direction.
2259+
2260+
Parameters
2261+
----------
2262+
degrees : bool
2263+
If mean wave direction should be returned in 'degrees'. If ``False``,
2264+
the direction is returned in 'radians'. Defaults to original unit used
2265+
during instantiation.
2266+
"""
2267+
2268+
dirm = self._mean_direction(*self.spectrum1d(axis=0, degrees=False))
2269+
2270+
if degrees:
2271+
dirm = np.degrees(dirm)
2272+
2273+
return dirm
2274+
2275+
21332276
def calculate_response(
21342277
rao, wave, heading, heading_degrees=False, coord_freq="wave", coord_dirs="wave"
21352278
):

0 commit comments

Comments
 (0)