Skip to content

Commit 3e347c0

Browse files
authored
Merge pull request #4085 from CliMA/sa/add_implicit_sgs_tracers_massflux
add implicit sgs mass flux for tracers
2 parents fef1fba + a908a7f commit 3e347c0

File tree

4 files changed

+136
-11
lines changed

4 files changed

+136
-11
lines changed

config/model_configs/prognostic_edmfx_rico_implicit_column.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ implicit_sgs_advection: true
99
implicit_sgs_entr_detr: true
1010
implicit_nh_pressure: true
1111
implicit_sgs_vertdiff: true
12+
implicit_sgs_mass_flux: true
1213
approximate_linear_solve_iters: 2
1314
max_newton_iters_ode: 3
1415
edmfx_entr_model: "Generalized"
@@ -30,7 +31,7 @@ y_elem: 2
3031
z_elem: 100
3132
z_stretch: false
3233
perturb_initstate: false
33-
dt: "50secs"
34+
dt: "100secs"
3435
t_end: "24hours"
3536
dt_save_state_to_disk: "60mins"
3637
toml: [toml/prognostic_edmfx_1M.toml]

config/model_configs/prognostic_edmfx_trmm_implicit_column.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ implicit_sgs_advection: true
99
implicit_sgs_entr_detr: true
1010
implicit_nh_pressure: true
1111
implicit_sgs_vertdiff: true
12+
implicit_sgs_mass_flux: true
1213
approximate_linear_solve_iters: 2
1314
max_newton_iters_ode: 3
15+
debug_jacobian: true
1416
edmfx_entr_model: "Generalized"
1517
edmfx_detr_model: "Generalized"
1618
edmfx_sgs_mass_flux: true

src/prognostic_equations/implicit/manual_sparse_jacobian.jl

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -267,18 +267,30 @@ function jacobian_cache(alg::ManualSparseJacobian, Y, atmos)
267267
@assert n_prognostic_mass_flux_subdomains(atmos.turbconv_model) == 1
268268
if use_derivative(sgs_mass_flux_flag)
269269
(
270+
MatrixFields.unrolled_map(
271+
name ->
272+
(name, get_χʲ_name_from_ρχ_name(name)) =>
273+
similar(Y.c, TridiagonalRow),
274+
available_tracer_names,
275+
)...,
276+
MatrixFields.unrolled_map(
277+
name ->
278+
(name, @name(c.sgsʲs.:(1).ρa)) =>
279+
similar(Y.c, TridiagonalRow),
280+
available_tracer_names,
281+
)...,
282+
MatrixFields.unrolled_map(
283+
name ->
284+
(name, @name(f.sgsʲs.:(1).u₃)) =>
285+
similar(Y.c, BidiagonalRow_ACT3),
286+
available_tracer_names,
287+
)...,
270288
(@name(c.ρe_tot), @name(c.sgsʲs.:(1).mse)) =>
271289
similar(Y.c, TridiagonalRow),
272-
(@name(c.ρq_tot), @name(c.sgsʲs.:(1).q_tot)) =>
273-
similar(Y.c, TridiagonalRow),
274290
(@name(c.ρe_tot), @name(f.sgsʲs.:(1).u₃)) =>
275291
similar(Y.c, BidiagonalRow_ACT3),
276-
(@name(c.ρq_tot), @name(f.sgsʲs.:(1).u₃)) =>
277-
similar(Y.c, BidiagonalRow_ACT3),
278292
(@name(c.ρe_tot), @name(c.sgsʲs.:(1).ρa)) =>
279293
similar(Y.c, TridiagonalRow),
280-
(@name(c.ρq_tot), @name(c.sgsʲs.:(1).ρa)) =>
281-
similar(Y.c, TridiagonalRow),
282294
)
283295
else
284296
()
@@ -742,6 +754,8 @@ function update_jacobian!(alg::ManualSparseJacobian, cache, Y, p, dtγ, t)
742754
if use_derivative(sgs_advection_flag)
743755
(; ᶜgradᵥ_ᶠΦ) = p.core
744756
(; ᶜρʲs, ᶠu³ʲs, ᶜtsʲs, ᶜKʲs, bdmr_l, bdmr_r, bdmr) = p.precomputed
757+
758+
# upwinding options for q_tot and mse
745759
is_third_order =
746760
p.atmos.numerics.edmfx_mse_q_tot_upwinding == Val(:third_order)
747761
ᶠupwind = is_third_order ? ᶠupwind3 : ᶠupwind1
@@ -757,6 +771,24 @@ function update_jacobian!(alg::ManualSparseJacobian, cache, Y, p, dtγ, t)
757771
bottom = Operators.SetValue(zero(UpwindMatrixRowType{CT3{FT}})),
758772
) # Need to wrap ᶠupwind_matrix in this for well-defined boundaries.
759773

774+
# upwinding options for other tracers
775+
is_tracer_upwinding_third_order =
776+
p.atmos.numerics.edmfx_tracer_upwinding == Val(:third_order)
777+
ᶠtracer_upwind = is_tracer_upwinding_third_order ? ᶠupwind3 : ᶠupwind1
778+
ᶠset_tracer_upwind_bcs = Operators.SetBoundaryOperator(;
779+
top = Operators.SetValue(zero(CT3{FT})),
780+
bottom = Operators.SetValue(zero(CT3{FT})),
781+
) # Need to wrap ᶠtracer_upwind in this for well-defined boundaries.
782+
TracerUpwindMatrixRowType =
783+
is_tracer_upwinding_third_order ? QuaddiagonalMatrixRow :
784+
BidiagonalMatrixRow
785+
ᶠtracer_upwind_matrix =
786+
is_tracer_upwinding_third_order ? ᶠupwind3_matrix : ᶠupwind1_matrix
787+
ᶠset_tracer_upwind_matrix_bcs = Operators.SetBoundaryOperator(;
788+
top = Operators.SetValue(zero(TracerUpwindMatrixRowType{CT3{FT}})),
789+
bottom = Operators.SetValue(zero(TracerUpwindMatrixRowType{CT3{FT}})),
790+
) # Need to wrap ᶠtracer_upwind_matrix in this for well-defined boundaries.
791+
760792
ᶠu³ʲ_data = ᶠu³ʲs.:(1).components.data.:1
761793

762794
ᶜkappa_mʲ = p.scratch.ᶜtemp_scalar
@@ -797,6 +829,7 @@ function update_jacobian!(alg::ManualSparseJacobian, cache, Y, p, dtγ, t)
797829
p.atmos.microphysics_model isa Microphysics1Moment ||
798830
p.atmos.microphysics_model isa Microphysics2Moment
799831
)
832+
800833
ᶜa = (@. lazy(draft_area(Y.c.sgsʲs.:(1).ρa, ᶜρʲs.:(1))))
801834
ᶜ∂a∂z =
802835
@. lazy(
@@ -832,15 +865,17 @@ function update_jacobian!(alg::ManualSparseJacobian, cache, Y, p, dtγ, t)
832865
dtγ * (
833866
DiagonalMatrixRow(ᶜadvdivᵥ(ᶠu³ʲs.:(1))) -
834867
ᶜadvdivᵥ_matrix()
835-
ᶠset_upwind_matrix_bcs(ᶠupwind_matrix(ᶠu³ʲs.:(1)))
868+
ᶠset_tracer_upwind_matrix_bcs(
869+
ᶠtracer_upwind_matrix(ᶠu³ʲs.:(1)),
870+
)
836871
) - (I,)
837872
∂ᶜχʲ_err_∂ᶠu₃ʲ =
838873
matrix[χʲ_name, @name(f.sgsʲs.:(1).u₃)]
839874
@. ∂ᶜχʲ_err_∂ᶠu₃ʲ =
840875
dtγ * (
841876
-(ᶜadvdivᵥ_matrix()) DiagonalMatrixRow(
842-
ᶠset_upwind_bcs(
843-
ᶠupwind(CT3(sign(ᶠu³ʲ_data)), ᶜχʲ),
877+
ᶠset_tracer_upwind_bcs(
878+
ᶠtracer_upwind(CT3(sign(ᶠu³ʲ_data)), ᶜχʲ),
844879
) * adjoint(C3(sign(ᶠu³ʲ_data))),
845880
) +
846881
DiagonalMatrixRow(ᶜχʲ) ᶜadvdivᵥ_matrix()
@@ -1267,6 +1302,64 @@ function update_jacobian!(alg::ManualSparseJacobian, cache, Y, p, dtγ, t)
12671302
(ᶠu³ʲs.:(1) - ᶠu³) *
12681303
ᶠinterp((Y.c.sgsʲs.:(1).q_tot - ᶜq_tot)) / ᶠJ,
12691304
) ᶠinterp_matrix() DiagonalMatrixRow(ᶜJ)
1305+
1306+
# grid-mean tracers
1307+
if p.atmos.moisture_model isa NonEquilMoistModel && (
1308+
p.atmos.microphysics_model isa Microphysics1Moment ||
1309+
p.atmos.microphysics_model isa Microphysics2Moment
1310+
)
1311+
1312+
microphysics_tracers = (
1313+
(@name(c.ρq_liq), @name(c.sgsʲs.:(1).q_liq)),
1314+
(@name(c.ρq_ice), @name(c.sgsʲs.:(1).q_ice)),
1315+
(@name(c.ρq_rai), @name(c.sgsʲs.:(1).q_rai)),
1316+
(@name(c.ρq_sno), @name(c.sgsʲs.:(1).q_sno)),
1317+
(@name(c.ρn_liq), @name(c.sgsʲs.:(1).n_liq)),
1318+
(@name(c.ρn_rai), @name(c.sgsʲs.:(1).n_rai)),
1319+
)
1320+
MatrixFields.unrolled_foreach(
1321+
microphysics_tracers,
1322+
) do (ρχ_name, χʲ_name)
1323+
MatrixFields.has_field(Y, ρχ_name) || return
1324+
ᶜχʲ = MatrixFields.get_field(Y, χʲ_name)
1325+
1326+
∂ᶜρχ_err_∂ᶜχʲ =
1327+
matrix[ρχ_name, χʲ_name]
1328+
@. ∂ᶜρχ_err_∂ᶜχʲ =
1329+
dtγ *
1330+
-(ᶜadvdivᵥ_matrix())
1331+
DiagonalMatrixRow(ᶠinterp(ᶜρʲs.:(1) * ᶜJ) / ᶠJ)
1332+
ᶠset_tracer_upwind_matrix_bcs(
1333+
ᶠtracer_upwind_matrix(ᶠu³ʲs.:(1)),
1334+
)
1335+
DiagonalMatrixRow(draft_area(Y.c.sgsʲs.:(1).ρa, ᶜρʲs.:(1)))
1336+
1337+
∂ᶜρχ_err_∂ᶜρa =
1338+
matrix[ρχ_name, @name(c.sgsʲs.:(1).ρa)]
1339+
@. ∂ᶜρχ_err_∂ᶜρa =
1340+
dtγ *
1341+
-(ᶜadvdivᵥ_matrix())
1342+
DiagonalMatrixRow(ᶠinterp(ᶜρʲs.:(1) * ᶜJ) / ᶠJ)
1343+
ᶠset_tracer_upwind_matrix_bcs(
1344+
ᶠtracer_upwind_matrix(ᶠu³ʲs.:(1)),
1345+
)
1346+
DiagonalMatrixRow(ᶜχʲ / ᶜρʲs.:(1))
1347+
1348+
∂ᶜρχ_err_∂ᶠu₃ʲ =
1349+
matrix[ρχ_name, @name(f.sgsʲs.:(1).u₃)]
1350+
@. ∂ᶜρχ_err_∂ᶠu₃ʲ =
1351+
dtγ * (
1352+
-(ᶜadvdivᵥ_matrix()) DiagonalMatrixRow(
1353+
ᶠinterp(ᶜρʲs.:(1) * ᶜJ) / ᶠJ *
1354+
ᶠset_tracer_upwind_bcs(
1355+
ᶠtracer_upwind(CT3(sign(ᶠu³ʲ_data)),
1356+
draft_area(Y.c.sgsʲs.:(1).ρa, ᶜρʲs.:(1)) * ᶜχʲ,
1357+
),
1358+
) * adjoint(C3(sign(ᶠu³ʲ_data))),
1359+
)) DiagonalMatrixRow(g³³(ᶠgⁱʲ))
1360+
1361+
end
1362+
end
12701363
end
12711364
elseif rs isa RayleighSponge
12721365
∂ᶠu₃ʲ_err_∂ᶠu₃ʲ =

src/utils/variable_manipulations.jl

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ If `χ_name` has internal structure (e.g. a composite name), the function
343343
recurses into the child names and prepends `ρ` at the lowest level.
344344
"""
345345
function get_ρχ_name(χ_name)
346-
parent_name = MatrixFields.extract_first(χ_name)
346+
parent_name = MatrixFields.FieldName(MatrixFields.extract_first(χ_name))
347347
child_name = MatrixFields.drop_first(χ_name)
348348
ρχ_name =
349349
(child_name == MatrixFields.@name()) ?
@@ -353,6 +353,35 @@ function get_ρχ_name(χ_name)
353353
return ρχ_name
354354
end
355355

356+
"""
357+
get_χʲ_name_from_ρχ_name(ρχ_name::FieldName)
358+
359+
Construct the `FieldName` corresponding to the specific tracer in the updraft
360+
(`χʲ` with j = 1) associated with a given density-weighted tracer on the grid mean (`ρ·χ`).
361+
362+
Given the name of a density-weighted tracer `ρχ_name`, this function returns the
363+
corresponding name of the specific tracer in the first subgrid updraft.
364+
365+
The function operates recursively on hierarchical field names:
366+
- If `ρχ_name` is a base name (no children), it replaces the `ρ` prefix with the
367+
appropriate updraft-specific prefix (e.g. `sgsʲs.:(1)`) and converts the variable
368+
to its specific form.
369+
- If `ρχ_name` has internal structure (e.g. a composite name), the function
370+
recurses into the child names and applies the transformation at the lowest level.
371+
"""
372+
function get_χʲ_name_from_ρχ_name(ρχ_name)
373+
parent_name = MatrixFields.FieldName(MatrixFields.extract_first(ρχ_name))
374+
child_name = MatrixFields.drop_first(ρχ_name)
375+
χʲ_name =
376+
(child_name == MatrixFields.@name()) ?
377+
MatrixFields.append_internal_name(
378+
@name(sgsʲs.:(1)),
379+
specific_tracer_name(ρχ_name),
380+
) :
381+
MatrixFields.append_internal_name(parent_name, get_χʲ_name_from_ρχ_name(child_name))
382+
return χʲ_name
383+
end
384+
356385
"""
357386
ρa⁰(ρ, sgsʲs, turbconv_model)
358387

0 commit comments

Comments
 (0)