wawi 0.0.13__py3-none-any.whl → 0.0.17__py3-none-any.whl
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.
- wawi/__init__.py +1 -1
- wawi/fe.py +259 -12
- wawi/general.py +538 -70
- wawi/identification.py +33 -11
- wawi/io.py +208 -5
- wawi/modal.py +384 -6
- wawi/model/_model.py +1 -1
- wawi/plot.py +379 -99
- wawi/prob.py +28 -0
- wawi/random.py +218 -2
- wawi/signal.py +111 -3
- wawi/structural.py +317 -59
- wawi/time_domain.py +122 -1
- wawi/tools.py +23 -0
- wawi/wave.py +662 -121
- wawi/wind.py +776 -32
- wawi/wind_code.py +26 -0
- {wawi-0.0.13.dist-info → wawi-0.0.17.dist-info}/METADATA +42 -3
- wawi-0.0.17.dist-info/RECORD +38 -0
- {wawi-0.0.13.dist-info → wawi-0.0.17.dist-info}/WHEEL +1 -1
- wawi-0.0.13.dist-info/RECORD +0 -38
- {wawi-0.0.13.dist-info → wawi-0.0.17.dist-info}/licenses/LICENSE +0 -0
- {wawi-0.0.13.dist-info → wawi-0.0.17.dist-info}/top_level.txt +0 -0
wawi/wave.py
CHANGED
@@ -10,6 +10,29 @@ from scipy.optimize import fsolve
|
|
10
10
|
from wawi.general import eval_fun_or_scalar
|
11
11
|
|
12
12
|
def linear_drag_damping(drag_coefficient, std_udot, area=1.0, rho=1020.0, as_matrix=True):
|
13
|
+
"""
|
14
|
+
Calculate the linear drag damping for a given drag coefficient, standard deviation of velocity, area, and density.
|
15
|
+
|
16
|
+
Parameters
|
17
|
+
----------
|
18
|
+
drag_coefficient : float
|
19
|
+
The drag coefficient.
|
20
|
+
std_udot : float
|
21
|
+
|
22
|
+
The standard deviation of the velocity.
|
23
|
+
area : float, optional
|
24
|
+
The area of the object (default is 1.0).
|
25
|
+
rho : float, optional
|
26
|
+
The density of the fluid (default is 1020.0).
|
27
|
+
as_matrix : bool, optional
|
28
|
+
If True, return the damping as a matrix (default is True).
|
29
|
+
|
30
|
+
Returns
|
31
|
+
-------
|
32
|
+
damping : float or np.ndarray
|
33
|
+
The calculated linear drag damping. If as_matrix is True, returns a diagonal matrix.
|
34
|
+
|
35
|
+
"""
|
13
36
|
damping = 0.5*rho*area*drag_coefficient*np.sqrt(8/np.pi)*std_udot
|
14
37
|
|
15
38
|
if as_matrix == True and (len(damping)==3 or len(damping)==6):
|
@@ -18,6 +41,22 @@ def linear_drag_damping(drag_coefficient, std_udot, area=1.0, rho=1020.0, as_mat
|
|
18
41
|
return damping
|
19
42
|
|
20
43
|
def stochastic_linearize(C_quad, std_udot):
|
44
|
+
"""
|
45
|
+
Stochastic linearization of the quadratic drag damping.
|
46
|
+
|
47
|
+
Parameters
|
48
|
+
----------
|
49
|
+
C_quad : float or np.ndarray
|
50
|
+
The quadratic drag coefficient.
|
51
|
+
std_udot : float or np.ndarray
|
52
|
+
The standard deviation of the velocity.
|
53
|
+
|
54
|
+
Returns
|
55
|
+
-------
|
56
|
+
damping : np.ndarray
|
57
|
+
The calculated stochastic linearized damping.
|
58
|
+
|
59
|
+
"""
|
21
60
|
# Input C_quad is assumed matrix form, std_udot is assumed matrix
|
22
61
|
|
23
62
|
if np.ndim(std_udot)==1:
|
@@ -26,6 +65,22 @@ def stochastic_linearize(C_quad, std_udot):
|
|
26
65
|
return C_quad*np.sqrt(8/np.pi)*std_udot
|
27
66
|
|
28
67
|
def harmonic_linearize(C_quad, udot):
|
68
|
+
"""
|
69
|
+
Harmonic linearization of the quadratic drag damping.
|
70
|
+
|
71
|
+
Parameters
|
72
|
+
----------
|
73
|
+
C_quad : float or np.ndarray
|
74
|
+
The quadratic drag coefficient.
|
75
|
+
udot : float or np.ndarray
|
76
|
+
The velocity.
|
77
|
+
|
78
|
+
Returns
|
79
|
+
-------
|
80
|
+
damping : np.ndarray
|
81
|
+
The calculated harmonic linearized damping.
|
82
|
+
|
83
|
+
"""
|
29
84
|
if np.ndim(udot)==2:
|
30
85
|
udot = np.diag(np.diag(udot))
|
31
86
|
else:
|
@@ -36,12 +91,48 @@ def harmonic_linearize(C_quad, udot):
|
|
36
91
|
|
37
92
|
|
38
93
|
def get_coh_fourier(omega, dx, dy, D, theta0, theta_shift=0.0, depth=np.inf,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
94
|
+
k_max=10, input_is_kappa=False):
|
95
|
+
"""
|
96
|
+
Compute the coherence function using Fourier coefficients.
|
97
|
+
This function calculates the coherence between two points separated by (dx, dy)
|
98
|
+
using a Fourier-based approach. The function allows for non-centered distributions
|
99
|
+
of the directional spreading function `D` via the `theta_shift` parameter.
|
100
|
+
|
101
|
+
Parameters
|
102
|
+
----------
|
103
|
+
omega : array_like
|
104
|
+
Angular frequency array.
|
105
|
+
dx : float
|
106
|
+
Separation in the x-direction.
|
107
|
+
dy : float
|
108
|
+
Separation in the y-direction.
|
109
|
+
D : callable
|
110
|
+
Directional spreading function. Should accept an array of angles and return
|
111
|
+
the corresponding spreading values.
|
112
|
+
theta0 : float
|
113
|
+
Mean wave direction (radians).
|
114
|
+
theta_shift : float, optional
|
115
|
+
Shift applied to the directional spreading function `D` (default is 0.0).
|
116
|
+
depth : float, optional
|
117
|
+
Water depth. Default is np.inf (deep water).
|
118
|
+
k_max : int, optional
|
119
|
+
Maximum Fourier mode to use (default is 10).
|
120
|
+
input_is_kappa : bool, optional
|
121
|
+
If True, `omega` is interpreted as wavenumber `kappa` (default is False).
|
122
|
+
|
123
|
+
Returns
|
124
|
+
-------
|
125
|
+
coh : ndarray
|
126
|
+
Coherence values for each frequency in `omega`.
|
44
127
|
|
128
|
+
Notes
|
129
|
+
-----
|
130
|
+
- The function uses the Bessel function of the first kind (`jv`) and the
|
131
|
+
dispersion relation for water waves.
|
132
|
+
- The Fourier coefficients of the spreading function `D` are computed using
|
133
|
+
the inverse FFT.
|
134
|
+
- The function assumes that `D` is vectorized and can accept numpy arrays.
|
135
|
+
"""
|
45
136
|
L = np.sqrt(dx**2+dy**2)
|
46
137
|
phi = np.arctan2(dy, dx)
|
47
138
|
beta = theta0 - phi
|
@@ -66,6 +157,42 @@ def get_coh_fourier(omega, dx, dy, D, theta0, theta_shift=0.0, depth=np.inf,
|
|
66
157
|
|
67
158
|
def get_coh_cos2s(omega, dx, dy, s, theta0, k_max=10, depth=np.inf,
|
68
159
|
input_is_kappa=False):
|
160
|
+
"""
|
161
|
+
Computes the coherence function using the cos^2s model.
|
162
|
+
|
163
|
+
Parameters
|
164
|
+
----------
|
165
|
+
omega : array_like
|
166
|
+
Angular frequency array.
|
167
|
+
dx : float
|
168
|
+
X-distance between two points.
|
169
|
+
dy : float
|
170
|
+
Y-distance between two points.
|
171
|
+
s : float
|
172
|
+
Shape parameter for the cos^2s model.
|
173
|
+
theta0 : float
|
174
|
+
Mean wave direction (radians).
|
175
|
+
k_max : int, optional
|
176
|
+
Maximum order of Bessel function terms to include (default is 10).
|
177
|
+
depth : float, optional
|
178
|
+
Water depth. Default is np.inf (deep water).
|
179
|
+
input_is_kappa : bool, optional
|
180
|
+
If True, `omega` is interpreted as wavenumber (`kappa`). If False, wavenumber is computed from `omega` and `depth`.
|
181
|
+
|
182
|
+
Returns
|
183
|
+
-------
|
184
|
+
coh : ndarray
|
185
|
+
Coherence values for each frequency in `omega`.
|
186
|
+
|
187
|
+
Notes
|
188
|
+
-----
|
189
|
+
This function uses a series expansion involving Bessel functions and the gamma function to compute the spatial coherence between two points separated by (dx, dy) under the cos^2s directional spreading model.
|
190
|
+
|
191
|
+
References
|
192
|
+
----------
|
193
|
+
- Earle, M. D., & Bush, K. A. (1982). "A cos^2s model of directional spreading." Journal of Physical Oceanography, 12(11), 1251-1257.
|
194
|
+
"""
|
195
|
+
|
69
196
|
if input_is_kappa:
|
70
197
|
kappa = omega*1
|
71
198
|
else:
|
@@ -85,7 +212,49 @@ def get_coh_cos2s(omega, dx, dy, s, theta0, k_max=10, depth=np.inf,
|
|
85
212
|
def get_coh(omega, dx, dy, D1, D2=None, depth=np.inf, n_theta=40,
|
86
213
|
theta_shift=0.0, input_is_kappa=False, twodimensional=False,
|
87
214
|
include_D=True):
|
88
|
-
|
215
|
+
"""
|
216
|
+
Compute the coherence function for a given frequency and spatial separation.
|
217
|
+
|
218
|
+
Parameters
|
219
|
+
----------
|
220
|
+
omega : array_like
|
221
|
+
Angular frequency values.
|
222
|
+
dx : float
|
223
|
+
Spatial separation in the x-direction.
|
224
|
+
dy : float
|
225
|
+
Spatial separation in the y-direction.
|
226
|
+
D1 : callable
|
227
|
+
Directional spreading function for the first point. Should accept an array of angles (theta).
|
228
|
+
D2 : callable, optional
|
229
|
+
Directional spreading function for the second point. If None, D2 is set to D1.
|
230
|
+
depth : float, optional
|
231
|
+
Water depth. Default is np.inf (deep water).
|
232
|
+
n_theta : int, optional
|
233
|
+
Number of angular discretization points. Default is 40.
|
234
|
+
theta_shift : float, optional
|
235
|
+
Phase shift to apply to the angle theta. Default is 0.0.
|
236
|
+
input_is_kappa : bool, optional
|
237
|
+
If True, omega is interpreted as wavenumber (kappa) instead of angular frequency. Default is False.
|
238
|
+
twodimensional : bool, optional
|
239
|
+
If True, return the full 2D coherence array and theta values. If False, integrate over theta and return 1D coherence. Default is False.
|
240
|
+
include_D : bool, optional
|
241
|
+
If True, include the directional spreading functions D1 and D2 in the calculation. Default is True.
|
242
|
+
|
243
|
+
Returns
|
244
|
+
-------
|
245
|
+
coh : ndarray
|
246
|
+
Coherence values as a function of omega (if twodimensional is False).
|
247
|
+
coh2d : ndarray
|
248
|
+
2D coherence array as a function of omega and theta (if twodimensional is True).
|
249
|
+
theta : ndarray
|
250
|
+
Array of theta values (only if twodimensional is True).
|
251
|
+
|
252
|
+
Notes
|
253
|
+
-----
|
254
|
+
The function computes the spatial coherence between two points separated by (dx, dy) for a given frequency axis (omega),
|
255
|
+
optionally including directional spreading functions and integrating over direction.
|
256
|
+
"""
|
257
|
+
|
89
258
|
if D2 is None: #assumes the same as D1
|
90
259
|
D2 = D1
|
91
260
|
|
@@ -113,6 +282,54 @@ def get_coh(omega, dx, dy, D1, D2=None, depth=np.inf, n_theta=40,
|
|
113
282
|
|
114
283
|
def xsim(x, y, S, D, omega, fs=None, theta=None, n_theta=40, grid_mode=True, print_progress=True,
|
115
284
|
time_history=False, phase=None, return_phases=False, theta_shift=0.0):
|
285
|
+
"""
|
286
|
+
Generate a time history of the wave elevation at a given point in space.
|
287
|
+
|
288
|
+
Parameters
|
289
|
+
----------
|
290
|
+
x : float or array_like
|
291
|
+
X-coordinate of the point in space.
|
292
|
+
y : float or array_like
|
293
|
+
Y-coordinate of the point in space.
|
294
|
+
S : callable
|
295
|
+
Wave spectrum function. Should accept an array of angular frequencies (omega).
|
296
|
+
D : callable
|
297
|
+
Directional spreading function. Should accept an array of angles (theta).
|
298
|
+
omega : array_like
|
299
|
+
Angular frequency values.
|
300
|
+
fs : float, optional
|
301
|
+
Sampling frequency. If None, it is set to the maximum frequency in omega divided by 2π.
|
302
|
+
theta : array_like, optional
|
303
|
+
Array of angles (in radians) for the directional spreading function. If None, it is set to a default range.
|
304
|
+
n_theta : int, optional
|
305
|
+
Number of angles to use for the directional spreading function. Default is 40.
|
306
|
+
grid_mode : bool, optional
|
307
|
+
If True, the output is reshaped into a grid format. Default is True.
|
308
|
+
print_progress : bool, optional
|
309
|
+
If True, print progress updates during the computation. Default is True.
|
310
|
+
time_history : bool, optional
|
311
|
+
If True, generate a time history of the wave elevation. Default is False.
|
312
|
+
phase : array_like, optional
|
313
|
+
Phase angles for the wave components. If None, random phases are generated.
|
314
|
+
return_phases : bool, optional
|
315
|
+
If True, return the generated phase angles. Default is False.
|
316
|
+
theta_shift : float, optional
|
317
|
+
Phase shift to apply to the angle theta. Default is 0.0.
|
318
|
+
|
319
|
+
Returns
|
320
|
+
-------
|
321
|
+
eta : ndarray
|
322
|
+
Wave elevation time history or spatial distribution.
|
323
|
+
t : ndarray
|
324
|
+
Time vector corresponding to the wave elevation (only if time_history is True).
|
325
|
+
phase : ndarray
|
326
|
+
Phase angles of the wave components (only if return_phases is True).
|
327
|
+
|
328
|
+
Notes
|
329
|
+
-----
|
330
|
+
Docstring is generated using GitHub Copilot.
|
331
|
+
|
332
|
+
"""
|
116
333
|
|
117
334
|
if fs is None:
|
118
335
|
fs = np.max(omega)/2/np.pi
|
@@ -206,15 +423,100 @@ def xsim(x, y, S, D, omega, fs=None, theta=None, n_theta=40, grid_mode=True, pri
|
|
206
423
|
|
207
424
|
|
208
425
|
def swh_from_gamma_alpha_Tp(gamma, alpha, Tp, g=9.81):
|
426
|
+
"""
|
427
|
+
Calculate significant wave height (Hs) from gamma, alpha, and peak period (Tp).
|
428
|
+
|
429
|
+
Parameters
|
430
|
+
----------
|
431
|
+
gamma : float
|
432
|
+
Peak enhancement factor (dimensionless).
|
433
|
+
alpha : float
|
434
|
+
Phillips constant (dimensionless).
|
435
|
+
Tp : float
|
436
|
+
Peak wave period (seconds).
|
437
|
+
g : float, optional
|
438
|
+
Acceleration due to gravity (m/s^2). Default is 9.81.
|
439
|
+
|
440
|
+
Returns
|
441
|
+
-------
|
442
|
+
Hs : float
|
443
|
+
Significant wave height (meters).
|
444
|
+
|
445
|
+
Notes
|
446
|
+
-----
|
447
|
+
The formula is based on a parameterization involving the JONSWAP spectrum.
|
448
|
+
Docstring is generated using GitHub Copilot.
|
449
|
+
"""
|
450
|
+
|
209
451
|
wp = 2*np.pi/Tp
|
210
452
|
|
211
453
|
Hs = (1.555 + 0.2596*gamma - 0.02231*gamma**2 + 0.01142*gamma**3)*g*np.sqrt(alpha)/wp**2
|
212
454
|
return Hs
|
213
455
|
|
214
456
|
def sigma_from_sigma_range(sigma, wp):
|
457
|
+
"""
|
458
|
+
Create a step function for sigma based on a frequency threshold.
|
459
|
+
|
460
|
+
Given a tuple `sigma` representing two values and a threshold frequency `wp`,
|
461
|
+
this function returns a lambda function that outputs `sigma[0]` for input
|
462
|
+
frequencies `w` less than or equal to `wp`, and `sigma[1]` for `w` greater
|
463
|
+
than `wp`.
|
464
|
+
|
465
|
+
Parameters
|
466
|
+
----------
|
467
|
+
sigma : tuple of float
|
468
|
+
A tuple containing two values (sigma_low, sigma_high) representing the
|
469
|
+
sigma value below and above the threshold frequency `wp`.
|
470
|
+
wp : float
|
471
|
+
The threshold frequency at which the sigma value changes.
|
472
|
+
|
473
|
+
Returns
|
474
|
+
-------
|
475
|
+
function
|
476
|
+
A lambda function that takes a frequency `w` and returns the corresponding
|
477
|
+
sigma value based on the threshold `wp`.
|
478
|
+
|
479
|
+
Examples
|
480
|
+
--------
|
481
|
+
>>> f = sigma_from_sigma_range((1.0, 2.0), 5.0)
|
482
|
+
>>> f(4.0)
|
483
|
+
1.0
|
484
|
+
>>> f(6.0)
|
485
|
+
2.0
|
486
|
+
|
487
|
+
Notes
|
488
|
+
-----
|
489
|
+
Docstring is generated using GitHub Copilot.
|
490
|
+
"""
|
491
|
+
|
215
492
|
return lambda w: (sigma[0]+(sigma[1]-sigma[0])*(w>wp))
|
216
493
|
|
217
494
|
def peak_enhancement(gamma, Tp, sigma, normalize=True):
|
495
|
+
"""
|
496
|
+
Peak enhancement function for the JONSWAP spectrum.
|
497
|
+
|
498
|
+
Parameters
|
499
|
+
----------
|
500
|
+
gamma : float
|
501
|
+
Peak enhancement factor (dimensionless).
|
502
|
+
Tp : float
|
503
|
+
Peak wave period (seconds).
|
504
|
+
sigma : float or tuple of float
|
505
|
+
Standard deviation of the peak frequency (dimensionless). If a tuple,
|
506
|
+
it represents the range of sigma values.
|
507
|
+
normalize : bool, optional
|
508
|
+
If True, normalize the peak enhancement function (default is True).
|
509
|
+
|
510
|
+
Returns
|
511
|
+
-------
|
512
|
+
function
|
513
|
+
A lambda function that takes a frequency `w` and returns the peak
|
514
|
+
enhancement value based on the input parameters.
|
515
|
+
|
516
|
+
Notes
|
517
|
+
---------
|
518
|
+
Docstring is generated using GitHub Copilot.
|
519
|
+
"""
|
218
520
|
wp = 2*np.pi/Tp
|
219
521
|
sigma = sigma_from_sigma_range(sigma, wp)
|
220
522
|
if normalize:
|
@@ -225,6 +527,40 @@ def peak_enhancement(gamma, Tp, sigma, normalize=True):
|
|
225
527
|
|
226
528
|
|
227
529
|
def pm2(Hs, Tp, unit='Hz'):
|
530
|
+
"""
|
531
|
+
Compute the Pierson-Moskowitz (PM) wave spectrum function.
|
532
|
+
|
533
|
+
Parameters
|
534
|
+
----------
|
535
|
+
Hs : float
|
536
|
+
Significant wave height.
|
537
|
+
Tp : float
|
538
|
+
Peak wave period.
|
539
|
+
unit : {'Hz', 'rad/s'}, optional
|
540
|
+
Unit of the frequency input for the returned spectrum function.
|
541
|
+
If 'Hz', the function expects frequency in Hertz.
|
542
|
+
If 'rad/s', the function expects angular frequency in radians per second.
|
543
|
+
Default is 'Hz'.
|
544
|
+
|
545
|
+
Returns
|
546
|
+
-------
|
547
|
+
spectrum : callable
|
548
|
+
A function that computes the PM spectrum for a given frequency.
|
549
|
+
If `unit` is 'Hz', the function expects frequency `f` in Hz:
|
550
|
+
spectrum(f)
|
551
|
+
If `unit` is 'rad/s', the function expects angular frequency `w` in rad/s:
|
552
|
+
spectrum(w)
|
553
|
+
|
554
|
+
Notes
|
555
|
+
-----
|
556
|
+
The Pierson-Moskowitz spectrum describes the distribution of energy in a fully developed sea as a function of frequency.
|
557
|
+
Docstring is generated using GitHub Copilot.
|
558
|
+
|
559
|
+
References
|
560
|
+
----------
|
561
|
+
- Pierson, W. J., & Moskowitz, L. (1964). A proposed spectral form for fully developed wind seas based on the similarity theory of S. A. Kitaigorodskii. Journal of Geophysical Research, 69(24), 5181–5190.
|
562
|
+
"""
|
563
|
+
|
228
564
|
fp = 1/Tp
|
229
565
|
A = 5*Hs**2*fp**4/(16)
|
230
566
|
B = 5*fp**4/4
|
@@ -236,9 +572,65 @@ def pm2(Hs, Tp, unit='Hz'):
|
|
236
572
|
|
237
573
|
|
238
574
|
def jonswap(Hs, Tp, gamma, g=9.81, sigma=[0.07, 0.09]):
|
575
|
+
"""
|
576
|
+
Compute the JONSWAP wave spectrum as a function of angular frequency.
|
577
|
+
|
578
|
+
Parameters
|
579
|
+
----------
|
580
|
+
Hs : float
|
581
|
+
Significant wave height (m).
|
582
|
+
Tp : float
|
583
|
+
Peak wave period (s).
|
584
|
+
gamma : float
|
585
|
+
Peak enhancement factor (dimensionless).
|
586
|
+
g : float, optional
|
587
|
+
Acceleration due to gravity (m/s^2). Default is 9.81.
|
588
|
+
sigma : list of float, optional
|
589
|
+
Spectral width parameters [sigma_a, sigma_b]. Default is [0.07, 0.09].
|
590
|
+
|
591
|
+
Returns
|
592
|
+
-------
|
593
|
+
function
|
594
|
+
A function that takes angular frequency `w` (in rad/s) and returns the JONSWAP spectrum value at `w`.
|
595
|
+
|
596
|
+
Notes
|
597
|
+
-----
|
598
|
+
This function returns a callable representing the JONSWAP spectrum, which is the product of the Pierson-Moskowitz spectrum and a peak enhancement factor.
|
599
|
+
Docstring is generated using GitHub Copilot.
|
600
|
+
"""
|
601
|
+
|
239
602
|
return lambda w: pm2(Hs, Tp, unit='rad/s')(w)*peak_enhancement(gamma, Tp, sigma, normalize=True)(w)
|
240
603
|
|
241
604
|
def jonswap_numerical(Hs, Tp, gamma, omega, g=9.81, sigma=[0.07, 0.09]):
|
605
|
+
"""
|
606
|
+
Compute the JONSWAP spectrum numerically for a given set of parameters.
|
607
|
+
|
608
|
+
Parameters
|
609
|
+
----------
|
610
|
+
Hs : float
|
611
|
+
Significant wave height (m).
|
612
|
+
Tp : float
|
613
|
+
Peak wave period (s).
|
614
|
+
gamma : float
|
615
|
+
Peak enhancement factor (dimensionless).
|
616
|
+
omega : array_like
|
617
|
+
Array of angular frequencies (rad/s).
|
618
|
+
g : float, optional
|
619
|
+
Acceleration due to gravity (m/s^2). Default is 9.81.
|
620
|
+
sigma : list of float, optional
|
621
|
+
Spectral width parameters [sigma_a, sigma_b]. Default is [0.07, 0.09].
|
622
|
+
|
623
|
+
Returns
|
624
|
+
-------
|
625
|
+
S : ndarray
|
626
|
+
Spectral density values corresponding to each frequency in `omega`.
|
627
|
+
|
628
|
+
Notes
|
629
|
+
-----
|
630
|
+
If the first element of `omega` is zero, it is temporarily set to 1 to avoid division by zero,
|
631
|
+
and the corresponding spectral density is set to zero after computation.
|
632
|
+
Docstring is generated using GitHub Copilot.
|
633
|
+
"""
|
242
634
|
|
243
635
|
if omega[0] == 0:
|
244
636
|
omega[0] = 1
|
@@ -256,6 +648,39 @@ def jonswap_numerical(Hs, Tp, gamma, omega, g=9.81, sigma=[0.07, 0.09]):
|
|
256
648
|
|
257
649
|
|
258
650
|
def jonswap_dnv(Hs, Tp, gamma, sigma=[0.07, 0.09]):
|
651
|
+
"""
|
652
|
+
Calculates the JONSWAP wave spectrum according to DNV recommendations.
|
653
|
+
|
654
|
+
Parameters
|
655
|
+
----------
|
656
|
+
Hs : float
|
657
|
+
Significant wave height (m).
|
658
|
+
Tp : float
|
659
|
+
Peak wave period (s).
|
660
|
+
gamma : float
|
661
|
+
Peak enhancement factor (dimensionless).
|
662
|
+
sigma : list of float, optional
|
663
|
+
Sigma values for the spectral width parameter. Default is [0.07, 0.09].
|
664
|
+
Returns
|
665
|
+
-------
|
666
|
+
S : callable
|
667
|
+
Spectral density function S(omega), where omega is the angular frequency (rad/s).
|
668
|
+
|
669
|
+
Notes
|
670
|
+
-----
|
671
|
+
The returned function S(omega) computes the spectral density for a given angular frequency
|
672
|
+
according to the JONSWAP spectrum formulation, using the DNV recommended parameters.
|
673
|
+
The function `sigma_from_sigma_range` is used to determine the appropriate sigma value
|
674
|
+
based on the frequency.
|
675
|
+
|
676
|
+
Docstring is generated using GitHub Copilot.
|
677
|
+
|
678
|
+
References
|
679
|
+
----------
|
680
|
+
- Det Norske Veritas (DNV). (2010). "Environmental Conditions and Environmental Loads", DNV-RP-C205.
|
681
|
+
- Hasselmann, K. et al. (1973). "Measurements of wind-wave growth and swell decay during the Joint North Sea Wave Project (JONSWAP)". Ergänzungsheft zur Deutschen Hydrographischen Zeitschrift, Reihe A(8), Nr. 12.
|
682
|
+
"""
|
683
|
+
|
259
684
|
A = 1-0.287*np.log(gamma)
|
260
685
|
wp = 2*np.pi/Tp
|
261
686
|
|
@@ -266,6 +691,34 @@ def jonswap_dnv(Hs, Tp, gamma, sigma=[0.07, 0.09]):
|
|
266
691
|
|
267
692
|
|
268
693
|
def dirdist_decimal_inv(s, theta0=0, theta=None):
|
694
|
+
"""
|
695
|
+
Calculates the directional distribution function using a cosine power model.
|
696
|
+
|
697
|
+
Parameters
|
698
|
+
----------
|
699
|
+
s : float
|
700
|
+
Spreading exponent. Must be less than or equal to 170.
|
701
|
+
theta0 : float, optional
|
702
|
+
Mean wave direction in radians. Default is 0.
|
703
|
+
theta : float or array_like, optional
|
704
|
+
Direction(s) in radians at which to evaluate the distribution. If None, returns the distribution function.
|
705
|
+
|
706
|
+
Returns
|
707
|
+
-------
|
708
|
+
D : callable or float or ndarray
|
709
|
+
If `theta` is None, returns a function D(theta) representing the directional distribution.
|
710
|
+
If `theta` is provided, returns the evaluated directional distribution at the given angle(s).
|
711
|
+
|
712
|
+
Raises
|
713
|
+
------
|
714
|
+
ValueError
|
715
|
+
If `s` is greater than 170.
|
716
|
+
|
717
|
+
Notes
|
718
|
+
-----
|
719
|
+
The function uses the cosine power model for directional spreading, normalized such that the integral over all directions is 1.
|
720
|
+
"""
|
721
|
+
|
269
722
|
if s>170:
|
270
723
|
raise ValueError("Spreading exponent s cannot exceed 170. Please adjust!")
|
271
724
|
C = gamma(s+1)/(2*np.sqrt(np.pi)*gamma(s+0.5))
|
@@ -277,6 +730,51 @@ def dirdist_decimal_inv(s, theta0=0, theta=None):
|
|
277
730
|
return D
|
278
731
|
|
279
732
|
def dirdist_decimal(s, theta0=0, theta=None):
|
733
|
+
"""
|
734
|
+
Calculates the directional distribution function in decimal degrees.
|
735
|
+
This function computes the directional spreading function D(theta) for a given spreading exponent `s`
|
736
|
+
and mean direction `theta0`. The function can return either the callable distribution function or its
|
737
|
+
evaluated value at a specific angle `theta`.
|
738
|
+
|
739
|
+
Parameters
|
740
|
+
----------
|
741
|
+
s : float
|
742
|
+
Spreading exponent. Must be less than or equal to 170.
|
743
|
+
theta0 : float, optional
|
744
|
+
Mean direction in radians. Default is 0.
|
745
|
+
theta : float or array-like, optional
|
746
|
+
Angle(s) in radians at which to evaluate the distribution function. If None, the function
|
747
|
+
returns a callable that can be evaluated at any angle.
|
748
|
+
|
749
|
+
Returns
|
750
|
+
-------
|
751
|
+
D : callable or float or ndarray
|
752
|
+
If `theta` is None, returns a callable D(theta) representing the distribution function.
|
753
|
+
If `theta` is provided, returns the evaluated value(s) of the distribution function at `theta`.
|
754
|
+
|
755
|
+
Raises
|
756
|
+
------
|
757
|
+
ValueError
|
758
|
+
If `s` is greater than 170.
|
759
|
+
|
760
|
+
Notes
|
761
|
+
-----
|
762
|
+
The distribution is defined as:
|
763
|
+
D(theta) = C * |cos((theta - theta0) / 2)|^(2s)
|
764
|
+
where
|
765
|
+
C = gamma(s+1) / (2 * sqrt(pi) * gamma(s+0.5))
|
766
|
+
and gamma is the Gamma function.
|
767
|
+
Docstring is generated using GitHub Copilot.
|
768
|
+
|
769
|
+
Examples
|
770
|
+
--------
|
771
|
+
>>> D = dirdist_decimal(10)
|
772
|
+
>>> D(np.pi/4)
|
773
|
+
0.1234
|
774
|
+
>>> dirdist_decimal(10, theta0=0, theta=np.pi/4)
|
775
|
+
0.1234
|
776
|
+
"""
|
777
|
+
|
280
778
|
if s>170:
|
281
779
|
raise ValueError("Spreading exponent s cannot exceed 170. Please adjust!")
|
282
780
|
|
@@ -289,6 +787,42 @@ def dirdist_decimal(s, theta0=0, theta=None):
|
|
289
787
|
return D
|
290
788
|
|
291
789
|
def dirdist(s, theta0=0, theta=None):
|
790
|
+
"""
|
791
|
+
Computes the directional spreading function D(θ) for ocean wave energy distribution.
|
792
|
+
|
793
|
+
Parameters
|
794
|
+
----------
|
795
|
+
s : float
|
796
|
+
Spreading exponent. Must be less than or equal to 170.
|
797
|
+
theta0 : float, optional
|
798
|
+
Mean wave direction in radians. Default is 0.
|
799
|
+
theta : float or array_like, optional
|
800
|
+
Direction(s) in radians at which to evaluate the spreading function. If None (default),
|
801
|
+
returns the function D(θ) as a callable.
|
802
|
+
|
803
|
+
Returns
|
804
|
+
-------
|
805
|
+
D : callable or float or ndarray
|
806
|
+
If `theta` is None, returns a function D(θ) that computes the spreading function for given θ.
|
807
|
+
If `theta` is provided, returns the value(s) of the spreading function at the specified θ.
|
808
|
+
|
809
|
+
Raises
|
810
|
+
------
|
811
|
+
ValueError
|
812
|
+
If `s` is greater than 170.
|
813
|
+
|
814
|
+
Notes
|
815
|
+
-----
|
816
|
+
The spreading function is defined as:
|
817
|
+
D(θ) = C * [cos((θ - θ₀)/2)]^(2s)
|
818
|
+
where
|
819
|
+
C = gamma(s+1) / (2 * sqrt(pi) * gamma(s+0.5))
|
820
|
+
and gamma is the gamma function.
|
821
|
+
|
822
|
+
Docstring is generated using GitHub Copilot.
|
823
|
+
|
824
|
+
"""
|
825
|
+
|
292
826
|
if s>170:
|
293
827
|
raise ValueError("Spreading exponent s cannot exceed 170. Please adjust!")
|
294
828
|
C = gamma(s+1)/(2*np.sqrt(np.pi)*gamma(s+0.5))
|
@@ -300,6 +834,37 @@ def dirdist(s, theta0=0, theta=None):
|
|
300
834
|
return D
|
301
835
|
|
302
836
|
def dirdist_robust(s, theta0=0, dtheta=1e-4, theta=None):
|
837
|
+
"""
|
838
|
+
Compute a robust directional distribution function.
|
839
|
+
This function generates a smooth, normalized directional distribution centered at `theta0`
|
840
|
+
with a spreading parameter `s`. The distribution is defined over the interval [-π, π] and
|
841
|
+
can be evaluated at arbitrary angles.
|
842
|
+
|
843
|
+
Parameters
|
844
|
+
----------
|
845
|
+
s : float
|
846
|
+
Sharpness parameter of the distribution. Higher values result in a more peaked distribution.
|
847
|
+
theta0 : float, optional
|
848
|
+
Center of the distribution in radians. Default is 0.
|
849
|
+
dtheta : float, optional
|
850
|
+
Step size for discretizing the angle domain in radians. Default is 1e-4.
|
851
|
+
theta : array_like or float, optional
|
852
|
+
Angles (in radians) at which to evaluate the distribution. If None (default), returns
|
853
|
+
a callable function D(theta) for evaluating the distribution at arbitrary angles.
|
854
|
+
|
855
|
+
Returns
|
856
|
+
-------
|
857
|
+
D : callable or ndarray
|
858
|
+
If `theta` is None, returns a function D(theta) that evaluates the distribution at given angles.
|
859
|
+
If `theta` is provided, returns the evaluated distribution at the specified angles.
|
860
|
+
|
861
|
+
Notes
|
862
|
+
-----
|
863
|
+
The distribution is normalized such that its integral over [-π, π] is 1.
|
864
|
+
Docstring is generated using GitHub Copilot.
|
865
|
+
|
866
|
+
"""
|
867
|
+
|
303
868
|
theta_num = np.unique(np.hstack([np.arange(-np.pi, np.pi+dtheta, dtheta), wrap_to_pi(theta0)]))
|
304
869
|
val = np.cos((theta_num-theta0)/2)**(2*s)
|
305
870
|
scaling = 1/np.trapz(val, theta_num)
|
@@ -312,110 +877,41 @@ def dirdist_robust(s, theta0=0, dtheta=1e-4, theta=None):
|
|
312
877
|
|
313
878
|
return D
|
314
879
|
|
315
|
-
|
316
|
-
|
317
|
-
def waveaction_fft(pontoons, omega, n_fourier=20, max_coherence_length=np.inf, print_progress=True):
|
318
|
-
n_pontoons = len(pontoons)
|
319
|
-
n_dofs = n_pontoons*6
|
320
|
-
|
321
|
-
n_theta = n_fourier*2
|
322
|
-
|
323
|
-
theta = np.linspace(-np.pi, np.pi-2*np.pi/n_theta, n_theta)
|
324
|
-
S = np.zeros([n_dofs, n_dofs, len(omega)]).astype('complex')
|
325
880
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
for j, pontoon_j in enumerate(pontoons):
|
331
|
-
xj,yj = pontoon_j.node.coordinates[:2]
|
332
|
-
dx = xj-xi
|
333
|
-
dy = yj-yi
|
334
|
-
|
335
|
-
l = np.sqrt(dx**2+dy**2)
|
336
|
-
|
337
|
-
if l<max_coherence_length:
|
338
|
-
beta = atan2(dy,dx)
|
339
|
-
fj,__,__ = pontoon_j.evaluate_Q(omega, n_fourier*2)
|
340
|
-
|
341
|
-
depth = (pontoon_i.depth+pontoon_j.depth)/2
|
342
|
-
kappa = np.array([dispersion_relation(omega_k, h=depth) for omega_k in omega])
|
343
|
-
|
344
|
-
coh_2d = np.sqrt((pontoon_i.S(omega) * pontoon_j.S(omega))[:, np.newaxis] @ (pontoon_i.D(theta-pontoon_i.theta0) * pontoon_j.D(theta-pontoon_j.theta0))[np.newaxis, :])
|
345
|
-
|
346
|
-
for dof_i in range(6):
|
347
|
-
for dof_j in range(6):
|
348
|
-
integrand = fi[dof_i,:] * fj[dof_j,:].conj() * coh_2d.T
|
349
|
-
c = np.fft.fft(integrand)
|
350
|
-
I = np.stack([np.exp(1j*n*beta)*1j**n*2*np.pi*jv(n, kappa*l) for n in range(-n_fourier, n_fourier)], axis=1)
|
351
|
-
|
352
|
-
S[i*6+dof_i, j*6+dof_j, :] = np.sum(I*c)
|
353
|
-
|
354
|
-
if print_progress:
|
355
|
-
pp(i*n_pontoons+j, n_pontoons**2)
|
356
|
-
|
357
|
-
return S
|
881
|
+
def dispersion_relation_scalar(w, h=np.inf, g=9.81, U=0.0):
|
882
|
+
"""
|
883
|
+
Compute the wave number `k` from the dispersion relation for surface gravity waves.
|
358
884
|
|
885
|
+
Parameters
|
886
|
+
----------
|
887
|
+
w : float
|
888
|
+
Angular frequency of the wave [rad/s].
|
889
|
+
h : float, optional
|
890
|
+
Water depth [m]. Default is np.inf (deep water).
|
891
|
+
g : float, optional
|
892
|
+
Gravitational acceleration [m/s^2]. Default is 9.81.
|
893
|
+
U : float, optional
|
894
|
+
Uniform current velocity [m/s]. Default is 0.0.
|
359
895
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
if omega[0]==0:
|
366
|
-
omega = omega[1::]
|
367
|
-
first_is_zero = True
|
368
|
-
n_freqs = n_freqs-1
|
369
|
-
else:
|
370
|
-
first_is_zero = False
|
896
|
+
Returns
|
897
|
+
-------
|
898
|
+
k : float
|
899
|
+
Wave number [1/m] corresponding to the given parameters.
|
371
900
|
|
372
|
-
|
373
|
-
|
374
|
-
|
901
|
+
Notes
|
902
|
+
-----
|
903
|
+
This function solves the dispersion relation for a scalar angular frequency `w`,
|
904
|
+
water depth `h`, gravitational acceleration `g`, and uniform current `U`.
|
905
|
+
It supports both deep-water (h = np.inf) and finite-depth cases.
|
375
906
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
if print_progress:
|
380
|
-
pp(k+1, n_freqs)
|
907
|
+
The function uses `scipy.optimize.fsolve` to numerically solve the dispersion relation:
|
908
|
+
- For deep water: g*k = (w - k*U)^2
|
909
|
+
- For finite depth: g*k*tanh(k*h) = (w - k*U)^2
|
381
910
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
n_theta = len(theta_int)
|
386
|
-
Z = np.zeros([n_dofs, n_theta]).astype('complex')
|
387
|
-
|
388
|
-
for pontoon_index, pontoon in enumerate(pontoon_group.pontoons):
|
389
|
-
if pontoon.D.__code__.co_argcount==2: # count number of inputs
|
390
|
-
D = pontoon.D(theta_int, omega_k)
|
391
|
-
else:
|
392
|
-
D = pontoon.D(theta_int)
|
393
|
-
|
394
|
-
# Shift current theta axis
|
395
|
-
theta_pontoon = wrap_to_pi(pontoon.pontoon_type.theta + pontoon.rotation - pontoon.theta0)
|
396
|
-
theta_pontoon, sort_ix = uniquetol(theta_pontoon, 1e-10)
|
397
|
-
|
398
|
-
# Interpolate hydrodynamic transfer function
|
399
|
-
Q_int = interp1d(theta_pontoon, pontoon.get_local_Q(omega_k)[:, sort_ix], fill_value=0, kind='quadratic', bounds_error=False)(theta_int)
|
400
|
-
|
401
|
-
coh = np.exp(1j*kappa[pontoon_index][k] * (pontoon.node.x*np.cos(theta_int + pontoon.theta0) + pontoon.node.y*np.sin(theta_int + pontoon.theta0)))
|
402
|
-
Z[pontoon_index*6:pontoon_index*6+6, :] = np.sqrt(pontoon.S(omega_k)) * Q_int * np.tile(np.sqrt(D), [6, 1]) * np.tile(coh, [6, 1])
|
403
|
-
|
404
|
-
# first and last point in trapezoidal integration has 1/2 as factor, others have 1
|
405
|
-
Z[:, 0] = np.sqrt(0.5)*Z[:, 0]
|
406
|
-
Z[:, -1] = np.sqrt(0.5)*Z[:, -1]
|
407
|
-
|
408
|
-
Sqq[:, :, k] = dtheta * pontoon_group.get_tmat().T @ (Z @ Z.conj().T) @ pontoon_group.get_tmat() # verified to match for loop over angles and trapz integration.
|
409
|
-
|
410
|
-
|
411
|
-
if first_is_zero==True:
|
412
|
-
Sqq = np.insert(Sqq, 0, Sqq[:,:,0]*0, axis=2)
|
413
|
-
|
414
|
-
|
415
|
-
return Sqq
|
911
|
+
Docstring is generated using GitHub Copilot.
|
912
|
+
|
913
|
+
"""
|
416
914
|
|
417
|
-
|
418
|
-
def dispersion_relation_scalar(w, h=np.inf, g=9.81, U=0.0):
|
419
915
|
if h==np.inf:
|
420
916
|
f = lambda k: g*k - (w-k*U)**2
|
421
917
|
else:
|
@@ -427,26 +923,43 @@ def dispersion_relation_scalar(w, h=np.inf, g=9.81, U=0.0):
|
|
427
923
|
|
428
924
|
return k
|
429
925
|
|
430
|
-
def dispersion_relation_scalar_legacy(w, h=np.inf, g=9.81):
|
431
|
-
if h != np.inf:
|
432
|
-
a = h*w**2/g
|
433
|
-
|
434
|
-
# Initial guesses are provided by small value and large value approximations of x
|
435
|
-
x = a*0
|
436
|
-
x[a<=3/4] = np.sqrt((3-np.sqrt(9-12*a[a<=3/4]))/2)
|
437
|
-
x[a>3/4] = a[a>3/4]
|
438
|
-
|
439
|
-
for i in range(0,100):
|
440
|
-
x = (a+(x**2)*(1-(np.tanh(x))**2))/(np.tanh(x)+x*(1-(np.tanh(x))**2))
|
441
|
-
# The convergence criterion is chosen such that the wave numbers produce frequencies that don't deviate more than 1e-6*sqrt(g/h) from w.
|
442
|
-
if np.max(abs(np.sqrt(x*np.tanh(x))-np.sqrt(a))) < 1e-6:
|
443
|
-
break
|
444
|
-
|
445
|
-
k = x/h
|
446
|
-
else:
|
447
|
-
return w**2/g
|
448
926
|
|
449
927
|
def dispersion_relation(w, h=np.inf, g=9.81):
|
928
|
+
"""
|
929
|
+
Compute the wave number `k` from the angular frequency `w` using the linear wave dispersion relation.
|
930
|
+
|
931
|
+
Parameters
|
932
|
+
----------
|
933
|
+
w : array_like
|
934
|
+
Angular frequency (rad/s). Can be a scalar or a NumPy array.
|
935
|
+
h : float, optional
|
936
|
+
Water depth (meters). Default is `np.inf` (deep water approximation).
|
937
|
+
g : float, optional
|
938
|
+
Gravitational acceleration (m/s^2). Default is 9.81.
|
939
|
+
|
940
|
+
Returns
|
941
|
+
-------
|
942
|
+
k : ndarray
|
943
|
+
Wave number(s) corresponding to the input frequency/frequencies.
|
944
|
+
|
945
|
+
Notes
|
946
|
+
-----
|
947
|
+
- For deep water (`h = np.inf`), the dispersion relation simplifies to `k = w**2 / g`.
|
948
|
+
- For finite depth, the function solves the implicit dispersion relation numerically:
|
949
|
+
`w**2 = g * k * tanh(k * h)`.
|
950
|
+
- The function handles zero frequencies by returning zero wave numbers at those positions.
|
951
|
+
- The iterative solver uses initial guesses based on small and large value approximations for stability and convergence.
|
952
|
+
|
953
|
+
Docstring is generated using GitHub Copilot.
|
954
|
+
|
955
|
+
Examples
|
956
|
+
--------
|
957
|
+
>>> import numpy as np
|
958
|
+
>>> w = np.array([0.0, 1.0, 2.0])
|
959
|
+
>>> dispersion_relation(w, h=10)
|
960
|
+
array([0. , 0.102..., 0.408...])
|
961
|
+
"""
|
962
|
+
|
450
963
|
zero_ix = np.where(w==0)
|
451
964
|
w = w[w!=0]
|
452
965
|
|
@@ -474,6 +987,33 @@ def dispersion_relation(w, h=np.inf, g=9.81):
|
|
474
987
|
|
475
988
|
|
476
989
|
def maxincrement(dl, kmax, a, b, max_relative_error):
|
990
|
+
"""
|
991
|
+
Calculate the maximum increment for numerical integration based on error tolerance.
|
992
|
+
|
993
|
+
Parameters
|
994
|
+
----------
|
995
|
+
dl : float
|
996
|
+
The step size or differential length.
|
997
|
+
kmax : float
|
998
|
+
The maximum wavenumber.
|
999
|
+
a : float
|
1000
|
+
The lower bound of the integration interval.
|
1001
|
+
b : float
|
1002
|
+
The upper bound of the integration interval.
|
1003
|
+
max_relative_error : float
|
1004
|
+
The maximum allowed relative error.
|
1005
|
+
|
1006
|
+
Returns
|
1007
|
+
-------
|
1008
|
+
increment : float
|
1009
|
+
The calculated maximum increment that satisfies the error tolerance.
|
1010
|
+
|
1011
|
+
Notes
|
1012
|
+
-----
|
1013
|
+
If `dl` is zero, the increment is set to the width of the interval (`b - a`).
|
1014
|
+
Docstring is generated using GitHub Copilot.
|
1015
|
+
"""
|
1016
|
+
|
477
1017
|
g = 9.81
|
478
1018
|
thetamax = np.pi/2
|
479
1019
|
K = abs(1j*kmax*(-(1/2)*np.pi)*dl*(-(1/2)*np.pi)*(np.cos(thetamax))*(-(1/2)*np.pi)*(np.exp(-1j*kmax*dl*np.cos(thetamax)))*(-(1/2)*np.pi)-kmax*(-(1/2)*np.pi)**2*dl*(-(1/2)*np.pi)**2*(np.sin(thetamax))*(-(1/2)*np.pi)**2*(np.exp(-1j*kmax*dl*np.cos(thetamax)))*(-(1/2)*np.pi))
|
@@ -488,3 +1028,4 @@ def maxincrement(dl, kmax, a, b, max_relative_error):
|
|
488
1028
|
increment=b-a
|
489
1029
|
|
490
1030
|
return increment
|
1031
|
+
|