From d1f873a80aa2f1997dcf1efc214f5b01f8ca3dac Mon Sep 17 00:00:00 2001 From: Datseris Date: Tue, 20 Feb 2024 15:51:11 +0000 Subject: [PATCH] finish readme --- README.md | 44 ++++++++++++++++++++++++++----------- docs/src/index.md | 23 ++++++++++++++----- docs/src/processes.md | 8 +------ src/statespace.jl | 7 +++--- src/variables.jl | 51 ++++++++++++++++++------------------------- 5 files changed, 74 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index a58dcb3..2229b60 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,36 @@ # EnergyBalanceModels.jl -Any quantity that may potentially be a dynamic (state) variable, or an explicit function of time, is defined in `src/variables.jl` and must have a default value when defined. They should also be exported and in the rest of the source code they are accessed from the module-level scope. _TODO: Example here_. -Variables that may be dynamic (state) variables must also obtain limits in the `physicall_plausible_limits` function which is in the same file. -Variables that can never be dynamic but are guaranteed to be observed, such as the outgoing longwave radiation, should be preferably defined in the same file but without a default value or limits. +[![docsdev](https://img.shields.io/badge/docs-dev-lightblue.svg)](https://juliadynamics.github.io/ProcessBasedModelling,jl/dev/) +[![docsstable](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliadynamics.github.io/EnergyBalanceModels.jl/stable/) +[![CI](https://github.com/JuliaDynamics/EnergyBalanceModels.jl/workflows/CI/badge.svg)](https://github.com/JuliaDynamics/EnergyBalanceModels.jl/actions?query=workflow%3ACI) +[![codecov](https://codecov.io/gh/JuliaDynamics/EnergyBalanceModels.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/JuliaDynamics/EnergyBalanceModels.jl) +[![Package Downloads](https://shields.io/endpoint?url=https://pkgs.genieframework.com/api/v1/badge/ProcessBasedModelling)](https://pkgs.genieframework.com?packages=ProcessBasedModelling) -# TODO: -explain that all processes by default use the global variables, -which is why docstrings are written this way. +EnergyBalanceModels.jl is a Julia package for creating and analyzing conceptual +models of climate, such as energy balance models or climate tipping models. +Such conceptual models are simplified representation of basic climate components, +and the processes that connect them, such as flows of energy or mass. +Within this context such models are typically coupled ordinary differential +equations (with partial or stochastic DEs also being possible). -EnergyBalanceModels +EnergyBalanceModels.jl accelerates both modelling and analysis aspects of working +with such models by: -""" -module defining various equations and models representing energy balance -processes. It utilizes ModelingToolkit.jl to create a flexible framework -for adding or remove dynamical state variables and adding or removing -equations that represent various processes in the earth system. -""" \ No newline at end of file +- Building upon [ModelingToolkit.jl](https://docs.sciml.ai/ModelingToolkit/stable/) + for creating equations from _symbolic expressions_. +- Utilizing [ProcessBasedModelling.jl](https://github.com/JuliaDynamics/ProcessBasedModelling.jl?tab=readme-ov-file) + to provide a field-specific framework that allows easily testing different physical + hypotheses regarding how climate variables couple to each + other, or how climate processes are represented by equations. +- Offering many predefined processes from current literature and ongoing research. +- Being easy to extend with more climate variables or physical processes. +- Allowing the straightforward coupling of different conceptual models with each other. +- Automating the naming of custom parameters relating to existing climate processes. +- Integrating with [DynamicalSystems.jl](https://juliadynamics.github.io/DynamicalSystemsDocs.jl/dynamicalsystems/dev/) + to automate the start-up phase of typical nonlinear dynamics based workflows. + +To install it, run `import Pkg; Pkg.add("EnergyBalanceModels")`. + +All further information is provided in the documentation, which you can either find +[online](https://juliadynamics.github.io/EnergyBalanceModels.jl/stable/) or build +locally by running the `docs/make.jl` file. diff --git a/docs/src/index.md b/docs/src/index.md index 51b188c..19fb568 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,8 +1,12 @@ # EnergyBalanceModels.jl + + ## [Premade symbolic variable instances](@id global_vars) -For convenience, EnergyBalanceModels.jl defines and exports some symbolic variables. These are used throughout the library as the default variables in [implemented processes](@id processes). +For convenience, EnergyBalanceModels.jl defines and exports some symbolic variables +that we [list below](@ref list_vars). These are used throughout the library as the +default variables in [implemented processes](@id processes). When going through documentation strings of processes, such as [`BasicRadiationBalance`](@ref), you will notice that the function call signature is like: @@ -10,13 +14,22 @@ you will notice that the function call signature is like: BasicRadiationBalance(; T=T, kwargs...) ``` -This `T=T` means that the keyword argument `T`, which represents the "temperature variable", takes the value of `EnergyBalanceModels.T`, which itself is a premade symbolic variable instance that is exported by EnergyBalanceModels.jl. You can pass in your own variables instead, by doing +This `T=T` means that the keyword argument `T`, which represents the +"temperature variable", takes the value of `EnergyBalanceModels.T`, +which itself is a premade symbolic variable instance that is exported by +EnergyBalanceModels.jl. You can pass in your own variables instead, by doing ```julia -@variables T1_tropics = 310.0 +@variables begin + (T1_tropics(t) = 290.0), [bounds = (200.0, 350.0), description = "temperature in tropical box 1, in Kelvin"] +end BasicRadiationBalance(; T=T1_tropics, kwargs...) ``` +_(you would also need to give `T1_tropics` to all other processes that utilize temperature)_ + +Defining variables with the extra `bounds, description` annotations is +useful for integrating with the rest of the functionality of the library. -The exported symbolic variables are: +### [List of premade variables](@id list_vars) ```@example MAIN using EnergyBalanceModels @@ -28,7 +41,7 @@ PREDEFINED_EBM_VARIABLES All [exported symbolic variable instances](@ref) are defiled with a default value and have plausible physical limits that can be obtained with the following function: ```@docs -physically_plausible_limits(::String) +physically_plausible_limits(::Any) ``` e.g., diff --git a/docs/src/processes.md b/docs/src/processes.md index ed7246e..58756fd 100644 --- a/docs/src/processes.md +++ b/docs/src/processes.md @@ -2,12 +2,7 @@ Predefined processes are provided in this page. Those extracted by the literature cite their according resource. -Note that by default all processes utilize the globally-exported [predefined variables](@ref variables) of EnergyBalanceModels.jl. -This is conveyed in the documentation strings by writing something like -``` -f(; T = T, kw...) -``` -this means that the variable representing "T" will be by default the global symbolic variable `T`. You could of course provide any other variable, such as `T1` or `T2` if you have two "boxes" with different temperatures for example. +Note that by default all processes utilize the globally-exported [predefined variables](@ref global_vars) of EnergyBalanceModels.jl. ## Temperature @@ -58,6 +53,5 @@ CO2Forcing ```@docs CloudAlbedoExponential CloudAlbedoLinear -EmissivityCumulativeSubtraction BudykoOLR ``` diff --git a/src/statespace.jl b/src/statespace.jl index 6362ab9..65b1cf6 100644 --- a/src/statespace.jl +++ b/src/statespace.jl @@ -6,10 +6,9 @@ The states need to be named as the limits are deduced from the function `physically_plausible_limits(varname::String)`. """ function physically_plausible_limits(ds::DynamicalSystem) - sys = referrenced_sciml_model(ds) - vars = ModelingToolkit.states(sys) - varstrings = @. string(ModelingToolkit.getname(vars)) - minmax = physically_plausible_limits.(varstrings) + model = referrenced_sciml_model(ds) + vars = ModelingToolkit.states(model) + minmax = physically_plausible_limits.(vars) return minmax end diff --git a/src/variables.jl b/src/variables.jl index 3c59268..cca72f8 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -5,48 +5,39 @@ # will exist in the equations. const PREDEFINED_EBM_VARIABLES = @variables begin - T(t) = 290.0 # [description = "temperature, in Kelvin"] - S(t) = 1.0 # [description = "insolation in units relative to solar constant"] - f(t) = 0.0 # [description = "external forcing, normalized to units of the solar constant"] - α(t) = 0.3 # [description = "planetary albedo, unitless"] - α_ice(t) = 0.05 # [description = "albedo of ice, unitless"] - α_cloud(t) = 0.1 # [description = "albedo of clouds, unitless"] - ΔT(t) = 17.0 # [description = "equator to pole temperature difference, in Kelvin"] - ε(t) = 0.5 # [description = "planetary effective emissivity, unitless"] - C(t) = 0.6744 # [description = "cloud fraction, unitless"] - ℓ(t) = 0.8 # [description = "sine of latitude of ice-line"] - CO2(t) = 400 # [description = "CO2 concentration, in ppm"] + (T(t) = 290.0), [bounds = (200.0, 350.0), description = "temperature, in Kelvin"] + (S(t) = 1.0), [bounds = (0.8, 1.2), description = "insolation, normalized to units of the solar constant"] + (f(t) = 0.0), [bounds = (-0.1, 0.1), description = "external forcing, normalized to units of the solar constant"] + (α(t) = 0.3), [bounds = (0.0, 1.0), description = "planetary albedo, unitless"] + (α_ice(t) = 0.05), [bounds = (0.0, 1.0), description = "albedo of ice, unitless"] + (α_cloud(t) = 0.1), [bounds = (0.0, 1.0), description = "albedo of clouds, unitless"] + (ΔT(t) = 17.0), [bounds = (0.0, 60.0), description = "equator to pole temperature difference, in Kelvin"] + (ε(t) = 0.5), [bounds = (0.0, 1.0), description = "planetary effective emissivity, unitless"] + (C(t) = 0.6744), [bounds = (0.0, 1.0), description = "cloud fraction, unitless"] + (ℓ(t) = 0.8), [bounds = (0.0, 1.0), description = "sine of latitude of ice-line"] + (CO2(t) = 400), [bounds = (200.0, 1800.0), description = "CO2 concentration, in ppm"] # Observables that can never be dynamic variables and hence have no default value: - OLR(t) # [description = "outgoing longwave radiation"] - ASR(t) # [description = "absorved shortwave radiation"] + (OLR(t)), [description = "outgoing longwave radiation"] + (ASR(t)), [description = "absorved shortwave radiation"] end -# TODO: Should we do this export...? export T, S, f, α, α_ice, α_cloud, ΔT, ε, ℓ, C, CO2, OLR, ASR # This function is only meaningful for dynamic variables! """ physically_plausible_limits(x) -Return a tuple (min, max) of limiting values for the variable `x`. +Return a tuple (min, max) of plausible limiting values for the variable `x`. """ -function physically_plausible_limits(var::String)::Tuple{Float64, Float64} - if var[1] == 'T' - return (200, 350) - elseif var == "α_ice" || var == "α_clouds" - return (0, 0.5) - elseif var[1] == 'α' || var[1] == 'ε' || var[1] == 'C' || var[1] == 'ℓ' - return (0, 1) - elseif var == "ΔT" - return (5.0, 60.0) - elseif var == "CO2" - return (200.0, 1600.0) +function physically_plausible_limits(var) + if ModelingToolkit.hasbounds(var) + return getbounds(var) elseif !isnothing(default_value(var)) return (0.8default_value(var), 1.2default_value(var)) else - error("Unpsecified plausible physical limits for $(var) or no default value. "* - "Please edit function `physically_plausible_limits` and add one.") + error(""" + Unpsecified plausible physical limits for $(var): it has no defined bounds or + a default variable. You need to redefine the variable to have either of the two. + """) end end - -physically_plausible_limits(var) = physically_plausible_limits(string(ModelingToolkit.getname(var))) \ No newline at end of file