Skip to content

Commit 8ed4154

Browse files
ssolsonkmruehl
andauthored
Update JS and PM to match IEC (#128)
* Update JS and PM to match IEC * Fix bug and adjust tests. * remove BS and update JS (#2) * remove BS and update JS * updated function reference to IEC TS62600-2 * replace BS calls with JS in tests Co-authored-by: Kelley Ruehl <[email protected]>
1 parent febb1a6 commit 8ed4154

File tree

2 files changed

+43
-75
lines changed

2 files changed

+43
-75
lines changed

mhkit/tests/test_wave.py

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,18 @@ def tearDownClass(self):
4141
pass
4242

4343
def test_pierson_moskowitz_spectrum(self):
44-
S = wave.resource.pierson_moskowitz_spectrum(self.f,self.Tp)
44+
S = wave.resource.pierson_moskowitz_spectrum(self.f,self.Tp,self.Hs)
45+
Hm0 = wave.resource.significant_wave_height(S).iloc[0,0]
4546
Tp0 = wave.resource.peak_period(S).iloc[0,0]
4647

47-
error = np.abs(self.Tp - Tp0)/self.Tp
48-
49-
self.assertLess(error, 0.01)
48+
errorHm0 = np.abs(self.Tp - Tp0)/self.Tp
49+
errorTp0 = np.abs(self.Hs - Hm0)/self.Hs
5050

51-
def test_bretschneider_spectrum(self):
52-
S = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs)
51+
self.assertLess(errorHm0, 0.01)
52+
self.assertLess(errorTp0, 0.01)
53+
54+
def test_jonswap_spectrum(self):
55+
S = wave.resource.jonswap_spectrum(self.f, self.Tp, self.Hs)
5356
Hm0 = wave.resource.significant_wave_height(S).iloc[0,0]
5457
Tp0 = wave.resource.peak_period(S).iloc[0,0]
5558

@@ -60,7 +63,7 @@ def test_bretschneider_spectrum(self):
6063
self.assertLess(errorTp0, 0.01)
6164

6265
def test_surface_elevation_seed(self):
63-
S = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs)
66+
S = wave.resource.jonswap_spectrum(self.f,self.Tp,self.Hs)
6467

6568
sig = inspect.signature(wave.resource.surface_elevation)
6669
seednum = sig.parameters['seed'].default
@@ -71,7 +74,7 @@ def test_surface_elevation_seed(self):
7174
assert_frame_equal(eta0, eta1)
7275

7376
def test_surface_elevation_phasing(self):
74-
S = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs)
77+
S = wave.resource.jonswap_spectrum(self.f,self.Tp,self.Hs)
7578
eta0 = wave.resource.surface_elevation(S, self.t)
7679
sig = inspect.signature(wave.resource.surface_elevation)
7780
seednum = sig.parameters['seed'].default
@@ -83,8 +86,8 @@ def test_surface_elevation_phasing(self):
8386

8487

8588
def test_surface_elevation_phases_np_and_pd(self):
86-
S0 = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs)
87-
S1 = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs*1.1)
89+
S0 = wave.resource.jonswap_spectrum(self.f,self.Tp,self.Hs)
90+
S1 = wave.resource.jonswap_spectrum(self.f,self.Tp,self.Hs*1.1)
8891
S = pd.concat([S0, S1], axis=1)
8992

9093
phases_np = np.random.rand(S.shape[0], S.shape[1]) * 2 * np.pi
@@ -96,8 +99,8 @@ def test_surface_elevation_phases_np_and_pd(self):
9699
assert_frame_equal(eta_np, eta_pd)
97100

98101
def test_surface_elevation_frequency_bins_np_and_pd(self):
99-
S0 = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs)
100-
S1 = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs*1.1)
102+
S0 = wave.resource.jonswap_spectrum(self.f,self.Tp,self.Hs)
103+
S1 = wave.resource.jonswap_spectrum(self.f,self.Tp,self.Hs*1.1)
101104
S = pd.concat([S0, S1], axis=1)
102105

103106
eta0 = wave.resource.surface_elevation(S, self.t)
@@ -145,23 +148,12 @@ def test_surface_elevation_rmse(self):
145148

146149
self.assertLess(rmse_sum, 0.02)
147150

148-
def test_jonswap_spectrum(self):
149-
S = wave.resource.jonswap_spectrum(self.f, self.Tp, self.Hs)
150-
Hm0 = wave.resource.significant_wave_height(S).iloc[0,0]
151-
Tp0 = wave.resource.peak_period(S).iloc[0,0]
152-
153-
errorHm0 = np.abs(self.Tp - Tp0)/self.Tp
154-
errorTp0 = np.abs(self.Hs - Hm0)/self.Hs
155-
156-
self.assertLess(errorHm0, 0.01)
157-
self.assertLess(errorTp0, 0.01)
158-
159151
def test_plot_spectrum(self):
160152
filename = abspath(join(testdir, 'wave_plot_spectrum.png'))
161153
if isfile(filename):
162154
os.remove(filename)
163155

164-
S = wave.resource.pierson_moskowitz_spectrum(self.f,self.Tp)
156+
S = wave.resource.pierson_moskowitz_spectrum(self.f,self.Tp,self.Hs)
165157

166158
plt.figure()
167159
wave.graphics.plot_spectrum(S)
@@ -351,7 +343,7 @@ def test_wave_celerity(self):
351343

352344
def test_energy_flux_deep(self):
353345
# Dependent on mhkit.resource.BS spectrum
354-
S = wave.resource.bretschneider_spectrum(self.f,self.Tp,self.Hs)
346+
S = wave.resource.jonswap_spectrum(self.f,self.Tp,self.Hs)
355347
Te = wave.resource.energy_period(S)
356348
Hm0 = wave.resource.significant_wave_height(S)
357349
rho=1025

mhkit/wave/resource.py

Lines changed: 26 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,18 @@ def elevation_spectrum(eta, sample_rate, nnft, window='hann', detrend=True, nove
6363
return S
6464

6565

66-
def pierson_moskowitz_spectrum(f, Tp):
66+
def pierson_moskowitz_spectrum(f, Tp, Hs):
6767
"""
68-
Calculates Pierson-Moskowitz Spectrum from Tucker and Pitt (2001)
68+
Calculates Pierson-Moskowitz Spectrum from IEC TS 62600-2 ED2 Annex C.2 (2019)
6969
7070
Parameters
7171
------------
7272
f: numpy array
7373
Frequency [Hz]
7474
Tp: float/int
7575
Peak period [s]
76+
Hs: float/int
77+
Significant wave height [m]
7678
7779
Returns
7880
---------
@@ -86,59 +88,22 @@ def pierson_moskowitz_spectrum(f, Tp):
8688
pass
8789
assert isinstance(f, np.ndarray), 'f must be of type np.ndarray'
8890
assert isinstance(Tp, (int,float)), 'Tp must be of type int or float'
91+
assert isinstance(Hs, (int,float)), 'Hs must be of type int or float'
8992

9093
f.sort()
91-
g = 9.81
92-
B_PM = (5/4)*(1/Tp)**(4)
93-
A_PM = 0.0081*g**2*(2*np.pi)**(-4)
94+
B_PM = (5/4)*(1/Tp)**4
95+
A_PM = B_PM*(Hs/2)**2
9496
Sf = A_PM*f**(-5)*np.exp(-B_PM*f**(-4))
9597

9698
col_name = 'Pierson-Moskowitz ('+str(Tp)+'s)'
9799
S = pd.DataFrame(Sf, index=f, columns=[col_name])
98100

99101
return S
100102

101-
def bretschneider_spectrum(f, Tp, Hs):
102-
"""
103-
Calculates Bretschneider Sprectrum from Tucker and Pitt (2001)
104-
105-
Parameters
106-
------------
107-
f: numpy array
108-
Frequency [Hz]
109-
Tp: float/int
110-
Peak period [s]
111-
Hs: float/int
112-
Significant wave height [m]
113-
114-
Returns
115-
---------
116-
S: pandas DataFrame
117-
Spectral density [m^2/Hz] indexed by frequency [Hz]
118103

104+
def jonswap_spectrum(f, Tp, Hs, gamma=None):
119105
"""
120-
try:
121-
f = np.array(f)
122-
except:
123-
pass
124-
assert isinstance(f, np.ndarray), 'f must be of type np.ndarray'
125-
assert isinstance(Tp, (int,float)), 'Tp must be of type int or float'
126-
assert isinstance(Hs, (int,float)), 'Hs must be of type int or float'
127-
128-
f.sort()
129-
B_BS = (1.057/Tp)**4
130-
A_BS = B_BS*(Hs/2)**2
131-
Sf = A_BS*f**(-5)*np.exp(-B_BS*f**(-4))
132-
133-
col_name = 'Bretschneider ('+str(Hs)+'m,'+str(Tp)+'s)'
134-
S = pd.DataFrame(Sf, index=f, columns=[col_name])
135-
136-
return S
137-
138-
139-
def jonswap_spectrum(f, Tp, Hs, gamma=3.3):
140-
"""
141-
Calculates JONSWAP spectrum from Hasselmann et al (1973)
106+
Calculates JONSWAP Spectrum from IEC TS 62600-2 ED2 Annex C.2 (2019)
142107
143108
Parameters
144109
------------
@@ -164,11 +129,23 @@ def jonswap_spectrum(f, Tp, Hs, gamma=3.3):
164129
assert isinstance(f, np.ndarray), 'f must be of type np.ndarray'
165130
assert isinstance(Tp, (int,float)), 'Tp must be of type int or float'
166131
assert isinstance(Hs, (int,float)), 'Hs must be of type int or float'
167-
assert isinstance(gamma, (int,float)), 'gamma must be of type int or float'
132+
assert isinstance(gamma, (int,float, type(None))), \
133+
'gamma must be of type int or float'
168134

169135
f.sort()
170-
g = 9.81
171-
136+
B_PM = (5/4)*(1/Tp)**4
137+
A_PM = B_PM*(Hs/2)**2
138+
S_f = A_PM*f**(-5)*np.exp(-B_PM*f**(-4))
139+
140+
if not gamma:
141+
TpsqrtHs = Tp/np.sqrt(Hs);
142+
if TpsqrtHs <= 3.6:
143+
gamma = 5;
144+
elif TpsqrtHs > 5:
145+
gamma = 1;
146+
else:
147+
gamma = np.exp(5.75 - 1.15*TpsqrtHs);
148+
172149
# Cutoff frequencies for gamma function
173150
siga = 0.07
174151
sigb = 0.09
@@ -179,9 +156,8 @@ def jonswap_spectrum(f, Tp, Hs, gamma=3.3):
179156
Gf = np.zeros(f.shape)
180157
Gf[lind] = gamma**np.exp(-(f[lind]-fp)**2/(2*siga**2*fp**2))
181158
Gf[hind] = gamma**np.exp(-(f[hind]-fp)**2/(2*sigb**2*fp**2))
182-
S_temp = g**2*(2*np.pi)**(-4)*f**(-5)*np.exp(-(5/4)*(f/fp)**(-4))
183-
alpha_JS = Hs**(2)/16/np.trapz(S_temp*Gf,f)
184-
Sf = alpha_JS*S_temp*Gf # Wave Spectrum [m^2-s]
159+
C = 1- 0.287*np.log(gamma)
160+
Sf = C*S_f*Gf
185161

186162
col_name = 'JONSWAP ('+str(Hs)+'m,'+str(Tp)+'s)'
187163
S = pd.DataFrame(Sf, index=f, columns=[col_name])

0 commit comments

Comments
 (0)