Skip to content

Commit 9292a01

Browse files
authored
Snow model constructor, Soil/snow integrated models (#1220)
1 parent b006c1d commit 9292a01

File tree

8 files changed

+218
-218
lines changed

8 files changed

+218
-218
lines changed

experiments/integrated/fluxnet/snow_soil/parameters_fluxnet.jl

Lines changed: 0 additions & 49 deletions
This file was deleted.

experiments/integrated/fluxnet/snow_soil/simulation.jl

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## Some site parameters have been taken from
2+
## Ma, S., Baldocchi, D. D., Xu, L., Hehn, T. (2007)
3+
## Inter-Annual Variability In Carbon Dioxide Exchange Of An
4+
## Oak/Grass Savanna And Open Grassland In California, Agricultural
5+
## And Forest Meteorology, 147(3-4), 157-171. https://doi.org/10.1016/j.agrformet.2007.07.008
6+
## CLM 5.0 Tech Note: https://www2.cesm.ucar.edu/models/cesm2/land/CLM50_Tech_Note.pdf
7+
# Bonan, G. Climate change and terrestrial ecosystem modeling. Cambridge University Press, 2019.
8+
19
import SciMLBase
210
import ClimaTimeSteppers as CTS
311
import ClimaComms
@@ -19,6 +27,11 @@ import ClimaParams
1927
climaland_dir = pkgdir(ClimaLand)
2028

2129
FT = Float64
30+
site_ID = "US-MOz"
31+
time_offset = 7 # offset from UTC in hrs
32+
lat = FT(38.7441) # degree
33+
long = FT(-92.2000) # degree
34+
2235
earth_param_set = LP.LandParameters(FT)
2336
# Utility functions for reading in and filling fluxnet data
2437
include(joinpath(climaland_dir, "experiments/integrated/fluxnet/data_tools.jl"))
@@ -30,7 +43,7 @@ zmax = FT(0)
3043
dz_bottom = FT(1.0)
3144
dz_top = FT(0.04)
3245

33-
land_domain = Column(;
46+
domain = Column(;
3447
zlim = (zmin, zmax),
3548
nelements = nelements,
3649
dz_tuple = (dz_bottom, dz_top),
@@ -43,13 +56,16 @@ N_days = N_days_spinup + 360
4356
dt = FT(900)
4457
tf = t0 + FT(3600 * 24 * N_days)
4558

46-
# Read in the parameters for the site
47-
include(
48-
joinpath(
49-
climaland_dir,
50-
"experiments/integrated/fluxnet/snow_soil/parameters_fluxnet.jl",
51-
),
52-
)
59+
60+
data_link = "https://caltech.box.com/shared/static/7r0ci9pacsnwyo0o9c25mhhcjhsu6d72.csv"
61+
# Height of sensor on flux tower
62+
atmos_h = FT(32)
63+
64+
# Required to use the same met_drivers_FLUXNET script, even though we currently have no canopy
65+
h_leaf = FT(0)
66+
f_root_to_shoot = FT(0)
67+
plant_ν = FT(0)
68+
5369
# This reads in the data from the flux tower site and creates
5470
# the atmospheric and radiative driver structs for the model
5571
include(
@@ -58,57 +74,54 @@ include(
5874
"experiments/integrated/fluxnet/met_drivers_FLUXNET.jl",
5975
),
6076
)
61-
62-
63-
# Now we set up the model. For the soil model, we pick
64-
# a model type and model args:
65-
soil_domain = land_domain
66-
soil_albedo = Soil.ConstantTwoBandSoilAlbedo{FT}(;
67-
PAR_albedo = soil_α_PAR,
68-
NIR_albedo = soil_α_NIR,
77+
forcing = (; atmos, radiation)
78+
prognostic_land_components = (:snow, :soil)
79+
α_soil = Soil.ConstantTwoBandSoilAlbedo{FT}(;
80+
PAR_albedo = FT(0.25),
81+
NIR_albedo = FT(0.25),
6982
)
70-
soil_ps = Soil.EnergyHydrologyParameters(
71-
FT;
72-
ν = soil_ν,
73-
ν_ss_om,
74-
ν_ss_quartz,
75-
ν_ss_gravel,
76-
hydrology_cm = vanGenuchten{FT}(; α = soil_vg_α, n = soil_vg_n),
77-
K_sat = soil_K_sat,
78-
S_s = soil_S_s,
79-
θ_r,
80-
z_0m = z_0m_soil,
81-
z_0b = z_0b_soil,
82-
emissivity = soil_ϵ,
83-
albedo = soil_albedo,
83+
runoff = ClimaLand.Soil.SurfaceRunoff()
84+
retention_parameters = (;
85+
ν = FT(0.5),
86+
K_sat = FT(0.45 / 3600 / 100),
87+
hydrology_cm = vanGenuchten{FT}(; α = FT(2), n = FT(2)),
88+
θ_r = FT(0.09),
8489
)
85-
86-
soil_args = (parameters = soil_ps,)
87-
soil_model_type = Soil.EnergyHydrology
88-
89-
ρ = 300.0
90-
α = 0.8
91-
snow_parameters = SnowParameters{FT}(
92-
dt;
93-
α_snow = Snow.ConstantAlbedoModel(α),
94-
density = Snow.MinimumDensityModel(ρ),
95-
earth_param_set = earth_param_set,
96-
);
97-
snow_args = (; parameters = snow_parameters);
98-
snow_model_type = Snow.SnowModel
99-
land_input = (
100-
atmos = atmos,
101-
radiation = radiation,
102-
domain = land_domain,
103-
runoff = ClimaLand.Soil.SurfaceRunoff(),
90+
composition_parameters =
91+
(; ν_ss_quartz = FT(0.2), ν_ss_om = FT(0.0), ν_ss_gravel = FT(0.4))
92+
S_s = FT(1e-3)
93+
z_0m = FT(0.01)
94+
z_0b = FT(0.001)
95+
emissivity = FT(0.98)
96+
soil_model = Soil.EnergyHydrology(
97+
FT,
98+
domain,
99+
forcing,
100+
earth_param_set;
101+
prognostic_land_components,
102+
albedo = α_soil,
103+
runoff,
104+
retention_parameters,
105+
S_s,
106+
composition_parameters,
107+
z_0m,
108+
z_0b,
109+
emissivity,
104110
)
105-
land = ClimaLand.SoilSnowModel{FT}(;
106-
land_args = land_input,
107-
soil_model_type = soil_model_type,
108-
soil_args = soil_args,
109-
snow_args = snow_args,
110-
snow_model_type = snow_model_type,
111+
α_snow = Snow.ConstantAlbedoModel(0.8)
112+
density = Snow.MinimumDensityModel(300.0)
113+
snow_model = Snow.SnowModel(
114+
FT,
115+
ClimaLand.Domains.obtain_surface_domain(domain),
116+
forcing,
117+
earth_param_set,
118+
dt;
119+
prognostic_land_components,
120+
α_snow,
121+
density,
111122
)
123+
124+
land = ClimaLand.SoilSnowModel(; snow = snow_model, soil = soil_model)
112125
Y, p, cds = initialize(land)
113126
exp_tendency! = make_exp_tendency(land)
114127
imp_tendency! = make_imp_tendency(land)
@@ -131,7 +144,7 @@ T_0 =
131144
volumetric_heat_capacity.(
132145
Y.soil.ϑ_l,
133146
Y.soil.θ_i,
134-
soil_ps.ρc_ds,
147+
soil_model.parameters.ρc_ds,
135148
earth_param_set,
136149
)
137150
Y.soil.ρe_int =

experiments/standalone/Snow/process_snowmip.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ atmos = PrescribedAtmosphere(
9595
h_atmos,
9696
earth_param_set,
9797
)
98+
forcing = (; atmos, radiation)
9899
# Snow data
99100
snow_data = NCDataset(joinpath(path, obs_filename))
100101

experiments/standalone/Snow/snowmip_simulation.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,28 @@ ndays = (tf - t0) / 3600 / 24
5050

5151
domain = ClimaLand.Domains.Point(; z_sfc = FT(0))
5252

53-
density_model = NeuralSnow.NeuralDepthModel(FT)
54-
#density_model = Snow.MinimumDensityModel(ρ)
53+
density = NeuralSnow.NeuralDepthModel(FT)
54+
#density = Snow.MinimumDensityModel(ρ)
55+
α_snow = Snow.ConstantAlbedoModel(α)
5556

56-
parameters = SnowParameters{FT}(
57-
Δt;
58-
α_snow = Snow.ConstantAlbedoModel(α),
59-
density = density_model,
60-
earth_param_set = param_set,
61-
)
6257
model = ClimaLand.Snow.SnowModel(
63-
parameters = parameters,
64-
domain = domain,
65-
boundary_conditions = ClimaLand.Snow.AtmosDrivenSnowBC(atmos, radiation),
58+
FT,
59+
domain,
60+
forcing,
61+
earth_param_set,
62+
Δt;
63+
density,
64+
α_snow,
6665
)
66+
6767
Y, p, coords = ClimaLand.initialize(model)
6868

6969
# Set initial conditions
7070
Y.snow.S .= FT(SWE[1]) # first data point
7171
Y.snow.S_l .= 0 # this is a guess
7272
Y.snow.Z .= FT(depths[1]) #first depth value - comment out if using MinimumDensityModel instead of NeuralDepthModel
7373
Y.snow.U .=
74-
ClimaLand.Snow.energy_from_q_l_and_swe(FT(SWE[1]), FT(0), parameters) # with q_l = 0
74+
ClimaLand.Snow.energy_from_q_l_and_swe(FT(SWE[1]), FT(0), model.parameters) # with q_l = 0
7575

7676
set_initial_cache! = ClimaLand.make_set_initial_cache(model)
7777
set_initial_cache!(p, Y, t0)

src/integrated/soil_snow_model.jl

Lines changed: 22 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ using ClimaCore.Operators: column_integral_definite!
1515
end
1616
1717
A concrete type of land model used for simulating systems with
18-
snow and soil (and eventually rivers).
18+
snow and soil.
19+
20+
The inner constructor checks that the two models are consistent with
21+
respect to the forcing (atmos, radiation), the parameters, the domain,
22+
and the prognostic land components of the model.
1923
$(DocStringExtensions.FIELDS)
2024
"""
2125
struct SoilSnowModel{
@@ -27,75 +31,25 @@ struct SoilSnowModel{
2731
snow::SnM
2832
"The soil model to be used"
2933
soil::SoM
30-
end
31-
32-
"""
33-
SoilSnowModel{FT}(;
34-
land_args::NamedTuple = (;),
35-
snow_model_type::Type{SnM},
36-
snow_args::NamedTuple = (;),
37-
soil_model_type::Type{SoM},
38-
soil_args::NamedTuple = (;),
39-
) where {
40-
FT,
41-
SnM <: Snow.SnowModel{FT},
42-
SoM <: Soil.EnergyHydrology{FT},
43-
}
44-
45-
A constructor for the `LandHydrology`, which takes in the concrete model
46-
type and required arguments for each component, constructs those models,
47-
and constructs the `SoilSnowModel` from them.
48-
49-
Each component model is constructed with everything it needs to be stepped
50-
forward in time, including boundary conditions, source terms, and interaction
51-
terms.
52-
"""
53-
function SoilSnowModel{FT}(;
54-
land_args::NamedTuple = (;),
55-
snow_model_type::Type{SnM},
56-
snow_args::NamedTuple = (;),
57-
soil_model_type::Type{SoM},
58-
soil_args::NamedTuple = (;),
59-
) where {FT, SnM <: Snow.SnowModel, SoM <: Soil.EnergyHydrology}
60-
(; atmos, radiation, domain) = land_args
61-
prognostic_land_components = (:snow, :soil)
62-
if :runoff propertynames(land_args)
63-
runoff_model = land_args.runoff
64-
else
65-
runoff_model = ClimaLand.Soil.Runoff.NoRunoff()
34+
function SoilSnowModel(; snow, soil)
35+
prognostic_land_components = (:snow, :soil)
36+
top_soil_bc = soil.boundary_conditions.top
37+
snow_bc = snow.boundary_conditions
38+
@assert top_soil_bc.prognostic_land_components ==
39+
prognostic_land_components
40+
@assert snow_bc.prognostic_land_components == prognostic_land_components
41+
42+
@assert top_soil_bc.atmos == snow_bc.atmos
43+
@assert top_soil_bc.radiation == snow_bc.radiation
44+
45+
@assert Domains.obtain_surface_domain(soil.domain) == snow.domain
46+
47+
@assert soil.parameters.earth_param_set ==
48+
snow.parameters.earth_param_set
49+
FT = eltype(soil.parameters.earth_param_set)
50+
new{FT, typeof(snow), typeof(soil)}(snow, soil)
6651
end
67-
top_bc = ClimaLand.AtmosDrivenFluxBC(
68-
atmos,
69-
radiation,
70-
runoff_model,
71-
prognostic_land_components,
72-
)
73-
sources = (Soil.PhaseChange{FT}(),)
74-
zero_flux = Soil.HeatFluxBC((p, t) -> 0.0)
75-
boundary_conditions = (;
76-
top = top_bc,
77-
bottom = Soil.WaterHeatBC(;
78-
water = Soil.FreeDrainage(),
79-
heat = zero_flux,
80-
),
81-
)
82-
soil = soil_model_type{FT}(;
83-
boundary_conditions = boundary_conditions,
84-
sources = sources,
85-
domain = domain,
86-
soil_args...,
87-
)
88-
snow = snow_model_type(;
89-
boundary_conditions = Snow.AtmosDrivenSnowBC(
90-
atmos,
91-
radiation,
92-
prognostic_land_components,
93-
),
94-
domain = Domains.obtain_surface_domain(domain),
95-
snow_args...,
96-
)
9752

98-
return SoilSnowModel{FT, typeof(snow), typeof(soil)}(snow, soil)
9953
end
10054

10155
"""

0 commit comments

Comments
 (0)