Skip to content

Commit ad4e9c4

Browse files
authored
Merge pull request #754 from CliMA/ln/generalize-to-dry-norad
Generalize atmos_init to dry and no radiation cases
2 parents 34553a4 + c76c080 commit ad4e9c4

File tree

7 files changed

+227
-118
lines changed

7 files changed

+227
-118
lines changed

.buildkite/pipeline.yml

+7
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ steps:
162162
agents:
163163
slurm_mem: 20GB
164164

165+
- label: "Slabplanet: dry, no radiation"
166+
key: "slabplanet_dry_norad"
167+
command: "julia --color=yes --project=experiments/AMIP/ experiments/AMIP/coupler_driver.jl --config_file $CONFIG_PATH/slabplanet_dry_norad.yml"
168+
artifact_paths: "experiments/AMIP/output/slabplanet/slabplanet_dry_norad_artifacts/*"
169+
agents:
170+
slurm_mem: 20GB
171+
165172
- label: "Slabplanet: default with Float32"
166173
key: "slabplanet_ft32"
167174
command: "julia --color=yes --project=experiments/AMIP/ experiments/AMIP/coupler_driver.jl --config_file $CONFIG_PATH/slabplanet_ft32.yml"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
anim: false
2+
apply_limiter: false
3+
conservation_softfail: true
4+
dt: "200secs"
5+
dt_cpl: 200
6+
dt_save_to_sol: "9days"
7+
energy_check: true
8+
h_elem: 4
9+
job_id: "slabplanet_dry_norad"
10+
mode_name: "slabplanet"
11+
moist: "dry"
12+
mono_surface: true
13+
precip_model: nothing
14+
rad: nothing
15+
run_name: "slabplanet_dry_norad"
16+
t_end: "10days"
17+
vert_diff: "true"
18+
output_default_diagnostics: false

experiments/AMIP/cli_options.jl

-13
Original file line numberDiff line numberDiff line change
@@ -185,19 +185,6 @@ function print_repl_script(str)
185185
println(ib)
186186
end
187187

188-
function time_to_seconds(s::String)
189-
factor = Dict("secs" => 1, "mins" => 60, "hours" => 60 * 60, "days" => 60 * 60 * 24)
190-
s == "Inf" && return Inf
191-
if count(occursin.(keys(factor), Ref(s))) != 1
192-
error("Bad format for flag $s. Examples: [`10secs`, `20mins`, `30hours`, `40days`]")
193-
end
194-
for match in keys(factor)
195-
occursin(match, s) || continue
196-
return parse(Float64, first(split(s, match))) * factor[match]
197-
end
198-
error("Uncaught case in computing time from given string.")
199-
end
200-
201188
parsed_args_from_ARGS(ARGS, parsed_args = Dict()) = parsed_args_from_ARGS_string(strip(join(ARGS, " ")), parsed_args)
202189

203190
parsed_args_from_command_line_flags(str, parsed_args = Dict()) =

experiments/AMIP/components/atmosphere/climaatmos.jl

+101-65
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ struct ClimaAtmosSimulation{P, Y, D, I} <: Interfacer.AtmosModelSimulation
2121
end
2222
Interfacer.name(::ClimaAtmosSimulation) = "ClimaAtmosSimulation"
2323

24+
function hasradiation(integrator)
25+
return !isnothing(integrator.p.atmos.radiation_mode)
26+
end
27+
28+
function hasmoisture(integrator)
29+
return !(integrator.p.atmos.moisture_model isa CA.DryModel)
30+
end
31+
2432
function atmos_init(::Type{FT}, atmos_config_dict::Dict) where {FT}
2533
# By passing `parsed_args` to `AtmosConfig`, `parsed_args` overwrites the default atmos config
2634
atmos_config = CA.AtmosConfig(atmos_config_dict)
@@ -34,27 +42,30 @@ function atmos_init(::Type{FT}, atmos_config_dict::Dict) where {FT}
3442
@warn("Running with ρe_int in coupled mode is not tested yet.", maxlog = 1)
3543
end
3644

37-
# define shorter references for long variable Interfacer.names to increase readability
45+
# define shorter references for long variable names to increase readability, and set to zero
3846
ρ_flux_h_tot = integrator.p.precomputed.sfc_conditions.ρ_flux_h_tot
39-
ρ_flux_q_tot = integrator.p.precomputed.sfc_conditions.ρ_flux_q_tot
40-
ᶠradiation_flux = integrator.p.radiation.ᶠradiation_flux
4147
ρ_flux_uₕ = integrator.p.precomputed.sfc_conditions.ρ_flux_uₕ
42-
col_integrated_rain = integrator.p.precipitation.col_integrated_rain
43-
col_integrated_snow = integrator.p.precipitation.col_integrated_snow
44-
ᶜS_ρq_tot = integrator.p.precipitation.ᶜS_ρq_tot
45-
ᶜ3d_rain = integrator.p.precipitation.ᶜ3d_rain
46-
ᶜ3d_snow = integrator.p.precipitation.ᶜ3d_snow
47-
48-
# set initial fluxes to zero
4948
@. ρ_flux_h_tot = CC.Geometry.Covariant3Vector(FT(0.0))
50-
@. ρ_flux_q_tot = CC.Geometry.Covariant3Vector(FT(0.0))
51-
@. ᶠradiation_flux = CC.Geometry.WVector(FT(0))
5249
ρ_flux_uₕ.components .= Ref(StaticArrays.SMatrix{1, 2}([FT(0), FT(0)]))
53-
col_integrated_rain .= FT(0)
54-
col_integrated_snow .= FT(0)
55-
ᶜS_ρq_tot .= FT(0)
56-
ᶜ3d_rain .= FT(0)
57-
ᶜ3d_snow .= FT(0)
50+
51+
if hasmoisture(integrator)
52+
ρ_flux_q_tot = integrator.p.precomputed.sfc_conditions.ρ_flux_q_tot
53+
col_integrated_rain = integrator.p.precipitation.col_integrated_rain
54+
col_integrated_snow = integrator.p.precipitation.col_integrated_snow
55+
ᶜS_ρq_tot = integrator.p.precipitation.ᶜS_ρq_tot
56+
ᶜ3d_rain = integrator.p.precipitation.ᶜ3d_rain
57+
ᶜ3d_snow = integrator.p.precipitation.ᶜ3d_snow
58+
@. ρ_flux_q_tot = CC.Geometry.Covariant3Vector(FT(0.0))
59+
col_integrated_rain .= FT(0)
60+
col_integrated_snow .= FT(0)
61+
ᶜS_ρq_tot .= FT(0)
62+
ᶜ3d_rain .= FT(0)
63+
ᶜ3d_snow .= FT(0)
64+
end
65+
if hasradiation(integrator)
66+
ᶠradiation_flux = integrator.p.radiation.ᶠradiation_flux
67+
@. ᶠradiation_flux = CC.Geometry.WVector(FT(0))
68+
end
5869

5970
sim = ClimaAtmosSimulation(integrator.p.params, Y, spaces, integrator)
6071

@@ -98,7 +109,7 @@ upward and downward longwave and shortwave radiation.
98109
function Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_toa})
99110
FT = eltype(atmos_sim.integrator.u)
100111

101-
if atmos_sim.integrator.p.radiation.radiation_model != nothing
112+
if hasradiation(atmos_sim.integrator)
102113
face_space = axes(atmos_sim.integrator.u.f)
103114
nz_faces = length(CC.Spaces.vertical_topology(face_space).mesh.faces)
104115

@@ -116,49 +127,74 @@ function Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_
116127

117128
return @. -(LWd_TOA + SWd_TOA - LWu_TOA - SWu_TOA)
118129
else
119-
return FT(0)
130+
return [FT(0)]
120131
end
121132
end
122133

123134
function Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:energy})
124-
thermo_params = get_thermo_params(atmos_sim)
135+
integrator = atmos_sim.integrator
136+
p = integrator.p
125137

126-
ᶜS_ρq_tot = atmos_sim.integrator.p.precipitation.ᶜS_ρq_tot
127-
ᶜts = atmos_sim.integrator.p.precomputed.ᶜts
128-
ᶜΦ = atmos_sim.integrator.p.core.ᶜΦ
129138

130139
# return total energy and (if Microphysics0Moment) the energy lost due to precipitation removal
131-
if atmos_sim.integrator.p.atmos.precip_model isa CA.Microphysics0Moment
132-
ᶜS_ρq_tot = atmos_sim.integrator.p.precipitation.ᶜS_ρq_tot
133-
ᶜts = atmos_sim.integrator.p.precomputed.ᶜts
134-
ᶜΦ = atmos_sim.integrator.p.core.ᶜΦ
135-
return atmos_sim.integrator.u.c.ρe_tot .-
136-
ᶜS_ρq_tot .* CA.e_tot_0M_precipitation_sources_helper.(Ref(thermo_params), ᶜts, ᶜΦ) .*
137-
atmos_sim.integrator.dt
140+
if p.atmos.precip_model isa CA.Microphysics0Moment
141+
ᶜts = p.precomputed.ᶜts
142+
ᶜΦ = p.core.ᶜΦ
143+
ᶜS_ρq_tot = p.precipitation.ᶜS_ρq_tot
144+
thermo_params = get_thermo_params(atmos_sim)
145+
return integrator.u.c.ρe_tot .-
146+
ᶜS_ρq_tot .* CA.e_tot_0M_precipitation_sources_helper.(Ref(thermo_params), ᶜts, ᶜΦ) .* integrator.dt
138147
else
139-
return atmos_sim.integrator.u.c.ρe_tot
148+
return integrator.u.c.ρe_tot
140149
end
141150
end
142151

152+
# helpers for get_field extensions, dipatchable on different moisture model options and radiation modes
153+
154+
col_integrated_rain(::CA.DryModel, integrator) = [eltype(integrator.u)(0)]
155+
col_integrated_rain(::Union{CA.EquilMoistModel, CA.NonEquilMoistModel}, integrator) =
156+
integrator.p.precipitation.col_integrated_rain
157+
158+
col_integrated_snow(::CA.DryModel, integrator) = [eltype(integrator.u)(0)]
159+
col_integrated_snow(::Union{CA.EquilMoistModel, CA.NonEquilMoistModel}, integrator) =
160+
integrator.p.precipitation.col_integrated_snow
161+
162+
surface_radiation_flux(::Nothing, integrator) = [eltype(integrator.u)(0)]
163+
surface_radiation_flux(::CA.RRTMGPI.AbstractRRTMGPMode, integrator) =
164+
CC.Fields.level(integrator.p.radiation.ᶠradiation_flux, CC.Utilities.half)
165+
166+
moisture_flux(::CA.DryModel, integrator) = [eltype(integrator.u)(0)]
167+
moisture_flux(::Union{CA.EquilMoistModel, CA.NonEquilMoistModel}, integrator) =
168+
CC.Geometry.WVector.(integrator.p.precomputed.sfc_conditions.ρ_flux_q_tot)
169+
170+
ρq_tot(::CA.DryModel, integrator) = [eltype(integrator.u)(0)]
171+
ρq_tot(::Union{CA.EquilMoistModel, CA.NonEquilMoistModel}, integrator) = integrator.u.c.ρq_tot
172+
143173
# extensions required by the Interfacer
144174
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:air_density}) =
145175
TD.air_density.(thermo_params, sim.integrator.p.precomputed.ᶜts)
146176
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:air_temperature}) =
147177
TD.air_temperature.(thermo_params, sim.integrator.p.precomputed.ᶜts)
148178
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:liquid_precipitation}) =
149-
sim.integrator.p.precipitation.col_integrated_rain
179+
col_integrated_rain(sim.integrator.p.atmos.moisture_model, sim.integrator)
150180
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_sfc}) =
151-
CC.Fields.level(sim.integrator.p.radiation.ᶠradiation_flux, CC.Utilities.half)
181+
surface_radiation_flux(sim.integrator.p.atmos.radiation_mode, sim.integrator)
152182
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:snow_precipitation}) =
153-
sim.integrator.p.precipitation.col_integrated_snow
183+
col_integrated_snow(sim.integrator.p.atmos.moisture_model, sim.integrator)
154184
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:turbulent_energy_flux}) =
155185
CC.Geometry.WVector.(sim.integrator.p.precomputed.sfc_conditions.ρ_flux_h_tot)
156186
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:turbulent_moisture_flux}) =
157-
CC.Geometry.WVector.(sim.integrator.p.precomputed.sfc_conditions.ρ_flux_q_tot)
187+
moisture_flux(sim.integrator.p.atmos.moisture_model, sim.integrator)
158188
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:thermo_state_int}) =
159189
CC.Spaces.level(sim.integrator.p.precomputed.ᶜts, 1)
160-
Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:water}) = atmos_sim.integrator.u.c.ρq_tot
161-
190+
Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:water}) =
191+
ρq_tot(atmos_sim.integrator.p.atmos.moisture_model, atmos_sim.integrator)
192+
function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_temperature}, csf)
193+
# note that this field is also being updated internally by the surface thermo state in ClimaAtmos
194+
# if turbulent fluxes are calculated, to ensure consistency. In case the turbulent fluxes are not
195+
# calculated, we update the field here.
196+
sim.integrator.p.radiation.radiation_model.surface_temperature .= CA.RRTMGPI.field2array(csf.T_S)
197+
end
162198
# extensions required by FluxCalculator (partitioned fluxes)
163199
Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:height_int}) =
164200
CC.Spaces.level(CC.Fields.coordinate_field(sim.integrator.u.c).z, 1)
@@ -178,10 +214,6 @@ function Interfacer.update_field!(atmos_sim::ClimaAtmosSimulation, ::Val{:co2},
178214
end
179215
end
180216
# extensions required by the Interfacer
181-
function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_temperature}, csf)
182-
sim.integrator.p.radiation.radiation_model.surface_temperature .= CA.RRTMGPI.field2array(csf.T_S)
183-
end
184-
185217
function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_direct_albedo}, field)
186218
sim.integrator.p.radiation.radiation_model.direct_sw_surface_albedo .=
187219
reshape(CA.RRTMGPI.field2array(field), 1, length(parent(field)))
@@ -196,7 +228,7 @@ function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:turbulent_fl
196228
(; F_turb_energy, F_turb_moisture, F_turb_ρτxz, F_turb_ρτyz) = fields
197229

198230
Y = sim.integrator.u
199-
surface_local_geometry = CC.Fields.level(CC.Fields.local_geometry_field(Y.f), CC.Fields.half)
231+
surface_local_geometry = CC.Fields.level(CC.Fields.local_geometry_field(Y.f), CC.Utilities.half)
200232
surface_normal = @. CA.C3(CA.unit_basis_vector_data(CA.C3, surface_local_geometry))
201233

202234
# get template objects for the contravariant components of the momentum fluxes (required by Atmos boundary conditions)
@@ -229,11 +261,16 @@ function FieldExchanger.update_sim!(atmos_sim::ClimaAtmosSimulation, csf, turbul
229261
p = atmos_sim.integrator.p
230262
t = atmos_sim.integrator.t
231263

232-
!p.radiation.idealized_insolation && CA.set_insolation_variables!(u, p, t)
264+
!isempty(atmos_sim.integrator.p.radiation) &&
265+
!p.radiation.idealized_insolation &&
266+
CA.set_insolation_variables!(u, p, t)
267+
268+
if hasradiation(atmos_sim.integrator)
269+
Interfacer.update_field!(atmos_sim, Val(:surface_direct_albedo), csf.surface_direct_albedo)
270+
Interfacer.update_field!(atmos_sim, Val(:surface_diffuse_albedo), csf.surface_diffuse_albedo)
271+
end
233272

234-
Interfacer.update_field!(atmos_sim, Val(:surface_direct_albedo), csf.surface_direct_albedo)
235-
Interfacer.update_field!(atmos_sim, Val(:surface_diffuse_albedo), csf.surface_diffuse_albedo)
236-
Interfacer.update_field!(atmos_sim, Val(:surface_temperature), csf)
273+
!isempty(atmos_sim.integrator.p.radiation) && Interfacer.update_field!(atmos_sim, Val(:surface_temperature), csf)
237274

238275
if turbulent_fluxes isa FluxCalculator.PartitionedStateFluxes
239276
Interfacer.update_field!(atmos_sim, Val(:turbulent_fluxes), csf)
@@ -258,12 +295,12 @@ FluxCalculator.get_surface_params(sim::ClimaAtmosSimulation) = CAP.surface_fluxe
258295
### ClimaAtmos.jl model-specific functions (not explicitly required by ClimaCoupler.jl)
259296
###
260297
"""
261-
get_atmos_config(coupler_dict::Dict)
298+
get_atmos_config_dict(coupler_dict::Dict)
262299
263-
Returns the specified atmospheric configuration (`atmos_config_dict`) overwitten by arguments
264-
in the coupler dictionary (`config_dict`).
300+
Returns the specified atmospheric configuration (`atmos_config`) overwitten by arguments
301+
in the coupler dictionary (`config_dict`). The returned dictionary will then be passed to CA.AtmosConfig().
265302
"""
266-
function get_atmos_config(coupler_dict)
303+
function get_atmos_config_dict(coupler_dict)
267304
atmos_config_file = coupler_dict["atmos_config_file"]
268305
# override default or specified configs with coupler arguments, and set the correct atmos config_file
269306
if isnothing(atmos_config_file)
@@ -318,8 +355,12 @@ Sets up `surface_setup` as a `CC.Fields.Field` of `SurfaceState`s.
318355
"""
319356
function coupler_surface_setup(
320357
::CoupledMoninObukhov,
321-
p;
322-
csf_sfc = (; T = nothing, z0m = nothing, z0b = nothing, beta = nothing, q_vap = nothing),
358+
p,
359+
T = nothing,
360+
z0m = nothing,
361+
z0b = nothing,
362+
beta = nothing,
363+
q_vap = nothing,
323364
)
324365

325366
surface_state(z0m, z0b, T, beta, q_vap) = CA.SurfaceConditions.SurfaceState(;
@@ -328,7 +369,7 @@ function coupler_surface_setup(
328369
beta,
329370
q_vap,
330371
)
331-
surface_state_field = @. surface_state(csf_sfc.z0m, csf_sfc.z0b, csf_sfc.T, csf_sfc.beta, csf_sfc.q_vap)
372+
surface_state_field = @. surface_state(z0m, z0b, T, beta, q_vap)
332373
return surface_state_field
333374
end
334375

@@ -338,23 +379,18 @@ end
338379
Returns a new `p` with the updated surface conditions.
339380
"""
340381
function get_new_cache(atmos_sim::ClimaAtmosSimulation, csf)
341-
csf_sfc = (; T = csf.T_S, z0m = csf.z0m_S, z0b = csf.z0b_S, beta = csf.beta, q_vap = csf.q_sfc)
342-
modified_atmos_cache(atmos_sim, csf_sfc)
343-
end
344-
345-
"""
346-
modified_atmos_cache(atmos_sim, csf_sfc)
347-
348-
Returns a new `p` with the updated surface conditions.
349-
"""
350-
function modified_atmos_cache(atmos_sim, csf_sfc)
382+
if hasmoisture(atmos_sim.integrator)
383+
csf_sfc = (csf.T_S, csf.z0m_S, csf.z0b_S, csf.beta, csf.q_sfc)
384+
else
385+
csf_sfc = (csf.T_S, csf.z0m_S, csf.z0b_S, csf.beta)
386+
end
351387

352388
p = atmos_sim.integrator.p
353389

354-
coupler_sfc_setup = coupler_surface_setup(CoupledMoninObukhov(), p; csf_sfc = csf_sfc)
390+
coupler_sfc_setup = coupler_surface_setup(CoupledMoninObukhov(), p, csf_sfc...)
355391

356392
p_names = propertynames(p)
357-
p_values = map(x -> x == :sfc_setup ? coupler_sfc_setup : getproperty(p, x), p_names) # TODO: use merge here
393+
p_values = map(x -> x == :sfc_setup ? coupler_sfc_setup : getproperty(p, x), p_names)
358394

359395
(; zip(p_names, p_values)...)
360396
end

0 commit comments

Comments
 (0)