From a7bae2211318d3d083949e96fdab1530de1a6a71 Mon Sep 17 00:00:00 2001 From: "Alexandre A. Renchon" Date: Thu, 19 Dec 2024 11:41:38 -0800 Subject: [PATCH 1/4] first commit --- experiments/yinon_script/code.jl | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 experiments/yinon_script/code.jl diff --git a/experiments/yinon_script/code.jl b/experiments/yinon_script/code.jl new file mode 100644 index 0000000000..10a374ff48 --- /dev/null +++ b/experiments/yinon_script/code.jl @@ -0,0 +1,67 @@ +# parameters: mu and sigma (mean and std distributions) +# auxiliary: k is an array of decay rate (constant) +# state variable: u (array of C with different decay rate) +# input: input of carbon per area per unit time +# we prescribe input for now, but it should be given by another +# module prognostically, e.g., litter fall etc. + +using QuadGK +using DifferentialEquations +using StaticArrays +using Distributions +using DataFrames +using CSV +using BenchmarkTools +# integral, error = quadgk(x -> cos(200x), 0, 1) + +# function dc_kt_dt(u, p, t)#, fixed=true) +# k,mu,sigma,input = p +# if fixed==true +# return SA[input*pdf(LogNormal(mu,sigma),k) - k*u] +# else +# ind = min(int(t+1),length(input)) +# return SA[input[ind]*pdf(LogNormal(mu,sigma),k) - k*u] +# end + + +# end + +function dc_kt_dt(u, p, t)#, fixed=true) + k,mu,sigma,input,fixed = p + if fixed== true + SA[input.*pdf(LogNormal(mu,sigma),k) .- k.*u[1]] + else + ind = min(floor(Int,t+1),length(input)) + return SA[input[ind]*pdf(LogNormal(mu,sigma),k) - k*u[1]] + end + + # SA[p[4].*pdf(LogNormal(p[2],p[3]),p[1]) .- p[1].*u[1]] +end +# prob = ODEProblem(dc_kt_dt, SA[0.], (0.,100.),[0.1,1.,2.,0.25,true]) +# solve(prob) + + +function solve_ode(k,tau,age,input,fixed=true) + sigma = sqrt(log(age/tau)) + mu = - log(sqrt(tau^3/age)); + ps = (k,mu,sigma,input,fixed) + tspan = (0.,100.) + u0 = SA[0.] + prob = ODEProblem(dc_kt_dt, u0,tspan , ps) + solve(prob,saveat=1) +end +function run_diskin(tau,age,input,fixed=true,discrete=false) + if discrete==true + krange = exp.(range(-10,stop=10,length=100000)); + dk = diff(vcat(0,krange)); + res = sum(reduce(hcat,[solve_ode(k,tau,age,input,fixed).u.*d for (k,d) in zip(krange,dk)]),dims=2); + else + res, error = quadgk(x -> solve_ode(x,tau,age,input,fixed), 0, Inf) + end + res +end +NPP = Array(CSV.read("experiments/yinon_script/test_NPP.csv",DataFrame)); + +cont = run_diskin(17,1000,0.25,true,false); +@btime noncont = run_diskin(17,1000,0.25,true,true); +cont'./noncont From 2a5decf7b540a2102d34a7a97ce990edbc3e752f Mon Sep 17 00:00:00 2001 From: yinonbaron Date: Thu, 19 Dec 2024 12:40:53 -0800 Subject: [PATCH 2/4] WIP --- .../Biogeochemistry/disordered_kinetics.jl | 0 .../Biogeochemistry/DisorderedKinetics.jl | 175 ++++++++++++++++++ src/standalone/Soil/Soil.jl | 2 + 3 files changed, 177 insertions(+) create mode 100644 experiments/standalone/Biogeochemistry/disordered_kinetics.jl create mode 100644 src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl diff --git a/experiments/standalone/Biogeochemistry/disordered_kinetics.jl b/experiments/standalone/Biogeochemistry/disordered_kinetics.jl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl b/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl new file mode 100644 index 0000000000..f4caed716b --- /dev/null +++ b/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl @@ -0,0 +1,175 @@ +module DisorderedKinetics +using Distributions +using ClimaLand +using DocStringExtensions +using ClimaCore +import ...Parameters as LP +import ClimaCore: Fields, Operators, Geometry, Spaces + +import ClimaLand.Domains: AbstractDomain +import ClimaLand: + AbstractExpModel, + make_update_aux, + make_compute_exp_tendency, + make_update_boundary_fluxes, + prognostic_vars, + auxiliary_vars, + name, + prognostic_types, + auxiliary_types, + prognostic_domain_names, + auxiliary_domain_names, + TopBoundary, + BottomBoundary, + AbstractBC, + boundary_flux!, + AbstractSource, + source! + +""" +DisorderedKineticsModelParameters{FT <: AbstractFloat, PSE} + +A struct for storing parameters of the `DisorderedKineticsModel`. + +All of these parameters are currently treated as global constants. +$(DocStringExtensions.FIELDS) +""" +Base.@kwdef struct DisorderedKineticsModelParameters{FT <: AbstractFloat} + "the mean of the log-normal distribution of the soil C decay rate []" + mu::FT + "the standard deviation of the log-normal distribution of the soil C decay rate []" + sigma::FT +end + + +""" +AbstractSoilCarbonModel{FT} <: ClimaLand.AbstractExpModel{FT} + +An abstract model type for soil biogeochemistry models. +""" +abstract type AbstractSoilCarbonModel{FT} <: + ClimaLand.AbstractExpModel{FT} end + +""" +DisorderedKineticsModel + +A model for simulating soil organic carbon dynamics using a disordered kinetics model. + +$(DocStringExtensions.FIELDS) +""" +struct DisorderedKineticsModel{FT, D, BC, S} <: + AbstractSoilCarbonModel{FT} +"the parameter set" +parameters::PS +"the number of soil carbon pools for discretization" +npools::Int64 +"the soil domain, using ClimaCore.Domains" +domain::D +"the boundary conditions, of type NamedTuple" +boundary_conditions::BC +"A tuple of sources, each of type AbstractSource" +sources::S +end + + +""" +DisorderedKineticsModel{FT}(; + parameters::DisorderedKineticsModelParameters{FT}, + npools::Int64, + domain::ClimaLand.AbstractDomain, + boundary_conditions::NamedTuple, + sources::Tuple, + ) where {FT, BC} + +A constructor for `DisorderedKineticsModel`. +""" +function DisorderedKineticsModel{FT}(; + parameters::DisorderedKineticsModelParameters{FT}, + npools::Int64, + domain::ClimaLand.AbstractDomain, + boundary_conditions::BC, + sources::Tuple, +) where {FT, BC} + args = (parameters, npools, domain, boundary_conditions, sources) + DisorderedKineticsModel{FT, typeof.(args)...}(args...) +end + +ClimaLand.name(model::DisorderedKineticsModel) = :soilC +ClimaLand.prognostic_vars(::DisorderedKineticsModel) = (:C,) +ClimaLand.prognostic_types(::DisorderedKineticsModel{FT}) where {FT} = (NTuple{model.npools, FT},) +ClimaLand.prognostic_domain_names(::DisorderedKineticsModel) = (:surface,) + +ClimaLand.auxiliary_vars(model::DisorderedKineticsModel) = (:ks,) +ClimaLand.auxiliary_types(model::DisorderedKineticsModel{FT}) where {FT} = (NTuple{model.npools, FT},) +ClimaLand.auxiliary_domain_names(model::DisorderedKineticsModel) = (:surface) + +""" + make_compute_exp_tendency(model::DisorderedKineticsModel) + +An extension of the function `make_compute_exp_tendency`, for the disordered kinetics model. +This function creates and returns a function which computes the entire +right hand side of the ODE for `C`, and updates `dY.soilC.C` in place +with that value. These quantities will be stepped explicitly. + +This has been written so as to work with Differential Equations.jl. +""" +function ClimaLand.make_compute_exp_tendency(model::DisorderedKineticsModel) + function compute_exp_tendency!(dY, Y, p, t) + + @. dY.soilC.C = -p.ks * Y.soilC.C + + # Source terms are added in here + for src in model.sources + source!(dY, src, Y, p, model.parameters) + end + end + return compute_exp_tendency! +end + +""" + AbstractCarbonSource{FT} <: ClimaLand.AbstractSource{FT} + +An abstract type for soil CO2 sources. There are two sources: +roots and microbes, in struct RootProduction and MicrobeProduction. +""" +abstract type AbstractSoilCarbonSource{FT} <: ClimaLand.AbstractSource{FT} end + +""" + ClimaLand.source!(dY::ClimaCore.Fields.FieldVector, + src::AbstractSoilCarbonSource, + Y::ClimaCore.Fields.FieldVector, + p::NamedTuple, + params) + +A method which extends the ClimaLand source! function for the +case of microbe production of CO2 in soil. +""" +function ClimaLand.source!( + dY::ClimaCore.Fields.FieldVector, + src::AbstractSoilCarbonSource, + Y::ClimaCore.Fields.FieldVector, + p::NamedTuple, + params, +) + @. dY.soilco2.C += src * pdf(LogNormal(params.mu, params.sigma), p.ks) +end + + +""" + make_update_aux(model::DisorderedKineticsModel) + +An extension of the function `make_update_aux`, for the disordered kinetics model. +This function creates and returns a function which updates the auxiliary +variables `p.soilC.variable` in place. +This has been written so as to work with Differential Equations.jl. + +This function is empty because the auxiliary variable `ks` is not updated in time. +""" +function ClimaLand.make_update_aux(model::DisorderedKineticsModel) + function update_aux!(p, Y, t) end + return update_aux! +end + +Base.broadcastable(ps::DisorderedKineticsModelParameters) = tuple(ps) + +end \ No newline at end of file diff --git a/src/standalone/Soil/Soil.jl b/src/standalone/Soil/Soil.jl index 19f7a9a5f7..9daede9706 100644 --- a/src/standalone/Soil/Soil.jl +++ b/src/standalone/Soil/Soil.jl @@ -184,4 +184,6 @@ include("./boundary_conditions.jl") include("./soil_hydrology_parameterizations.jl") include("Biogeochemistry/Biogeochemistry.jl") using .Biogeochemistry +include("Biogeochemistry/DisorderedKinetics.jl") +using .DisorderedKinetics end From a4dd32b17f389d3f23ffd613ee73d9577413709f Mon Sep 17 00:00:00 2001 From: yinonbaron Date: Fri, 20 Dec 2024 15:49:49 -0800 Subject: [PATCH 3/4] start writing a test for the disordered kinetics --- .../Biogeochemistry/disordered_kinetics.jl | 86 +++++++++++++++++++ src/shared_utilities/drivers.jl | 18 ++++ .../Biogeochemistry/DisorderedKinetics.jl | 80 +++++++++++------ 3 files changed, 160 insertions(+), 24 deletions(-) diff --git a/experiments/standalone/Biogeochemistry/disordered_kinetics.jl b/experiments/standalone/Biogeochemistry/disordered_kinetics.jl index e69de29bb2..9a9ee98866 100644 --- a/experiments/standalone/Biogeochemistry/disordered_kinetics.jl +++ b/experiments/standalone/Biogeochemistry/disordered_kinetics.jl @@ -0,0 +1,86 @@ +import SciMLBase +import ClimaComms +ClimaComms.@import_required_backends +import ClimaTimeSteppers as CTS +using ClimaCore +using ClimaLand +using ClimaLand.Domains: Column +using ClimaLand.Soil +using ClimaLand.Soil.DisorderedKinetics +using Dates + +import ClimaLand.Parameters as LP + +# Define simulation times +t0 = Float64(0); +tf = Float64(10000); +dt = Float64(10); + +FT = Float64; + +model_params = DisorderedKineticsModelParameters{FT}( + mu = FT(-1.0), + sigma = FT(2.0)); +zmax = FT(0); +zmin = FT(-1); +nelems = 1; +# we use a vertical column with one layer to in the future interface with other variables like temperature and moisture that are depth dependent +lsm_domain = Column(; zlim = (zmin, zmax), nelements = nelems); + +# Make biogeochemistry model args +NPP = PrescribedSOCInputs{FT}(TimeVaryingInput((t) -> 500)); +sources = (AbstractSoilCarbonSource{FT},); + +model = DisorderedKineticsModel{FT}(; + parameters=model_params, + npools=1000, + domain=lsm_domain, + sources=sources, + drivers=NPP +); + +Y, p, coords = initialize(model); +set_initial_cache! = make_set_initial_cache(model); + +function init_model!(Y, p, model) + function calc_k(mu::FT, sigma::FT) where {FT} + C = FT(0.0) + return FT(C) + end + p.soilC.ks .= collect(exp.(range(model.parameters.mu-8*model.parameters.sigma,model.parameters.mu+8*model.parameters.sigma,length=model.npools))); +end + + +init_model!(Y, p, model); + +set_initial_cache!(p, Y, t0); +disordered_kinetics_exp_tendency! = make_exp_tendency(model); + +timestepper = CTS.RK4() +ode_algo = CTS.ExplicitAlgorithm(timestepper) + +saveat = collect(t0:FT(10 * dt):tf) +sv = (; + t = Array{Float64}(undef, length(saveat)), + saveval = Array{NamedTuple}(undef, length(saveat)), +) +saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) +# updateat = deepcopy(saveat) +# drivers = ClimaLand.get_drivers(model) +# updatefunc = ClimaLand.make_update_drivers(drivers) +# driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc) +# cb = SciMLBase.CallbackSet(driver_cb, saving_cb) + +prob = SciMLBase.ODEProblem( + CTS.ClimaODEFunction(T_exp! = Soil_bio_exp_tendency!), + Y, + (t0, tf), + p, +) +# sol = SciMLBase.solve(prob, ode_algo; dt = dt, callback = cb) +sol = SciMLBase.solve(prob, ode_algo; dt = dt, callback = saving_cb) + +# Check that simulation still has correct float type +@assert eltype(sol.u[end].soil) == FT +@assert eltype(sol.u[end].soilco2) == FT + diff --git a/src/shared_utilities/drivers.jl b/src/shared_utilities/drivers.jl index feed5fce62..c59de1c8b6 100644 --- a/src/shared_utilities/drivers.jl +++ b/src/shared_utilities/drivers.jl @@ -18,6 +18,7 @@ export AbstractAtmosphericDrivers, PrescribedAtmosphere, PrescribedPrecipitation, PrescribedSoilOrganicCarbon, + PrescribedSOCInputs, CoupledAtmosphere, PrescribedRadiativeFluxes, CoupledRadiativeFluxes, @@ -95,6 +96,23 @@ end PrescribedSoilOrganicCarbon{FT}(soc) where {FT} = PrescribedSoilOrganicCarbon{FT, typeof(soc)}(soc) + +""" + PrescribedSOCInputs{FT} + +A type for prescribing soil organic carbon inputs into our disordered kinetics model. +$(DocStringExtensions.FIELDS) +""" +struct PrescribedSOCInputs{FT, SOC_input <: AbstractTimeVaryingInput} <: + AbstractClimaLandDrivers{FT} + "Soil organic carbon input, function of time and space: kg C/m^2/yr" + soc_input::SOC_input +end + +PrescribedSOCInputs{FT}(soc_input) where {FT} = + PrescribedSoilOrganicCarbon{FT, typeof(soc_input)}(soc_input) + + """ PrescribedAtmosphere{FT, CA, DT} <: AbstractAtmosphericDrivers{FT} diff --git a/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl b/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl index f4caed716b..0a77f8439d 100644 --- a/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl +++ b/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl @@ -26,6 +26,13 @@ import ClimaLand: AbstractSource, source! +export DisorderedKineticsModelParameters, +DisorderedKineticsModel, + AbstractSoilCarbonSource, + AbstractSoilDriver, + DisorderedKineticsDrivers + + """ DisorderedKineticsModelParameters{FT <: AbstractFloat, PSE} @@ -57,18 +64,18 @@ A model for simulating soil organic carbon dynamics using a disordered kinetics $(DocStringExtensions.FIELDS) """ -struct DisorderedKineticsModel{FT, D, BC, S} <: +struct DisorderedKineticsModel{FT, PS, N, D, S, DT} <: AbstractSoilCarbonModel{FT} -"the parameter set" -parameters::PS -"the number of soil carbon pools for discretization" -npools::Int64 -"the soil domain, using ClimaCore.Domains" -domain::D -"the boundary conditions, of type NamedTuple" -boundary_conditions::BC -"A tuple of sources, each of type AbstractSource" -sources::S + "the parameter set" + parameters::PS + "the number of soil carbon pools for discretization" + npools::N + "the soil domain, using ClimaCore.Domains" + domain::D + "A tuple of sources, each of type AbstractSource" + sources::S + "Drivers" + drivers::DT end @@ -77,8 +84,8 @@ DisorderedKineticsModel{FT}(; parameters::DisorderedKineticsModelParameters{FT}, npools::Int64, domain::ClimaLand.AbstractDomain, - boundary_conditions::NamedTuple, sources::Tuple, + drivers::DT ) where {FT, BC} A constructor for `DisorderedKineticsModel`. @@ -87,21 +94,23 @@ function DisorderedKineticsModel{FT}(; parameters::DisorderedKineticsModelParameters{FT}, npools::Int64, domain::ClimaLand.AbstractDomain, - boundary_conditions::BC, sources::Tuple, -) where {FT, BC} - args = (parameters, npools, domain, boundary_conditions, sources) + drivers::DT, +) where {FT, DT} + args = (parameters, npools, domain, sources, drivers) DisorderedKineticsModel{FT, typeof.(args)...}(args...) end ClimaLand.name(model::DisorderedKineticsModel) = :soilC ClimaLand.prognostic_vars(::DisorderedKineticsModel) = (:C,) -ClimaLand.prognostic_types(::DisorderedKineticsModel{FT}) where {FT} = (NTuple{model.npools, FT},) -ClimaLand.prognostic_domain_names(::DisorderedKineticsModel) = (:surface,) +ClimaLand.prognostic_types(model::DisorderedKineticsModel{FT}) where {FT} = (NTuple{model.npools, FT},) +# we use a vertical column with one layer to in the future interface with other variables like temperature and moisture that are depth dependent +ClimaLand.prognostic_domain_names(::DisorderedKineticsModel) = (:subsurface,) -ClimaLand.auxiliary_vars(model::DisorderedKineticsModel) = (:ks,) -ClimaLand.auxiliary_types(model::DisorderedKineticsModel{FT}) where {FT} = (NTuple{model.npools, FT},) -ClimaLand.auxiliary_domain_names(model::DisorderedKineticsModel) = (:surface) +ClimaLand.auxiliary_vars(model::DisorderedKineticsModel) = (:ks,:inputs) +ClimaLand.auxiliary_types(model::DisorderedKineticsModel{FT}) where {FT} = (NTuple{model.npools, FT}, FT) +# we use a vertical column with one layer to in the future interface with other variables like temperature and moisture that are depth dependent +ClimaLand.auxiliary_domain_names(model::DisorderedKineticsModel) = (:subsurface, :subsurface) """ make_compute_exp_tendency(model::DisorderedKineticsModel) @@ -116,7 +125,7 @@ This has been written so as to work with Differential Equations.jl. function ClimaLand.make_compute_exp_tendency(model::DisorderedKineticsModel) function compute_exp_tendency!(dY, Y, p, t) - @. dY.soilC.C = -p.ks * Y.soilC.C + @. dY.soilC.C = - p.ks * Y.soilC.C # Source terms are added in here for src in model.sources @@ -129,7 +138,7 @@ end """ AbstractCarbonSource{FT} <: ClimaLand.AbstractSource{FT} -An abstract type for soil CO2 sources. There are two sources: +An abstract type for soil carbon sources. There are two sources: roots and microbes, in struct RootProduction and MicrobeProduction. """ abstract type AbstractSoilCarbonSource{FT} <: ClimaLand.AbstractSource{FT} end @@ -151,10 +160,31 @@ function ClimaLand.source!( p::NamedTuple, params, ) - @. dY.soilco2.C += src * pdf(LogNormal(params.mu, params.sigma), p.ks) + @. dY.soilco2.C += p.inputs * pdf(LogNormal(params.mu, params.sigma), p.ks) +end + + + + +""" + SoilDrivers + +A container which passes in the soil drivers to the biogeochemistry +model. These drivers are either of type Prescribed (for standalone mode) +or Prognostic (for running with a prognostic model for soil temp and moisture). + +$(DocStringExtensions.FIELDS) +""" +struct DisorderedKineticsDrivers{ + FT, + SOC_input <: PrescribedSOCInputs{FT}, +} + "Soil SOM driver - Prescribed only" + soc_inputs::SOC_input end + """ make_update_aux(model::DisorderedKineticsModel) @@ -166,7 +196,9 @@ This has been written so as to work with Differential Equations.jl. This function is empty because the auxiliary variable `ks` is not updated in time. """ function ClimaLand.make_update_aux(model::DisorderedKineticsModel) - function update_aux!(p, Y, t) end + function update_aux!(p, Y, t) + p.inputs = model.drivers.soc.func(t) + end return update_aux! end From 51cbfd5c0106fe7e4c98c51573d64408d9f5efc5 Mon Sep 17 00:00:00 2001 From: yinonbaron Date: Fri, 20 Dec 2024 20:42:16 -0800 Subject: [PATCH 4/4] finished writing the testing for the disordered kinetics mode --- .../Biogeochemistry/disordered_kinetics.jl | 40 ++++++++++--------- .../Biogeochemistry/DisorderedKinetics.jl | 34 ++++++++++------ 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/experiments/standalone/Biogeochemistry/disordered_kinetics.jl b/experiments/standalone/Biogeochemistry/disordered_kinetics.jl index 9a9ee98866..df6d7685be 100644 --- a/experiments/standalone/Biogeochemistry/disordered_kinetics.jl +++ b/experiments/standalone/Biogeochemistry/disordered_kinetics.jl @@ -13,8 +13,8 @@ import ClimaLand.Parameters as LP # Define simulation times t0 = Float64(0); -tf = Float64(10000); -dt = Float64(10); +tf = Float64(100); +dt = Float64(0.01); FT = Float64; @@ -29,25 +29,29 @@ lsm_domain = Column(; zlim = (zmin, zmax), nelements = nelems); # Make biogeochemistry model args NPP = PrescribedSOCInputs{FT}(TimeVaryingInput((t) -> 500)); -sources = (AbstractSoilCarbonSource{FT},); +model_sources = (LitterInput{FT}(),); model = DisorderedKineticsModel{FT}(; parameters=model_params, - npools=1000, + npools=100, domain=lsm_domain, - sources=sources, + sources=model_sources, drivers=NPP ); Y, p, coords = initialize(model); set_initial_cache! = make_set_initial_cache(model); + + + function init_model!(Y, p, model) - function calc_k(mu::FT, sigma::FT) where {FT} - C = FT(0.0) - return FT(C) + N = NTuple{model.npools, FT} + function set_k(k::N) where {N} + k = collect(exp.(range(model.parameters.mu-8*model.parameters.sigma,model.parameters.mu+8*model.parameters.sigma,length=model.npools))); + return ntuple(x->k[x],model.npools) end - p.soilC.ks .= collect(exp.(range(model.parameters.mu-8*model.parameters.sigma,model.parameters.mu+8*model.parameters.sigma,length=model.npools))); + p.soilC.ks .= set_k.(p.soilC.ks); end @@ -56,15 +60,15 @@ init_model!(Y, p, model); set_initial_cache!(p, Y, t0); disordered_kinetics_exp_tendency! = make_exp_tendency(model); -timestepper = CTS.RK4() -ode_algo = CTS.ExplicitAlgorithm(timestepper) +timestepper = CTS.RK4(); +ode_algo = CTS.ExplicitAlgorithm(timestepper); -saveat = collect(t0:FT(10 * dt):tf) +saveat = collect(t0:FT(10 * dt):tf); sv = (; t = Array{Float64}(undef, length(saveat)), saveval = Array{NamedTuple}(undef, length(saveat)), -) -saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) +); +saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat); # updateat = deepcopy(saveat) # drivers = ClimaLand.get_drivers(model) # updatefunc = ClimaLand.make_update_drivers(drivers) @@ -72,15 +76,15 @@ saving_cb = ClimaLand.NonInterpSavingCallback(sv, saveat) # cb = SciMLBase.CallbackSet(driver_cb, saving_cb) prob = SciMLBase.ODEProblem( - CTS.ClimaODEFunction(T_exp! = Soil_bio_exp_tendency!), + CTS.ClimaODEFunction(T_exp! = disordered_kinetics_exp_tendency!), Y, (t0, tf), p, ) # sol = SciMLBase.solve(prob, ode_algo; dt = dt, callback = cb) -sol = SciMLBase.solve(prob, ode_algo; dt = dt, callback = saving_cb) +sol = SciMLBase.solve(prob, ode_algo; dt = dt, callback = saving_cb); # Check that simulation still has correct float type -@assert eltype(sol.u[end].soil) == FT -@assert eltype(sol.u[end].soilco2) == FT +@assert eltype(sol.u[end].soilC) == FT; + diff --git a/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl b/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl index 0a77f8439d..fb0ebbbb22 100644 --- a/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl +++ b/src/standalone/Soil/Biogeochemistry/DisorderedKinetics.jl @@ -27,8 +27,8 @@ import ClimaLand: source! export DisorderedKineticsModelParameters, -DisorderedKineticsModel, - AbstractSoilCarbonSource, + DisorderedKineticsModel, + LitterInput, AbstractSoilDriver, DisorderedKineticsDrivers @@ -125,23 +125,25 @@ This has been written so as to work with Differential Equations.jl. function ClimaLand.make_compute_exp_tendency(model::DisorderedKineticsModel) function compute_exp_tendency!(dY, Y, p, t) - @. dY.soilC.C = - p.ks * Y.soilC.C + @. dY.soilC.C = - p.soilC.ks * Y.soilC.C # Source terms are added in here for src in model.sources + print(src) source!(dY, src, Y, p, model.parameters) end end return compute_exp_tendency! end + """ - AbstractCarbonSource{FT} <: ClimaLand.AbstractSource{FT} + LitterInput{FT} <: AbstractCarbonSource{FT} -An abstract type for soil carbon sources. There are two sources: -roots and microbes, in struct RootProduction and MicrobeProduction. +Struct for the litter input of C into the SOC pool, appearing as a source +term in the differential equation. """ -abstract type AbstractSoilCarbonSource{FT} <: ClimaLand.AbstractSource{FT} end +struct LitterInput{FT} <: ClimaLand.AbstractSource{FT} end """ ClimaLand.source!(dY::ClimaCore.Fields.FieldVector, @@ -151,18 +153,26 @@ abstract type AbstractSoilCarbonSource{FT} <: ClimaLand.AbstractSource{FT} end params) A method which extends the ClimaLand source! function for the -case of microbe production of CO2 in soil. +case of the disordered kinetics model. """ function ClimaLand.source!( dY::ClimaCore.Fields.FieldVector, - src::AbstractSoilCarbonSource, + src::LitterInput, Y::ClimaCore.Fields.FieldVector, p::NamedTuple, - params, + params::DisorderedKineticsModelParameters, ) - @. dY.soilco2.C += p.inputs * pdf(LogNormal(params.mu, params.sigma), p.ks) + @. dY.soilC.C += p.soilC.inputs * p_k(p.soilC.ks,params.mu,params.sigma); end +""" + p_k(k::N) + + a function to calculate the pdf of the lognormal distribution of each decay rate k +""" +function p_k(k,mu,sigma) @. (1/(k* sigma * sqrt(2*π))) * exp(-((log(k) - mu)^2)/(2*sigma^2)); end + + @@ -197,7 +207,7 @@ This function is empty because the auxiliary variable `ks` is not updated in tim """ function ClimaLand.make_update_aux(model::DisorderedKineticsModel) function update_aux!(p, Y, t) - p.inputs = model.drivers.soc.func(t) + p.soilC.inputs .= model.drivers.soc.func.(t) end return update_aux! end