Skip to content

Commit 3c3aaf9

Browse files
authored
Some improvements on saturation adjustment docs (#56)
* add show method for HeightReferenceThermodynamicState * fix show method for HeightReferenceThermodynamicState * punctuation * docstring for temperature * add rh subplot
1 parent 68ae5db commit 3c3aaf9

File tree

3 files changed

+57
-16
lines changed

3 files changed

+57
-16
lines changed

docs/src/microphysics/saturation_adjustment.md

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,21 @@ where ``Π`` is the Exner function, ``θ`` is potential temperature, ``T`` is te
1313
``qᵛ⁺`` is the saturation specific humidity, and ``cᵖᵐ`` is the moist air specific heat.
1414
The condensate specific humidity is ``qˡ = \max(0, qᵗ - qᵛ⁺)``: ``qˡ = 0`` if the air is undersaturated with ``qᵗ < qᵛ⁺``.
1515
Both ``Π`` and ``cᵖᵐ`` depend on the dry and vapor mass fractions ``qᵈ = 1 - qᵗ`` and
16-
``qᵛ = qᵗ - qˡ``, and ``qᵛ⁺`` is an increasing function of temperature ``T``.
17-
Rewriting the potential temperature relation, saturation adjustment requires solving ``r(T) = 0``,
16+
``qᵛ = qᵗ - qˡ``, and the saturation specific humidity``qᵛ⁺`` is an increasing function of temperature ``T``.
17+
18+
Rewriting the potential temperature relation above, saturation adjustment requires solving ``r(T) = 0``, where
1819

1920
```math
20-
r(T) = T - θ Π - \frac{ℒᵥ₀}{cᵖᵐ} \max[0, qᵗ - qᵛ⁺(T)] .
21+
r(T) T - θ Π - \frac{ℒᵥ₀}{cᵖᵐ} \max[0, qᵗ - qᵛ⁺(T)] .
2122
```
2223

2324
We use a secant method after checking for ``θ = 0`` and ``qˡ = 0`` given the guess ``T₁ = θ Π(qᵗ)``.
2425
If ``qᵗ > qᵛ⁺(T₁)``, then we are guaranteed that ``T > T₁`` because ``qᵛ⁺`` is an increasing function of ``T``.
2526
We initialize the secant iteration with a second guess ``T₂ = θ Π - [qᵗ - qᵛ⁺(T₁)] ℒᵥ₀ / cᵖᵐ``.
27+
See [`temperature`](@ref Breeze.MoistAirBuoyancies.temperature) for more details.
2628

27-
28-
As an example, we consider an air parcel at sea-level and with potential temperature of ``θ = 290``ᵒK, within a reference state with base pressure of 101325 Pa and a reference potential temperature ``288``ᵒK.
29+
As an example, we consider an air parcel at sea-level and with potential temperature of ``θ = 290``ᵒK,
30+
within a reference state with base pressure of 101325 Pa and a reference potential temperature ``288``ᵒK.
2931
The saturation specific humidity is then
3032

3133
```@example microphysics
@@ -67,7 +69,7 @@ As a second example, we examine the dependence of temperature on total specific
6769
when the potential temperature is constant:
6870

6971
```@example microphysics
70-
qᵗ = 0:1e-4:0.02 # [kg kg⁻¹] total specific humidity
72+
qᵗ = 0:1e-4:0.04 # [kg kg⁻¹] total specific humidity
7173
U = [HeightReferenceThermodynamicState(θ, qᵗⁱ, z) for qᵗⁱ in qᵗ]
7274
T = [temperature(Uⁱ, ref, thermo) for Uⁱ in U]
7375
@@ -81,8 +83,8 @@ using CairoMakie
8183
fig = Figure()
8284
ax = Axis(fig[1, 1], xlabel="Total specific humidity (kg kg⁻¹)", ylabel="Temperature (ᵒK)")
8385
lines!(ax, qᵗ, T, label="Temperature from saturation adjustment")
84-
lines!(ax, qᵗ, T̃, label="Temperature from linearized formula")
85-
axislegend(ax)
86+
lines!(ax, qᵗ, T̃, label="Temperature from linearized formula", linewidth=2)
87+
axislegend(ax, position=:lt)
8688
fig
8789
```
8890

@@ -94,26 +96,34 @@ but at varying heights:
9496
```@example microphysics
9597
qᵗ = 0.005
9698
z = 0:100:10e3
99+
97100
T = [temperature(HeightReferenceThermodynamicState(θ, qᵗ, zᵏ), ref, thermo) for zᵏ in z]
98101
qᵛ⁺ = [saturation_specific_humidity(T[k], z[k], ref, thermo, thermo.liquid) for k = 1:length(z)]
99102
qˡ = [max(0, qᵗ - qᵛ⁺ᵏ) for qᵛ⁺ᵏ in qᵛ⁺]
103+
rh = [100 * min(qᵗ, qᵛ⁺ᵏ) / qᵛ⁺ᵏ for qᵛ⁺ᵏ in qᵛ⁺]
100104
101105
cᵖᵈ = thermo.dry_air.heat_capacity
102106
g = thermo.gravitational_acceleration
103107
104-
fig = Figure()
108+
fig = Figure(size=(700, 350))
105109
106110
yticks = 0:2e3:10e3
107-
axT = Axis(fig[1, 1]; xlabel = "Temperature (ᵒK)", ylabel = "Height (m)", yticks)
108-
axq⁺ = Axis(fig[1, 2]; xlabel = "Saturation \n specific humidity \n (kg kg⁻¹)",
111+
112+
axT = Axis(fig[1, 1:2]; xlabel = "Temperature (ᵒK)", ylabel = "Height (m)", yticks)
113+
axq⁺ = Axis(fig[1, 3]; xlabel = "Saturation\n specific humidity\n (kg kg⁻¹)",
114+
yticks, yticklabelsvisible = false)
115+
axqˡ = Axis(fig[1, 4]; xlabel = "Liquid\n specific humidity\n (kg kg⁻¹)",
109116
yticks, yticklabelsvisible = false)
110-
axqˡ = Axis(fig[1, 3]; xlabel = "Liquid \n specific humidity \n (kg kg⁻¹)",
117+
118+
axrh = Axis(fig[1, 5]; xlabel = "Relative\n humidity (%)",
119+
xticks = 0:20:100,
111120
yticks, yticklabelsvisible = false)
112121
113122
lines!(axT, T, z)
114123
lines!(axT, T[1] .- g * z / cᵖᵈ, z, linestyle = :dash, color = :orange, linewidth = 2)
115124
lines!(axq⁺, qᵛ⁺, z)
116125
lines!(axqˡ, qˡ, z)
126+
lines!(axrh, rh, z)
117127
118128
fig
119129
```

src/MoistAirBuoyancies.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,44 @@ struct HeightReferenceThermodynamicState{FT}
225225
z :: FT
226226
end
227227

228+
Base.summary(::HeightReferenceThermodynamicState{FT}) where FT = "HeightReferenceThermodynamicState{$FT}"
229+
230+
function Base.show(io::IO, hrts::HeightReferenceThermodynamicState)
231+
print(io, summary(hrts), ":", '\n',
232+
"├── θ: ", hrts.θ, "\n",
233+
"├── q: ", hrts.q, "\n",
234+
"└── z: ", hrts.z)
235+
end
236+
228237
condensate_specific_humidity(T, state::HeightReferenceThermodynamicState, ref, thermo) =
229238
condensate_specific_humidity(T, state.q, state.z, ref, thermo)
230239

240+
231241
# Solve
232242
# θ = T/Π ( 1 - ℒ qˡ / (cᵖᵐ T))
233243
# for temperature T with qˡ = max(0, q - qᵛ★).
234244
# root of: f(T) = T - Π θ - ℒ qˡ / cᵖᵐ
245+
246+
"""
247+
temperature(state::HeightReferenceThermodynamicState, ref, thermo)
248+
249+
Return the temperature ``T`` that satisfies saturation adjustment, that is, the
250+
temperature for which
251+
252+
```math
253+
θ = [1 - ℒ qˡ / (cᵖᵐ T)] T / Π ,
254+
```
255+
256+
with ``qˡ = \\max(0, qᵗ - qᵛ⁺)`` the condensate specific humidity, where ``qᵗ`` is the
257+
total specific humidity, ``qᵛ⁺`` is the saturation specific humidity.
258+
259+
The saturation adjustment temperature is obtained by solving ``r(T)``, where
260+
```math
261+
r(T) ≡ T - θ Π - ℒ qˡ / (cᵖᵐ T) .
262+
```
263+
264+
Solution of ``r(T) = 0`` is found via the [secant method](https://en.wikipedia.org/wiki/Secant_method).
265+
"""
235266
@inline function temperature(state::HeightReferenceThermodynamicState{FT}, ref, thermo) where FT
236267
state.θ == 0 && return zero(FT)
237268

src/Thermodynamics/vapor_saturation.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ Compute the saturation vapor pressure ``pᵛ⁺`` over a fluid surface at
55
`phase` (i.e., liquid or solid) from the Clausius-Clapeyron relation,
66
77
```math
8-
dpᵛ⁺ / dT = pᵛ⁺ ℒᵛ(T) / (Rᵛ T^2)
8+
dpᵛ⁺ / dT = pᵛ⁺ ℒᵛ(T) / (Rᵛ T^2) ,
99
```
1010
11-
where ``ℒᵛ(T) = ℒᵛ(T=0) + Δcᵖ T``, with ``Δcᵖ ≡ (cᵖᵛ - cᵖˡ)``.
11+
where ``ℒᵛ(T) = ℒᵛ(T=0) + Δcᵖ T``, with ``Δcᵖ ≡ (cᵖᵛ - cᵖˡ)`` .
1212
1313
The saturation vapor pressure ``pᵛ⁺`` is obtained after integrating the above from
1414
the triple point, i.e., ``p(Tᵗʳ) = pᵗʳ`` to get
1515
1616
```math
17-
pᵛ⁺(T) = pᵗʳ \\left ( \\frac{T}{Tᵗʳ} \\right )^{Δcᵖ / Rᵛ} \\exp \\left [ (1/Tᵗʳ - 1/T) ℒᵛ(T=0) / Rᵛ \\right ]
17+
pᵛ⁺(T) = pᵗʳ \\left ( \\frac{T}{Tᵗʳ} \\right )^{Δcᵖ / Rᵛ} \\exp \\left [ (1/Tᵗʳ - 1/T) ℒᵛ(T=0) / Rᵛ \\right ] .
1818
```
1919
"""
2020
@inline function saturation_vapor_pressure(T, thermo, phase::CondensedPhase)
@@ -42,7 +42,7 @@ Compute the saturation specific humidity for a gas at temperature `T`, total
4242
density `ρ`, `thermo`dynamics, and `condensed_phase` via:
4343
4444
```math
45-
qᵛ⁺ = pᵛ⁺ / (ρ Rᵛ T)
45+
qᵛ⁺ = pᵛ⁺ / (ρ Rᵛ T) ,
4646
```
4747
4848
where ``pᵛ⁺`` is the [`saturation_vapor_pressure`](@ref), and ``Rᵛ`` is the specific gas

0 commit comments

Comments
 (0)