Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
richardson_kernel.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2026, The Neko Authors
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above
13 copyright notice, this list of conditions and the following
14 disclaimer in the documentation and/or other materials provided
15 with the distribution.
16
17 * Neither the name of the authors nor the names of its
18 contributors may be used to endorse or promote products derived
19 from this software without specific prior written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 POSSIBILITY OF SUCH DAMAGE.
33*/
34
35#ifndef RICHARDSON_KERNEL_H
36#define RICHARDSON_KERNEL_H
37
38#include <cmath>
39#include <algorithm>
40
41/*
42* Similarity laws and corrections for the STABLE regime:
43* Based on Mauritsen et al. 2007
44*/
45
46template<typename T>
48{
49 return 0.17 * (0.25 + 0.75 / (1.0 + 4.0 * Ri_b));
50}
51
52template<typename T>
54{
55 return -0.145 / (1.0 + 4.0 * Ri_b);
56}
57
58template<typename T>
59__device__ T tau_stable(T magu, T Ri_b, T h, T z0, T kappa)
60{
61 T log_hz0 = log(h / z0);
62 return (magu * magu) / (log_hz0 * log_hz0) * (f_tau_stable<T>(Ri_b) / f_tau_stable<T>(0.0)) * (kappa * kappa);
63}
64
65template<typename T>
66__device__ T heat_flux_stable(T ti, T ts, T Ri_b, T h, T z0h, T utau, T kappa, T Pr)
67{
68 return (ti - ts) / log(h / z0h) * (f_theta_stable<T>(Ri_b) / fabs(f_theta_stable<T>(0.0))) * kappa * (utau / Pr);
69}
70
71/*
72* Similarity laws and corrections for the UNSTABLE (convective) regime:
73* Based on Louis 1979
74*/
75
76template<typename T>
78{
79 return 1.0 - (2.0 * Ri_b) / (1.0 + c * sqrt(fabs(Ri_b)));
80}
81
82template<typename T>
84{
85 // Functionally identical to f_tau_convective in Louis 1979
86 return 1.0 - (2.0 * Ri_b) / (1.0 + c * sqrt(fabs(Ri_b)));
87}
88
89template<typename T>
90__device__ T tau_convective(T magu, T Ri_b, T h, T z0, T kappa)
91{
92 T a = kappa / log(h / z0);
93 T b = 2.0;
94 T c = 7.4 * (a * a) * b * sqrt(h / z0);
95
96 return (a * a) * (magu * magu) * f_tau_convective<T>(Ri_b, c);
97}
98
99template<typename T>
100__device__ T heat_flux_convective(T ti, T ts, T Ri_b, T h, T magu, T z0h, T kappa)
101{
102 T a = kappa / log(h / z0h);
103 T b = 2.0;
104 T c = 5.3 * (a * a) * b * sqrt(h / z0h);
105
106 return -(a * a) / 0.74 * magu * (ti - ts) * f_theta_convective<T>(Ri_b, c);
107}
108
109/*
110* Similarity laws and corrections for the NEUTRAL regime:
111*/
112
113template<typename T>
114__device__ T tau_neutral(T magu, T h, T z0, T kappa)
115{
116 T val = (kappa * magu) / log(h / z0);
117 return val * val;
118}
119
120template<typename T>
121__device__ T heat_flux_neutral(T ti, T ts, T h, T z0h, T utau, T kappa)
122{
123 return kappa * utau * (ti - ts) / log(h / z0h);
124}
125
126/*
127 * CUDA kernel for the Richardson wall model.
128 */
129template<typename T, int BC_TYPE>
131 const T* __restrict__ u_d,
132 const T* __restrict__ v_d,
133 const T* __restrict__ w_d,
134 const T* __restrict__ temp_d,
135 const T* __restrict__ temp_w_d,
136 const T* __restrict__ h_d,
137 const T* __restrict__ n_x_d,
138 const T* __restrict__ n_y_d,
139 const T* __restrict__ n_z_d,
143 int n_nodes,
144 T kappa,
145 const T * __restrict__ mu_w_d,
146 const T * __restrict__ rho_w_d,
147 T g1,
148 T g2,
149 T g3,
150 T Pr,
151 T z0,
152 T z0h_in,
153 T bc_value,
161) {
162 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
163 const int str = blockDim.x * gridDim.x;
164 if(idx >= n_nodes) return;
165
166 const T Ri_threshold = 1e-4;
167
168 for (int i = idx; i < n_nodes; i += str) {
169 T ui = u_d[i];
170 T vi = v_d[i];
171 T wi = w_d[i];
172 T ti = temp_d[i];
173 T hi = h_d[i];
174 T mu = mu_w_d[i];
175 T rho = rho_w_d[i];
176
177 // Extract the local normal vector
178 T nx = n_x_d[i];
179 T ny = n_y_d[i];
180 T nz = n_z_d[i];
181
182 // Get the tangential component
183 T normu = ui * nx + vi * ny + wi * nz;
184 ui -= normu * nx;
185 vi -= normu * ny;
186 wi -= normu * nz;
187
188 T magu = sqrt(ui*ui + vi*vi + wi*wi);
189 magu = fmax(magu, (T)1e-6);
190
191 T utau = kappa * magu / log(hi/z0);
192
193 // Zilitinkevich 1995 correlation for thermal roughness
194 T z0h;
195 if (z0h_in < 0.0) {
196 // Note that this uses previous timestep's utau, hence
197 // lags behind by one dt. usually very negligible
198 z0h = z0 * exp(z0h_in * sqrt((utau*z0)/(mu/rho)));
199 } else {
200 z0h = z0h_in;
201 }
202
203 T ts = 0;
204 T q = 0;
205
206 // Initialize variables based on Boundary Condition
207 if constexpr (BC_TYPE == 0) { // Neumann
208 q = bc_value;
209 } else { // Dirichlet
210 ts = bc_value;
211 q = kappa * utau * (ts - ti) / log(hi/z0h);
212 }
213
214 T Ri_b;
215 T g_dot_n = fabs(g1*nx + g2*ny + g3*nz);
216
217 // Compute Bulk Richardson Number
218 if constexpr (BC_TYPE == 0) {
219 Ri_b = -g_dot_n * hi / ti * q / (magu * magu * magu * kappa * kappa);
220 } else {
221 Ri_b = g_dot_n * hi / ti * (ti - ts) / (magu * magu);
222 }
223
224 T tau_mag = 0;
225
226 // Stability regime branching
227 if (Ri_b > Ri_threshold) { // Stable
228 tau_mag = tau_stable<T>(magu, Ri_b, hi, z0, kappa);
229 utau = sqrt(tau_mag);
230 if constexpr (BC_TYPE == 1) {
231 q = heat_flux_stable<T>(ti, ts, Ri_b, hi, z0h, utau, kappa, Pr);
232 }
233 }
234 else if (Ri_b < -Ri_threshold) { // Convective
235 tau_mag = tau_convective<T>(magu, Ri_b, hi, z0, kappa);
236 utau = sqrt(tau_mag);
237 if constexpr (BC_TYPE == 1) {
238 q = heat_flux_convective<T>(ti, ts, Ri_b, hi, magu, z0h, kappa);
239 }
240 }
241 else { // Neutral
242 tau_mag = tau_neutral<T>(magu, hi, z0, kappa);
243 utau = sqrt(tau_mag);
244 if constexpr (BC_TYPE == 1) {
245 q = heat_flux_neutral<T>(ti, ts, hi, z0h, utau, kappa);
246 }
247 }
248
249 // Apply spatial distribution
250 tau_x_d[i] = -rho*tau_mag * ui / magu;
251 tau_y_d[i] = -rho*tau_mag * vi / magu;
252 tau_z_d[i] = -rho*tau_mag * wi / magu;
253
254 // Note: L_ob calculation is omitted from the kernel as it is a pure
255 // diagnostic variable and writing it to global memory would require
256 // passing an extra L_ob_d array pointer if GPU diagnostics are needed.
257
258 Ri_b_diagn[i] = Ri_b;
259 L_ob_diagn[i] = 9999;
260 utau_diagn[i] = utau;
261 magu_diagn[i] = magu;
262 ti_diagn[i] = ti;
263 ts_diagn[i] = temp_w_d[i];
264 q_diagn[i] = q;
265 }
266}
267
268#endif // RICHARDSON_KERNEL_H
__global__ void ale_add_kinematics_kernel(const int n, T *__restrict__ wx, T *__restrict__ wy, T *__restrict__ wz, const T *__restrict__ x_ref, const T *__restrict__ y_ref, const T *__restrict__ z_ref, const T *__restrict__ phi, const T *__restrict__ x, const T *__restrict__ y, const T *__restrict__ z, const kinematics_params_t kin_params)
const int i
const int e
__device__ T f_tau_stable(T Ri_b)
__device__ T tau_convective(T magu, T Ri_b, T h, T z0, T kappa)
__device__ T heat_flux_neutral(T ti, T ts, T h, T z0h, T utau, T kappa)
__device__ T tau_neutral(T magu, T h, T z0, T kappa)
__device__ T heat_flux_convective(T ti, T ts, T Ri_b, T h, T magu, T z0h, T kappa)
__device__ T tau_stable(T magu, T Ri_b, T h, T z0, T kappa)
__device__ T f_theta_convective(T Ri_b, T c)
__device__ T heat_flux_stable(T ti, T ts, T Ri_b, T h, T z0h, T utau, T kappa, T Pr)
__device__ T f_tau_convective(T Ri_b, T c)
__global__ void richardson_compute(const T *__restrict__ u_d, const T *__restrict__ v_d, const T *__restrict__ w_d, const T *__restrict__ temp_d, const T *__restrict__ temp_w_d, const T *__restrict__ h_d, const T *__restrict__ n_x_d, const T *__restrict__ n_y_d, const T *__restrict__ n_z_d, T *__restrict__ tau_x_d, T *__restrict__ tau_y_d, T *__restrict__ tau_z_d, int n_nodes, T kappa, const T *__restrict__ mu_w_d, const T *__restrict__ rho_w_d, T g1, T g2, T g3, T Pr, T z0, T z0h_in, T bc_value, T *__restrict__ Ri_b_diagn, T *__restrict__ L_ob_diagn, T *__restrict__ utau_diagn, T *__restrict__ magu_diagn, T *__restrict__ ti_diagn, T *__restrict__ ts_diagn, T *__restrict__ q_diagn)
__device__ T f_theta_stable(T Ri_b)