Skip to content

Commit be6d8df

Browse files
authored
Merge pull request #708 from CliMA/aj/evaporation_fix
Use condensate in evaporation/sublimation
2 parents 8f2976c + e6581c3 commit be6d8df

File tree

9 files changed

+29
-450
lines changed

9 files changed

+29
-450
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "CloudMicrophysics"
22
uuid = "6a9e3e04-43cd-43ba-94b9-e8782df3c71b"
3-
version = "0.32"
3+
version = "0.33"
44
authors = ["Climate Modeling Alliance"]
55

66
[deps]

docs/src/API.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,10 @@ MicrophysicsNonEq
1111
MicrophysicsNonEq.τ_relax
1212
MicrophysicsNonEq.conv_q_vap_to_q_lcl_icl
1313
MicrophysicsNonEq.conv_q_vap_to_q_lcl_icl_MM2015
14-
MicrophysicsNonEq.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld
15-
MicrophysicsNonEq.limit_MM2015_sinks
1614
MicrophysicsNonEq.INP_limiter
1715
MicrophysicsNonEq.terminal_velocity
1816
MicrophysicsNonEq.dqcld_dT
1917
MicrophysicsNonEq.gamma_helper
20-
MicrophysicsNonEq.d2qcld_dT2
2118
```
2219

2320
# 0-moment precipitation microphysics

docs/src/MicrophysicsNonEq.md

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -124,38 +124,8 @@ include("plots/NonEqCondEvapRate.jl")
124124
![](condensation_evaporation_ql_z.svg)
125125
![](condensation_evaporation_ql_T.svg)
126126

127-
### Derivative of the tendency
128127

129-
For implicit time integration, we also compute the total derivative of the tendency
130-
with respect to the cloud condensate, accounting for the implicit feedback
131-
between the condensate and temperature through latent heat release
132-
(``dT/dq = L/c_p``):
133128

134-
```math
135-
\begin{equation}
136-
\frac{d\dot{q}_{lcl}}{dq_{lcl}} = -\frac{1}{\tau_l}
137-
- \frac{\dot{q}_{lcl}}{\Gamma_l} \left(\frac{L_v}{c_p}\right)^2 \frac{d^2 q_{sl}}{dT^2};
138-
\;\;\;\;\;\;\;\;
139-
\frac{d\dot{q}_{icl}}{dq_{icl}} = -\frac{1}{\tau_i}
140-
- \frac{\dot{q}_{icl}}{\Gamma_i} \left(\frac{L_s}{c_p}\right)^2 \frac{d^2 q_{si}}{dT^2}
141-
\end{equation}
142-
```
143-
where ``\dot{q}_{lcl}`` and ``\dot{q}_{icl}`` denote the condensation/evaporation
144-
and deposition/sublimation tendencies respectively,
145-
and the second derivative of saturation specific humidity is:
146-
```math
147-
\begin{equation}
148-
\frac{d^2 q_{sl}}{dT^2} = q_{sl} \left[\left(\frac{L_v}{R_v T^2} - \frac{1}{T}\right)^2
149-
+ \frac{1}{T^2} - \frac{2 L_v}{R_v T^3}\right]
150-
\end{equation}
151-
```
152-
153-
The leading-order term ``-1/\tau`` provides a simple approximation,
154-
while the second term captures the higher-order correction from the
155-
thermodynamic adjustment factor ``\Gamma``.
156-
157-
![](condensation_evaporation_deriv.svg)
158-
![](condensation_evaporation_deriv_vs_qvap.svg)
159129

160130

161131
## Cloud condensate sedimentation

docs/src/plots/NonEqCondEvapRate.jl

Lines changed: 0 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ function generate_cond_evap_rate(::Range_qₗ_T)
9898
)
9999
end
100100

101-
# --- Derivative line plots ---
102101

103102
minussign(s) = replace(s, "-" => "")
104103

@@ -122,200 +121,6 @@ function make_figure(case)
122121
save(file_name, fig)
123122
end
124123

125-
function make_deriv_line_plot()
126-
# Temperature range
127-
T_range = collect(220:0.5:310)
128-
129-
# Fixed parameters
130-
qₜ = FT(10e-3) # 10 g/kg total water
131-
qₗ = FT(1e-3) # 1 g/kg cloud liquid
132-
qᵢ = FT(1e-3) # 1 g/kg cloud ice
133-
qᵣ = FT(0)
134-
qₛ = FT(0)
135-
ρ = FT(1) # kg/m³
136-
137-
τ_relax = FT(1) # 1 s relaxation time
138-
_liq = CM.Parameters.CloudLiquid(FT)
139-
cm_liq = CM.Parameters.CloudLiquid(τ_relax, _liq.ρw, _liq.r_eff, _liq.N_0)
140-
_ice = CM.Parameters.CloudIce(FT)
141-
cm_ice = CM.Parameters.CloudIce(_ice.pdf, _ice.mass, _ice.r0, _ice.r_ice_snow, τ_relax, _ice.ρᵢ, _ice.r_eff, _ice.N_0)
142-
143-
# Compute tendency and derivatives for each temperature
144-
S_liq = zeros(FT, length(T_range))
145-
dS_liq_simple = zeros(FT, length(T_range))
146-
dS_liq_full = zeros(FT, length(T_range))
147-
148-
S_ice = zeros(FT, length(T_range))
149-
dS_ice_simple = zeros(FT, length(T_range))
150-
dS_ice_full = zeros(FT, length(T_range))
151-
152-
for (i, T) in enumerate(T_range)
153-
T_ft = FT(T)
154-
s = CMNe.conv_q_vap_to_q_lcl_icl_MM2015(
155-
cm_liq, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T_ft,
156-
)
157-
ds_s = CMNe.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(
158-
cm_liq, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T_ft,
159-
)
160-
ds_f = CMNe.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(
161-
cm_liq, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T_ft;
162-
simplified = false,
163-
)
164-
S_liq[i] = s
165-
dS_liq_simple[i] = ds_s
166-
dS_liq_full[i] = ds_f
167-
168-
s = CMNe.conv_q_vap_to_q_lcl_icl_MM2015(
169-
cm_ice, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T_ft,
170-
)
171-
ds_s = CMNe.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(
172-
cm_ice, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T_ft,
173-
)
174-
ds_f = CMNe.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(
175-
cm_ice, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T_ft;
176-
simplified = false,
177-
)
178-
S_ice[i] = s
179-
dS_ice_simple[i] = ds_s
180-
dS_ice_full[i] = ds_f
181-
end
182-
183-
T_freeze = FT(TD.Parameters.T_freeze(thp))
184-
185-
# --- Figure: 2 panels ---
186-
fig = Figure(size = (900, 450))
187-
188-
# Left panel: tendency vs T
189-
ax1 = Axis(fig[1, 1];
190-
xlabel = "Temperature (K)",
191-
ylabel = "Tendency (1/s)",
192-
title = "Condensation/Evaporation & Deposition/Sublimation\n(qₜ=10, qₗ=1, qᵢ=1 g/kg, ρ=1 kg/m³, τ=1 s)",
193-
)
194-
lines!(ax1, T_range, S_liq; color = :blue, label = "Liquid (cond/evap)")
195-
lines!(ax1, T_range, S_ice; color = :magenta, label = "Ice (dep/subl)")
196-
vlines!(ax1, [T_freeze]; color = :gray, linestyle = :dash, label = "T_freeze")
197-
hlines!(ax1, [0]; color = :black, linewidth = 0.5)
198-
text!(ax1, T_freeze + 2, minimum(S_ice) * 0.5;
199-
text = "INP limiter\n(ice dep → 0)",
200-
fontsize = 10, color = :gray,
201-
)
202-
axislegend(ax1; position = :lb, framevisible = false)
203-
204-
# Right panel: derivative vs T
205-
ax2 = Axis(fig[1, 2];
206-
xlabel = "Temperature (K)",
207-
ylabel = "∂(tendency)/∂q (1/s)",
208-
title = "Derivative of tendency w.r.t. cloud condensate",
209-
)
210-
lines!(ax2, T_range, dS_liq_full; color = :blue, label = "Liquid (full)")
211-
lines!(ax2, T_range, dS_ice_full; color = :magenta, label = "Ice (full)")
212-
# Simple derivative is -1/τ for both phases (identical when τ_liq == τ_ice)
213-
lines!(ax2, T_range, dS_liq_simple; color = :black, linestyle = :dash, label = "Simple = −1/τ (both)")
214-
vlines!(ax2, [T_freeze]; color = :gray, linestyle = :dash, label = "T_freeze")
215-
text!(ax2, T_freeze + 2, minimum(dS_ice_full) * 0.5;
216-
text = "INP limiter\n(∂ice → 0)",
217-
fontsize = 10, color = :gray,
218-
)
219-
axislegend(ax2; position = :lt, framevisible = false)
220-
221-
save("condensation_evaporation_deriv.svg", fig)
222-
end
223-
224-
function make_deriv_vs_qvap_plot()
225-
# Available water vapor range (q_vap = q_tot - q_lcl - q_icl - q_rai - q_sno)
226-
# We vary q_tot while keeping q_lcl, q_icl fixed, so q_vap changes
227-
qₗ = FT(1e-3) # 1 g/kg cloud liquid (fixed)
228-
qᵢ = FT(1e-3) # 1 g/kg cloud ice (fixed)
229-
qᵣ = FT(0)
230-
qₛ = FT(0)
231-
ρ = FT(1) # kg/m³
232-
233-
# q_tot range: from 2 g/kg (all condensate, no vapor) to 20 g/kg
234-
qₜ_range = collect(range(FT(2e-3), stop = FT(20e-3), length = 181))
235-
qᵥ_range = qₜ_range .- qₗ .- qᵢ # available water vapor
236-
237-
τ_relax = FT(1)
238-
_liq = CM.Parameters.CloudLiquid(FT)
239-
cm_liq = CM.Parameters.CloudLiquid(τ_relax, _liq.ρw, _liq.r_eff, _liq.N_0)
240-
_ice = CM.Parameters.CloudIce(FT)
241-
cm_ice = CM.Parameters.CloudIce(_ice.pdf, _ice.mass, _ice.r0, _ice.r_ice_snow, τ_relax, _ice.ρᵢ, _ice.r_eff, _ice.N_0)
242-
243-
# Evaluate at two representative temperatures
244-
T_vals = [FT(250), FT(280)]
245-
T_labels = ["T=250K", "T=280K"]
246-
T_colors_liq = [:royalblue, :cornflowerblue]
247-
T_colors_ice = [:magenta, :hotpink]
248-
249-
fig = Figure(size = (900, 450))
250-
251-
ax1 = Axis(fig[1, 1];
252-
xlabel = "Water vapor, qᵥ (g/kg)",
253-
ylabel = "Tendency (1/s)",
254-
title = "Tendency vs available water vapor\n(qₗ=1, qᵢ=1 g/kg, ρ=1 kg/m³, τ=1 s)",
255-
)
256-
ax2 = Axis(fig[1, 2];
257-
xlabel = "Water vapor, qᵥ (g/kg)",
258-
ylabel = "∂(tendency)/∂q (1/s)",
259-
title = "Derivative vs available water vapor",
260-
)
261-
262-
for (j, T) in enumerate(T_vals)
263-
S_liq = zeros(FT, length(qₜ_range))
264-
dS_liq_f = zeros(FT, length(qₜ_range))
265-
dS_liq_s = zeros(FT, length(qₜ_range))
266-
S_ice = zeros(FT, length(qₜ_range))
267-
dS_ice_f = zeros(FT, length(qₜ_range))
268-
269-
for (i, qₜ) in enumerate(qₜ_range)
270-
s = CMNe.conv_q_vap_to_q_lcl_icl_MM2015(
271-
cm_liq, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T,
272-
)
273-
ds_s = CMNe.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(
274-
cm_liq, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T,
275-
)
276-
ds_f = CMNe.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(
277-
cm_liq, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T;
278-
simplified = false,
279-
)
280-
S_liq[i] = s
281-
dS_liq_f[i] = ds_f
282-
dS_liq_s[i] = ds_s
283-
284-
s = CMNe.conv_q_vap_to_q_lcl_icl_MM2015(
285-
cm_ice, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T,
286-
)
287-
ds_f = CMNe.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(
288-
cm_ice, thp, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, T;
289-
simplified = false,
290-
)
291-
S_ice[i] = s
292-
dS_ice_f[i] = ds_f
293-
end
294-
295-
qᵥ_gkg = qᵥ_range * 1e3
296-
297-
# Left panel: tendency
298-
lines!(ax1, qᵥ_gkg, S_liq; color = T_colors_liq[j], label = "Liquid $(T_labels[j])")
299-
lines!(ax1, qᵥ_gkg, S_ice; color = T_colors_ice[j], label = "Ice $(T_labels[j])")
300-
301-
# Right panel: derivative
302-
lines!(ax2, qᵥ_gkg, dS_liq_f; color = T_colors_liq[j], label = "Liquid $(T_labels[j])")
303-
lines!(ax2, qᵥ_gkg, dS_ice_f; color = T_colors_ice[j], label = "Ice $(T_labels[j])")
304-
305-
if j == 1
306-
lines!(ax2, qᵥ_gkg, dS_liq_s; color = :black, linestyle = :dash, label = "Simple = −1/τ")
307-
end
308-
end
309-
310-
hlines!(ax1, [0]; color = :black, linewidth = 0.5)
311-
axislegend(ax1; position = :lt, framevisible = false)
312-
axislegend(ax2; position = :lb, framevisible = false)
313-
314-
save("condensation_evaporation_deriv_vs_qvap.svg", fig)
315-
end
316-
317124
make_figure(HydrostaticBalance_qₗ_z())
318125
make_figure(Range_qₗ_T())
319-
make_deriv_line_plot()
320-
make_deriv_vs_qvap_plot()
321126

src/BulkMicrophysicsTendencies.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ and avoids quadratures over the derivatives.
434434
vel = mp.terminal_velocity
435435

436436
# --- Cloud liquid: condensation + sink self-derivatives ---
437-
∂tendency_∂q_lcl = CMNonEq.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(lcl, tps, q_tot, q_lcl, q_icl, q_rai, q_sno, ρ, T)
437+
∂tendency_∂q_lcl = -1 / CMNonEq.τ_relax(lcl)
438438
# Autoconversion q_lcl → q_rai (sink)
439439
S_acnv_lcl = CM1.conv_q_lcl_to_q_rai(rai.acnv1M, q_lcl, true)
440440
# Accretion q_lcl + q_rai → q_rai (rate is exactly linear in q_lcl)
@@ -446,7 +446,7 @@ and avoids quadratures over the derivatives.
446446
max(q_lcl, UT.ϵ_numerics(typeof(q_lcl)))
447447

448448
# --- Cloud ice: deposition + sink self-derivatives ---
449-
∂tendency_∂q_icl = CMNonEq.∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld(icl, tps, q_tot, q_lcl, q_icl, q_rai, q_sno, ρ, T)
449+
∂tendency_∂q_icl = -1 / CMNonEq.τ_relax(icl)
450450
# Autoconversion q_icl → q_sno
451451
S_acnv_icl = CM1.conv_q_icl_to_q_sno_no_supersat(sno.acnv1M, q_icl, true)
452452
# Accretion q_icl + q_rai → q_sno (rate is exactly linear in q_icl)
@@ -519,7 +519,7 @@ derivatives; snow and cloud formation derivatives are zero for now.
519519
N_rai = ρ * n_rai
520520

521521
# TODO: Cloud formation — 2M bulk_microphysics_tendencies does not call cloud formation yet;
522-
# once it does, set these from MM2015 (same as 1M) via ∂conv_q_vap_to_q_lcl_icl_MM2015_∂q_cld.
522+
# once it does, set these from MM2015 (same as 1M) via -1/τ_relax.
523523
∂tendency_∂q_lcl = zero(ρ)
524524
∂tendency_∂q_icl = zero(ρ)
525525

0 commit comments

Comments
 (0)