Skip to content

Commit 51f6711

Browse files
committed
init
clean
1 parent eccb719 commit 51f6711

File tree

1 file changed

+374
-0
lines changed

1 file changed

+374
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
# # Cloudless Aquaplanet
2+
3+
redirect_stderr(IOContext(stderr, :stacktrace_types_limited => Ref(false)))
4+
5+
#=
6+
## Configuration initialization
7+
=#
8+
9+
#=
10+
### Package Import
11+
=#
12+
13+
## standard packages
14+
import Dates
15+
import YAML
16+
17+
# ## ClimaESM packages
18+
import ClimaAtmos as CA
19+
import ClimaComms
20+
import ClimaCore as CC
21+
22+
# ## Coupler specific imports
23+
import ClimaCoupler
24+
import ClimaCoupler:
25+
BCReader,
26+
ConservationChecker,
27+
Checkpointer,
28+
Diagnostics,
29+
FieldExchanger,
30+
FluxCalculator,
31+
Interfacer,
32+
Regridder,
33+
TimeManager,
34+
Utilities
35+
36+
pkg_dir = pkgdir(ClimaCoupler)
37+
38+
#=
39+
### Helper Functions
40+
These will be eventually moved to their respective component model and diagnostics packages, and so they should not
41+
contain any internals of the ClimaCoupler source code, except extensions to the Interfacer functions.
42+
=#
43+
44+
## helpers for component models
45+
include("components/atmosphere/climaatmos.jl")
46+
include("components/ocean/slab_ocean.jl")
47+
48+
## helpers for user-specified IO
49+
include("user_io/user_diagnostics.jl")
50+
include("user_io/user_logging.jl")
51+
52+
include("user_io/io_helpers.jl")
53+
54+
#=
55+
### Setup simulation parameters
56+
Here we follow ClimaCore's dry Held-Suarez `held_suarez_rhoe` example.
57+
=#
58+
59+
## run names
60+
run_name = "cloudless_aquaplanet"
61+
coupler_output_dir = "$run_name"
62+
const FT = Float64
63+
restart_dir = "unspecified"
64+
restart_t = Int(0)
65+
66+
## coupler simulation specific configuration
67+
Δt_cpl = Float64(400)
68+
t_end = "1000days"
69+
tspan = (Float64(0.0), Float64(time_to_seconds(t_end)))
70+
start_date = "19790301"
71+
hourly_checkpoint = true
72+
73+
## namelist
74+
config_dict = Dict(
75+
# general
76+
"FLOAT_TYPE" => string(FT),
77+
# file paths
78+
"atmos_config_file" => nothing,
79+
"coupler_toml_file" => nothing,
80+
"coupler_output_dir" => coupler_output_dir,
81+
"mode_name" => "",
82+
"run_name" => run_name,
83+
"atmos_config_repo" => "ClimaAtmos",
84+
# timestepping
85+
"dt" => "$(Δt_cpl)secs",
86+
"dt_save_to_sol" => "1days",
87+
"t_end" => t_end,
88+
"start_date" => "19790301",
89+
# domain
90+
"h_elem" => 4,
91+
"z_elem" => 10,
92+
"z_max" => 30000.0, # semi-high top
93+
"dz_bottom" => 300.0,
94+
"nh_poly" => 4,
95+
# output
96+
"dt_save_to_sol" => "1days",
97+
# numerics
98+
"apply_limiter" => false,
99+
"viscous_sponge" => false,
100+
"rayleigh_sponge" => false,
101+
"vert_diff" => "true",
102+
"hyperdiff" => "ClimaHyperdiffusion",
103+
# run
104+
"job_id" => run_name,
105+
"surface_setup" => "PrescribedSurface",
106+
# diagnostic (nested with period and short_name)
107+
"output_default_diagnostics" => false,
108+
"diagnostics" => [
109+
Dict(
110+
"short_name" => [
111+
"mse",
112+
"lr",
113+
"mass_strf",
114+
"stab",
115+
"vt",
116+
"egr",
117+
"ua",
118+
"va",
119+
"wa",
120+
"ta",
121+
"rhoa",
122+
"pfull",
123+
],
124+
"period" => "6hours",
125+
"reduction" => "inst",
126+
),
127+
],
128+
# held-suarez specific
129+
"precip_model" => "0M",
130+
"moist" => "equil",
131+
"prognostic_surface" => "PrescribedSurfaceTemperature",
132+
"turb_flux_partition" => "CombinedStateFluxes",
133+
"rad" => "gray",
134+
)
135+
# TODO: increase diffusion coef?
136+
137+
## merge dictionaries of command line arguments, coupler dictionary and component model dictionaries
138+
atmos_config_dict, config_dict = get_atmos_config_dict(config_dict)
139+
atmos_config_object = CA.AtmosConfig(atmos_config_dict)
140+
141+
#=
142+
## Setup Communication Context
143+
=#
144+
145+
comms_ctx = Utilities.get_comms_context(Dict("device" => "auto"))
146+
ClimaComms.init(comms_ctx)
147+
148+
#=
149+
### I/O Directory Setup
150+
=#
151+
152+
dir_paths = setup_output_dirs(output_dir = coupler_output_dir, comms_ctx = comms_ctx)
153+
ClimaComms.iamroot(comms_ctx) ? @info(config_dict) : nothing
154+
155+
#=
156+
## Component Model Initialization
157+
=#
158+
159+
#=
160+
### Atmosphere
161+
This uses the `ClimaAtmos.jl` model, with parameterization options specified in the `config_dict_atmos` dictionary.
162+
=#
163+
164+
## init atmos model component
165+
atmos_sim = atmos_init(atmos_config_object);
166+
thermo_params = get_thermo_params(atmos_sim)
167+
168+
#=
169+
### Boundary Space
170+
=#
171+
172+
## init a 2D boundary space at the surface
173+
boundary_space = CC.Spaces.horizontal_space(atmos_sim.domain.face_space) # TODO: specify this in the coupler and pass it to all component models #665
174+
175+
#=
176+
### Surface Model: Slab Ocean
177+
=#
178+
ocean_sim = ocean_init(
179+
FT;
180+
tspan = tspan,
181+
dt = Δt_cpl,
182+
space = boundary_space,
183+
saveat = saveat,
184+
area_fraction = ones(boundary_space),
185+
thermo_params = thermo_params,
186+
evolving = evolving_ocean,
187+
)
188+
189+
#=
190+
## Coupler Initialization
191+
=#
192+
193+
## coupler exchange fields
194+
coupler_field_names = (
195+
:T_S,
196+
:z0m_S,
197+
:z0b_S,
198+
:ρ_sfc,
199+
:q_sfc,
200+
:surface_direct_albedo,
201+
:surface_diffuse_albedo,
202+
:beta,
203+
:F_turb_energy,
204+
:F_turb_moisture,
205+
:F_turb_ρτxz,
206+
:F_turb_ρτyz,
207+
:F_radiative,
208+
:P_liq,
209+
:P_snow,
210+
:radiative_energy_flux_toa,
211+
:P_net,
212+
:temp1,
213+
:temp2,
214+
)
215+
coupler_fields =
216+
NamedTuple{coupler_field_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_field_names)))
217+
Utilities.show_memory_usage(comms_ctx)
218+
219+
## model simulations
220+
model_sims = (atmos_sim = atmos_sim, ocean_sim = ocean_sim);
221+
222+
## dates
223+
dates = (; date = [date], date0 = [date0], date1 = [Dates.firstdayofmonth(date0)], new_month = [false])
224+
225+
#=
226+
## Initialize Callbacks
227+
=#
228+
checkpoint_cb = TimeManager.HourlyCallback(
229+
dt = hourly_checkpoint_dt,
230+
func = checkpoint_sims,
231+
ref_date = [dates.date[1]],
232+
active = hourly_checkpoint,
233+
) # 20 days
234+
update_firstdayofmonth!_cb = TimeManager.MonthlyCallback(
235+
dt = FT(1),
236+
func = TimeManager.update_firstdayofmonth!,
237+
ref_date = [dates.date1[1]],
238+
active = true,
239+
)
240+
dt_water_albedo = parse(FT, filter(x -> !occursin(x, "hours"), dt_rad))
241+
albedo_cb = TimeManager.HourlyCallback(
242+
dt = dt_water_albedo,
243+
func = FluxCalculator.water_albedo_from_atmosphere!,
244+
ref_date = [dates.date[1]],
245+
active = mode_name == "amip",
246+
)
247+
callbacks =
248+
(; checkpoint = checkpoint_cb, update_firstdayofmonth! = update_firstdayofmonth!_cb, water_albedo = albedo_cb)
249+
250+
#=
251+
## Initialize turbulent fluxes
252+
253+
Decide on the type of turbulent flux partition (see `FluxCalculator` documentation for more details).
254+
=#
255+
turbulent_fluxes = nothing
256+
if config_dict["turb_flux_partition"] == "CombinedStateFluxes"
257+
turbulent_fluxes = FluxCalculator.CombinedStateFluxes()
258+
else
259+
error("turb_flux_partition must be either PartitionedStateFluxes or CombinedStateFluxes")
260+
end
261+
262+
#=
263+
## Initialize Coupled Simulation
264+
=#
265+
266+
cs = Interfacer.CoupledSimulation{FT}(
267+
comms_ctx,
268+
dates,
269+
boundary_space,
270+
coupler_fields,
271+
config_dict,
272+
conservation_checks,
273+
[tspan[1], tspan[2]],
274+
atmos_sim.integrator.t,
275+
Δt_cpl,
276+
(; land = land_area_fraction, ocean = zeros(boundary_space), ice = zeros(boundary_space)),
277+
model_sims,
278+
mode_specifics,
279+
diagnostics,
280+
callbacks,
281+
dir_paths,
282+
turbulent_fluxes,
283+
thermo_params,
284+
);
285+
286+
#=
287+
## Restart component model states if specified in the config_dict
288+
=#
289+
290+
if restart_dir !== "unspecified"
291+
for sim in cs.model_sims
292+
if Checkpointer.get_model_prog_state(sim) !== nothing
293+
Checkpointer.restart_model_state!(sim, comms_ctx, restart_t; input_dir = restart_dir)
294+
end
295+
end
296+
end
297+
298+
#=
299+
## Initialize Component Model Exchange
300+
=#
301+
302+
# 1.coupler updates surface model area fractions
303+
Regridder.update_surface_fractions!(cs)
304+
305+
# 2.surface density (`ρ_sfc`): calculated by the coupler by adiabatically extrapolating atmospheric thermal state to the surface.
306+
# For this, we need to import surface and atmospheric fields. The model sims are then updated with the new surface density.
307+
FieldExchanger.import_combined_surface_fields!(cs.fields, cs.model_sims, cs.turbulent_fluxes)
308+
FieldExchanger.import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes)
309+
FieldExchanger.update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes)
310+
311+
# 3.surface vapor specific humidity (`q_sfc`): step surface models with the new surface density to calculate their respective `q_sfc` internally
312+
Interfacer.step!(ocean_sim, Δt_cpl)
313+
314+
# 4.turbulent fluxes
315+
## import the new surface properties into the coupler (note the atmos state was also imported in step 3.)
316+
FieldExchanger.import_combined_surface_fields!(cs.fields, cs.model_sims, cs.turbulent_fluxes) # i.e. T_sfc, albedo, z0, beta, q_sfc
317+
## calculate turbulent fluxes inside the atmos cache based on the combined surface state in each grid box
318+
FluxCalculator.combined_turbulent_fluxes!(cs.model_sims, cs.fields, cs.turbulent_fluxes) # this updates the atmos thermo state, sfc_ts
319+
320+
# 5.reinitialize models + radiative flux: prognostic states and time are set to their initial conditions. For atmos, this also triggers the callbacks and sets a nonzero radiation flux (given the new sfc_conditions)
321+
FieldExchanger.reinit_model_sims!(cs.model_sims)
322+
323+
# 6.update all fluxes: coupler re-imports updated atmos fluxes
324+
FieldExchanger.import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes)
325+
FieldExchanger.update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes)
326+
327+
#=
328+
## Coupling Loop
329+
=#
330+
331+
function solve_coupler!(cs)
332+
(; Δt_cpl, tspan, comms_ctx) = cs
333+
334+
ClimaComms.iamroot(comms_ctx) && @info("Starting coupling loop")
335+
## step in time
336+
for t in ((tspan[begin] + Δt_cpl):Δt_cpl:tspan[end])
337+
338+
cs.dates.date[1] = TimeManager.current_date(cs, t)
339+
340+
## print date on the first of month
341+
if cs.dates.date[1] >= cs.dates.date1[1]
342+
ClimaComms.iamroot(comms_ctx) && @show(cs.dates.date[1])
343+
end
344+
345+
ClimaComms.barrier(comms_ctx)
346+
347+
## update water albedo from wind at dt_water_albedo (this will be extended to a radiation callback from the coupler)
348+
TimeManager.trigger_callback!(cs, cs.callbacks.water_albedo)
349+
350+
## run component models sequentially for one coupling timestep (Δt_cpl)
351+
FieldExchanger.update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes)
352+
353+
## step sims
354+
FieldExchanger.step_model_sims!(cs.model_sims, t)
355+
356+
## exchange combined fields and (if specified) calculate fluxes using combined states
357+
FieldExchanger.import_combined_surface_fields!(cs.fields, cs.model_sims, cs.turbulent_fluxes) # i.e. T_sfc, surface_albedo, z0, beta
358+
FluxCalculator.combined_turbulent_fluxes!(cs.model_sims, cs.fields, cs.turbulent_fluxes)
359+
360+
FieldExchanger.import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) # radiative and/or turbulent
361+
362+
## callback to update the fist day of month if needed (for BCReader)
363+
TimeManager.trigger_callback!(cs, cs.callbacks.update_firstdayofmonth!)
364+
365+
## callback to checkpoint model state
366+
TimeManager.trigger_callback!(cs, cs.callbacks.checkpoint)
367+
368+
end
369+
370+
return nothing
371+
end
372+
373+
374+
solve_coupler!(cs)

0 commit comments

Comments
 (0)