Skip to content

Commit 05553da

Browse files
committed
use ClimaParams for shared parameters in coupled sims
1 parent 39c9c8e commit 05553da

22 files changed

+343
-134
lines changed

experiments/ClimaEarth/components/atmosphere/climaatmos.jl

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import LinearAlgebra
55
import ClimaAtmos as CA
66
import ClimaAtmos: set_surface_albedo!
77
import ClimaAtmos.Parameters as CAP
8+
import ClimaParams as CP
89
import ClimaCore as CC
910
import ClimaCore.Geometry:
1011
import SurfaceFluxes as SF
@@ -611,14 +612,17 @@ The TOML parameter file to use is chosen using the following priority:
611612
If a coupler TOML file is provided, it is used.
612613
Otherwise we use an atmos TOML file if it's provided.
613614
If neither file is provided, the default ClimaAtmos parameters are used.
615+
This is analogous to the logic in `get_coupler_config_dict` for selecting
616+
the coupler configuration TOML file.
614617
"""
615-
function get_atmos_config_dict(coupler_config::Dict, atmos_output_dir)
618+
function get_atmos_config_dict(
619+
coupler_config::Dict,
620+
atmos_output_dir,
621+
coupled_param_dict::CP.ParamDict,
622+
comms_ctx,
623+
)
616624
atmos_config = deepcopy(coupler_config)
617625

618-
# Rename parameters for consistency with ClimaAtmos.jl
619-
# Rename atmosphere config file from ClimaCoupler convention to ClimaAtmos convention
620-
atmos_config["config_file"] = coupler_config["atmos_config_file"]
621-
622626
# Ensure Atmos's own checkpoints are synced up with ClimaCoupler, so that we
623627
# can pick up from where we have left. NOTE: This should not be needed, but
624628
# there is no easy way to initialize ClimaAtmos with a different t_start
@@ -631,18 +635,25 @@ function get_atmos_config_dict(coupler_config::Dict, atmos_output_dir)
631635
coupler_config["dt"]
632636
atmos_config["dt"] = dt_atmos
633637

634-
# Use atmos toml if coupler toml is not defined
635-
# If we can't find the file at the relative path, prepend pkgdir(ClimaAtmos)
638+
# Set up the atmosphere parameter dictionary (`toml_dict`)
639+
# If we can't find the atmos TOML file at the relative path, prepend pkgdir(ClimaAtmos)
636640
atmos_toml = map(atmos_config["toml"]) do file
637641
isfile(file) ? file : joinpath(pkgdir(CA), file)
638642
end
643+
# Use atmos toml only if coupler toml is not defined
639644
coupler_toml = atmos_config["coupler_toml"]
640645
toml = isempty(coupler_toml) ? atmos_toml : coupler_toml
641646
if !isempty(toml)
642647
@info "Overwriting Atmos parameters from input TOML file(s): $toml"
643648
atmos_config["toml"] = toml
644649
end
645650

651+
# Override atmos parameters with coupled parameters
652+
FT = atmos_config["FLOAT_TYPE"] == "Float64" ? Float64 : Float32
653+
override_file = CP.merge_toml_files(atmos_config["toml"], override = true)
654+
atmos_toml = CP.create_toml_dict(FT; override_file) # TODO use ClimaParams 1.0.3
655+
toml_dict = CP.merge_override_default_values(coupled_param_dict, atmos_toml)
656+
646657
# Specify atmos output directory to be inside the coupler output directory
647658
atmos_config["output_dir_style"] = "RemovePreexisting"
648659
atmos_config["output_dir"] = atmos_output_dir
@@ -659,7 +670,25 @@ function get_atmos_config_dict(coupler_config::Dict, atmos_output_dir)
659670
atmos_config["restart_file"] =
660671
replace(atmos_config["restart_file"], "active" => "0000")
661672
end
662-
return CA.AtmosConfig(atmos_config)
673+
674+
# Construct the AtmosConfig object
675+
toml_dict = coupled_param_dict
676+
config_files = atmos_config["toml"]
677+
job_id = atmos_config["job_id"]
678+
config = CA.override_default_config(atmos_config)
679+
680+
TD = typeof(toml_dict)
681+
PA = typeof(config)
682+
C = typeof(comms_ctx)
683+
CF = typeof(config_files)
684+
return CA.AtmosConfig{FT, TD, PA, C, CF}(
685+
toml_dict,
686+
config,
687+
comms_ctx,
688+
config_files,
689+
job_id,
690+
)
691+
# return CA.AtmosConfig(atmos_config)
663692
end
664693

665694
"""

experiments/ClimaEarth/components/land/climaland_bucket.jl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,17 @@ function BucketSimulation(
6666
stepper = CTS.RK4(),
6767
albedo_type::String = "map_static",
6868
bucket_initial_condition::String = "",
69-
energy_check::Bool = false,
70-
parameter_files = [],
69+
coupled_param_dict = CP.create_toml_dict(FT),
7170
) where {FT, TT <: Union{Float64, ITime}}
72-
# Construct the parameter dictionary based on the float type and any parameter files
73-
toml_dict = LP.create_toml_dict(FT; override_files = parameter_files)
71+
# Get default land parameters from ClimaLand.LandParameters
72+
land_toml_dict = LP.create_toml_dict(FT)
73+
# Override land parameters with coupled parameters
74+
toml_dict = CP.merge_override_default_values(coupled_param_dict, land_toml_dict)
7475

7576
# Note that this does not take into account topography of the surface, which is OK for this land model.
7677
# But it must be taken into account when computing surface fluxes, for Δz.
7778
if isnothing(shared_surface_space)
78-
domain = make_land_domain(depth; nelements, dz_tuple)
79+
domain = make_land_domain(depth, toml_dict; nelements, dz_tuple)
7980
else
8081
domain = make_land_domain(
8182
shared_surface_space,
@@ -379,11 +380,9 @@ function FieldExchanger.update_sim!(sim::BucketSimulation, csf)
379380
p = sim.integrator.p
380381
t = sim.integrator.t
381382

382-
# TODO: get sigma from parameters
383-
FT = eltype(Y)
384-
σ = FT(5.67e-8)
385383
# Note: here we add negative signs to account for a difference in sign convention
386384
# between ClimaLand.jl and ClimaCoupler.jl in SW_d and LW_d.
385+
σ = model.parameters.earth_param_set.Stefan
387386
sim.integrator.p.bucket.R_n .=
388387
.-(1 .- CL.surface_albedo(model, Y, p)) .* sim.radiative_fluxes.SW_d .-
389388
Interfacer.get_field(sim, Val(:emissivity)) .*

experiments/ClimaEarth/components/land/climaland_helpers.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import ClimaCore as CC
22
import ClimaLand as CL
3+
import ClimaParams as CP
4+
35
"""
46
temp_anomaly_aquaplanet(coord)
57
@@ -19,17 +21,22 @@ and result in stable simulations.
1921
temp_anomaly_amip(coord) = 40 * cosd(coord.lat)^4
2022

2123
"""
22-
make_land_domain(nelements::Tuple{Int, Int}, depth::FT) where {FT}
23-
24+
make_land_domain(
25+
depth::FT,
26+
toml_dict::CP.ParamDict;
27+
nelements::Tuple{Int, Int} = (101, 15),
28+
dz_tuple::Tuple{FT, FT} = FT.((10.0, 0.05)),
29+
) where {FT}
2430
Creates a land model domain with the given number of vertical elements
2531
and vertical depth.
2632
"""
2733
function make_land_domain(
28-
depth::FT;
34+
depth::FT,
35+
toml_dict::CP.ParamDict;
2936
nelements::Tuple{Int, Int} = (101, 15),
3037
dz_tuple::Tuple{FT, FT} = FT.((10.0, 0.05)),
3138
) where {FT}
32-
radius = FT(6378.1e3)
39+
radius = toml_dict["planet_radius"] # in meters
3340
npolynomial = 0
3441
domain = CL.Domains.SphericalShell(; radius, depth, nelements, npolynomial, dz_tuple)
3542
return domain

experiments/ClimaEarth/components/land/climaland_integrated.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,19 @@ function ClimaLandSimulation(
8686
atmos_h,
8787
land_temperature_anomaly::String = "amip",
8888
use_land_diagnostics::Bool = true,
89-
parameter_files = [],
89+
coupled_param_dict = CP.create_toml_dict(FT),
9090
land_ic_path::Union{Nothing, String} = nothing,
9191
) where {FT, TT <: Union{Float64, ITime}}
92+
# Get default land parameters from ClimaLand.LandParameters
93+
land_toml_dict = LP.create_toml_dict(FT)
94+
# Override land parameters with coupled parameters
95+
toml_dict = CP.merge_override_default_values(coupled_param_dict, land_toml_dict)
96+
earth_param_set = CL.Parameters.LandParameters(toml_dict)
97+
9298
# Note that this does not take into account topography of the surface, which is OK for this land model.
9399
# But it must be taken into account when computing surface fluxes, for Δz.
94100
if isnothing(shared_surface_space)
95-
domain = make_land_domain(depth; nelements, dz_tuple)
101+
domain = make_land_domain(depth, toml_dict; nelements, dz_tuple)
96102
else
97103
domain = make_land_domain(
98104
shared_surface_space,
@@ -114,10 +120,6 @@ function ClimaLandSimulation(
114120
# since that's where we compute fluxes for this land model
115121
atmos_h = Interfacer.remap(atmos_h, surface_space)
116122

117-
# Set up spatially-varying parameters
118-
toml_dict = LP.create_toml_dict(FT; override_files = parameter_files)
119-
earth_param_set = CL.Parameters.LandParameters(toml_dict)
120-
121123
# Set up atmosphere and radiation forcing
122124
forcing = (;
123125
atmos = CL.CoupledAtmosphere{FT}(surface_space, atmos_h),

experiments/ClimaEarth/components/ocean/oceananigans.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ClimaCoupler: Checkpointer, FieldExchanger, FluxCalculator, Interfacer, U
44
import ClimaComms
55
import ClimaCore as CC
66
import Thermodynamics as TD
7+
import ClimaParams as CP
78
import ClimaOcean.EN4: download_dataset
89
using KernelAbstractions: @kernel, @index, @inbounds
910

@@ -46,6 +47,7 @@ function OceananigansSimulation(
4647
stop_date;
4748
output_dir,
4849
comms_ctx = ClimaComms.context(),
50+
coupled_param_dict = CP.create_toml_dict(eltype(area_fraction)),
4951
)
5052
arch = comms_ctx.device isa ClimaComms.CUDADevice ? OC.GPU() : OC.CPU()
5153

@@ -154,10 +156,13 @@ function OceananigansSimulation(
154156

155157
remapping = (; remapper_cc, scratch_cc1, scratch_cc2, scratch_arr1, scratch_arr2)
156158

159+
# Get some ocean properties and parameters
157160
ocean_properties = (;
158161
ocean_reference_density = 1020,
159162
ocean_heat_capacity = 3991,
160163
ocean_fresh_water_density = 999.8,
164+
σ = coupled_param_dict["stefan_boltzmann_constant"],
165+
C_to_K = coupled_param_dict["temperature_water_freeze"],
161166
)
162167

163168
# Before version 0.96.22, the NetCDFWriter was broken on GPU
@@ -241,7 +246,7 @@ Interfacer.get_field(sim::OceananigansSimulation, ::Val{:surface_diffuse_albedo}
241246

242247
# NOTE: This is 3D, but it will be remapped to 2D
243248
Interfacer.get_field(sim::OceananigansSimulation, ::Val{:surface_temperature}) =
244-
273.15 + sim.ocean.model.tracers.T
249+
sim.ocean.model.tracers.T + sim.ocean_properties.C_to_K # convert from Celsius to Kelvin
245250

246251
"""
247252
FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fields)
@@ -365,16 +370,15 @@ function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf)
365370
# Update only the part due to radiative fluxes. For the full update, the component due
366371
# to latent and sensible heat is missing and will be updated in update_turbulent_fluxes.
367372
oc_flux_T = surface_flux(sim.ocean.model.tracers.T)
368-
# TODO: get sigma from parameters
369-
σ = 5.67e-8
373+
(; σ, C_to_K) = sim.ocean_properties
370374
α = Interfacer.get_field(sim, Val(:surface_direct_albedo)) # scalar
371375
ϵ = Interfacer.get_field(sim, Val(:emissivity)) # scalar
372376
OC.interior(oc_flux_T, :, :, 1) .=
373377
(
374378
-(1 - α) .* remapped_SW_d .-
375379
ϵ * (
376380
remapped_LW_d .-
377-
σ .* (273.15 .+ OC.interior(sim.ocean.model.tracers.T, :, :, 1)) .^ 4
381+
σ .* (C_to_K .+ OC.interior(sim.ocean.model.tracers.T, :, :, 1)) .^ 4
378382
)
379383
) ./ (ocean_reference_density * ocean_heat_capacity)
380384

experiments/ClimaEarth/components/ocean/prescr_ocean.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ function PrescribedOceanSimulation(
6666
start_date,
6767
t_start,
6868
area_fraction,
69+
coupled_param_dict,
6970
thermo_params,
7071
comms_ctx;
7172
z0m = FT(5.8e-5),
@@ -92,12 +93,13 @@ function PrescribedOceanSimulation(
9293
end : sst_path
9394
@info "PrescribedOcean: using SST file" sst_data
9495

96+
C_to_K = coupled_param_dict["temperature_water_freeze"]
9597
SST_timevaryinginput = TimeVaryingInput(
9698
sst_data,
9799
"SST",
98100
space,
99101
reference_date = start_date,
100-
file_reader_kwargs = (; preprocess_func = (data) -> data + FT(273.15),), ## convert to Kelvin
102+
file_reader_kwargs = (; preprocess_func = (data) -> data + C_to_K,), ## convert Celsius to Kelvin
101103
)
102104

103105
SST_init = zeros(space)

experiments/ClimaEarth/components/ocean/prescr_seaice.jl

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,68 @@ end
3434

3535
# sea-ice parameters
3636
Base.@kwdef struct IceSlabParameters{FT <: AbstractFloat}
37-
h::FT = 2 # ice thickness [m]
38-
ρ::FT = 900 # density of sea ice [kg / m3]
39-
c::FT = 2100 # specific heat of sea ice [J / kg / K]
40-
T_base::FT = 271.2 # temperature of sea water at the ice base
41-
z0m::FT = 1e-4 # roughness length for momentum [m]
42-
z0b::FT = 1e-4 # roughness length for tracers [m]
43-
T_freeze::FT = 271.2 # freezing temperature of sea water [K]
44-
k_ice::FT = 2 # thermal conductivity of sea ice [W / m / K] (less in HM71)
45-
α::FT = 0.65 # albedo of sea ice, roughly tuned to match observations
46-
ϵ::FT = 1 # emissivity of sea ice
37+
h::FT # ice thickness [m]
38+
ρ::FT # density of sea ice [kg / m3]
39+
c::FT # specific heat of sea ice [J / kg / K]
40+
T_base::FT # temperature of sea water at the ice base
41+
z0m::FT # roughness length for momentum [m]
42+
z0b::FT # roughness length for tracers [m]
43+
T_freeze::FT # freezing temperature of sea water [K]
44+
k_ice::FT # thermal conductivity of sea ice [W / m / K] (less in HM71)
45+
α::FT # albedo of sea ice, roughly tuned to match observations
46+
ϵ::FT # emissivity of sea ice
47+
σ::FT # Stefan-Boltzmann constant [W / m2 / K4]
48+
end
49+
50+
"""
51+
IceSlabParameters{FT}(coupled_param_dict; h = FT(2), ρ = FT(900), c = FT(2100),
52+
T_base = FT(271.2), z0m = FT(1e-4), z0b = FT(1e-4),
53+
T_freeze = FT(271.2), k_ice = FT(2), α = FT(0.65), ϵ = FT(1))
54+
55+
Initialize the `IceSlabParameters` object with the coupled parameters.
56+
57+
# Arguments
58+
- `coupled_param_dict`: a dictionary of coupled parameters (required)
59+
- `h`: ice thickness [m] (default: 2)
60+
- `ρ`: density of sea ice [kg / m3] (default: 900)
61+
- `c`: specific heat of sea ice [J / kg / K] (default: 2100)
62+
- `T_base`: temperature of sea water at the ice base [K] (default: 271.2)
63+
- `z0m`: roughness length for momentum [m] (default: 1e-4)
64+
- `z0b`: roughness length for tracers [m] (default: 1e-4)
65+
- `T_freeze`: freezing temperature of sea water [K] (default: 271.2)
66+
- `k_ice`: thermal conductivity of sea ice [W / m / K] (default: 2)
67+
- `α`: albedo of sea ice (default: 0.65)
68+
- `ϵ`: emissivity of sea ice (default: 1)
69+
70+
# Returns
71+
- `IceSlabParameters{FT}`: an `IceSlabParameters` object
72+
"""
73+
function IceSlabParameters{FT}(
74+
coupled_param_dict;
75+
h = FT(2),
76+
ρ = FT(900),
77+
c = FT(2100),
78+
T_base = FT(271.2),
79+
z0m = FT(1e-4),
80+
z0b = FT(1e-4),
81+
T_freeze = FT(271.2),
82+
k_ice = FT(2),
83+
α = FT(0.65),
84+
ϵ = FT(1),
85+
) where {FT}
86+
return IceSlabParameters{FT}(;
87+
h,
88+
ρ,
89+
c,
90+
T_base,
91+
z0m,
92+
z0b,
93+
T_freeze,
94+
k_ice,
95+
α,
96+
ϵ,
97+
σ = coupled_param_dict["stefan_boltzmann_constant"],
98+
)
4799
end
48100

49101
# init simulation
@@ -86,6 +138,7 @@ function PrescribedIceSimulation(
86138
dt,
87139
saveat,
88140
space,
141+
coupled_param_dict,
89142
thermo_params,
90143
comms_ctx,
91144
start_date,
@@ -127,7 +180,7 @@ function PrescribedIceSimulation(
127180
ice_fraction = @. max(min(SIC_init, FT(1) - land_fraction), FT(0))
128181
ice_fraction = ifelse.(ice_fraction .> FT(0.5), FT(1), FT(0))
129182

130-
params = IceSlabParameters{FT}()
183+
params = IceSlabParameters{FT}(coupled_param_dict)
131184

132185
Y = slab_ice_space_init(FT, space, params)
133186
cache = (;
@@ -266,7 +319,7 @@ for temperature (curbed at the freezing point).
266319
"""
267320
function ice_rhs!(dY, Y, p, t)
268321
FT = eltype(Y)
269-
(; k_ice, h, T_base, ρ, c, ϵ, α, T_freeze) = p.params
322+
(; k_ice, h, T_base, ρ, c, ϵ, α, T_freeze, σ) = p.params
270323

271324
# Update the cached area fraction with the current SIC
272325
evaluate!(p.area_fraction, p.SIC_timevaryinginput, t)
@@ -278,9 +331,6 @@ function ice_rhs!(dY, Y, p, t)
278331

279332
# Calculate the conductive flux, and set it to zero if the area fraction is zero
280333
F_conductive = @. k_ice / (h) * (T_base - Y.T_bulk) # fluxes are defined to be positive when upward
281-
282-
# TODO: get sigma from parameters
283-
σ = FT(5.67e-8)
284334
rhs = @. (
285335
-p.F_turb_energy +
286336
(1 - α) * p.SW_d +

0 commit comments

Comments
 (0)