-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add capability for user-defined parameter sets, with an example #172
Changes from 7 commits
58d207b
bb2f57d
bd77233
1616f72
6d8bc57
5005aed
29b219d
5d1ab4e
aa35ca8
2ba599d
6ec15d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,5 @@ docs/site/ | |
|
||
# Deps | ||
Manifest.toml | ||
|
||
*.jld2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# # Defining a simple parameter set and using it to compute density | ||
# | ||
# This script shows how to define a simple parameter set, and then using it to | ||
# compute density as a function of pressure, temperature, and humidity. | ||
|
||
# # Define parameters for computing the density of air | ||
# | ||
# First, we build a parameter set suitable for computing the density of | ||
# _moist_ air --- that is, a mixture of dry air, water vapor, liquid droplets, | ||
# and ice crystals (the latter two are called "condensates"). | ||
|
||
using Thermodynamics | ||
using Thermodynamics.Parameters: AbstractThermodynamicsParameters | ||
|
||
struct ConstitutiveParameters{FT} <: AbstractThermodynamicsParameters{FT} | ||
gas_constant :: FT | ||
dry_air_molar_mass :: FT | ||
water_molar_mass :: FT | ||
end | ||
|
||
""" | ||
ConstitutiveParameters(FT; gas_constant = 8.3144598, | ||
dry_air_molar_mass = 0.02897, | ||
water_molar_mass = 0.018015) | ||
|
||
Construct a set of parameters that define the density of moist air, | ||
|
||
```math | ||
ρ = p / Rᵐ(q) T, | ||
``` | ||
|
||
where ``p`` is pressure, ``T`` is temperature, ``q`` defines the partition | ||
of total mass into vapor, liqiud, and ice mass fractions, and | ||
``Rᵐ`` is the effective specific gas constant for the mixture, | ||
|
||
```math | ||
Rᵐ(q) = | ||
``` | ||
|
||
where | ||
|
||
For more information see [reference docs]. | ||
""" | ||
function ConstitutiveParameters(FT = Float64; | ||
gas_constant = 8.3144598, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had some discussions a while back with SEs asking if the parameters constructors should come with default values. The feedback I got was leaning towards not having the defaults, unless it is really beneficial to the structure of the code. From what I understood the defaults should live in a toml file. - I don't have any opinions on this personally. More of a question of convention and how much we want to stick to it in different examples There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a "classroom example" that illustrates some thermodynamic calculations. I think we want to include examples like this in the docs as well to form a link between theromdynamic theory explained in the docs and functions that one calls to make thermodynamic calculations. This example is not intended to illustrate how to use this package within an Earth system model. So, I understand the validity of the argument to avoid defaults in parameter structs that are designed for use in ClimaAtmos. However, this parameter struct is not for ClimaAtmos or an Earth system model --- it's just for this small example. For this use case, it's nice to have the defaults here so that we can read this file and understand what the code is doing. The same applies for code snippets embedded in the documentation, I think. |
||
dry_air_molar_mass = 0.02897, | ||
water_molar_mass = 0.018015) | ||
|
||
return ConstitutiveParameters(convert(FT, gas_constant), | ||
convert(FT, dry_air_molar_mass), | ||
convert(FT, water_molar_mass)) | ||
end | ||
|
||
# Next, we define functions that return: | ||
# 1. The specific gas constant for dry air | ||
# 2. The specific gas constant for water vapor | ||
# 3. The ratio between the dry air molar mass and water molar mass | ||
|
||
const VTP = ConstitutiveParameters | ||
using Thermodynamics: Parameters | ||
|
||
Parameters.R_d(p::VTP) = p.gas_constant / p.dry_air_molar_mass | ||
Parameters.R_v(p::VTP) = p.gas_constant / p.water_molar_mass | ||
Parameters.molmass_ratio(p::VTP) = p.dry_air_molar_mass / p.water_molar_mass | ||
|
||
# # The density of dry air | ||
|
||
import Thermodynamics as AtmosphericThermodynamics | ||
|
||
# To compute the density of dry air, we first build a default | ||
# parameter set, | ||
|
||
parameters = ConstitutiveParameters() | ||
|
||
# Next, we construct a phase partitioning representing dry air --- the trivial | ||
# case where the all mass ratios are 0. | ||
# | ||
# Note that the syntax for `PhasePartition` is | ||
# | ||
# ``` | ||
# PhasePartition(q_total, q_liquid, q_ice) | ||
# ``` | ||
# where the q's are mass fractions of total water components, | ||
# liquid droplet condensate, and ice crystal condensate. | ||
|
||
q_dry = AtmosphericThermodynamics.PhasePartition(0.0) | ||
|
||
# Finally, we define the pressure and temperature, which constitute | ||
# the "state" of our atmosphere, | ||
|
||
p₀ = 101325.0 # sea level pressure in Pascals (here taken to be mean sea level pressure) | ||
T₀ = 273.15 # temperature in Kelvin | ||
|
||
# Note that the above must be defined with the same float point precision as | ||
# used for the parameters and PhasePartition. | ||
# We're now ready to compute the density of dry air, | ||
|
||
ρ = air_density(parameters, T₀, p₀, q_dry) | ||
|
||
@show ρ | ||
|
||
# Note that the above must be defined with the same float point precision as | ||
# used for the parameters and PhasePartition. | ||
# We're now ready to compute the density of dry air, | ||
|
||
using JLD2 | ||
|
||
# Next, we load an atmospheric state correpsonding to atmospheric surface | ||
# variables substampled from the JRA55 dataset, from the date Jan 1, 1991: | ||
|
||
@load "JRA55_atmospheric_state_Jan_1_1991.jld2" q T p | ||
nothing | ||
|
||
# The variables q, T and p correspond to the total specific humidity (a mass fraction), | ||
# temperature (Kelvin), and sea level pressure (Pa) | ||
# | ||
# We use q to build a vector of PhasePartition, | ||
|
||
qp = PhasePartition.(q) | ||
|
||
# And then compute the density using the same parameters as before: | ||
|
||
ρ = air_density.(parameters, T, p, qp) | ||
|
||
# Finally, we plot the density as a function of temperature and specific humidity, | ||
|
||
using CairoMakie | ||
|
||
## Pressure range, centered around the mean sea level pressure defined above | ||
pmax = maximum(abs, p) | ||
dp = 3/4 * (pmax - p₀) | ||
prange = (p₀ - dp, p₀ + dp) | ||
pmap = :balance | ||
|
||
## Compute temperature range | ||
Tmin = minimum(T) | ||
Tmax = maximum(T) | ||
Trange = (Tmin, Tmax) | ||
Tmap = :viridis | ||
|
||
fig = Figure(size=(1200, 500)) | ||
|
||
axρ = Axis(fig[2, 1], xlabel="Temperature (K) ", ylabel="Density (kg m⁻³)") | ||
axq = Axis(fig[2, 2], xlabel="Specific humidity", ylabel="Density (kg m⁻³)") | ||
|
||
scatter!(axρ, T[:], ρ[:], color=p[:], colorrange=prange, colormap=pmap, alpha=0.1) | ||
scatter!(axq, q[:], ρ[:], color=T[:], colorrange=Trange, colormap=Tmap, alpha=0.1) | ||
|
||
Colorbar(fig[1, 1], label="Pressure (Pa)", vertical=false, colorrange=prange, colormap=pmap) | ||
Colorbar(fig[1, 2], label="Temperature (K)", vertical=false, colorrange=Trange, colormap=Tmap) | ||
|
||
save(fig, "density_versus_temperature.png") | ||
|
||
display(fig) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why don't we literate it and add it in the docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I asked about in the original post