Skip to content

Commit 4ab368f

Browse files
committed
feat(edmfx): add the momentum component of the horizontal SGS diffusive flux
Apply the horizontal weak divergence of the SGS stress `τ = -2 K_u S`, with the full three-dimensional strain rate of the grid-mean velocity, to the grid-mean momentum: on cell centers for the horizontal wind and on cell faces for the vertical wind, mirroring the Smagorinsky-Lilly stress split by divergence direction. The eddy viscosity is the horizontal `K_u` from the TKE-based closure with the mixing length limited by the horizontal node spacing. Enabled under the existing `edmfx_sgs_horizontal_diffusive_flux` option; the vertical stress divergence remains with the vertical diffusion pathway. The shear production of TKE from horizontal gradients is not included here. Extend the tests: horizontally uniform velocity produces no momentum tendency, perturbed velocity produces one, and the single-column tendency is identically zero.
1 parent da92061 commit 4ab368f

4 files changed

Lines changed: 50 additions & 5 deletions

File tree

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ ClimaAtmos.jl Release Notes
44
main
55
----
66

7+
- ![][badge-✨feature/enhancement] Add the momentum component of the horizontal
8+
EDMFX SGS diffusive flux: the horizontal weak divergence of the stress
9+
`τ = -2 K_u S` with the full strain rate, applied under the same
10+
`edmfx_sgs_horizontal_diffusive_flux` option.
711
- [#4657](https://github.com/CliMA/ClimaAtmos.jl/pull/4657) ![][badge-✨feature/enhancement]
812
Add a horizontal component to the EDMFX SGS diffusive flux, enabled by the opt-in
913
`edmfx_sgs_horizontal_diffusive_flux` config option (default `false`), for

docs/src/edmf_horizontal_diffusion.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,22 @@ tracers — i.e. the same set the vertical flux diffuses. The microphysics and p
3535
tracers are scaled by the tracer diffusion factor ``\alpha`` (`α_vert_diff_tracer`,
3636
shared with the vertical flux). The total-specific-humidity flux additionally enters the
3737
air-mass tendency ``\partial_t \rho``; condensate and precipitation fluxes do not.
38-
Horizontal momentum diffusion is not included.
38+
39+
The momentum tendency is the horizontal weak divergence of the subgrid-scale
40+
stress ``\tau = -2 K_u \mathcal{S}``, with ``\mathcal{S}`` the full
41+
three-dimensional strain rate of the grid-mean velocity,
42+
43+
```math
44+
\partial_t u_h \mathrel{-}= \frac{1}{\rho} \nabla_h \cdot (\rho \, \tau),
45+
\qquad
46+
\partial_t u_3 \mathrel{-}= \frac{1}{\rho} \nabla_h \cdot (\rho \, \tau),
47+
```
48+
49+
evaluated on cell centers for the horizontal wind and on cell faces for the
50+
vertical wind, mirroring the Smagorinsky-Lilly stress split by divergence
51+
direction; the vertical stress divergence is handled by the vertical diffusion
52+
pathway. The shear production of TKE from horizontal gradients is not
53+
included.
3954

4055
The horizontal flux mirrors the vertical EDMFX diffusive flux: the same variables, the
4156
same tracer set, and the same scaling, with horizontal rather than vertical operators.

src/prognostic_equations/edmfx_sgs_flux.jl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,8 @@ function edmfx_sgs_horizontal_diffusive_flux_tendency!(
359359
buoyancy_gradient = ᶜlinear_buoygrad,
360360
)
361361

362-
ᶜK_u_h =
363-
@. lazy(eddy_viscosity(turbconv_params, ᶜtke, ᶜmixing_length_h))
362+
ᶜK_u_h = p.scratch.ᶜtemp_scalar_4
363+
@. ᶜK_u_h = eddy_viscosity(turbconv_params, ᶜtke, ᶜmixing_length_h)
364364
ᶜprandtl_nvec = @. lazy(
365365
turbulent_prandtl_number(params, ᶜlinear_buoygrad, ᶜstrain_rate_norm),
366366
)
@@ -395,5 +395,17 @@ function edmfx_sgs_horizontal_diffusive_flux_tendency!(
395395
@. Yₜ.c.ρtke += wdivₕ(ᶜρ * ᶜK_u_h * gradₕ(ᶜtke))
396396
end
397397

398+
# Momentum: horizontal weak divergence of the SGS stress `τ = -2 K_u S`
399+
# with the full strain rate. The vertical stress divergence is handled by
400+
# the vertical diffusion pathway.
401+
(; ᶜu, ᶠu) = p.precomputed
402+
ᶠρ = @. p.scratch.ᶠtemp_scalar = ᶠinterp(ᶜρ)
403+
ᶜτ_h = compute_strain_rate_center_full!(p.scratch.ᶜtemp_UVWxUVW, ᶜu, ᶠu)
404+
@. ᶜτ_h = -2 * ᶜK_u_h * ᶜτ_h
405+
@. Yₜ.c.uₕ -= C12(wdivₕ(ᶜρ * ᶜτ_h) / ᶜρ)
406+
ᶠτ_h = compute_strain_rate_face_full!(p.scratch.ᶠtemp_UVWxUVW, ᶜu, ᶠu)
407+
@. ᶠτ_h = -2 * ᶠinterp(ᶜK_u_h) * ᶠτ_h
408+
@. Yₜ.f.u₃ -= C3(wdivₕ(ᶠρ * ᶠτ_h) / ᶠρ)
409+
398410
return nothing
399411
end

test/prognostic_equations/edmfx_horizontal_diffusion_tests.jl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,21 @@ box_config_dict(; extra...) = bomex_edmfx_config_dict(;
7474
uniform_max = Dict(
7575
name => maximum(abs, parent(getproperty(Yₜ.c, name))) for name in rows
7676
)
77+
uniform_max_uₕ = maximum(abs, parent(Yₜ.c.uₕ))
78+
uniform_max_u₃ = maximum(abs, parent(Yₜ.f.u₃))
7779

7880
# Perturb the state horizontally to activate every flux pathway. The
79-
# enthalpy flux reads the precomputed `ᶜh_tot`, which is perturbed
80-
# directly.
81+
# enthalpy flux reads the precomputed `ᶜh_tot` and the momentum stress
82+
# reads the precomputed `ᶜu`/`ᶠu`, which are perturbed directly.
8183
ᶜx = Fields.coordinate_field(Y.c).x
84+
ᶠx = Fields.coordinate_field(Y.f).x
8285
ᶜpert = @. 1 + FT(0.1) * sin(FT(2π) * ᶜx / FT(6400))
8386
@. Y.c.ρq_tot *= ᶜpert
8487
@. Y.c.ρq_rai = FT(1e-5) * Y.c.ρ * ᶜpert
8588
@. Y.c.ρtke *= ᶜpert
8689
@. p.precomputed.ᶜh_tot *= 1 + FT(1e-3) * sin(FT(2π) * ᶜx / FT(6400))
90+
@. p.precomputed.ᶜu *= ᶜpert
91+
@. p.precomputed.ᶠu *= 1 + FT(0.1) * sin(FT(2π) * ᶠx / FT(6400))
8792

8893
Yₜ .= zero(eltype(Yₜ))
8994
CA.edmfx_sgs_horizontal_diffusive_flux_tendency!(
@@ -93,11 +98,16 @@ box_config_dict(; extra...) = bomex_edmfx_config_dict(;
9398
name => maximum(abs, parent(getproperty(Yₜ.c, name))) for name in rows
9499
)
95100

101+
perturbed_max_uₕ = maximum(abs, parent(Yₜ.c.uₕ))
102+
perturbed_max_u₃ = maximum(abs, parent(Yₜ.f.u₃))
103+
96104
# Non-vacuous fluxes for every perturbed pathway.
97105
@test perturbed_max[:ρq_tot] > 0
98106
@test perturbed_max[:ρe_tot] > 0
99107
@test perturbed_max[:ρq_rai] > 0
100108
@test perturbed_max[:ρtke] > 0
109+
@test perturbed_max_uₕ > 0
110+
@test perturbed_max_u₃ > 0
101111

102112
# The moist air mass tendency matches the ρq_tot tendency bitwise.
103113
@test parent(Yₜ.c.ρ) == parent(Yₜ.c.ρq_tot)
@@ -108,6 +118,8 @@ box_config_dict(; extra...) = bomex_edmfx_config_dict(;
108118
reference = max(perturbed_max[name], perturbed_max[:ρq_tot])
109119
@test uniform_max[name] <= FT(1e-10) * reference
110120
end
121+
@test uniform_max_uₕ <= FT(1e-10) * perturbed_max_uₕ
122+
@test uniform_max_u₃ <= FT(1e-10) * perturbed_max_u₃
111123
# Identically zero tracers stay identically zero.
112124
for name in (:ρq_lcl, :ρq_icl, :ρq_sno)
113125
@test uniform_max[name] == 0
@@ -165,6 +177,8 @@ end
165177
for name in (, :ρe_tot, :ρq_tot, :ρq_lcl, :ρq_icl, :ρq_rai, :ρq_sno, :ρtke)
166178
@test maximum(abs, parent(getproperty(Yₜ.c, name))) == 0
167179
end
180+
@test maximum(abs, parent(Yₜ.c.uₕ)) == 0
181+
@test maximum(abs, parent(Yₜ.f.u₃)) == 0
168182
end
169183

170184
@testset "Incompatible horizontal SGS closures are rejected" begin

0 commit comments

Comments
 (0)