Skip to content

Commit 784884d

Browse files
authored
Merge pull request #27 from NumericalEarth/glw/new-oceananigans
More tests, bugfixes, and docs
2 parents 07212ff + 20eea23 commit 784884d

27 files changed

+985
-444
lines changed

.github/workflows/Validation.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ jobs:
3838
- name: Run BOMEX example
3939
env:
4040
VALIDATION: "true"
41+
CI: "true"
4142
run: |
4243
julia --project=. examples/bomex.jl

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ CloudMicrophysics = "6a9e3e04-43cd-43ba-94b9-e8782df3c71b"
1010
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
1111
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
1212
Oceananigans = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09"
13+
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1314
RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74"
1415

1516
[compat]
1617
Adapt = "4.3.0"
1718
AtmosphericProfilesLibrary = "0.1.7"
1819
CloudMicrophysics = "0.22.13"
1920
JLD2 = "0.5.13"
20-
Oceananigans = "0.97"
21+
Oceananigans = "0.99"
22+
Printf = "1"
2123
RootSolvers = "0.4.4"
2224

2325
[extras]

docs/make.jl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
using Breeze
22
using Documenter
33

4-
using Breeze.AtmosphereModels
5-
64
DocMeta.setdocmeta!(Breeze, :DocTestSetup, :(using Breeze); recursive=true)
75

86
makedocs(sitename="Breeze",
97
pages=[
108
"Home" => "index.md",
9+
"Thermodynamics" => "thermodynamics.md",
1110
"API" => "api.md",
1211
]
1312
)
1413

1514
deploydocs(;
16-
repo="github.com/NumericalEarth/Breeze.jl",
17-
devbranch="main",
18-
push_preview=true,
19-
forcepush=true
15+
repo = "github.com/NumericalEarth/Breeze.jl",
16+
devbranch = "main",
17+
push_preview = true,
18+
forcepush = true
2019
)

docs/src/index.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
# Breeze.jl
22

3-
Documentation for Breeze.jl
3+
Fast, friendly atmosphere simulations on CPUs and GPUs.
44

5-
## Overview
6-
7-
Breeze.jl is a Julia package for finite volume GPU and CPU large eddy simulations (LES) of atmospheric flows.
8-
Under the hood, Breeze's abstractions, design, and finite volume engine are based on [Oceananigans](https://github.com/CliMA/Oceananigans.jl).
5+
Breeze provides software for flexible software package for finite volume atmosphere simulations on CPUs and GPUs, based on [Oceananigans](https://github.com/CliMA/Oceananigans.jl).
6+
Like Oceanaingans, it provides a radically productive user interface that makes simple simulations easy, and complex, creative simulations possible.
97

108
## Features
119

12-
Breeze provides two ways to simulate atmospheric flows:
10+
Breeze provides two ways to simulate moist atmospheres:
1311

1412
* A [`MoistAirBuoyancy`](@ref) that can be used with [Oceananigans](https://clima.github.io/OceananigansDocumentation/stable/)' [`NonhydrostaticModel`](https://clima.github.io/OceananigansDocumentation/stable/appendix/library/#Oceananigans.Models.NonhydrostaticModels.NonhydrostaticModel-Tuple{}) to simulate atmospheric flows with the Boussinesq approximation.
1513

@@ -43,7 +41,7 @@ Nx = Nz = 64
4341
Lz = 4 * 1024
4442
grid = RectilinearGrid(CPU(), size=(Nx, Nz), x=(0, 2Lz), z=(0, Lz), topology=(Periodic, Flat, Bounded))
4543
46-
reference_constants = Breeze.Thermodynamics.ReferenceConstants(base_pressure=1e5, potential_temperature=288)
44+
reference_constants = Breeze.Thermodynamics.ReferenceStateConstants(base_pressure=1e5, potential_temperature=288)
4745
buoyancy = Breeze.MoistAirBuoyancy(; reference_constants)
4846
4947
Q₀ = 1000 # heat flux in W / m²
@@ -58,7 +56,7 @@ model = NonhydrostaticModel(; grid, advection, buoyancy,
5856
tracers = (:θ, :q),
5957
boundary_conditions = (θ=θ_bcs, q=q_bcs))
6058
61-
Δθ = 5 # ᵒK
59+
Δθ = 2 # ᵒK
6260
Tₛ = reference_constants.reference_potential_temperature # K
6361
θᵢ(x, z) = Tₛ + Δθ * z / grid.Lz + 1e-2 * Δθ * randn()
6462
qᵢ(x, z) = 0 # 1e-2 + 1e-5 * rand()

docs/src/thermodynamics.md

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
# Atmosphere Thermodynamics
2+
3+
```@setup thermo
4+
using Breeze
5+
thermo = AtmosphereThermodynamics()
6+
```
7+
8+
Breeze implements thermodynamic relations for moist atmospheres ---
9+
fluids that can be described as a binary mixture of _(i)_ "dry" air, and _(ii)_ vapor,
10+
as well as liquid and solid condensates of the vapor component of various shapes and sizes.
11+
12+
On Earth, dry air is itself a mixture of gases, the vapor component is ``\mathrm{H_2 O}``,
13+
and the condensates comprise clouds and precipitation such as rain, snow, hail, and grapuel.
14+
Breeze models dry air as having a fixed composition with
15+
constant [molar mass](https://en.wikipedia.org/wiki/Molar_mass).
16+
Dry air on Earth's is mostly nitrogen, oxygen, and argon.
17+
18+
## Two laws for ideal gases
19+
20+
Both dry air and vapor are modeled as ideal gases, which means that
21+
the [ideal gas law](https://en.wikipedia.org/wiki/Ideal_gas_law) relates
22+
pressure ``p``, temperature ``T``, and density ``ρ``,
23+
24+
```math
25+
p = ρ R T .
26+
```
27+
28+
Above, ``R = ℛ / m`` is the specific gas constant given the
29+
[molar gas constant](https://en.wikipedia.org/wiki/Gas_constant)
30+
``ℛ ≈ 8.31 J / K / \mathrm{mol}`` and molar mass ``m`` of the gas species under consideration.
31+
32+
The [first law of thermodynamics](https://en.wikipedia.org/wiki/First_law_of_thermodynamics) applied to dry air,
33+
a.k.a. "conservation of energy", states that infinitesimal changes
34+
in internal energy ``\mathrm{d} ê`` are related to infinestimal changes
35+
in temperature ``\mathrm{d} T`` and pressure ``\mathrm{d} p`` according to
36+
37+
```math
38+
\mathrm{d} ê = cᵖᵈ \mathrm{d} T - \frac{\mathrm{d} p}{\rho}
39+
```
40+
41+
where ``cᵖᵈ`` is the specific heat capacity of dry air at constant pressure.
42+
43+
For example, to represent dry air typical for Earth, with ``m = 0.029`` and ``c^p = 1005``,
44+
we write
45+
46+
```@example thermo
47+
using Breeze.Thermodynamics: IdealGas
48+
dry_air = IdealGas(molar_mass=0.029, heat_capacity=1005)
49+
```
50+
51+
### Adiabatic transformations and potential temperature, ``θ``
52+
53+
Within adiabatic transformations, ``\mathrm{d} ê = 0``.
54+
Combining the ideal gas law with conservation of energy then yields
55+
56+
```math
57+
\frac{\mathrm{d} p}{\mathrm{d} T} = ρ cᵖᵈ = \frac{p}{R T} cᵖ \qquad \text{which implies} \qquad T ∼ \left ( \frac{p}{p₀} \right )^{R / cᵖ} .
58+
```
59+
60+
where ``p₀`` is some reference pressure. As a result, the _potential temperature_
61+
62+
```math
63+
θ ≡ T \left ( \frac{p₀}{p} \right )^{Rᵈ / cᵖᵈ} ,
64+
```
65+
66+
is constant under adiabatic transformations, defined such that ``θ(z=0) = T(z=0)``.
67+
68+
### Hydrostatic balance
69+
70+
Next we consider a reference state with constant internal energy and thus constant potential temperature
71+
72+
```math
73+
θ₀ = Tᵣ \left ( \frac{p₀}{pᵣ} \right )^{Rᵈ / cᵖᵈ}
74+
```
75+
76+
!!! note "About subscripts"
77+
Subscripts ``0`` typically indicate evaluated values.
78+
For example, in the above formula, ``p₀ ≡ pᵣ(z=0)``.
79+
Subscripts ``r`` indicate _reference_ states, which typically are
80+
functions of ``z``. This differs from the usual notation in which
81+
the subscripts ``0`` indicate "reference" (why?) and the "very referencey" ``00`` (lol) applies to ``z=0``.
82+
83+
84+
Hydrostatic balance requires
85+
86+
```math
87+
∂_z pᵣ = - ρᵣ g
88+
```
89+
90+
we get
91+
92+
```math
93+
\frac{pᵣ}{p₀} = \left (1 - \frac{g z}{cᵖ θ₀} \right )^(cᵖᵈ / Rᵈ)
94+
```
95+
96+
Thus
97+
98+
```math
99+
Tᵣ(z) = θ₀ \left ( \frac{pᵣ}{p₀} \right )^{Rᵈ / cᵖ} = θ₀ \left ( 1 - \frac{g z}{cᵖᵈ θ₀} \right )
100+
```
101+
102+
and
103+
104+
```math
105+
ρᵣ(z) = \frac{p₀}{R θ₀} \left ( 1 - \frac{g z}{cᵖ θ₀} \right )^{cᵖᵈ / Rᵈ - 1}
106+
```
107+
108+
```@example thermo
109+
thermo = AtmosphereThermodynamics()
110+
```
111+
112+
We can visualise the hydrostatic reference column implied by `thermo` by
113+
evaluating Breeze's reference-state utilities on a one-dimensional
114+
`RectilinearGrid`. Using Oceananigans `Field`s highlights how Breeze stores and
115+
accesses these background diagnostics.
116+
117+
```@example reference_state
118+
using Breeze
119+
using Breeze.Thermodynamics: reference_pressure, reference_density
120+
using CairoMakie
121+
122+
thermo = AtmosphereThermodynamics()
123+
constants = ReferenceStateConstants(base_pressure=101325, potential_temperature=288)
124+
grid = RectilinearGrid(size=160, z=(1, 12_000), topology=(Flat, Flat, Bounded))
125+
126+
pᵣ = CenterField(grid)
127+
ρᵣ = CenterField(grid)
128+
129+
set!(pᵣ, z -> reference_pressure(z, constants, thermo))
130+
set!(ρᵣ, z -> reference_density(z, constants, thermo))
131+
132+
Rᵈ = Breeze.Thermodynamics.dry_air_gas_constant(thermo)
133+
cᵖᵈ = thermo.dry_air.heat_capacity
134+
p₀ = constants.base_pressure
135+
θ₀ = constants.reference_potential_temperature
136+
g = thermo.gravitational_acceleration
137+
138+
# Verify that Tᵣ = θ₀ (1 - g z / (cᵖᵈ θ₀))
139+
z = KernelFunctionOperation{Center, Center, Center}(znode, grid, Center(), Center(), Center())
140+
Tᵣ₁ = Field(θ₀ * (pᵣ / p₀)^(Rᵈ / cᵖᵈ))
141+
Tᵣ₂ = Field(θ₀ * (1 - g * z / (cᵖᵈ * θ₀)))
142+
143+
fig = Figure(resolution = (900, 300))
144+
axT = Axis(fig[1, 1]; xlabel = "Temperature (K)", ylabel = "Height (m)")
145+
lines!(axT, Tᵣ₁)
146+
lines!(axT, Tᵣ₂)
147+
148+
axp = Axis(fig[1, 2]; xlabel = "Pressure (Pa)", ylabel = "")
149+
lines!(axp, pᵣ)
150+
151+
axρ = Axis(fig[1, 3]; xlabel = "Density (kg m⁻³)", ylabel = "")
152+
lines!(axρ, ρᵣ)
153+
154+
fig
155+
```
156+
157+
## Thermodynamic relations for gaseous mixtures
158+
159+
of gaseous dry air, water vapor, and liquid and solid condensates of various shapes and sizes.
160+
We assume that the volume of the condensates is negligible, which means that the total
161+
pressure is the sum of partial pressures of vapor and dry air,
162+
163+
```math
164+
p = pᵈ + pᵛ .
165+
```
166+
167+
The partial pressure of the dry air and vapor components are related to the component densities
168+
``ρᵈ`` and ``ρᵛ`` through the ideal gas law,
169+
170+
```math
171+
pᵈ = ρᵈ Rᵈ T \qquad \text{and} \qquad pᵛ = ρᵛ Rᵛ T
172+
```
173+
174+
where ``T`` is temperature, ``Rⁱ = ℛ / m^β`` is the specific gas constant for component ``β``,
175+
```` is the [molar or "universal" gas constant](https://en.wikipedia.org/wiki/Gas_constant),
176+
and ``m^β`` is the molar mass of component ``β``.
177+
178+
Central to Breeze's implementation of moist thermodynamics is a struct that
179+
holds parameters like the molar gas constant and molar masses,
180+
181+
```@example thermo
182+
thermo = AtmosphereThermodynamics()
183+
```
184+
185+
The default parameter evince basic facts about water vapor air typical to Earth's atmosphere:
186+
for example, the molar masses of dry air (itself a mixture of mostly nitrogen, oxygen, and argon),
187+
and water vapor are ``mᵈ = 0.029`` kg/mol and ``mᵛ = 0.018`` kg/mol.
188+
189+
To write the effective gas law for moist air, we introduce the mass ratios
190+
191+
```math
192+
qᵈ \equiv \frac{ρᵈ}{ρ} \qquad \text{and \qquad qᵛ \equiv \frac{ρᵛ}{ρ}
193+
```
194+
195+
where ``ρ`` is total density of the fluid including dry air, vapor, and condensates,
196+
``ρᵈ`` is the density of dry air, and ``ρᵛ`` is the density of vapor.
197+
It's then convenient to introduce the "mixture" gas constant ``Rᵐ(qᵛ)`` such that
198+
199+
```math
200+
p = ρ Rᵐ T \qquad \mathrm{where} \qquad Rᵐ ≡ qᵈ Rᵈ + qᵛ Rᵛ .
201+
```
202+
203+
In "clear" (not cloudy) air, we have that ``qᵈ = 1 - qᵛ``.
204+
More generally, ``qᵈ = 1 - qᵛ - qᶜ``, where ``qᶜ`` is the total mass
205+
ratio of condensed species. In most situations on Earth, ``qᶜ ≪ qᵛ``.
206+
207+
```@example thermo
208+
# Compute mixture properties for air with 0.01 specific humidity
209+
qᵛ = 0.01 # 1% water vapor by mass
210+
Rᵐ = mixture_gas_constant(qᵛ, thermo)
211+
```
212+
213+
We likewise define a mixture heat capacity via ``cᵖᵐ = qᵈ cᵖᵈ + qᵛ cᵖᵛ``,
214+
215+
216+
```@example thermo
217+
q = 0.01 # 1% water vapor by mass
218+
cᵖᵐ = mixture_heat_capacity(qᵛ, thermo)
219+
```
220+
221+
## The Clasuius-Claperyon relation and saturation specific humidity
222+
223+
The [Clausius-Claperyon relation](https://en.wikipedia.org/wiki/Clausius%E2%80%93Clapeyron_relation)
224+
for an ideal gas
225+
226+
```math
227+
\frac{\mathrm{d} pᵛ}{\mathrm{d} T} = \frac{pᵛ ℒ^β(T)}{Rᵛ T^2}
228+
```
229+
230+
where ``pᵛ`` is vapor pressure, ``T`` is temperature, ``Rᵛ`` is the specific gas constant for vapor,
231+
``ℒ^β(T)`` is the latent heat of the transition from vapor to the
232+
``β`` phase (e.g. ``l ≡ β`` for vapor → liquid and ``i ≡ β`` for vapor to ice).
233+
234+
For a thermodynamic formulation that uses constant (i.e. temperature-independent) specific heats,
235+
the latent heat of a phase transition is linear in temperature.
236+
For example, for phase change from vapor to liquid,
237+
238+
```math
239+
ℒˡ(T) = ℒˡ₀ + \big ( \underbrace{cᵖᵛ - cˡ}{≡Δcˡ} \big ) T
240+
```
241+
242+
where ``ℒˡ₀`` is the latent heat at ``T = 0``, with ``T`` in Kelvin.
243+
Integrate that to get
244+
245+
```math
246+
pᵛ^\dagger(T) = pᵗʳ \left ( \frac{T}{Tᵗʳ} \right )^{Δcˡ / Rᵛ} \exp \left \{ \frac{ℒˡ₀}{Rᵛ} \left (\frac{1}{Tᵗʳ} - \frac{1}{T} \right ) \right \}
247+
```
248+
249+
Consider parameters for liquid water,
250+
251+
```@example thermo
252+
using Breeze.Thermodynamics: CondensedPhase
253+
liquid_water = CondensedPhase(latent_heat=2500800, heat_capacity=4181)
254+
```
255+
256+
or water ice,
257+
258+
```@example thermo
259+
water_ice = CondensedPhase(latent_heat=2834000, heat_capacity=2108)
260+
```

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.ReferenceConstants{Float64}(101325, 20)
27+
= Breeze.ReferenceStateConstants{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ᵛ★)

0 commit comments

Comments
 (0)