From 21ee803a78cb4b40a7eb04dfb1a47eec288df480 Mon Sep 17 00:00:00 2001 From: kmdeck Date: Sun, 7 Jul 2024 14:54:35 -0700 Subject: [PATCH] generalize_drivers --- .buildkite/Manifest.toml | 42 +++--- .buildkite/target/pipeline.yml | 5 + NEWS.md | 1 + docs/src/APIs/SoilBiogeochemistry.md | 1 - docs/src/APIs/shared_utilities.md | 1 + .../integrated/soil_canopy_tutorial.jl | 5 +- .../standalone/Bucket/bucket_tutorial.jl | 3 +- .../standalone/Canopy/canopy_tutorial.jl | 3 +- docs/tutorials/standalone/Soil/evaporation.jl | 3 +- .../Soil/evaporation_gilat_loess.jl | 6 +- docs/tutorials/standalone/Soil/sublimation.jl | 3 +- experiments/benchmarks/bucket.jl | 3 +- experiments/benchmarks/land.jl | 5 +- experiments/benchmarks/richards.jl | 3 +- experiments/integrated/fluxnet/ozark_pft.jl | 6 +- experiments/integrated/fluxnet/run_fluxnet.jl | 6 +- .../integrated/global/global_soil_canopy.jl | 5 +- .../conservation/ozark_conservation.jl | 3 +- .../conservation/ozark_conservation_setup.jl | 2 +- .../performance/profile_allocations.jl | 11 +- .../standalone/Biogeochemistry/experiment.jl | 5 +- .../Bucket/global_bucket_staticmap.jl | 3 +- .../Bucket/global_bucket_temporalmap.jl | 3 +- experiments/standalone/Snow/snow_cdp.jl | 3 +- .../standalone/Soil/richards_runoff.jl | 3 +- .../standalone/Vegetation/no_vegetation.jl | 3 +- .../standalone/Vegetation/varying_lai.jl | 3 +- .../Vegetation/varying_lai_with_stem.jl | 3 +- .../src/Fluxnet/run_fluxnet.jl | 2 +- src/integrated/soil_canopy_model.jl | 6 +- .../soil_energy_hydrology_biogeochemistry.jl | 4 +- src/shared_utilities/drivers.jl | 129 ++++++++++++------ src/shared_utilities/models.jl | 19 +-- .../Soil/Biogeochemistry/Biogeochemistry.jl | 38 +----- src/standalone/Soil/energy_hydrology.jl | 2 +- src/standalone/Soil/rre.jl | 4 +- .../soil_energy_hydrology_biogeochemistry.jl | 20 +-- test/shared_utilities/drivers.jl | 38 ++++-- test/shared_utilities/variable_types.jl | 2 +- test/standalone/Bucket/snow_bucket_tests.jl | 2 + test/standalone/Snow/snow.jl | 2 + .../Biogeochemistry/biogeochemistry_module.jl | 17 +-- test/standalone/Soil/climate_drivers.jl | 3 +- test/standalone/Soil/soiltest.jl | 3 +- test/standalone/Vegetation/canopy_model.jl | 2 + 45 files changed, 245 insertions(+), 191 deletions(-) diff --git a/.buildkite/Manifest.toml b/.buildkite/Manifest.toml index bda9cd2849..0ffdeb970a 100644 --- a/.buildkite/Manifest.toml +++ b/.buildkite/Manifest.toml @@ -124,9 +124,9 @@ version = "7.12.0" [[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "8556500c18fcad8b4c44058e23fbc4a36143f6be" +git-tree-sha1 = "ce2ca959f932f5dad70697dd93133d1167cf1e4e" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.1" +version = "1.10.2" weakdeps = ["SparseArrays"] [deps.ArrayLayouts.extensions] @@ -277,9 +277,9 @@ version = "0.10.14" [[deps.CUDA]] deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CUDA_Driver_jll", "CUDA_Runtime_Discovery", "CUDA_Runtime_jll", "Crayons", "DataFrames", "ExprTools", "GPUArrays", "GPUCompiler", "KernelAbstractions", "LLVM", "LLVMLoopInfo", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "NVTX", "Preferences", "PrettyTables", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "StaticArrays", "Statistics"] -git-tree-sha1 = "6e945e876652f2003e6ca74e19a3c45017d3e9f6" +git-tree-sha1 = "fdd9dfb67dfefd548f51000cc400bb51003de247" uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" -version = "5.4.2" +version = "5.4.3" weakdeps = ["ChainRulesCore", "EnzymeCore", "SpecialFunctions"] [deps.CUDA.extensions] @@ -625,9 +625,9 @@ version = "0.1.2" [[deps.DelaunayTriangulation]] deps = ["EnumX", "ExactPredicates", "Random"] -git-tree-sha1 = "b0cb128d2e100646573e1da8565b02491fddb5ef" +git-tree-sha1 = "078c716cbb032242df18b960e8b1fec6b1b0b9f9" uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" -version = "1.0.4" +version = "1.0.5" [[deps.DelimitedFiles]] deps = ["Mmap"] @@ -1066,10 +1066,10 @@ uuid = "46192b85-c4d5-4398-a991-12ede77f4527" version = "0.1.6" [[deps.GPUCompiler]] -deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "Scratch", "TimerOutputs", "UUIDs"] -git-tree-sha1 = "518ebd058c9895de468a8c255797b0c53fdb44dd" +deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "Preferences", "Scratch", "Serialization", "TOML", "TimerOutputs", "UUIDs"] +git-tree-sha1 = "ab29216184312f99ff957b32cd63c2fe9c928b91" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" -version = "0.26.5" +version = "0.26.7" [[deps.GR]] deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] @@ -1279,9 +1279,9 @@ weakdeps = ["ClimaParams"] [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "be50fe8df3acbffa0274a744f1a99d29c45a57f4" +git-tree-sha1 = "14eb2b542e748570b56446f4c50fbfb2306ebc45" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.1.0+0" +version = "2024.2.0+0" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -1440,9 +1440,9 @@ version = "3.0.0+1" [[deps.LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Preferences", "Printf", "Requires", "Unicode"] -git-tree-sha1 = "389aea28d882a40b5e1747069af71bdbd47a1cae" +git-tree-sha1 = "020abd49586480c1be84f57da0017b5d3db73f7c" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "7.2.1" +version = "8.0.0" weakdeps = ["BFloat16s"] [deps.LLVM.extensions] @@ -1450,9 +1450,9 @@ weakdeps = ["BFloat16s"] [[deps.LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl", "TOML"] -git-tree-sha1 = "88b916503aac4fb7f701bb625cd84ca5dd1677bc" +git-tree-sha1 = "c2636c264861edc6d305e6b4d528f09566d24c5e" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.29+0" +version = "0.0.30+0" [[deps.LLVMLoopInfo]] git-tree-sha1 = "2e5c102cfc41f48ae4740c7eca7743cc7e7b75ea" @@ -1700,9 +1700,9 @@ weakdeps = ["ChainRulesCore", "ForwardDiff", "SpecialFunctions"] [[deps.MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "80b2833b56d466b3858d565adcd16a4a05f2089b" +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.1.0+0" +version = "2024.2.0+0" [[deps.MLStyle]] git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" @@ -2332,9 +2332,9 @@ version = "0.6.12" [[deps.RecursiveArrayTools]] deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "SparseArrays", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "76a35763102e16c97d7302f5dc67837d24802035" +git-tree-sha1 = "b450d967a770fb13d0e26358f58375e20361cf9c" uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" -version = "3.25.0" +version = "3.26.0" [deps.RecursiveArrayTools.extensions] RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" @@ -2477,9 +2477,9 @@ version = "1.2.1" [[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "6bb314cb1aacfa37ef58e5a0ccf4a1ec0311f495" +git-tree-sha1 = "ff11acffdb082493657550959d4feb4b6149e73a" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.4" +version = "1.4.5" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" diff --git a/.buildkite/target/pipeline.yml b/.buildkite/target/pipeline.yml index 6d284d9504..72db3c955d 100644 --- a/.buildkite/target/pipeline.yml +++ b/.buildkite/target/pipeline.yml @@ -13,6 +13,11 @@ steps: key: "init_env" command: - "echo $$JULIA_DEPOT_PATH" + + - echo "--- Instantiate project" + - "julia --project -e 'using Pkg; Pkg.instantiate(;verbose=true)'" + - "julia --project -e 'using Pkg; Pkg.status()'" + - echo "--- Instantiate experiments" - "julia --project=.buildkite -e 'using Pkg; Pkg.develop(;path=\".\"); Pkg.instantiate(;verbose=true)'" - "julia --project=.buildkite -e 'using Pkg; Pkg.status()'" diff --git a/NEWS.md b/NEWS.md index 3d3f7003bd..c7119cffb1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ ClimaLand.jl Release Notes main -------- - PR[#690] Use the soil parameters in creating the biogeochemistry SoilMet driver for consistency. +- PR[#692] Generalize our forcing ``drivers" to include prescribed soil organic carbon v0.13.0 -------- 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..48355a029f 100644 --- a/experiments/benchmarks/land.jl +++ b/experiments/benchmarks/land.jl @@ -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..38d9bb06a7 100644 --- a/experiments/benchmarks/richards.jl +++ b/experiments/benchmarks/richards.jl @@ -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/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_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 a5d0ddd907..6a0837a5df 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..99af758ff6 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 diff --git a/src/integrated/soil_canopy_model.jl b/src/integrated/soil_canopy_model.jl index 04577975d6..f8dc8cfa79 100644 --- a/src/integrated/soil_canopy_model.jl +++ b/src/integrated/soil_canopy_model.jl @@ -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.driver.soc, + ) end diff --git a/src/integrated/soil_energy_hydrology_biogeochemistry.jl b/src/integrated/soil_energy_hydrology_biogeochemistry.jl index e25d351f6c..1f50aee9fd 100644 --- a/src/integrated/soil_energy_hydrology_biogeochemistry.jl +++ b/src/integrated/soil_energy_hydrology_biogeochemistry.jl @@ -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.driver.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..5a0c80e780 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,41 @@ 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 + +""" + 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 +783,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"). """ -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 +899,15 @@ function make_update_drivers(r::PrescribedRadiativeFluxes{FT}) where {FT} end return update_drivers! end + +""" + make_update_drivers(a::PrescribedSoilOrganicCarbon{FT}) where {FT} +Creates and returns a function which updates the driver variables +in the case of a PrescribedSoilOrganicCarbon. +""" +function make_update_drivers(a::PrescribedSoilOrganicCarbon{FT}) where {FT} + function update_drivers!(p, t) + evaluate!(p.drivers.soc, a.soc, t) + end + return update_drivers! +end diff --git a/src/shared_utilities/models.jl b/src/shared_utilities/models.jl index b45bfa97a4..4aa1830acc 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,10 @@ 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, ...). """ 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..eb719c07a7 100644 --- a/src/standalone/Soil/Biogeochemistry/Biogeochemistry.jl +++ b/src/standalone/Soil/Biogeochemistry/Biogeochemistry.jl @@ -27,7 +27,7 @@ import ClimaLand: export SoilCO2ModelParameters, SoilCO2Model, PrescribedMet, - PrescribedSOC, + PrescribedSoilOrganicCarbon, MicrobeProduction, SoilCO2FluxBC, AtmosCO2StateBC, @@ -286,7 +286,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 +358,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 +378,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 +392,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 +571,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,