Skip to content

Commit a3e1cf1

Browse files
committed
scripts for creating, running, and plotting ensembles of EDMF+1M/2M
1 parent 957694a commit a3e1cf1

File tree

10 files changed

+1084
-3
lines changed

10 files changed

+1084
-3
lines changed

docs/make.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ include(joinpath(@__DIR__, "src", "config_table.jl"))
1010
doctest(ClimaAtmos; plugins = [bib])
1111
disable_logging(Base.CoreLogging.BelowMinLevel) # Re-enable all logging
1212

13-
example_pages = ["Perturbation Experiments" => "perturbation_experiments.md"]
13+
example_pages = ["Perturbation Experiments" => "perturbed_dycoms_rf02.md"]
1414

1515
makedocs(;
1616
plugins = [bib],
@@ -26,7 +26,7 @@ makedocs(;
2626
),
2727
pages = [
2828
"Home" => "index.md",
29-
"Installation instructions" => "installation_instructions.md",
29+
"Installation Instructions" => "installation_instructions.md",
3030
"Contributor Guide" => "contributor_guide.md",
3131
"Equations" => "equations.md",
3232
"EDMF Equations" => "edmf_equations.md",

docs/src/assets/Hoffmann2020.jpg

177 KB
Loading

docs/src/installation_instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Installation instructions
1+
# Installation Instructions
22

33
ClimaAtmos.jl is a [registered Julia package](https://julialang.org/packages/). To install
44

docs/src/perturbed_dycoms_rf02.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
2+
# Perturbed Initial Conditions Ensembles
3+
With a bit of setup, ClimaAtmos can be used to run ensembles of perturbed initial conditions. Here, we will use perturbed DYCOMS-RF02 initial conditions to create a spread of simulated marine stratocumulus clouds in a single column configuration with the EDMF scheme and 2-moment microphysics. As an example, we will create something similiar to the figure below (Hoffmann, 2020) using ClimaAtmos.
4+
5+
![](assets/Hoffmann2020.jpg)
6+
7+
# Creating Custom Config Arguments
8+
In cases where it is of interest to run ensembles of the same model configuration but with perturbed initial conditions, it may be of use to add new keyword arguments to the .yml file. The idea is to allow modifications to initial conditions to be passed directly through the .yml file and then creating multiple copies of the files but with different variations and combinations of these parameters.
9+
10+
As an example, we will explore modifying the total water mixing ratio (`q_tot_0`), liquid-ice potential temperature profiles (`theta_0` & `theta_i`), and initial boundary layer height (`z_i`) in the DYCOMS-RF02 simulation setup.
11+
12+
### Modify initial_conditions.jl
13+
To start, we need to go to the part of the source code responsible for defining the initial conditions. For both of the DYCOMS research flight simulation setups, the total water mixing ratio and liquid-ice potential temperature are pulled from another library called AtmosphericProfilesLibrary (APL).
14+
15+
There are many ways to modify the functions to allow for perturbation to the initial conditions, but for the sake of this example we will overwrite the function signature with our own version where we can pass in our own custom profiles. We must import the functions from the APL library and then define a new function with the same signature. This looks like:
16+
17+
```
18+
import AtmosphericProfilesLibrary: Dycoms_RF02_θ_liq_ice, Dycoms_RF02_q_tot
19+
[...]
20+
21+
# Redefine the profile functions here. Here we redefine the functions such that
22+
# we can pass in our own values for q_tot and θ_liq_ice, as well as modifying
23+
# the initial boundary layer height.
24+
25+
""" [Ackerman2009](@cite) """
26+
Dycoms_RF02_θ_liq_ice(::Type{FT}, theta_0, theta_i, z_i) where {FT} =
27+
APL.ZProfile(z -> if z <= z_i
28+
FT(theta_0)
29+
else
30+
FT(theta_i) + (z - FT(z_i))^FT(1.0 / 3.0)
31+
end)
32+
33+
""" [Ackerman2009](@cite) """
34+
Dycoms_RF02_q_tot(::Type{FT}, q_tot_0, z_i) where {FT} =
35+
APL.ZProfile(z -> if z <= z_i
36+
FT(q_tot_0) / FT(1000.0)
37+
else
38+
(FT(5) - FT(3) * (FT(1) - exp(-(z - FT(z_i)) / FT(500)))) / FT(1000)
39+
end)
40+
```
41+
42+
Now that we have redefined our functions, we can pass these new functions to our intial conditions structure for the DYCOMS-RF02 setup. Here we can also begin to define our new keyword arguments that will serve as inputs for the new functions we defined. In the `DYCOMS_RF02` structure, we add the names of our new inputs:
43+
44+
```
45+
Base.@kwdef struct DYCOMS_RF02 <: InitialCondition
46+
prognostic_tke::Bool = false
47+
q_tot_0_dycoms_rf02 # Define total water mixing ratio.
48+
theta_0_dycoms_rf02 # Define liquid-ice θ at surface.
49+
theta_i_dycoms_rf02 # Define liquid-ice θ at initial boundary layer height.
50+
z_i_dycoms_rf02 # Define initial boundary layer height.
51+
end
52+
53+
for IC in (:Dycoms_RF01, :Dycoms_RF02)
54+
IC_Type = Symbol(uppercase(string(IC)))
55+
θ_func_name = Symbol(IC, :_θ_liq_ice)
56+
q_tot_func_name = Symbol(IC, :_q_tot)
57+
[...]
58+
if IC == :Dycoms_RF02
59+
@eval function (initial_condition::$IC_Type)(params)
60+
(; prognostic_tke, q_tot_0_dycoms_rf02, theta_0_dycoms_rf02, theta_i_dycoms_rf02, z_i_dycoms_rf02) = initial_condition #unpack the new arguments here. These arguments will be provided through the model .yml file.
61+
FT = eltype(params)
62+
[...]
63+
θ = $θ_func_name(FT, FT(theta_0_dycoms_rf02), FT(theta_i_dycoms_rf02), FT(z_i_dycoms_rf02)) # Change function signature here.
64+
q_tot = $q_tot_func_name(FT, FT(q_tot_0_dycoms_rf02), FT(z_i_dycoms_rf02)) # Change function signature here.
65+
end
66+
else
67+
[...]
68+
end
69+
end
70+
```
71+
72+
### Add Keywords to .yml & Modify default_config.yml
73+
Now that we have added a new keyword argument to be used in initial conditions, it is time to define the keyword argument in the YAML files responsible for configuring the model runs. In the YAML file for running a DYCOMS-RF02 single column experiment, we add and adjust the following lines:
74+
75+
```
76+
q_tot_0_dycoms_rf02: 9.45 # Define variables here.
77+
theta_0_dycoms_rf02: 288.3 # Define variables here.
78+
theta_i_dycoms_rf02: 295.0 # Define variables here.
79+
z_i_dycoms_rf02: 795.0 # Define variables here.
80+
```
81+
82+
These are the same default values being used by the original APL function, but we can modify it in the YAML file if we want to test different initial conditions. Additionally, we need to provide a backup default value in the case that the keyword argument is not used or provided in the setup YAML file. To do this, we go to default_config.yml and add the following:
83+
84+
```
85+
q_tot_0_dycoms_rf02: # Additional variables here.
86+
help: "Surface total water mixing ratio for DYCOMS RF02."
87+
value: 9.45 # Default value.
88+
theta_0_dycoms_rf02: # Additional variables here.
89+
help: "Surface liquid-ice potential temperature for DYCOMS RF02."
90+
value: 288.3 # Default value.
91+
theta_i_dycoms_rf02: # Additional variables here.
92+
help: "Initial boundary layer liquid-ice potential temperature for DYCOMS RF02."
93+
value: 295.0 # Default value.
94+
z_i_dycoms_rf02: # Additional variables here.
95+
help: "Initial boundary layer height."
96+
value: 795.0 # Default value.
97+
```
98+
99+
### Modify type_getters.jl
100+
Finally, we need to update type_getters.jl with our new keyword arguments as well. We find where DYCOMS-RF02 is specified and then we replace it with a separate block that looks like:
101+
102+
```
103+
[...]
104+
elseif parsed_args["initial_condition"] == "DYCOMS_RF02"
105+
return getproperty(ICs, Symbol(parsed_args["initial_condition"]))(
106+
parsed_args["prognostic_tke"],
107+
parsed_args["q_tot_0_dycoms_rf02"], # Add parsed args here.
108+
parsed_args["theta_0_dycoms_rf02"], # Add parsed args here.
109+
parsed_args["theta_i_dycoms_rf02"], # Add parsed args here.
110+
parsed_args["z_i_dycoms_rf02"], # Add parsed args here.
111+
)
112+
[...]
113+
```
114+
115+
With this step complete, we are now ready to pass a new keyword argument that we defined ourselves to modify or change the initial conditions. The scripts currently contained in /examples/perturbed_dycoms_rf02 contain code for running the ensemble as well as post-processing/plotting capabilities.
116+
117+
# Creating & Running Ensembles
118+
All example scripts referenced are stored in examples/perturbed_dycoms_rf02:
119+
120+
```
121+
script_folder = joinpath(pkgdir(CA), "examples", "perturbed_dycoms_rf02")
122+
```
123+
124+
The script `pipeline.jl` handles this entire step without parallelized runs, but the steps can also be performed individually.
125+
126+
The recommended workflow is the create multiple copies of the .yml files and label them according to the initial conditions used. For example, the default file might look like `prognostic_edmfx_dycoms_rf02_column_qtot0_9.45_theta0_288.3_thetai_295.0_zi_795.0_prescribedN_1.0e8.yml`.
127+
128+
We can use `make_yaml.jl` to generate multiple different configurations with ease. To do so, provide the function `make_yamls()` with a default file for which to copy, and an output path. This script uses nested for loops to generate every possible combination of parameters, and they can be altered directly in the file. This might look like:
129+
130+
```
131+
include(joinpath(script_folder, "make_yaml.jl"))
132+
133+
default_data_path = default_prog_2M
134+
output_dir = prognostic_2M_config
135+
make_yamls(default_data_path, output_dir, is_prog=true)
136+
```
137+
138+
To use these configuration files, we can either parallelize or run them serially. If parallelization is desired, run:
139+
140+
```
141+
julia -p <num_processors> ./examples/perturbed_dycoms_rf02/parallel_driver.jl
142+
```
143+
144+
Be sure that the paths, which are handled by `all_paths.jl`, are referring to the right set of simulations. This example focuses in on prognostic EDMF+2M simulations, but the scripts have implementation for diagnostic EDMF as well as 1-moment microphysics as well.
145+
146+
# Visualizing Ensemble Output
147+
After the simulations are done being ran, we can use `process_plot_outputs.jl` to visualize changes in the liquid water path (LWP) and cloud droplet number concentration (N), in order to recreate the figure above.
148+
149+
```
150+
include(joinpath(script_folder, "process_plot_outputs.jl"))
151+
152+
output_dir = "./output" #output_2M
153+
all_outputs = make_edmf_vec(output_dir)
154+
filtered = filter_runs(all_outputs)
155+
# sampled = StatsBase.sample(filtered, 40; replace=false)
156+
157+
fig = plot_edmf(filtered, is_1M=false, is_time=false)
158+
```
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Dependencies
2+
import ClimaAtmos as CA
3+
4+
base_path = joinpath(pkgdir(CA), "LWP_N")
5+
mkpath(base_path)
6+
7+
base_reference_path = joinpath(base_path, "references")
8+
mkpath(base_reference_path)
9+
10+
default_diag_1M = joinpath(
11+
pkgdir(CA),
12+
base_reference_path,
13+
"diagnostic_edmfx_dycoms_rf02_column_1M.yml",
14+
)
15+
default_diag_2M = joinpath(
16+
pkgdir(CA),
17+
base_reference_path,
18+
"diagnostic_edmfx_dycoms_rf02_column_2M.yml",
19+
)
20+
default_prog_1M = joinpath(
21+
pkgdir(CA),
22+
base_reference_path,
23+
"prognostic_edmfx_dycoms_rf02_column_1M.yml",
24+
)
25+
default_prog_2M = joinpath(
26+
pkgdir(CA),
27+
base_reference_path,
28+
"prognostic_edmfx_dycoms_rf02_column_2M.yml",
29+
)
30+
31+
diagnostic_1M_config = joinpath(pkgdir(CA), base_path, "config_diagnostic_1M")
32+
diagnostic_2M_config = joinpath(pkgdir(CA), base_path, "config_diagnostic_2M")
33+
prognostic_1M_config = joinpath(pkgdir(CA), base_path, "config_prognostic_1M")
34+
prognostic_2M_config = joinpath(pkgdir(CA), base_path, "config_prognostic_2M")
35+
36+
mkpath(diagnostic_1M_config)
37+
mkpath(diagnostic_2M_config)
38+
mkpath(prognostic_1M_config)
39+
mkpath(prognostic_2M_config)
40+
41+
output_1M = joinpath(pkgdir(CA), base_path, "output_1M")
42+
output_2M = joinpath(pkgdir(CA), base_path, "output_2M")

0 commit comments

Comments
 (0)