diff --git a/NEWS.md b/NEWS.md index 0615f2a580..6711b078c7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,9 +3,13 @@ ClimaLand.jl Release Notes main -------- -- PR[#690] Use the soil parameters in creating the biogeochemistry SoilMet driver for consistency. +- Use the soil parameters in creating the biogeochemistry SoilMet driver for consistency. + PR[#690](https://github.com/CliMA/ClimaLand.jl/pull/690) +- ![][badge-💥breaking] Generalize our forcing ``drivers" to include prescribed soil organic carbon + PR[#692](https://github.com/CliMA/ClimaLand.jl/pull/692) - Add a long run with diagnostics PR[#688](https://github.com/CliMA/ClimaLand.jl/pull/688) + v0.13.0 -------- - NOTE: the breaking PR below was merged by accident in v0.12.5 diff --git a/docs/src/APIs/SoilBiogeochemistry.md b/docs/src/APIs/SoilBiogeochemistry.md index 3de0d106c2..123a73be58 100644 --- a/docs/src/APIs/SoilBiogeochemistry.md +++ b/docs/src/APIs/SoilBiogeochemistry.md @@ -25,7 +25,6 @@ ClimaLand.Soil.Biogeochemistry.AtmosCO2StateBC ClimaLand.Soil.Biogeochemistry.AbstractSoilDriver ClimaLand.Soil.Biogeochemistry.SoilDrivers ClimaLand.Soil.Biogeochemistry.PrescribedMet -ClimaLand.Soil.Biogeochemistry.PrescribedSOC ``` ## Functions of State diff --git a/docs/src/APIs/shared_utilities.md b/docs/src/APIs/shared_utilities.md index 56ca3fac0d..bc75c2bb35 100644 --- a/docs/src/APIs/shared_utilities.md +++ b/docs/src/APIs/shared_utilities.md @@ -71,6 +71,7 @@ ClimaLand.get_drivers ClimaLand.PrescribedAtmosphere ClimaLand.PrescribedPrecipitation ClimaLand.PrescribedRadiativeFluxes +ClimaLand.PrescribedSoilOrganicCarbon ClimaLand.CoupledAtmosphere ClimaLand.CoupledRadiativeFluxes ClimaLand.AbstractAtmosphericDrivers diff --git a/docs/tutorials/integrated/soil_canopy_tutorial.jl b/docs/tutorials/integrated/soil_canopy_tutorial.jl index ed52d28940..ff4ece3849 100644 --- a/docs/tutorials/integrated/soil_canopy_tutorial.jl +++ b/docs/tutorials/integrated/soil_canopy_tutorial.jl @@ -177,7 +177,7 @@ soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} soilco2_ps = SoilCO2ModelParameters(FT); # soil microbes args -Csom = (z, t) -> eltype(z)(5); # kg C m⁻³, this is a guess, not measured at the site +Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() soilco2_bot_bc = Soil.Biogeochemistry.SoilCO2StateBC((p, t) -> 0.0); @@ -390,7 +390,8 @@ sv = (; saveval = Array{NamedTuple}(undef, length(saveat)), ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(land) +updatefunc = ClimaLand.make_update_drivers(model_drivers) updateat = Array(t0:1800:tf) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/docs/tutorials/standalone/Bucket/bucket_tutorial.jl b/docs/tutorials/standalone/Bucket/bucket_tutorial.jl index d8f2fddb38..ab2fad4df1 100644 --- a/docs/tutorials/standalone/Bucket/bucket_tutorial.jl +++ b/docs/tutorials/standalone/Bucket/bucket_tutorial.jl @@ -329,7 +329,8 @@ saved_values = (; ); saving_cb = ClimaLand.NonInterpSavingCallback(saved_values, saveat); updateat = copy(saveat) -updatefunc = ClimaLand.make_update_drivers(bucket_atmos, bucket_rad) +model_drivers = ClimaLand.get_drivers(model) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) diff --git a/docs/tutorials/standalone/Canopy/canopy_tutorial.jl b/docs/tutorials/standalone/Canopy/canopy_tutorial.jl index e84449b1d7..f6efdcf314 100644 --- a/docs/tutorials/standalone/Canopy/canopy_tutorial.jl +++ b/docs/tutorials/standalone/Canopy/canopy_tutorial.jl @@ -314,7 +314,8 @@ saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat); # Create the callback function which updates the forcing variables, # or drivers. updateat = Array(t0:1800:tf) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(canopy) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/docs/tutorials/standalone/Soil/evaporation.jl b/docs/tutorials/standalone/Soil/evaporation.jl index 0698e0d2f0..2ead932021 100644 --- a/docs/tutorials/standalone/Soil/evaporation.jl +++ b/docs/tutorials/standalone/Soil/evaporation.jl @@ -211,7 +211,8 @@ sv = (; ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = deepcopy(saveat) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(soil) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/docs/tutorials/standalone/Soil/evaporation_gilat_loess.jl b/docs/tutorials/standalone/Soil/evaporation_gilat_loess.jl index e8424a31a6..f5b666d587 100644 --- a/docs/tutorials/standalone/Soil/evaporation_gilat_loess.jl +++ b/docs/tutorials/standalone/Soil/evaporation_gilat_loess.jl @@ -250,7 +250,8 @@ sv = (; ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = deepcopy(saveat) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(soil) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) sol = SciMLBase.solve(prob, ode_algo; dt = dt, callback = cb, saveat = saveat) @@ -315,7 +316,8 @@ sv = (; ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = deepcopy(saveat) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(soil) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) sol_no_drainage = diff --git a/docs/tutorials/standalone/Soil/sublimation.jl b/docs/tutorials/standalone/Soil/sublimation.jl index 0ee7093353..b143883be4 100644 --- a/docs/tutorials/standalone/Soil/sublimation.jl +++ b/docs/tutorials/standalone/Soil/sublimation.jl @@ -197,7 +197,8 @@ sv = (; ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = deepcopy(saveat) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(soil) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/experiments/benchmarks/bucket.jl b/experiments/benchmarks/bucket.jl index d602d00f71..31864c900d 100644 --- a/experiments/benchmarks/bucket.jl +++ b/experiments/benchmarks/bucket.jl @@ -133,7 +133,8 @@ function setup_prob(t0, tf, Δt; nelements = (100, 10)) p, ) updateat = collect(t0:(3Δt):tf) - updatefunc = ClimaLand.make_update_drivers(bucket_atmos, bucket_rad) + drivers = ClimaLand.get_drivers(model) + updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb) diff --git a/experiments/benchmarks/land.jl b/experiments/benchmarks/land.jl index 013ae3abaf..6d7c07b983 100644 --- a/experiments/benchmarks/land.jl +++ b/experiments/benchmarks/land.jl @@ -205,7 +205,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) soil_params_mask = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "vGalpha_map_gupta_etal2020_2.5x2.5x4.nc", + "vGalpha_map_gupta_etal2020_1.0x1.0x4.nc", ), "α", subsurface_space; @@ -219,7 +219,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) vg_α = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "vGalpha_map_gupta_etal2020_2.5x2.5x4.nc", + "vGalpha_map_gupta_etal2020_1.0x1.0x4.nc", ), "α", subsurface_space; @@ -229,7 +229,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) vg_n = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "vGn_map_gupta_etal2020_2.5x2.5x4.nc", + "vGn_map_gupta_etal2020_1.0x1.0x4.nc", ), "n", subsurface_space; @@ -251,7 +251,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) θ_r = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "residual_map_gupta_etal2020_2.5x2.5x4.nc", + "residual_map_gupta_etal2020_1.0x1.0x4.nc", ), "θ_r", subsurface_space; @@ -262,7 +262,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) ν = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "porosity_map_gupta_etal2020_2.5x2.5x4.nc", + "porosity_map_gupta_etal2020_1.0x1.0x4.nc", ), "ν", subsurface_space; @@ -272,7 +272,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) K_sat = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "ksat_map_gupta_etal2020_2.5x2.5x4.nc", + "ksat_map_gupta_etal2020_1.0x1.0x4.nc", ), "Ksat", subsurface_space; @@ -409,7 +409,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} # soil microbes args - Csom = (z, t) -> eltype(z)(5.0) + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) # Set the soil CO2 BC to being atmospheric CO2 soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() @@ -591,7 +591,8 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) ) updateat = Array(t0:(3600 * 3):tf) - updatefunc = ClimaLand.make_update_drivers(atmos, radiation) + drivers = ClimaLand.get_drivers(land) + updatefunc = ClimaLand.make_update_drivers(drivers) cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) return prob, cb end diff --git a/experiments/benchmarks/richards.jl b/experiments/benchmarks/richards.jl index 1a3ade6148..c24dd998f4 100644 --- a/experiments/benchmarks/richards.jl +++ b/experiments/benchmarks/richards.jl @@ -115,7 +115,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) vg_α = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "vGalpha_map_gupta_etal2020_2.5x2.5x4.nc", + "vGalpha_map_gupta_etal2020_1.0x1.0x4.nc", ), "α", subsurface_space; @@ -135,7 +135,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) vg_n = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "vGn_map_gupta_etal2020_2.5x2.5x4.nc", + "vGn_map_gupta_etal2020_1.0x1.0x4.nc", ), "n", subsurface_space; @@ -150,7 +150,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) θ_r = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "residual_map_gupta_etal2020_2.5x2.5x4.nc", + "residual_map_gupta_etal2020_1.0x1.0x4.nc", ), "θ_r", subsurface_space; @@ -161,7 +161,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) ν = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "porosity_map_gupta_etal2020_2.5x2.5x4.nc", + "porosity_map_gupta_etal2020_1.0x1.0x4.nc", ), "ν", subsurface_space; @@ -172,7 +172,7 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) K_sat = SpaceVaryingInput( joinpath( soil_params_artifact_path, - "ksat_map_gupta_etal2020_2.5x2.5x4.nc", + "ksat_map_gupta_etal2020_1.0x1.0x4.nc", ), "Ksat", subsurface_space; @@ -277,7 +277,8 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) p, ) updateat = Array(t0:(2Δt):tf) - updatefunc = ClimaLand.make_update_drivers(atmos, nothing) + drivers = ClimaLand.get_drivers(model) + updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb) diff --git a/experiments/integrated/fluxnet/ozark_pft.jl b/experiments/integrated/fluxnet/ozark_pft.jl index 3ccde05c20..680afe5cff 100644 --- a/experiments/integrated/fluxnet/ozark_pft.jl +++ b/experiments/integrated/fluxnet/ozark_pft.jl @@ -137,8 +137,7 @@ soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} soilco2_ps = SoilCO2ModelParameters(FT) -# soil microbes args -Csom = (z, t) -> eltype(z)(5.0) +Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) # Set the soil CO2 BC to being atmospheric CO2 soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() @@ -298,7 +297,8 @@ saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) ## How often we want to update the drivers. Note that this uses the defined `t0` and `tf` ## defined in the simulatons file updateat = Array(t0:DATA_DT:tf) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(land) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/experiments/integrated/fluxnet/run_fluxnet.jl b/experiments/integrated/fluxnet/run_fluxnet.jl index d802db5b5f..1dfc7cd645 100644 --- a/experiments/integrated/fluxnet/run_fluxnet.jl +++ b/experiments/integrated/fluxnet/run_fluxnet.jl @@ -96,8 +96,7 @@ soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} soilco2_ps = SoilCO2ModelParameters(FT) -# soil microbes args -Csom = (z, t) -> eltype(z)(5.0) +Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) # Set the soil CO2 BC to being atmospheric CO2 soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() @@ -254,7 +253,8 @@ saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) ## How often we want to update the drivers. Note that this uses the defined `t0` and `tf` ## defined in the simulatons file updateat = Array(t0:DATA_DT:tf) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +model_drivers = ClimaLand.get_drivers(land) +updatefunc = ClimaLand.make_update_drivers(model_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) diff --git a/experiments/integrated/global/global_soil_canopy.jl b/experiments/integrated/global/global_soil_canopy.jl index ef2bb9c0d9..4a6962e807 100644 --- a/experiments/integrated/global/global_soil_canopy.jl +++ b/experiments/integrated/global/global_soil_canopy.jl @@ -181,7 +181,7 @@ soil_model_type = Soil.EnergyHydrology{FT} soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} # soil microbes args -Csom = (z, t) -> eltype(z)(5.0) +Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) # Set the soil CO2 BC to being atmospheric CO2 soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() @@ -368,7 +368,8 @@ sv = (; ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = Array(t0:(3600 * 3):tf) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +drivers = ClimaLand.get_drivers(land) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) @time sol = SciMLBase.solve( diff --git a/experiments/integrated/performance/conservation/ozark_conservation.jl b/experiments/integrated/performance/conservation/ozark_conservation.jl index 91755ca1c1..1b4e6d7150 100644 --- a/experiments/integrated/performance/conservation/ozark_conservation.jl +++ b/experiments/integrated/performance/conservation/ozark_conservation.jl @@ -41,7 +41,8 @@ for float_type in (Float32, Float64) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = deepcopy(saveat) - updatefunc = ClimaLand.make_update_drivers(atmos, radiation) + drivers = ClimaLand.get_drivers(land) + updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) prob = SciMLBase.ODEProblem( CTS.ClimaODEFunction( diff --git a/experiments/integrated/performance/conservation/ozark_conservation_setup.jl b/experiments/integrated/performance/conservation/ozark_conservation_setup.jl index b40f34c80c..39c7e87a32 100644 --- a/experiments/integrated/performance/conservation/ozark_conservation_setup.jl +++ b/experiments/integrated/performance/conservation/ozark_conservation_setup.jl @@ -82,7 +82,7 @@ soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} soilco2_ps = SoilCO2ModelParameters(FT) # soil microbes args -Csom = (z, t) -> eltype(z)(5.0) +Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) # Set the soil CO2 BC to being atmospheric CO2 soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() diff --git a/experiments/integrated/performance/profile_allocations.jl b/experiments/integrated/performance/profile_allocations.jl index e9b898383c..a539ac4a45 100644 --- a/experiments/integrated/performance/profile_allocations.jl +++ b/experiments/integrated/performance/profile_allocations.jl @@ -229,7 +229,7 @@ soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} soilco2_ps = SoilCO2ModelParameters(FT) # soil microbes args -Csom = (z, t) -> eltype(z)(5.0) +Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) # Set the soil CO2 BC to being atmospheric CO2 soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() @@ -339,7 +339,8 @@ ode_algo = CTS.IMEXAlgorithm( ), ); -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +drivers = ClimaLand.get_drivers(land) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) # Set initial conditions Y, p = set_initial_conditions(land, t0) @@ -370,7 +371,8 @@ if PROFILING # Now that we compiled, solve again but collect profiling information Y, p = set_initial_conditions(land, t0) updateat = Array(t0:dt:tf) - updatefunc = ClimaLand.make_update_drivers(atmos, radiation) + drivers = ClimaLand.get_drivers(land) + updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) prob = SciMLBase.ODEProblem( CTS.ClimaODEFunction( @@ -394,7 +396,8 @@ if PROFILING @info "Save compute flame to flame_file" Y, p = set_initial_conditions(land, t0) updateat = Array(t0:dt:tf) - updatefunc = ClimaLand.make_update_drivers(atmos, radiation) + drivers = ClimaLand.get_drivers(land) + updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) prob = SciMLBase.ODEProblem( CTS.ClimaODEFunction( diff --git a/experiments/long_runs/land.jl b/experiments/long_runs/land.jl index 2188dfaaf3..e48d785748 100644 --- a/experiments/long_runs/land.jl +++ b/experiments/long_runs/land.jl @@ -408,7 +408,7 @@ function setup_prob(t0, tf, Δt; outdir = outdir, nelements = (101, 15)) soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} # soil microbes args - Csom = (z, t) -> eltype(z)(5.0) + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) # Set the soil CO2 BC to being atmospheric CO2 soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() @@ -590,7 +590,8 @@ function setup_prob(t0, tf, Δt; outdir = outdir, nelements = (101, 15)) ) updateat = Array(t0:(3600 * 3):tf) - updatefunc = ClimaLand.make_update_drivers(atmos, radiation) + drivers = ClimaLand.get_drivers(land) + updatefunc = ClimaLand.make_update_drivers(drivers) # ClimaDiagnostics diff --git a/experiments/standalone/Biogeochemistry/experiment.jl b/experiments/standalone/Biogeochemistry/experiment.jl index a45fc4559f..5df25a8df8 100644 --- a/experiments/standalone/Biogeochemistry/experiment.jl +++ b/experiments/standalone/Biogeochemistry/experiment.jl @@ -69,7 +69,7 @@ for (FT, tf) in ((Float32, 2 * dt), (Float64, tf)) ) # Make biogeochemistry model args - Csom = (z, t) -> eltype(z)(5.0) + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) co2_parameters = Soil.Biogeochemistry.SoilCO2ModelParameters(FT) C = FT(100) @@ -165,7 +165,8 @@ for (FT, tf) in ((Float32, 2 * dt), (Float64, tf)) ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = deepcopy(saveat) - updatefunc = ClimaLand.make_update_drivers(atmos, nothing) + drivers = ClimaLand.get_drivers(model) + updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) diff --git a/experiments/standalone/Bucket/global_bucket_function.jl b/experiments/standalone/Bucket/global_bucket_function.jl index 46db512373..926346362f 100644 --- a/experiments/standalone/Bucket/global_bucket_function.jl +++ b/experiments/standalone/Bucket/global_bucket_function.jl @@ -178,7 +178,8 @@ diagnostic_handler = diag_cb = ClimaDiagnostics.DiagnosticsCallback(diagnostic_handler) updateat = collect(t0:(Δt * 3):tf); -updatefunc = ClimaLand.make_update_drivers(bucket_atmos, bucket_rad) +drivers = ClimaLand.get_drivers(model) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) sol = ClimaComms.@time ClimaComms.device() SciMLBase.solve( diff --git a/experiments/standalone/Bucket/global_bucket_staticmap.jl b/experiments/standalone/Bucket/global_bucket_staticmap.jl index 14db779dc3..924f948425 100644 --- a/experiments/standalone/Bucket/global_bucket_staticmap.jl +++ b/experiments/standalone/Bucket/global_bucket_staticmap.jl @@ -243,7 +243,8 @@ saved_values = (; ); saving_cb = ClimaLand.NonInterpSavingCallback(saved_values, saveat); updateat = copy(saveat) -updatefunc = ClimaLand.make_update_drivers(bucket_atmos, bucket_rad) +drivers = ClimaLand.get_drivers(model) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) diff --git a/experiments/standalone/Bucket/global_bucket_temporalmap.jl b/experiments/standalone/Bucket/global_bucket_temporalmap.jl index 02be3bf584..1616a81df0 100644 --- a/experiments/standalone/Bucket/global_bucket_temporalmap.jl +++ b/experiments/standalone/Bucket/global_bucket_temporalmap.jl @@ -181,7 +181,8 @@ function setup_prob(t0, tf, Δt) ) saving_cb = ClimaLand.NonInterpSavingCallback(saved_values, saveat) updateat = copy(saveat) - updatefunc = ClimaLand.make_update_drivers(bucket_atmos, bucket_rad) + drivers = ClimaLand.get_drivers(model) + updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) diff --git a/experiments/standalone/Snow/snow_cdp.jl b/experiments/standalone/Snow/snow_cdp.jl index 01bce4d488..10c1aa97c6 100644 --- a/experiments/standalone/Snow/snow_cdp.jl +++ b/experiments/standalone/Snow/snow_cdp.jl @@ -58,7 +58,8 @@ sv = (; ); saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat); updateat = copy(saveat) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +drivers = ClimaLand.get_drivers(model) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) diff --git a/experiments/standalone/Soil/richards_runoff.jl b/experiments/standalone/Soil/richards_runoff.jl index 90d6db37d8..91b980afcc 100644 --- a/experiments/standalone/Soil/richards_runoff.jl +++ b/experiments/standalone/Soil/richards_runoff.jl @@ -249,7 +249,8 @@ sv = (; ) saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) updateat = Array(t0:dt:tf) -updatefunc = ClimaLand.make_update_drivers(atmos, nothing) +drivers = ClimaLand.get_drivers(model) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) sol = @time SciMLBase.solve(prob, ode_algo; dt = dt, saveat = dt, callback = cb) diff --git a/experiments/standalone/Vegetation/no_vegetation.jl b/experiments/standalone/Vegetation/no_vegetation.jl index 86f3993549..570e456825 100644 --- a/experiments/standalone/Vegetation/no_vegetation.jl +++ b/experiments/standalone/Vegetation/no_vegetation.jl @@ -192,7 +192,8 @@ sv = (; saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat); updateat = Array(t0:1800:tf) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +drivers = ClimaLand.get_drivers(canopy) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/experiments/standalone/Vegetation/varying_lai.jl b/experiments/standalone/Vegetation/varying_lai.jl index 673c83769f..883b424af3 100644 --- a/experiments/standalone/Vegetation/varying_lai.jl +++ b/experiments/standalone/Vegetation/varying_lai.jl @@ -192,7 +192,8 @@ sv = (; saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat); updateat = Array(t0:1800:tf) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +drivers = ClimaLand.get_drivers(canopy) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/experiments/standalone/Vegetation/varying_lai_with_stem.jl b/experiments/standalone/Vegetation/varying_lai_with_stem.jl index a1f45b7fdb..ac403f8fe1 100644 --- a/experiments/standalone/Vegetation/varying_lai_with_stem.jl +++ b/experiments/standalone/Vegetation/varying_lai_with_stem.jl @@ -199,7 +199,8 @@ sv = (; saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat); updateat = Array(t0:1800:tf) -updatefunc = ClimaLand.make_update_drivers(atmos, radiation) +drivers = ClimaLand.get_drivers(canopy) +updatefunc = ClimaLand.make_update_drivers(drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb); diff --git a/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl b/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl index e5920a746c..97907d5fa3 100644 --- a/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl +++ b/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl @@ -60,7 +60,7 @@ function run_fluxnet( ) # soil microbes args - Csom = (z, t) -> eltype(z)(5.0) + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) soilco2_top_bc = Soil.Biogeochemistry.AtmosCO2StateBC() soilco2_bot_bc = Soil.Biogeochemistry.SoilCO2FluxBC((p, t) -> 0.0) # no flux @@ -286,7 +286,8 @@ function run_fluxnet( ## How often we want to update the drivers. Note that this uses the defined `t0` and `tf` ## defined in the simulatons file updateat = Array((setup.t0):(drivers.DATA_DT):(timestepper.tf)) - updatefunc = ClimaLand.make_update_drivers(drivers.atmos, drivers.radiation) + land_drivers = ClimaLand.get_drivers(land) + updatefunc = ClimaLand.make_update_drivers(land_drivers) driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) cb = SciMLBase.CallbackSet(driver_cb, saving_cb) diff --git a/src/integrated/soil_canopy_model.jl b/src/integrated/soil_canopy_model.jl index 04577975d6..ee49351665 100644 --- a/src/integrated/soil_canopy_model.jl +++ b/src/integrated/soil_canopy_model.jl @@ -175,7 +175,7 @@ function SoilCanopyModel{FT}(; co2_prognostic_soil = Soil.Biogeochemistry.PrognosticMet(soil.parameters) soilco2_drivers = Soil.Biogeochemistry.SoilDrivers( co2_prognostic_soil, - Soil.Biogeochemistry.PrescribedSOC{FT}(soil_organic_carbon), + soil_organic_carbon, atmos, ) soilco2 = soilco2_type(; soilco2_args..., drivers = soilco2_drivers) @@ -592,5 +592,9 @@ end function ClimaLand.get_drivers(model::SoilCanopyModel) - return (model.canopy.atmos, model.canopy.radiation) + return ( + model.canopy.atmos, + model.canopy.radiation, + model.soilco2.drivers.soc, + ) end diff --git a/src/integrated/soil_energy_hydrology_biogeochemistry.jl b/src/integrated/soil_energy_hydrology_biogeochemistry.jl index e25d351f6c..9d0ef24446 100644 --- a/src/integrated/soil_energy_hydrology_biogeochemistry.jl +++ b/src/integrated/soil_energy_hydrology_biogeochemistry.jl @@ -50,7 +50,7 @@ function LandSoilBiogeochemistry{FT}(; prognostic_soil = Soil.Biogeochemistry.PrognosticMet(soil.parameters) soil_co2_drivers = Soil.Biogeochemistry.SoilDrivers( prognostic_soil, - Soil.Biogeochemistry.PrescribedSOC{FT}(soil_organic_carbon), + soil_organic_carbon, atmos, ) soilco2 = Soil.Biogeochemistry.SoilCO2Model{FT}(; @@ -115,8 +115,8 @@ function ClimaLand.get_drivers(model::LandSoilBiogeochemistry) <:AbstractRadiativeDrivers, <:Soil.AbstractRunoffModel, } - return (bc.atmos, bc.radiation) + return (bc.atmos, bc.radiation, model.soilco2.drivers.soc) else - return (model.soilco2.drivers.atmos, nothing) + return (model.soilco2.drivers.atmos, model.soilco2.drivers.soc) end end diff --git a/src/shared_utilities/drivers.jl b/src/shared_utilities/drivers.jl index c97348eadd..00cee967dc 100644 --- a/src/shared_utilities/drivers.jl +++ b/src/shared_utilities/drivers.jl @@ -11,6 +11,7 @@ export AbstractAtmosphericDrivers, AbstractRadiativeDrivers, PrescribedAtmosphere, PrescribedPrecipitation, + PrescribedSoilOrganicCarbon, CoupledAtmosphere, PrescribedRadiativeFluxes, CoupledRadiativeFluxes, @@ -27,18 +28,64 @@ export AbstractAtmosphericDrivers, make_update_drivers """ - AbstractAtmosphericDrivers{FT <: AbstractFloat} + AbstractClimaLandDrivers{FT <: AbstractFloat} + +An abstract type of radiative drivers of land models. +""" +abstract type AbstractClimaLandDrivers{FT <: AbstractFloat} end + +""" + initialize_drivers(::AbstractClimaLandDrivers, coords) + +Creates and returns a default empty NamedTuple for AbstractClimaLandDrivers. +More generally this should return a named tuple of the driver fields, which will +then be stored in the cache under `p.drivers`. +""" +initialize_drivers(::AbstractClimaLandDrivers, coords) = (;) + + +""" + make_update_drivers(::AbstractClimaLandDrivers) + +Creates and returns a function which updates the driver variables +in the default case of no drivers. More generally, this should return +a function which updates the driver fields stored in `p.drivers`. +""" +function make_update_drivers(::AbstractClimaLandDrivers) + update_drivers!(p, t) = nothing + return update_drivers! +end + + +""" + AbstractAtmosphericDrivers{FT} An abstract type of atmospheric drivers of land models. """ -abstract type AbstractAtmosphericDrivers{FT <: AbstractFloat} end +abstract type AbstractAtmosphericDrivers{FT} <: AbstractClimaLandDrivers{FT} end """ - AbstractRadiativeDrivers{FT <: AbstractFloat} + AbstractRadiativeDrivers{FT} An abstract type of radiative drivers of land models. """ -abstract type AbstractRadiativeDrivers{FT <: AbstractFloat} end +abstract type AbstractRadiativeDrivers{FT} <: AbstractClimaLandDrivers{FT} end + + +""" + PrescribedSoilOrganicCarbon{FT} + +A type for prescribing soil organic carbon. +$(DocStringExtensions.FIELDS) +""" +struct PrescribedSoilOrganicCarbon{FT, SOC <: AbstractTimeVaryingInput} <: + AbstractClimaLandDrivers{FT} + "Soil organic carbon, function of time and space: kg C/m^3" + soc::SOC +end + +PrescribedSoilOrganicCarbon{FT}(soc) where {FT} = + PrescribedSoilOrganicCarbon{FT, typeof(soc)}(soc) """ PrescribedAtmosphere{FT, CA, DT} <: AbstractAtmosphericDrivers{FT} @@ -759,68 +806,71 @@ function initialize_drivers(r::PrescribedRadiativeFluxes{FT}, coords) where {FT} end """ - initialize_drivers(d::Union{AbstractAtmosphericDrivers, AbstractRadiativeDrivers, Nothing}, coords) + initialize_drivers(r::PrescribedSoilOrganicCarbon{FT}, coords) where {FT} -Creates and returns a NamedTuple with `nothing` when no driver cache variables are needed. +Creates and returns a NamedTuple for the `PrescribedSoilOrganicCarbon` driver, + with variable `soc`. """ function initialize_drivers( - d::Union{AbstractAtmosphericDrivers, AbstractRadiativeDrivers, Nothing}, + r::PrescribedSoilOrganicCarbon{FT}, coords, -) - return (;) +) where {FT} + keys = (:soc,) + types = (FT,) + domain_names = (:subsurface,) + model_name = :drivers + # intialize_vars packages the variables as a named tuple, + # as part of a named tuple with `model_name` as the key. + # Here we just want the variable named tuple itself + vars = + ClimaLand.initialize_vars(keys, types, domain_names, coords, model_name) + return vars.drivers end """ - initialize_drivers(a::Union{AbstractAtmosphericDrivers, Nothing}, - r::Union{AbstractRadiativeDrivers, Nothing}, + initialize_drivers(driver_tuple::Tuple, coords) Creates and returns a NamedTuple with the cache variables required by the -atmospheric and radiative drivers. +model drivers. -If no forcing is required, `a` and `r` are type `Nothing` and an +If no forcing is required, driver_tuple is an empty tuple, and an empty NamedTuple is returned. """ -function initialize_drivers( - a::Union{AbstractAtmosphericDrivers, Nothing}, - r::Union{AbstractRadiativeDrivers, Nothing}, - coords, -) - atmos_drivers = initialize_drivers(a, coords) - radiation_drivers = initialize_drivers(r, coords) - merge(atmos_drivers, radiation_drivers) +function initialize_drivers(driver_tuple::Tuple, coords) + if isempty(driver_tuple) + return (;) + else + tmp = map(driver_tuple) do (driver) + nt = initialize_drivers(driver, coords) + end + merge(tmp...) + end + end """ - make_update_drivers(a::Union{AbstractAtmosphericDrivers, Nothing}, - r::Union{AbstractRadiativeDrivers, Nothing}, - ) + make_update_drivers(driver_tuple) -Creates and returns a function which updates the atmospheric -and radiative forcing variables ("drivers"). +Creates and returns a function which updates the forcing variables ("drivers"). If no drivers are being used, driver_tuple is empty, and the update +function does nothing. """ -function make_update_drivers( - a::Union{AbstractAtmosphericDrivers, Nothing}, - r::Union{AbstractRadiativeDrivers, Nothing}, -) - update_atmos! = make_update_drivers(a) - update_radiation! = make_update_drivers(r) - function update_drivers!(p, t) - update_atmos!(p, t) - update_radiation!(p, t) +function make_update_drivers(driver_tuple::Tuple) + if isempty(driver_tuple) + return (p, t) -> nothing + else + update_driver_list = map(driver_tuple) do (driver) + make_update_drivers(driver) + end + function update_drivers!(p, t) + for ud! in update_driver_list + ud!(p, t) + end + end + return update_drivers! end - return update_drivers! end -""" - make_update_drivers(d::Union{AbstractAtmosphericDrivers, AbstractRadiativeDrivers, Nothing}) - -Creates and returns a function which updates the driver variables -in the case of no driver variables. This is also the default. -""" -make_update_drivers( - d::Union{AbstractAtmosphericDrivers, AbstractRadiativeDrivers, Nothing}, -) = (p, t) -> nothing """ make_update_drivers(a::PrescribedAtmosphere{FT}) where {FT} @@ -872,3 +922,15 @@ function make_update_drivers(r::PrescribedRadiativeFluxes{FT}) where {FT} end return update_drivers! end + +""" + make_update_drivers(d::PrescribedSoilOrganicCarbon{FT}) where {FT} +Creates and returns a function which updates the driver variables +in the case of a PrescribedSoilOrganicCarbon. +""" +function make_update_drivers(d::PrescribedSoilOrganicCarbon{FT}) where {FT} + function update_drivers!(p, t) + evaluate!(p.drivers.soc, d.soc, t) + end + return update_drivers! +end diff --git a/src/shared_utilities/models.jl b/src/shared_utilities/models.jl index b45bfa97a4..eef7083c57 100644 --- a/src/shared_utilities/models.jl +++ b/src/shared_utilities/models.jl @@ -170,8 +170,8 @@ cache variables are updated at the same time. function make_update_cache(model::AbstractModel) # if not forced using atmospheric/radiatiave drivers # this return (nothing, nothing) - (atmos, radiation) = get_drivers(model) - update_drivers! = make_update_drivers(atmos, radiation) + drivers = get_drivers(model) + update_drivers! = make_update_drivers(drivers) update_aux! = make_update_aux(model) update_boundary_fluxes! = make_update_boundary_fluxes(model) function update_cache!(p, Y, t) @@ -395,23 +395,24 @@ Domains.coordinates(model::AbstractModel) = Domains.coordinates(model.domain) """ add_drivers_to_cache(p::NamedTuple, model::AbstractModel, coords) -Creates the driver variable NamedTuple (atmospheric and radiative forcing), +Creates the driver variable NamedTuple (atmospheric and radiative forcing, etc), and merges it into `p` under the key `drivers`. If no driver variables are required, `p` is returned unchanged. """ function add_drivers_to_cache(p::NamedTuple, model::AbstractModel, coords) - (atmos, radiation) = get_drivers(model) + drivers = get_drivers(model) if hasproperty(model, :parameters) && hasproperty(model.parameters, :earth_param_set) && - atmos isa ClimaLand.PrescribedAtmosphere + hasproperty(drivers, :atmos) && + drivers.atmos isa ClimaLand.PrescribedAtmosphere if LP.thermodynamic_parameters(model.parameters.earth_param_set) != - atmos.thermo_params + drivers.atmos.thermo_params error( "earth_param_set is inconsistent between the model and the atmosphere", ) end end - driver_nt = initialize_drivers(atmos, radiation, coords) + driver_nt = initialize_drivers(drivers, coords) if driver_nt == (;) return p else @@ -422,10 +423,11 @@ end """ get_drivers(model::AbstractModel) -Returns the `driver` objects for the model - atmospheric and radiative forcing - as a tuple (atmos, radiation). +Returns the `driver` objects for the model - atmospheric and radiative forcing, etc - as a tuple (atmos, radiation, ...). If no drivers are needed +by a model, an empty tuple should be returned """ function get_drivers(model::AbstractModel) - return (nothing, nothing) + return () end """ diff --git a/src/standalone/Soil/Biogeochemistry/Biogeochemistry.jl b/src/standalone/Soil/Biogeochemistry/Biogeochemistry.jl index 0e3b2c00e6..161b962205 100644 --- a/src/standalone/Soil/Biogeochemistry/Biogeochemistry.jl +++ b/src/standalone/Soil/Biogeochemistry/Biogeochemistry.jl @@ -27,7 +27,6 @@ import ClimaLand: export SoilCO2ModelParameters, SoilCO2Model, PrescribedMet, - PrescribedSOC, MicrobeProduction, SoilCO2FluxBC, AtmosCO2StateBC, @@ -286,7 +285,7 @@ $(DocStringExtensions.FIELDS) struct SoilDrivers{ FT, MET <: AbstractSoilDriver, - SOC <: AbstractSoilDriver, + SOC <: PrescribedSoilOrganicCarbon{FT}, ATM <: PrescribedAtmosphere{FT}, } "Soil temperature and moisture drivers - Prescribed or Prognostic" @@ -358,25 +357,6 @@ function PrescribedMet{FT}( ) end -""" - PrescribedSOC <: AbstractSoilDriver - -A container which holds the prescribed function for soil organic carbon - -This is meant for use when running the biogeochemistry model without a soil -organic carbon model. - -$(DocStringExtensions.FIELDS) -""" -struct PrescribedSOC{FT, F <: Function} <: AbstractSoilDriver - "Carbon content of soil organic matter, of the form f(z::FT, t) where FT <: AbstractFloat" - soil_organic_carbon::F -end - -function PrescribedSOC{FT}(Csom) where {FT <: AbstractFloat} - return PrescribedSOC{FT, typeof(Csom)}(Csom) -end - """ soil_temperature(driver::PrescribedMet, p, Y, t, z) @@ -397,17 +377,6 @@ function soil_moisture(driver::PrescribedMet, p, Y, t, z) return driver.volumetric_liquid_fraction.(z, t) end -""" - soil_som_C(driver::PrescribedSOC, p, Y, t, z) - -Returns the carbon soil organic matter (SOM) at location (z) and time (t) for the prescribed -soil case. -""" -function soil_SOM_C(driver::PrescribedSOC, p, Y, t, z) - return driver.soil_organic_carbon.(z, t) -end - - """ make_update_aux(model::SoilCO2Model) @@ -422,7 +391,7 @@ function ClimaLand.make_update_aux(model::SoilCO2Model) z = model.domain.fields.z T_soil = soil_temperature(model.drivers.met, p, Y, t, z) θ_l = soil_moisture(model.drivers.met, p, Y, t, z) - Csom = soil_SOM_C(model.drivers.soc, p, Y, t, z) + Csom = p.drivers.soc P_sfc = p.drivers.P θ_w = θ_l ν = model.drivers.met.ν @@ -601,7 +570,7 @@ function ClimaLand.boundary_flux( end function ClimaLand.get_drivers(model::SoilCO2Model) - return (model.drivers.atmos, nothing) + return (model.drivers.atmos, model.drivers.soc) end Base.broadcastable(ps::SoilCO2ModelParameters) = tuple(ps) diff --git a/src/standalone/Soil/energy_hydrology.jl b/src/standalone/Soil/energy_hydrology.jl index c58328bbdb..b08699ca0c 100644 --- a/src/standalone/Soil/energy_hydrology.jl +++ b/src/standalone/Soil/energy_hydrology.jl @@ -880,6 +880,6 @@ function ClimaLand.get_drivers(model::EnergyHydrology) } return (bc.atmos, bc.radiation) else - return (nothing, nothing) + return () end end diff --git a/src/standalone/Soil/rre.jl b/src/standalone/Soil/rre.jl index aa890f17c6..795159a7d8 100644 --- a/src/standalone/Soil/rre.jl +++ b/src/standalone/Soil/rre.jl @@ -442,8 +442,8 @@ function ClimaLand.get_drivers(model::RichardsModel) <:PrescribedPrecipitation, <:AbstractRunoffModel, } - return (bc.precip, nothing) + return (bc.precip,) else - return (nothing, nothing) + return () end end diff --git a/test/integrated/soil_energy_hydrology_biogeochemistry.jl b/test/integrated/soil_energy_hydrology_biogeochemistry.jl index bb7431ef17..8ad122aba7 100644 --- a/test/integrated/soil_energy_hydrology_biogeochemistry.jl +++ b/test/integrated/soil_energy_hydrology_biogeochemistry.jl @@ -63,7 +63,9 @@ for FT in (Float32, Float64) ) # Make biogeochemistry model args - Csom = (z, t) -> eltype(z)(5.0) + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}( + TimeVaryingInput((t) -> 5), + ) co2_parameters = Soil.Biogeochemistry.SoilCO2ModelParameters(FT) C = FT(4) @@ -111,9 +113,11 @@ for FT in (Float32, Float64) ) @test model.soilco2.drivers.met.ν == model.soil.parameters.ν @test model.soilco2.drivers.met isa ClimaLand.PrognosticMet + drivers = ClimaLand.get_drivers(model) + @test drivers == (atmos, Csom) Y, p, coords = initialize(model) @test propertynames(p.drivers) == - (:P_liq, :P_snow, :T, :P, :u, :q, :c_co2, :thermal_state) + (:P_liq, :P_snow, :T, :P, :u, :q, :c_co2, :thermal_state, :soc) function init_soil!(Y, z, params) ν = params.ν FT = eltype(Y.soil.ϑ_l) @@ -156,17 +160,7 @@ for FT in (Float32, Float64) t0, z, ) - @test all( - parent( - Soil.Biogeochemistry.soil_SOM_C( - model.soilco2.drivers.soc, - p, - Y, - t0, - z, - ), - ) .== FT(5.0), - ) + @test all(parent(p.drivers.soc) .== FT(5.0)) @test p.soil.θ_l ≈ Soil.Biogeochemistry.soil_moisture( model.soilco2.drivers.met, p, diff --git a/test/shared_utilities/drivers.jl b/test/shared_utilities/drivers.jl index 4c7bcc4714..bc75662a67 100644 --- a/test/shared_utilities/drivers.jl +++ b/test/shared_utilities/drivers.jl @@ -27,9 +27,9 @@ FT = Float32 liquid_precip = TimeVaryingInput((t) -> -1.0) pp = ClimaLand.PrescribedPrecipitation{FT}(liquid_precip) coords = (; surface = [1, 2, 3]) - @test ClimaLand.initialize_drivers(pp, nothing, coords) == + @test ClimaLand.initialize_drivers((pp,), coords) == NamedTuple{(:P_liq,)}((zeros(FT, 3),)) - @test ClimaLand.initialize_drivers(nothing, nothing, coords) == (;) + @test ClimaLand.initialize_drivers((), coords) == (;) pa_keys = (:P_liq, :P_snow, :T, :P, :u, :q, :c_co2) zero_thermal_state = map( _ -> ClimaCore.RecursiveApply.rzero(Thermodynamics.PhaseEquil{FT}), @@ -38,13 +38,13 @@ FT = Float32 pa_vals = ([zeros(FT, 3) for k in pa_keys]...,) all_pa_keys = (pa_keys..., :thermal_state) all_pa_vals = (pa_vals..., zero_thermal_state) - @test ClimaLand.initialize_drivers(pa, nothing, coords) == + @test ClimaLand.initialize_drivers((pa,), coords) == NamedTuple{all_pa_keys}(all_pa_vals) pr_keys = (:SW_d, :LW_d, :θs) pr_vals = ([zeros(FT, 3) for k in pr_keys]...,) all_papr_keys = (pa_keys..., :thermal_state, pr_keys...) all_papr_vals = (pa_vals..., zero_thermal_state, pr_vals...) - @test ClimaLand.initialize_drivers(pa, pr, coords) == + @test ClimaLand.initialize_drivers((pa, pr), coords) == NamedTuple{all_papr_keys}(all_papr_vals) end @@ -111,13 +111,13 @@ end earth_param_set, ) pr = ClimaLand.PrescribedRadiativeFluxes(FT, f, f, f) - coords = (; surface = [1]) - p = (; drivers = ClimaLand.initialize_drivers(nothing, nothing, coords)) - nothing_update! = ClimaLand.make_update_drivers(nothing, nothing) + coords = (; surface = [1], subsurface = [1, 2]) + p = (; drivers = ClimaLand.initialize_drivers((), coords)) + nothing_update! = ClimaLand.make_update_drivers(()) nothing_update!(p, 0.0) @test p.drivers == (;) - p = (; drivers = ClimaLand.initialize_drivers(pa, nothing, coords)) - atmos_only_update! = ClimaLand.make_update_drivers(pa, nothing) + p = (; drivers = ClimaLand.initialize_drivers((pa,), coords)) + atmos_only_update! = ClimaLand.make_update_drivers((pa,)) atmos_only_update!(p, 0.0) @test p.drivers.P_liq == [FT(10)] @test p.drivers.P_snow == [FT(10)] @@ -127,8 +127,8 @@ end @test p.drivers.u == [FT(10)] @test p.drivers.c_co2 == [FT(4.2e-4)] - p = (; drivers = ClimaLand.initialize_drivers(pa, pr, coords)) - update! = ClimaLand.make_update_drivers(pa, pr) + p = (; drivers = ClimaLand.initialize_drivers((pa, pr), coords)) + update! = ClimaLand.make_update_drivers((pa, pr)) update!(p, 0.0) @test p.drivers.P_liq == [FT(10)] @test p.drivers.P_snow == [FT(10)] @@ -141,8 +141,8 @@ end @test p.drivers.LW_d == [FT(10)] @test p.drivers.θs == [FT(0)] - p = (; drivers = ClimaLand.initialize_drivers(nothing, pr, coords)) - rad_only_update! = ClimaLand.make_update_drivers(nothing, pr) + p = (; drivers = ClimaLand.initialize_drivers((pr,), coords)) + rad_only_update! = ClimaLand.make_update_drivers((pr,)) rad_only_update!(p, 0.0) @test p.drivers.SW_d == [FT(10)] @test p.drivers.LW_d == [FT(10)] @@ -150,8 +150,16 @@ end liquid_precip = TimeVaryingInput((t) -> -1.0) pp = ClimaLand.PrescribedPrecipitation{FT}(liquid_precip) - precip_update! = ClimaLand.make_update_drivers(pp, nothing) - p = (; drivers = ClimaLand.initialize_drivers(pp, nothing, coords)) + precip_update! = ClimaLand.make_update_drivers((pp,)) + p = (; drivers = ClimaLand.initialize_drivers((pp,), coords)) precip_update!(p, 0.0) @test p.drivers.P_liq == [FT(-1.0)] + + + soc = TimeVaryingInput((t) -> 1.0) + soc_driver = ClimaLand.PrescribedSoilOrganicCarbon{FT}(soc) + soc_update! = ClimaLand.make_update_drivers((soc_driver,)) + soc_p = (; drivers = ClimaLand.initialize_drivers((soc_driver,), coords)) + soc_update!(soc_p, 0.0) + @test soc_p.drivers.soc == [FT(1.0), FT(1.0)] end diff --git a/test/shared_utilities/variable_types.jl b/test/shared_utilities/variable_types.jl index fd7f9957aa..88ce05cea1 100644 --- a/test/shared_utilities/variable_types.jl +++ b/test/shared_utilities/variable_types.jl @@ -56,7 +56,7 @@ FT = Float32 @test isnothing(dm_update_aux!(x...)) @test x == [1, 2, 3] - @test ClimaLand.get_drivers(dm) == (nothing, nothing) + @test ClimaLand.get_drivers(dm) == () @test ClimaLand.add_drivers_to_cache((;), dm, nothing) == (;) end diff --git a/test/standalone/Bucket/snow_bucket_tests.jl b/test/standalone/Bucket/snow_bucket_tests.jl index 4d0d92e2a8..6ea2518eec 100644 --- a/test/standalone/Bucket/snow_bucket_tests.jl +++ b/test/standalone/Bucket/snow_bucket_tests.jl @@ -103,6 +103,8 @@ for FT in (Float32, Float64) atmosphere = bucket_atmos, radiation = bucket_rad, ) + drivers = ClimaLand.get_drivers(model) + @test drivers == (bucket_atmos, bucket_rad) _LH_f0 = LP.LH_f0(model.parameters.earth_param_set) _ρ_liq = LP.ρ_cloud_liq(model.parameters.earth_param_set) _ρLH_f0 = _ρ_liq * _LH_f0 # Latent heat per unit volume diff --git a/test/standalone/Snow/snow.jl b/test/standalone/Snow/snow.jl index 956db41b62..530ef19d99 100644 --- a/test/standalone/Snow/snow.jl +++ b/test/standalone/Snow/snow.jl @@ -49,6 +49,8 @@ import ClimaLand.Parameters as LP atmosphere = atmos, radiation = rad, ) + drivers = ClimaLand.get_drivers(model) + @test drivers == (atmos, rad) Y, p, coords = ClimaLand.initialize(model) @test (Y.snow |> propertynames) == (:S, :U) @test (p.snow |> propertynames) == ( diff --git a/test/standalone/Soil/Biogeochemistry/biogeochemistry_module.jl b/test/standalone/Soil/Biogeochemistry/biogeochemistry_module.jl index 6373c6a7c0..29bbc1ac6e 100644 --- a/test/standalone/Soil/Biogeochemistry/biogeochemistry_module.jl +++ b/test/standalone/Soil/Biogeochemistry/biogeochemistry_module.jl @@ -17,7 +17,9 @@ for FT in (Float32, Float64) T_soil = (z, t) -> eltype(z)(t) θ_l = (z, t) -> eltype(z)(0.3) θ_i = (z, t) -> eltype(z)(0) - Csom = (z, t) -> eltype(z)(5.0) # 3 [kg C m-3] soil organic C content at depth z + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}( + TimeVaryingInput((t) -> 5), + ) D_ref = FT(0.0) parameters = SoilCO2ModelParameters(FT; D_ref) @@ -59,11 +61,8 @@ for FT in (Float32, Float64) α = FT(0.1) n = FT(2) hcm = ClimaLand.Soil.vanGenuchten{FT}(; α = α, n = n) - soil_drivers = SoilDrivers( - PrescribedMet{FT}(T_soil, θ_l, ν, θ_r, hcm), - PrescribedSOC{FT}(Csom), - atmos, - ) + prescribed_met = PrescribedMet{FT}(T_soil, θ_l, ν, θ_r, hcm) + soil_drivers = SoilDrivers(prescribed_met, Csom, atmos) model = SoilCO2Model{FT}(; parameters = parameters, @@ -91,7 +90,9 @@ for FT in (Float32, Float64) T_soil = (z, t) -> eltype(z)(303) θ_l = (z, t) -> eltype(z)(0.3) θ_i = (z, t) -> eltype(z)(0.0) - Csom = (z, t) -> eltype(z)(5.0) # 3 [kg C m-3] soil organic C content at depth z + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}( + TimeVaryingInput((t) -> 5), + ) parameters = SoilCO2ModelParameters(FT) C = FT(4) @@ -135,7 +136,7 @@ for FT in (Float32, Float64) hcm = ClimaLand.Soil.vanGenuchten{FT}(; α = α, n = n) soil_drivers = SoilDrivers( PrescribedMet{FT}(T_soil, θ_l, ν, θ_r, hcm), - PrescribedSOC{FT}(Csom), + Csom, atmos, # need to create some functions ) diff --git a/test/standalone/Soil/climate_drivers.jl b/test/standalone/Soil/climate_drivers.jl index 124669791f..4a17a82ecc 100644 --- a/test/standalone/Soil/climate_drivers.jl +++ b/test/standalone/Soil/climate_drivers.jl @@ -113,7 +113,8 @@ for FT in (Float32, Float64) boundary_conditions = boundary_fluxes, sources = (), ) - + drivers = ClimaLand.get_drivers(model) + @test drivers == (atmos, radiation) Y, p, coords = initialize(model) Δz_top = model.domain.fields.Δz_top @test propertynames(p.drivers) == ( diff --git a/test/standalone/Soil/soiltest.jl b/test/standalone/Soil/soiltest.jl index 5641980f86..f84b38c1df 100644 --- a/test/standalone/Soil/soiltest.jl +++ b/test/standalone/Soil/soiltest.jl @@ -43,7 +43,8 @@ for FT in (Float32, Float64) boundary_conditions = boundary_fluxes, sources = sources, ) - + drivers = ClimaLand.get_drivers(soil) + @test drivers == () Y, p, coords = initialize(soil) # Here we do not add a dss buffer: check that `initialize` # has not added a buffer to `p` and that it only contains the diff --git a/test/standalone/Vegetation/canopy_model.jl b/test/standalone/Vegetation/canopy_model.jl index ceb3aa1d17..8f7bc84c1b 100644 --- a/test/standalone/Vegetation/canopy_model.jl +++ b/test/standalone/Vegetation/canopy_model.jl @@ -188,6 +188,8 @@ for FT in (Float32, Float64) atmos = atmos, radiation = radiation, ) + drivers = ClimaLand.get_drivers(canopy) + @test drivers == (atmos, radiation) Y, p, coords = ClimaLand.initialize(canopy) @test propertynames(p.drivers) == ( :P_liq,