/home/runner/work/kynema-sgf/kynema-sgf/src/ocean_waves/relaxation_zones/stokes_waves_K.H Source File

Kynema-SGF API: /home/runner/work/kynema-sgf/kynema-sgf/src/ocean_waves/relaxation_zones/stokes_waves_K.H Source File
Kynema-SGF API v0.1.0
CFD solver for wind plant simulations
Loading...
Searching...
No Matches
stokes_waves_K.H
Go to the documentation of this file.
1#ifndef STOKES_WAVES_K_H
2#define STOKES_WAVES_K_H
3
4#include <algorithm>
5#include <cmath>
6#include <numbers>
7#include "AMReX_FArrayBox.H"
9
10using namespace amrex::literals;
11
12// Stokes waves theory adapted from
13// Fenton, J., Fifth Order Stokes Theory for Steady Waves
14// Journal of Waterway, Port, Coastal and Ocean Engineering, 1985, 111, 216-234
15
16// Updated Table 1 from Fenton 1985 found in
17// Fenton, J., Nonlinear Wave Theories
18// The Sea Vol. 9 Ocean Engineering Science, 1990
19// https://johndfenton.com/Papers/Fenton90b-Nonlinear-wave-theories.pdf
20
21// Relevant results are summarized in
22// Kinnas S. A., Notes on fifth order gravity wave theory
23// https://www.caee.utexas.edu/prof/kinnas/ce358/oenotes/kinnas_stokes11.pdf
24
26
27// Compute wavelength as a function of wave period, water depth, and g
28AMREX_FORCE_INLINE amrex::Real stokes_wave_length(
29 const amrex::Real T,
30 const amrex::Real d,
31 const amrex::Real H,
32 const int order,
33 const amrex::Real g,
34 const amrex::Real tol,
35 const int iter_max)
36{
37 // Calculate constants and derivatives that do not change with iteration
38 const amrex::Real omega = 2.0_rt * std::numbers::pi_v<amrex::Real> / T;
39 const amrex::Real depsdk = H / 2.0_rt;
40
41 // Begin Newton-Raphson iterations
42 int iter = 0;
43 // First guess is first-order
44 amrex::Real k = (omega * omega) / g;
45 // Cannot skip loop
46 amrex::Real f = tol + 1.0_rt;
47 while (std::abs(f) > tol && iter < iter_max) {
48 // Calculate current constants
49
50 // Exponential definition of S = sech(2kd)
51 const amrex::Real S = 2.0_rt * std::exp(2.0_rt * k * d) /
52 (std::exp(4.0_rt * k * d) + 1.0_rt);
53 const amrex::Real C = 1.0_rt - S;
54 const amrex::Real eps = k * H / 2.0_rt;
55 const amrex::Real C0 = std::sqrt(std::tanh(k * d));
56 const amrex::Real C2 =
57 C0 * (2.0_rt + (7.0_rt * S * S)) / (4.0_rt * C * C);
58 const amrex::Real numterm_C4 =
59 (4.0_rt + (32.0_rt * S) - (116.0_rt * utils::powi(S, 2)) -
60 (400.0_rt * utils::powi(S, 3)) - (71.0_rt * utils::powi(S, 4)) +
61 (146.0_rt * utils::powi(S, 5)));
62 const amrex::Real C4 = C0 * numterm_C4 / (32.0_rt * utils::powi(C, 5));
63 // Calculate pure derivates
64 const amrex::Real dSdk = -2.0_rt * d * std::sinh(2.0_rt * k * d) /
65 utils::powi(std::cosh(2.0_rt * k * d), 2);
66 const amrex::Real dCdk = -dSdk;
67 const amrex::Real dC0dk =
68 d / (2.0_rt * C0 * utils::powi(std::cosh(k * d), 2));
69 // Calculate derivatives with products
70 const amrex::Real dC2dk =
71 ((4.0_rt * utils::powi(C, 2) *
72 ((dC0dk * (2.0_rt + (7.0_rt * utils::powi(S, 2)))) +
73 (C0 * 14.0_rt * S * dSdk))) -
74 (C0 * (2.0_rt + (7.0_rt * utils::powi(S, 2))) * 8.0_rt * C *
75 dCdk)) /
76 (16.0_rt * utils::powi(C, 4));
77 const amrex::Real dC4dk =
78 ((32.0_rt * utils::powi(C, 5) *
79 ((dC0dk * numterm_C4) +
80 (C0 * ((32.0_rt * dSdk) - (232.0_rt * S * dSdk) -
81 (1200.0_rt * utils::powi(S, 2) * dSdk) -
82 (284.0_rt * utils::powi(S, 3) * dSdk) +
83 (730.0_rt * utils::powi(S, 4)))))) -
84 (C0 * numterm_C4 * 160.0_rt * utils::powi(C, 4) * dCdk)) /
85 (1024.0_rt * utils::powi(C, 10));
86
87 // Calculate derivative for loop convergence
88 amrex::Real dfdk =
89 (g * utils::powi(C0, 2)) + (g * k * 2.0_rt * C0 * dC0dk);
90 // Add additional terms depending on order
91 if (order >= 2) {
92 dfdk +=
93 (g * ((2.0_rt * C0 * utils::powi(eps, 2) * C2) +
94 (utils::powi(eps, 4) * utils::powi(C2, 2)))) +
95 (g * k *
96 ((2.0_rt * dC0dk * utils::powi(eps, 2) * C2) +
97 (2.0_rt * C0 *
98 ((2.0_rt * eps * depsdk * C2) +
99 (utils::powi(eps, 2) * dC2dk))) +
100 (4.0_rt * utils::powi(eps, 3) * utils::powi(C2, 2) * depsdk) +
101 (utils::powi(eps, 4) * 2.0_rt * C2 * dC2dk)));
102 }
103 if (order >= 4) {
104 dfdk +=
105 (g * ((2.0_rt * utils::powi(eps, 4) * C0 * C4) +
106 (2.0_rt * utils::powi(eps, 6) * C2 * C4) +
107 (utils::powi(eps, 8) * utils::powi(C4, 2)))) +
108 (g * k *
109 ((8.0_rt * utils::powi(eps, 3) * depsdk * C0 * C4) +
110 (2.0_rt * utils::powi(eps, 4) *
111 ((C0 * dC4dk) + (C4 * dC0dk))) +
112 (12.0_rt * utils::powi(eps, 5) * depsdk * C2 * C4) +
113 (2.0_rt * utils::powi(eps, 6) *
114 ((C2 * dC4dk) + (C4 * dC2dk))) +
115 (8.0_rt * utils::powi(eps, 7) * depsdk * utils::powi(C4, 2)) +
116 (utils::powi(eps, 8) * 2.0_rt * C4 * dC4dk)));
117 }
118 k = k - (f / dfdk);
119
120 iter += 1;
121 f = g * k * utils::powi(C0, 2);
122 // Add additional terms depending on order
123 if (order >= 2) {
124 f += g * k *
125 ((2.0_rt * C0 * utils::powi(eps, 2) * C2) +
126 (utils::powi(eps, 4) * utils::powi(C2, 2)));
127 }
128 if (order >= 4) {
129 f += g * k *
130 ((2.0_rt * utils::powi(eps, 4) * C0 * C4) +
131 (2.0_rt * utils::powi(eps, 6) * C2 * C4) +
132 (utils::powi(eps, 8) * utils::powi(C4, 2)));
133 }
134 // Subtract omega^2 to measure convergence
135 f -= omega * omega;
136 }
137
138 if (k < tol) {
139 // Return negative wavelength if faulty result
140 return -1;
141 }
142 if (std::isnan(k)) {
143 return -2;
144 }
145 if (iter == iter_max) {
146 return -3;
147 }
148 // Return wavelength calculated from wavenumber
149 return 2.0_rt * std::numbers::pi_v<amrex::Real> / k;
150}
151
152AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void stokes_coefficients(
153 int stokes_order,
154 amrex::Real wavenumber,
155 amrex::Real water_depth,
156 amrex::Real& c0,
157 amrex::Real& a11,
158 amrex::Real& a22,
159 amrex::Real& b22,
160 amrex::Real& c2,
161 amrex::Real& a31,
162 amrex::Real& a33,
163 amrex::Real& b31,
164 amrex::Real& a42,
165 amrex::Real& a44,
166 amrex::Real& b42,
167 amrex::Real& b44,
168 amrex::Real& c4,
169 amrex::Real& a51,
170 amrex::Real& a53,
171 amrex::Real& a55,
172 amrex::Real& b53,
173 amrex::Real& b55)
174{
175
176 amrex::Real kd = wavenumber * water_depth;
177 kd = amrex::min<amrex::Real>(kd, 50.0_rt * std::numbers::pi_v<amrex::Real>);
178
179 // Exponential definition of S = sech(2kd)
180 amrex::Real S =
181 2.0_rt * std::exp(2.0_rt * kd) / (std::exp(4.0_rt * kd) + 1.0_rt);
182 amrex::Real C = 1.0_rt - S;
183 amrex::Real Sh = std::sinh(kd);
184 amrex::Real Th = std::tanh(kd);
185 // Exponential definition of coth(kd)
186 amrex::Real CTh =
187 (1.0_rt + std::exp(-2.0_rt * kd)) / (1.0_rt - std::exp(-2 * kd));
188
189 c0 = std::sqrt(Th);
190 a11 = 1.0_rt / std::sinh(kd); // Hyperbolic cosecant
191 // Second order coefficients
192 a22 = 3.0_rt * utils::powi(S, 2) / (2.0_rt * utils::powi(C, 2));
193 b22 = CTh * (1.0_rt + (2.0_rt * S)) / (2.0_rt * C);
194 c2 = std::sqrt(Th) * (2.0_rt + (7.0_rt * utils::powi(S, 2))) /
195 (4.0_rt * utils::powi(C, 2));
196 if (stokes_order == 2) {
197 return;
198 }
199
200 // Third order coefficients
201 a31 = (-4.0_rt - (20.0_rt * S) + (10.0_rt * utils::powi(S, 2)) -
202 (13.0_rt * utils::powi(S, 3))) /
203 (8.0_rt * Sh * utils::powi(C, 3));
204 a33 = ((-2.0_rt * utils::powi(S, 2)) + (11.0_rt * utils::powi(S, 3))) /
205 (8.0_rt * Sh * utils::powi(C, 3));
206 b31 = -3.0_rt *
207 (1.0_rt + (3.0_rt * S) + (3.0_rt * utils::powi(S, 2)) +
208 (2.0_rt * utils::powi(S, 3))) /
209 (8.0_rt * utils::powi(C, 3));
210 if (stokes_order == 3) {
211 return;
212 }
213
214 // Fourth order coefficients
215 a42 = ((12.0_rt * S) - (14.0_rt * utils::powi(S, 2)) -
216 (264.0_rt * utils::powi(S, 3)) - (45.0_rt * utils::powi(S, 4)) -
217 (13.0_rt * utils::powi(S, 5))) /
218 (24.0_rt * utils::powi(C, 5));
219 a44 = ((10.0_rt * utils::powi(S, 3)) - (174.0_rt * utils::powi(S, 4)) +
220 (291.0_rt * utils::powi(S, 5)) + (278.0_rt * utils::powi(S, 6))) /
221 (48.0_rt * (3.0_rt + (2.0_rt * S)) * utils::powi(C, 5));
222 b42 = CTh *
223 (6.0_rt - (26.0_rt * S) - (182.0_rt * utils::powi(S, 2)) -
224 (204.0_rt * utils::powi(S, 3)) - (25.0_rt * utils::powi(S, 4)) +
225 (26.0_rt * utils::powi(S, 5))) /
226 (6.0_rt * (3.0_rt + (2.0_rt * S)) * utils::powi(C, 4));
227 b44 = CTh *
228 (24.0_rt + (92.0_rt * S) + (122.0_rt * utils::powi(S, 2)) +
229 (66.0_rt * utils::powi(S, 3)) + (67.0_rt * utils::powi(S, 4)) +
230 (34.0_rt * utils::powi(S, 5))) /
231 (24.0_rt * (3.0_rt + (2.0_rt * S)) * utils::powi(C, 4));
232 c4 = std::sqrt(Th) *
233 (4.0_rt + (32.0_rt * S) - (116.0_rt * utils::powi(S, 2)) -
234 (400.0_rt * utils::powi(S, 3)) - (71.0_rt * utils::powi(S, 4)) +
235 (146.0_rt * utils::powi(S, 5))) /
236 (32.0_rt * utils::powi(C, 5));
237 if (stokes_order == 4) {
238 return;
239 }
240
241 // Fifth order coefficients
242 a51 = (-1184.0_rt + (32.0_rt * S) + (13232.0_rt * utils::powi(S, 2)) +
243 (21712.0_rt * utils::powi(S, 3)) + (20940.0_rt * utils::powi(S, 4)) +
244 (12554.0_rt * utils::powi(S, 5)) - (500.0_rt * utils::powi(S, 6)) -
245 (3341.0_rt * utils::powi(S, 7)) - (670.0_rt * utils::powi(S, 8))) /
246 (64.0_rt * Sh * (3.0_rt + (2.0_rt * S)) * (4.0_rt + S) *
247 utils::powi(C, 6));
248 a53 = ((4.0_rt * S) + (105.0_rt * utils::powi(S, 2)) +
249 (198.0_rt * utils::powi(S, 3)) - (1376.0_rt * utils::powi(S, 4)) -
250 (1302.0_rt * utils::powi(S, 5)) - (117.0_rt * utils::powi(S, 6)) +
251 (58.0_rt * utils::powi(S, 7))) /
252 (32.0_rt * Sh * (3.0_rt + (2.0_rt * S)) * utils::powi(C, 6));
253 a55 = ((-6.0_rt * utils::powi(S, 3)) + (272.0_rt * utils::powi(S, 4)) -
254 (1552.0_rt * utils::powi(S, 5)) + (852.0_rt * utils::powi(S, 6)) +
255 (2029.0_rt * utils::powi(S, 7)) + (430.0_rt * utils::powi(S, 8))) /
256 (64.0_rt * Sh * (3.0_rt + (2.0_rt * S)) * (4.0_rt + S) *
257 utils::powi(C, 6));
258 b53 =
259 9.0_rt *
260 (132.0_rt + (17.0_rt * S) - (2216.0_rt * utils::powi(S, 2)) -
261 (5897.0_rt * utils::powi(S, 3)) - (6292.0_rt * utils::powi(S, 4)) -
262 (2687.0_rt * utils::powi(S, 5)) + (194.0_rt * utils::powi(S, 6)) +
263 (467.0_rt * utils::powi(S, 7)) + (82.0_rt * utils::powi(S, 8))) /
264 (128.0_rt * (3.0_rt + (2.0_rt * S)) * (4.0_rt + S) * utils::powi(C, 6));
265 b55 =
266 5.0_rt *
267 (300.0_rt + (1579.0_rt * S) + (3176.0_rt * utils::powi(S, 2)) +
268 (2949.0_rt * utils::powi(S, 3)) + (1188.0_rt * utils::powi(S, 4)) +
269 (675.0_rt * utils::powi(S, 5)) + (1326.0_rt * utils::powi(S, 6)) +
270 (827.0_rt * utils::powi(S, 7)) + (130.0_rt * utils::powi(S, 8))) /
271 (384.0_rt * (3.0_rt + (2.0_rt * S)) * (4.0_rt + S) * utils::powi(C, 6));
272 if (stokes_order == 5) {
273 return;
274 }
275
276 if (stokes_order > 5 || stokes_order < 2) {
277 amrex::Abort(
278 "invalid stokes order specified. It should be between 2,3,4 or 5 ");
279 }
280}
281
282// Based on Fenton 1985
283AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void stokes_waves(
284 int stokes_order,
285 amrex::Real wavelength,
286 amrex::Real water_depth,
287 amrex::Real wave_height,
288 amrex::Real zsl,
289 amrex::Real g,
290 amrex::Real x,
291 amrex::Real z,
292 amrex::Real time,
293 amrex::Real phase_offset,
294 amrex::Real hyp_lo_limit,
295 amrex::Real hyp_hi_limit,
296 amrex::Real& eta,
297 amrex::Real& u_w,
298 amrex::Real& v_w,
299 amrex::Real& w_w)
300{
301 const amrex::Real wavenumber =
302 2.0_rt * std::numbers::pi_v<amrex::Real> / wavelength;
303
304 // some parameters
305 amrex::Real c0{0.0_rt};
306 amrex::Real a11{0.0_rt}, a22{0.0_rt}, b22{0.0_rt}, c2{0.0_rt};
307 amrex::Real a31{0.0_rt}, a33{0.0_rt}, b31{0.0_rt}, a42{0.0_rt}, a44{0.0_rt};
308 amrex::Real b42{0.0_rt}, b44{0.0_rt}, c4{0.0_rt};
309 amrex::Real a51{0.0_rt}, a53{0.0_rt}, a55{0.0_rt}, b53{0.0_rt}, b55{0.0_rt};
310
312 stokes_order, wavenumber, water_depth, c0, a11, a22, b22, c2, a31, a33,
313 b31, a42, a44, b42, b44, c4, a51, a53, a55, b53, b55);
314
315 const amrex::Real eps = wavenumber * wave_height / 2.0_rt; // Steepness (ka)
316 const amrex::Real c =
317 (c0 + (utils::powi(eps, 2) * c2) + (utils::powi(eps, 4) * c4)) *
318 std::sqrt(g / wavenumber);
319
320 const amrex::Real omega = c * wavenumber;
321 const amrex::Real phase = (wavenumber * x) - (omega * time) - phase_offset;
322
323 eta = (((eps * std::cos(phase)) // first order term
324 + (utils::powi(eps, 2) * b22 *
325 std::cos(2.0_rt * phase)) // second order term
326 + (utils::powi(eps, 3) * b31 *
327 (std::cos(phase) - std::cos(3.0_rt * phase))) +
328 (utils::powi(eps, 4) * ((b42 * std::cos(2.0_rt * phase)) +
329 (b44 * std::cos(4.0_rt * phase)))) +
330 (utils::powi(eps, 5) * ((-(b53 + b55) * std::cos(phase)) +
331 (b53 * std::cos(3.0_rt * phase)) +
332 (b55 * std::cos(5.0_rt * phase))))) /
333 wavenumber) +
334 zsl;
335
336 // Compute velocities components using Eq.(21) Eq.(23) from Kinnas
337 // https://www.caee.utexas.edu/prof/kinnas/ce358/oenotes/kinnas_stokes11.pdf
338 // Define coefficients using Eq.(19)
339
340 const int MAX_ORDER = 5;
341 amrex::GpuArray<amrex::Real, MAX_ORDER> a;
342 if (stokes_order == 2) {
343 a[0] = a11;
344 a[1] = a22;
345 }
346 if (stokes_order == 3) {
347 a[0] = a11 + ((eps * eps) * a31);
348 a[1] = a22;
349 a[2] = a33;
350 }
351 if (stokes_order == 4) {
352 a[0] = a11 + ((eps * eps) * a31);
353 a[1] = a22 + ((eps * eps) * a42);
354 a[2] = a33;
355 a[3] = a44;
356 }
357 if (stokes_order == 5) {
358 a[0] = a11 + ((eps * eps) * a31) + (utils::powi(eps, 4) * a51);
359 a[1] = a22 + ((eps * eps) * a42);
360 a[2] = a33 + ((eps * eps) * a53);
361 a[3] = a44;
362 a[4] = a55;
363 }
364
365 u_w = 0.0_rt;
366 v_w = 0.0_rt;
367 w_w = 0.0_rt;
368 for (int n = 1; n <= stokes_order; ++n) {
369 // Ensure finite values of velocity for large kd
370 const amrex::Real nkdz = n * wavenumber * (water_depth + (z - zsl));
371 amrex::Real cosh_nkdz = std::cosh(nkdz);
372 amrex::Real sinh_nkdz = std::sinh(nkdz);
373 cosh_nkdz = amrex::min<amrex::Real>(
374 hyp_hi_limit, amrex::max<amrex::Real>(hyp_lo_limit, cosh_nkdz));
375 sinh_nkdz = amrex::min<amrex::Real>(
376 hyp_hi_limit, amrex::max<amrex::Real>(hyp_lo_limit, sinh_nkdz));
377 // Contributions to velocity
378 u_w += utils::powi(eps, n) * static_cast<amrex::Real>(n) * a[n - 1] *
379 cosh_nkdz * std::cos(static_cast<amrex::Real>(n) * phase);
380 w_w += utils::powi(eps, n) * static_cast<amrex::Real>(n) * a[n - 1] *
381 sinh_nkdz * std::sin(static_cast<amrex::Real>(n) * phase);
382 }
383 u_w *= (c0 * std::sqrt(g / wavenumber));
384 w_w *= (c0 * std::sqrt(g / wavenumber));
385}
386
387} // namespace kynema_sgf::ocean_waves::relaxation_zones
388#endif
Definition relaxation_zones_ops.cpp:15
AMREX_FORCE_INLINE amrex::Real stokes_wave_length(const amrex::Real T, const amrex::Real d, const amrex::Real H, const int order, const amrex::Real g, const amrex::Real tol, const int iter_max)
Definition stokes_waves_K.H:28
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void stokes_coefficients(int stokes_order, amrex::Real wavenumber, amrex::Real water_depth, amrex::Real &c0, amrex::Real &a11, amrex::Real &a22, amrex::Real &b22, amrex::Real &c2, amrex::Real &a31, amrex::Real &a33, amrex::Real &b31, amrex::Real &a42, amrex::Real &a44, amrex::Real &b42, amrex::Real &b44, amrex::Real &c4, amrex::Real &a51, amrex::Real &a53, amrex::Real &a55, amrex::Real &b53, amrex::Real &b55)
Definition stokes_waves_K.H:152
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void stokes_waves(int stokes_order, amrex::Real wavelength, amrex::Real water_depth, amrex::Real wave_height, amrex::Real zsl, amrex::Real g, amrex::Real x, amrex::Real z, amrex::Real time, amrex::Real phase_offset, amrex::Real hyp_lo_limit, amrex::Real hyp_hi_limit, amrex::Real &eta, amrex::Real &u_w, amrex::Real &v_w, amrex::Real &w_w)
Definition stokes_waves_K.H:283
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real powi(const amrex::Real &x, const int &y)
Helper function to do pow() with integer exponents and output amrex::Real.
Definition math_ops.H:18
phase
Definition BoundaryPlane.H:21