Skip to content

Commit 0bcfbc5

Browse files
authored
Merge pull request #1284 from CliMA/kd/js/use_model_constructors
use new canopy and soil constructors in tutorials and experiments
2 parents 54a9000 + c6d9563 commit 0bcfbc5

File tree

14 files changed

+633
-1252
lines changed

14 files changed

+633
-1252
lines changed

docs/src/tutorials/integrated/soil_canopy_tutorial.jl

Lines changed: 109 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,6 @@ long = FT(-92.2000) # degree
7474
# Height of the sensor at the site
7575
atmos_h = FT(32) # m
7676

77-
# Setup the domain for the model:
78-
nelements = 10
79-
zmin = FT(-2)
80-
zmax = FT(0)
81-
f_root_to_shoot = FT(3.5)
82-
plant_ν = FT(2.46e-4)
83-
n_stem = Int64(1)
84-
n_leaf = Int64(1)
85-
h_stem = FT(9)
86-
h_leaf = FT(9.5)
87-
compartment_midpoints = [h_stem / 2, h_stem + h_leaf / 2]
88-
compartment_surfaces = [zmax, h_stem, h_stem + h_leaf]
89-
land_domain =
90-
Column(; zlim = (zmin, zmax), nelements = nelements, longlat = (long, lat));
91-
9277
# Specify the time range and dt value over which to perform the simulation.
9378
t0 = Float64(150 * 3600 * 24)# start mid year
9479
N_days = 100
@@ -112,6 +97,15 @@ dt = Float64(30)
11297
earth_param_set,
11398
FT,
11499
);
100+
101+
# Setup the domain for the model:
102+
103+
nelements = 10
104+
zmin = FT(-2)
105+
zmax = FT(0)
106+
domain =
107+
Column(; zlim = (zmin, zmax), nelements = nelements, longlat = (long, lat));
108+
115109
# # Setup the Coupled Canopy and Soil Physics Model
116110

117111
# We want to simulate the canopy-soil system together, so the model type
@@ -121,15 +115,15 @@ dt = Float64(30)
121115
# type and arguments as well as the canopy model component types, component
122116
# arguments, and the canopy model arguments, so we first need to initialize
123117
# all of these.
118+
prognostic_land_components = (:canopy, :soil, :soilco2);
124119

125120
# For our soil model, we will choose the
126121
# [`EnergyHydrology`](https://clima.github.io/ClimaLand.jl/dev/APIs/Soil/#Soil-Models-2)
127122
# and set up all the necessary arguments. See the
128123
# [tutorial](https://clima.github.io/ClimaLand.jl/dev/generated/Soil/soil_energy_hydrology/)
129124
# on the model for a more detailed explanation of the soil model.
130125

131-
# Define the parameters for the soil model and provide them to the model
132-
# parameters struct:
126+
# Define the parameters for the soil model and provide them to the model constructor.
133127

134128
# Soil parameters
135129
soil_ν = FT(0.5) # m3/m3
@@ -138,184 +132,146 @@ soil_S_s = FT(1e-3) # 1/m
138132
soil_vg_n = FT(2.05) # unitless
139133
soil_vg_α = FT(0.04) # inverse meters
140134
θ_r = FT(0.067); # m3/m3
141-
142-
# Soil heat transfer parameters
143135
ν_ss_quartz = FT(0.1)
144136
ν_ss_om = FT(0.1)
145137
ν_ss_gravel = FT(0.0);
138+
146139
z_0m_soil = FT(0.1)
147140
z_0b_soil = FT(0.1)
148141
soil_ϵ = FT(0.98)
149142
soil_α_PAR = FT(0.2)
150143
soil_α_NIR = FT(0.4)
151144

152-
soil_domain = land_domain
153145
soil_albedo = ClimaLand.Soil.ConstantTwoBandSoilAlbedo{FT}(;
154146
PAR_albedo = soil_α_PAR,
155147
NIR_albedo = soil_α_NIR,
156148
)
157-
soil_ps = Soil.EnergyHydrologyParameters(
158-
FT;
159-
ν = soil_ν,
160-
ν_ss_om = ν_ss_om,
161-
ν_ss_quartz = ν_ss_quartz,
162-
ν_ss_gravel = ν_ss_gravel,
163-
hydrology_cm = vanGenuchten{FT}(; α = soil_vg_α, n = soil_vg_n),
164-
K_sat = soil_K_sat,
149+
runoff_model = ClimaLand.Soil.Runoff.NoRunoff()
150+
151+
soil = EnergyHydrology{FT}(
152+
domain,
153+
(; atmos, radiation),
154+
earth_param_set;
155+
prognostic_land_components,
156+
albedo = soil_albedo,
157+
runoff = runoff_model,
158+
additional_sources = (ClimaLand.RootExtraction{FT}(),),
159+
retention_parameters = (;
160+
ν = soil_ν,
161+
θ_r,
162+
K_sat = soil_K_sat,
163+
hydrology_cm = vanGenuchten{FT}(; α = soil_vg_α, n = soil_vg_n),
164+
),
165+
composition_parameters = (; ν_ss_om, ν_ss_quartz, ν_ss_gravel),
165166
S_s = soil_S_s,
166-
θ_r = θ_r,
167-
earth_param_set = earth_param_set,
168167
z_0m = z_0m_soil,
169168
z_0b = z_0b_soil,
170169
emissivity = soil_ϵ,
171-
albedo = soil_albedo,
172170
);
173171

174-
soil_args = (domain = soil_domain, parameters = soil_ps)
175-
soil_model_type = Soil.EnergyHydrology{FT}
176-
177172
# For the heterotrophic respiration model, see the
178173
# [documentation](https://clima.github.io/ClimaLand.jl/previews/PR214/dynamicdocs/pages/soil_biogeochemistry/microbial_respiration/)
179174
# to understand the parameterisation.
180175
# The domain is defined similarly to the soil domain described above.
181-
soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT}
182-
soilco2_ps = SoilCO2ModelParameters(FT);
183-
Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5))
184-
soilco2_args = (; domain = soil_domain, parameters = soilco2_ps)
185-
186-
# Next we need to set up the [`CanopyModel`](https://clima.github.io/ClimaLand.jl/dev/APIs/canopy/Canopy/#Canopy-Model-Structs).
187-
# For more details on the specifics of this model see the previous tutorial.
188-
189-
# Begin by declaring the component types of the canopy model. Unlike in the
190-
# previous tutorial, collect the arguments to each component into tuples and do
191-
# not instantiate the component models yet. The constructor for the
192-
# SoilPlantHydrologyModel will use these arguments and internally instatiate the
193-
# component CanopyModel and RichardsModel instances. This is done so that the
194-
# constructor may enforce consistency constraints between the two models, and
195-
# this must be done internally from the constructor.
196-
197-
canopy_component_types = (;
198-
autotrophic_respiration = Canopy.AutotrophicRespirationModel{FT},
199-
radiative_transfer = Canopy.TwoStreamModel{FT},
200-
photosynthesis = Canopy.FarquharModel{FT},
201-
conductance = Canopy.MedlynConductanceModel{FT},
202-
hydraulics = Canopy.PlantHydraulicsModel{FT},
176+
soil_organic_carbon =
177+
ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5));
178+
co2_prognostic_soil = Soil.Biogeochemistry.PrognosticMet(soil.parameters);
179+
drivers = Soil.Biogeochemistry.SoilDrivers(
180+
co2_prognostic_soil,
181+
soil_organic_carbon,
182+
atmos,
203183
);
184+
soilco2 = Soil.Biogeochemistry.SoilCO2Model{FT}(; domain, drivers);
204185

205-
# Then provide arguments to the canopy radiative transfer, stomatal conductance,
206-
# and photosynthesis models as was done in the previous tutorial.
207-
208-
autotrophic_respiration_args =
209-
(; parameters = AutotrophicRespirationParameters(FT))
210-
211-
radiative_transfer_args = (;
212-
parameters = TwoStreamParameters(
213-
FT;
214-
G_Function = ConstantGFunction(FT(0.5)),
215-
α_PAR_leaf = 0.1,
216-
α_NIR_leaf = 0.45,
217-
τ_PAR_leaf = 0.05,
218-
τ_NIR_leaf = 0.25,
219-
Ω = 0.69,
220-
)
221-
)
222-
223-
conductance_args = (; parameters = MedlynConductanceParameters(FT; g1 = 141))
224-
225-
is_c3 = FT(1) # set the photosynthesis mechanism to C3
226-
photosynthesis_args =
227-
(; parameters = FarquharParameters(FT, is_c3; Vcmax25 = FT(5e-5)));
228-
229-
K_sat_plant = FT(1.8e-8)
230-
231-
# Read in LAI from MODIS data
232-
surface_space = land_domain.space.surface;
186+
# Read in prescribed LAI at the site from global MODIS data
187+
surface_space = domain.space.surface;
233188
modis_lai_ncdata_path = ClimaLand.Artifacts.modis_lai_multiyear_paths(;
234189
start_date = start_date + Second(t0),
235190
end_date = start_date + Second(t0) + Second(tf),
236-
)
191+
);
237192
LAI = ClimaLand.prescribed_lai_modis(
238193
modis_lai_ncdata_path,
239194
surface_space,
240195
start_date,
241196
);
242197
# Get the maximum LAI at this site over the first year of the simulation
243198
maxLAI =
244-
FluxnetSimulations.get_maxLAI_at_site(modis_lai_ncdata_path[1], lat, long)
199+
FluxnetSimulations.get_maxLAI_at_site(modis_lai_ncdata_path[1], lat, long);
200+
201+
# For a coupled SoilCanopyModel, we provide a flag to the canopy that indicates
202+
# the ground forcing is prognostic (i.e. the soil model) rather than prescribed.
203+
ground = ClimaLand.PrognosticSoilConditions{FT}();
204+
205+
# Next we need to set up the [`CanopyModel`](https://clima.github.io/ClimaLand.jl/dev/APIs/canopy/Canopy/#Canopy-Model-Structs).
206+
# For more details on the specifics of this model see the previous tutorial.
207+
surface_domain = ClimaLand.Domains.obtain_surface_domain(domain);
208+
209+
# Let's overwrite some default parameters for the canopy model components.
210+
# This involves constructing the components individually and then
211+
# passing them to the canopy model constructor.
212+
213+
# Canopy conductance
214+
conductance = Canopy.MedlynConductanceModel{FT}(surface_domain; g1 = 141);
215+
216+
# Canopy radiative transfer
217+
radiation_parameters = (;
218+
G_Function = ConstantGFunction(FT(0.5)),
219+
α_PAR_leaf = 0.1,
220+
α_NIR_leaf = 0.45,
221+
τ_PAR_leaf = 0.05,
222+
τ_NIR_leaf = 0.25,
223+
Ω = 0.69,
224+
);
225+
radiative_transfer = TwoStreamModel{FT}(surface_domain; radiation_parameters);
245226

227+
# Canopy photosynthesis
228+
photosynthesis_parameters = (; is_c3 = FT(1), Vcmax25 = FT(5e-5));
229+
photosynthesis = FarquharModel{FT}(surface_domain; photosynthesis_parameters);
230+
231+
# Canopy hydraulics
232+
n_stem = Int64(1)
233+
n_leaf = Int64(1)
234+
h_stem = FT(9)
235+
h_leaf = FT(9.5)
236+
f_root_to_shoot = FT(3.5)
237+
plant_ν = FT(0.7)
238+
plant_S_s = FT(1e-2 * 0.0098)
246239
SAI = FT(0.00242)
247240
RAI = (SAI + maxLAI) * f_root_to_shoot;
248-
ai_parameterization = PrescribedSiteAreaIndex{FT}(LAI, SAI, RAI)
249-
241+
K_sat_plant = FT(1.8e-8)
250242
ψ63 = FT(-4 / 0.0098)
251243
Weibull_param = FT(4)
252-
a = FT(0.05 * 0.0098)
253-
254244
conductivity_model =
255245
PlantHydraulics.Weibull{FT}(K_sat_plant, ψ63, Weibull_param)
256-
257-
retention_model = PlantHydraulics.LinearRetentionCurve{FT}(a)
258-
259-
plant_ν = FT(0.7)
260-
plant_S_s = FT(1e-2 * 0.0098)
261-
262-
plant_hydraulics_ps = PlantHydraulics.PlantHydraulicsParameters(;
263-
ai_parameterization = ai_parameterization,
246+
hydraulics = Canopy.PlantHydraulicsModel{FT}(
247+
surface_domain,
248+
LAI;
249+
SAI,
250+
RAI,
251+
n_stem,
252+
n_leaf,
253+
h_stem,
254+
h_leaf,
264255
ν = plant_ν,
265256
S_s = plant_S_s,
266-
rooting_depth = FT(1.0),
267-
conductivity_model = conductivity_model,
268-
retention_model = retention_model,
257+
conductivity_model,
258+
rooting_depth = FT(1),
269259
)
270260

271-
plant_hydraulics_args = (
272-
parameters = plant_hydraulics_ps,
273-
n_stem = n_stem,
274-
n_leaf = n_leaf,
275-
compartment_midpoints = compartment_midpoints,
276-
compartment_surfaces = compartment_surfaces,
261+
canopy = ClimaLand.Canopy.CanopyModel{FT}(
262+
surface_domain,
263+
(; atmos, radiation, ground),
264+
LAI,
265+
earth_param_set;
266+
prognostic_land_components = (:canopy, :soil, :soilco2),
267+
conductance,
268+
radiative_transfer,
269+
photosynthesis,
270+
hydraulics,
277271
);
278272

279-
# We may now collect all of the canopy component argument tuples into one
280-
# arguments tuple for the canopy component models.
281-
282-
canopy_component_args = (;
283-
autotrophic_respiration = autotrophic_respiration_args,
284-
radiative_transfer = radiative_transfer_args,
285-
photosynthesis = photosynthesis_args,
286-
conductance = conductance_args,
287-
hydraulics = plant_hydraulics_args,
288-
);
289-
290-
# We also need to provide the shared parameter struct to the canopy.
291-
z0_m = FT(2)
292-
z0_b = FT(0.2)
293-
294-
shared_params = SharedCanopyParameters{FT, typeof(earth_param_set)}(
295-
z0_m,
296-
z0_b,
297-
earth_param_set,
298-
)
299-
canopy_domain = obtain_surface_domain(land_domain)
300-
canopy_model_args = (; parameters = shared_params, domain = canopy_domain);
301-
302-
# We may now instantiate the integrated plant and soil model. In this example,
303-
# we will compute transpiration diagnostically, and work with prescribed
304-
# atmospheric and radiative flux conditions from the observations at the Ozark
305-
# site as was done in the previous tutorial.
306-
307-
land_input = (atmos = atmos, radiation = radiation, soil_organic_carbon = Csom)
308-
309-
land = SoilCanopyModel{FT}(;
310-
soilco2_type = soilco2_type,
311-
soilco2_args = soilco2_args,
312-
land_args = land_input,
313-
soil_model_type = soil_model_type,
314-
soil_args = soil_args,
315-
canopy_component_types = canopy_component_types,
316-
canopy_component_args = canopy_component_args,
317-
canopy_model_args = canopy_model_args,
318-
);
273+
# Now we can combine the soil and canopy models into a single combined model.
274+
land = SoilCanopyModel{FT}(soilco2, soil, canopy);
319275

320276
# Now we can initialize the state vectors and model coordinates, and initialize
321277
# the explicit/implicit tendencies as usual. The Richard's equation time
@@ -329,8 +285,7 @@ jacobian! = make_jacobian(land);
329285
jac_kwargs =
330286
(; jac_prototype = ClimaLand.FieldMatrixWithSolver(Y), Wfact = jacobian!);
331287

332-
# We need to provide initial conditions for the soil and canopy hydraulics
333-
# models:
288+
# We need to provide initial conditions for the model:
334289
Y.soil.ϑ_l = FT(0.4)
335290
Y.soil.θ_i = FT(0.0)
336291
T_0 = FT(288.7)
@@ -346,24 +301,24 @@ Y.soil.ρe_int =
346301

347302
Y.soilco2.C .= FT(0.000412) # set to atmospheric co2, mol co2 per mol air
348303

304+
canopy_retention_model = canopy.hydraulics.parameters.retention_model
349305
ψ_stem_0 = FT(-1e5 / 9800)
350306
ψ_leaf_0 = FT(-2e5 / 9800)
351307

352308
S_l_ini =
353309
inverse_water_retention_curve.(
354-
retention_model,
310+
canopy_retention_model,
355311
[ψ_stem_0, ψ_leaf_0],
356312
plant_ν,
357313
plant_S_s,
358314
)
359-
360315
for i in 1:2
361316
Y.canopy.hydraulics.ϑ_l.:($i) .=
362317
augmented_liquid_fraction.(plant_ν, S_l_ini[i])
363-
end;
364-
365-
# Select the timestepper and solvers needed for the specific problem.
318+
end
319+
evaluate!(Y.canopy.energy.T, atmos.T, t0)
366320

321+
# Choose the timestepper and solver needed for the problem.
367322
timestepper = CTS.ARS343()
368323
ode_algo = CTS.IMEXAlgorithm(
369324
timestepper,
@@ -374,7 +329,6 @@ ode_algo = CTS.IMEXAlgorithm(
374329
);
375330

376331
# Now set the initial values for the cache variables for the combined soil and plant model.
377-
378332
set_initial_cache! = make_set_initial_cache(land)
379333
set_initial_cache!(p, Y, t0);
380334

0 commit comments

Comments
 (0)