Skip to content

Commit ab6c0d2

Browse files
committed
Aerosol MMR set by condensation of requested species. Add coldtrap option to config. Error->warn in solver functions
1 parent 70a76f2 commit ab6c0d2

7 files changed

Lines changed: 106 additions & 37 deletions

File tree

docs/src/reference/configuration.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ ratios (`vmr_dict` or `vmr_file`), or by metallicities (`metallicities`).
5656
| `metallicities` | Dictionary of elemental **mass** abundance ratios relative to hydrogen. Must also set `p_surf`. |
5757
| `condensates ` | List of volatiles which are allowed to condense. Can be used together with thermochemical equilibrium (`physics.chemistry = true`). |
5858
| `transparent ` | Make the atmosphere transparent (see below). Replaces all of the above parameters in this table. |
59-
| `aerosols ` | Dictionary of aerosols and their mass mixing ratios [kg/kg]. |
59+
| `aerosols ` | Dictionary of aerosols, and their mass mixing ratios [kg/kg] or associated condensate species. |
6060

6161

6262

@@ -104,6 +104,7 @@ Parameters that describe how the model should treat the physics.
104104
| `convection_crit` | Criterion for convective stability. Options: (s)chwarzschild, (l)edoux. |
105105
| `latent_heat ` | Include vertical heat transport from condensation and evaporation (true/false). |
106106
| `rainout ` | Enable condensation and evaporation of condensables aloft. Required for `latent_heat=true`. |
107+
| `coldtrap ` | Enable cold-trapping effect on abundances aloft, so that VMR always decreases with height. |
107108
| `oceans ` | Enable condensation and evaporation of condensables at the surface. |
108109

109110
## `[plots]`

res/config/default.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ title = "Default" # Name for this configuration file
6868
conduction = true # Include thermal conductive heat transport?
6969
oceans = false # Model ocean formation - check saturation at surface
7070
rainout = false # Model rainout - phase change impacts gas mixing ratios, not just energy fluxes
71+
coldtrap = true # Enable cold-trapping effect on abundances aloft
7172
latent_heat = false # Include heat release from phase change
7273
convection_crit = "s" # Stability criterion for convection. Options: (s)chwarzschild or (l)edoux.
7374
convection = true # Include heat transport by convection

res/config/physics/aerosols.toml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title = "Aerosols radiative properties test"
33

44
[planet]
5-
tmp_surf = 2000.0 # Surface temperature [kelvin].
5+
tmp_surf = 600.0 # Surface temperature [kelvin].
66
instellation = 44000.0 # Stellar flux at planet's orbital distance [W m-2].
77
albedo_b = 0.18 # Pseudo bond-albedo which downscales the stellar flux by 1-this_value.
88
s0_fact = 0.6652 # Stellar flux scale factor which accounts for planetary rotation (c.f. Cronin+13).
@@ -26,15 +26,15 @@ title = "Aerosols radiative properties test"
2626
io_dir = "out/" # Path for fast file operations (optional).
2727

2828
[composition]
29-
p_surf = 270.0 # Total surface pressure [bar].
29+
p_surf = 1000.0 # Total surface pressure [bar].
3030
p_top = 1e-5 # Total top-of-atmosphere pressure [bar].
31-
vmr_dict = { H2O=0.05, CH4=0.1, CO=0.05, N2=0.8 } # Volatile volume mixing ratios (=mole fractions).
32-
condensates = [] # List of volatiles which are allowed to condense.
33-
aerosols = { "soot"=1e-4, "nitrate"=1e-2, "sulph"=1e-7 } # List of aerosols and default MMRs
31+
vmr_dict = { H2=0.3, HCN=0.5, NH3=0.1, CO=0.05, H2O=0.05} # Volatile volume mixing ratios (=mole fractions).
32+
condensates = ["HCN","NH3"] # List of volatiles which are allowed to condense.
33+
aerosols = { "soot"=1e-4, "nitrate"=1e-2, "biogenic"="HCN" } # List of aerosols and default MMRs
3434

3535
[execution]
3636
clean_output = true # Clean the output folder at model startup.
37-
verbosity = 1 # Log level (0: none, 1: normal, 2: debug)
37+
verbosity = 2 # Log level (0: none, 1: normal, 2: debug)
3838
max_steps = 20000 # Maximum number of solver steps.
3939
max_runtime = 400 # Maximum wall-clock solver runtime [s].
4040
num_levels = 200 # Number of model levels.
@@ -44,7 +44,7 @@ title = "Aerosols radiative properties test"
4444
solution_type = 0 # Solution type (see wiki).
4545
solver = "" # Solver to use (see wiki).
4646
dx_max = 200.0 # Maximum step size [Kelvin], when using nonlinear solvers
47-
initial_state = ["dry", "sat", "H2O"] # Ordered list of requests describing the initial state of the atmosphere (see wiki).
47+
initial_state = ["dry"] # Ordered list of requests describing the initial state of the atmosphere (see wiki).
4848
linesearch = 0 # Linesearch method to be used (0: None, 1: Golden, 2: Backtracking)
4949
easy_start = false # Initially down-scale convective/condensation fluxes, if initial guess is poor.
5050
grey_start = false # Initially solve for RCE using double-grey rad trans.
@@ -64,7 +64,8 @@ title = "Aerosols radiative properties test"
6464
sensible_heat = false # Include sensible heat transport at the surface?
6565
conduction = true # Include thermal conductive heat transport?
6666
oceans = false # Model ocean formation - check saturation at surface
67-
rainout = false # Model rainout - phase change impacts gas mixing ratios, not just energy fluxes
67+
rainout = true # Model rainout - phase change impacts gas mixing ratios, not just energy fluxes
68+
coldtrap = true # Enable cold-trapping effect on abundances aloft
6869
latent_heat = false # Include heat release from phase change
6970
convection_crit = "s" # Stability criterion for convection. Options: (s)chwarzschild or (l)edoux.
7071
convection = true # Include heat transport by convection

src/AGNI.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ module AGNI
267267
chem::Bool = false
268268
rainout::Bool = false
269269
oceans::Bool = false
270+
coldtrap::Bool = true
270271
p_surf::Float64 = 0.0
271272
p_top::Float64 = 0.0
272273
pp_dict::Dict{String, Float64} = Dict{String, Float64}()
@@ -297,6 +298,11 @@ module AGNI
297298
oceans = Bool(cfg["physics"]["oceans"])
298299
condensates = cfg["composition"]["condensates"]
299300

301+
coldtrap = true
302+
if haskey(cfg["physics"], "coldtrap")
303+
coldtrap = Bool(cfg["physics"]["coldtrap"])
304+
end
305+
300306
comp_set_by::Int = 0
301307

302308
# composition set by VMRs and Psurf
@@ -490,10 +496,10 @@ module AGNI
490496
end
491497

492498
# Optional aerosol parametrization controls
493-
aerosol_species::Dict{String, Float64} = Dict{String, Float64}()
499+
aerosol_species::Dict = Dict()
494500
if haskey(cfg["composition"], "aerosols")
495501
for (k, v) in cfg["composition"]["aerosols"]
496-
aerosol_species[string(k)] = Float64(v)
502+
aerosol_species[string(k)] = v
497503
end
498504
end
499505

@@ -518,6 +524,7 @@ module AGNI
518524

519525
IO_DIR=io_dir,
520526
condensates=condensates,
527+
coldtrap=coldtrap,
521528
metallicities=metallicities,
522529
flag_gcontinuum = cfg["physics"]["continua"],
523530
flag_rayleigh = cfg["physics"]["rayleigh"],
@@ -641,6 +648,7 @@ module AGNI
641648
conv_rtol=conv_rtol,
642649
method=Int(method_idx),
643650
rainout=rainout,
651+
coldtrap=coldtrap,
644652
oceans=oceans,
645653
dx_max=Float64(cfg["execution"]["dx_max"]),
646654
ls_method=Int(cfg["execution"]["linesearch"]),

src/atmosphere.jl

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ module atmosphere
290290
aerosol_arr_l::Dict{String, Array{Float64,1}} # Aerosol mass mixing ratio profiles [kg/kg]
291291
aerosol_arr_r::Dict{String, Array{Float64,1}} # Aerosol particle size profiles [m]
292292
aerosol_val_r::Float64 # Default particle size for aerosol species, if not specified in array
293+
aerosol_setby::Dict{String, String} # Dict of how each aerosol is set (e.g. "value", "S8", "H2O", etc.)
293294
aerosol_names::Array{String,1} # Map SOCRATES index (int) to name (string)
294295
aerosol_relhumid::Float64 # Mean relative humidity used by moist aerosol schemes [0,1]
295296
aerosol_phase_num::Int # Number of phase-function moments retained when averaging
@@ -440,7 +441,7 @@ module atmosphere
440441
- `flag_gcontinuum::Bool` include generalised continuum absorption?
441442
- `flag_continuum::Bool` include continuum absorption?
442443
- `flag_aerosol::Bool` include aerosols?
443-
- `aerosol_species::Dict` aerosols MMR values to initialise profile with
444+
- `aerosol_species::Dict` aerosols MMR values or associated condensates
444445
- `flag_cloud::Bool` include clouds?
445446
- `phs_timescale::Float64` phase change timescale [s]
446447
- `evap_efficiency::Float64` re-evaporatione efficiency compared to saturating amount
@@ -497,7 +498,7 @@ module atmosphere
497498
flag_continuum::Bool = false,
498499
flag_aerosol::Bool = false,
499500
flag_cloud::Bool = false,
500-
aerosol_species::Dict = Dict{String, Float64}(),
501+
aerosol_species::Dict = Dict{String, Union{Float64,String}}(),
501502

502503
phs_timescale::Float64 = 1e6,
503504
evap_efficiency::Float64 = 0.05,
@@ -807,16 +808,33 @@ module atmosphere
807808
atmos.aerosol_val_r = 1.0e-5 # [INPUT] default particle size for aerosol species
808809
atmos.aerosol_arr_l = Dict{String, Array{Float64,1}}() # list of MMR profiles
809810
atmos.aerosol_arr_r = Dict{String, Array{Float64,1}}() # list of particle size profiles
811+
atmos.aerosol_setby = Dict{String, String}() # dictionary of how each aerosol is set (e.g. "value", "S8", "H2O", etc.)
810812
atmos.aerosol_names = String[] # list of species names, in same order as spectral file
811813
for (k, v) in aerosol_species
814+
815+
# set to zero for now (true values will be set elsewhere)
812816
k = lowercase(k)
813-
_check_range("Aerosol mass mixing ratio override for type $k", v; min=0.0) || return false
814817
atmos.aerosol_arr_l[k] = zeros(Float64, atmos.nlev_c)
815818
atmos.aerosol_arr_r[k] = zeros(Float64, atmos.nlev_c)
816-
set_aerosol!(atmos,k , v)
819+
push!(atmos.aerosol_names, UNSET_STR) # set in allocate!
817820

818-
# Store empty strings for now (set in allocate! function)
819-
push!(atmos.aerosol_names, UNSET_STR)
821+
# MMR profile tied to condensate or set by value?
822+
if isa(v, String)
823+
# interpret as condensate name
824+
if !(v in condensates)
825+
@error "Aerosol '$k' is tied to '$v', but $v is not in the list of condensates"
826+
return false
827+
end
828+
@debug "Aerosol '$k' to be associated with species '$v'"
829+
set_aerosol!(atmos, k, 0.0)
830+
atmos.aerosol_setby[k] = v
831+
else
832+
# interpret as MMR value
833+
v = Float64(v)
834+
_check_range("Aerosol mass mixing ratio override for type $k", v; min=0.0) || return false
835+
set_aerosol!(atmos, k, v)
836+
atmos.aerosol_setby[k] = "value"
837+
end
820838
end
821839

822840
# Read VMRs
@@ -2819,6 +2837,25 @@ module atmosphere
28192837
return aerosol_names
28202838
end
28212839

2840+
"""
2841+
**Calculate condensate mass mixing ratio at a specific layer.**
2842+
2843+
Arguments:
2844+
- `atmos::atmosphere.Atmos_t` atmosphere struct instance to be used.
2845+
- `c::String` condensate species to calculate for (e.g. "H2O")
2846+
- `i::Int` layer index
2847+
2848+
Returns:
2849+
- `cond_mmr::Float64` condensate `c` mass mixing ratio at layer `i`
2850+
"""
2851+
function calc_cond_mmr(atmos::atmosphere.Atmos_t, c::String, i::Int64)::Float64
2852+
if atmos.cond_yield[c][i] > 0.0
2853+
return atmos.cond_yield[c][i]*atmos.cloud_alpha / atmos.layer_σ[i]
2854+
else
2855+
return 0.0
2856+
end
2857+
end
2858+
28222859
"""
28232860
**Set water cloud profile based on saturation.**
28242861
@@ -2841,8 +2878,7 @@ module atmosphere
28412878
# liquid water content (take ratio of mass surface densities [kg/m^2])
28422879
if from_yield
28432880
# set by condensation yield
2844-
atmos.cloud_arr_l[i] = (atmos.cond_yield["H2O"][i]*atmos.cloud_alpha) /
2845-
atmos.layer_σ[i]
2881+
atmos.cloud_arr_l[i] = calc_cond_mmr(atmos, "H2O", i)
28462882
else
28472883
# set by mask
28482884
atmos.cloud_arr_l[i] = atmos.gas_sat["H2O"][i] ? atmos.cloud_val_l : 0.0
@@ -2864,27 +2900,37 @@ module atmosphere
28642900
end
28652901

28662902
"""
2867-
**Set aerosol profiles for a given species.**
2903+
**Set aerosol MMR profiles for a given species.**
28682904
28692905
Arguments:
28702906
- `atmos::atmosphere.Atmos_t` the atmosphere struct instance to be used
28712907
- `species::String` the name of the aerosol species to set
2872-
- `mmr` the mixing ratio (profile or scalar) to set
2908+
- `mmr` the mixing ratio to assign (1D array, or Float, or species String)
28732909
28742910
Optional arguments
28752911
- `pmin::Float64` the minimum pressure [Pa]
28762912
- `pmax::Float64` the maximum pressure [Pa]
28772913
28782914
"""
28792915
function set_aerosol!(atmos::atmosphere.Atmos_t, species::String,
2880-
mmr::Union{Array{Float64, 1}, Float64} = 0.0;
2916+
mmr::Union{Array{Float64, 1}, Float64, String} = 0.0;
28812917
pmin::Float64 = 0.0, pmax::Float64 = 1e9)::Bool
28822918

2919+
2920+
# Reset
2921+
fill!(atmos.aerosol_arr_l[species], 0.0)
2922+
fill!(atmos.aerosol_arr_r[species], 0.0)
2923+
28832924
# Mask for pressure range
28842925
idx_mask = (atmos.p .>= pmin) .& (atmos.p .<= pmax)
28852926

28862927
# Set mixing ratio profile for aerosol
2887-
if isa(mmr, Float64)
2928+
if isa(mmr, String)
2929+
# set by species mmr
2930+
for i in collect(1:atmos.nlev_c)[idx_mask]
2931+
atmos.aerosol_arr_l[species][i] = atmosphere.calc_cond_mmr(atmos, mmr, i)
2932+
end
2933+
elseif isa(mmr, Float64)
28882934
# constant profile
28892935
atmos.aerosol_arr_l[species][idx_mask] .= mmr
28902936
else

src/chemistry.jl

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ module chemistry
256256

257257
# Apply cold trap, implicitly accounting for condensable not making it
258258
# this high up by vertical dynamical-transport processes.
259-
if atmos.gas_vmr[c][i] > maxvmr[c]
259+
if (atmos.gas_vmr[c][i] > maxvmr[c]) && atmos.coldtrap
260260
atmos.gas_vmr[c][i] = maxvmr[c]
261261
atmos.gas_sat[c][i] = true
262262
end
@@ -388,6 +388,13 @@ module chemistry
388388
atmosphere.set_cloud!(atmos; from_yield=true)
389389
end
390390

391+
# Set aerosol profiles at levels where condensation occurs
392+
for aer in atmos.aerosol_names
393+
if atmos.aerosol_setby[aer] != "value"
394+
atmosphere.set_aerosol!(atmos, aer, atmos.aerosol_setby[aer])
395+
end
396+
end
397+
391398
return nothing
392399
end
393400

@@ -706,6 +713,11 @@ module chemistry
706713
# recalculate layer properties
707714
atmosphere.calc_layer_props!(atmos)
708715

716+
# Warn on failure
717+
if state > 0
718+
@warn "FastChem internal failure; elements may not be conserved (state=$state)"
719+
end
720+
709721
# See docstring for return codes
710722
return state
711723
end
@@ -770,10 +782,10 @@ module chemistry
770782
end
771783
end
772784

773-
# keep cold trapping effect on abundances?
774-
if !atmos.coldtrap
775-
reset_to_chem!(atmos)
776-
end
785+
# keep cold trapping rainout effect on abundances?
786+
# if !atmos.coldtrap
787+
# reset_to_chem!(atmos)
788+
# end
777789

778790
return state
779791
end

src/solver.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -961,30 +961,30 @@ module solver
961961
rm(path_plt, force=true)
962962
rm(path_jac, force=true)
963963
elseif code == CODE_ITE
964-
@error " failure (maximum iterations)"
964+
@warn " failure (maximum iterations)"
965965
plot_step()
966966
elseif code == CODE_SIN
967-
@error " failure (singular jacobian)"
967+
@warn " failure (singular jacobian)"
968968
plotting.jacobian(b, path_jac)
969969
plot_step()
970970
elseif code == CODE_TIM
971-
@error " failure (maximum time)"
971+
@warn " failure (maximum time)"
972972
elseif code == CODE_NAN
973-
@error " failure (NaN values)"
973+
@warn " failure (NaN values)"
974974
plot_step()
975975
elseif code == CODE_CFG
976-
@error " failure (configuration)"
976+
@warn " failure (configuration)"
977977
elseif code == CODE_OBJ
978-
@error " failure (objective function)"
978+
@warn " failure (objective function)"
979979
plot_step()
980980
elseif code == CODE_STP
981-
@error " failure (other; last step not ok)"
981+
@warn " failure (other; last step not ok)"
982982
plot_step()
983983
elseif code == CODE_HYD
984-
@error " failure (hydrostatic integration)"
984+
@warn " failure (hydrostatic integration)"
985985
plot_step()
986986
else
987-
@error " failure (other)"
987+
@warn " failure (other)"
988988
plot_step()
989989
end
990990

0 commit comments

Comments
 (0)