From 12e8b0ad9a4773abcb455936091268a3908c61d1 Mon Sep 17 00:00:00 2001 From: amylu00 Date: Wed, 2 Oct 2024 13:20:54 -0700 Subject: [PATCH] Cleaning parcel docs --- docs/src/IceNucleationParcel0D.md | 248 ++++++++++++------------ parcel/Example_AerosolActivation.jl | 23 +-- parcel/Example_Deposition_Nucleation.jl | 17 +- parcel/Example_Jensen_et_al_2022.jl | 59 ++---- parcel/Example_Liquid_only.jl | 26 ++- parcel/Example_P3_ice_nuc.jl | 25 +-- parcel/Example_Tully_et_al_2023.jl | 1 + test/performance_tests.jl | 2 +- 8 files changed, 178 insertions(+), 223 deletions(-) diff --git a/docs/src/IceNucleationParcel0D.md b/docs/src/IceNucleationParcel0D.md index b020467ca..2988eab1c 100644 --- a/docs/src/IceNucleationParcel0D.md +++ b/docs/src/IceNucleationParcel0D.md @@ -122,49 +122,50 @@ The crux of the problem is modeling the ``\frac{dq_l}{dt}`` and ``\frac{dq_i}{dt for different homogeneous and heterogeneous ice nucleation paths. ## Supported size distributions -Currently, the parcel model supports monodisperse, gamma, and lognormal size distributions. For - monodisperse size distributions, the total mass is evenly distributed to all particles. +Currently the parcel model supports monodisperse and gamma size distributions of cloud droplets and ice crystals, + and solves prognostic equations for the cloud water and cloud ice specific humidities + ``q_l``, ``q_i`` and number concentrations ``N_l``, ``N_i``. + +For a monodisperse size distribution of cloud droplets or ice crystals ```math \begin{equation} - n(r) = N_{tot} \delta(r-\bar{r}) -\end{equation} -\begin{equation} - bar{r} = \left( \frac{3 \rho_a q_l}{4 \pi \rho_l N_{tot}} \right)^{1/3} + n(r) = N \delta(r-\bar{r}) \end{equation} ``` - -For a gamma distribution of droplets, a free parameter, ``\lambda``, must be constrained. - This can be done through relating the specific humidity and volume (assuming spherical - droplets and crystals). The mean of ``r^3`` is found by dividing the third moment, ``M_3``, by the - zeroth moment, ``M_0``. Once ``\lambda`` is found, we can use it to find the mean radius by - dividing the first moment, ``M_1``, by the zeroth moment, ``M_0``. - +and assuming spherical shape, the mean radius is defined as ```math \begin{equation} - n(r) = A \; r \; exp(-\lambda r) -\end{equation} -\begin{equation} - \bar{r} = \frac{M_1}{M_0} = \frac{2}{\lambda} + \bar{r} = \left( \frac{3 \rho_a q}{4 \pi \rho N} \right)^{1/3} \end{equation} ``` -where ``\lambda = \left(\frac{32 \pi N_{tot} \rho_l}{q_l \rho_a}\right)^{1/3}``. - -A similar approach is used for the lognormal size distribution. We assume that the geometric - standard deviation, ``\sigma_g``, is measured so that the only parameter that needs to be constrained is the median radius, ``r_m``. +where: + - ``\rho_a`` is the air density, + - ``\rho`` is the cloud water or cloud ice density. +For a gamma distribution of cloud droplets ```math \begin{equation} - n(r) = \frac{N_0}{\sqrt{2\pi}} \; \frac{1}{r \: ln(\sigma_g)} \; exp \left[ -frac{(ln(r)-ln(r_m))^2}{2 \: ln^2(\sigma_g)} \right] + n(r) = N_0 \; r \; \exp(-\lambda r) \end{equation} +``` +where: + - ``\lambda`` and ``N_0`` are the free parameters. +```math \begin{equation} - \bar{r^3} = \frac{\rho_a}{\rho_w}\frac{3}{4\pi}\frac{q}{N} = \frac{M_3}{M_0} = r_m^3 exp \left( \frac{9}{2} ln^2(\sigma_g)\right) + N = {\int_0^\infty \, n(r) \, dr}, \;\;\;\;\; + q = \frac{1}{\rho_a}{\int_0^\infty \, \frac{4}{3} \pi \rho r^3 n(r) \, dr} \end{equation} +``` +As a result +```math \begin{equation} - \bar{r} = \frac{M_1}{M_0} = r_m exp \left( \frac{1}{2} ln^2(\sigma_g)\right) + \lambda = \left(\frac{32 \pi N \rho}{q \rho_a}\right)^{1/3}, \;\;\;\;\; + N_0 = N \lambda^2 \end{equation} ``` -## Aerosol Activation +## Supported source terms +### Aerosol Activation Aerosol activation is described by ([see discussion](https://clima.github.io/CloudMicrophysics.jl/dev/AerosolActivation/#Number-and-mass-of-activated-particles)). It is inherently assumed that the aerosols have a lognormal size distribution. For simplicity, the parcel accepts one mode and one aerosol type at a time, therefore, @@ -176,7 +177,7 @@ Aerosol activation is described by ([see discussion](https://clima.github.io/Clo Standard deviation and r_{mean} of the aerosol size distribution may change as aerosols activate. For now, we will neglect these effects. -## Condensation growth +### Condensation growth The diffusional growth of individual cloud droplet is described by ([see discussion](https://clima.github.io/CloudMicrophysics.jl/dev/Microphysics1M/#Snow-autoconversion)), @@ -214,7 +215,7 @@ where: - ``\bar{r}`` is their mean radius - ``\rho_a`` is the density of air. -## Deposition growth +### Deposition growth Similarly, for a case of a spherical ice particle growing through water vapor deposition ```math @@ -245,7 +246,7 @@ It follows that where: - ``N_{act}`` is the number of activated ice particles. -## Deposition Nucleation on dust particles +### Deposition Nucleation on dust particles There are multiple ways of running deposition nucleation in the parcel. `"MohlerAF_Deposition"` will trigger an activated fraction approach from [Mohler2006](@cite). `"MohlerRate_Deposition"` will trigger a @@ -266,7 +267,7 @@ where ``N_{areo}`` is total number of unactiviated ice nucleating particles and The deposition nucleation methods are parameterized as described in [Ice Nucleation](https://clima.github.io/CloudMicrophysics.jl/dev/IceNucleation/). -## Immersion Freezing +### Immersion Freezing Following the water activity based immersion freezing model (ABIFM), the ABIFM derived nucleation rate coefficient, ``J_{immer}``, can be determined. The ice production rate,``P_{ice, immer}``, per second via immersion freezing can then be calculating using @@ -279,7 +280,7 @@ Following the water activity based immersion freezing model (ABIFM), the ABIFM d where ``N_{liq}`` is total number of ice nuclei containing droplets and ``A_{aero}`` is surface area of the ice nucleating particle. -## Homogeneous Freezing +### Homogeneous Freezing Homogeneous freezing follows the water-activity based model described in the [Ice Nucleation](https://clima.github.io/CloudMicrophysics.jl/dev/IceNucleation/) section which gives a nucleation rate coefficient of units ``cm^{-3}s^{-1}``. @@ -293,6 +294,75 @@ The ice production rate from homogeneous freezing can then be determined: where ``N_{liq}`` is total number of ice nuclei containing droplets and ``V`` is the volume of those droplets. +### P3 Ice Nucleation Parameterizations +The parcel also includes ice nucleation parameterizations used in + the P3 scheme as described in [MorrisonMilbrandt2015](@cite). + Deposition nucleation is based on the ice crystal number parameterization + from Cooper (1986). The heterogeneous freezing parameterization, which + follows Bigg(1953) with parameters from Barklie aand Gokhale (1959), is + treated as immersion freezing in the parcel. Homogeneous freezing happens + instantaneously at 233.15K. + +### Frostenberg et al. 2023 Immersion Freezing +An immersion freezing parameterization based on [Frostenberg2023](@cite) is also available. +The concentration of ice nucleating particles (INPC) depends only on air temperature, + and is based on a lognormal relative frequency distribution. +New ice crystals are created if the INPC exceeds the existing concentration of ice crystals, + provided there are sufficient numbers of cloud liquid droplets to freeze. + +Three different implementations of this parametrization are used in the parcel model: +- `mean` - in which INPC is equal to its mean value defined in [Frostenberg2023](@cite). +- `random` - in which INPC is sampled randomly from the distribution defined in [Frostenberg2023](@cite). + The number of model time steps between sampling is set by `sampling_interval`. +- `stochastic` - in which INPC is solved for as a stochastic process, + with the mean and standard deviation defined in [Frostenberg2023](@cite). + The inverse timescale of the process is set by ``\gamma``. + +The stochastic implementation is based on the [Ornstein-Uhlenbeck process](https://en.wikipedia.org/wiki/Ornstein–Uhlenbeck_process), +in which the variable ``x`` is a mean-reverting process perturbed by Gaussian random noise (i.e. increments of the Wiener process ``W``): +```math +\begin{equation} + dx = - \gamma(x - \mu)dt + \sqrt{2\gamma} \sigma dW; \quad\quad dW \sim N(0, dt), +\end{equation} +``` +where ``N`` is a zero-mean normal distribution with variance ``dt``. +For constant ``\gamma`` and ``\sigma``, and given some initial condition ``x(0)=x_0``, ``x`` has the analytical solution: +```math +\begin{equation} + x(t) = + x_0 e^{-\gamma t} + \mu (1 - e^{-\gamma t}) + + \sqrt{2\gamma} \sigma \int_0^t e^{-\gamma(t-s)} dW, +\end{equation} +``` +where ``\tau \equiv 1 / \gamma`` is the assumed timescale of the process. The process mean is ``x_0 e^{-\gamma t} + \mu (1 - e^{-\gamma t})``. We can calculate the variance ``\mathbb{V}(t)`` as, +```math +\begin{equation} + \mathbb{V}(t) + = 2\gamma \sigma^2 \int_0^t e^{-2\gamma(t-s)} ds + = \frac{g^2}{2\gamma} \left( 1 - e^{-2\gamma t} \right). +\end{equation} +``` + +We use this process to model ``x=\log(\text{INPC})``, which tends toward a temperature-dependent mean value ``\mu(T)``. +The equation for ``\log(\text{INPC})`` is then, +```math +\begin{equation} + d\log(\text{INPC}) = + - \frac{\log(\text{INPC}) - μ}{\tau} dt + + \sigma \sqrt{\frac{2}{\tau}} dW +\end{equation} +``` + +This equation is currently implemented with the simple [Euler-Maruyama method](https://en.wikipedia.org/wiki/Euler–Maruyama_method), which is the stochastic analog of the forward Euler method for (deterministic) ordinary differential equations, so that +```math +\begin{equation} + \log(\text{INPC})_{t+dt} = \log(\text{INPC})_{t} + - \gamma\left(\log(\text{INPC})_t - μ(T_t)\right) dt + + \sigma \sqrt{2\gamma dt} z_t +\end{equation} +``` +where ``z_t \sim N(0,1)`` is a standard normal random variable. + ## Example figures Here we show various example simulation results from the adiabatic parcel @@ -303,7 +373,7 @@ Here we show various example simulation results from the adiabatic parcel First, we check that aerosol activation works reasonably within the parcel. ```@example -include("../../parcel/Example_AerosolActivation.jl") + include("../../parcel/Example_AerosolActivation.jl") ``` ![](Parcel_Aerosol_Activation.svg) @@ -315,6 +385,7 @@ Between each run the water vapor specific humidity is changed, while keeping all other state variables the same as at the last time step of the previous run. The prescribed vertical velocity is equal to 3.5 cm/s. + Supersaturation is plotted for both liquid (solid lines) and ice (dashed lines). The pale blue line uses the `"MohlerRate_Deposition"` approach. We only run it for the first GCM timestep because the rate approach requires @@ -325,7 +396,10 @@ The pale blue line uses the `"MohlerRate_Deposition"` approach. in the `"MohlerAF_Deposition"` approach for the first GCM timestep. ```@example -include("../../parcel/Example_Tully_et_al_2023.jl") +using Suppressor: @suppress #hide + @suppress begin #hide + include("../../parcel/Example_Tully_et_al_2023.jl") +end # hide ``` ![](cirrus_box.svg) @@ -337,30 +411,20 @@ The water activity based parameterization for deposition nucleation shows is no common aerosol type between the two parameterizations. ```@example -include("../../parcel/Example_Deposition_Nucleation.jl") + include("../../parcel/Example_Deposition_Nucleation.jl") ``` ![](deposition_nucleation.svg) In the plots below, the parcel model is ran with only condensation (no ice or freezing) assuming either a monodisperse or a gamma distribution of droplets. -It is compared to [Rogers1975](@cite). +It is compared to [Rogers1975](@cite). This is to ensure the parcel can run +with liquid phase and different size distributions without issues. ```@example -include("../../parcel/Example_Liquid_only.jl") + include("../../parcel/Example_Liquid_only.jl") ``` ![](liquid_only_parcel.svg) -The plots below are the results of the adiabatic parcel model - with immersion freezing, condensation growth, and deposition growth for - both a monodisperse and gamma size distribution. Note that this has not - yet been validated against literature. Results are very sensitive to - initial conditions. - -```@example -include("../../parcel/Example_Immersion_Freezing.jl") -``` -![](immersion_freezing.svg) - The following plots show the parcel model running with homogeneous freezing and depositional growth assuming a lognormal distribution of aerosols. It is compared against [Jensen2022](@cite). Note that running with the initial @@ -370,96 +434,26 @@ The following plots show the parcel model running with homogeneous freezing and for demonstrative purposes. ```@example -include("../../parcel/Example_Jensen_et_al_2022.jl") + include("../../parcel/Example_Jensen_et_al_2022.jl") ``` ![](Jensen_et_al_2022.svg) -## P3 Ice Nucleation Parameterizations -The parcel also includes ice nucleation parameterizations used in - the P3 scheme as described in [MorrisonMilbrandt2015](@cite). - Deposition nucleation is based on the ice crystal number parameterization - from Cooper (1986). The heterogeneous freezing parameterization, which - follows Bigg(1953) with parameters from Barklie aand Gokhale (1959), is - treated as immersion freezing in the parcel. Homogeneous freezing happens - instantaneously at 233.15K. Shown below are three separate parcel simulations for deposition nucleation, - immersion freezing, and homogeneous freezing. Note that initial temperature - varies for each run. The deposition nucleation run does not conserve - INP number, while the other two freezing modes do. Updraft velocity is - set to 0.5 m/s. + immersion freezing, and homogeneous freezing as parameterized in the P3 scheme. + Note that initial temperature varies for each run. The deposition nucleation + run does not conserve INP number, while the other two freezing modes do. + Updraft velocity is set to 0.5 m/s. ```@example -include("../../parcel/Example_P3_ice_nuc.jl") + include("../../parcel/Example_P3_ice_nuc.jl") ``` ![](P3_ice_nuc.svg) - -## Immersion Freezing Parametrization based on Frostenberg et al. 2023 - -Here we show the parcel model results when using the parametrization of immersion freezing - based on [Frostenberg2023](@cite). -The concentration of ice nucleating particles (INPC) depends only on air temperature, - and is based on a lognormal relative frequency distribution. -New ice crystals are created if the INPC exceeds the existing concentration of ice crystals, - provided there are sufficient numbers of cloud liquid droplets to freeze. - -Three different implementations of this parametrization are used in the parcel model: -- `mean` - in which INPC is equal to its mean value defined in [Frostenberg2023](@cite). -- `random` - in which INPC is sampled randomly from the distribution defined in [Frostenberg2023](@cite). - The number of model time steps between sampling is set by `sampling_interval`. -- `stochastic` - in which INPC is solved for as a stochastic process, - with the mean and standard deviation defined in [Frostenberg2023](@cite). - The inverse timescale of the process is set by ``\gamma``. - -The stochastic implementation is based on the [Ornstein-Uhlenbeck process](https://en.wikipedia.org/wiki/Ornstein–Uhlenbeck_process), -in which the variable ``x`` is a mean-reverting process perturbed by Gaussian random noise (i.e. increments of the Wiener process ``W``): -```math -\begin{equation} - dx = - \gamma(x - \mu)dt + \sqrt{2\gamma} \sigma dW; \quad\quad dW \sim N(0, dt), -\end{equation} -``` -where ``N`` is a zero-mean normal distribution with variance ``dt``. -For constant ``\gamma`` and ``\sigma``, and given some initial condition ``x(0)=x_0``, ``x`` has the analytical solution: -```math -\begin{equation} - x(t) = - x_0 e^{-\gamma t} + \mu (1 - e^{-\gamma t}) - + \sqrt{2\gamma} \sigma \int_0^t e^{-\gamma(t-s)} dW, -\end{equation} -``` -where ``\tau \equiv 1 / \gamma`` is the assumed timescale of the process. The process mean is ``x_0 e^{-\gamma t} + \mu (1 - e^{-\gamma t})``. We can calculate the variance ``\mathbb{V}(t)`` as, -```math -\begin{equation} - \mathbb{V}(t) - = 2\gamma \sigma^2 \int_0^t e^{-2\gamma(t-s)} ds - = \frac{g^2}{2\gamma} \left( 1 - e^{-2\gamma t} \right). -\end{equation} -``` - -We use this process to model ``x=\log(\text{INPC})``, which tends toward a temperature-dependent mean value ``\mu(T)``. -The equation for ``\log(\text{INPC})`` is then, -```math -\begin{equation} - d\log(\text{INPC}) = - - \frac{\log(\text{INPC}) - μ}{\tau} dt - + \sigma \sqrt{\frac{2}{\tau}} dW -\end{equation} -``` - -This equation is currently implemented with the simple [Euler-Maruyama method](https://en.wikipedia.org/wiki/Euler–Maruyama_method), which is the stochastic analog of the forward Euler method for (deterministic) ordinary differential equations, so that -```math -\begin{equation} - \log(\text{INPC})_{t+dt} = \log(\text{INPC})_{t} - - \gamma\left(\log(\text{INPC})_t - μ(T_t)\right) dt - + \sigma \sqrt{2\gamma dt} z_t -\end{equation} -``` -where ``z_t \sim N(0,1)`` is a standard normal random variable. - -The following plot shows resuls of the parcel model with the `mean` (black line), `random` (dotted lines) and `stochastic` (solid lines) parameterization options. -We show results for two sampling intervals ``\Delta t`` (random), two process time scales ``\tau`` (stochastic), and two model time steps `dt`. +The results of the immersion freezing parameterization from Frostenberg et al. 2023 are shown below. +The following plot shows resuls of the parcel model with the `mean` (black line), `random` (dotted lines) +and `stochastic` (solid lines) parameterization options. We show results for two sampling intervals +``\Delta t`` (random), two process time scales ``\tau`` (stochastic), and two model time steps `dt`. ```@example -using Suppressor: @suppress # hide -@suppress include("../../parcel/Example_Frostenberg_Immersion_Freezing.jl") # hide + include("../../parcel/Example_Frostenberg_Immersion_Freezing.jl") ``` ![](frostenberg_immersion_freezing.svg) diff --git a/parcel/Example_AerosolActivation.jl b/parcel/Example_AerosolActivation.jl index fadd8bd47..8e3dc4e86 100644 --- a/parcel/Example_AerosolActivation.jl +++ b/parcel/Example_AerosolActivation.jl @@ -42,7 +42,7 @@ IC = [Sₗ, p₀, T₀, qᵥ, qₗ, qᵢ, Nₐ, Nₗ, Nᵢ, ln_INPC] # Simulation parameters passed into ODE solver w = FT(1.2) # updraft speed const_dt = FT(1) # model timestep -t_max = FT(100) # total time +t_max = FT(35) # total time aerosol = CMP.Sulfate(FT) condensation_growth = "Condensation" @@ -64,20 +64,15 @@ params = parcel_params{FT}( sol = run_parcel(IC, FT(0), t_max, params) # Plot results -fig = MK.Figure(size = (1000, 800), fontsize = 20) -ax1 = MK.Axis(fig[1, 1], ylabel = "Liquid Saturation [-]") -ax2 = MK.Axis(fig[1, 2], xlabel = "Time [s]", ylabel = "Temperature [K]") -ax3 = MK.Axis(fig[2, 1], ylabel = "N_aero [m^-3]", xlabel = "Time [s]") -ax4 = MK.Axis(fig[2, 2], ylabel = "N_liq [m^-3]", xlabel = "Time [s]") -ax5 = MK.Axis(fig[3, 1], ylabel = "q_vap [g/kg]", xlabel = "Time [s]") -ax6 = MK.Axis(fig[3, 2], ylabel = "q_liq [g/kg]", xlabel = "Time [s]") +fig = MK.Figure(size = (800, 300), fontsize = 20) +ax1 = MK.Axis(fig[1, 1], ylabel = "Liquid Saturation [-]", xlabel = "Time [s]") +ax2 = MK.Axis(fig[1, 2], ylabel = "N [m^-3]", xlabel = "Time [s]") -MK.lines!(ax1, sol.t, (sol[1, :])) # liq saturation -MK.lines!(ax2, sol.t, sol[3, :]) # temperature -MK.lines!(ax3, sol.t, sol[7, :]) # N_aero -MK.lines!(ax4, sol.t, sol[8, :]) # N_liq -MK.lines!(ax5, sol.t, sol[4, :] .* 1e3) # q_vap -MK.lines!(ax6, sol.t, sol[5, :] .* 1e3) # q_liq +MK.lines!(ax1, sol.t, (sol[1, :]), linewidth = 2) +MK.lines!(ax2, sol.t, sol[7, :], label = "N_aero", linewidth = 2, color = :red) +MK.lines!(ax2, sol.t, sol[8, :], label = "N_liq", linewidth = 2, color = :blue) +MK.axislegend(ax2, framevisible = false, labelsize = 16, position = :rc) MK.save("Parcel_Aerosol_Activation.svg", fig) +nothing diff --git a/parcel/Example_Deposition_Nucleation.jl b/parcel/Example_Deposition_Nucleation.jl index 0f316fabb..c4dfb76af 100644 --- a/parcel/Example_Deposition_Nucleation.jl +++ b/parcel/Example_Deposition_Nucleation.jl @@ -46,11 +46,9 @@ deposition_growth = "Deposition" size_distribution = "Monodisperse" # Plotting -fig = MK.Figure(size = (800, 600)) -ax1 = MK.Axis(fig[1, 1], ylabel = "Ice Saturation [-]") -ax2 = MK.Axis(fig[1, 2], ylabel = "Temperature [K]") -ax3 = MK.Axis(fig[2, 1], ylabel = "q_ice [g/kg]", xlabel = "time") -ax4 = MK.Axis(fig[2, 2], ylabel = "N_ice [m^-3]", xlabel = "time") +fig = MK.Figure(size = (800, 300)) +ax1 = MK.Axis(fig[1, 1], ylabel = "Ice Saturation [-]", xlabel = "time") +ax2 = MK.Axis(fig[1, 2], ylabel = "N_ice [m^-3]", xlabel = "time") for deposition in deposition_modes if deposition == "MohlerRate" @@ -79,9 +77,7 @@ for deposition in deposition_modes S_i.(tps, sol[3, :], sol[1, :]), label = aero_label, ) - MK.lines!(ax2, sol.t, sol[3, :]) # temperature - MK.lines!(ax3, sol.t, sol[6, :] * 1e3) # q_ice - MK.lines!(ax4, sol.t, sol[9, :]) # N_ice + MK.lines!(ax2, sol.t, sol[9, :]) # N_ice end elseif deposition == "ABDINM" @@ -113,9 +109,7 @@ for deposition in deposition_modes label = aero_label, linestyle = :dash, ) - MK.lines!(ax2, sol.t, sol[3, :], linestyle = :dash) # temperature - MK.lines!(ax3, sol.t, sol[6, :] * 1e3, linestyle = :dash) # q_ice - MK.lines!(ax4, sol.t, sol[9, :], linestyle = :dash) # N_ice + MK.lines!(ax2, sol.t, sol[9, :], linestyle = :dash) # N_ice end end end @@ -130,3 +124,4 @@ MK.axislegend( ) MK.save("deposition_nucleation.svg", fig) +nothing diff --git a/parcel/Example_Jensen_et_al_2022.jl b/parcel/Example_Jensen_et_al_2022.jl index 21d06ece3..b455c4eee 100644 --- a/parcel/Example_Jensen_et_al_2022.jl +++ b/parcel/Example_Jensen_et_al_2022.jl @@ -61,18 +61,9 @@ Jensen_t_ICNC = [0.217, 42.69, 50.02, 54.41, 58.97, 65.316, 72.477, 82.08, 92.65 Jensen_ICNC = [0, 0, 0.282, 0.789, 1.804, 4.1165, 7.218, 12.12, 16.35, 16.8, 16.97, 17.086] #! format: on -fig = MK.Figure(size = (1000, 1000)) -ax1 = MK.Axis(fig[1, 1], ylabel = "Saturation") -ax2 = MK.Axis(fig[3, 1], xlabel = "Time [s]", ylabel = "Temperature [K]") -ax3 = MK.Axis(fig[2, 1], ylabel = "q_vap [g/kg]") -ax4 = MK.Axis(fig[2, 2], xlabel = "Time [s]", ylabel = "q_liq [g/kg]") -ax5 = MK.Axis(fig[1, 2], ylabel = "ICNC [cm^-3]") -ax6 = MK.Axis(fig[3, 2], ylabel = "q_ice [g/kg]") - -MK.ylims!(ax2, 188.5, 190) -MK.xlims!(ax2, -5, 150) -MK.xlims!(ax3, -5, 150) -MK.xlims!(ax4, -5, 150) +fig = MK.Figure(size = (800, 300), fontsize = 20) +ax1 = MK.Axis(fig[1, 1], ylabel = "Saturation", xlabel = "Time [s]") +ax2 = MK.Axis(fig[1, 2], ylabel = "ICNC [cm^-3]", xlabel = "Time [s]") MK.lines!( ax1, @@ -80,20 +71,15 @@ MK.lines!( Jensen_sat, label = "Jensen et al 2022", color = :green, + linewidth = 2, ) MK.lines!( ax2, - Jensen_t_T, - Jensen_T, - color = :green, - label = "Jensen et al 2022", -) -MK.lines!( - ax5, Jensen_t_ICNC, Jensen_ICNC, color = :green, label = "Jensen et al 2022", + linewidth = 2, ) params = parcel_params{FT}( @@ -113,37 +99,32 @@ MK.lines!( S_i.(tps, sol[3, :], (sol[1, :])), label = "ice", color = :blue, + linewidth = 2, ) -MK.lines!(ax1, sol.t, (sol[1, :]), label = "liquid", color = :red) # liq saturation -MK.lines!(ax2, sol.t, sol[3, :], label = "CM.jl") # temperature -MK.lines!(ax3, sol.t, sol[4, :] * 1e3) # q_vap -MK.lines!(ax4, sol.t, sol[5, :] * 1e3) # q_liq -MK.lines!(ax5, sol.t, sol[9, :] * 1e-6, label = "CM.jl") # ICNC -MK.lines!(ax6, sol.t, sol[6, :] * 1e3) # q_ice +MK.lines!( + ax1, + sol.t, + (sol[1, :]), + label = "liquid", + color = :red, + linewidth = 2, +) # liq saturation +MK.lines!(ax2, sol.t, sol[9, :] * 1e-6, label = "CM.jl", linewidth = 2) # ICNC MK.axislegend( ax1, framevisible = false, - labelsize = 12, - orientation = :horizontal, - nbanks = 2, - position = :lc, -) -MK.axislegend( - ax5, - framevisible = false, - labelsize = 12, - orientation = :horizontal, - nbanks = 2, + labelsize = 16, + orientation = :vertical, position = :lc, ) MK.axislegend( ax2, framevisible = false, - labelsize = 12, - orientation = :horizontal, - nbanks = 2, + labelsize = 16, + orientation = :vertical, position = :lc, ) MK.save("Jensen_et_al_2022.svg", fig) +nothing diff --git a/parcel/Example_Liquid_only.jl b/parcel/Example_Liquid_only.jl index 623b67617..4a6cac472 100644 --- a/parcel/Example_Liquid_only.jl +++ b/parcel/Example_Liquid_only.jl @@ -50,17 +50,15 @@ Rogers_time_supersat = [0.0645, 0.511, 0.883, 1.4, 2.07, 2.72, 3.24, 3.89, 4.53, Rogers_supersat = [0.0268, 0.255, 0.393, 0.546, 0.707, 0.805, 0.863, 0.905, 0.938, 0.971, 0.978, 0.963, 0.910, 0.885] Rogers_time_radius = [0.561, 2, 3.99, 10.7, 14.9, 19.9] Rogers_radius = [8.0, 8.08, 8.26, 8.91, 9.26, 9.68] -#! format: on # Setup the plots -fig = MK.Figure(size = (800, 600)) -ax1 = MK.Axis(fig[1, 1], ylabel = "Supersaturation [%]") -ax2 = MK.Axis(fig[3, 1], xlabel = "Time [s]", ylabel = "Temperature [K]") -ax3 = MK.Axis(fig[2, 1], ylabel = "q_vap [g/kg]") -ax4 = MK.Axis(fig[2, 2], xlabel = "Time [s]", ylabel = "q_liq [g/kg]") -ax5 = MK.Axis(fig[1, 2], ylabel = "radius [μm]") -MK.lines!(ax1, Rogers_time_supersat, Rogers_supersat, label = "Rogers_1975") -MK.lines!(ax5, Rogers_time_radius, Rogers_radius) +fig = MK.Figure(size = (800, 300)) +ax1 = MK.Axis(fig[1, 1], xlabel = "Time [s]", ylabel = "Supersaturation [%]") +ax2 = MK.Axis(fig[1, 2], xlabel = "Time [s]", ylabel = "radius [μm]") +ax3 = MK.Axis(fig[1, 3], xlabel = "Time [s]", ylabel = "q_liq [g/kg]") +MK.lines!(ax1, Rogers_time_supersat, Rogers_supersat, label = "Rogers_1975", color = :red) +MK.lines!(ax2, Rogers_time_radius, Rogers_radius, color = :red) +#! format: on for DSD in liq_size_distribution_list local params = parcel_params{FT}( @@ -74,9 +72,7 @@ for DSD in liq_size_distribution_list # Plot results MK.lines!(ax1, sol.t, (sol[1, :] .- 1) * 100.0, label = DSD) - MK.lines!(ax2, sol.t, sol[3, :]) - MK.lines!(ax3, sol.t, sol[4, :] * 1e3) - MK.lines!(ax4, sol.t, sol[5, :] * 1e3) + MK.lines!(ax3, sol.t, sol[5, :] * 1e3) sol_Nₗ = sol[8, :] sol_Nᵢ = sol[9, :] @@ -96,16 +92,16 @@ for DSD in liq_size_distribution_list for it in range(1, length(sol_T)) r[it] = moms[it].r end - MK.lines!(ax5, sol.t, r * 1e6) + MK.lines!(ax2, sol.t, r * 1e6) end MK.axislegend( ax1, framevisible = false, labelsize = 12, - orientation = :horizontal, - nbanks = 2, + orientation = :vertical, position = :rb, ) MK.save("liquid_only_parcel.svg", fig) +nothing diff --git a/parcel/Example_P3_ice_nuc.jl b/parcel/Example_P3_ice_nuc.jl index a9865a871..d7281c65e 100644 --- a/parcel/Example_P3_ice_nuc.jl +++ b/parcel/Example_P3_ice_nuc.jl @@ -17,7 +17,7 @@ Nₗ = FT(2000) Nᵢ = FT(0) rₗ = FT(1.25e-6) p₀ = FT(20000) -qᵥ = FT(8.3e-4) +qᵥ = FT(5e-4) qₗ = FT(Nₗ * 4 / 3 * π * rₗ^3 * wps.ρw / 1.2) qᵢ = FT(0) ln_INPC = FT(0) @@ -37,21 +37,15 @@ deposition_growth = "Deposition" size_distribution = "Monodisperse" # Plotting -fig = MK.Figure(size = (900, 600)) -ax1 = MK.Axis(fig[1, 1], ylabel = "Ice Saturation [-]") -ax2 = MK.Axis(fig[1, 2], ylabel = "ICNC [cm^-3]") -ax7 = MK.Axis(fig[1, 3], ylabel = "Temperature [K]") -ax3 = MK.Axis(fig[2, 1], ylabel = "Ice Saturation [-]") -ax4 = MK.Axis(fig[2, 2], ylabel = "ICNC [cm^-3]") -ax8 = MK.Axis(fig[2, 3], ylabel = "Temperature [K]") -ax5 = MK.Axis(fig[3, 1], ylabel = "Ice Saturation [-]", xlabel = "Time [s]") -ax6 = MK.Axis(fig[3, 2], ylabel = "ICNC [cm^-3]", xlabel = "Time [s]") -ax9 = MK.Axis(fig[3, 3], ylabel = "Temperature [K]", xlabel = "Time [s]") +fig = MK.Figure(size = (1000, 350), fontsize = 20) +ax1 = MK.Axis(fig[1, 1], ylabel = "ICNC [cm^-3]", xlabel = "Time [s]") +ax2 = MK.Axis(fig[1, 2], ylabel = "ICNC [cm^-3]", xlabel = "Time [s]") +ax3 = MK.Axis(fig[1, 3], ylabel = "ICNC [cm^-3]", xlabel = "Time [s]") ice_nucleation_modes_list = ["P3_dep", "P3_het", "P3_hom"] T₀_list = [FT(235), FT(235), FT(233.2)] color = [:blue, :red, :orange] -ax_row = [[ax1, ax2, ax7], [ax3, ax4, ax8], [ax5, ax6, ax9]] +ax_list = [ax1, ax2, ax3] for it in [1, 2, 3] mode = ice_nucleation_modes_list[it] @@ -94,12 +88,11 @@ for it in [1, 2, 3] # solve ODE local sol = run_parcel(IC, FT(0), t_max, params) - MK.lines!(ax_row[it][1], sol.t, S_i.(tps, sol[3, :], (sol[1, :])), label = mode, color = color[it]) # saturation - MK.lines!(ax_row[it][2], sol.t, sol[9, :] * 1e-6, color = color[it]) # ICNC - MK.lines!(ax_row[it][3], sol.t, sol[3, :], color = color[it]) # Temperature - MK.axislegend(ax_row[it][1], framevisible = false, labelsize = 12, orientation = :horizontal, position = :rt) + MK.lines!(ax_list[it], sol.t, sol[9, :] * 1e-6, label = mode, color = color[it]) # saturation + MK.axislegend(ax_list[it], framevisible = false, labelsize = 12, orientation = :horizontal, position = :rb) #! format: on end MK.save("P3_ice_nuc.svg", fig) +nothing diff --git a/parcel/Example_Tully_et_al_2023.jl b/parcel/Example_Tully_et_al_2023.jl index 99fdd69ae..2106e1eb0 100644 --- a/parcel/Example_Tully_et_al_2023.jl +++ b/parcel/Example_Tully_et_al_2023.jl @@ -175,3 +175,4 @@ function Tully_et_al_2023(FT) MK.save("cirrus_box.svg", fig) end Tully_et_al_2023(Float32) +nothing diff --git a/test/performance_tests.jl b/test/performance_tests.jl index aaea155ba..15d623bcf 100644 --- a/test/performance_tests.jl +++ b/test/performance_tests.jl @@ -241,7 +241,7 @@ function benchmark_test(FT) bench_press(CMI_hom.homogeneous_J_linear, (ip.homogeneous, Delta_a_w), 230) # non-equilibrium - bench_press(CMN.τ_relax, (liquid,), 10) + bench_press(CMN.τ_relax, (liquid,), 15) bench_press( CMN.conv_q_vap_to_q_liq_ice, (