Neko 1.99.5
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
richardson_cpu.f90
Go to the documentation of this file.
1! Copyright (c) 2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
35 use num_types, only : rp
36 use utils, only : neko_error, neko_warning
37 use logger, only : log_size, neko_log
38 use math, only : glsum, glmin, glmax
39 implicit none
40 private
41
43
44 abstract interface
45 function tau_interface(magu, Ri_b, h, z0, l, kappa) result(tau)
46 import rp
47 real(kind=rp), intent(in) :: magu, ri_b, h, z0, l, kappa
48 real(kind=rp) :: tau
49 end function tau_interface
50
51 function heat_flux_interface(ti, ts, Ri_b, h, magu, z0h, Pr,&
52 l, utau, kappa) result(heat_flux)
53 import rp
54 real(kind=rp), intent(in) :: ts, ti, ri_b, h, magu
55 real(kind=rp), intent(in) :: z0h, pr, l, utau, kappa
56 real(kind=rp) :: heat_flux
57 end function heat_flux_interface
58
59 end interface
60
61 ! These will point to the correct functions
62 ! depending on stability regime and bc_type.
63 procedure(tau_interface), pointer :: tau_ptr => null()
64 procedure(heat_flux_interface), pointer :: heat_flux_ptr => null()
65
66contains
67
69 subroutine compute_ri_b(bc_type, g_dot_n, hi, ti, ts, magu, kappa, q, Ri_b)
70 character(len=*), intent(in) :: bc_type
71 real(kind=rp), intent(in) :: hi, ti, ts
72 real(kind=rp), intent(in) :: magu, kappa, g_dot_n
73 real(kind=rp), intent(inout) :: q, ri_b
74
75 select case (bc_type)
76 case ("neumann")
77 ri_b = - g_dot_n*hi / ti*q / (magu**3*kappa**2)
78 case ("dirichlet")
79 ri_b = g_dot_n*hi/ti*(ti - ts)/magu**2
80 case default
81 call neko_error("Invalid specified temperature b.c. type " // &
82 "('neumann' or 'dirichlet'?)")
83 end select
84 end subroutine compute_ri_b
85
87 subroutine assign_bc_value(bc_type,bc_value,q,ts,ti,kappa,utau,z0h,hi)
88 character(len=*), intent(in) :: bc_type
89 real(kind=rp), intent(in) :: hi, ti, kappa, utau, z0h,bc_value
90 real(kind=rp), intent(inout) :: q,ts
91
92 select case (bc_type)
93 case ("neumann")
94 ! ts not used
95 q = bc_value
96 case ("dirichlet")
97 ts = bc_value
98 q = kappa*utau*(ts - ti)/log(hi/z0h)
99 case default
100 call neko_error("Invalid specified temperature b.c. type " // &
101 "('neumann' or 'dirichlet'?)")
102 end select
103 end subroutine assign_bc_value
104
107 subroutine set_stability_regime(Ri_b,Ri_threshold)
108 real(kind=rp), intent(in) :: ri_b, ri_threshold
109
110 if (ri_b > ri_threshold) then
113 elseif (ri_b < -ri_threshold) then
116 else
119 end if
120 end subroutine set_stability_regime
121
124 subroutine richardson_compute_cpu(u, v, w, temp, ind_r, ind_s, ind_t, ind_e, &
125 n_x, n_y, n_z, h, tau_x, tau_y, tau_z, n_nodes, lx, nelv, &
126 kappa, mu_w, rho_w, g_vec, Pr, z0, z0h_in, bc_type, bc_value, tstep, &
127 Ri_b_diagn, L_ob_diagn, utau_diagn, magu_diagn, ti_diagn, ts_diagn,&
128 q_diagn, h_x_idx, h_y_idx, h_z_idx)
129 integer, intent(in) :: n_nodes, lx, nelv, tstep
130 real(kind=rp), dimension(lx, lx, lx, nelv), intent(in) :: u, v, w, temp
131 integer, intent(in), dimension(n_nodes) :: ind_r, ind_s, ind_t, ind_e
132 real(kind=rp), dimension(n_nodes), intent(in) :: n_x, n_y, n_z, h
133 real(kind=rp), intent(in) :: kappa, z0, z0h_in, bc_value, pr
134 real(kind=rp), dimension(3), intent(in) :: g_vec
135 real(kind=rp), dimension(n_nodes), intent(in) :: mu_w, rho_w
136 real(kind=rp) :: g_dot_n
137 character(len=*), intent(in) :: bc_type
138 real(kind=rp), dimension(n_nodes), intent(inout) :: tau_x, tau_y, tau_z
139 integer :: i
140 real(kind=rp) :: ui, vi, wi, hi, rho, mu
141 real(kind=rp) :: normu, z0h
142 real(kind=rp) :: l
143 real(kind=rp), parameter :: tol = 0.001_rp
144 real(kind=rp), parameter :: nr_step = 0.001_rp
145 real(kind=rp), parameter :: ri_threshold = 0.0001_rp
146 character(len=LOG_SIZE) :: log_buf
147 real(kind=rp) :: utau, ri_b, l_ob, magu, q, ti, ts
148 real(kind=rp), dimension(n_nodes), intent(inout) :: ri_b_diagn, l_ob_diagn
149 real(kind=rp), dimension(n_nodes), intent(inout) :: utau_diagn, magu_diagn
150 real(kind=rp), dimension(n_nodes), intent(inout) :: ti_diagn, ts_diagn
151 real(kind=rp), dimension(n_nodes), intent(inout) :: q_diagn
152 integer, dimension(n_nodes), intent(in) :: h_x_idx
153 integer, dimension(n_nodes), intent(in) :: h_y_idx
154 integer, dimension(n_nodes), intent(in) :: h_z_idx
155
156 do i=1, n_nodes
157 ! Sample the variables
158 ui = u(ind_r(i), ind_s(i), ind_t(i), ind_e(i))
159 vi = v(ind_r(i), ind_s(i), ind_t(i), ind_e(i))
160 wi = w(ind_r(i), ind_s(i), ind_t(i), ind_e(i))
161 ti = temp(ind_r(i), ind_s(i), ind_t(i), ind_e(i))
162 hi = h(i)
163 rho = rho_w(i)
164 mu = mu_w(i)
165
166 ! Project on horizontal directions
167 normu = ui * n_x(i) + vi * n_y(i) + wi * n_z(i)
168 ui = ui - normu * n_x(i)
169 vi = vi - normu * n_y(i)
170 wi = wi - normu * n_z(i)
171
172 ! Compute velocity magnitude
173 magu = sqrt(ui**2 + vi**2 + wi**2)
174 magu = max(magu, 1.0e-6_rp)
175 utau = magu*kappa / log(hi/z0)
176
177 ! Compute thermal roughness length from Zilitinkevich, 1995
178 if (z0h_in < 0) then
179 ! z0h_in is interpreted as -C_Zil (Zilitinkevich constant) for z0h
180 z0h = z0 * exp(z0h_in*sqrt((utau*z0)/(mu/rho)))
181 else
182 z0h = z0h_in
183 end if
184
185 ! Get q, ts based on bc_type
186 ! Maybe redundant, but needed to initialise Rib
187 call assign_bc_value(bc_type,bc_value,q,ts,ti,kappa,utau,z0h,hi)
188
189 ! Compute g along the normal (generalisation for hills and similar)
190 g_dot_n = abs(g_vec(1)*n_x(i) + g_vec(2)*n_y(i) + g_vec(3)*n_z(i))
191
192 ! Compute Richardson and set stability accordingly
193 call compute_ri_b(bc_type, g_dot_n, hi, ti, ts, magu, kappa, q, ri_b)
194 call set_stability_regime(ri_b, ri_threshold)
195
196 ! Set length scale
197 l = kappa * hi
198 ! Compute u*
199 utau = sqrt(tau_ptr(magu, ri_b, hi, z0, l, kappa))
200 select case (bc_type)
201 case ("neumann")
202 !!! TEMPORARY: neutral log-law approximation
203 ts = ti - (q * pr * log(hi/z0h)) / (max(utau, 1e-6_rp) * kappa)
204 q = q
205 case ("dirichlet")
206 ! Compute q
207 q = heat_flux_ptr(ti, ts, ri_b, hi, magu, z0h, pr, l, utau, kappa)
208 case default
209 call neko_error("Invalid specified temperature b.c. type " // &
210 "('neumann' or 'dirichlet'?)")
211 end select
212
213 ! Distribute according to the velocity vector and bound magu
214 ! to avoid 0 division
215 magu = max(magu, 1.0e-6_rp)
216 tau_x(i) = -rho*utau**2 * ui / magu
217 tau_y(i) = -rho*utau**2 * vi / magu
218 tau_z(i) = -rho*utau**2 * wi / magu
219 if (abs(ri_b) <= ri_threshold) then
220 ! Neutral (L_ob undefined)
221 l_ob = 1e10_rp
222 else
223 l_ob = -(ts*utau**3)/(kappa*g_dot_n*q)
224 end if
225
226 ri_b_diagn(i) = ri_b
227 l_ob_diagn(i) = l_ob
228 utau_diagn(i) = utau
229 magu_diagn(i) = magu
230 ti_diagn(i) = ti
231 ts_diagn(i) = temp(ind_r(i) - h_x_idx(i), ind_s(i) - h_y_idx(i), &
232 ind_t(i) - h_z_idx(i), ind_e(i))
233 q_diagn(i) = q
234 end do
235
236 end subroutine richardson_compute_cpu
237
240 function tau_stable(magu, Ri_b, h, z0, l, kappa) result(tau)
241 real(kind=rp), intent(in) :: magu, ri_b, h, z0, l, kappa
242 real(kind=rp) :: tau
243
244 tau = magu**2/(log(h/z0)**2) * f_tau_stable(ri_b)/ &
245 f_tau_stable(0.0_rp) * (l/h)**2
246 end function tau_stable
247
248 function heat_flux_stable(ti, ts, Ri_b, h, magu, z0h, Pr,&
249 l, utau, kappa) result(heat_flux)
250 real(kind=rp), intent(in) :: ts, ti, ri_b, h, magu
251 real(kind=rp), intent(in) :: z0h, pr, l, utau, kappa
252 real(kind=rp) :: heat_flux
253
254 heat_flux = (ti - ts)/(log(h/z0h)) * &
255 f_theta_stable(ri_b)/abs(f_theta_stable(0.0_rp)) * &
256 (l/h) * utau/pr
257 end function heat_flux_stable
258
259 function f_tau_stable(Ri_b) result(f_tau)
260 real(kind=rp), intent(in) :: ri_b
261 real(kind=rp) :: f_tau
262
263 f_tau = 0.17 * (0.25 + 0.75 / (1.0 + 4.0*ri_b))
264 end function f_tau_stable
265
266 function f_theta_stable(Ri_b) result(f_theta)
267 real(kind=rp), intent(in) :: ri_b
268 real(kind=rp) :: f_theta
269
270 f_theta = -0.145 / (1.0 + 4.0 * ri_b)
271 end function f_theta_stable
272
275 function tau_convective(magu, Ri_b, h, z0, l, kappa) result(tau)
276 real(kind=rp), intent(in) :: magu, ri_b, h, z0, l, kappa
277 real(kind=rp) :: tau
278 real(kind=rp) :: a, b, c
279
280 a = kappa / log(h/z0)
281 b = 2.0
282 c = 7.4 * a**2 * b * (h/z0)**0.5
283
284 tau = a**2 * magu**2 * f_tau_convective(ri_b, c)
285 end function tau_convective
286
287 function heat_flux_convective(ti, ts, Ri_b, h, magu, z0h, Pr,&
288 l, utau, kappa) result(heat_flux)
289 real(kind=rp), intent(in) :: ts, ti, ri_b, h, magu
290 real(kind=rp), intent(in) :: z0h, pr, l, utau, kappa
291 real(kind=rp) :: heat_flux
292 real(kind=rp) :: a, b, c
293
294 a = kappa / log(h/z0h)
295 b = 2.0
296 c = 5.3 * a**2 * b * (h/z0h)**0.5
297
298 heat_flux = - a**2 / 0.74 * magu * &
299 (ti - ts) * f_theta_convective(ri_b, c)
300
301 end function heat_flux_convective
302
303 function f_tau_convective(Ri_b, c) result(f_tau)
304 real(kind=rp), intent(in) :: ri_b, c
305 real(kind=rp) :: f_tau
306
307 f_tau = 1.0 - 2*ri_b / (1.0 + c * abs(ri_b)**0.5)
308 end function f_tau_convective
309
310 function f_theta_convective(Ri_b, c) result(f_theta)
311 real(kind=rp), intent(in) :: ri_b, c
312 real(kind=rp) :: f_theta
313
314 f_theta = 1.0 - 2*ri_b / (1.0 + c * abs(ri_b)**0.5)
315 end function f_theta_convective
316
318 function tau_neutral(magu, Ri_b, h, z0, l, kappa) result(tau)
319 real(kind=rp), intent(in) :: magu, ri_b, h, z0, l, kappa
320 real(kind=rp) :: tau
321
322 tau = (kappa*magu/log(h/z0))**2
323 end function tau_neutral
324
325 function heat_flux_neutral(ti, ts, Ri_b, h, magu, z0h, Pr,&
326 l, utau, kappa) result(heat_flux)
327 real(kind=rp), intent(in) :: ts, ti, ri_b, h, magu
328 real(kind=rp), intent(in) :: z0h, pr, l, utau, kappa
329 real(kind=rp) :: heat_flux
330
331 heat_flux = kappa*utau * (ti - ts)/log(h/z0h)
332 end function heat_flux_neutral
333
334end module richardson_cpu
Logging routines.
Definition log.f90:34
type(log_t), public neko_log
Global log stream.
Definition log.f90:80
integer, parameter, public log_size
Definition log.f90:46
Definition math.f90:60
real(kind=rp) function, public glsum(a, n)
Sum a vector of length n.
Definition math.f90:629
real(kind=rp) function, public glmax(a, n)
Max of a vector of length n.
Definition math.f90:650
real(kind=rp) function, public glmin(a, n)
Min of a vector of length n.
Definition math.f90:688
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Implements the CPU kernel for the richardson_t type.
subroutine assign_bc_value(bc_type, bc_value, q, ts, ti, kappa, utau, z0h, hi)
Initialises q when the temperature surface bc is dirichlet.
procedure(heat_flux_interface), pointer heat_flux_ptr
real(kind=rp) function f_theta_convective(ri_b, c)
real(kind=rp) function f_tau_stable(ri_b)
real(kind=rp) function heat_flux_neutral(ti, ts, ri_b, h, magu, z0h, pr, l, utau, kappa)
subroutine, public richardson_compute_cpu(u, v, w, temp, ind_r, ind_s, ind_t, ind_e, n_x, n_y, n_z, h, tau_x, tau_y, tau_z, n_nodes, lx, nelv, kappa, mu_w, rho_w, g_vec, pr, z0, z0h_in, bc_type, bc_value, tstep, ri_b_diagn, l_ob_diagn, utau_diagn, magu_diagn, ti_diagn, ts_diagn, q_diagn, h_x_idx, h_y_idx, h_z_idx)
Main routine to compute the surface stresses based on richardson.
real(kind=rp) function f_theta_stable(ri_b)
real(kind=rp) function tau_convective(magu, ri_b, h, z0, l, kappa)
Similarity laws and corrections for the UNSTABLE (convective) regime: Based on Louis 1979.
subroutine set_stability_regime(ri_b, ri_threshold)
Sets the stability regime based on the Richardson number value (quite arbitrary).
real(kind=rp) function f_tau_convective(ri_b, c)
procedure(tau_interface), pointer tau_ptr
real(kind=rp) function heat_flux_stable(ti, ts, ri_b, h, magu, z0h, pr, l, utau, kappa)
real(kind=rp) function tau_stable(magu, ri_b, h, z0, l, kappa)
Similarity laws and corrections for the STABLE regime: Based on Mauritsen et al. 2007.
real(kind=rp) function heat_flux_convective(ti, ts, ri_b, h, magu, z0h, pr, l, utau, kappa)
real(kind=rp) function tau_neutral(magu, ri_b, h, z0, l, kappa)
Similarity laws and corrections for the NEUTRAL regime:
subroutine compute_ri_b(bc_type, g_dot_n, hi, ti, ts, magu, kappa, q, ri_b)
Computes the Richardson number.
Utilities.
Definition utils.f90:35
subroutine, public neko_warning(warning_msg)
Reports a warning to standard output.
Definition utils.f90:398
#define max(a, b)
Definition tensor.cu:40