Skip to content

Commit df066f8

Browse files
authored
More general set! plus MoistureMassFractions abstraction (#72)
* More general set! plus SpecificHumidities abstraction * try to get SpecificHumidities working * reduce thermodynamic states * get SpecificHumidities into docs * SpecificHumidities -> MassRatios * humidities -> mass_ratios * mass ratio -> specific moisture content * lets try "mass fractions" * change "latent heat" to "reference latent heat" * reorganize stuff * cleanup * fixing trhe reference state * bomex running * thermal bubble runss * fix thermal bubble * fix tests * fix * use approx * more test fixes * rm unused import * fix docs * fix * fix saturation adj bug * fix saturation adjustment solver for boussinesq model * fix broken imports * another import fix * clean up * fix docs example * fix another saturation adj bug * add comments * dont show iter * fix diagnostics * update saturation adj docs * use rand not randn * add tests and change algorithm * rm show methods * change ref from chammas to pressel * fix quick start * another fix
1 parent 6c386a7 commit df066f8

30 files changed

+845
-719
lines changed

docs/src/breeze.bib

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,14 @@ @book{Straka2009
2828
year = {2009},
2929
doi = {10.1017/CBO9780511581168}
3030
}
31+
32+
@article{Pressel2015,
33+
title={Large-eddy simulation in an anelastic framework with closed water and entropy balances},
34+
author={Pressel, Kyle G and Kaul, Colleen M and Schneider, Tapio and Tan, Zhihong and Mishra, Siddhartha},
35+
journal={Journal of Advances in Modeling Earth Systems},
36+
volume={7},
37+
number={3},
38+
pages={1425--1456},
39+
year={2015},
40+
publisher={Wiley Online Library}
41+
}

docs/src/index.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,27 @@ seed!(42)
3939
4040
Nx = Nz = 64
4141
Lz = 4 * 1024
42-
grid = RectilinearGrid(CPU(), size=(Nx, Nz), x=(0, 2Lz), z=(0, Lz), topology=(Periodic, Flat, Bounded))
42+
grid = RectilinearGrid(size=(Nx, Nz), x=(0, 2Lz), z=(0, Lz), topology=(Periodic, Flat, Bounded))
4343
44-
reference_constants = Breeze.Thermodynamics.ReferenceStateConstants(base_pressure=1e5, potential_temperature=288)
45-
buoyancy = Breeze.MoistAirBuoyancy(; reference_constants)
44+
p₀, θᵣ = 1e5, 288 # reference state parameters
45+
buoyancy = Breeze.MoistAirBuoyancy(grid, base_pressure=p₀, reference_potential_temperature=θᵣ)
4646
47-
Q₀ = 1000 # heat flux in W / m²
48-
ρ₀ = Breeze.MoistAirBuoyancies.base_density(buoyancy) # air density at z=0
47+
thermo = buoyancy.thermodynamics
48+
ρ₀ = Breeze.Thermodynamics.base_density(p₀, θᵣ, thermo)
4949
cₚ = buoyancy.thermodynamics.dry_air.heat_capacity
50+
Q₀ = 1000 # heat flux in W / m²
5051
θ_bcs = FieldBoundaryConditions(bottom=FluxBoundaryCondition(Q₀ / (ρ₀ * cₚ)))
51-
q_bcs = FieldBoundaryConditions(bottom=FluxBoundaryCondition(1e-2))
52+
qᵗ_bcs = FieldBoundaryConditions(bottom=FluxBoundaryCondition(1e-2))
5253
5354
advection = WENO()
54-
tracers = (:θ, :q)
5555
model = NonhydrostaticModel(; grid, advection, buoyancy,
56-
tracers = (:θ, :q),
57-
boundary_conditions = (θ=θ_bcs, q=q_bcs))
56+
tracers = (:θ, :qᵗ),
57+
boundary_conditions = (θ=θ_bcs, qᵗ=qᵗ_bcs))
5858
5959
Δθ = 2 # ᵒK
60-
Tₛ = reference_constants.reference_potential_temperature # K
60+
Tₛ = buoyancy.reference_state.potential_temperature # K
6161
θᵢ(x, z) = Tₛ + Δθ * z / grid.Lz + 2e-2 * Δθ * (rand() - 0.5)
62-
qᵢ(x, z) = 0 # 1e-2 + 1e-5 * rand()
63-
set!(model, θ=θᵢ, q=qᵢ)
62+
set!(model, θ=θᵢ)
6463
6564
simulation = Simulation(model, Δt=10, stop_time=2hours)
6665
conjure_time_step_wizard!(simulation, cfl=0.7)

docs/src/microphysics/saturation_adjustment.md

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Warm-phase saturation adjustment
22

33
Warm-phase saturation adjustment is a model for water droplet nucleation that assumes that water vapor in excess of the saturation specific humidity is instantaneously converted to liquid water.
4-
Mixed-phase saturation adjustment is described by [Chammas2023](@citet).
4+
Mixed-phase saturation adjustment is described by [Pressel2015](@citet).
55
Saturation adjustment may be formulated as a nonlinear algebraic equation that relates temperature, potential temperature, and total specific humidity, derived from the definition of liquid potential temperature,
66

77
```math
8-
θ = \frac{T}{Π} \left \{1 - \frac{ℒᵥ₀}{cᵖᵐ T} \max \left [0, qᵗ - qᵛ⁺(T) \right ] \right \} ,
8+
θ = \frac{1}{Π} \left \{T - \frac{ℒᵥ₀}{cᵖᵐ} \max \left [0, qᵗ - qᵛ⁺(T) \right ] \right \} ,
99
```
1010

1111
where ``Π`` is the Exner function, ``θ`` is potential temperature, ``T`` is temperature,
@@ -32,14 +32,15 @@ The saturation specific humidity is then
3232

3333
```@example microphysics
3434
using Breeze
35-
using Breeze.MoistAirBuoyancies: saturation_specific_humidity, HeightReferenceThermodynamicState
35+
using Breeze.Thermodynamics: saturation_specific_humidity
3636
3737
thermo = ThermodynamicConstants()
38-
ref = ReferenceStateConstants(base_pressure=101325, potential_temperature=288)
3938
40-
z = 0.0 # [m] height
41-
θ = 290.0 # [ᵒK] potential temperature
42-
qᵛ⁺₀ = saturation_specific_humidity(θ, z, ref, thermo, thermo.liquid)
39+
p₀ = 101325.0
40+
θ₀ = 288.0
41+
Rᵈ = Breeze.Thermodynamics.dry_air_gas_constant(thermo)
42+
ρ₀ = p₀ / (Rᵈ * θ₀)
43+
qᵛ⁺₀ = saturation_specific_humidity(θ₀, ρ₀, thermo, thermo.liquid)
4344
```
4445

4546
Recall that the specific humidity is unitless, or has units "``kg / kg``": kg of water vapor
@@ -49,17 +50,20 @@ given a total specific humidity slightly above saturation:
4950

5051
```@example microphysics
5152
using Breeze.MoistAirBuoyancies: temperature
53+
using Breeze.Thermodynamics: PotentialTemperatureState, MoistureMassFractions
5254
55+
z = 0.0
5356
qᵗ = 0.012 # [kg kg⁻¹] total specific humidity
54-
U = HeightReferenceThermodynamicState(θ, qᵗ, z)
55-
T = temperature(U, ref, thermo)
57+
q = MoistureMassFractions(qᵗ, zero(qᵗ), zero(qᵗ))
58+
U = PotentialTemperatureState(θ₀, q, z, p₀, p₀, ρ₀)
59+
T = temperature(U, thermo)
5660
```
5761

5862
Finally, we recover the amount of liquid condensate by subtracting the saturation
5963
specific humidity from the total:
6064

6165
```@example microphysics
62-
qᵛ⁺ = saturation_specific_humidity(T, z, ref, thermo, thermo.liquid)
66+
qᵛ⁺ = saturation_specific_humidity(T, ρ₀, thermo, thermo.liquid)
6367
qˡ = qᵗ - qᵛ⁺
6468
```
6569

@@ -69,14 +73,15 @@ As a second example, we examine the dependence of temperature on total specific
6973
when the potential temperature is constant:
7074

7175
```@example microphysics
72-
qᵗ = 0:1e-4:0.04 # [kg kg⁻¹] total specific humidity
73-
U = [HeightReferenceThermodynamicState(θ, qᵗⁱ, z) for qᵗⁱ in qᵗ]
74-
T = [temperature(Uⁱ, ref, thermo) for Uⁱ in U]
76+
qᵗ = 0:1e-4:0.035 # [kg kg⁻¹] total specific humidity
77+
q = [MoistureMassFractions(qᵗⁱ, 0.0, 0.0) for qᵗⁱ in qᵗ]
78+
U = [PotentialTemperatureState(θ₀, qⁱ, z, p₀, p₀, ρ₀) for qⁱ in q]
79+
T = [temperature(Uⁱ, thermo) for Uⁱ in U]
7580
7681
## Compare with a simple piecewise linear model
77-
ℒᵥ₀ = thermo.liquid.latent_heat
82+
ℒᵥ₀ = thermo.liquid.reference_latent_heat
7883
cᵖᵈ = thermo.dry_air.heat_capacity
79-
T̃ = [290 + ℒᵥ₀ / cᵖᵈ * max(0, qᵗⁱ - qᵛ⁺₀) for qᵗⁱ in qᵗ]
84+
T̃ = [288 + ℒᵥ₀ / cᵖᵈ * max(0, qᵗⁱ - qᵛ⁺₀) for qᵗⁱ in qᵗ]
8085
8186
using CairoMakie
8287
@@ -94,13 +99,32 @@ For a third example, we consider a state with constant potential temperature and
9499
but at varying heights:
95100

96101
```@example microphysics
102+
grid = RectilinearGrid(size=100, z=(0, 1e4), topology=(Flat, Flat, Bounded))
103+
thermo = ThermodynamicConstants()
104+
reference_state = ReferenceState(grid, thermo)
105+
106+
θᵣ = reference_state.potential_temperature
107+
p₀ = reference_state.base_pressure
97108
qᵗ = 0.005
98-
z = 0:100:10e3
109+
q = MoistureMassFractions(qᵗ, 0.0, 0.0)
110+
111+
z = znodes(grid, Center())
112+
T = zeros(grid.Nz)
113+
qᵛ⁺ = zeros(grid.Nz)
114+
qˡ = zeros(grid.Nz)
115+
rh = zeros(grid.Nz)
116+
117+
for k = 1:grid.Nz
118+
ρᵣ = reference_state.density[1, 1, k]
119+
pᵣ = reference_state.pressure[1, 1, k]
120+
121+
U = PotentialTemperatureState(θᵣ, q, z[k], p₀, pᵣ, ρᵣ)
122+
T[k] = temperature(U, thermo)
99123
100-
T = [temperature(HeightReferenceThermodynamicState(θ, qᵗ, zᵏ), ref, thermo) for zᵏ in z]
101-
qᵛ⁺ = [saturation_specific_humidity(T[k], z[k], ref, thermo, thermo.liquid) for k = 1:length(z)]
102-
qˡ = [max(0, qᵗ - qᵛ⁺ᵏ) for qᵛ⁺ᵏ in qᵛ⁺]
103-
rh = [100 * min(qᵗ, qᵛ⁺ᵏ) / qᵛ⁺ᵏ for qᵛ⁺ᵏ in qᵛ⁺]
124+
qᵛ⁺[k] = saturation_specific_humidity(T[k], ρᵣ, thermo, thermo.liquid)
125+
[k] = max(0, qᵗ - qᵛ⁺[k])
126+
rh[k] = 100 * min(qᵗ, qᵛ⁺[k]) / qᵛ⁺[k]
127+
end
104128
105129
cᵖᵈ = thermo.dry_air.heat_capacity
106130
g = thermo.gravitational_acceleration

docs/src/thermodynamics.md

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -187,23 +187,19 @@ with ``Rᵈ = 286.71 \; \mathrm{J} \, \mathrm{K}^{-1}``:
187187

188188
```@example reference_state
189189
using Breeze
190-
using Breeze.Thermodynamics: reference_pressure, reference_density
191190
using CairoMakie
192191
193-
thermo = ThermodynamicConstants()
194-
constants = ReferenceStateConstants(base_pressure=101325, potential_temperature=288)
195192
grid = RectilinearGrid(size=160, z=(0, 12_000), topology=(Flat, Flat, Bounded))
193+
thermo = ThermodynamicConstants()
194+
reference_state = ReferenceState(grid, thermo, base_pressure=101325, potential_temperature=288)
196195
197-
pᵣ = CenterField(grid)
198-
ρᵣ = CenterField(grid)
199-
200-
set!(pᵣ, z -> reference_pressure(z, constants, thermo))
201-
set!(ρᵣ, z -> reference_density(z, constants, thermo))
196+
pᵣ = reference_state.pressure
197+
ρᵣ = reference_state.density
202198
203199
Rᵈ = Breeze.Thermodynamics.dry_air_gas_constant(thermo)
204200
cᵖᵈ = thermo.dry_air.heat_capacity
205-
p₀ = constants.base_pressure
206-
θ₀ = constants.reference_potential_temperature
201+
p₀ = reference_state.base_pressure
202+
θ₀ = reference_state.potential_temperature
207203
g = thermo.gravitational_acceleration
208204
209205
# Verify that Tᵣ = θ₀ (1 - g z / (cᵖᵈ θ₀))
@@ -280,17 +276,18 @@ More generally, ``qᵈ = 1 - qᵛ - qᶜ``, where ``qᶜ`` is the total mass
280276
ratio of condensed species. In most situations on Earth, ``qᶜ ≪ qᵛ``.
281277

282278
```@example thermo
279+
using Breeze.Thermodynamics: MoistureMassFractions
280+
283281
# Compute mixture properties for air with 0.01 specific humidity
284-
qᵛ = 0.01 # 1% water vapor by mass
285-
Rᵐ = mixture_gas_constant(qᵛ, thermo)
282+
qᵗ = 0.01 # 1% water vapor by mass
283+
q = MoistureMassFractions(qᵗ, zero(qᵗ), zero(qᵗ))
284+
Rᵐ = mixture_gas_constant(q, thermo)
286285
```
287286

288287
We likewise define a mixture heat capacity via ``cᵖᵐ = qᵈ cᵖᵈ + qᵛ cᵖᵛ``,
289288

290-
291289
```@example thermo
292-
q = 0.01 # 1% water vapor by mass
293-
cᵖᵐ = mixture_heat_capacity(qᵛ, thermo)
290+
cᵖᵐ = mixture_heat_capacity(q, thermo)
294291
```
295292

296293
## Liquid-ice potential temperature
@@ -313,7 +310,7 @@ heats, the latent heat of a phase transition is linear in temperature.
313310
For example, for phase change from vapor to liquid,
314311

315312
```math
316-
ℒˡ(T) = ℒˡ(T=0) + \big ( \underbrace{cᵖᵛ - cᵖˡ}_{≡Δcˡ} \big ) T ,
313+
ℒˡ(T) = ℒˡ(T=0) + \big ( \underbrace{cᵖᵛ - }_{≡Δcˡ} \big ) T ,
317314
```
318315

319316
where ``ℒˡ(T=0)`` is the latent heat at absolute zero, ``T = 0 \; \mathrm{K}``.
@@ -327,13 +324,13 @@ Consider parameters for liquid water,
327324

328325
```@example thermo
329326
using Breeze.Thermodynamics: CondensedPhase
330-
liquid_water = CondensedPhase(latent_heat=2500800, heat_capacity=4181)
327+
liquid_water = CondensedPhase(reference_latent_heat=2500800, heat_capacity=4181)
331328
```
332329

333330
or water ice,
334331

335332
```@example thermo
336-
water_ice = CondensedPhase(latent_heat=2834000, heat_capacity=2108)
333+
water_ice = CondensedPhase(reference_latent_heat=2834000, heat_capacity=2108)
337334
```
338335

339336
The saturation vapor pressure is
@@ -369,14 +366,19 @@ and this is what it looks like:
369366

370367
```@example
371368
using Breeze
372-
using Breeze.MoistAirBuoyancies: saturation_specific_humidity
369+
using Breeze.Thermodynamics: saturation_specific_humidity
373370
374371
thermo = ThermodynamicConstants()
375-
ref = ReferenceStateConstants(base_pressure=101325, potential_temperature=288)
376372
377-
z = 0
373+
p₀ = 101325
374+
Rᵈ = Breeze.Thermodynamics.dry_air_gas_constant(thermo)
378375
T = collect(273.2:0.1:313.2)
379-
qᵛ⁺ = [saturation_specific_humidity(Tⁱ, z, ref, thermo, thermo.liquid) for Tⁱ in T]
376+
qᵛ⁺ = zeros(length(T))
377+
378+
for i = 1:length(T)
379+
ρ = p₀ / (Rᵈ * T[i])
380+
qᵛ⁺[i] = saturation_specific_humidity(T[i], ρ, thermo, thermo.liquid)
381+
end
380382
381383
using CairoMakie
382384

examples/JRA55_saturation_specific_humidity.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ ax4 = Axis(fig[2, 2], title="Liquid specific humidity")
2424
# Compute cloudiness for instantaneous drop
2525
θ⁻ = T .- 10
2626
Ψ = Breeze.ThermodynamicState{Float64}.(θ⁻, q, 0)
27-
= Breeze.ReferenceStateConstants{Float64}(101325, 20)
27+
= Breeze.ReferenceState{Float64}(101325, 20)
2828
T⁻ = Breeze.temperature.(Ψ, Ref(ℛ), Ref(thermo))
2929
qᵛ★ = saturation_specific_humidity.(T⁻, 1.2, Ref(thermo))
3030
= @. max(0, q - qᵛ★)

examples/atmos_convection.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ grid = RectilinearGrid(size=(Nx, Nz), x=(0, Lx), z=(0, Lz), topology=(Periodic,
1111
ρe_bcs = FieldBoundaryConditions(bottom=FluxBoundaryCondition(1000))
1212
model = AtmosphereModel(grid, advection=WENO(), boundary_conditions=(; ρe=ρe_bcs))
1313

14-
Tₛ = model.formulation.constants.reference_potential_temperature
14+
Tₛ = model.formulation.reference_state.potential_temperature
1515
g = model.thermodynamics.gravitational_acceleration
1616
= 1e-6
1717
dθdz =* Tₛ / g # Background potential temperature gradient

0 commit comments

Comments
 (0)