feat(edmfx): add horizontal component to the EDMFX SGS diffusive flux#4657
feat(edmfx): add horizontal component to the EDMFX SGS diffusive flux#4657haakon-e wants to merge 1 commit into
Conversation
|
📖 Docs preview for this PR: https://clima.github.io/ClimaAtmos.jl/previews/PR4657/ |
90631b0 to
da92061
Compare
|
This change is part of the following stack: Change managed by git-spice. |
05ec4d9 to
e5787ab
Compare
ee9a351 to
ded13f0
Compare
231327f to
e9244b3
Compare
|
|
||
| - `(; ᶜK_u, ᶜK_h)`. | ||
| """ | ||
| function ᶜeddy_diffusivities!( |
There was a problem hiding this comment.
The center-K consolidation looks like it collides with #4647, which moved everything to face-native set_face_diffusivities! (ᶠK_h/ᶠK_u/ᶠK_entr). (
There was a problem hiding this comment.
The face-native vertical pipeline from #4647 (set_face_diffusivities!, ᶠK_h/ᶠK_u/ᶠK_entr) is untouched, and the horizontal path gets its own set_horizontal_diffusivities!(Y, p) that caches center-based ᶜK_u_h/ᶜK_h_h once per explicit cache update.
These coefficients are center-fields because the horizontal operators themselves (gradₕ, wdivₕ) act at cell centers. the vertical fluxes stay on faces. Both are built from the same closure functions and stability inputs, so there's no duplicated physics.
| @inline get_mixing_length_field(ml::MixingLength, ::Val{:l_grid}) = ml.l_grid | ||
|
|
||
| function ᶜmixing_length(Y, p, property::Val{P} = Val{:master}()) where {P} | ||
| function ᶜmixing_length( |
There was a problem hiding this comment.
Both this PR and #4647 edit ᶜmixing_length, but differently. Here you add grid_scale / buoyancy_gradient so the horizontal path can pass Δx and a centered buoyancy gradient. On main, it's written around resolvability_filter_scale and a split ᶜN²_eff / ᶜbuoygrad without keywards kwargs. So this can be consolidated
There was a problem hiding this comment.
I think this is resolved now. I kept a single grid_scale kwarg, but it now defaults to resolvability_filter_scale(axes(Y.c)), so the default path is exactly the formulation on main (there's a unit test asserting the identity). The horizontal path just passes the horizontal grid spacing in set_horizontal_diffusivities!(Y, p):
ᶜl_h = ᶜmixing_length(Y, p; grid_scale = Δx_h)The buoyancy_gradient kwarg is dropped, so the horizontal path reads the same ᶜN²_eff and strain rate from the cache as the vertical closure.
| return nothing | ||
| iscolumn(axes(Y.c)) && return nothing | ||
| (; params) = p | ||
| (; ᶜlinear_buoygrad) = p.precomputed |
There was a problem hiding this comment.
ᶜlinear_buoygrad got renamed to ᶜbuoygrad
| # Mixing length limited by the horizontal node spacing, with the centered | ||
| # buoyancy gradient as the stability input | ||
| Δx = Spaces.node_horizontal_length_scale(Spaces.horizontal_space(axes(Y.c))) | ||
| ᶜK = ᶜeddy_diffusivities!( |
There was a problem hiding this comment.
The vertical path adds ᶠK_entr into all its diffusivities now, the horizontal terms should likely not include it after rebasing .
| # buoyancy gradient as the stability input | ||
| Δx = Spaces.node_horizontal_length_scale(Spaces.horizontal_space(axes(Y.c))) | ||
| ᶜK = ᶜeddy_diffusivities!( | ||
| Y, p; |
There was a problem hiding this comment.
We recompute the horizontal diffusivities here + in the updraft tendency + a few times in these diagnostics. Since main precomputes face K's anyway, could we precompute ᶜK_u_h/ᶜK_h_h once?
173f886 to
ec636d3
Compare
ec636d3 to
f3aca88
Compare
2967b19 to
077de51
Compare
159e1aa to
0d46663
Compare
c39237d to
e9c164e
Compare
87b971a to
2141622
Compare
e9c164e to
4bfcd52
Compare
2141622 to
5e0c98b
Compare
4bfcd52 to
f5f88e2
Compare
|
This is implemented. The grid-mean horizontal specific tendencies are applied uniformly to the updraft scalars, behind the |
5e0c98b to
cd56d43
Compare
844918e to
5f7ca9d
Compare
626a24c to
0decd0d
Compare
86f1665 to
03fe0c8
Compare
haakon-e
left a comment
There was a problem hiding this comment.
a few comments to assist review
| @inline get_mixing_length_field(ml::MixingLength, ::Val{:l_grid}) = ml.l_grid | ||
|
|
||
| function ᶜmixing_length(Y, p, property::Val{P} = Val{:master}()) where {P} | ||
| function ᶜmixing_length( |
There was a problem hiding this comment.
I think this is resolved now. I kept a single grid_scale kwarg, but it now defaults to resolvability_filter_scale(axes(Y.c)), so the default path is exactly the formulation on main (there's a unit test asserting the identity). The horizontal path just passes the horizontal grid spacing in set_horizontal_diffusivities!(Y, p):
ᶜl_h = ᶜmixing_length(Y, p; grid_scale = Δx_h)The buoyancy_gradient kwarg is dropped, so the horizontal path reads the same ᶜN²_eff and strain rate from the cache as the vertical closure.
|
|
||
| - `(; ᶜK_u, ᶜK_h)`. | ||
| """ | ||
| function ᶜeddy_diffusivities!( |
There was a problem hiding this comment.
The face-native vertical pipeline from #4647 (set_face_diffusivities!, ᶠK_h/ᶠK_u/ᶠK_entr) is untouched, and the horizontal path gets its own set_horizontal_diffusivities!(Y, p) that caches center-based ᶜK_u_h/ᶜK_h_h once per explicit cache update.
These coefficients are center-fields because the horizontal operators themselves (gradₕ, wdivₕ) act at cell centers. the vertical fluxes stay on faces. Both are built from the same closure functions and stability inputs, so there's no duplicated physics.
| @. Yₜ.c.uₕ -= C12(wdivₕ(ᶜρ * ᶜτ_h) / ᶜρ) | ||
| ᶠτ_h = compute_strain_rate_face_full!(p.scratch.ᶠtemp_UVWxUVW, ᶜu, ᶠu) | ||
| @. ᶠτ_h = -2 * ᶠinterp(ᶜK_u_h) * ᶠτ_h | ||
| @. Yₜ.f.u₃ -= C3(wdivₕ(ᶠρ * ᶠτ_h) / ᶠρ) |
There was a problem hiding this comment.
I include u₃ here because the horizontal flux of vertical momentum u'w' is a covariance. The vertical counterpart w'w' is a variance and is already in TKE / mass flux / pressure, which is why the vertical pathway doesn't diffuse u₃. Some additional explanation in the new docs page.
| ice. The gradient is accumulated in two broadcasts; see the horizontal EDMF | ||
| diffusion documentation page. | ||
| """ | ||
| function ᶜtotal_enthalpy_gradientₕ!( |
There was a problem hiding this comment.
This mirrors ᶠtotal_enthalpy_gradientᵥ, but I materialize into scratch in two broadcasts. A single fused broadcast with four spectral gradients exceeds GPU kernel parameter limits on extruded spaces with warped topography (the lazy form also needs CliMA/ClimaCore.jl#2551). There's a regression test that the ρe_tot tendency equals the sum of the four constituent fluxes.
| `ᶜK_h_h` of the TKE-based closure, with the mixing length limited by the | ||
| horizontal node spacing, `l_h = min(l_phys, Δx_h)`. | ||
| """ | ||
| function set_horizontal_diffusivities!(Y, p) |
There was a problem hiding this comment.
I don't include ᶠK_entr here, since my understanding is that it parameterizes vertical entrainment across an unresolved inversion face (K_e = γ w_e Δz) and has no horizontal analogue.
03fe0c8 to
5ae7b0d
Compare
5ae7b0d to
ecf3379
Compare
Add the horizontal counterpart of the EDMFX environment SGS diffusive flux, enabled by the opt-in `edmfx_sgs_horizontal_diffusive_flux` option: scalar fluxes (total enthalpy via the dry-static-energy + water-enthalpy decomposition, specific humidity with its moist-air mass counterpart, and grid-scale tracers with the sedimentation α weighting), turbulent TKE transport with the horizontal shear production, and the momentum stress τ = -2 K_u S with the full strain rate. The eddy diffusivity uses the TKE-based closure with the mixing length limited by the horizontal node spacing, cached in ᶜK_u_h/ᶜK_h_h with diagnostics lmixh, edth, and evuh. With prognostic updrafts, the separate opt-in `edmfx_horizontal_diffusion` option applies the grid-mean horizontal specific tendencies uniformly to the updraft scalars (mse, q_tot with the ρa dry-mass correction, and SGS tracers). The enthalpy gradient is materialized by ᶜtotal_enthalpy_gradientₕ! into scratch before the weak divergence, since a single fused broadcast with four spectral gradients exceeds GPU kernel parameter limits on topography-warped extruded spaces.
ecf3379 to
fa5abe6
Compare
Add the horizontal component of the previously vertical-only EDMFX environment SGS diffusive flux, enabled by the opt-in option
edmfx_sgs_horizontal_diffusive_flux(defaultfalse). It is intended for high-resolution configurations (the gray-zone to LES regime), where the horizontal grid spacing is comparable to or smaller than the environment mixing length; on single columns the tendency is identically zero, so column results are unchanged.The horizontal fluxes mirror the vertical EDMFX diffusive flux term by term: the same variables (total enthalpy with the dry-static-energy + water-enthalpy decomposition, total specific humidity with its moist-air-mass counterpart, grid-mean tracers with the
α_vert_diff_tracerweighting, and TKE), the same TKE-based eddy diffusivity, and horizontal rather than vertical operators. The momentum tendency is the horizontal weak divergence of the SGS stressτ = -2 K_u Swith the full strain rate, applied to the horizontal and vertical wind, with the corresponding TKE shear production from horizontal gradients. Anisotropy enters only through the mixing-length grid-scale limiter:ᶜmixing_lengthgains agrid_scalekeyword defaulting to the resolvability filter scale, and the horizontal path passes the horizontal node scale, givingl_h = min(l_phys, Δx_h). The diffusivities are cached once per explicit cache update (ᶜK_u_h/ᶜK_h_h) and exposed by the new diagnosticslmixh,edth, andevuh. All horizontal terms are always explicit and never enter the implicit Jacobian.With prognostic updrafts, the separate opt-in
edmfx_horizontal_diffusion(defaultfalse, requires the flux option) applies the grid-mean horizontal specific tendencies uniformly to the updraft scalars, matching the uniform vertical treatment ofedmfx_vertical_diffusion(#4720).Combining the flux option with Smagorinsky-Lilly or AMD, which already apply horizontal SGS diffusion to the same fields, is rejected at model construction. The closure reuses the vertical flux's stability inputs (
ᶜN²_eff, vertical shear); horizontal-specific stability formulations are out of scope. The formulation and its limits are documented in the newdocs/src/edmf_horizontal_diffusion.md. Neither flag is set in any bundled configuration, and thegrid_scalegeneralization does not change results on the default path, which the EDMF reproducibility jobs verify.Unit tests cover the null states (horizontally uniform, single column), reference-based checks of each flux term, the updraft identities, the solver wiring, and the construction-time rejections.