Skip to content

Commit 7ac4cd4

Browse files
authored
Model setup of soil models (#1170)
1 parent f82212e commit 7ac4cd4

File tree

7 files changed

+170
-116
lines changed

7 files changed

+170
-116
lines changed

experiments/benchmarks/richards.jl

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,6 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15))
8080
subsurface_space = domain.space.subsurface
8181

8282
start_date = DateTime(2008)
83-
(; ν, hydrology_cm, K_sat, θ_r) =
84-
ClimaLand.Soil.soil_vangenuchten_parameters(subsurface_space, FT)
85-
S_s = ClimaCore.Fields.zeros(subsurface_space) .+ FT(1e-3)
86-
f_over = FT(3.28) # 1/m
87-
R_sb = FT(1.484e-4 / 1000) # m/s
88-
runoff_model = ClimaLand.Soil.Runoff.TOPMODELRunoff{FT}(;
89-
f_over = f_over,
90-
f_max = ClimaLand.Soil.topmodel_fmax(surface_space, FT),
91-
R_sb = R_sb,
92-
)
93-
soil_params = ClimaLand.Soil.RichardsParameters(;
94-
hydrology_cm = hydrology_cm,
95-
ν = ν,
96-
K_sat = K_sat,
97-
S_s = S_s,
98-
θ_r = θ_r,
99-
)
100-
10183
era5_ncdata_path =
10284
ClimaLand.Artifacts.era5_land_forcing_data2008_path(; context)
10385

@@ -113,20 +95,12 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15))
11395
regridder_type,
11496
file_reader_kwargs = (; preprocess_func = (data) -> -data / 1000),
11597
)
116-
atmos = ClimaLand.PrescribedPrecipitation{FT, typeof(precip)}(precip)
117-
bottom_bc = ClimaLand.Soil.WaterFluxBC((p, t) -> 0.0)
118-
bc = (;
119-
top = ClimaLand.Soil.RichardsAtmosDrivenFluxBC(atmos, runoff_model),
120-
bottom = bottom_bc,
121-
)
122-
model = ClimaLand.Soil.RichardsModel{FT}(;
123-
parameters = soil_params,
124-
domain = domain,
125-
boundary_conditions = bc,
126-
sources = (),
127-
lateral_flow = false,
98+
forcing = (;
99+
atmos = ClimaLand.PrescribedPrecipitation{FT, typeof(precip)}(precip),
128100
)
129101

102+
model = ClimaLand.Soil.RichardsModel(FT, domain, forcing)
103+
130104
Y, p, cds = initialize(model)
131105
z = ClimaCore.Fields.coordinate_field(cds.subsurface).z
132106
lat = ClimaCore.Fields.coordinate_field(domain.space.subsurface).lat
@@ -138,13 +112,12 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15))
138112
α::FT,
139113
n::FT,
140114
S_s::FT,
141-
fmax,
142115
) where {FT}
143116
m = 1 - 1 / n
144117
zmin = FT(-50.0)
145118
zmax = FT(0.0)
146119

147-
z_∇ = FT(zmin / 5.0 + (zmax - zmin) / 2.5 * (fmax - 0.35) / 0.7)
120+
z_∇ = FT(zmin / 5.0 + (zmax - zmin) / 2.5)
148121
if z > z_∇
149122
S = FT((FT(1) +* (z - z_∇))^n)^(-m))
150123
ϑ_l = S *- θ_r) + θ_r
@@ -154,10 +127,14 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15))
154127
return FT(ϑ_l)
155128
end
156129

130+
hydrology_cm = model.parameters.hydrology_cm
131+
ν = model.parameters.ν
132+
θ_r = model.parameters.S_s
133+
S_s = model.parameters.θ_r
157134
# Set initial state values
158135
vg_α = hydrology_cm.α
159136
vg_n = hydrology_cm.n
160-
Y.soil.ϑ_l .= hydrostatic_profile.(lat, z, ν, θ_r, vg_α, vg_n, S_s, f_max)
137+
Y.soil.ϑ_l .= hydrostatic_profile.(lat, z, ν, θ_r, vg_α, vg_n, S_s)
161138
# Create model update functions
162139
set_initial_cache! = make_set_initial_cache(model)
163140
exp_tendency! = make_exp_tendency(model)

experiments/long_runs/land_region.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import ClimaUtilities.TimeVaryingInputs:
2929
import ClimaUtilities.TimeManager: ITime, date
3030
import ClimaUtilities.ClimaArtifacts: @clima_artifact
3131
import ClimaParams as CP
32-
32+
using ClimaCore
3333
using ClimaLand
3434
using ClimaLand.Snow
3535
using ClimaLand.Soil

experiments/long_runs/snowy_land.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import ClimaUtilities.TimeVaryingInputs:
3030
TimeVaryingInput, LinearInterpolation, PeriodicCalendar
3131
import ClimaUtilities.ClimaArtifacts: @clima_artifact
3232
import ClimaParams as CP
33-
33+
using ClimaCore
3434
using ClimaLand
3535
using ClimaLand.Snow
3636
using ClimaLand.Soil

experiments/long_runs/soil.jl

Lines changed: 26 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
# land/sea mask are not yet supported by ClimaCore.
77

88
# Simulation Setup
9-
# Number of spatial elements: 101 1in horizontal, 15 in vertical
9+
# Number of spatial elements: 101 in horizontal, 15 in vertical
1010
# Soil depth: 50 m
11-
# Simulation duration: 365 d
11+
# Simulation duration: 2-20 years, based on LONGER_RUN setting
1212
# Timestep: 450 s
1313
# Timestepper: ARS111
14-
# Fixed number of iterations: 1
15-
# Jacobian update: every new timestep
14+
# Fixed number of iterations: 3
15+
# Jacobian update: every new Newton iteration
1616
# Atmos forcing update: every 3 hours
1717
import ClimaComms
1818
ClimaComms.@import_required_backends
@@ -28,7 +28,7 @@ import ClimaUtilities.TimeVaryingInputs: LinearInterpolation, PeriodicCalendar
2828
import ClimaUtilities.ClimaArtifacts: @clima_artifact
2929
import ClimaUtilities.TimeManager: ITime
3030
import ClimaParams as CP
31-
31+
using ClimaCore
3232
using ClimaLand
3333
using ClimaLand.Soil
3434
import ClimaLand
@@ -57,13 +57,25 @@ root_path = "soil_longrun_$(device_suffix)"
5757
diagnostics_outdir = joinpath(root_path, "global_diagnostics")
5858
outdir =
5959
ClimaUtilities.OutputPathGenerator.generate_output_path(diagnostics_outdir)
60+
time_interpolation_method =
61+
LONGER_RUN ? LinearInterpolation() : LinearInterpolation(PeriodicCalendar())
6062

61-
function setup_model(FT, start_date, stop_date, domain, earth_param_set)
62-
time_interpolation_method =
63-
LONGER_RUN ? LinearInterpolation() :
64-
LinearInterpolation(PeriodicCalendar())
65-
surface_space = domain.space.surface
66-
subsurface_space = domain.space.subsurface
63+
function setup_simulation(; greet = false)
64+
# If not LONGER_RUN, run for 2 years; note that the forcing from 2008 is repeated.
65+
# If LONGER run, run for 20 years, with the correct forcing each year.
66+
start_date = LONGER_RUN ? DateTime(2000) : DateTime(2008)
67+
stop_date = LONGER_RUN ? DateTime(2020) : DateTime(2010)
68+
Δt = 450.0
69+
nelements = (101, 15)
70+
if greet
71+
@info "Run: Global Soil Model"
72+
@info "Resolution: $nelements"
73+
@info "Timestep: $Δt s"
74+
@info "Start Date: $start_date"
75+
@info "Stop Date: $stop_date"
76+
end
77+
domain = ClimaLand.Domains.global_domain(FT; context, nelements)
78+
earth_param_set = LP.LandParameters(FT)
6779
# Forcing data
6880
if LONGER_RUN
6981
era5_ncdata_path = ClimaLand.Artifacts.find_era5_year_paths(
@@ -75,81 +87,16 @@ function setup_model(FT, start_date, stop_date, domain, earth_param_set)
7587
era5_ncdata_path =
7688
ClimaLand.Artifacts.era5_land_forcing_data2008_path(; context)
7789
end
78-
atmos, radiation = ClimaLand.prescribed_forcing_era5(
90+
forcing = ClimaLand.prescribed_forcing_era5(
7991
era5_ncdata_path,
80-
surface_space,
92+
domain.space.surface,
8193
start_date,
8294
earth_param_set,
8395
FT;
8496
max_wind_speed = 25.0,
8597
time_interpolation_method,
8698
)
87-
88-
(; ν_ss_om, ν_ss_quartz, ν_ss_gravel) =
89-
ClimaLand.Soil.soil_composition_parameters(subsurface_space, FT)
90-
(; ν, hydrology_cm, K_sat, θ_r) =
91-
ClimaLand.Soil.soil_vangenuchten_parameters(subsurface_space, FT)
92-
soil_albedo = Soil.CLMTwoBandSoilAlbedo{FT}(;
93-
ClimaLand.Soil.clm_soil_albedo_parameters(surface_space)...,
94-
)
95-
S_s = ClimaCore.Fields.zeros(subsurface_space) .+ FT(1e-3)
96-
soil_params = Soil.EnergyHydrologyParameters(
97-
FT;
98-
ν,
99-
ν_ss_om,
100-
ν_ss_quartz,
101-
ν_ss_gravel,
102-
hydrology_cm,
103-
K_sat,
104-
S_s,
105-
θ_r,
106-
albedo = soil_albedo,
107-
)
108-
f_over = FT(3.28) # 1/m
109-
R_sb = FT(1.484e-4 / 1000) # m/s
110-
runoff_model = ClimaLand.Soil.Runoff.TOPMODELRunoff{FT}(;
111-
f_over = f_over,
112-
f_max = ClimaLand.Soil.topmodel_fmax(surface_space, FT),
113-
R_sb = R_sb,
114-
)
115-
116-
soil_args = (domain = domain, parameters = soil_params)
117-
soil_model_type = Soil.EnergyHydrology{FT}
118-
sources = (Soil.PhaseChange{FT}(),)# sublimation and subsurface runoff are added automatically
119-
top_bc = ClimaLand.Soil.AtmosDrivenFluxBC(
120-
atmos,
121-
radiation,
122-
runoff_model,
123-
(:soil,),
124-
)
125-
zero_flux = Soil.HeatFluxBC((p, t) -> 0.0)
126-
boundary_conditions =
127-
(; top = top_bc, bottom = Soil.EnergyWaterFreeDrainage())
128-
soil = soil_model_type(;
129-
boundary_conditions = boundary_conditions,
130-
sources = sources,
131-
soil_args...,
132-
)
133-
return soil
134-
end
135-
136-
function setup_simulation(; greet = false)
137-
# If not LONGER_RUN, run for 2 years; note that the forcing from 2008 is repeated.
138-
# If LONGER run, run for 20 years, with the correct forcing each year.
139-
start_date = LONGER_RUN ? DateTime(2000) : DateTime(2008)
140-
stop_date = LONGER_RUN ? DateTime(2020) : DateTime(2010)
141-
Δt = 450.0
142-
nelements = (101, 15)
143-
if greet
144-
@info "Run: Global Soil Model"
145-
@info "Resolution: $nelements"
146-
@info "Timestep: $Δt s"
147-
@info "Start Date: $start_date"
148-
@info "Stop Date: $stop_date"
149-
end
150-
domain = ClimaLand.Domains.global_domain(FT; context, nelements)
151-
params = LP.LandParameters(FT)
152-
model = setup_model(FT, start_date, stop_date, domain, params)
99+
model = ClimaLand.Soil.EnergyHydrology(FT, domain, forcing, earth_param_set)
153100
diagnostics = ClimaLand.default_diagnostics(
154101
model,
155102
start_date;

src/shared_utilities/drivers.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,7 +1506,7 @@ function prescribed_forcing_era5(
15061506
earth_param_set = earth_param_set,
15071507
frac_diff = frac_diff,
15081508
)
1509-
return atmos, radiation
1509+
return (; atmos, radiation)
15101510
end
15111511

15121512

src/simulations/Simulations.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function LandSimulation(
8484
Dates.Month(1),
8585
start_date,
8686
ITime(Δt, epoch = start_date),
87-
mask = ClimaLand.landsea_mask(ClimaLand.get_domain(model)),
87+
mask = ClimaLand.Domains.landsea_mask(ClimaLand.get_domain(model)),
8888
),
8989
ClimaLand.ReportCallback(1000),
9090
),

src/standalone/Soil/Soil.jl

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,134 @@ include("./soil_hydrology_parameterizations.jl")
188188
include("./spatially_varying_parameters.jl")
189189
include("Biogeochemistry/Biogeochemistry.jl")
190190
using .Biogeochemistry
191+
192+
193+
# Soil model constructor useful for working with simulations forced by
194+
# the atmosphere
195+
"""
196+
EnergyHydrology(FT, domain, forcing, earth_param_set;
197+
prognostic_land_components = (:soil),
198+
albedo = Soil.CLMTwoBandSoilAlbedo{FT}(; clm_soil_albedo_parameters(domain.space.surface)...),
199+
runoff = ClimaLand.Soil.Runoff.TOPMODELRunoff{FT}(f_over = FT(3.28),
200+
R_sb = FT(1.484e-4 / 1000),
201+
f_max = topmodel_fmax(domain.space.surface,FT),
202+
),
203+
retention_parameters = soil_vangenuchten_parameters(domain.space.subsurface, FT),
204+
composition_parameters = soil_composition_parameters(domain.space.subsurface, FT),
205+
S_s = ClimaCore.Fields.zeros(domain.space.subsurface) .+ 1e-3,
206+
additional_sources = (),
207+
)
208+
209+
Creates a EnergyHydrology model with the given float type FT, domain, earth_param_set, forcing, and prognostic land components.
210+
211+
When running the soil model in standalone mode, `prognostic_land_components = (:soil,)`, while for running integrated land models,
212+
this should be a list of the component models. This value of this argument must be the same across all components in the land model.
213+
214+
Default spatially varying parameters (for retention curve parameters, composition, and specific storativity) are provided but can be
215+
changed with keyword arguments.
216+
217+
The runoff and albedo parameterizations are also provided and can be changed via keyword argument;
218+
additional sources may be required in your model if the soil model will be composed with other
219+
component models.
220+
221+
TODO: Move scalar parameters to ClimaParams and obtain from earth_param_set, possibly use types in retention and composition arguments.
222+
"""
223+
function EnergyHydrology(
224+
FT,
225+
domain,
226+
forcing,
227+
earth_param_set;
228+
prognostic_land_components = (:soil,),
229+
albedo::AbstractSoilAlbedoParameterization = CLMTwoBandSoilAlbedo{FT}(;
230+
clm_soil_albedo_parameters(domain.space.surface)...,
231+
),
232+
runoff::Runoff.AbstractRunoffModel = Runoff.TOPMODELRunoff{FT}(
233+
f_over = FT(3.28), # extract from EPS
234+
R_sb = FT(1.484e-4 / 1000),# extract from EPS
235+
f_max = topmodel_fmax(domain.space.surface, FT),
236+
),
237+
retention_parameters = soil_vangenuchten_parameters(
238+
domain.space.subsurface,
239+
FT,
240+
),
241+
composition_parameters = soil_composition_parameters(
242+
domain.space.subsurface,
243+
FT,
244+
),
245+
S_s = ClimaCore.Fields.zeros(domain.space.subsurface) .+ 1e-3,
246+
additional_sources = (),
247+
)
248+
top_bc = AtmosDrivenFluxBC(
249+
forcing.atmos,
250+
forcing.radiation,
251+
runoff;
252+
prognostic_land_components,
253+
)
254+
bottom_bc = EnergyWaterFreeDrainage()
255+
boundary_conditions = (; top = top_bc, bottom = bottom_bc)
256+
# sublimation and subsurface runoff are added automatically
257+
sources = (additional_sources..., PhaseChange{FT}())
258+
parameters = EnergyHydrologyParameters(
259+
FT;
260+
retention_parameters...,
261+
composition_parameters...,
262+
albedo,
263+
S_s,
264+
)
265+
return EnergyHydrology{FT}(;
266+
parameters,
267+
domain,
268+
boundary_conditions,
269+
sources,
270+
)
271+
end
272+
273+
274+
"""
275+
RichardsModel(FT, domain, forcing;
276+
runoff = ClimaLand.Soil.Runoff.TOPMODELRunoff{FT}(f_over = FT(3.28),
277+
R_sb = FT(1.484e-4 / 1000),
278+
f_max = topmodel_fmax(domain.space.surface,FT),
279+
),
280+
retention_parameters = soil_vangenuchten_parameters(domain.space.subsurface, FT),
281+
S_s = ClimaCore.Fields.zeros(domain.space.subsurface) .+ 1e-3,
282+
)
283+
284+
Creates a RichardsModel model with the given float type FT, domain, earth_param_set and forcing.
285+
286+
Default spatially varying parameters (for retention curve parameters and specific storativity) are provided but can be
287+
changed with keyword arguments.
288+
289+
The runoff parameterization is also provided and can be changed via keyword argument.
290+
291+
TODO: Move scalar parameters to ClimaParams and obtain from earth_param_set, possibly use types in retention argument.
292+
"""
293+
function RichardsModel(
294+
FT,
295+
domain,
296+
forcing;
297+
runoff::Runoff.AbstractRunoffModel = Runoff.TOPMODELRunoff{FT}(
298+
f_over = FT(3.28), # extract from EPS
299+
R_sb = FT(1.484e-4 / 1000),# extract from EPS
300+
f_max = topmodel_fmax(domain.space.surface, FT),
301+
),
302+
retention_parameters = soil_vangenuchten_parameters(
303+
domain.space.subsurface,
304+
FT,
305+
),
306+
S_s = ClimaCore.Fields.zeros(domain.space.subsurface) .+ 1e-3,
307+
)
308+
top_bc = RichardsAtmosDrivenFluxBC(forcing.atmos, runoff)
309+
bottom_bc = WaterFluxBC((p, t) -> 0.0)
310+
boundary_conditions = (; top = top_bc, bottom = bottom_bc)
311+
parameters = RichardsParameters(retention_parameters..., S_s)
312+
return RichardsModel{FT}(;
313+
parameters,
314+
domain,
315+
boundary_conditions,
316+
sources = (),
317+
lateral_flow = false,
318+
)
319+
end
320+
191321
end

0 commit comments

Comments
 (0)