diff --git a/src/AGNI.jl b/src/AGNI.jl index c511aff3..7c531fb4 100755 --- a/src/AGNI.jl +++ b/src/AGNI.jl @@ -336,9 +336,12 @@ module AGNI target_olr = cfg["planet"]["target_olr"] end - # Setup atmosphere - @info "Setting up" + # Create atmosphere structure + @debug "Instantiate atmosphere" atmos = atmosphere.Atmos_t() + + # Setup atmosphere + @debug "Setup atmosphere " return_success = atmosphere.setup!(atmos, ROOT_DIR, output_dir, spfile_name, instellation, asf_sf, albedo_b, zenith, diff --git a/src/atmosphere.jl b/src/atmosphere.jl index 8872d653..923c2c81 100644 --- a/src/atmosphere.jl +++ b/src/atmosphere.jl @@ -435,7 +435,6 @@ module atmosphere @error "VMRs provided twice" return false end - if isempty(mf_path) && isempty(mf_dict) @error "VMRs not provided" return false @@ -832,7 +831,8 @@ module atmosphere end # Mass (per unit area, kg m-2) and density (kg m-3) - for i in 1:atmos.atm.n_layer + # Loop from top downards + for i in 1:atmos.nlev_c atmos.layer_mass[i] = (atmos.pl[i+1] - atmos.pl[i])/atmos.layer_grav[i] atmos.atm.mass[1, i] = atmos.layer_mass[i] # pass to SOCRATES @@ -875,7 +875,8 @@ module atmosphere length=atmos.nlev_l-1)) # Shrink near-surface layers by stretching all layers above - p_mid::Float64 = atmos.pl[end-1]*0.6 + atmos.pl[end-2]*0.4 + p_fact::Float64 = 0.6 + p_mid::Float64 = atmos.pl[end-1]*p_fact + atmos.pl[end-2]*(1.0-p_fact) atmos.pl[1:end-2] .= collect(Float64, range( start=atmos.pl[1], stop=p_mid, length=atmos.nlev_l-2)) diff --git a/src/energy.jl b/src/energy.jl index 5068ce08..11b4c40b 100644 --- a/src/energy.jl +++ b/src/energy.jl @@ -473,14 +473,14 @@ module energy # Check for spurious shallow convection in condensing regions # If found, reset convective flux to zero AT THIS LAYER ONLY. - # for i in 2:atmos.nlev_l-1 - # if any(atmos.mask_l[i:end]) - # if atmos.mask_c[i] && !atmos.mask_c[i-1] && !atmos.mask_c[i+1] - # atmos.mask_c[i] = false - # atmos.flux_cdry[i] = 0.0 - # end - # end - # end + for i in 2:atmos.nlev_l-1 + if atmos.mask_l[i] + if atmos.mask_c[i] && !atmos.mask_c[i-1] && !atmos.mask_c[i+1] + atmos.mask_c[i] = false + atmos.flux_cdry[i] = 0.0 + end + end + end return nothing end # end of mlt @@ -539,8 +539,8 @@ module energy end # relaxation function - atmos.phs_wrk_df[i] = (atmos.gas_vmr[c][i]-qsat) * - atmos.p[i] * atmos.layer_thick[i]/atmos.phs_tau_sgl + atmos.phs_wrk_df[i] = (atmos.gas_vmr[c][i]-qsat)* atmos.layer_thick[i]* + (atmos.pl[i+1]-atmos.pl[i])/ atmos.phs_tau_sgl # flag layer as set by saturation if atmos.phs_wrk_df[i] > 1.0e-10 @@ -572,16 +572,16 @@ module energy E_accum = sum(atmos.phs_wrk_df[1:i_dry_top]) # evaporative flux in dry region - for i in i_dry_top:atmos.nlev_c + for i in i_dry_top+1:atmos.nlev_c - if E_accum < 1.0e-2 + if E_accum < 0.1 # dissipate all of the flux atmos.phs_wrk_df[i] = -E_accum E_accum = 0.0 break else # dissipate some fraction of the accumuated flux - atmos.phs_wrk_df[i] = -0.9*E_accum + atmos.phs_wrk_df[i] = -0.8*E_accum E_accum += atmos.phs_wrk_df[i] end diff --git a/src/solver.jl b/src/solver.jl index ba195f5b..aeb0f67e 100644 --- a/src/solver.jl +++ b/src/solver.jl @@ -91,6 +91,7 @@ module solver - `modplot::Int` iteration frequency at which to make plots - `save_frames::Bool` save plotting frames - `modprint::Int` iteration frequency at which to print info + - `plot_jacobian::Bool` plot jacobian too? - `conv_atol::Float64` convergence: absolute tolerance on per-level flux deviation [W m-2] - `conv_rtol::Float64` convergence: relative tolerance on per-level flux deviation [dimensionless] @@ -106,10 +107,10 @@ module solver max_steps::Int=2000, max_runtime::Float64=600.0, fdw::Float64=3.0e-5, fdc::Bool=true, fdo::Int=2, method::Int=1, - linesearch::Bool=true, easy_start::Bool=true, + linesearch::Bool=true, easy_start::Bool=false, detect_plateau::Bool=true, perturb_all::Bool=false, modplot::Int=1, save_frames::Bool=true, - modprint::Int=1, + modprint::Int=1, plot_jacobian::Bool=true, conv_atol::Float64=1.0e-2, conv_rtol::Float64=1.0e-3 )::Bool @@ -135,20 +136,21 @@ module solver # -------------------- # Execution parameters # -------------------- - # convection + # easy_start easy_incr::Float64 = 3.0 # Factor by which to increase easy_sf at each step - easy_sf::Float64 = 1.0e-5 # Convective & phase change flux scale factor + easy_sf::Float64 = 2.0e-4 # Convective & phase change flux scale factor + easy_trig::Float64 = 0.5 # Increase sf when cost*easy_trig satisfies convergence # finite difference fdr::Float64 = 0.01 # Use forward difference if cost ratio is below this value - perturb_conv::Float64 = 0.02 # Require full Jacobian update when c_cur*peturb_conv satisfies convergence + perturb_trig::Float64 = 0.1 # Require full Jacobian update when cost*peturb_trig satisfies convergence perturb_crit::Float64 = 0.1 # Require Jacobian update at level i when r_i>perturb_crit perturb_mod::Int = 10 # Do full jacobian at least this frequently # linesearch ls_method::Int = 1 # linesearch algorithm (1: golden, 2: backtracking) ls_tau::Float64 = 0.5 # backtracking downscale size - ls_increase::Float64 = 1.5 # factor by which cost can increase + ls_increase::Float64 = 2.0 # factor by which cost can increase ls_max_steps::Int = 15 # maximum steps ls_min_scale::Float64 = 4.0e-5 # minimum scale @@ -195,6 +197,7 @@ module solver runtime::Float64 = 0.0 # Model runtime [s] fc_retcode::Int = 0 # Fastchem return code step_ok::Bool = true # Current step was fine + easy_step::Bool = false # easy_start sf increased in this step plateau_i::Int = 0 # Number of iterations for which step was small # statistics @@ -233,10 +236,10 @@ module solver fill!(atmos.mask_c, false) fill!(atmos.mask_l, false) - # Set temperatures + # Set new temperatures _set_tmps!(x) - # Calculate layer properties if required and haven't already + # Calculate layer properties atmosphere.calc_layer_props!(atmos) # Handle rainout (but not energy release) @@ -509,22 +512,26 @@ module solver step_ok = step_ok && atmosphere.calc_layer_props!(atmos) # Check convective modulation + easy_step = false if easy_start # We are modulating stepflags *= "M" # Check if sf needs to be increased - if c_cur < max(100.0*conv_rtol, 10.0) + if c_cur*easy_trig < conv_atol + conv_rtol * c_max if easy_sf < 1.0 # increase sf => reduce modulation by easy_incr stepflags *= "r" easy_sf = min(1.0, easy_sf*easy_incr) + easy_step = true @debug "easy_sf = $easy_sf" # done modulating if easy_sf > 0.99 easy_start = false end + else + easy_sf = 1.0 end end @@ -539,8 +546,8 @@ module solver # Determine which parts of the Jacobian matrix need to be updated @debug " jacobian" - if (step == 1) || perturb_all || - (c_cur*perturb_conv < conv_atol + conv_rtol * c_max) || + if (step == 1) || perturb_all || easy_step || + (c_cur*perturb_trig < conv_atol + conv_rtol * c_max) || mod(step,perturb_mod)==0 # Update whole matrix if: # on first step, was requested, or near global convergence @@ -613,9 +620,9 @@ module solver stepflags *= "Lm-" end - # Extrapolate step if on plateau - # this acts to give the solver a 'nudge' in (hopefully) the - # right direction. Otherwise, this perturbation can still help. + # Extrapolate step if on plateau. + # This acts to give the solver a 'nudge' in (hopefully) the right direction. + # Otherwise, this perturbation can still help. if plateau_i > plateau_n x_dif[:] .*= plateau_s plateau_i = 0 @@ -627,55 +634,65 @@ module solver # Linesearch # https://people.maths.ox.ac.uk/hauser/hauser_lecture2.pdf - if linesearch #&& (step > 1) + if linesearch @debug " linesearch" # Reset - stepflags *= "Ls-" ls_alpha = 1.0 ls_cost = 1.0e99 # big number - # Internal function minimised by GS search + # Internal function minimised by linesearch method function _ls_func(scale::Float64)::Float64 x_cur[:] .= x_old[:] .+ scale .* x_dif[:] _fev!(x_cur,r_tst) return _cost(r_tst) end - if ls_method == 1 - # Use golden-section minimisation to find best scale - ls_alpha = gs_search(_ls_func, ls_min_scale, ls_alpha, - 1.0e-9, ls_min_scale, ls_max_steps) - - elseif ls_method == 2 - # Use backtracking line search to find best scale - for il in 1:ls_max_steps - ls_cost = _ls_func(ls_alpha) - if ls_cost <= c_cur*ls_increase - # this scale is good enough - break - else - # scale is not good - try shrinking it further + # Do we need to do linesearch? + # A small amount of cost increase is allowed. + # Get the cost if we used the full step size. + ls_cost = _ls_func(ls_alpha) + if ls_cost > c_cur*ls_increase + + # Yes, we do need to do linesearch... + stepflags *= "Ls-" + + if ls_method == 1 + # Use golden-section search method + ls_alpha = gs_search(_ls_func, ls_min_scale, ls_alpha, + 1.0e-9, ls_min_scale, ls_max_steps) + + elseif ls_method == 2 + # Use backtracking method + + for il in 1:ls_max_steps + # try shrinking it further ls_alpha *= ls_tau + if ls_alpha < ls_min_scale # scale too small! ls_alpha = ls_min_scale break end + + ls_cost = _ls_func(ls_alpha) + if ls_cost <= c_cur*ls_increase + # this scale is good enough + break + end end - end - else - @error "Invalid linesearch algorithm $ls_method" - code = 99 - break + else + @error "Invalid linesearch algorithm $ls_method" + code = 99 + break + end + + # Apply best scale from linesearch + ls_alpha = max(ls_alpha, ls_min_scale) + x_dif[:] .*= ls_alpha end - ls_alpha = max(ls_alpha, ls_min_scale) - - # Apply best scale from linesearch - x_dif[:] .*= ls_alpha - end # end linesearch # Take the step @@ -709,7 +726,9 @@ module solver # Plot if (modplot > 0) && (mod(step, modplot) == 0) plot_step() - # plotting.jacobian(b, path_jac, perturb=perturb) + if plot_jacobian + plotting.jacobian(b, path_jac, perturb=perturb) + end end # Inform user