Neko 1.99.9
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 !! @note These are reassigned per node inside the compute loop, so every
64 !! thread needs its own copy. They are always set before being used.
65 procedure(tau_interface), pointer :: tau_ptr => null()
66 procedure(heat_flux_interface), pointer :: heat_flux_ptr => null()
67 !$omp threadprivate(tau_ptr, heat_flux_ptr)
68
69contains
70
72 subroutine compute_ri_b(bc_type, g_dot_n, hi, ti, ts, magu, kappa, q, Ri_b)
73 character(len=*), intent(in) :: bc_type
74 real(kind=rp), intent(in) :: hi, ti, ts
75 real(kind=rp), intent(in) :: magu, kappa, g_dot_n
76 real(kind=rp), intent(inout) :: q, ri_b
77
78 select case (bc_type)
79 case ("neumann")
80 ri_b = - g_dot_n*hi / ti*q / (magu**3*kappa**2)
81 case ("dirichlet")
82 ri_b = g_dot_n*hi/ti*(ti - ts)/magu**2
83 case default
84 call neko_error("Invalid specified temperature b.c. type " // &
85 "('neumann' or 'dirichlet'?)")
86 end select
87 end subroutine compute_ri_b
88
90 subroutine assign_bc_value(bc_type,bc_value,q,ts,ti,kappa,utau,z0h,hi)
91 character(len=*), intent(in) :: bc_type
92 real(kind=rp), intent(in) :: hi, ti, kappa, utau, z0h,bc_value
93 real(kind=rp), intent(inout) :: q,ts
94
95 select case (bc_type)
96 case ("neumann")
97 ! ts not used
98 q = bc_value
99 case ("dirichlet")
100 ts = bc_value
101 q = kappa*utau*(ts - ti)/log(hi/z0h)
102 case default
103 call neko_error("Invalid specified temperature b.c. type " // &
104 "('neumann' or 'dirichlet'?)")
105 end select
106 end subroutine assign_bc_value
107
110 subroutine set_stability_regime(Ri_b,Ri_threshold)
111 real(kind=rp), intent(in) :: ri_b, ri_threshold
112
113 if (ri_b > ri_threshold) then
116 elseif (ri_b < -ri_threshold) then
119 else
122 end if
123 end subroutine set_stability_regime
124
127 subroutine richardson_compute_cpu(u, v, w, temp, temp_w, &
128 n_x, n_y, n_z, h, tau_x, tau_y, tau_z, n_nodes, &
129 kappa, mu_w, rho_w, g_vec, Pr, z0, z0h_in, bc_type, bc_value, tstep, &
130 Ri_b_diagn, L_ob_diagn, utau_diagn, magu_diagn, ti_diagn, ts_diagn,&
131 q_diagn)
132 integer, intent(in) :: n_nodes, tstep
133 real(kind=rp), dimension(n_nodes), intent(in) :: u, v, w, temp, temp_w
134 real(kind=rp), dimension(n_nodes), intent(in) :: n_x, n_y, n_z, h
135 real(kind=rp), intent(in) :: kappa, z0, z0h_in, bc_value, pr
136 real(kind=rp), dimension(3), intent(in) :: g_vec
137 real(kind=rp), dimension(n_nodes), intent(in) :: mu_w, rho_w
138 real(kind=rp) :: g_dot_n
139 character(len=*), intent(in) :: bc_type
140 real(kind=rp), dimension(n_nodes), intent(inout) :: tau_x, tau_y, tau_z
141 integer :: i
142 real(kind=rp) :: ui, vi, wi, hi, rho, mu
143 real(kind=rp) :: normu, z0h
144 real(kind=rp) :: l
145 real(kind=rp), parameter :: tol = 0.001_rp
146 real(kind=rp), parameter :: nr_step = 0.001_rp
147 real(kind=rp), parameter :: ri_threshold = 0.0001_rp
148 character(len=LOG_SIZE) :: log_buf
149 real(kind=rp) :: utau, ri_b, l_ob, magu, q, ti, ts
150 real(kind=rp), dimension(n_nodes), intent(inout) :: ri_b_diagn, l_ob_diagn
151 real(kind=rp), dimension(n_nodes), intent(inout) :: utau_diagn, magu_diagn
152 real(kind=rp), dimension(n_nodes), intent(inout) :: ti_diagn, ts_diagn
153 real(kind=rp), dimension(n_nodes), intent(inout) :: q_diagn
154
155 !$omp parallel do private(i, ui, vi, wi, hi, rho, mu, normu, z0h, l, &
156 !$omp& utau, Ri_b, L_ob, magu, q, ti, ts, g_dot_n)
157 do i=1, n_nodes
158 ! Sample the variables
159 ui = u(i)
160 vi = v(i)
161 wi = w(i)
162 ti = temp(i)
163 hi = h(i)
164 rho = rho_w(i)
165 mu = mu_w(i)
166
167 ! Project on horizontal directions
168 normu = ui * n_x(i) + vi * n_y(i) + wi * n_z(i)
169 ui = ui - normu * n_x(i)
170 vi = vi - normu * n_y(i)
171 wi = wi - normu * n_z(i)
172
173 ! Compute velocity magnitude
174 magu = sqrt(ui**2 + vi**2 + wi**2)
175 magu = max(magu, 1.0e-6_rp)
176 utau = magu*kappa / log(hi/z0)
177
178 ! Compute thermal roughness length from Zilitinkevich, 1995
179 if (z0h_in < 0) then
180 ! z0h_in is interpreted as -C_Zil (Zilitinkevich constant) for z0h
181 z0h = z0 * exp(z0h_in*sqrt((utau*z0)/(mu/rho)))
182 else
183 z0h = z0h_in
184 end if
185
186 ! Get q, ts based on bc_type
187 ! Maybe redundant, but needed to initialise Rib
188 call assign_bc_value(bc_type,bc_value,q,ts,ti,kappa,utau,z0h,hi)
189
190 ! Compute g along the normal (generalisation for hills and similar)
191 g_dot_n = abs(g_vec(1)*n_x(i) + g_vec(2)*n_y(i) + g_vec(3)*n_z(i))
192
193 ! Compute Richardson and set stability accordingly
194 call compute_ri_b(bc_type, g_dot_n, hi, ti, ts, magu, kappa, q, ri_b)
195 call set_stability_regime(ri_b, ri_threshold)
196
197 ! Set length scale
198 l = kappa * hi
199 ! Compute u*
200 utau = sqrt(tau_ptr(magu, ri_b, hi, z0, l, kappa))
201 select case (bc_type)
202 case ("neumann")
203 !!! TEMPORARY: neutral log-law approximation
204 ts = ti - (q * pr * log(hi/z0h)) / (max(utau, 1e-6_rp) * kappa)
205 q = q
206 case ("dirichlet")
207 ! Compute q
208 q = heat_flux_ptr(ti, ts, ri_b, hi, magu, z0h, pr, l, utau, kappa)
209 case default
210 call neko_error("Invalid specified temperature b.c. type " // &
211 "('neumann' or 'dirichlet'?)")
212 end select
213
214 ! Distribute according to the velocity vector and bound magu
215 ! to avoid 0 division
216 magu = max(magu, 1.0e-6_rp)
217 tau_x(i) = -rho*utau**2 * ui / magu
218 tau_y(i) = -rho*utau**2 * vi / magu
219 tau_z(i) = -rho*utau**2 * wi / magu
220 if (abs(ri_b) <= ri_threshold) then
221 ! Neutral (L_ob undefined)
222 l_ob = 1e10_rp
223 else
224 l_ob = -(ts*utau**3)/(kappa*g_dot_n*q)
225 end if
226
227 ri_b_diagn(i) = ri_b
228 l_ob_diagn(i) = l_ob
229 utau_diagn(i) = utau
230 magu_diagn(i) = magu
231 ti_diagn(i) = ti
232 ts_diagn(i) = temp_w(i)
233 q_diagn(i) = q
234 end do
235 !$omp end parallel do
236
237 end subroutine richardson_compute_cpu
238
241 function tau_stable(magu, Ri_b, h, z0, l, kappa) result(tau)
242 real(kind=rp), intent(in) :: magu, ri_b, h, z0, l, kappa
243 real(kind=rp) :: tau
244
245 tau = magu**2/(log(h/z0)**2) * f_tau_stable(ri_b)/ &
246 f_tau_stable(0.0_rp) * (l/h)**2
247 end function tau_stable
248
249 function heat_flux_stable(ti, ts, Ri_b, h, magu, z0h, Pr,&
250 l, utau, kappa) result(heat_flux)
251 real(kind=rp), intent(in) :: ts, ti, ri_b, h, magu
252 real(kind=rp), intent(in) :: z0h, pr, l, utau, kappa
253 real(kind=rp) :: heat_flux
254
255 heat_flux = (ti - ts)/(log(h/z0h)) * &
256 f_theta_stable(ri_b)/abs(f_theta_stable(0.0_rp)) * &
257 (l/h) * utau/pr
258 end function heat_flux_stable
259
260 function f_tau_stable(Ri_b) result(f_tau)
261 real(kind=rp), intent(in) :: ri_b
262 real(kind=rp) :: f_tau
263
264 f_tau = 0.17 * (0.25 + 0.75 / (1.0 + 4.0*ri_b))
265 end function f_tau_stable
266
267 function f_theta_stable(Ri_b) result(f_theta)
268 real(kind=rp), intent(in) :: ri_b
269 real(kind=rp) :: f_theta
270
271 f_theta = -0.145 / (1.0 + 4.0 * ri_b)
272 end function f_theta_stable
273
276 function tau_convective(magu, Ri_b, h, z0, l, kappa) result(tau)
277 real(kind=rp), intent(in) :: magu, ri_b, h, z0, l, kappa
278 real(kind=rp) :: tau
279 real(kind=rp) :: a, b, c
280
281 a = kappa / log(h/z0)
282 b = 2.0
283 c = 7.4 * a**2 * b * (h/z0)**0.5
284
285 tau = a**2 * magu**2 * f_tau_convective(ri_b, c)
286 end function tau_convective
287
288 function heat_flux_convective(ti, ts, Ri_b, h, magu, z0h, Pr,&
289 l, utau, kappa) result(heat_flux)
290 real(kind=rp), intent(in) :: ts, ti, ri_b, h, magu
291 real(kind=rp), intent(in) :: z0h, pr, l, utau, kappa
292 real(kind=rp) :: heat_flux
293 real(kind=rp) :: a, b, c
294
295 a = kappa / log(h/z0h)
296 b = 2.0
297 c = 5.3 * a**2 * b * (h/z0h)**0.5
298
299 heat_flux = - a**2 / 0.74 * magu * &
300 (ti - ts) * f_theta_convective(ri_b, c)
301
302 end function heat_flux_convective
303
304 function f_tau_convective(Ri_b, c) result(f_tau)
305 real(kind=rp), intent(in) :: ri_b, c
306 real(kind=rp) :: f_tau
307
308 f_tau = 1.0 - 2*ri_b / (1.0 + c * abs(ri_b)**0.5)
309 end function f_tau_convective
310
311 function f_theta_convective(Ri_b, c) result(f_theta)
312 real(kind=rp), intent(in) :: ri_b, c
313 real(kind=rp) :: f_theta
314
315 f_theta = 1.0 - 2*ri_b / (1.0 + c * abs(ri_b)**0.5)
316 end function f_theta_convective
317
319 function tau_neutral(magu, Ri_b, h, z0, l, kappa) result(tau)
320 real(kind=rp), intent(in) :: magu, ri_b, h, z0, l, kappa
321 real(kind=rp) :: tau
322
323 tau = (kappa*magu/log(h/z0))**2
324 end function tau_neutral
325
326 function heat_flux_neutral(ti, ts, Ri_b, h, magu, z0h, Pr,&
327 l, utau, kappa) result(heat_flux)
328 real(kind=rp), intent(in) :: ts, ti, ri_b, h, magu
329 real(kind=rp), intent(in) :: z0h, pr, l, utau, kappa
330 real(kind=rp) :: heat_flux
331
332 heat_flux = kappa*utau * (ti - ts)/log(h/z0h)
333 end function heat_flux_neutral
334
335end 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:632
real(kind=rp) function, public glmax(a, n)
Max of a vector of length n.
Definition math.f90:653
real(kind=rp) function, public glmin(a, n)
Min of a vector of length n.
Definition math.f90:691
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
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)
subroutine, public richardson_compute_cpu(u, v, w, temp, temp_w, n_x, n_y, n_z, h, tau_x, tau_y, tau_z, n_nodes, 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)
Main routine to compute the surface stresses based on richardson.
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)
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