Skip to content

Commit

Permalink
Adding homogeneous J example plot
Browse files Browse the repository at this point in the history
  • Loading branch information
amylu00 committed Jul 21, 2023
1 parent 6ee0abd commit 64e2de8
Show file tree
Hide file tree
Showing 14 changed files with 549 additions and 232 deletions.
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ version = "0.11.1"
CLIMAParameters = "6eacf6c3-8458-43b9-ae03-caf5306d3d53"
CUDAKernels = "72cfdca4-0801-4ab0-bf6a-d52aa10adc57"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c"

Expand All @@ -17,7 +17,7 @@ CLIMAParameters = "0.7"
CUDAKernels = "0.4"
DocStringExtensions = "0.8, 0.9"
ForwardDiff = "0.10"
KernelAbstractions = "0.8"
KernelAbstractions = "0.8, 0.9"
SpecialFunctions = "1, 2"
Thermodynamics = "0.9, 0.10"
julia = "1.5"
40 changes: 33 additions & 7 deletions docs/bibliography.bib
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,19 @@ @article{Kirkby2016
year = {2016}
}

@Article{KnopfAlpert2013,
author = "Knopf, Daniel A. and Alpert, Peter A.",
title = "A water activity based model of heterogeneous ice nucleation kinetics for freezing of water and aqueous solution droplets",
journal = "Faraday Discuss.",
year = "2013",
volume = "165",
issue = "0",
pages = "513-534",
publisher = "The Royal Society of Chemistry",
doi = "10.1039/C3FD00035D",
url = "http://dx.doi.org/10.1039/C3FD00035D",
}

@article{Koop2000,
author = {Koop, Thomas and al, et},
title = {Water activity as the determinant for homogeneous ice nucleation in aqueous solutions},
Expand Down Expand Up @@ -439,14 +452,15 @@ @article{TripoliCotton1980
doi = {10.1175/1520-0450(1980)019<1037:ANIOSF>2.0.CO;2}
}

@article{Tully2022,
@article{Tully2023,
title = {Assessing predicted cirrus ice properties between two deterministic ice formation parameterizations},
author = {Tully, C. and Neubauer, D. and Lohmann, U.},
title = {Technical Note: assessing predicted cirrus ice properties between two deterministic ice formation parameterizations},
journal = {EGUsphere},
volume = {2022},
year = {2022},
pages = {1--25},
doi = {10.5194/egusphere-2022-1057}
journal = {Geoscientific Model Development},
volume = {16},
year = {2023},
number = {10},
pages = {2957--2973},
doi = {10.5194/gmd-16-2957-2023}
}

@article{Vehkamaki2002,
Expand Down Expand Up @@ -492,3 +506,15 @@ @article{SeifertBeheng2006
year={2006},
publisher={Springer}
}

@Article{Spichtinger2023,
AUTHOR = {Spichtinger, P. and Marschalik, P. and Baumgartner, M.},
TITLE = {Impact of formulations of the homogeneous nucleation rate on ice nucleation events in cirrus},
JOURNAL = {Atmospheric Chemistry and Physics},
VOLUME = {23},
YEAR = {2023},
NUMBER = {3},
PAGES = {2035--2060},
URL = {https://acp.copernicus.org/articles/23/2035/2023/},
DOI = {10.5194/acp-23-2035-2023}
}
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pages = Any[
"Precipitation Susceptibility" => "PrecipitationSusceptibility.md",
"API" => "API.md",
"References" => "References.md",
"Developer's Guide" => "DevelopersGuide.md",
]

mathengine = MathJax(
Expand Down
194 changes: 194 additions & 0 deletions docs/src/DevelopersGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Developer's Guide

This doc is meant to guide any beginner developers with organization
and common surprises associated with contributing to `CloudMicrophysics.jl`.

## GitHub workflow

We work on branches of the main repository, rather than personal forks.
Each branch name should ideally start with your initials, followed by short
name relevant to what will be added in that branch.
It is considered a good practice to open an issue that describes the planned work
before starting the implementation.
This allows you to get feedback from other developers and advertise the planned work to the group.
When the implemented code is ready for review, create a pull request (PR) on GitHub
and tag the issue it is addressing.
In the PR you can describe which parts of the relevant issue are solved and what needs to
be done to reach that goal.
After creating a PR on GitHub, you will find that every following commit & push you make
will go through continuous integration (CI) checks:

- The, `ci / ci 1.8.1 ` runs the tests on Ubuntu, Windows and OSX machines
using GitHub Actions.
The tests include some unit tests and very simple performance tests.
It may happen that the tests pass on your local machine,
but fail on one of the machines provided in the cloud for the CI.
This is especially true for the performance tests,
as the execution times may vary a lot depending on the machine.
The CI is the source of truth over local tests, and all tests must pass in the CI before code can be merged.
See the [Tests](https://clima.github.io/CloudMicrophysics.jl/dev/DevelopersGuide/#Tests)
section below for debugging tips.
- `CloudMicrophysics.jl` has a small set of tests that are run on the GPUs using `buildkite`.
They are triggered automatically when trying to merge a PR,
or manually by commenting `bors try` on your PR.
If the GPU tests fail, check first for use of any "out of place" functions
(for example, `@warn` will not work on GPU).
- The `Documentation / docbuild` builds the documentation.
Most common errors in the docbuild are related to missing docstrings,
see the [Documentation](https://clima.github.io/CloudMicrophysics.jl/dev/DevelopersGuide/#Documentation) section for hints.
This check may fail if the source code itself is not compiling,
so please handle the compilation errors first.
If the documentation build was successful, the `documenter/deploy`
will display the documentation page based on the PR (click on the details).
- The `JuliaFormatter / format` ensures consistent formatting throughout the repository.
If this check fails, you can click on details
to see which files are not following the formatting rules.
You can apply the formatter by running
`julia --project=.dev .dev/climaformat.jl file_name` in the terminal.
The formatter might break if the code does not compile,
so make sure you handle the compilation errors first.
- The `codecov` shows what percentage of source code lines are exercised inside the tests.
We strive to keep the test coverage high, but this test is not strictly required to pass before merging a PR.

Once the PR is opened, you can request reviewers to look over and approve your work.
To keep things tidy, we want PRs to have very few (ideally only one) commit
before merging to the main branch.
You can squash and rebase multiple commits into one
in your favorite editor (VS Code, Vim etc).
In case of doubt, see Git tutorials on how to squash and rebase
or reach out to other developers in our team.
The first couple of times it pays off to create a "backup branch" before starting your rebase,
and then comparing afterwards if the rebased and backup branches are identical.
If the main branch has been updated after your branch was created,
you will need to rebase onto the main branch. To do this,
run git rebase origin/main and solve any conflicts manually.
This is done more easily if you only have one commit.

We (still) use `bors` bot to manage PR queues.
To merge your PR into the `main` branch, comment `bors r+` and wait for all checks to pass.

The easiest way to use the new additions in the `main` branch of `CloudMicrophysics.jl`
from another package is to do a package release.
To do that, you need to change the package version in the `Project.toml`.
We do a patch release if the API did not change (for example `0.11.1 -> 0.11.2`)
and a minor version release if it did (for example `0.11.1 -> 0.12.0`).
We use the `JuliaRegistrator` bot to register new versions in the `Julia` package ecosystem,
by commenting `@JuliaRegistrator register` under the merged PR that changes the package version.
You can also use specific branches from the repository,
if you are working with commits that were not yet merged into `main`.
See [Pkg.jl docs](https://pkgdocs.julialang.org/v1/managing-packages/#Adding-registered-packages)
for more details.

## New contributions

`CloudMicrophysics.jl` is a collection of point-wise functions grouped
in different modules depending on which aspect of aerosol and cloud microphysics
they address.
When adding a new function, decide if a new module is required, or add to the existing one.
If the added function will be used in other modules, export the function. Avoid exporting
functions that are only used within the module it is defined in or functions that have the
same name as a function already in the API.
Avoid shortening words or phrases when naming new functions. The name of the function
should be self-explanatory yet brief.
All functions should have docstrings describing the API, as well as
documentation focusing on the scientific aspects of what they do.
All functions should have their own unit, performance and GPU tests.
All free parameters should be stored in [CLIMAParameters](https://github.com/CliMA/CLIMAParameters.jl).
It is usually faster to prototype defining the free parameters locally,
and move them to `CLIMAParameters` at a last step.

Other files that may require editing after you make a new function are:
- `CloudMicrophysics.jl` (found in `src` folder) if you need to include a new source file,
- `index.md` (found in `docs/src` folder) if you want to mention the new additions in the main documentation page,
- `make.jl` (found in `docs` folder) if you are adding a new file to the documentation,
- `API.md` (found in `docs/src` folder) if you are adding functions (see ``Documentation`` section for more details),
- `runtests.jl` (found in `test` folder) if you are adding new test file for unit tests.

## Documentation

Each new addition to the library should be accompanied by a documentation page
summarizing the derivation/assumptions and it's potential uses.
If possible, it's really appreciated to also add short code snippets that reproduce
results from the literature.
Those code snippets are executed every time documentation is build and provide
great examples on how different available parameterizations work.

Additionally, each function in the source code is required to have a docstring
and should be added to the `API` documentation page.
The docstring formatting is pretty strict:
(i) no empty lines between the docstring and the function/module,
(ii) docstring starts and ends with a line consisting of three quotation marks,
(iii) the second line has the function's name preceded by exactly 4 spaces.
Examples can be found in the source files.
Be sure to add the function to `API.md` in the docs folder.
Missing docstrings and functions in the API cause the documentation build to break.

To build and work the documentation locally, you can use [LiveServer.jl](https://github.com/tlienart/LiveServer.jl#serve-docs). This will compile the documentation to a local server that is updated whenever you make changes.
Alternatively, you can save the documentation to a static webpage: `julia --project=docs docs/make.jl`.
The index page will be saved in `docs/build` folder.

## Tests

### Unit Tests

Unit tests aim to ensure that parameterizations make physical sense.
They can be found in the `test` folder under files named after corresponding source files.
If you create a new function, please also create a new test that checks it.
If creating a new file for unit tests, make sure you import `Test` and any other necessary libraries.
There is some boilerplate code needed to create the set with free parameters
based on the default `toml` file from [CLIMAParameters](https://github.com/CliMA/CLIMAParameters.jl).
This would be the `include(joinpath(pkgdir(CM), "test", "create_parameters.jl"))` line found in
the existing unit tests.

Some possible tests include checking if the returned values agree with values
in the literature, if something is smaller/greater at warmer/cooler
temperatures, if assertion errors are returned when a function is used outside its
valid range of parameters, or if a function is zero at certain input values.
In general, writing good tests is difficult and we are always on the lookout for new good candidates.
We strive to exercise all functions in some way in tests,
so that at minimum we can catch changes in the API.

You can run the tests locally: `julia --project=test test/runtests.jl`.

### Performance Tests

Performance tests check the memory allocations (there should be none) and execution times
of some of the functions.
They are found in the `test` folder under a single file named `performance_tests.jl`.

You can add performance tests for your new functions in the `benchmark_test(FT)` function.
Parameters are listed at the start of the function.
Next, add your performance tests under the comment with the file name which your
new function is in.
This is done by calling `bench_press(function_name, (Parameter1, Parameter2), min_time)`.
The last argument is an estimate of the minimum run-time of the function.
It may take some trial and error to find a number
that will satisfy the `ci` tests that are run on different operating systems
and for different floating-point precision.
You can identify which specific performance test is failing on GitHub
by clicking on details next to a failed check from one of the `ci / ci 1.8.1` checks.
Adjust the estimated minimum run-time appropriately.

### GPU Tests

Graphic Processing Unit (GPU) tests check that `CloudMicrophysics.jl` functions are able to run on GPU.
They are as simple as checking that a certain input returns a known value.
Right now, we do not test the whole library on the GPUs,
so the support is limited.
GPU tests can be found in the `test` folder under a single file named `gpu_tests.jl`.
There are two things to add: a kernel for your function and the actual test.

Kernels are added at the top half of the file using `@kernel`.
The naming convention follows `test_<function name>_kernel!`.
Within the kernel, use `@inbounds` to place any outputs into an output array.

The test itself should be added in the `test_gpu(FT)` function.
The test starts with defining `data_length` and ends after an `@test` macro.
`data_length` corresponds to the number of outputs your function has.
Add an array for each input required by your function.
For example, if you want to test at temperature of 230K,
you can add `T = ArrayType([FT(230)])`.
Add a comment of what you are testing and use `@test` to create your test.
The GPU tests are ran twice: for `Float64` and `Float32`.
Similar as with performance tests, some trial and error is needed
to find good tolerances for both options.
85 changes: 85 additions & 0 deletions docs/src/HomFreezingPlots.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import OrdinaryDiffEq as ODE
import CairoMakie as MK

import Thermodynamics as TD
import CloudMicrophysics as CM
import CLIMAParameters as CP

const CMT = CM.CommonTypes
const CMO = CM.Common
const CMI_het = CM.HetIceNucleation
const CMI_hom = CM.HomIceNucleation
const CMP = CM.Parameters

include(joinpath(pkgdir(CM), "test", "create_parameters.jl"))
FT = Float64
toml_dict = CP.create_toml_dict(FT; dict_type = "alias")
const prs = cloud_microphysics_parameters(toml_dict)
thermo_params = CMP.thermodynamics_params(prs)

# Initializing
temp = collect(230.0:0.25:239.5) # air temperature
x_sulph = 0.027 # wt% sulphuric acid in droplets
Delta_a = Vector{Float64}(undef, length(temp))
J = Vector{Float64}(undef, length(temp))

it = 1
for T in temp
Delta_a[it] = CMO.Delta_a_w(prs, x_sulph, T)
J[it] = CMI_hom.homogeneous_J(Delta_a[it])
global it += 1
end
log10J = @. log10(J)

fig = MK.Figure(resolution = (800, 600))
ax1 = MK.Axis(
fig[1, 1],
ylabel = "log10(J) with J in SI units",
xlabel = "Temperature (K)",
title = "Homogeneous Ice Nucleation Rate",
)

Koop2000_temp = [
230.0139,
231.0179,
231.9860,
233.00797,
234.01195,
235.01594,
236.00199,
237.16733,
238.51195,
239.51594,
240.46613,
241.30876,
241.91832,
242.33068,
242.74303,
243.10159,
]
Koop2000_log10J = [
24.33735,
22.6506,
21.0843,
19.5181,
18.072289,
16.626506,
15.24096,
13.493975,
11.26506,
9.39759,
7.349397,
5.3012,
3.674698,
2.4698,
1.1445783,
0,
]

MK.lines!(ax1, temp, log10J, label = "CliMA")
MK.lines!(ax1, Koop2000_temp, Koop2000_log10J, label = "Koop 2000")

# MK.Legend(fig[1, 1], ["CliMA", "Koop 2000"])
fig[1, 2] = MK.Legend(fig, ax1, "Legend", framevisible = false)

MK.save("homogeneous_J.svg", fig)
18 changes: 17 additions & 1 deletion docs/src/IceNucleation.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ The nucleation rate coefficient is determined with the cubic function from [Koop
```
This parameterization is valid only when ``0.26 < \Delta a_w < 0.36`` and ``185K < T < 235K``.

## ABIFM Example Figures
## Example Figures
### ABIFM Ice Nucleation Rate Coefficient
Plotted below is the ABIFM derived ice nucleation rate alongside data and a parameterization from [KnopfAlpert2013](@cite).
```@example
import Plots
Expand Down Expand Up @@ -170,3 +172,17 @@ PL.plot!(KA13_Delta_a_param, KA13_log10J_param, linecolor = :red, label="paper p
PL.savefig("Knopf_Alpert_fig_1.svg")
```
![](Knopf_Alpert_fig_1.svg)

### Homogeneous Ice Nucleation Rate Coefficient
Here is a comparison of our parameterization of ``J_{hom}`` compared to Koop 2000 as
plotted in figure 1 of [Spichtinger2023](@cite). Our parameterization differs in the calculation
of ``\Delta a_w``. We define water activity to be a ratio of saturated vapor pressures whereas
Koop 2000 uses the difference in chemical potential. It should be noted that the Koop 2000
parameterization is only valid for temperatures up to 240K and a temperature-dependent max
pressure. The max valid pressure becomes negative around 237K, so the Koop 2000 parameterizaiton
should not be valid beyond 237K.

```@example
include("HomFreezingPlots.jl")
```
![](homogeneous_J.svg)
Loading

0 comments on commit 64e2de8

Please sign in to comment.