Skip to content

Commit 2c47c39

Browse files
committed
use default land constructors
1 parent 1cf172f commit 2c47c39

File tree

4 files changed

+61
-196
lines changed

4 files changed

+61
-196
lines changed

docs/src/interfacer.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ for the following properties:
132132
| `air_density` | air density at the bottom cell centers of the atmosphere | kg m⁻³ |
133133
| `air_pressure` | air pressure at the bottom cell centers of the atmosphere | Pa |
134134
| `air_temperature` | air temperature at the bottom cell centers of the atmosphere | K |
135-
| `height_int` | height at the first internal model level | m |
136-
| `height_sfc` | height at the surface | m |
135+
| `height_int` | height at the bottom cell center of the atmosphere space | m |
136+
| `height_sfc` | height at the bottom face of the atmosphere space | m |
137137
| `liquid_precipitation` | liquid precipitation at the surface | kg m⁻² s⁻¹ |
138138
| `radiative_energy_flux_sfc` | net radiative flux at the surface | W m⁻² |
139139
| `radiative_energy_flux_toa` | net radiative flux at the top of the atmosphere | W m⁻² |

experiments/ClimaEarth/components/land/climaland_integrated.jl

Lines changed: 41 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,22 @@ end
3636

3737
"""
3838
ClimaLandSimulation(
39-
::Type{FT},
39+
::Type{FT};
4040
dt::TT,
4141
tspan::Tuple{TT, TT},
4242
start_date::Dates.DateTime,
4343
output_dir::String,
44-
area_fraction;
44+
area_fraction,
45+
nelements::Tuple{Int, Int} = (101, 15),
46+
depth::FT = FT(50),
47+
dz_tuple::Tuple{FT, FT} = FT.((10.0, 0.05)),
48+
shared_surface_space = nothing,
49+
land_spun_up_ic::Bool = true,
4550
saveat::Vector{TT} = [tspan[1], tspan[2]],
51+
surface_elevation = nothing,
52+
atmos_h,
4653
land_temperature_anomaly::String = "amip",
4754
use_land_diagnostics::Bool = true,
48-
stepper = CTS.ARS111(),
4955
parameter_files = [],
5056
)
5157
@@ -70,9 +76,9 @@ function ClimaLandSimulation(
7076
land_spun_up_ic::Bool = true,
7177
saveat::Vector{TT} = [tspan[1], tspan[2]],
7278
surface_elevation = nothing,
79+
atmos_h,
7380
land_temperature_anomaly::String = "amip",
7481
use_land_diagnostics::Bool = true,
75-
stepper = CTS.ARS111(),
7682
parameter_files = [],
7783
) where {FT, TT <: Union{Float64, ITime}}
7884
# Note that this does not take into account topography of the surface, which is OK for this land model.
@@ -91,71 +97,40 @@ function ClimaLandSimulation(
9197
else
9298
surface_elevation = Interfacer.remap(surface_elevation, surface_space)
9399
end
100+
# Interpolate atmosphere height field to surface space of land model,
101+
# since that's where we compute fluxes for this land model
102+
atmos_h = Interfacer.remap(atmos_h, surface_space)
94103

95104
# Set up spatially-varying parameters
96-
earth_param_set = CL.Parameters.LandParameters(
97-
CP.create_toml_dict(FT; override_file = CP.merge_toml_files(parameter_files; override = true)),
98-
)
99-
100-
# Set up the soil model args
101-
soil_model_type = CL.Soil.EnergyHydrology{FT}
102-
soil_params = create_soil_params(FT, domain)
103-
104-
# Set up the soil microbes model args
105-
soilco2_type = CL.Soil.Biogeochemistry.SoilCO2Model{FT}
106-
soilco2_params = CL.Soil.Biogeochemistry.SoilCO2ModelParameters(FT)
107-
108-
# Set up the canopy model args
109-
stop_date = start_date + Dates.Second(float(tspan[2] - tspan[1]))
110-
canopy_model_args, canopy_component_args = create_canopy_args(FT, domain, earth_param_set, start_date, stop_date)
111-
canopy_component_types = (;
112-
autotrophic_respiration = CL.Canopy.AutotrophicRespirationModel{FT},
113-
radiative_transfer = CL.Canopy.TwoStreamModel{FT},
114-
photosynthesis = CL.Canopy.FarquharModel{FT},
115-
conductance = CL.Canopy.MedlynConductanceModel{FT},
116-
hydraulics = CL.Canopy.PlantHydraulicsModel{FT},
117-
energy = CL.Canopy.BigLeafEnergyModel{FT},
118-
)
119-
120-
# Set up the snow model args
121-
snow_model_type = CL.Snow.SnowModel
122-
snow_params = CL.Snow.SnowParameters{FT}(dt; earth_param_set)
123-
124-
# Setup overall land model
125-
f_over = FT(3.28) # 1/m
126-
R_sb = FT(1.484e-4 / 1000) # m/s
127-
runoff_model = CL.Soil.Runoff.TOPMODELRunoff{FT}(;
128-
f_over = f_over,
129-
f_max = CL.Soil.topmodel_fmax(surface_space, FT),
130-
R_sb = R_sb,
105+
default_parameter_file = joinpath(pkgdir(CL), "toml", "default_parameters.toml")
106+
toml_dict = CP.create_toml_dict(
107+
FT;
108+
override_file = CP.merge_toml_files([default_parameter_file, parameter_files...]; override = true),
131109
)
110+
earth_param_set = CL.Parameters.LandParameters(toml_dict)
132111

133-
Csom = CL.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5))
134-
land_input = (
135-
atmos = CL.CoupledAtmosphere{FT}(surface_space),
112+
# Set up atmosphere and radiation forcing
113+
forcing = (;
114+
atmos = CL.CoupledAtmosphere{FT}(surface_space, atmos_h),
136115
radiation = CL.CoupledRadiativeFluxes{FT}(
137116
start_date;
138117
insol_params = LP.insolation_parameters(earth_param_set),
139118
latitude = ClimaCore.Fields.coordinate_field(domain.space.surface).lat,
140119
longitude = ClimaCore.Fields.coordinate_field(domain.space.surface).long,
141120
),
142-
runoff = runoff_model,
143-
soil_organic_carbon = Csom,
144121
)
145122

146-
model = CL.LandModel{FT}(;
147-
soilco2_type = soilco2_type,
148-
soilco2_args = (; domain = domain, parameters = soilco2_params),
149-
land_args = land_input,
150-
soil_model_type = soil_model_type,
151-
soil_args = (domain = domain, parameters = soil_params),
152-
canopy_component_types = canopy_component_types,
153-
canopy_component_args = canopy_component_args,
154-
canopy_model_args = canopy_model_args,
155-
snow_model_type = snow_model_type,
156-
snow_args = (; domain = CL.obtain_surface_domain(domain), parameters = snow_params),
123+
# Set up leaf area index (LAI)
124+
stop_date = start_date + Dates.Second(float(tspan[2] - tspan[1]))
125+
LAI = CL.prescribed_lai_modis(
126+
surface_space,
127+
start_date,
128+
stop_date;
129+
time_interpolation_method = LinearInterpolation(PeriodicCalendar()),
157130
)
158131

132+
model = CL.LandModel{FT}(forcing, LAI, toml_dict, domain, dt)
133+
159134
Y, p, coords = CL.initialize(model)
160135

161136
# Set initial conditions
@@ -175,11 +150,11 @@ function ClimaLandSimulation(
175150

176151
# Set initial conditions that aren't read in from file
177152
Y.soilco2.C .= FT(0.000412) # set to atmospheric co2, mol co2 per mol air
178-
Y.canopy.hydraulics.ϑ_l.:1 .= canopy_component_args.hydraulics.parameters.ν
153+
Y.canopy.hydraulics.ϑ_l.:1 .= model.canopy.hydraulics.parameters.ν
179154
@. Y.canopy.energy.T = orog_adjusted_T_surface
180155

181156
# Read in initial conditions for snow and soil from file, if requested
182-
θ_r, ν, ρc_ds = soil_params.θ_r, soil_params.ν, soil_params.ρc_ds
157+
(; θ_r, ν, ρc_ds) = model.soil.parameters
183158
if land_spun_up_ic
184159
# Set snow T first to use in computing snow internal energy
185160
@. p.snow.T = orog_adjusted_T_surface
@@ -246,148 +221,21 @@ function ClimaLandSimulation(
246221
end
247222

248223
# Set up time stepper and integrator
224+
stepper = CTS.ARS111()
249225
ode_algo =
250226
CTS.IMEXAlgorithm(stepper, CTS.NewtonsMethod(max_iters = 3, update_j = CTS.UpdateEvery(CTS.NewNewtonIteration)))
251227
integrator = SciMLBase.init(
252228
prob,
253229
ode_algo;
254-
dt = dt,
255-
saveat = saveat,
230+
dt,
231+
saveat,
256232
adaptive = false,
257233
callback = SciMLBase.CallbackSet(driver_cb, diag_cb),
258234
)
259235

260236
return ClimaLandSimulation(model, integrator, area_fraction, output_writer)
261237
end
262238

263-
###############################################################################
264-
# Helper functions for constructing the land model
265-
###############################################################################
266-
"""
267-
create_canopy_args(
268-
::Type{FT},
269-
domain,
270-
earth_param_set,
271-
start_date,
272-
)
273-
274-
Creates the arguments for the canopy model.
275-
"""
276-
function create_canopy_args(::Type{FT}, domain, earth_param_set, start_date, stop_date) where {FT}
277-
surface_space = domain.space.surface
278-
279-
# Spatially varying canopy parameters from CLM
280-
g1 = CL.Canopy.clm_medlyn_g1(surface_space)
281-
rooting_depth = CL.Canopy.clm_rooting_depth(surface_space)
282-
(; is_c3, Vcmax25) = CL.Canopy.clm_photosynthesis_parameters(surface_space)
283-
(; Ω, G_Function, α_PAR_leaf, τ_PAR_leaf, α_NIR_leaf, τ_NIR_leaf) =
284-
CL.Canopy.clm_canopy_radiation_parameters(surface_space)
285-
286-
# Energy Balance model
287-
ac_canopy = FT(2.5e3)
288-
# Plant Hydraulics and general plant parameters
289-
SAI = FT(0.0) # m2/m2
290-
f_root_to_shoot = FT(3.5)
291-
RAI = FT(1.0)
292-
K_sat_plant = FT(5e-9) # m/s # seems much too small?
293-
ψ63 = FT(-4 / 0.0098) # / MPa to m, Holtzman's original parameter value is -4 MPa
294-
Weibull_param = FT(4) # unitless, Holtzman's original c param value
295-
a = FT(0.05 * 0.0098) # Holtzman's original parameter for the bulk modulus of elasticity
296-
conductivity_model = CL.Canopy.PlantHydraulics.Weibull{FT}(K_sat_plant, ψ63, Weibull_param)
297-
retention_model = CL.Canopy.PlantHydraulics.LinearRetentionCurve{FT}(a)
298-
plant_ν = FT(1.44e-4)
299-
plant_S_s = FT(1e-2 * 0.0098) # m3/m3/MPa to m3/m3/m
300-
n_stem = 0
301-
n_leaf = 1
302-
h_stem = FT(0.0)
303-
h_leaf = FT(1.0)
304-
zmax = FT(0.0)
305-
h_canopy = h_stem + h_leaf
306-
compartment_midpoints = n_stem > 0 ? [h_stem / 2, h_stem + h_leaf / 2] : [h_leaf / 2]
307-
compartment_surfaces = n_stem > 0 ? [zmax, h_stem, h_canopy] : [zmax, h_leaf]
308-
309-
z0_m = FT(0.13) * h_canopy
310-
z0_b = FT(0.1) * z0_m
311-
312-
# Individual Component arguments
313-
# Set up autotrophic respiration
314-
autotrophic_respiration_args = (; parameters = CL.Canopy.AutotrophicRespirationParameters(FT))
315-
# Set up radiative transfer
316-
radiative_transfer_args = (;
317-
parameters = CL.Canopy.TwoStreamParameters(FT; Ω, α_PAR_leaf, τ_PAR_leaf, α_NIR_leaf, τ_NIR_leaf, G_Function)
318-
)
319-
# Set up conductance
320-
conductance_args = (; parameters = CL.Canopy.MedlynConductanceParameters(FT; g1))
321-
# Set up photosynthesis
322-
photosynthesis_args = (; parameters = CL.Canopy.FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25))
323-
# Set up plant hydraulics
324-
LAIfunction = CL.prescribed_lai_modis(surface_space, start_date, stop_date)
325-
ai_parameterization = CL.Canopy.PrescribedSiteAreaIndex{FT}(LAIfunction, SAI, RAI)
326-
327-
plant_hydraulics_ps = CL.Canopy.PlantHydraulics.PlantHydraulicsParameters(;
328-
ai_parameterization = ai_parameterization,
329-
ν = plant_ν,
330-
S_s = plant_S_s,
331-
rooting_depth = rooting_depth,
332-
conductivity_model = conductivity_model,
333-
retention_model = retention_model,
334-
)
335-
plant_hydraulics_args = (
336-
parameters = plant_hydraulics_ps,
337-
n_stem = n_stem,
338-
n_leaf = n_leaf,
339-
compartment_midpoints = compartment_midpoints,
340-
compartment_surfaces = compartment_surfaces,
341-
)
342-
343-
energy_args = (parameters = CL.Canopy.BigLeafEnergyParameters{FT}(ac_canopy),)
344-
345-
canopy_component_args = (;
346-
autotrophic_respiration = autotrophic_respiration_args,
347-
radiative_transfer = radiative_transfer_args,
348-
photosynthesis = photosynthesis_args,
349-
conductance = conductance_args,
350-
hydraulics = plant_hydraulics_args,
351-
energy = energy_args,
352-
)
353-
354-
shared_params = CL.Canopy.SharedCanopyParameters{FT, typeof(earth_param_set)}(z0_m, z0_b, earth_param_set)
355-
356-
canopy_model_args = (; parameters = shared_params, domain = CL.obtain_surface_domain(domain))
357-
return canopy_model_args, canopy_component_args
358-
end
359-
360-
361-
"""
362-
create_soil_params(::Type{FT}, domain)
363-
364-
Creates the parameters struct for the soil model.
365-
"""
366-
function create_soil_params(::Type{FT}, domain) where {FT}
367-
subsurface_space = domain.space.subsurface
368-
surface_space = domain.space.surface
369-
370-
(; ν_ss_om, ν_ss_quartz, ν_ss_gravel) = CL.Soil.soil_composition_parameters(subsurface_space, FT)
371-
(; ν, hydrology_cm, K_sat, θ_r) = CL.Soil.soil_vangenuchten_parameters(subsurface_space, FT)
372-
soil_albedo = CL.Soil.CLMTwoBandSoilAlbedo{FT}(; CL.Soil.clm_soil_albedo_parameters(surface_space)...)
373-
S_s = ClimaCore.Fields.zeros(subsurface_space) .+ FT(1e-3)
374-
375-
376-
soil_params = CL.Soil.EnergyHydrologyParameters(
377-
FT;
378-
ν,
379-
ν_ss_om,
380-
ν_ss_quartz,
381-
ν_ss_gravel,
382-
hydrology_cm,
383-
K_sat,
384-
S_s,
385-
θ_r,
386-
albedo = soil_albedo,
387-
)
388-
return soil_params
389-
end
390-
391239
###############################################################################
392240
### Functions required by ClimaCoupler.jl for a SurfaceModelSimulation
393241
###############################################################################
@@ -437,7 +285,12 @@ function Interfacer.update_field!(sim::ClimaLandSimulation, ::Val{:sw_d}, field)
437285
Interfacer.remap!(sim.integrator.p.drivers.SW_d, field)
438286
end
439287

440-
Interfacer.step!(sim::ClimaLandSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true)
288+
function Interfacer.step!(sim::ClimaLandSimulation, t)
289+
while float(sim.integrator.t) < float(t)
290+
Interfacer.step!(sim.integrator)
291+
end
292+
return nothing
293+
end
441294
Interfacer.close_output_writers(sim::ClimaLandSimulation) = isnothing(sim.output_writer) || close(sim.output_writer)
442295

443296
function FieldExchanger.update_sim!(sim::ClimaLandSimulation, csf, area_fraction)

experiments/ClimaEarth/setup_run.jl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,6 @@ function CoupledSimulation(config_dict::AbstractDict)
208208

209209
## init atmos model component
210210
atmos_sim = ClimaAtmosSimulation(CA.AtmosConfig(atmos_config_dict))
211-
212-
# Get surface elevation from `atmos` coordinate field
213-
surface_elevation = CC.Fields.level(CC.Fields.coordinate_field(atmos_sim.integrator.u.f).z, CC.Utilities.half)
214-
215211
thermo_params = get_thermo_params(atmos_sim) # TODO: this should be shared by all models #342
216212

217213
#=
@@ -238,6 +234,19 @@ function CoupledSimulation(config_dict::AbstractDict)
238234
boundary_space = CC.CommonSpaces.CubedSphereSpace(FT; radius = FT(6371e3), n_quad_points, h_elem)
239235
end
240236

237+
# Get surface elevation on the boundary space from `atmos` coordinate field
238+
surface_elevation = Interfacer.get_field(atmos_sim, Val(:height_sfc), boundary_space) # on surface space
239+
240+
# Get atmospheric height on the boundary space
241+
# Since the atmospheric height is defined on centers, we need to copy the values onto the boundary space
242+
# to be able to subtract the surface elevation
243+
# Note: This pattern is not reliable and should not be reused.
244+
atmos_h =
245+
ClimaCore.Fields.Field(
246+
ClimaCore.Fields.field_values(Interfacer.get_field(atmos_sim, Val(:height_int))),
247+
boundary_space,
248+
) .- surface_elevation # atmos height relative to the surface, on the surface space
249+
241250
#=
242251
### Land-sea Fraction
243252
This is a static field that contains the area fraction of land and sea, ranging from 0 to 1.
@@ -303,6 +312,7 @@ function CoupledSimulation(config_dict::AbstractDict)
303312
land_spun_up_ic,
304313
saveat,
305314
surface_elevation,
315+
atmos_h,
306316
land_temperature_anomaly,
307317
use_land_diagnostics,
308318
parameter_files,

experiments/ClimaEarth/test/component_model_tests/climaland_tests.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ FT = Float32
2323
output_dir = pwd()
2424
boundary_space = CC.CommonSpaces.CubedSphereSpace(FT; radius = FT(6371e3), n_quad_points = 4, h_elem = 4)
2525
area_fraction = CC.Fields.ones(boundary_space)
26+
atmos_h = CC.Fields.zeros(boundary_space) .+ 2
2627

2728
# Construct simulation object
28-
land_sim = ClimaLandSimulation(FT; dt, tspan, start_date, output_dir, area_fraction)
29+
land_sim = ClimaLandSimulation(FT; dt, tspan, start_date, output_dir, area_fraction, atmos_h)
2930

3031
# Try taking a timestep
3132
Interfacer.step!(land_sim, dt)
@@ -67,7 +68,8 @@ end
6768

6869
boundary_space = ClimaCore.Spaces.horizontal_space(atmos_sim.domain.face_space)
6970
area_fraction = ClimaCore.Fields.ones(boundary_space)
70-
land_sim = ClimaLandSimulation(FT; dt, tspan, start_date, output_dir, area_fraction)
71+
atmos_h = CC.Fields.zeros(boundary_space) .+ 2
72+
land_sim = ClimaLandSimulation(FT; dt, tspan, start_date, output_dir, area_fraction, atmos_h)
7173
model_sims = (; land_sim = land_sim, atmos_sim = atmos_sim)
7274

7375
# Initialize the coupler fields so we can perform exchange

0 commit comments

Comments
 (0)