Skip to content

Model specification in simulations struct #1140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/shared_utilities/drivers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1389,7 +1389,7 @@ function prescribed_forcing_era5(
earth_param_set = earth_param_set,
frac_diff = frac_diff,
)
return atmos, radiation
return (; atmos, radiation)
end


Expand Down
2 changes: 1 addition & 1 deletion src/simulations/ModelSetup.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ extrapolation_bc =

include("domains.jl")
include("spatial_parameters.jl")

include("model_setup.jl")
end # module
145 changes: 145 additions & 0 deletions src/simulations/model_setup.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Goal:
# each model has the following signature: Model(FT, domain, earth_param_set, forcing, prognostic_land_components; params = default_params(domain))

function EnergyHydrology(FT, domain, earth_param_set, forcing, prognostic_land_components;
runoff_model = ClimaLand.Soil.Runoff.TOPMODELRunoff{FT}(f_over = FT(3.28), # extract from EPS
R_sb = FT(1.484e-4 / 1000),# extract from EPS
f_max = ClimaLand.topmodel_fmax(domain.space.surface,FT),
),
retention_parameters = soil_vangenuchten_parameters(domain.space.subsurface, FT,), # Should this be vanGenuchten type?
composition_parameters = soil_composition_parameters(domain.space.subsurface, FT,),
albedo_parameters = clm_soil_albedo_parameters(domain.space.surface, FT), # eventually, can be CLMSoilAlbedo type
S_s = ClimaCore.Fields.zeros(domain.space.subsurface) .+ 1e-3,# extract from EPS or get from file
)
top_bc = ClimaLand.AtmosDrivenFluxBC(forcing.atmos,
forcing.radiation,
runoff_model,
prognostic_land_components),
bottom_bc = Soil.EnergyWaterFreeDrainage()
boundary_conditions = (; top = top_bc, bottom = bottom_bc)
if :canopy ∈ prognostic_land_components
sources = (RootExtraction{FT}(), Soil.PhaseChange{FT}())
else
sources = (Soil.PhaseChange{FT}(),)
end
parameters = Soil.EnergyHydrologyParameters(FT; retention_parameters..., composition_parameters..., albedo_parameters..., S_s)
return EnergyHydrology{FT}(; parameters, domain, boundary_conditions, sources)
end

function SnowModel(FT, domain, earth_param_set, forcing, prognostic_land_components, Δt; # how to handle Δt long temr
parameters = SnowParameters{FT}(Δt; earth_param_set = earth_param_set)
)
boundary_conditions = Snow.AtmosDrivenSnowBC(forcing.atmos,
forcing.radiation,
prognostic_land_components,
)
return SnowModel{FT}(; boundary_conditions, domain, parameters)
end

function SoilCO2Model(FT, domain, earth_param_set, forcing, prognostic_land_components, soil_params; # how to handle soil params
parameters = Soil.Biogeochemistry.SoilCO2ModelParameters(FT),
)
top_bc = Soil.Biogeochemistry.AtmosCO2StateBC()
bottom_bc = Soil.Biogeochemistry.SoilCO2FluxBC((p, t) -> 0.0)
drivers = Soil.Biogeochemistry.SoilDrivers(Soil.Biogeochemistry.PrognosticMet(soil_params),
forcing.soil_organic_carbon,
forcing.atmos,
)
boundary_conditions =
(; top = top_bc, bottom = bot_bc)
return SoilCO2Model{FT}(; boundary_conditions, sources, domain, parameters, drivers)
end
function CanopyModel(FT, domain, earth_param_set, forcing, prognostic_land_components;
scalar_params = ;
autotrophic_respiration = Canopy.AutotrophicRespirationModel{FT}(Canopy.AutotrophicRespirationParameters(FT)),
radiative_transfer = Canopy.TwoStreamModel{FT}(Canopy.TwoStreamParameters(FT, domain, scalar_params)),
photosynthesis = Canopy.FarquharModel{FT}(Canopy.FarquharParameters(FT, domain, scalar_params)),
conductance = Canopy.MedlynConductanceModel{FT}(Canopy.MedlynConductanceParameters(FT, domain, scalar_params)),
hydraulics = Canopy.PlantHydraulicsModel{FT}(Canopy.PlantHydraulicsParameters(scalar_params)),
energy = Canopy.BigLeafEnergyModel{FT}(Canopy.BigLeafEnergyParameters{FT}(scalar_params)),
parameters = Canopy.SharedCanopyParameters{FT, typeof(earth_param_set)}(scalar_params
earth_param_set,
)
)
return CanopyModel()
end
# I need canopy component constructors that take in EPS and CLM path to spatially varying parameters, plus additional scalar params


function LandModel(FT, start_date, Δt, domain, earth_param_set;
prognostic_land_components = (:canopy, :snow, :soil, :soilco2),
forcing = ClimaLand.prescribed_forcing_era5(
ClimaLand.Artifacts.era5_land_forcing_data2008_path(; ClimaComms.context(domain)),
domain.space.surface,
start_date,
earth_param_set,
FT;
time_interpolation_method = LinearInterpolation(PeriodicCalendar()),
),
LAI = ClimaLand.prescribed_lai_modis(
ClimaLand.Artifacts.modis_lai_single_year_path(;
context = ClimaComms.context(domain),
year = Dates.year(start_date),
),
domain.space.surface,
start_date;
time_interpolation_method = LinearInterpolation(PeriodicCalendar()),
),
soil_organic_carbon = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)),
soil = EnergyHydrology(FT, domain, earth_param_set, forcing, prognostic_land_components),
canopy = CanopyModel(FT, domain, earth_param_set, forcing, prognostic_land_components)
snow = SnowModel(FT, domain, earth_param_set, forcing, prognostic_land_components, Δt),
soilco2 = SoilCO2Model(FT, domain, earth_param_set, merge(forcing, (;soil_organic_carbon = soil_organic_carbon)), prognostic_land_components, soil_params),
)
# Check that prognostic land components are the same for all components
@assert snow.boundary_conditions.prognostic_land_components == prognostic_land_components
@assert soil.boundary_conditions.top.prognostic_land_components == prognostic_land_components
@assert canopy.boundary_conditions.prognostic_land_components == prognostic_land_components
@assert progonostic_land_components == (:canopy, :snow, :soil, :soilco2)

# Check that we are applying the correct boundary condition type, and that the forcings are the same
@assert snow.boundary_conditions isa Snow.AtmosDrivenSnowBC
@assert soil.boundary_conditions.top isa Soil.AtmosDrivenFluxBC
@assert canopy.boundary_conditions isa Canopy.AtmosDrivenCanopyBC
@assert soilco2.boundary_conditions.top isa AtmosCO2StateBC()

@assert soil.boundary_conditions.top.atmos == forcing.atmos
@assert soil.boundary_conditions.top.radiation == forcing.radiation
@assert snow.boundary_conditions.atmos == forcing.atmos
@assert snow.boundary_conditions.radiation == forcing.radiation
@assert canopy.boundary_conditions.atmos == forcing.atmos
@assert canopy.boundary_conditions.radiation == forcing.radiation
@assert soilco2.soilco2_drivers.atmos == forcing.atmos

# Make sure the soilco2 model knows that the soil is prognostic
@assert soilco2.soilco2_drivers.soil == Soil.Biogeochemistry.PrognosticMet(soil.parameters)

# Make sure the canopy knows that the ground is prognostic
@assert canopy.boundary_conditions.ground == PrognosticGroundConditions()

# Make sure that the soil knows that the canopy is present
@assert RootExtraction{FT}() ∈ soil.sources
Comment on lines +118 to +121
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing namespace prefixes

-    @assert  canopy.boundary_conditions.ground == PrognosticGroundConditions()
+    @assert  canopy.boundary_conditions.ground == Canopy.PrognosticGroundConditions()
...
-    @assert RootExtraction{FT}() ∈ soil.sources
+    @assert Soil.RootExtraction{FT}() ∈ soil.sources
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@assert canopy.boundary_conditions.ground == PrognosticGroundConditions()
# Make sure that the soil knows that the canopy is present
@assert RootExtraction{FT}() soil.sources
@assert canopy.boundary_conditions.ground == Canopy.PrognosticGroundConditions()
# Make sure that the soil knows that the canopy is present
@assert Soil.RootExtraction{FT}() soil.sources
🤖 Prompt for AI Agents
In src/simulations/model_setup.jl around lines 118 to 121, the code uses types
or functions like PrognosticGroundConditions and RootExtraction without their
required namespace prefixes. To fix this, identify the correct modules or
namespaces these belong to and prefix them accordingly to avoid undefined or
ambiguous references. Update the code to use fully qualified names for these
types or functions.


# Make sure all have the same earth_param_set
@assert soil.parameters.earth_param_set == earth_param_set
@assert snow.parameters.earth_param_set == earth_param_set
@assert canopy.parameters.earth_param_set == earth_param_set
@assert soilco2.parameters.earth_param_set == earth_param_set

# Check for dt consistency:
@assert FT(float(Δt)) == snow.parameters.Δt

# Make sure that the LAI and other forcings have the same start date
@assert forcing.atmos.start_date == start_date
@assert forcing.radiation.start_date == start_date
# @assert LAI.start_date == start_date

# Make sure that the domains are consistent
@assert soil.domain == domain
@assert soilco2.domain == domain
@assert snow.domain == ClimaLand.Domains.obtain_surface_domain(domain)
@assert canopy.domain == ClimaLand.Domains.obtain_surface_domain(domain)

land = LandModel{FT}(soilco2, soil, canopy, snow)
return land
end
Loading