From 078022779eeb437ecf8aa9b355068fa18b1eea96 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 30 Jan 2024 15:08:56 -0500 Subject: [PATCH 01/23] init --- .../dsl_advanced_options.md | 1 + .../catalyst_functionality/dsl_description.md | 847 ++++++++++++++++++ 2 files changed, 848 insertions(+) create mode 100644 docs/src/catalyst_functionality/dsl_advanced_options.md create mode 100644 docs/src/catalyst_functionality/dsl_description.md diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md new file mode 100644 index 0000000000..7d305b64ac --- /dev/null +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -0,0 +1 @@ +# [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) \ No newline at end of file diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md new file mode 100644 index 0000000000..6e84f5bd4f --- /dev/null +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -0,0 +1,847 @@ +# [The Catalyst DSL - Introduction](@id dsl_description) +In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` macro can be used to create chemical reaction network models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating chemical reaction network (CRN) models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSLs more advanced features. + +The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of teh various ways to create `ReactionSystems`s can be found [here](@ref ref). + +Before we begin the tutorial, we will first load the `Catalyst` package (which is required to run the code). +```@example dsl_1 +using Catalyst +``` + +### QUick-start summary + + +## [Basic syntax](@id dsl_description_basic_syntax) +The basic syntax of the DSL is +```@example dsl_1 +rn = @reaction_network begin + 2.0, X --> Y + 1.0, Y --> X +end +``` +Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with an `end`. Each reaction consists of +- A rate +- A (potentially empty) set of substrates. +- A (potentially empty) set of products. + +Each reaction line declares, in order, the rate, the substrate(s), and the products. The rate is separated from the substrate(s) by a `,`, ad the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. + +Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). + +## [Defining parameters and species in the DSL](@id dsl_description_parameters_basics) +Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you which to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: +```@example dsl_1 +rn1 = @reaction_network begin + a, X --> Y + b, Y --> X +end +``` + +Here we have used single-character symbols to designate all species and parameters. Multi-character symbols, however, are also permitted. E.g. we could call the rates `kX` and `kY`: +```@example dsl_1 +rn1 = @reaction_network begin + kX, X --> Y + kY, Y --> X +end +nothing # hide +``` +Generally, anything that is a [permitted Julia variable name](@id https://docs.julialang.org/en/v1/manual/variables/#man-allowed-variable-names) can be used to designate a species or parameter in Catalyst. + +## [Different types of reactions](@id dsl_description_reactions) + +### Reactions with multiple substrate(s) or product(s) +Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` binds (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: +```@example dsl_1 +rn2 = @reaction_network begin + kB, X + Y --> XY + kD, XY --> X + Y +end +``` +Reactions can have any number of substrates and products, and their names does not need to have any relationship to each other, as demonstrated by the following mock-model: +```@example dsl_1 +rn3 = @reaction_network begin + k, X + Y + Z --> A + B + C + D +end +``` + +### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) +Some reactions has no products, in which case the substrate(s) are degraded (ie.e removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions has no substrate(s), in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species $X$ is both created and degraded, we use: +```@example dsl_1 +rn4 = @reaction_network begin + p, 0 --> X + d, X --> 0 +end +``` + +### Reactions with non-unitary stoichiometries +Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which may dissociate back to two `X` copies) we use: +```@example dsl_1 +rn5 = @reaction_network begin + kB, 2X --> X2 + kD, X2 --> 2X +end +``` +Reactant which stoichiometry is not defined is assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affects the created model can be found [here](@ref ref). + +Stoichiometries can be combined with `( )` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: +```@example dsl_1 +rn6 = @reaction_network begin + k, 2X + 3(Y + 2Z) --> 5(V + W) + k, 2X + 3Y + 6Z --> 5V + 5W +end +nothing # hide +``` + +## [Bundling of similar reactions](@id dsl_description_reaction_bundling) + +### Bi-directional (or reversible) reactions +As is the case for the following two-state model: +```@example dsl_1 +rn7 = @reaction_network begin + k1, X1 --> X2 + k2, X2 --> X1 +end +``` +it is common that reactions occurs in both direction. Here, it is possible ot bundle the reactions into a single line by using the `<-->` arrow. When we do this, we the rate term includes bother the rates (enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: +```@example dsl_1 +rn7 = @reaction_network begin + (k1,k2), X1 <--> X2 +end +``` +Here, the first rate (`k1`) denotes the *forward rate* and the second rate (`k2`) the *backwards rate*. + +Catalyst also permits writing backwards reactions only. This uses the identical syntax to when forward reactions are created, but using the `<--` arrow: +```@example dsl_1 +rn8 = @reaction_network begin + k, X <-- Y +end +``` +Here, the substrate(s) are on the right-hand side and the product(s) on the left-hand side. Hence, the above model can be written identically using: +```@example dsl_1 +rn8 = @reaction_network begin + k, Y --> X +end +``` +Generally, using forward reactions is clearer than backwards ones, with the letter generally not being used. + +### Bundling similar reactions on a singe line +There exists several other situation where models contain similar reactions (e.g. the systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following mode, where species $X$ and $Y$ both degrades at the rate $d$: +```@example dsl_1 +rn8 = @reaction_network begin + d, X --> 0 + d, Y --> 0 +end +``` +These share both the rates and products, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression. However, we have to provide two separate substrate expressions: +```@example dsl_1 +rn8 = @reaction_network begin + d, (X,Y) --> 0 +end +``` +This declaration of the model is identical to the one previously.Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, does not make sense to use). I.e. if the tw reactions had different degradation rates: +```@example dsl_1 +rn9 = @reaction_network begin + dX, X --> 0 + dY, Y --> 0 +end +``` +we could represent this using: +```@example dsl_1 +rn9 = @reaction_network begin + (dX,dY), (X,Y) --> 0 +end +``` + +It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions uses the same rate $k$): +```@example dsl_1 +rn10 = @reaction_network begin + k, (X0,X1,X2,X3) --> (X1,X2,X3,X4) +end +``` + +It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rate(s). These may then (or may not) indicate several rates. We exemplify this using the two following (identical) networks, created with and without bundling. +```@example dsl_1 +rn11 = @reaction_network begin + kf, S --> P1 + kf, S --> P2 + kb_1, P1 --> S + kb_2, P2 --> S +end +``` +```@example dsl_1 +rn11 = @reaction_network begin + (kf, (kb_1, kb_2)), S <--> (P1,P2) +end +``` + +Like when we designated stoichiometries, reaction bundling can be applied very generally to create some truly complicated reactions: +```@example dsl_1 +rn12 = @reaction_network begin + ((pX, pY, pZ),d), (0, Y0, Z0) <--> (X, Y, Z1+Z2) +end +``` +However, as for the above model, bundling reactions too zealously can reduce (rather improve) the model's readability. + +## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) +So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. + +Let us consider a model with an activator ($A$, which degraded at a constant rate) and a protein ($P$). The production rate of $P$ depends both on $A$ and parameter ($kp$). We model this through: +```@example dsl_1 +rn_13 = @reaction_network begin + d, A --> 0 + kp*A, 0 --> P +end +``` +Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`]: +```@example dsl_1 +using Latexify +latexify(rn_13; form=:ode) +``` + +In this case, we can generate an equivalent model by instead adding $A$ as both a substrate and a product to $A$'s production reaction: +```@example dsl_1 +rn_13_alt = @reaction_network begin + d, A --> 0 + kp, A --> A + P +end +``` +We can confirm that this generate the same ODE: +```@example dsl_1 +latexify(rn_13_alt; form=:ode) +``` +Here, while the model will generate the same ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. +!!! warn + While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). + +!!! danger + Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This mean that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). + + +### Time-dependant rates + +### Using functions in rates + +### Using pre-declared Michaelis-Menten or Hill function rate + + +## [Using special symbols](@id dsl_description_symbols) +Julia permits any unicode characters to be used in variable names, thus Catalyst are able to use these as well. Below we describe some case where these to alter the appearance of models. No functionality is, however, tied to this. + +### [Using ∅ to denote degradation/production reactions](@id dsl_description_symbols_empty_set) +Previously, we described how `0` could be used to [denote degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system alternatively can be written as: +```@example dsl_1 +rn4 = @reaction_network begin + p, ∅ --> X + d, X --> ∅ +end +``` + +### Using special arrow symbols +Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representation of these arrows are available. Here, +- `>`, `→`, `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, and `⇁` can be used to represent forward reactions. +- `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent reversible reactions. +- `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. + +E.g. the production/degradation system alternatively can be written as: +```@example dsl_1 +rn4 = @reaction_network begin + p, ∅ → X + d, X → ∅ +end +``` + +### Using special symbols to denote species or parameters +A range of possible characters are available which can be incorporated into species and parameter names. This includes, but is not limited to: +- Greek letters (e.g `α`, `σ`, `τ`, and `Ω`). +- Superscript and subscript characters (to create e.g `k₁`, `k₂`, `Xₐ`, and `Xᴾ`). +- Non-latin, non-greek, letters (e.g. `ä`, `Д`, `س`, and `א`). +- Other symbols (e.g. `£`, `ℂ`, `▲`, and `♠`). + +An example of how this can be used to create neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used it the creation a model of the sigma factor V circuit in the bacteria *Bacillus subtilis*: +```@example dsl_1 +σᵛ_model = @reaction_network begin + v₀ + hill(σᵛ,v,K,n), ∅ → σᵛ + A + kdeg, (σᵛ, A, Aσᵛ) → ∅ + (kB,kD), A + σᵛ ↔ Aσᵛ + L, Aσᵛ → σᵛ +end +nothing # hide +``` + +This functionality can also be used to create less-serious models: +rn_13 = @reaction_network begin + 🍦, 😢 --> 😃 +end + +It should be noted that the following symbols are *not permitted* to be used as species or parameter names: +- `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)) +- `pi` and `π` (used in Julia to denote [`3.1415926535897...`](https://en.wikipedia.org/wiki/Pi)). +- `t` (used to denote the [time variable](@ref ref)). +- `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)) +- `im` (used in Julia to represent [complex numbers](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/#Complex-Numbers)). +- `nothing` ([used in Julia](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). +- `Γ` (used by Catalyst to represent [conserved quantities](@ref ref)). + + + + + + +## [Basic syntax](@id basic_examples) + +The `@reaction_network` macro allows the (symbolic) specification of reaction +networks with a simple format. Its input is a set of chemical reactions, and +from them it generates a symbolic [`ReactionSystem`](@ref) reaction network +object. The `ReactionSystem` can be used as input to ModelingToolkit +`ODEProblem`, `NonlinearProblem`, `SteadyStateProblem`, `SDEProblem`, +`JumpProblem`, and more. `ReactionSystem`s can also be incrementally extended as +needed, allowing for programmatic construction of networks and network +composition. + +The basic syntax is: + +```@example dsl_1 +rn = @reaction_network begin + 2.0, X + Y --> XY + 1.0, XY --> Z1 + Z2 +end +``` +where each line of the [`@reaction_network`](@ref) macro corresponds to a +chemical reaction. Each reaction consists of a reaction rate (the expression on +the left-hand side of `,`), a set of substrates (the expression in-between `,` +and `-->`), and a set of products (the expression on the right-hand side of +`-->`). The substrates and the products may contain one or more reactants, +separated by `+`. The naming convention for these is the same as for normal +variables in Julia. + +The chemical reaction model is generated by the `@reaction_network` macro and +stored in the `rn` variable (a normal Julia variable, which does not need to be +called `rn`). It corresponds to a [`ReactionSystem`](@ref), a symbolic +representation of the chemical network. The generated `ReactionSystem` can be +converted to a symbolic differential equation model via +```@example dsl_1 +osys = convert(ODESystem, rn) +``` + +We can then convert the symbolic ODE model into a compiled, optimized +representation for use in the SciML ODE solvers by constructing an `ODEProblem`. +Creating an `ODEProblem` also requires our specifying the initial conditions for +the model. We do this by creating a mapping from each symbolic variable +representing a chemical species to its initial value +```@example dsl_1 +# define the symbolic variables +@variables t +@species X(t) Y(t) Z(t) XY(t) Z1(t) Z2(t) + +# create the mapping +u0 = [X => 1.0, Y => 1.0, XY => 1.0, Z1 => 1.0, Z2 => 1.0] +``` +Alternatively, we can create a mapping using Julia `Symbol`s for each variable, +and then convert them to a mapping involving symbolic variables like +```@example dsl_1 +u0 = symmap_to_varmap(rn, [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0]) +``` +Given the mapping, we can then create an `ODEProblem` from our symbolic `ODESystem` +```@example dsl_1 +tspan = (0.0, 1.0) # the time interval to solve on +oprob = ODEProblem(osys, u0, tspan, []) +``` + +Catalyst provides a shortcut to avoid having to explicitly `convert` to an +`ODESystem` and/or use `symmap_to_varmap`, allowing direct construction +of the `ODEProblem` like +```@example dsl_1 +u0 = [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0] +oprob = ODEProblem(rn, u0, tspan, []) +``` + +For more detailed examples, see the [Basic Chemical Reaction Network +Examples](@ref basic_CRN_examples). + +## Defining parameters and species +Numeric parameter values do not need to be set when the model is created, i.e. +Catalyst supports symbolic parameters too: +```@example dsl_1 +rn = @reaction_network begin + k1, X --> Y + k2, Y --> X +end +``` +All symbols that do not appear as a substrate or product in a reaction are +designated by Catalyst as a parameter (i.e. all symbols appearing only within +rate expressions and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). In this example `X` and `Y` +appear as a substrates and products, but neither `k1` nor `k2`. Hence `k1` and `k2` are +designated as parameters. Later in this tutorial, we will describe how to manually specify what should be +considered a species or parameter. + +## Production, Destruction, and Stoichiometry +Sometimes reactants are produced/destroyed from/to nothing. This can be +designated using either `0` or `∅`: +```@example dsl_1 +rn = @reaction_network begin + 2.0, 0 --> X + 1.0, X --> 0 +end +``` +If several molecules of the same reactant are involved in a reaction, the +stoichiometry of a reactant in a reaction can be set using a number. Here, two +molecules of species `X` form the dimer `X2`: +```@example dsl_1 +rn = @reaction_network begin + 1.0, 2X --> Y +end +``` +this corresponds to the differential equation: +```@example dsl_1 +convert(ODESystem, rn) +``` +Other numbers than 2 can be used, and parenthesis can be used to reuse the same +stoichiometry for several reactants: +```@example dsl_1 +rn = @reaction_network begin + 1.0, X + 2(Y + Z) --> W +end +``` +Note, one can explicitly multiply by integer coefficients too, i.e. +```@example dsl_1 +rn = @reaction_network begin + 1.0, X + 2*(Y + Z) --> W +end +``` + +## Arrow variants +A variety of Unicode arrows are accepted by the DSL in addition to `-->`. All of +these work: `>`, `→` `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, `⇁`. Backwards +arrows can also be used to write the reaction in the opposite direction. For +example, these reactions are equivalent: +```@example dsl_1 +rn = @reaction_network begin + 1.0, X + Y --> XY + 1.0, X + Y → XY + 1.0, XY ← X + Y + 1.0, XY <-- X + Y +end +``` + +## Bi-directional arrows for reversible reactions +Bi-directional arrows, including bidirectional Unicode arrows like ↔, can be +used to designate a reversible reaction. For example, these two models are +equivalent: +```@example dsl_1 +rn = @reaction_network begin + 2.0, X + Y --> XY + 2.0, X + Y <-- XY +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + (2.0,2.0), X + Y <--> XY +end +``` + +If the reaction rates in the backward and forward directions are different, they +can be designated in the following way: +```@example dsl_1 +rn = @reaction_network begin + (2.0,1.0), X + Y <--> XY +end +``` +which is identical to +```@example dsl_1 +rn = @reaction_network begin + 2.0, X + Y --> XY + 1.0, X + Y <-- XY +end +``` + +Finally, Catalyst also + +## Combining several reactions in one line +Several similar reactions can be combined in one line by providing a tuple of +reaction rates and/or substrates and/or products. If several tuples are provided, +they must all be of identical length. These pairs of reaction networks are all +identical. + +Pair 1: +```@example dsl_1 +rn1 = @reaction_network begin + 1.0, S --> (P1,P2) +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S --> P1 + 1.0, S --> P2 +end +``` +Pair 2: +```@example dsl_1 +rn1 = @reaction_network begin + (1.0,2.0), (S1,S2) --> P +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S1 --> P + 2.0, S2 --> P +end +``` +Pair 3: +```@example dsl_1 +rn1 = @reaction_network begin + (1.0,2.0,3.0), (S1,S2,S3) --> (P1,P2,P3) +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S1 --> P1 + 2.0, S2 --> P2 + 3.0, S3 --> P3 +end +``` +This can also be combined with bi-directional arrows, in which case separate +tuples can be provided for the backward and forward reaction rates. +These reaction networks are identical +```@example dsl_1 +rn1 = @reaction_network begin + (1.0,(1.0,2.0)), S <--> (P1,P2) +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S --> P1 + 1.0, S --> P2 + 1.0, P1 --> S + 2.0, P2 --> S +end +``` + +## Variable reaction rates +Reaction rates do not need to be a single parameter or a number, but can also be +expressions depending on time or the current amounts of system species (when, for +example, one species can activate the production of another). For instance, this +is a valid notation: +```@example dsl_1 +rn = @reaction_network begin + 1.0, X --> ∅ + k*X, Y --> ∅ +end +``` +corresponding to the ODE model +```@example dsl_1 +convert(ODESystem,rn) +``` + +With respect to the corresponding mass action ODE model, this is actually +equivalent to the reaction system +```@example dsl_1 +rn = @reaction_network begin + 1.0, X --> ∅ + k, X + Y --> X +end +``` +```@example dsl_1 +convert(ODESystem,rn) +``` +!!! note + While the ODE models corresponding to the preceding two reaction systems are + identical, in the latter example the `Reaction` stored in `rn` will be classified as + [`ismassaction`](@ref) while in the former it will not, which can impact optimizations + used in generating `JumpSystem`s. For this reason, it is recommended to use the + latter representation when possible. + +Most expressions and functions are valid reaction rates, e.g.: +```@example dsl_1 +using SpecialFunctions +rn = @reaction_network begin + 2.0*X^2, 0 --> X + Y + t*gamma(Y), X --> ∅ + pi*X/Y, Y --> ∅ +end +``` +where here `t` always denotes Catalyst's time variable. Please note that many +user-defined functions can be called directly, but others will require +registration with Symbolics.jl ([see the faq](@ref user_functions)). + +## Explicit specification of network species and parameters +Recall that the `@reaction_network` macro automatically designates symbols used +in the macro as either parameters or species, with symbols that appear as a +substrate or product being species, and all other symbols becoming parameters +(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default +behavior for a given symbol. E.g one might want something to be considered as a +species, even if it only appears within a rate expression. In the following +network +```@example dsl_1 +rn = @reaction_network begin + k*X, Y --> 0 +end +``` +`X` (as well as `k`) will be considered a parameter. + +By using the `@species` and `@parameters` options within the `@reaction_network` +macro, one can manually declare that specified symbols should be considered a +species or parameter. E.g in: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + k*X, Y --> 0 +end +``` +`X` and `Y` are set as species. Please note that when declaring species using +the `@species` option, their dependant variable (almost always `t`) also needs +to be designated. Similarly in +```@example dsl_1 +rn = @reaction_network begin + @parameters k + k*X, Y --> 0 +end +``` +both `X` and `k` will be considered as parameters. It is also possible to use +both options simultaneously, allowing users to fully specify which symbols are +species and/or parameters: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + @parameters k + k*X, Y --> 0 +end +``` +Here, `X` and `Y` are designated as species and `k` as a parameter. + +The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + k*X, Y --> 0 +end +``` + +Finally, note that the `@species` and `@parameters` options can also be used in +`begin ... end` block form, allowing more formatted lists of species/parameters: +```@example dsl_1 +rn = @reaction_network begin + @parameters begin + d1 + d2 + end + @species begin + X1(t) + X2(t) + end + d2, X2 --> 0 + d1, X1 --> 0 +end +``` +This can be especially useful when declaring default values for clarity of model +specification (see the next section). + +## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) +When using the `@species` and ` @parameters` macros to declare species and/or +parameters, one can also provide default initial conditions for each species and +values for each parameter: +```@example dsl_1 +rn = @reaction_network begin + @species X(t)=1.0 + @parameters p=1.0 d=0.1 + p, 0 --> X + d, X --> ∅ +end +``` +This system can now be simulated without providing initial condition or +parameter vectors to the DifferentialEquations.jl solvers: +```@example dsl_1 +using DifferentialEquations, Plots +u0 = [] +tspan = (0.0, 10.0) +p = [] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +When providing default values, it is possible to do so for only a subset of the +species or parameters, in which case the rest can be specified when constructing +the problem type to solve: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + @parameters p=1.0 d + p, 0 --> X + d, X --> 0 +end + +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:d => .1] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +Finally, default values can be overridden by passing mapping vectors to the +DifferentialEquations.jl problem being constructed. Only those initial conditions +or parameters for which we want to change their value from the default will need to be passed +```@example dsl_1 +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1] # we change p to 2.0 +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) +It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: +```@example dsl_1 +rn = @reaction_network begin + @parameters X0 + @species X(t)=X0 + p, 0 --> X + d, X --> ∅ +end +``` +We can now simulate the network without providing any initial conditions: +```@example dsl_1 +u0 = [] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1, :X0 => 1.0] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## Naming the generated `ReactionSystem` +ModelingToolkit uses system names to allow for compositional and hierarchical +models. To specify a name for the generated `ReactionSystem` via the +[`@reaction_network`](@ref) macro, just place the name before `begin`: +```@example dsl_1 +rn = @reaction_network production_degradation begin + p, ∅ --> X + d, X --> ∅ +end +ModelingToolkit.nameof(rn) == :production_degradation +``` + +## Pre-defined functions +Hill functions and a Michaelis-Menten function are pre-defined and can be used +as rate laws. Below, the pair of reactions within `rn1` are equivalent, as are +the pair of reactions within `rn2`: +```@example dsl_1 +rn1 = @reaction_network begin + hill(X,v,K,n), ∅ --> X + v*X^n/(X^n+K^n), ∅ --> X +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + mm(X,v,K), ∅ --> X + v*X/(X+K), ∅ --> X +end +``` +Repressor Hill (`hillr`) and Michaelis-Menten (`mmr`) functions are also +provided: +```@example dsl_1 +rn1 = @reaction_network begin + hillr(X,v,K,n), ∅ --> X + v*K^n/(X^n+K^n), ∅ --> X +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + mmr(X,v,K), ∅ --> X + v*K/(X+K), ∅ --> X +end +``` + +Please see the API [Rate Laws](@ref api_rate_laws) section for more details. + +## Including non-species variables +Non-species state variables can be specified in the DSL using the `@variables` +macro. These are declared similarly to species. For example, +```@example dsl_1 +rn_with_volume = @reaction_network begin + @variables V(t) + k*V, 0 --> A +end +``` +creates a network with one species +```@example dsl_1 +species(rn_with_volume) +``` +and one non-species +```@example dsl_1 +nonspecies(rn_with_volume) +``` +giving two state variables, always internally ordered by species and then +nonspecies: +```@example dsl_1 +states(rn_with_volume) +``` + +`rn_with_volume` could then be extended with constraint equations for how `V(t)` +evolves in time, see the [associated tutorial](@ref constraint_equations). + +## Specifying alternative time variables and/or extra independent variables +While the DSL defaults to allowing `t` as the time variable, one can use the +`@ivs` macro to specify an alternative independent variable. For example, to +make `s` the default time variable one can say +```@example dsl_1 +rn_with_s = @reaction_network begin + @ivs s + @variables V(s) + @species B(s) + k, A + V*B --> C +end +show(stdout, MIME"text/plain"(), rn_with_s) # hide +``` +where we see all states are now functions of `s`. + +Similarly, if one wants states to be functions of more than one independent +variable, for example to encode a spatial problem, one can list more than one +variable, i.e. `@ivs t x y`. Here the first listed independent variable is +always chosen to represent time. For example, +```@example dsl_1 +rn_with_many_ivs = @reaction_network begin + @ivs s x + @variables V1(s) V2(s,x) + @species A(s) B(s,x) + k, V1*A --> V2*B + C +end +show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide +``` +Here again `s` will be the time variable, and any inferred species, `C` in this +case, are made functions of both variables, i.e. `C(s, x)`. + +## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) +The DSL allows Julia variables to be interpolated for the network name, within +rate constant expressions, or for species/stoichiometry within reactions. Using +the lower-level symbolic interface we can then define symbolic variables and +parameters outside of the macro, which can then be used within expressions in +the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) +tutorial for details on the lower-level symbolic interface). For example, +```@example dsl_1 +@parameters k α +@variables t +@species A(t) +spec = A +par = α +rate = k*A +name = :network +rn = @reaction_network $name begin + $rate*B, 2*$spec + $par*B --> $spec + C + end +``` +As the parameters `k` and `α` were pre-defined and appeared via interpolation, +we did not need to declare them within the `@reaction_network` macro, +i.e. they are automatically detected as parameters: +```@example dsl_1 +parameters(rn) +``` +as are the species coming from interpolated variables +```@example dsl_1 +species(rn) +``` + +!!! note + When using interpolation, expressions like `2$spec` won't work; the + multiplication symbol must be explicitly included like `2*$spec`. From 8c7c872af64e8b0bb9d7fa40344dd5368125f380 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 30 Jan 2024 16:46:45 -0500 Subject: [PATCH 02/23] save changes --- .../dsl_advanced_options.md | 280 +++++++- .../catalyst_functionality/dsl_description.md | 616 ++---------------- 2 files changed, 335 insertions(+), 561 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index 7d305b64ac..7af4fec0c7 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -1 +1,279 @@ -# [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) \ No newline at end of file +# [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) + +Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous tutorial](@ref dsl_description) described how to create reactions. This one will instead describe how to use options. These are typically used to supplant the model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. + +All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here], with most (but not all) being described in more detail below. + + This tutorial will also describe some additional advanced DSL features that does not include using an option. + +## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) +Previously, we mentioned that + +### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) + +### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) + +### [Specifying non-species variables](@id dsl_advanced_options_variables) + +### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) + +## [Setting reaction metadata](@id dsl_advanced_options_) + +## [Naming reaction networks](@id dsl_advanced_options_) + +## [Creating observables](@id dsl_advanced_options_) + +## [Creating events](@id dsl_advanced_options_) + +## [Specifying non-time independent variables](@id dsl_advanced_options_) + + +## Explicit specification of network species and parameters +Recall that the `@reaction_network` macro automatically designates symbols used +in the macro as either parameters or species, with symbols that appear as a +substrate or product being species, and all other symbols becoming parameters +(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default +behavior for a given symbol. E.g one might want something to be considered as a +species, even if it only appears within a rate expression. In the following +network +```@example dsl_1 +rn = @reaction_network begin + k*X, Y --> 0 +end +``` +`X` (as well as `k`) will be considered a parameter. + +By using the `@species` and `@parameters` options within the `@reaction_network` +macro, one can manually declare that specified symbols should be considered a +species or parameter. E.g in: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + k*X, Y --> 0 +end +``` +`X` and `Y` are set as species. Please note that when declaring species using +the `@species` option, their dependant variable (almost always `t`) also needs +to be designated. Similarly in +```@example dsl_1 +rn = @reaction_network begin + @parameters k + k*X, Y --> 0 +end +``` +both `X` and `k` will be considered as parameters. It is also possible to use +both options simultaneously, allowing users to fully specify which symbols are +species and/or parameters: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + @parameters k + k*X, Y --> 0 +end +``` +Here, `X` and `Y` are designated as species and `k` as a parameter. + +The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + k*X, Y --> 0 +end +``` + +Finally, note that the `@species` and `@parameters` options can also be used in +`begin ... end` block form, allowing more formatted lists of species/parameters: +```@example dsl_1 +rn = @reaction_network begin + @parameters begin + d1 + d2 + end + @species begin + X1(t) + X2(t) + end + d2, X2 --> 0 + d1, X1 --> 0 +end +``` +This can be especially useful when declaring default values for clarity of model +specification (see the next section). + +## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) +When using the `@species` and ` @parameters` macros to declare species and/or +parameters, one can also provide default initial conditions for each species and +values for each parameter: +```@example dsl_1 +rn = @reaction_network begin + @species X(t)=1.0 + @parameters p=1.0 d=0.1 + p, 0 --> X + d, X --> ∅ +end +``` +This system can now be simulated without providing initial condition or +parameter vectors to the DifferentialEquations.jl solvers: +```@example dsl_1 +using DifferentialEquations, Plots +u0 = [] +tspan = (0.0, 10.0) +p = [] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +When providing default values, it is possible to do so for only a subset of the +species or parameters, in which case the rest can be specified when constructing +the problem type to solve: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + @parameters p=1.0 d + p, 0 --> X + d, X --> 0 +end + +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:d => .1] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +Finally, default values can be overridden by passing mapping vectors to the +DifferentialEquations.jl problem being constructed. Only those initial conditions +or parameters for which we want to change their value from the default will need to be passed +```@example dsl_1 +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1] # we change p to 2.0 +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) +It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: +```@example dsl_1 +rn = @reaction_network begin + @parameters X0 + @species X(t)=X0 + p, 0 --> X + d, X --> ∅ +end +``` +We can now simulate the network without providing any initial conditions: +```@example dsl_1 +u0 = [] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1, :X0 => 1.0] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## Naming the generated `ReactionSystem` +ModelingToolkit uses system names to allow for compositional and hierarchical +models. To specify a name for the generated `ReactionSystem` via the +[`@reaction_network`](@ref) macro, just place the name before `begin`: +```@example dsl_1 +rn = @reaction_network production_degradation begin + p, ∅ --> X + d, X --> ∅ +end +ModelingToolkit.nameof(rn) == :production_degradation +``` + +## Including non-species variables +Non-species state variables can be specified in the DSL using the `@variables` +macro. These are declared similarly to species. For example, +```@example dsl_1 +rn_with_volume = @reaction_network begin + @variables V(t) + k*V, 0 --> A +end +``` +creates a network with one species +```@example dsl_1 +species(rn_with_volume) +``` +and one non-species +```@example dsl_1 +nonspecies(rn_with_volume) +``` +giving two state variables, always internally ordered by species and then +nonspecies: +```@example dsl_1 +states(rn_with_volume) +``` + +`rn_with_volume` could then be extended with constraint equations for how `V(t)` +evolves in time, see the [associated tutorial](@ref constraint_equations). + +## Specifying alternative time variables and/or extra independent variables +While the DSL defaults to allowing `t` as the time variable, one can use the +`@ivs` macro to specify an alternative independent variable. For example, to +make `s` the default time variable one can say +```@example dsl_1 +rn_with_s = @reaction_network begin + @ivs s + @variables V(s) + @species B(s) + k, A + V*B --> C +end +show(stdout, MIME"text/plain"(), rn_with_s) # hide +``` +where we see all states are now functions of `s`. + +Similarly, if one wants states to be functions of more than one independent +variable, for example to encode a spatial problem, one can list more than one +variable, i.e. `@ivs t x y`. Here the first listed independent variable is +always chosen to represent time. For example, +```@example dsl_1 +rn_with_many_ivs = @reaction_network begin + @ivs s x + @variables V1(s) V2(s,x) + @species A(s) B(s,x) + k, V1*A --> V2*B + C +end +show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide +``` +Here again `s` will be the time variable, and any inferred species, `C` in this +case, are made functions of both variables, i.e. `C(s, x)`. + +## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) +The DSL allows Julia variables to be interpolated for the network name, within +rate constant expressions, or for species/stoichiometry within reactions. Using +the lower-level symbolic interface we can then define symbolic variables and +parameters outside of the macro, which can then be used within expressions in +the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) +tutorial for details on the lower-level symbolic interface). For example, +```@example dsl_1 +@parameters k α +@variables t +@species A(t) +spec = A +par = α +rate = k*A +name = :network +rn = @reaction_network $name begin + $rate*B, 2*$spec + $par*B --> $spec + C + end +``` +As the parameters `k` and `α` were pre-defined and appeared via interpolation, +we did not need to declare them within the `@reaction_network` macro, +i.e. they are automatically detected as parameters: +```@example dsl_1 +parameters(rn) +``` +as are the species coming from interpolated variables +```@example dsl_1 +species(rn) +``` + +!!! note + When using interpolation, expressions like `2$spec` won't work; the + multiplication symbol must be explicitly included like `2*$spec`. diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index 6e84f5bd4f..c70d6a259c 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -1,14 +1,14 @@ # [The Catalyst DSL - Introduction](@id dsl_description) In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` macro can be used to create chemical reaction network models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating chemical reaction network (CRN) models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSLs more advanced features. -The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of teh various ways to create `ReactionSystems`s can be found [here](@ref ref). +The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial wil solely focus on options for model creation. Before we begin the tutorial, we will first load the `Catalyst` package (which is required to run the code). ```@example dsl_1 using Catalyst ``` -### QUick-start summary +### Quick-start summary ## [Basic syntax](@id dsl_description_basic_syntax) @@ -192,7 +192,7 @@ rn_13 = @reaction_network begin kp*A, 0 --> P end ``` -Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`]: +Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`](@ref ref): ```@example dsl_1 using Latexify latexify(rn_13; form=:ode) @@ -216,12 +216,65 @@ Here, while the model will generate the same ODE, SDE, and jump simulations, the !!! danger Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This mean that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). +Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: +```@example dsl_1 +rn_14 = @reaction_network begin + 2.0 + X^2, 0 --> X + Y + k1+k2^k3, X --> ∅ + pi*X/(sqrt(2)+Y), Y → ∅ +end +``` ### Time-dependant rates +Previously we have assumed that that the rates are independent on the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progress, we can use: +```@example dsl_1 +rn_14 = @reaction_network begin + kp/t, 0 --> P + d, P --> 0 +end +``` + +Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to representing a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: +```@example dsl_1 +rn_15 = @reaction_network begin + A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P + d, P --> 0 +end +``` ### Using functions in rates +It is possible for the rate to use Julia function. These can either be functions from Julia's standard library: +```@example dsl_1 +rn_16 = @reaction_network begin + d, A --> 0 + kp*sqrt(A), 0 --> P +end +``` +or ones defined by the user: +```@example dsl_1 +custom_function(p1,p2,X) = (p1+E)/(p2+E) +rn_17 = @reaction_network begin + d, A --> 0 + custom_function(k1,k2,E), 0 --> P +end +``` ### Using pre-declared Michaelis-Menten or Hill function rate +Two function frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where $X$ activates its own production through a hill function can be created using: +```@example dsl_1 +custom_function(p1,p2,X) = (p1+E)/(p2+E) +rn_18 = @reaction_network begin + hill(X,v,K,n), 0 --> P + d, X --> 0 +end +``` + +Catalyst comes with the following predefined functions: +- The Michaelis-Menten function: $mm(X,v,K) = v*X/(X + K)$. +- The repressive Michaelis-Menten function: $mmr(X,v,K) = v*K/(X + K)$. +- The Hill function: $hill(X,v,K,n) = v*(X^n)/(X^n + K^n)$. +- The repressive Hill function: $hillr(X,v,K,n) = v*(K^n)/(X^n + K^n)$. +- The activating/repressive Hill function: $hillar(X,Y,v,K,n) = v*(X^n)/(X^n + Y^n + K^n)$. ## [Using special symbols](@id dsl_description_symbols) @@ -287,561 +340,4 @@ It should be noted that the following symbols are *not permitted* to be used as -## [Basic syntax](@id basic_examples) - -The `@reaction_network` macro allows the (symbolic) specification of reaction -networks with a simple format. Its input is a set of chemical reactions, and -from them it generates a symbolic [`ReactionSystem`](@ref) reaction network -object. The `ReactionSystem` can be used as input to ModelingToolkit -`ODEProblem`, `NonlinearProblem`, `SteadyStateProblem`, `SDEProblem`, -`JumpProblem`, and more. `ReactionSystem`s can also be incrementally extended as -needed, allowing for programmatic construction of networks and network -composition. - -The basic syntax is: - -```@example dsl_1 -rn = @reaction_network begin - 2.0, X + Y --> XY - 1.0, XY --> Z1 + Z2 -end -``` -where each line of the [`@reaction_network`](@ref) macro corresponds to a -chemical reaction. Each reaction consists of a reaction rate (the expression on -the left-hand side of `,`), a set of substrates (the expression in-between `,` -and `-->`), and a set of products (the expression on the right-hand side of -`-->`). The substrates and the products may contain one or more reactants, -separated by `+`. The naming convention for these is the same as for normal -variables in Julia. - -The chemical reaction model is generated by the `@reaction_network` macro and -stored in the `rn` variable (a normal Julia variable, which does not need to be -called `rn`). It corresponds to a [`ReactionSystem`](@ref), a symbolic -representation of the chemical network. The generated `ReactionSystem` can be -converted to a symbolic differential equation model via -```@example dsl_1 -osys = convert(ODESystem, rn) -``` - -We can then convert the symbolic ODE model into a compiled, optimized -representation for use in the SciML ODE solvers by constructing an `ODEProblem`. -Creating an `ODEProblem` also requires our specifying the initial conditions for -the model. We do this by creating a mapping from each symbolic variable -representing a chemical species to its initial value -```@example dsl_1 -# define the symbolic variables -@variables t -@species X(t) Y(t) Z(t) XY(t) Z1(t) Z2(t) - -# create the mapping -u0 = [X => 1.0, Y => 1.0, XY => 1.0, Z1 => 1.0, Z2 => 1.0] -``` -Alternatively, we can create a mapping using Julia `Symbol`s for each variable, -and then convert them to a mapping involving symbolic variables like -```@example dsl_1 -u0 = symmap_to_varmap(rn, [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0]) -``` -Given the mapping, we can then create an `ODEProblem` from our symbolic `ODESystem` -```@example dsl_1 -tspan = (0.0, 1.0) # the time interval to solve on -oprob = ODEProblem(osys, u0, tspan, []) -``` - -Catalyst provides a shortcut to avoid having to explicitly `convert` to an -`ODESystem` and/or use `symmap_to_varmap`, allowing direct construction -of the `ODEProblem` like -```@example dsl_1 -u0 = [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0] -oprob = ODEProblem(rn, u0, tspan, []) -``` - -For more detailed examples, see the [Basic Chemical Reaction Network -Examples](@ref basic_CRN_examples). - -## Defining parameters and species -Numeric parameter values do not need to be set when the model is created, i.e. -Catalyst supports symbolic parameters too: -```@example dsl_1 -rn = @reaction_network begin - k1, X --> Y - k2, Y --> X -end -``` -All symbols that do not appear as a substrate or product in a reaction are -designated by Catalyst as a parameter (i.e. all symbols appearing only within -rate expressions and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). In this example `X` and `Y` -appear as a substrates and products, but neither `k1` nor `k2`. Hence `k1` and `k2` are -designated as parameters. Later in this tutorial, we will describe how to manually specify what should be -considered a species or parameter. - -## Production, Destruction, and Stoichiometry -Sometimes reactants are produced/destroyed from/to nothing. This can be -designated using either `0` or `∅`: -```@example dsl_1 -rn = @reaction_network begin - 2.0, 0 --> X - 1.0, X --> 0 -end -``` -If several molecules of the same reactant are involved in a reaction, the -stoichiometry of a reactant in a reaction can be set using a number. Here, two -molecules of species `X` form the dimer `X2`: -```@example dsl_1 -rn = @reaction_network begin - 1.0, 2X --> Y -end -``` -this corresponds to the differential equation: -```@example dsl_1 -convert(ODESystem, rn) -``` -Other numbers than 2 can be used, and parenthesis can be used to reuse the same -stoichiometry for several reactants: -```@example dsl_1 -rn = @reaction_network begin - 1.0, X + 2(Y + Z) --> W -end -``` -Note, one can explicitly multiply by integer coefficients too, i.e. -```@example dsl_1 -rn = @reaction_network begin - 1.0, X + 2*(Y + Z) --> W -end -``` - -## Arrow variants -A variety of Unicode arrows are accepted by the DSL in addition to `-->`. All of -these work: `>`, `→` `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, `⇁`. Backwards -arrows can also be used to write the reaction in the opposite direction. For -example, these reactions are equivalent: -```@example dsl_1 -rn = @reaction_network begin - 1.0, X + Y --> XY - 1.0, X + Y → XY - 1.0, XY ← X + Y - 1.0, XY <-- X + Y -end -``` - -## Bi-directional arrows for reversible reactions -Bi-directional arrows, including bidirectional Unicode arrows like ↔, can be -used to designate a reversible reaction. For example, these two models are -equivalent: -```@example dsl_1 -rn = @reaction_network begin - 2.0, X + Y --> XY - 2.0, X + Y <-- XY -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - (2.0,2.0), X + Y <--> XY -end -``` - -If the reaction rates in the backward and forward directions are different, they -can be designated in the following way: -```@example dsl_1 -rn = @reaction_network begin - (2.0,1.0), X + Y <--> XY -end -``` -which is identical to -```@example dsl_1 -rn = @reaction_network begin - 2.0, X + Y --> XY - 1.0, X + Y <-- XY -end -``` - -Finally, Catalyst also - -## Combining several reactions in one line -Several similar reactions can be combined in one line by providing a tuple of -reaction rates and/or substrates and/or products. If several tuples are provided, -they must all be of identical length. These pairs of reaction networks are all -identical. - -Pair 1: -```@example dsl_1 -rn1 = @reaction_network begin - 1.0, S --> (P1,P2) -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S --> P1 - 1.0, S --> P2 -end -``` -Pair 2: -```@example dsl_1 -rn1 = @reaction_network begin - (1.0,2.0), (S1,S2) --> P -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S1 --> P - 2.0, S2 --> P -end -``` -Pair 3: -```@example dsl_1 -rn1 = @reaction_network begin - (1.0,2.0,3.0), (S1,S2,S3) --> (P1,P2,P3) -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S1 --> P1 - 2.0, S2 --> P2 - 3.0, S3 --> P3 -end -``` -This can also be combined with bi-directional arrows, in which case separate -tuples can be provided for the backward and forward reaction rates. -These reaction networks are identical -```@example dsl_1 -rn1 = @reaction_network begin - (1.0,(1.0,2.0)), S <--> (P1,P2) -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S --> P1 - 1.0, S --> P2 - 1.0, P1 --> S - 2.0, P2 --> S -end -``` - -## Variable reaction rates -Reaction rates do not need to be a single parameter or a number, but can also be -expressions depending on time or the current amounts of system species (when, for -example, one species can activate the production of another). For instance, this -is a valid notation: -```@example dsl_1 -rn = @reaction_network begin - 1.0, X --> ∅ - k*X, Y --> ∅ -end -``` -corresponding to the ODE model -```@example dsl_1 -convert(ODESystem,rn) -``` - -With respect to the corresponding mass action ODE model, this is actually -equivalent to the reaction system -```@example dsl_1 -rn = @reaction_network begin - 1.0, X --> ∅ - k, X + Y --> X -end -``` -```@example dsl_1 -convert(ODESystem,rn) -``` -!!! note - While the ODE models corresponding to the preceding two reaction systems are - identical, in the latter example the `Reaction` stored in `rn` will be classified as - [`ismassaction`](@ref) while in the former it will not, which can impact optimizations - used in generating `JumpSystem`s. For this reason, it is recommended to use the - latter representation when possible. - -Most expressions and functions are valid reaction rates, e.g.: -```@example dsl_1 -using SpecialFunctions -rn = @reaction_network begin - 2.0*X^2, 0 --> X + Y - t*gamma(Y), X --> ∅ - pi*X/Y, Y --> ∅ -end -``` -where here `t` always denotes Catalyst's time variable. Please note that many -user-defined functions can be called directly, but others will require -registration with Symbolics.jl ([see the faq](@ref user_functions)). - -## Explicit specification of network species and parameters -Recall that the `@reaction_network` macro automatically designates symbols used -in the macro as either parameters or species, with symbols that appear as a -substrate or product being species, and all other symbols becoming parameters -(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default -behavior for a given symbol. E.g one might want something to be considered as a -species, even if it only appears within a rate expression. In the following -network -```@example dsl_1 -rn = @reaction_network begin - k*X, Y --> 0 -end -``` -`X` (as well as `k`) will be considered a parameter. - -By using the `@species` and `@parameters` options within the `@reaction_network` -macro, one can manually declare that specified symbols should be considered a -species or parameter. E.g in: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) Y(t) - k*X, Y --> 0 -end -``` -`X` and `Y` are set as species. Please note that when declaring species using -the `@species` option, their dependant variable (almost always `t`) also needs -to be designated. Similarly in -```@example dsl_1 -rn = @reaction_network begin - @parameters k - k*X, Y --> 0 -end -``` -both `X` and `k` will be considered as parameters. It is also possible to use -both options simultaneously, allowing users to fully specify which symbols are -species and/or parameters: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) Y(t) - @parameters k - k*X, Y --> 0 -end -``` -Here, `X` and `Y` are designated as species and `k` as a parameter. - -The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) - k*X, Y --> 0 -end -``` - -Finally, note that the `@species` and `@parameters` options can also be used in -`begin ... end` block form, allowing more formatted lists of species/parameters: -```@example dsl_1 -rn = @reaction_network begin - @parameters begin - d1 - d2 - end - @species begin - X1(t) - X2(t) - end - d2, X2 --> 0 - d1, X1 --> 0 -end -``` -This can be especially useful when declaring default values for clarity of model -specification (see the next section). - -## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) -When using the `@species` and ` @parameters` macros to declare species and/or -parameters, one can also provide default initial conditions for each species and -values for each parameter: -```@example dsl_1 -rn = @reaction_network begin - @species X(t)=1.0 - @parameters p=1.0 d=0.1 - p, 0 --> X - d, X --> ∅ -end -``` -This system can now be simulated without providing initial condition or -parameter vectors to the DifferentialEquations.jl solvers: -```@example dsl_1 -using DifferentialEquations, Plots -u0 = [] -tspan = (0.0, 10.0) -p = [] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -When providing default values, it is possible to do so for only a subset of the -species or parameters, in which case the rest can be specified when constructing -the problem type to solve: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) - @parameters p=1.0 d - p, 0 --> X - d, X --> 0 -end - -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:d => .1] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -Finally, default values can be overridden by passing mapping vectors to the -DifferentialEquations.jl problem being constructed. Only those initial conditions -or parameters for which we want to change their value from the default will need to be passed -```@example dsl_1 -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1] # we change p to 2.0 -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) -It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: -```@example dsl_1 -rn = @reaction_network begin - @parameters X0 - @species X(t)=X0 - p, 0 --> X - d, X --> ∅ -end -``` -We can now simulate the network without providing any initial conditions: -```@example dsl_1 -u0 = [] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1, :X0 => 1.0] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -## Naming the generated `ReactionSystem` -ModelingToolkit uses system names to allow for compositional and hierarchical -models. To specify a name for the generated `ReactionSystem` via the -[`@reaction_network`](@ref) macro, just place the name before `begin`: -```@example dsl_1 -rn = @reaction_network production_degradation begin - p, ∅ --> X - d, X --> ∅ -end -ModelingToolkit.nameof(rn) == :production_degradation -``` - -## Pre-defined functions -Hill functions and a Michaelis-Menten function are pre-defined and can be used -as rate laws. Below, the pair of reactions within `rn1` are equivalent, as are -the pair of reactions within `rn2`: -```@example dsl_1 -rn1 = @reaction_network begin - hill(X,v,K,n), ∅ --> X - v*X^n/(X^n+K^n), ∅ --> X -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - mm(X,v,K), ∅ --> X - v*X/(X+K), ∅ --> X -end -``` -Repressor Hill (`hillr`) and Michaelis-Menten (`mmr`) functions are also -provided: -```@example dsl_1 -rn1 = @reaction_network begin - hillr(X,v,K,n), ∅ --> X - v*K^n/(X^n+K^n), ∅ --> X -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - mmr(X,v,K), ∅ --> X - v*K/(X+K), ∅ --> X -end -``` - -Please see the API [Rate Laws](@ref api_rate_laws) section for more details. - -## Including non-species variables -Non-species state variables can be specified in the DSL using the `@variables` -macro. These are declared similarly to species. For example, -```@example dsl_1 -rn_with_volume = @reaction_network begin - @variables V(t) - k*V, 0 --> A -end -``` -creates a network with one species -```@example dsl_1 -species(rn_with_volume) -``` -and one non-species -```@example dsl_1 -nonspecies(rn_with_volume) -``` -giving two state variables, always internally ordered by species and then -nonspecies: -```@example dsl_1 -states(rn_with_volume) -``` - -`rn_with_volume` could then be extended with constraint equations for how `V(t)` -evolves in time, see the [associated tutorial](@ref constraint_equations). - -## Specifying alternative time variables and/or extra independent variables -While the DSL defaults to allowing `t` as the time variable, one can use the -`@ivs` macro to specify an alternative independent variable. For example, to -make `s` the default time variable one can say -```@example dsl_1 -rn_with_s = @reaction_network begin - @ivs s - @variables V(s) - @species B(s) - k, A + V*B --> C -end -show(stdout, MIME"text/plain"(), rn_with_s) # hide -``` -where we see all states are now functions of `s`. - -Similarly, if one wants states to be functions of more than one independent -variable, for example to encode a spatial problem, one can list more than one -variable, i.e. `@ivs t x y`. Here the first listed independent variable is -always chosen to represent time. For example, -```@example dsl_1 -rn_with_many_ivs = @reaction_network begin - @ivs s x - @variables V1(s) V2(s,x) - @species A(s) B(s,x) - k, V1*A --> V2*B + C -end -show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide -``` -Here again `s` will be the time variable, and any inferred species, `C` in this -case, are made functions of both variables, i.e. `C(s, x)`. - -## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) -The DSL allows Julia variables to be interpolated for the network name, within -rate constant expressions, or for species/stoichiometry within reactions. Using -the lower-level symbolic interface we can then define symbolic variables and -parameters outside of the macro, which can then be used within expressions in -the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) -tutorial for details on the lower-level symbolic interface). For example, -```@example dsl_1 -@parameters k α -@variables t -@species A(t) -spec = A -par = α -rate = k*A -name = :network -rn = @reaction_network $name begin - $rate*B, 2*$spec + $par*B --> $spec + C - end -``` -As the parameters `k` and `α` were pre-defined and appeared via interpolation, -we did not need to declare them within the `@reaction_network` macro, -i.e. they are automatically detected as parameters: -```@example dsl_1 -parameters(rn) -``` -as are the species coming from interpolated variables -```@example dsl_1 -species(rn) -``` -!!! note - When using interpolation, expressions like `2$spec` won't work; the - multiplication symbol must be explicitly included like `2*$spec`. From 0694bb308138e8e9db34b18f5b52d67fefb7bb89 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 30 Jan 2024 20:08:06 -0500 Subject: [PATCH 03/23] save progress --- .../dsl_advanced_options.md | 557 +++++++++++------- 1 file changed, 347 insertions(+), 210 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index 7af4fec0c7..aaf09c0edc 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -4,276 +4,413 @@ Within the Catalyst DSL, each line can represent either *a reaction* or *an opti All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here], with most (but not all) being described in more detail below. - This tutorial will also describe some additional advanced DSL features that does not include using an option. +This tutorial will also describe some additional advanced DSL features that does not include using an option. As a first step, we import Catalyst (which is required to run the tutorial): +```@example dsl_advanced_1 +using Catalyst +``` ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) -Previously, we mentioned that - -### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) - -### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) - -### [Specifying non-species variables](@id dsl_advanced_options_variables) +[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining symbols as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default +behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein $P$ from its inactive ($Pᵢ$) to its active ($Pₐ$) for for is catalysed by an enzyme $E$. Using the most natural description: +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + k*E, Pᵢ --> Pₐ +end +``` +`X` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want $E$ to be considered a species, we can designate this using the `@species` option: +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + @species E(t) + k*E, Pᵢ --> Pₐ +end +``` +!!! note + When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-time dependant species is also possible](@ref ref)). -### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) +Similarly, the `@parameters` option can be used to +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + @parameters k + k*E, Pᵢ --> Pₐ +end +``` +Here, while `k` is explicitly defined as a parameter, not information is provided about $E$. Hence, the default case will be used (setting $E$ to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by an extensive list of all species/parameters, or just a subset. -## [Setting reaction metadata](@id dsl_advanced_options_) +While designating something which would default to a parameter as a species is straightforward, the other direction (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). -## [Naming reaction networks](@id dsl_advanced_options_) +Rather than listing all species/parameters on a single line after the options, a `begin ... end` block can be used (listing one species/parameter on each line). E.g. in the following example we use this notation to explicitly designate all species and parameters of the system: +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + @species begin + E(t) + Pᵢ(t) + Pₐ(t) + end + @parameters begin + k + end + k*E, Pᵢ --> Pₐ +end +``` -## [Creating observables](@id dsl_advanced_options_) +A side-effect of using the `@species` and `@parameter` options is that they specify *the order in which the species and parameters are stored*. I.e. lets check the order of the parameters in the parameters in the following dimerisation model: +```@example dsl_advanced_1 +dimerisation = @reaction_network begin + (p,d), 0 <--> X + (kB,kD), 2X <--> X2 +end +parameters(dimerisation) +``` +The default order is typically equal to the order with which the parameters (or species) are encountered in the DSL (this is, however, not guaranteed). If we specify the parameters using `@parameters`, the order used within the option is used instead: +```@example dsl_advanced_1 +dimerisation = @reaction_network begin + @parameters kB kD p d + (p,d), 0 <--> X + (kB,kD), 2X <--> X2 +end +parameters(dimerisation) +``` +!!! danger + Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary. -## [Creating events](@id dsl_advanced_options_) +The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either system, that knowledge can be transferred to the other one. -## [Specifying non-time independent variables](@id dsl_advanced_options_) +Generally, there are dour main reasons for specifying species/parameters using the `@species` and `@parameters` option: +1. To designate a symbol, that would otherwise have defaulted to a parameter, as a species. +2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). +3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). +1. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) +!!!! warn + Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *symbols that appear in reactions*. Until now this has not been relevant. However, this tutorial will demosntrate use cases where species/parameters, that are not part of reactions, are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). -## Explicit specification of network species and parameters -Recall that the `@reaction_network` macro automatically designates symbols used -in the macro as either parameters or species, with symbols that appear as a -substrate or product being species, and all other symbols becoming parameters -(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default -behavior for a given symbol. E.g one might want something to be considered as a -species, even if it only appears within a rate expression. In the following -network -```@example dsl_1 +### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) +When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` and the desired default value). E.g here we set $X$'s default initial condition value to $1.0$, and $p$ and $d$'s default values to $1.0$ and $0.2$, respectively: +```@example dsl_advanced_defaults +using Catalyst # hide rn = @reaction_network begin - k*X, Y --> 0 + @species X(t)=1.0 + @parameters p=1.0 d=0.1 + (p,d), 0 <--> X end ``` -`X` (as well as `k`) will be considered a parameter. - -By using the `@species` and `@parameters` options within the `@reaction_network` -macro, one can manually declare that specified symbols should be considered a -species or parameter. E.g in: -```@example dsl_1 +Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` are set to empty vectors: +```@example dsl_advanced_defaults +using OrdinaryDiffEq, Plots +u0 = [] +tspan = (0.0, 10.0) +p = [] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` +It is still possible to provide values for some (or all) initial conditions/parameters in `u0` and `ps` (in which case these overrides the default values): +```@example dsl_advanced_defaults +u0 = [:X => 4.0] +p = [:d => 0.5] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` +It is also possible to declare a model with default values for only some initial conditions/parameters: +```@example dsl_advanced_defaults +using Catalyst # hide rn = @reaction_network begin - @species X(t) Y(t) - k*X, Y --> 0 + @species X(t)=1.0 + (p,d), 0 <--> X end + +tspan = (0.0, 10.0) +p = [:p => 1.0, :D => 0.2] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) ``` -`X` and `Y` are set as species. Please note that when declaring species using -the `@species` option, their dependant variable (almost always `t`) also needs -to be designated. Similarly in -```@example dsl_1 +API for checking the default values of a species or parameters can be found [here](@ref ref). + +### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) +In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of $X$ using the parameter $X₀$. +```@example dsl_advanced_defaults rn = @reaction_network begin - @parameters k - k*X, Y --> 0 + @species X(t)=X₀ + @parameters X₀ + (p,d), 0 <--> X end ``` -both `X` and `k` will be considered as parameters. It is also possible to use -both options simultaneously, allowing users to fully specify which symbols are -species and/or parameters: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) Y(t) - @parameters k - k*X, Y --> 0 +Please note that as the parameter $X₀$ does not occur as part of any reactions, Catalyst's DSL cannot infer whether it is a species or a parameter. This must hence be explicitly declared. We can now simulate our model while providing $X$'s value through the $X₀$ parameter: +```@example dsl_advanced_defaults +u0 = [] +p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` +It is still possible to designate $X$'s value in `u0`, in which case this overrides the default value. Please note that $X₀$ is still a parameter of the system, and its value must still be designated to simulate the model. +```@example dsl_advanced_defaults +u0 = [:X => 0.5] +p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` + +### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) +Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically ho this is done). Here we will introduce how to set metadata, and describe some common metadata types. + +Whenever a species/parameter is declared using the `@species`/`@parameters` options, it can be followed by a `[]` within which the metadata is given. Each metadata entry consists of teh metadata's name, followed by a `=`, followed by its value. E.g. the `description` metadata allows you to attach a `String` to a species/parameter. Here we create a simple model where we add descriptions to all species and parameters. +```@example dsl_advanced_metadata +using Catalyst # hide +two_state_system = @reaction_network begin + @species Xi(t) [description="The species X's inactive form"] Xa(t) [description="The species X's active form"] + @parameters kA [description="X's activation rate"] kD [description="X's deactivation rate"] + (ka,kD), Xi <--> Xa +end +``` +A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we remove some of the descriptions, and also add a [bounds metadata](@ref ref) to $kA$, +```@example dsl_advanced_metadata +two_state_system = @reaction_network begin + @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD [description="X's deactivation rate"] + (ka,kD), Xi <--> Xa end ``` -Here, `X` and `Y` are designated as species and `k` as a parameter. -The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) - k*X, Y --> 0 +It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kD$'s default value to $1.0$ we use +```@example dsl_advanced_metadata +two_state_system = @reaction_network begin + @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD = 1.0 [description="X's deactivation rate"] + (ka,kD), Xi <--> Xa end ``` -Finally, note that the `@species` and `@parameters` options can also be used in -`begin ... end` block form, allowing more formatted lists of species/parameters: -```@example dsl_1 -rn = @reaction_network begin +When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. the previous example is rewritten as +```@example dsl_advanced_metadata +two_state_system = @reaction_network begin @parameters begin - d1 - d2 + kA, [description="X's activation rate", bound=(0.01,10.0)] + kD = 1.0, [description="X's deactivation rate"] end - @species begin - X1(t) - X2(t) - end - d2, X2 --> 0 - d1, X1 --> 0 + (ka,kD), Xi <--> Xa +end +``` + +Each metadata has its own getter functions. E.g. we can get the description of the parameter $pA$ using `getdescription` (here we use [system indexing](@ref ref) to access the parameter): +```@example dsl_advanced_metadata +getdescription(two_state_system.kA) +``` + +It is not possible for the user to directly designate their own metadata. These have to first be added to Catalyst. Doing so is somewhat involved, and described in detail [here](). A full list of metadata that can be used for species and/or parameters can be found [here](@ref ref). + +### [Designating constant-valued/fixed species-parameters](@id dsl_advanced_options_constant_species) + +Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of a callback]@ref advanced_simulations_callbacks). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species $X$ is converted to $Xᴾ$ at rate $k$. By designating $X$ as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. +```@example dsl_advanced_constant_species +using Catalyst # hide +rn = @reaction_network begin + @parameters X [isconstantspecies=true] + k, X --> Xᴾ +end +``` +We can confirm that $X$ is the only species of the system: +```@example dsl_advanced_constant_species +species(rn) +``` +Here, the produced model is actually identical to if $X$ had simply been put as a parameter in the reaction's rate: +```@example dsl_advanced_constant_species +rn = @reaction_network begin + k*X, 0 --> Xᴾ end ``` -This can be especially useful when declaring default values for clarity of model -specification (see the next section). -## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) -When using the `@species` and ` @parameters` macros to declare species and/or -parameters, one can also provide default initial conditions for each species and -values for each parameter: -```@example dsl_1 +A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is [the Brusselator](https://en.wikipedia.org/wiki/Brusselator). + +### [Specifying non-species variables](@id dsl_advanced_options_variables) +Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of reaction events. When they are converted to ODEs, the species are the variables of the system. However, Catalyst permits the creation of hybrid CRN models. These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it does not participate in reactions). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: +```@example dsl_advanced_variables +using Catalyst # hide rn = @reaction_network begin - @species X(t)=1.0 - @parameters p=1.0 d=0.1 - p, 0 --> X - d, X --> ∅ + @variables V(t) + (p,d), 0 <--> G end ``` -This system can now be simulated without providing initial condition or -parameter vectors to the DifferentialEquations.jl solvers: -```@example dsl_1 -using DifferentialEquations, Plots -u0 = [] +Note that $V$ (like species) is time-dependant, and (like species) must be declared as such when the `@variables` option is used. We can now simulate our model (remembering to provide a value for $V$ as well as $G$): +```@example dsl_advanced_variables +using OrdinaryDiffEq, Plots +u0 = [:G => 0.1, :V => 1.0] tspan = (0.0, 10.0) -p = [] +p = [:p => 1.0, :d => 0.5] oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) +sol = solve(oprob, Tsit5()) plot(sol) ``` +Here, we have not actually described how $V$ interacting with our model, or how its value may change. Primarily variables are declared as part of hybrid CRN/equation modelling, which is described in more detail [here](@ref ref). + +You can set metadata and default initial condition values for variables using the same syntax as used for parameters and species. -When providing default values, it is possible to do so for only a subset of the -species or parameters, in which case the rest can be specified when constructing -the problem type to solve: -```@example dsl_1 +You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknown` function can be used to return both. + +## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) +Reactions can also have metadata. This is described in detail [here](@ref ref). + +## [Naming reaction networks](@id dsl_advanced_options_naming) +Each reaction network model has a name. It can be accessed using the `nameof` function. By default, some generic name is used: +```@example dsl_advanced_names +using Catalyst # hide rn = @reaction_network begin - @species X(t) - @parameters p=1.0 d - p, 0 --> X - d, X --> 0 + (p,d), 0 <--> X end - -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:d => .1] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) +nameof(rn) +``` +A specific name can be given as an argument between the `@reaction_network` and the `begin`. E.g. to name a network `my_network` we can use: +```@example dsl_advanced_names +rn = @reaction_network my_network begin + (p,d), 0 <--> X +end +nameof(rn) ``` -Finally, default values can be overridden by passing mapping vectors to the -DifferentialEquations.jl problem being constructed. Only those initial conditions -or parameters for which we want to change their value from the default will need to be passed -```@example dsl_1 -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1] # we change p to 2.0 -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) +A consequence of generic names being used by default is that networks, even if seemingly identical, by default are not. E.g. +```@example dsl_advanced_names +rn1 = @reaction_network begin + (p,d), 0 <--> X +end +rn2 = @reaction_network begin + (p,d), 0 <--> X +end +nothing # hide +``` +Here, since the networks' names are different: +```@example dsl_advanced_names +nameof(rn1) == nameof(rn2) +``` +they are different +```@example dsl_advanced_names +rn1 == rn2 +``` +By designating the networks to have the same name, however, identity is achieved. +```@example dsl_advanced_names +rn1 = @reaction_network my_network begin + (p,d), 0 <--> X +end +rn2 = @reaction_network my_network begin + (p,d), 0 <--> X +end +rn1 == rn2 ``` -## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) -It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: -```@example dsl_1 +## [Creating observables](@id dsl_advanced_options_observables) + +Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an equation). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first given the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. + +Let us consider a model where two species ($X$ and $Y$) can bind to form a complex ($XY$, which also can dissociate back into $X$ and $Y$). If we wish to create a representation for the total amount of $X$ and $Y$ in the system, we can do this by creating observables $Xtot$ and $Ytot$: +```@example dsl_advanced_observables +using Catalyst # hide rn = @reaction_network begin - @parameters X0 - @species X(t)=X0 - p, 0 --> X - d, X --> ∅ + @observables begin + Xtot ~ X + XY + Ytot ~ Y + XY + end + (kB,kD), X + Y <--> XY end ``` -We can now simulate the network without providing any initial conditions: -```@example dsl_1 -u0 = [] +We can now simulate our model using normal syntax (initial condition values for observables should not, and can not, be provided): +```@example dsl_advanced_observables +using OrdinaryDiffEq +u0 = [:X => 1.0, :Y => 2.0, :XY => 0.0] tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1, :X0 => 1.0] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) +ps = [:kB => 1.0, :kD => 1.5] +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(oprob, Tsit5()) +nothing # hide ``` -## Naming the generated `ReactionSystem` -ModelingToolkit uses system names to allow for compositional and hierarchical -models. To specify a name for the generated `ReactionSystem` via the -[`@reaction_network`](@ref) macro, just place the name before `begin`: -```@example dsl_1 -rn = @reaction_network production_degradation begin - p, ∅ --> X - d, X --> ∅ +Next, we can use [symbolic indexing](@ref ref) of our solution object, but with the observable as input. E.g. we can use +```@example dsl_advanced_observables +sol[:Xtot] +``` +to get a vector with teh value of $Xtot$ throughout the simulation. We can also use +```@example dsl_advanced_observables +using Plots +plot(sol; idxs = [:Xtot, :Ytot]) +``` +to plot the observables (rather than the species). + +Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex $Xn$ containing $n$ copies of $X$. Here, we create an observable describing the total number of $X$ molecules in the system: +```@example dsl_advanced_observables +using Catalyst # hide +rn = @reaction_network begin + @observables Xtot ~ X + n*XnXY + (kB,kD), n*X <--> Xn end -ModelingToolkit.nameof(rn) == :production_degradation +nothing # hide ``` +Note that, since we only have a single observable, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. -## Including non-species variables -Non-species state variables can be specified in the DSL using the `@variables` -macro. These are declared similarly to species. For example, -```@example dsl_1 -rn_with_volume = @reaction_network begin - @variables V(t) - k*V, 0 --> A +[Metadata](@ref ref) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref ref) to our observable we can do +```@example dsl_advanced_observables +using Catalyst # hide +rn = @reaction_network begin + @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY + (kB,kD), n*X <--> Xn end +nothing # hide ``` -creates a network with one species -```@example dsl_1 -species(rn_with_volume) -``` -and one non-species -```@example dsl_1 -nonspecies(rn_with_volume) -``` -giving two state variables, always internally ordered by species and then -nonspecies: -```@example dsl_1 -states(rn_with_volume) -``` - -`rn_with_volume` could then be extended with constraint equations for how `V(t)` -evolves in time, see the [associated tutorial](@ref constraint_equations). - -## Specifying alternative time variables and/or extra independent variables -While the DSL defaults to allowing `t` as the time variable, one can use the -`@ivs` macro to specify an alternative independent variable. For example, to -make `s` the default time variable one can say -```@example dsl_1 -rn_with_s = @reaction_network begin - @ivs s - @variables V(s) - @species B(s) - k, A + V*B --> C + +Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here $Xtot$ becomes a species: +```@example dsl_advanced_observables +using Catalyst # hide +rn = @reaction_network begin + @species Xtot(t) + @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY + (kB,kD), n*X <--> Xn end -show(stdout, MIME"text/plain"(), rn_with_s) # hide -``` -where we see all states are now functions of `s`. - -Similarly, if one wants states to be functions of more than one independent -variable, for example to encode a spatial problem, one can list more than one -variable, i.e. `@ivs t x y`. Here the first listed independent variable is -always chosen to represent time. For example, -```@example dsl_1 -rn_with_many_ivs = @reaction_network begin - @ivs s x - @variables V1(s) V2(s,x) - @species A(s) B(s,x) - k, V1*A --> V2*B + C +nothing # hide +``` + +Some final notes regarding observables: +- The right-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). +- All symbols appearing on the left-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). +- Observables may not depend on other observables. +- Observables have their [dependent variables](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. + +## [Creating events](@id dsl_advanced_options_events) + +## [Specifying non-time independent variables](@id dsl_advanced_options_ivs) + +As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on on time independent variable, and potentially one or more spatial independent variables. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using teh `@ivs` option. E.g. to use `s` instead of `t` we can use +```@example dsl_advanced_ivs +using Catalyst # hide +rn = @reaction_network my_network begin + @ivs s + (ka,kD), Xi <--> Xa end -show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide -``` -Here again `s` will be the time variable, and any inferred species, `C` in this -case, are made functions of both variables, i.e. `C(s, x)`. - -## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) -The DSL allows Julia variables to be interpolated for the network name, within -rate constant expressions, or for species/stoichiometry within reactions. Using -the lower-level symbolic interface we can then define symbolic variables and -parameters outside of the macro, which can then be used within expressions in -the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) -tutorial for details on the lower-level symbolic interface). For example, -```@example dsl_1 -@parameters k α -@variables t -@species A(t) -spec = A -par = α -rate = k*A -name = :network -rn = @reaction_network $name begin - $rate*B, 2*$spec + $par*B --> $spec + C - end +nothing # hide +``` +We can confirm that $Xi$ and $Xa$ depend on `s` (and not `t`): +```@example dsl_advanced_ivs +species(rn) ``` -As the parameters `k` and `α` were pre-defined and appeared via interpolation, -we did not need to declare them within the `@reaction_network` macro, -i.e. they are automatically detected as parameters: -```@example dsl_1 -parameters(rn) + +It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default, time, variable, while the following one is considered spatial variables. If we want some species to depend on a non-time independent variable, this has to be explicitly declared: +```@example dsl_advanced_ivs +using Catalyst # hide +rn = @reaction_network my_network begin + @ivs t x + @species Y(s) + (p1,d1), 0 <--> X + (p2,d2), 0 <--> Y +end +species(rn) ``` -as are the species coming from interpolated variables -```@example dsl_1 +Finally, it is possible to have species which depends on several independent variables: +```@example dsl_advanced_ivs +using Catalyst # hide +rn = @reaction_network my_network begin + @ivs t x + @species Xi(t,x) Xa(t,x) + (ka,kD), Xi <--> Xa +end species(rn) ``` !!! note - When using interpolation, expressions like `2$spec` won't work; the - multiplication symbol must be explicitly included like `2*$spec`. + Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying indepdent variables is currently limited. \ No newline at end of file From 030a3a63e9696666db125bfbd142ffb894bf2b8a Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 18 Apr 2024 10:29:52 -0400 Subject: [PATCH 04/23] save progress --- .../dsl_advanced_options.md | 311 +++++++++++++++--- .../catalyst_functionality/dsl_description.md | 150 +++++---- 2 files changed, 337 insertions(+), 124 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index aaf09c0edc..d0edec4458 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -1,8 +1,7 @@ # [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) - Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous tutorial](@ref dsl_description) described how to create reactions. This one will instead describe how to use options. These are typically used to supplant the model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. -All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here], with most (but not all) being described in more detail below. +All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that does not include using an option. As a first step, we import Catalyst (which is required to run the tutorial): ```@example dsl_advanced_1 @@ -11,13 +10,13 @@ using Catalyst ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) [Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining symbols as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default -behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein $P$ from its inactive ($Pᵢ$) to its active ($Pₐ$) for for is catalysed by an enzyme $E$. Using the most natural description: +behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive (`Pᵢ`) to its active (`Pₐ`) is catalysed by an enzyme `E`. Using the most natural description: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ end ``` -`X` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want $E$ to be considered a species, we can designate this using the `@species` option: +`E` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want `E` to be considered a species, we can designate this using the `@species` option: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin @species E(t) @@ -27,16 +26,16 @@ end !!! note When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-time dependant species is also possible](@ref ref)). -Similarly, the `@parameters` option can be used to +Similarly, the `@parameters` option can be used to explicitly designate something as a parameter: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin @parameters k k*E, Pᵢ --> Pₐ end ``` -Here, while `k` is explicitly defined as a parameter, not information is provided about $E$. Hence, the default case will be used (setting $E$ to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by an extensive list of all species/parameters, or just a subset. +Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. -While designating something which would default to a parameter as a species is straightforward, the other direction (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). +While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). Rather than listing all species/parameters on a single line after the options, a `begin ... end` block can be used (listing one species/parameter on each line). E.g. in the following example we use this notation to explicitly designate all species and parameters of the system: ```@example dsl_advanced_1 @@ -71,11 +70,12 @@ end parameters(dimerisation) ``` !!! danger - Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary. + Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary, and also checks the order manually. -The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either system, that knowledge can be transferred to the other one. +!!! note + The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either approach, that knowledge can be transferred to the other one. -Generally, there are dour main reasons for specifying species/parameters using the `@species` and `@parameters` option: +Generally, there are four main reasons for specifying species/parameters using the `@species` and `@parameters` option: 1. To designate a symbol, that would otherwise have defaulted to a parameter, as a species. 2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). 3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). @@ -85,7 +85,7 @@ Generally, there are dour main reasons for specifying species/parameters using t Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *symbols that appear in reactions*. Until now this has not been relevant. However, this tutorial will demosntrate use cases where species/parameters, that are not part of reactions, are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). ### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) -When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` and the desired default value). E.g here we set $X$'s default initial condition value to $1.0$, and $p$ and $d$'s default values to $1.0$ and $0.2$, respectively: +When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` followed by the desired default value). E.g here we set `X`'s default initial condition value to $1.0$, and `p` and `d`'s default values to $1.0$ and $0.2$, respectively: ```@example dsl_advanced_defaults using Catalyst # hide rn = @reaction_network begin @@ -94,7 +94,7 @@ rn = @reaction_network begin (p,d), 0 <--> X end ``` -Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` are set to empty vectors: +Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` can be empty vectors: ```@example dsl_advanced_defaults using OrdinaryDiffEq, Plots u0 = [] @@ -129,7 +129,7 @@ plot(sol) API for checking the default values of a species or parameters can be found [here](@ref ref). ### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) -In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of $X$ using the parameter $X₀$. +In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of `X` using the parameter `X₀`. ```@example dsl_advanced_defaults rn = @reaction_network begin @species X(t)=X₀ @@ -137,7 +137,7 @@ rn = @reaction_network begin (p,d), 0 <--> X end ``` -Please note that as the parameter $X₀$ does not occur as part of any reactions, Catalyst's DSL cannot infer whether it is a species or a parameter. This must hence be explicitly declared. We can now simulate our model while providing $X$'s value through the $X₀$ parameter: +Please note that as the parameter `X₀` does not occur as part of any reactions, Catalyst's DSL cannot infer whether it is a species or a parameter. This must hence be explicitly declared. We can now simulate our model while providing `X`'s value through the `X₀` parameter: ```@example dsl_advanced_defaults u0 = [] p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] @@ -145,7 +145,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -It is still possible to designate $X$'s value in `u0`, in which case this overrides the default value. Please note that $X₀$ is still a parameter of the system, and its value must still be designated to simulate the model. +It is still possible to designate $X$'s value in `u0`, in which case this overrides the default value. ```@example dsl_advanced_defaults u0 = [:X => 0.5] p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] @@ -153,11 +153,12 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` +Please note that `X₀` is still a parameter of the system, and its value must still be designated to simulate the model. ### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) -Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically ho this is done). Here we will introduce how to set metadata, and describe some common metadata types. +Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically how this is done). Here we will introduce how to set metadata, and describe some common metadata types. -Whenever a species/parameter is declared using the `@species`/`@parameters` options, it can be followed by a `[]` within which the metadata is given. Each metadata entry consists of teh metadata's name, followed by a `=`, followed by its value. E.g. the `description` metadata allows you to attach a `String` to a species/parameter. Here we create a simple model where we add descriptions to all species and parameters. +Whenever a species/parameter is declared using the `@species`/`@parameters` options, it can be followed by a `[]` within which the metadata is given. Each metadata entry consists of the metadata's name, followed by a `=`, followed by its value. E.g. the `description` metadata allows you to attach a [`String`](https://docs.julialang.org/en/v1/base/strings/) to a species/parameter. Here we create a simple model where we add descriptions to all species and parameters. ```@example dsl_advanced_metadata using Catalyst # hide two_state_system = @reaction_network begin @@ -166,18 +167,18 @@ two_state_system = @reaction_network begin (ka,kD), Xi <--> Xa end ``` -A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we remove some of the descriptions, and also add a [bounds metadata](@ref ref) to $kA$, +A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we only provide a description for `kA`, for which we also provide a [bounds metadata](@ref https://docs.sciml.ai/ModelingToolkit/dev/basics/Variable_metadata/#Bounds), ```@example dsl_advanced_metadata two_state_system = @reaction_network begin - @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD [description="X's deactivation rate"] + @parameters kA [description="X's activation rate", bound=(0.01,10.0)] (ka,kD), Xi <--> Xa end ``` -It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kD$'s default value to $1.0$ we use +It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kA$'s default value to $1.0$ we use ```@example dsl_advanced_metadata two_state_system = @reaction_network begin - @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD = 1.0 [description="X's deactivation rate"] + @parameters kA=1.0 [description="X's activation rate", bound=(0.01,10.0)] (ka,kD), Xi <--> Xa end ``` @@ -193,7 +194,7 @@ two_state_system = @reaction_network begin end ``` -Each metadata has its own getter functions. E.g. we can get the description of the parameter $pA$ using `getdescription` (here we use [system indexing](@ref ref) to access the parameter): +Each metadata has its own getter functions. E.g. we can get the description of the parameter `kA` using `getdescription` (here we use [system indexing](@ref ref) to access the parameter): ```@example dsl_advanced_metadata getdescription(two_state_system.kA) ``` @@ -202,7 +203,7 @@ It is not possible for the user to directly designate their own metadata. These ### [Designating constant-valued/fixed species-parameters](@id dsl_advanced_options_constant_species) -Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of a callback]@ref advanced_simulations_callbacks). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species $X$ is converted to $Xᴾ$ at rate $k$. By designating $X$ as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. +Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of an event]@ref dsl_advanced_options_events). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species `X` is converted to `Xᴾ` at rate `k`. By designating `X` as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. ```@example dsl_advanced_constant_species using Catalyst # hide rn = @reaction_network begin @@ -224,7 +225,7 @@ end A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is [the Brusselator](https://en.wikipedia.org/wiki/Brusselator). ### [Specifying non-species variables](@id dsl_advanced_options_variables) -Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of reaction events. When they are converted to ODEs, the species are the variables of the system. However, Catalyst permits the creation of hybrid CRN models. These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it does not participate in reactions). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: +Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of *reaction events*. When they are converted to ODEs, the species become unknowns of the system. However, Catalyst permits the creation of [hybrid CRN models](@ref ref). These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it cannot plausibly be a reaction reactant). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: ```@example dsl_advanced_variables using Catalyst # hide rn = @reaction_network begin @@ -246,7 +247,7 @@ Here, we have not actually described how $V$ interacting with our model, or how You can set metadata and default initial condition values for variables using the same syntax as used for parameters and species. -You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknown` function can be used to return both. +You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknowns` function retrieves both. ## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) Reactions can also have metadata. This is described in detail [here](@ref ref). @@ -297,11 +298,12 @@ end rn1 == rn2 ``` -## [Creating observables](@id dsl_advanced_options_observables) +Setting names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. -Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an equation). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first given the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. +## [Creating observables](@id dsl_advanced_options_observables) +Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. -Let us consider a model where two species ($X$ and $Y$) can bind to form a complex ($XY$, which also can dissociate back into $X$ and $Y$). If we wish to create a representation for the total amount of $X$ and $Y$ in the system, we can do this by creating observables $Xtot$ and $Ytot$: +Let us consider a model where two species (`X` and `Y`) can bind to form a complex ($XY$, which also can dissociate back into `X` and `Y`). If we wish to create a representation for the total amount of `X` and `Y` in the system, we can do this by creating observables `Xtot` and `Ytot`: ```@example dsl_advanced_observables using Catalyst # hide rn = @reaction_network begin @@ -323,31 +325,30 @@ sol = solve(oprob, Tsit5()) nothing # hide ``` -Next, we can use [symbolic indexing](@ref ref) of our solution object, but with the observable as input. E.g. we can use +Next, we can use [symbolic indexing](@ref simulation_structure_interfacing) of our solution object, but with the observable as input. E.g. we can use ```@example dsl_advanced_observables sol[:Xtot] ``` -to get a vector with teh value of $Xtot$ throughout the simulation. We can also use +to get a vector with `Xtot`'s throughout the simulation. We can also use ```@example dsl_advanced_observables using Plots plot(sol; idxs = [:Xtot, :Ytot]) ``` to plot the observables (rather than the species). -Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex $Xn$ containing $n$ copies of $X$. Here, we create an observable describing the total number of $X$ molecules in the system: +Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: ```@example dsl_advanced_observables -using Catalyst # hide rn = @reaction_network begin - @observables Xtot ~ X + n*XnXY + @observables Xtot ~ X + n*Xn (kB,kD), n*X <--> Xn end nothing # hide ``` -Note that, since we only have a single observable, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. +!!! + If only a single observable is declared, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. -[Metadata](@ref ref) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref ref) to our observable we can do +[Metadata](@ref dsl_advanced_options_species_and_parameters_metadata) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref dsl_advanced_options_species_and_parameters_metadata) to our observable we can use ```@example dsl_advanced_observables -using Catalyst # hide rn = @reaction_network begin @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY (kB,kD), n*X <--> Xn @@ -355,9 +356,8 @@ end nothing # hide ``` -Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here $Xtot$ becomes a species: +Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here `Xtot` becomes a species: ```@example dsl_advanced_observables -using Catalyst # hide rn = @reaction_network begin @species Xtot(t) @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY @@ -367,35 +367,243 @@ nothing # hide ``` Some final notes regarding observables: -- The right-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). -- All symbols appearing on the left-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). +- The left-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). +- All symbols appearing on the right-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). - Observables may not depend on other observables. - Observables have their [dependent variables](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. ## [Creating events](@id dsl_advanced_options_events) +Sometimes one wishes to model events, describing things that can happen to it during a simulation. + - A chemical system where an amount of some species is added at a time point after the simulation's initiation. + - A simulation of a circadian rhythm, where light is turned on/off every 12 hours. + - A cell divides when some size variable reaches a certain threshold, halving the amount of each species in the system. + +Events are divided into *continuous* and *discrete* events, and these can be added directly to a system using the `continuous_events` and `discrete_events` options. Events can also be modelled through *callbacks*. These are different in that they are supplied in the simulation step (rather than on system creation), and generally provide more flexibility in how they may affect the system. Callbacks are described on a separate [page](@ref advanced_simulations_callbacks). + +The notation described below for creating continuous and discrete events is the same which is used in [ModelingToolkit to create events](https://docs.sciml.ai/ModelingToolkit/stable/basics/Events/), and which is used for [events for programmatic model creation](@ref ref). + +### [Continuous vs discrete events](@id dsl_advanced_options_events_continuous_vs_discrete) +Both continuous and discrete events combine some condition (for triggering the event) with some affect (describing their effects on the system). They differ in the following ways: +- They use slightly different notation. +- Discrete events' conditions are checked at *the end of* each simulation time step. For continuous events, the simulation instead finds the *exact time point* when the event is triggered at. +- Continuous events cannot be supplied to jump simulations. + +### [Continuous events](@id dsl_advanced_options_events_continuous) +Let us consider a simple system where species `X` degraded at a constant rate `d`. Next, we wish to add an event which adds `2.0` units of `X` whenever `X` reaches a critical threshold `1.0`. This can be done in the following manner: +```@example dsl_advanced_events +using Catalyst # hide +rn = @reaction_network begin + @continuous_events begin + X ~ 1.0 => [X ~ X + 2.0] + end + d, X --> 0 +end +nothing # hide +``` +Here, the `@continuous_events` option is followed by a `begin ... end` block. Next, each line corresponds to a separate event. Each event is created in the following manner: +- It combines a *condition* (denoting when the event will happen) with one (or several) *affects* (denoting what the event does to the system when it happens). +- The condition (left) and affect (right) are separated by a `=>`. +- The condition takes the form of an [equation](). Here, the event is triggered whenever the equation's two sides (separated by a `~`) are equal. +- The affect(s) are enclosed within `[]`. If there are multiple affects, these are separated by `,` (the example above contains a single affect). +- Each affect is a single equation that describes how a parameter, species, or [variable](@ref dsl_advanced_options_variables) is updated when the event is triggered. +- Each affect's equation's left-hand side must contain only the parameter/species/variable whose value should be updated. +- Each affect's equation's right-hand side is an expression describing its updated value. + +We can simulate the model we declared, just like any other model: +```@example dsl_advanced_events +using OrdinaryDiffEq, Plots +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +Inspecting the solution, we can confirm that whenever `X` reaches a value of `1.0`, `2.0` units of `X` is added to the system. + +In our example, we can also denote the critical quantities using parameters: +```@example dsl_advanced_events +rn = @reaction_network begin + @parameters X_thres X_add + @continuous_events begin + X ~ X_thres => [X ~ X + X_add] + end + d, X --> 0 +end +nothing # hide +``` +Here, since `X_thres` and `X_add` do not appear in any reactions, Catalyst cannot determine whether they are parameters or species. hence, they must be [explicitly designated as parameters by using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters). Next, these can be designated as any value, and supplied to the `ODEProblem`: +```@example dsl_advanced_events +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0, :X_thres => 0.5, :X_add => 3.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` + +As previously noted, each continuous event can have multiple affects. The following system has two components (`X` and `Y`, one being produced and one being degraded). When their concentrations are equal, a continuous events reduce the concentration of `X` while increasing the concentration of `Y`: +```@example dsl_advanced_events +rn = @reaction_network begin + @continuous_events begin + X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] + end + p, 0 --> X + d, Y --> 0 +end + +u0 = [:X => 1.0, :Y => 3.0] +tspan = (0.0, 10.0) +ps = [:p => 1.0, :d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` + +!!!warn + A single event (continuous or discrete) can update the value of either (one or several) species (and variables), or of (one or several) parameters. It is not possible for an event to update the values of both species/variables and parameters. + +In the above examples we have modelled a system with a single event. In these cases, the `begin end` block is not required, and the event can be provided on the same line as the `@continuous_events` option: +```@example dsl_advanced_events +rn = @reaction_network begin + @continuous_events X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] + p, 0 --> X + d, Y --> 0 +end +nothing # hide +``` + +### [Discrete events](@id dsl_advanced_options_events_discrete) +Just like [continuous events](dsl_advanced_options_events_continuous), discrete events combine a condition with one or more affect statements. Here, discrete events' affects are created identically to those for continuous events. Discrete events' conditions are different. There exist 3 different types of discrete events, each with a different type of condition. All three types are created using the `@discrete_events` option, and a single system can contain a mix of all types. The three types are: +- Preset-time discrete events. +- Periodic discrete events. +- Conditional discrete events. + +#### [Preset-time discrete events](@id dsl_advanced_options_events_discrete_presettime) +*Present-time events* are events that happen at specific time points. Here, the condition is a vector with all the time points at which an event is triggered. E.g. here we create a production/degradation loop, where `2.0` units of `X` is added at time points `3.0` and `7.0` +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + [3.0, 7.0] => [X ~ X + 2.0] + end + (p,d), 0 <--> X +end + +u0 = [:X => 0.1, :Y => 3.0] +tspan = (0.0, 10.0) +ps = [:p => 1.0, :d => 0.5] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` + +The preset time points can also be parameters (in which case, they have to be [designated as such using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters)): +```@example dsl_advanced_events +rn = @reaction_network begin + @parameters t1 t2 + @discrete_events begin + [t1, t2] => [X ~ X + 2.0] + end + (p,d), 0 <--> X +end +nothing +``` + +#### [Periodic discrete events](@id dsl_advanced_options_events_discrete_periodic) +When a discrete event's condition is a vector, a preset-time event is created. If it instead is a single value, a *periodic event* is created. These occur repeatedly throughout a simulation, with its period set by the affect term. E.g. here we create a system where `0.5` units of `X` is added every `1.0` time units. +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + 1.0 => [X ~ X + 0.5] + end + d, X --> 0 +end + +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +Like for preset-time events, periodic events' affects may contain parameters. + +#### [Conditional discrete events](@id dsl_advanced_options_events_discrete_conditional) +Finally, discrete events' condition may be a boolean expression (consisting of parameters, species, variables, and the time variable). Let's say that we want to create an event which, if the concentration of `X` is below a threshold `1.0`, adds `1.0` units of `X` to the system, then we can use the following discrete event: +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + X < 1.0 => [X ~ X + 2.0] + end + d, X --> 0 +end +``` +If we simulate the system using the same conditions as for our [similar, continuous, example](@ref dsl_advanced_options_events_continuous) we get the same result: +```@example dsl_advanced_events +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +So, how is modelling this event as a discrete or continuous event different? There are four differences: +1) For continuous events, the simulation method finds the exact time point when the condition triggers. Discrete events are triggered at the first time step when the condition holds. +2) This discrete event will be triggered whenever `X < 1.0` holds, not just when the concentration of `X` passes the threshold. E.g. it will be triggered if the initial concentration of `X` is less than `1.0`. +3) Only the discrete event event can be used with jump simulations. +4) The discrete event can be used to create more advanced conditions. + +E.g. using (4), we can modify our system so that the event is only triggered when time is less than `5.0` (after which `X` decays towards `0`): +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + (X < 1.0) & (t < 5.0) => [X ~ X + 2.0] + end + d, X --> 0 +end + +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +!!!note + When we form composite boolean conditions for conditional discrete events, we use `&` to denote the AND operator (not `&&`, as this is currently not supported). + +!!!warn + Generally, discrete events including equality (`==`) will not be triggered. The reason is that the condition is only checked at the end of every time step. Hence, unless special precautions are taken to ensure that the simulator stops when the condition holds, it will unlikely be triggered. + ## [Specifying non-time independent variables](@id dsl_advanced_options_ivs) -As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on on time independent variable, and potentially one or more spatial independent variables. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using teh `@ivs` option. E.g. to use `s` instead of `t` we can use +As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on a *time independent variable*, and potentially one or more *spatial independent variables*. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using the `@ivs` option. E.g. to use `τ` instead of `t` we can use ```@example dsl_advanced_ivs using Catalyst # hide -rn = @reaction_network my_network begin - @ivs s +rn = @reaction_network begin + @ivs τ (ka,kD), Xi <--> Xa end nothing # hide ``` -We can confirm that $Xi$ and $Xa$ depend on `s` (and not `t`): +We can confirm that `Xi` and `Xa` depend on `τ` (and not `t`): ```@example dsl_advanced_ivs species(rn) ``` -It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default, time, variable, while the following one is considered spatial variables. If we want some species to depend on a non-time independent variable, this has to be explicitly declared: +It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default (time) independent variable, while the following one is considered spatial independent variables. If we want some species to depend on a non-default independent variable, this has to be explicitly declared: ```@example dsl_advanced_ivs -using Catalyst # hide -rn = @reaction_network my_network begin - @ivs t x - @species Y(s) +rn = @reaction_network begin + @ivs τ x + @species X(x) Y(x) (p1,d1), 0 <--> X (p2,d2), 0 <--> Y end @@ -403,8 +611,7 @@ species(rn) ``` Finally, it is possible to have species which depends on several independent variables: ```@example dsl_advanced_ivs -using Catalyst # hide -rn = @reaction_network my_network begin +rn = @reaction_network begin @ivs t x @species Xi(t,x) Xa(t,x) (ka,kD), Xi <--> Xa @@ -413,4 +620,4 @@ species(rn) ``` !!! note - Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying indepdent variables is currently limited. \ No newline at end of file + Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying spatial independent variables is currently limited. \ No newline at end of file diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index c70d6a259c..2819704642 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -1,15 +1,22 @@ # [The Catalyst DSL - Introduction](@id dsl_description) -In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` macro can be used to create chemical reaction network models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating chemical reaction network (CRN) models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSLs more advanced features. +In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSL's more advanced features. -The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial wil solely focus on options for model creation. +The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction) or [compositionally](@ref compositional_modeling)). A summary of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial will solely focus on model creation. -Before we begin the tutorial, we will first load the `Catalyst` package (which is required to run the code). +Before we begin, we will first load the `Catalyst` package (which is required to run the code). ```@example dsl_1 using Catalyst ``` -### Quick-start summary - +### [Quick-start summary](@id dsl_description_quick_start) +The DSL is initiated through the `@reaction_network`, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a Michaelis-Menten enzyme kinetics system can be written as +```@example dsl_0 +rn = @reaction_network begin + (kB,kC), S + E <--> SE + kP, SE --> P + E +end +``` +Here, `<-->` is used to create a bi-directional reaction (with forward rate `kP` and backward rate `kD`). Next, the model (stored in the variable `rn`) can be used as input to various types of [simulations](@ref ref). ## [Basic syntax](@id dsl_description_basic_syntax) The basic syntax of the DSL is @@ -19,12 +26,12 @@ rn = @reaction_network begin 1.0, Y --> X end ``` -Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with an `end`. Each reaction consists of -- A rate -- A (potentially empty) set of substrates. -- A (potentially empty) set of products. +Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with `end`. Each reaction consists of +- A *rate*. +- A (potentially empty) set of *substrates*. +- A (potentially empty) set of *products*. -Each reaction line declares, in order, the rate, the substrate(s), and the products. The rate is separated from the substrate(s) by a `,`, ad the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. +Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). @@ -49,15 +56,15 @@ Generally, anything that is a [permitted Julia variable name](@id https://docs.j ## [Different types of reactions](@id dsl_description_reactions) -### Reactions with multiple substrate(s) or product(s) -Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` binds (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: +### [Reactions with multiple substrates or products](@id dsl_description_reactions_multiples) +Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: ```@example dsl_1 rn2 = @reaction_network begin kB, X + Y --> XY kD, XY --> X + Y end ``` -Reactions can have any number of substrates and products, and their names does not need to have any relationship to each other, as demonstrated by the following mock-model: +Reactions can have any number of substrates and products, and their names do not need to have any relationship to each other, as demonstrated by the following mock model: ```@example dsl_1 rn3 = @reaction_network begin k, X + Y + Z --> A + B + C + D @@ -65,7 +72,7 @@ end ``` ### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) -Some reactions has no products, in which case the substrate(s) are degraded (ie.e removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions has no substrate(s), in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species $X$ is both created and degraded, we use: +Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created and degraded, we use: ```@example dsl_1 rn4 = @reaction_network begin p, 0 --> X @@ -73,17 +80,17 @@ rn4 = @reaction_network begin end ``` -### Reactions with non-unitary stoichiometries -Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which may dissociate back to two `X` copies) we use: +### [Reactions with non-unitary stoichiometries](@id dsl_description_reactions_stoichiometries) +Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: ```@example dsl_1 rn5 = @reaction_network begin kB, 2X --> X2 kD, X2 --> 2X end ``` -Reactant which stoichiometry is not defined is assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affects the created model can be found [here](@ref ref). +Reactants whose stoichiometries are not defined are assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affecting the created model can be found [here](@ref ref). -Stoichiometries can be combined with `( )` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: +Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: ```@example dsl_1 rn6 = @reaction_network begin k, 2X + 3(Y + 2Z) --> 5(V + W) @@ -94,7 +101,7 @@ nothing # hide ## [Bundling of similar reactions](@id dsl_description_reaction_bundling) -### Bi-directional (or reversible) reactions +### [Bi-directional (or reversible) reactions](@id dsl_description_reaction_bundling_reversible) As is the case for the following two-state model: ```@example dsl_1 rn7 = @reaction_network begin @@ -102,7 +109,7 @@ rn7 = @reaction_network begin k2, X2 --> X1 end ``` -it is common that reactions occurs in both direction. Here, it is possible ot bundle the reactions into a single line by using the `<-->` arrow. When we do this, we the rate term includes bother the rates (enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: +it is common that reactions occur in both directions (so-called *bi-directional* reactions). Here, it is possible to bundle the reactions into a single line by using the `<-->` arrow. When we do this, the rate term must include two separate rates (one for each direction, these are enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: ```@example dsl_1 rn7 = @reaction_network begin (k1,k2), X1 <--> X2 @@ -110,13 +117,13 @@ end ``` Here, the first rate (`k1`) denotes the *forward rate* and the second rate (`k2`) the *backwards rate*. -Catalyst also permits writing backwards reactions only. This uses the identical syntax to when forward reactions are created, but using the `<--` arrow: +Catalyst also permits writing pure backwards reactions. These use identical syntax to forward reactions, but with the `<--` arrow: ```@example dsl_1 rn8 = @reaction_network begin k, X <-- Y end ``` -Here, the substrate(s) are on the right-hand side and the product(s) on the left-hand side. Hence, the above model can be written identically using: +Here, the substrate(s) are on the right-hand side and the product(s) are on the left-hand side. Hence, the above model can be written identically using: ```@example dsl_1 rn8 = @reaction_network begin k, Y --> X @@ -124,35 +131,35 @@ end ``` Generally, using forward reactions is clearer than backwards ones, with the letter generally not being used. -### Bundling similar reactions on a singe line -There exists several other situation where models contain similar reactions (e.g. the systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following mode, where species $X$ and $Y$ both degrades at the rate $d$: +### [Bundling similar reactions on a single line](@id dsl_description_reaction_bundling_similar) +There exist several other situations where models contain similar reactions (e.g. systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model, where species `X` and `Y` both degrade at the rate `d`: ```@example dsl_1 rn8 = @reaction_network begin d, X --> 0 d, Y --> 0 end ``` -These share both the rates and products, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression. However, we have to provide two separate substrate expressions: +These share both the rates and product, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: ```@example dsl_1 rn8 = @reaction_network begin d, (X,Y) --> 0 end ``` -This declaration of the model is identical to the one previously.Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, does not make sense to use). I.e. if the tw reactions had different degradation rates: +This declaration of the model is identical to the one previously. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: ```@example dsl_1 rn9 = @reaction_network begin dX, X --> 0 dY, Y --> 0 end ``` -we could represent this using: +This can be represented using: ```@example dsl_1 rn9 = @reaction_network begin (dX,dY), (X,Y) --> 0 end ``` -It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions uses the same rate $k$): +It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions use the same rate $k$): ```@example dsl_1 rn10 = @reaction_network begin k, (X0,X1,X2,X3) --> (X1,X2,X3,X4) @@ -185,27 +192,27 @@ However, as for the above model, bundling reactions too zealously can reduce (ra ## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. -Let us consider a model with an activator ($A$, which degraded at a constant rate) and a protein ($P$). The production rate of $P$ depends both on $A$ and parameter ($kp$). We model this through: +Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on $A$ and a parameter (`kP`). We model this through: ```@example dsl_1 rn_13 = @reaction_network begin d, A --> 0 - kp*A, 0 --> P + kP*A, 0 --> P end ``` -Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`](@ref ref): +Here, `P`'s production rate will decay as `A` is slowly removed from the system. We can [print the ODE this model produces by using `Latexify`](@ref ref): ```@example dsl_1 using Latexify latexify(rn_13; form=:ode) ``` -In this case, we can generate an equivalent model by instead adding $A$ as both a substrate and a product to $A$'s production reaction: +In this case, we can generate an equivalent model by instead adding `A` as both a substrate and a product to `P`'s production reaction: ```@example dsl_1 rn_13_alt = @reaction_network begin d, A --> 0 kp, A --> A + P end ``` -We can confirm that this generate the same ODE: +We can confirm that this generates the same ODE: ```@example dsl_1 latexify(rn_13_alt; form=:ode) ``` @@ -214,7 +221,7 @@ Here, while the model will generate the same ODE, SDE, and jump simulations, the While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). !!! danger - Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This mean that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). + Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: ```@example dsl_1 @@ -225,25 +232,8 @@ rn_14 = @reaction_network begin end ``` -### Time-dependant rates -Previously we have assumed that that the rates are independent on the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progress, we can use: -```@example dsl_1 -rn_14 = @reaction_network begin - kp/t, 0 --> P - d, P --> 0 -end -``` - -Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to representing a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: -```@example dsl_1 -rn_15 = @reaction_network begin - A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P - d, P --> 0 -end -``` - -### Using functions in rates -It is possible for the rate to use Julia function. These can either be functions from Julia's standard library: +### [Using functions in rates](@id dsl_description_nonconstant_rates_functions) +It is possible for the rate to contain Julia function. These can either be functions from Julia's standard library: ```@example dsl_1 rn_16 = @reaction_network begin d, A --> 0 @@ -252,15 +242,15 @@ end ``` or ones defined by the user: ```@example dsl_1 -custom_function(p1,p2,X) = (p1+E)/(p2+E) +custom_function(p1, p2, X) = (p1 + X) / (p2 + X) rn_17 = @reaction_network begin d, A --> 0 custom_function(k1,k2,E), 0 --> P end ``` -### Using pre-declared Michaelis-Menten or Hill function rate -Two function frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where $X$ activates its own production through a hill function can be created using: +### (@id dsl_description_nonconstant_rates_available_functions) +Two functions frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where `X` activates its own production through a Hill function can be created using: ```@example dsl_1 custom_function(p1,p2,X) = (p1+E)/(p2+E) rn_18 = @reaction_network begin @@ -270,18 +260,34 @@ end ``` Catalyst comes with the following predefined functions: -- The Michaelis-Menten function: $mm(X,v,K) = v*X/(X + K)$. -- The repressive Michaelis-Menten function: $mmr(X,v,K) = v*K/(X + K)$. -- The Hill function: $hill(X,v,K,n) = v*(X^n)/(X^n + K^n)$. -- The repressive Hill function: $hillr(X,v,K,n) = v*(K^n)/(X^n + K^n)$. -- The activating/repressive Hill function: $hillar(X,Y,v,K,n) = v*(X^n)/(X^n + Y^n + K^n)$. +- The Michaelis-Menten function: $mm(X,v,K) = v * X/(X + K)$. +- The repressive Michaelis-Menten function: $mmr(X,v,K) = v * K/(X + K)$. +- The Hill function: $hill(X,v,K,n) = v * (X^n)/(X^n + K^n)$. +- The repressive Hill function: $hillr(X,v,K,n) = v * (K^n)/(X^n + K^n)$. +- The activating/repressive Hill function: $hillar(X,Y,v,K,n) = v * (X^n)/(X^n + Y^n + K^n)$. +### [Time-dependant rates](@id dsl_description_nonconstant_rates_time) +Previously we have assumed that the rates are independent of the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progresses, we can use: +```@example dsl_1 +rn_14 = @reaction_network begin + kp/(1 + t), 0 --> P + d, P --> 0 +end +``` + +Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to represent a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: +```@example dsl_1 +rn_15 = @reaction_network begin + A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P + d, P --> 0 +end +``` ## [Using special symbols](@id dsl_description_symbols) -Julia permits any unicode characters to be used in variable names, thus Catalyst are able to use these as well. Below we describe some case where these to alter the appearance of models. No functionality is, however, tied to this. +Julia permits any Unicode characters to be used in variable names, thus Catalyst can use these as well. Below we describe some cases where this can be useful. No functionality is, however, tied to this. -### [Using ∅ to denote degradation/production reactions](@id dsl_description_symbols_empty_set) -Previously, we described how `0` could be used to [denote degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system alternatively can be written as: +### [Using ∅ in degradation/production reactions](@id dsl_description_symbols_empty_set) +Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system can alternatively be written as: ```@example dsl_1 rn4 = @reaction_network begin p, ∅ --> X @@ -289,8 +295,8 @@ rn4 = @reaction_network begin end ``` -### Using special arrow symbols -Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representation of these arrows are available. Here, +### [Using special arrow symbols](@id dsl_description_symbols_arrows) +Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representations of these arrows are available. Here, - `>`, `→`, `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, and `⇁` can be used to represent forward reactions. - `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent reversible reactions. - `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. @@ -303,14 +309,14 @@ rn4 = @reaction_network begin end ``` -### Using special symbols to denote species or parameters +### [Using special symbols to denote species or parameters](@id dsl_description_symbols_special) A range of possible characters are available which can be incorporated into species and parameter names. This includes, but is not limited to: - Greek letters (e.g `α`, `σ`, `τ`, and `Ω`). -- Superscript and subscript characters (to create e.g `k₁`, `k₂`, `Xₐ`, and `Xᴾ`). +- Superscript and subscript characters (to create e.g. `k₁`, `k₂`, `Xₐ`, and `Xᴾ`). - Non-latin, non-greek, letters (e.g. `ä`, `Д`, `س`, and `א`). - Other symbols (e.g. `£`, `ℂ`, `▲`, and `♠`). -An example of how this can be used to create neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used it the creation a model of the sigma factor V circuit in the bacteria *Bacillus subtilis*: +An example of how this can be used to create a neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used to model a sigma factor V circuit in the bacteria *Bacillus subtilis*: ```@example dsl_1 σᵛ_model = @reaction_network begin v₀ + hill(σᵛ,v,K,n), ∅ → σᵛ + A @@ -321,7 +327,7 @@ end nothing # hide ``` -This functionality can also be used to create less-serious models: +This functionality can also be used to create less serious models: rn_13 = @reaction_network begin 🍦, 😢 --> 😃 end @@ -329,10 +335,10 @@ end It should be noted that the following symbols are *not permitted* to be used as species or parameter names: - `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)) - `pi` and `π` (used in Julia to denote [`3.1415926535897...`](https://en.wikipedia.org/wiki/Pi)). -- `t` (used to denote the [time variable](@ref ref)). +- `t` (used to denote the [time variable](@ref dsl_description_nonconstant_rates_time)). - `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)) - `im` (used in Julia to represent [complex numbers](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/#Complex-Numbers)). -- `nothing` ([used in Julia](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). +- `nothing` (used in Julia to denote [nothing](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). - `Γ` (used by Catalyst to represent [conserved quantities](@ref ref)). From bc4929bc540d7739df91d212b38086fb4ac62511 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 26 Apr 2024 17:30:19 -0400 Subject: [PATCH 05/23] save progress --- .../dsl_advanced_options.md | 85 ++++++++++--------- .../catalyst_functionality/dsl_description.md | 64 +++++++------- 2 files changed, 73 insertions(+), 76 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index d0edec4458..7afb7a6427 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -1,30 +1,34 @@ # [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) -Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous tutorial](@ref dsl_description) described how to create reactions. This one will instead describe how to use options. These are typically used to supplant the model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. +Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous DSL tutorial](@ref dsl_description) described how to create reactions. This one will focus on options. These are typically used to supplant a model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. -All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. +All options designations begins with declaration starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each use of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that does not directly include using an option. -This tutorial will also describe some additional advanced DSL features that does not include using an option. As a first step, we import Catalyst (which is required to run the tutorial): +As a first step, we import Catalyst (which is required to run the tutorial): ```@example dsl_advanced_1 using Catalyst ``` ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) -[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining symbols as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default -behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive (`Pᵢ`) to its active (`Pₐ`) is catalysed by an enzyme `E`. Using the most natural description: +[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining quantities as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive form (`Pᵢ`) to its active form (`Pₐ`) is catalysed by an enzyme (`E`). Using the most natural description: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ end ``` -`E` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want `E` to be considered a species, we can designate this using the `@species` option: +`E` (as well as `k`) will be considered a parameter, something we can confirm directly: +```@example dsl_advanced_1 +parameters(catalysis_sys) +``` +If we want `E` to be considered a species, we can designate this using the `@species` option: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin @species E(t) k*E, Pᵢ --> Pₐ end +parameters(catalysis_sys) ``` !!! note - When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-time dependant species is also possible](@ref ref)). + When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-`t` dependant species is also possible](@ref ref)). Similarly, the `@parameters` option can be used to explicitly designate something as a parameter: ```@example dsl_advanced_1 @@ -33,7 +37,7 @@ catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ end ``` -Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. +Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a quantity cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). @@ -76,13 +80,13 @@ parameters(dimerisation) The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either approach, that knowledge can be transferred to the other one. Generally, there are four main reasons for specifying species/parameters using the `@species` and `@parameters` option: -1. To designate a symbol, that would otherwise have defaulted to a parameter, as a species. +1. To designate a quantity, that would otherwise have defaulted to a parameter, as a species. 2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). 3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). -1. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) +4. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) !!!! warn - Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *symbols that appear in reactions*. Until now this has not been relevant. However, this tutorial will demosntrate use cases where species/parameters, that are not part of reactions, are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). + Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *quantities that appear in reactions*. Until now this has not been relevant. However, this tutorial will demonstrate cases where species/parameters that are not part of reactions are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). ### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` followed by the desired default value). E.g here we set `X`'s default initial condition value to $1.0$, and `p` and `d`'s default values to $1.0$ and $0.2$, respectively: @@ -94,7 +98,7 @@ rn = @reaction_network begin (p,d), 0 <--> X end ``` -Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` can be empty vectors: +Next, if we simulate the model, we do not need to provide values for species or parameters that have default values. In this case all have default values, so both `u0` and `ps` can be empty vectors: ```@example dsl_advanced_defaults using OrdinaryDiffEq, Plots u0 = [] @@ -104,7 +108,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -It is still possible to provide values for some (or all) initial conditions/parameters in `u0` and `ps` (in which case these overrides the default values): +It is still possible to provide values for some (or all) initial conditions/parameters in `u0`/`ps` (in which case these overrides the default values): ```@example dsl_advanced_defaults u0 = [:X => 4.0] p = [:d => 0.5] @@ -126,7 +130,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -API for checking the default values of a species or parameters can be found [here](@ref ref). +API for checking the default values of species and parameters can be found [here](@ref ref). ### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of `X` using the parameter `X₀`. @@ -153,7 +157,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -Please note that `X₀` is still a parameter of the system, and its value must still be designated to simulate the model. +Please note that `X₀` is still a parameter of the system, and as such its value must still be designated to simulate the model (even if it is not actually used). ### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically how this is done). Here we will introduce how to set metadata, and describe some common metadata types. @@ -162,12 +166,12 @@ Whenever a species/parameter is declared using the `@species`/`@parameters` opti ```@example dsl_advanced_metadata using Catalyst # hide two_state_system = @reaction_network begin - @species Xi(t) [description="The species X's inactive form"] Xa(t) [description="The species X's active form"] + @species Xi(t) [description="The X's inactive form"] Xa(t) [description="The X's active form"] @parameters kA [description="X's activation rate"] kD [description="X's deactivation rate"] (ka,kD), Xi <--> Xa end ``` -A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we only provide a description for `kA`, for which we also provide a [bounds metadata](@ref https://docs.sciml.ai/ModelingToolkit/dev/basics/Variable_metadata/#Bounds), +A metadata can be given to only a subset of a system's species/parameters, and a quantity can be given several metadata entries. To give several metadata, separate each by a `,`. Here we only provide a description for `kA`, for which we also provide a [bounds metadata](@ref https://docs.sciml.ai/ModelingToolkit/dev/basics/Variable_metadata/#Bounds), ```@example dsl_advanced_metadata two_state_system = @reaction_network begin @parameters kA [description="X's activation rate", bound=(0.01,10.0)] @@ -183,7 +187,7 @@ two_state_system = @reaction_network begin end ``` -When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. the previous example is rewritten as +When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. a version of the previous example can be written as ```@example dsl_advanced_metadata two_state_system = @reaction_network begin @parameters begin @@ -199,11 +203,11 @@ Each metadata has its own getter functions. E.g. we can get the description of t getdescription(two_state_system.kA) ``` -It is not possible for the user to directly designate their own metadata. These have to first be added to Catalyst. Doing so is somewhat involved, and described in detail [here](). A full list of metadata that can be used for species and/or parameters can be found [here](@ref ref). +It is not possible for the user to directly designate their own metadata. These have to first be added to Catalyst. Doing so is somewhat involved, and described in detail [here](@ref ref). A full list of metadata that can be used for species and/or parameters can be found [here](@ref ref). -### [Designating constant-valued/fixed species-parameters](@id dsl_advanced_options_constant_species) +### [Designating constant-valued/fixed species parameters](@id dsl_advanced_options_constant_species) -Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of an event]@ref dsl_advanced_options_events). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species `X` is converted to `Xᴾ` at rate `k`. By designating `X` as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. +Catalyst enables the designation of parameters as `constantspecies`. These parameters can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of an event]@ref ref). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species `X` is converted to `Xᴾ` at rate `k`. By designating `X` as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. ```@example dsl_advanced_constant_species using Catalyst # hide rn = @reaction_network begin @@ -211,20 +215,21 @@ rn = @reaction_network begin k, X --> Xᴾ end ``` -We can confirm that $X$ is the only species of the system: +We can confirm that $Xᴾ$ is the only species of the system: ```@example dsl_advanced_constant_species species(rn) ``` -Here, the produced model is actually identical to if $X$ had simply been put as a parameter in the reaction's rate: +Here, the produced model is actually identical to if $X$ had simply been a parameter in the reaction's rate: ```@example dsl_advanced_constant_species rn = @reaction_network begin k*X, 0 --> Xᴾ end ``` -A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is [the Brusselator](https://en.wikipedia.org/wiki/Brusselator). +A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is the [Brusselator](https://en.wikipedia.org/wiki/Brusselator). ### [Specifying non-species variables](@id dsl_advanced_options_variables) +--- MOVE THIS TO HYBRID SECTION --- Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of *reaction events*. When they are converted to ODEs, the species become unknowns of the system. However, Catalyst permits the creation of [hybrid CRN models](@ref ref). These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it cannot plausibly be a reaction reactant). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: ```@example dsl_advanced_variables using Catalyst # hide @@ -250,6 +255,7 @@ You can set metadata and default initial condition values for variables using th You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknowns` function retrieves both. ## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) +--- DISCUSS WHAT TO DO WITH THIS SECTION --- Reactions can also have metadata. This is described in detail [here](@ref ref). ## [Naming reaction networks](@id dsl_advanced_options_naming) @@ -277,16 +283,12 @@ end rn2 = @reaction_network begin (p,d), 0 <--> X end -nothing # hide +rn1 == rn2 ``` -Here, since the networks' names are different: +The reason can be confirmed by checking that their respective (randomly generated) names are different: ```@example dsl_advanced_names nameof(rn1) == nameof(rn2) ``` -they are different -```@example dsl_advanced_names -rn1 == rn2 -``` By designating the networks to have the same name, however, identity is achieved. ```@example dsl_advanced_names rn1 = @reaction_network my_network begin @@ -298,12 +300,12 @@ end rn1 == rn2 ``` -Setting names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. +Setting model names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. ## [Creating observables](@id dsl_advanced_options_observables) -Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. +Sometimes one might want to use observable variables. These are variables with values that can be computed directly from a system's state (rather than having their values implicitly given by reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. -Let us consider a model where two species (`X` and `Y`) can bind to form a complex ($XY$, which also can dissociate back into `X` and `Y`). If we wish to create a representation for the total amount of `X` and `Y` in the system, we can do this by creating observables `Xtot` and `Ytot`: +Let us consider a model where two species (`X` and `Y`) can bind to form a complex (`XY`, which also can dissociate back into `X` and `Y`). If we wish to create a representation for the total amount of `X` and `Y` in the system, we can do this by creating observables `Xtot` and `Ytot`: ```@example dsl_advanced_observables using Catalyst # hide rn = @reaction_network begin @@ -329,14 +331,14 @@ Next, we can use [symbolic indexing](@ref simulation_structure_interfacing) of o ```@example dsl_advanced_observables sol[:Xtot] ``` -to get a vector with `Xtot`'s throughout the simulation. We can also use +to get a vector with `Xtot`'s value throughout the simulation. We can also use ```@example dsl_advanced_observables using Plots plot(sol; idxs = [:Xtot, :Ytot]) ``` to plot the observables (rather than the species). -Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: +Observables can be defined using complicated expression containing species, parameters, and [variables](@ref ref) (but not other observables). In the following example (which uses a [parametric stoichiometry](@ref ref)) `X` polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: ```@example dsl_advanced_observables rn = @reaction_network begin @observables Xtot ~ X + n*Xn @@ -350,13 +352,13 @@ nothing # hide [Metadata](@ref dsl_advanced_options_species_and_parameters_metadata) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref dsl_advanced_options_species_and_parameters_metadata) to our observable we can use ```@example dsl_advanced_observables rn = @reaction_network begin - @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY + @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*Xn (kB,kD), n*X <--> Xn end nothing # hide ``` -Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here `Xtot` becomes a species: +Observables are by default considered [variables](@ref ref) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here `Xtot` becomes a species: ```@example dsl_advanced_observables rn = @reaction_network begin @species Xtot(t) @@ -368,11 +370,12 @@ nothing # hide Some final notes regarding observables: - The left-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). -- All symbols appearing on the right-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). +- All quantities appearing on the right-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). - Observables may not depend on other observables. -- Observables have their [dependent variables](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. +- Observables have their [dependent variable(s)](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. ## [Creating events](@id dsl_advanced_options_events) +--- MOVE TO SEPARATE DOC PAGE --- Sometimes one wishes to model events, describing things that can happen to it during a simulation. - A chemical system where an amount of some species is added at a time point after the simulation's initiation. - A simulation of a circadian rhythm, where light is turned on/off every 12 hours. @@ -599,7 +602,7 @@ We can confirm that `Xi` and `Xa` depend on `τ` (and not `t`): species(rn) ``` -It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default (time) independent variable, while the following one is considered spatial independent variables. If we want some species to depend on a non-default independent variable, this has to be explicitly declared: +It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default (time) independent variable, while the following one(s) are considered spatial independent variable(s). If we want some species to depend on a non-default independent variable, this has to be explicitly declared: ```@example dsl_advanced_ivs rn = @reaction_network begin @ivs τ x @@ -609,7 +612,7 @@ rn = @reaction_network begin end species(rn) ``` -Finally, it is possible to have species which depends on several independent variables: +It is also possible to have species which depends on several independent variables: ```@example dsl_advanced_ivs rn = @reaction_network begin @ivs t x diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index 2819704642..ea89995ec0 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -1,9 +1,9 @@ # [The Catalyst DSL - Introduction](@id dsl_description) -In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSL's more advanced features. +In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref ref) will describe some of the DSL's more advanced features. The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction) or [compositionally](@ref compositional_modeling)). A summary of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial will solely focus on model creation. -Before we begin, we will first load the `Catalyst` package (which is required to run the code). +Before we begin, we will first load the Catalyst package (which is required to run the code). ```@example dsl_1 using Catalyst ``` @@ -12,7 +12,7 @@ using Catalyst The DSL is initiated through the `@reaction_network`, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a Michaelis-Menten enzyme kinetics system can be written as ```@example dsl_0 rn = @reaction_network begin - (kB,kC), S + E <--> SE + (kB,kD), S + E <--> SE kP, SE --> P + E end ``` @@ -31,12 +31,12 @@ Here, you start with `@reaction_network begin`, next list all of the model's rea - A (potentially empty) set of *substrates*. - A (potentially empty) set of *products*. -Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. +Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above example, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. In the second reaction, `Y` becomes `X` at rate `1.0`. Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). ## [Defining parameters and species in the DSL](@id dsl_description_parameters_basics) -Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you which to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: +Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you wish to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: ```@example dsl_1 rn1 = @reaction_network begin a, X --> Y @@ -57,7 +57,7 @@ Generally, anything that is a [permitted Julia variable name](@id https://docs.j ## [Different types of reactions](@id dsl_description_reactions) ### [Reactions with multiple substrates or products](@id dsl_description_reactions_multiples) -Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: +Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate, at rate `kD`, to form `XY`) we use: ```@example dsl_1 rn2 = @reaction_network begin kB, X + Y --> XY @@ -72,7 +72,7 @@ end ``` ### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) -Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created and degraded, we use: +Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created (in the first reaction) and degraded (in a second reaction), we use: ```@example dsl_1 rn4 = @reaction_network begin p, 0 --> X @@ -81,7 +81,7 @@ end ``` ### [Reactions with non-unitary stoichiometries](@id dsl_description_reactions_stoichiometries) -Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: +Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating its number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: ```@example dsl_1 rn5 = @reaction_network begin kB, 2X --> X2 @@ -90,7 +90,7 @@ end ``` Reactants whose stoichiometries are not defined are assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affecting the created model can be found [here](@ref ref). -Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: +Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction twice, both with and without this notation: ```@example dsl_1 rn6 = @reaction_network begin k, 2X + 3(Y + 2Z) --> 5(V + W) @@ -129,23 +129,23 @@ rn8 = @reaction_network begin k, Y --> X end ``` -Generally, using forward reactions is clearer than backwards ones, with the letter generally not being used. +Generally, using forward reactions is clearer than backwards ones, with the later generally not being used. ### [Bundling similar reactions on a single line](@id dsl_description_reaction_bundling_similar) -There exist several other situations where models contain similar reactions (e.g. systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model, where species `X` and `Y` both degrade at the rate `d`: +There exist additional situations where models contain similar reactions (e.g. systems where all system components degrade at identical rates). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model where species `X` and `Y` both degrade at the rate `d`: ```@example dsl_1 rn8 = @reaction_network begin d, X --> 0 d, Y --> 0 end ``` -These share both the rates and product, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: +These share both the rates (`d`) and product (`0`), however, the substrates are different (`X` and `Y`). Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: ```@example dsl_1 rn8 = @reaction_network begin d, (X,Y) --> 0 end ``` -This declaration of the model is identical to the one previously. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: +This declaration of the model is identical to the previous one. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: ```@example dsl_1 rn9 = @reaction_network begin dX, X --> 0 @@ -166,7 +166,7 @@ rn10 = @reaction_network begin end ``` -It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rate(s). These may then (or may not) indicate several rates. We exemplify this using the two following (identical) networks, created with and without bundling. +It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rates. These may then (or may not) indicate several rates. We exemplify this using the two following two (identical) networks, created with and without bundling. ```@example dsl_1 rn11 = @reaction_network begin kf, S --> P1 @@ -187,19 +187,19 @@ rn12 = @reaction_network begin ((pX, pY, pZ),d), (0, Y0, Z0) <--> (X, Y, Z1+Z2) end ``` -However, as for the above model, bundling reactions too zealously can reduce (rather improve) the model's readability. +However, like for the above model, bundling reactions too zealously can reduce (rather improve) a model's readability. ## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. -Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on $A$ and a parameter (`kP`). We model this through: +Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on `A` and a parameter (`kP`). We model this through: ```@example dsl_1 rn_13 = @reaction_network begin d, A --> 0 kP*A, 0 --> P end ``` -Here, `P`'s production rate will decay as `A` is slowly removed from the system. We can [print the ODE this model produces by using `Latexify`](@ref ref): +Here, `P`'s production rate will be reduced as `A` decays. We can [print the ODE this model produces with `Latexify`](@ref ref): ```@example dsl_1 using Latexify latexify(rn_13; form=:ode) @@ -216,24 +216,24 @@ We can confirm that this generates the same ODE: ```@example dsl_1 latexify(rn_13_alt; form=:ode) ``` -Here, while the model will generate the same ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. +Here, while these models will generate identical ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. !!! warn While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). !!! danger - Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). + Catalyst automatically infers whether quantities appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: ```@example dsl_1 rn_14 = @reaction_network begin 2.0 + X^2, 0 --> X + Y - k1+k2^k3, X --> ∅ - pi*X/(sqrt(2)+Y), Y → ∅ + k1 + k2^k3, X --> ∅ + pi * X/(sqrt(2) + Y), Y → ∅ end ``` ### [Using functions in rates](@id dsl_description_nonconstant_rates_functions) -It is possible for the rate to contain Julia function. These can either be functions from Julia's standard library: +It is possible for the rate to contain Julia functions. These can either be functions from Julia's standard library: ```@example dsl_1 rn_16 = @reaction_network begin d, A --> 0 @@ -249,7 +249,7 @@ rn_17 = @reaction_network begin end ``` -### (@id dsl_description_nonconstant_rates_available_functions) +### [Pre-defined functions](@id dsl_description_nonconstant_rates_available_functions) Two functions frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where `X` activates its own production through a Hill function can be created using: ```@example dsl_1 custom_function(p1,p2,X) = (p1+E)/(p2+E) @@ -287,7 +287,7 @@ end Julia permits any Unicode characters to be used in variable names, thus Catalyst can use these as well. Below we describe some cases where this can be useful. No functionality is, however, tied to this. ### [Using ∅ in degradation/production reactions](@id dsl_description_symbols_empty_set) -Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system can alternatively be written as: +Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅` symbol. E.g. the production/degradation system can alternatively be written as: ```@example dsl_1 rn4 = @reaction_network begin p, ∅ --> X @@ -296,12 +296,12 @@ end ``` ### [Using special arrow symbols](@id dsl_description_symbols_arrows) -Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representations of these arrows are available. Here, +Catalyst uses `-->`, `<-->`, and `<--` to denote forward, bi-directional, and backwards reactions, respectively. Several unicode representations of these arrows are available. Here, - `>`, `→`, `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, and `⇁` can be used to represent forward reactions. -- `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent reversible reactions. +- `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent bi-directional reactions. - `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. -E.g. the production/degradation system alternatively can be written as: +E.g. the production/degradation system can alternatively be written as: ```@example dsl_1 rn4 = @reaction_network begin p, ∅ → X @@ -333,17 +333,11 @@ rn_13 = @reaction_network begin end It should be noted that the following symbols are *not permitted* to be used as species or parameter names: -- `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)) - `pi` and `π` (used in Julia to denote [`3.1415926535897...`](https://en.wikipedia.org/wiki/Pi)). +- `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)). - `t` (used to denote the [time variable](@ref dsl_description_nonconstant_rates_time)). -- `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)) +- `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)). - `im` (used in Julia to represent [complex numbers](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/#Complex-Numbers)). - `nothing` (used in Julia to denote [nothing](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). - `Γ` (used by Catalyst to represent [conserved quantities](@ref ref)). - - - - - - From 66f472f0266c39cd30e0ec9e942c087eee06f8cb Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 2 May 2024 16:40:30 -0400 Subject: [PATCH 06/23] save progress. Add stuff on non-standard stoichiometries. --- .../catalyst_functionality/dsl_description.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index ea89995ec0..dce49c3037 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -283,6 +283,33 @@ rn_15 = @reaction_network begin end ``` +## [Non-standard stoichiometries](@id dsl_description_stoichiometries) + +### [Non-integer stoichiometries](@id dsl_description_stoichiometries_decimal) +Previously all stoichiometric constants have been integer numbers, however, decimal numbers are also permitted. Here we create a birth-death model where each production reaction produces 1.5 units of `X`: +```@example dsl_1 +rn_16 = @reaction_network begin + p, 0 --> 1.5X + d, X --> 0 +end +``` +It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the `combinatoric_ratelaw = false` option must be used. + +### [Parametric stoichiometries](@id dsl_description_stoichiometries_decimal) +It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: +```@example dsl_1 +rn_17 = @reaction_network begin + (kB,kD), n*X <--> Xn +end +``` +Now we can designate the value of `n` through a parameter when we e.g. create an `ODEProblem`: +```@example dsl_1 +u0 = [:X => 5.0, :Xn => 1.0] +ps = [:kB => 1.0, :kD => 0.1, :n => 4] +oprob = ODEProblem(rn, u0, (0.0, 1.0), ps) +nothing # hide +``` + ## [Using special symbols](@id dsl_description_symbols) Julia permits any Unicode characters to be used in variable names, thus Catalyst can use these as well. Below we describe some cases where this can be useful. No functionality is, however, tied to this. From 47ef780f96f2918ee829b8a51cc21ee2bc8d4438 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 2 May 2024 16:40:51 -0400 Subject: [PATCH 07/23] save progress --- docs/src/catalyst_functionality/dsl_description.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index dce49c3037..9f82386d74 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -295,7 +295,7 @@ end ``` It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the `combinatoric_ratelaw = false` option must be used. -### [Parametric stoichiometries](@id dsl_description_stoichiometries_decimal) +### [Parametric stoichiometries](@id dsl_description_stoichiometries_parameters) It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: ```@example dsl_1 rn_17 = @reaction_network begin From b2490018d862d65be963256be66ebd794f9fdeac Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 2 May 2024 16:49:41 -0400 Subject: [PATCH 08/23] save progress --- docs/src/catalyst_functionality/dsl_description.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index 9f82386d74..a230c2de00 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -293,7 +293,7 @@ rn_16 = @reaction_network begin d, X --> 0 end ``` -It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the `combinatoric_ratelaw = false` option must be used. +It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the [`combinatoric_ratelaw = false`](@ref ref) option must be used. We note that non-integer stoichiometric coefficients does not make sense in most fields, however, this features is available for use for models where it does make sense. ### [Parametric stoichiometries](@id dsl_description_stoichiometries_parameters) It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: @@ -306,7 +306,7 @@ Now we can designate the value of `n` through a parameter when we e.g. create an ```@example dsl_1 u0 = [:X => 5.0, :Xn => 1.0] ps = [:kB => 1.0, :kD => 0.1, :n => 4] -oprob = ODEProblem(rn, u0, (0.0, 1.0), ps) +oprob = ODEProblem(rn_17, u0, (0.0, 1.0), ps) nothing # hide ``` From f42b804f221a2e8ac7d4c53aa5b8852871f267c8 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 9 May 2024 13:17:06 -0400 Subject: [PATCH 09/23] move to new doc structure --- .../dsl_description.md => 02_model_creation/01_dsl_basics.md} | 0 .../02_dsl_advanced.md} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/src/{catalyst_functionality/dsl_description.md => 02_model_creation/01_dsl_basics.md} (100%) rename docs/src/{catalyst_functionality/dsl_advanced_options.md => 02_model_creation/02_dsl_advanced.md} (100%) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/02_model_creation/01_dsl_basics.md similarity index 100% rename from docs/src/catalyst_functionality/dsl_description.md rename to docs/src/02_model_creation/01_dsl_basics.md diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/02_model_creation/02_dsl_advanced.md similarity index 100% rename from docs/src/catalyst_functionality/dsl_advanced_options.md rename to docs/src/02_model_creation/02_dsl_advanced.md From 3b22ee6f772cd69bab6f973f7150b91cc9f1f46a Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 16 May 2024 16:42:05 -0400 Subject: [PATCH 10/23] rebase --- docs/pages.jl | 4 +- .../dsl_advanced.md} | 325 ++------ .../dsl_basics.md} | 28 +- docs/src/model_creation/dsl_description.md | 767 ------------------ 4 files changed, 77 insertions(+), 1047 deletions(-) rename docs/src/{02_model_creation/02_dsl_advanced.md => model_creation/dsl_advanced.md} (50%) rename docs/src/{02_model_creation/01_dsl_basics.md => model_creation/dsl_basics.md} (88%) delete mode 100644 docs/src/model_creation/dsl_description.md diff --git a/docs/pages.jl b/docs/pages.jl index d914752c8a..0f31fdcbe2 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -6,8 +6,8 @@ pages = Any[ # Advanced introduction. ], "Model Creation and Properties" => Any[ - "model_creation/dsl_description.md", - # DSL - Advanced + "model_creation/dsl_basics.md", + "model_creation/dsl_advanced.md", "model_creation/programmatic_CRN_construction.md", "model_creation/compositional_modeling.md", "model_creation/constraint_equations.md", diff --git a/docs/src/02_model_creation/02_dsl_advanced.md b/docs/src/model_creation/dsl_advanced.md similarity index 50% rename from docs/src/02_model_creation/02_dsl_advanced.md rename to docs/src/model_creation/dsl_advanced.md index 7afb7a6427..ec678daeb4 100644 --- a/docs/src/02_model_creation/02_dsl_advanced.md +++ b/docs/src/model_creation/dsl_advanced.md @@ -1,7 +1,7 @@ # [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) -Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous DSL tutorial](@ref dsl_description) described how to create reactions. This one will focus on options. These are typically used to supplant a model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. +Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous DSL tutorial](@ref dsl_description) described how to create reactions. This one will focus on options. These are typically used to supply a model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation of observables. -All options designations begins with declaration starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each use of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that does not directly include using an option. +All option designations begin with a declaration starting with `@`, followed by its input. E.g. the `@observables` option allows for the generation of observables. Each option can only be used once within each use of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that do not involve using an option. As a first step, we import Catalyst (which is required to run the tutorial): ```@example dsl_advanced_1 @@ -9,7 +9,7 @@ using Catalyst ``` ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) -[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining quantities as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive form (`Pᵢ`) to its active form (`Pₐ`) is catalysed by an enzyme (`E`). Using the most natural description: +[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols correspond to species and which to parameters. This is done by designating everything that appears as either a substrate or a product as a species, and all remaining quantities as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive form (`Pᵢ`) to its active form (`Pₐ`) is catalysed by an enzyme (`E`). Using the most natural description: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ @@ -39,7 +39,7 @@ end ``` Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a quantity cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. -While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). +While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occurs as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). Rather than listing all species/parameters on a single line after the options, a `begin ... end` block can be used (listing one species/parameter on each line). E.g. in the following example we use this notation to explicitly designate all species and parameters of the system: ```@example dsl_advanced_1 @@ -74,19 +74,19 @@ end parameters(dimerisation) ``` !!! danger - Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary, and also checks the order manually. + Generally, Catalyst and the SciML ecosystem *do not* guarantee that parameter and species order are preserved throughout various operations on a model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depend on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary, and also checks the order manually. !!! note - The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either approach, that knowledge can be transferred to the other one. + The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one has learnt how to specify species/parameters using either approach, that knowledge can be transferred to the other one. -Generally, there are four main reasons for specifying species/parameters using the `@species` and `@parameters` option: +Generally, there are four main reasons for specifying species/parameters using the `@species` and `@parameters` options: 1. To designate a quantity, that would otherwise have defaulted to a parameter, as a species. 2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). 3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). -4. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) +4. To designate a species or parameters that do not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) !!!! warn - Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *quantities that appear in reactions*. Until now this has not been relevant. However, this tutorial will demonstrate cases where species/parameters that are not part of reactions are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). + Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *quantities that appear in reactions*. Until now this has not been relevant. However, this tutorial will demonstrate cases where species/parameters that are not part of reactions are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref ref)). ### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` followed by the desired default value). E.g here we set `X`'s default initial condition value to $1.0$, and `p` and `d`'s default values to $1.0$ and $0.2$, respectively: @@ -105,7 +105,7 @@ u0 = [] tspan = (0.0, 10.0) p = [] oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) +sol = solve(oprob) plot(sol) ``` It is still possible to provide values for some (or all) initial conditions/parameters in `u0`/`ps` (in which case these overrides the default values): @@ -113,7 +113,7 @@ It is still possible to provide values for some (or all) initial conditions/para u0 = [:X => 4.0] p = [:d => 0.5] oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) +sol = solve(oprob) plot(sol) ``` It is also possible to declare a model with default values for only some initial conditions/parameters: @@ -127,13 +127,13 @@ end tspan = (0.0, 10.0) p = [:p => 1.0, :D => 0.2] oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) +sol = solve(oprob) plot(sol) ``` API for checking the default values of species and parameters can be found [here](@ref ref). ### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) -In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of `X` using the parameter `X₀`. +In the previous section, we designated default values for initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use case is to designate a species's initial condition as a parameter. E.g. in the following example we represent the initial condition of `X` using the parameter `X₀`. ```@example dsl_advanced_defaults rn = @reaction_network begin @species X(t)=X₀ @@ -179,7 +179,7 @@ two_state_system = @reaction_network begin end ``` -It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kA$'s default value to $1.0$ we use +It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, and next the metadata. I.e. to in the above example set $kA$'s default value to $1.0$ we use ```@example dsl_advanced_metadata two_state_system = @reaction_network begin @parameters kA=1.0 [description="X's activation rate", bound=(0.01,10.0)] @@ -187,7 +187,7 @@ two_state_system = @reaction_network begin end ``` -When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. a version of the previous example can be written as +When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slightly. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. a version of the previous example can be written as ```@example dsl_advanced_metadata two_state_system = @reaction_network begin @parameters begin @@ -226,37 +226,7 @@ rn = @reaction_network begin end ``` -A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is the [Brusselator](https://en.wikipedia.org/wiki/Brusselator). - -### [Specifying non-species variables](@id dsl_advanced_options_variables) ---- MOVE THIS TO HYBRID SECTION --- -Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of *reaction events*. When they are converted to ODEs, the species become unknowns of the system. However, Catalyst permits the creation of [hybrid CRN models](@ref ref). These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it cannot plausibly be a reaction reactant). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: -```@example dsl_advanced_variables -using Catalyst # hide -rn = @reaction_network begin - @variables V(t) - (p,d), 0 <--> G -end -``` -Note that $V$ (like species) is time-dependant, and (like species) must be declared as such when the `@variables` option is used. We can now simulate our model (remembering to provide a value for $V$ as well as $G$): -```@example dsl_advanced_variables -using OrdinaryDiffEq, Plots -u0 = [:G => 0.1, :V => 1.0] -tspan = (0.0, 10.0) -p = [:p => 1.0, :d => 0.5] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) -plot(sol) -``` -Here, we have not actually described how $V$ interacting with our model, or how its value may change. Primarily variables are declared as part of hybrid CRN/equation modelling, which is described in more detail [here](@ref ref). - -You can set metadata and default initial condition values for variables using the same syntax as used for parameters and species. - -You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknowns` function retrieves both. - -## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) ---- DISCUSS WHAT TO DO WITH THIS SECTION --- -Reactions can also have metadata. This is described in detail [here](@ref ref). +A common use-case for constant species is when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is the [Brusselator](https://en.wikipedia.org/wiki/Brusselator). ## [Naming reaction networks](@id dsl_advanced_options_naming) Each reaction network model has a name. It can be accessed using the `nameof` function. By default, some generic name is used: @@ -300,7 +270,7 @@ end rn1 == rn2 ``` -Setting model names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. +Setting model names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display names of subnetworks' species and parameters. ## [Creating observables](@id dsl_advanced_options_observables) Sometimes one might want to use observable variables. These are variables with values that can be computed directly from a system's state (rather than having their values implicitly given by reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. @@ -338,7 +308,7 @@ plot(sol; idxs = [:Xtot, :Ytot]) ``` to plot the observables (rather than the species). -Observables can be defined using complicated expression containing species, parameters, and [variables](@ref ref) (but not other observables). In the following example (which uses a [parametric stoichiometry](@ref ref)) `X` polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: +Observables can be defined using complicated expressions containing species, parameters, and [variables](@ref ref) (but not other observables). In the following example (which uses a [parametric stoichiometry](@ref ref)) `X` polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: ```@example dsl_advanced_observables rn = @reaction_network begin @observables Xtot ~ X + n*Xn @@ -349,7 +319,7 @@ nothing # hide !!! If only a single observable is declared, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. -[Metadata](@ref dsl_advanced_options_species_and_parameters_metadata) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref dsl_advanced_options_species_and_parameters_metadata) to our observable we can use +[Metadata](@ref dsl_advanced_options_species_and_parameters_metadata) can be supplied to an observable directly after its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref dsl_advanced_options_species_and_parameters_metadata) to our observable we can use ```@example dsl_advanced_observables rn = @reaction_network begin @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*Xn @@ -362,7 +332,7 @@ Observables are by default considered [variables](@ref ref) (not species). To de ```@example dsl_advanced_observables rn = @reaction_network begin @species Xtot(t) - @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY + @observables Xtot ~ X + n*XnXY (kB,kD), n*X <--> Xn end nothing # hide @@ -374,221 +344,9 @@ Some final notes regarding observables: - Observables may not depend on other observables. - Observables have their [dependent variable(s)](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. -## [Creating events](@id dsl_advanced_options_events) ---- MOVE TO SEPARATE DOC PAGE --- -Sometimes one wishes to model events, describing things that can happen to it during a simulation. - - A chemical system where an amount of some species is added at a time point after the simulation's initiation. - - A simulation of a circadian rhythm, where light is turned on/off every 12 hours. - - A cell divides when some size variable reaches a certain threshold, halving the amount of each species in the system. - -Events are divided into *continuous* and *discrete* events, and these can be added directly to a system using the `continuous_events` and `discrete_events` options. Events can also be modelled through *callbacks*. These are different in that they are supplied in the simulation step (rather than on system creation), and generally provide more flexibility in how they may affect the system. Callbacks are described on a separate [page](@ref advanced_simulations_callbacks). - -The notation described below for creating continuous and discrete events is the same which is used in [ModelingToolkit to create events](https://docs.sciml.ai/ModelingToolkit/stable/basics/Events/), and which is used for [events for programmatic model creation](@ref ref). - -### [Continuous vs discrete events](@id dsl_advanced_options_events_continuous_vs_discrete) -Both continuous and discrete events combine some condition (for triggering the event) with some affect (describing their effects on the system). They differ in the following ways: -- They use slightly different notation. -- Discrete events' conditions are checked at *the end of* each simulation time step. For continuous events, the simulation instead finds the *exact time point* when the event is triggered at. -- Continuous events cannot be supplied to jump simulations. - -### [Continuous events](@id dsl_advanced_options_events_continuous) -Let us consider a simple system where species `X` degraded at a constant rate `d`. Next, we wish to add an event which adds `2.0` units of `X` whenever `X` reaches a critical threshold `1.0`. This can be done in the following manner: -```@example dsl_advanced_events -using Catalyst # hide -rn = @reaction_network begin - @continuous_events begin - X ~ 1.0 => [X ~ X + 2.0] - end - d, X --> 0 -end -nothing # hide -``` -Here, the `@continuous_events` option is followed by a `begin ... end` block. Next, each line corresponds to a separate event. Each event is created in the following manner: -- It combines a *condition* (denoting when the event will happen) with one (or several) *affects* (denoting what the event does to the system when it happens). -- The condition (left) and affect (right) are separated by a `=>`. -- The condition takes the form of an [equation](). Here, the event is triggered whenever the equation's two sides (separated by a `~`) are equal. -- The affect(s) are enclosed within `[]`. If there are multiple affects, these are separated by `,` (the example above contains a single affect). -- Each affect is a single equation that describes how a parameter, species, or [variable](@ref dsl_advanced_options_variables) is updated when the event is triggered. -- Each affect's equation's left-hand side must contain only the parameter/species/variable whose value should be updated. -- Each affect's equation's right-hand side is an expression describing its updated value. - -We can simulate the model we declared, just like any other model: -```@example dsl_advanced_events -using OrdinaryDiffEq, Plots -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -Inspecting the solution, we can confirm that whenever `X` reaches a value of `1.0`, `2.0` units of `X` is added to the system. - -In our example, we can also denote the critical quantities using parameters: -```@example dsl_advanced_events -rn = @reaction_network begin - @parameters X_thres X_add - @continuous_events begin - X ~ X_thres => [X ~ X + X_add] - end - d, X --> 0 -end -nothing # hide -``` -Here, since `X_thres` and `X_add` do not appear in any reactions, Catalyst cannot determine whether they are parameters or species. hence, they must be [explicitly designated as parameters by using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters). Next, these can be designated as any value, and supplied to the `ODEProblem`: -```@example dsl_advanced_events -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0, :X_thres => 0.5, :X_add => 3.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` - -As previously noted, each continuous event can have multiple affects. The following system has two components (`X` and `Y`, one being produced and one being degraded). When their concentrations are equal, a continuous events reduce the concentration of `X` while increasing the concentration of `Y`: -```@example dsl_advanced_events -rn = @reaction_network begin - @continuous_events begin - X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] - end - p, 0 --> X - d, Y --> 0 -end - -u0 = [:X => 1.0, :Y => 3.0] -tspan = (0.0, 10.0) -ps = [:p => 1.0, :d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` - -!!!warn - A single event (continuous or discrete) can update the value of either (one or several) species (and variables), or of (one or several) parameters. It is not possible for an event to update the values of both species/variables and parameters. - -In the above examples we have modelled a system with a single event. In these cases, the `begin end` block is not required, and the event can be provided on the same line as the `@continuous_events` option: -```@example dsl_advanced_events -rn = @reaction_network begin - @continuous_events X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] - p, 0 --> X - d, Y --> 0 -end -nothing # hide -``` - -### [Discrete events](@id dsl_advanced_options_events_discrete) -Just like [continuous events](dsl_advanced_options_events_continuous), discrete events combine a condition with one or more affect statements. Here, discrete events' affects are created identically to those for continuous events. Discrete events' conditions are different. There exist 3 different types of discrete events, each with a different type of condition. All three types are created using the `@discrete_events` option, and a single system can contain a mix of all types. The three types are: -- Preset-time discrete events. -- Periodic discrete events. -- Conditional discrete events. - -#### [Preset-time discrete events](@id dsl_advanced_options_events_discrete_presettime) -*Present-time events* are events that happen at specific time points. Here, the condition is a vector with all the time points at which an event is triggered. E.g. here we create a production/degradation loop, where `2.0` units of `X` is added at time points `3.0` and `7.0` -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - [3.0, 7.0] => [X ~ X + 2.0] - end - (p,d), 0 <--> X -end - -u0 = [:X => 0.1, :Y => 3.0] -tspan = (0.0, 10.0) -ps = [:p => 1.0, :d => 0.5] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` - -The preset time points can also be parameters (in which case, they have to be [designated as such using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters)): -```@example dsl_advanced_events -rn = @reaction_network begin - @parameters t1 t2 - @discrete_events begin - [t1, t2] => [X ~ X + 2.0] - end - (p,d), 0 <--> X -end -nothing -``` - -#### [Periodic discrete events](@id dsl_advanced_options_events_discrete_periodic) -When a discrete event's condition is a vector, a preset-time event is created. If it instead is a single value, a *periodic event* is created. These occur repeatedly throughout a simulation, with its period set by the affect term. E.g. here we create a system where `0.5` units of `X` is added every `1.0` time units. -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - 1.0 => [X ~ X + 0.5] - end - d, X --> 0 -end - -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -Like for preset-time events, periodic events' affects may contain parameters. - -#### [Conditional discrete events](@id dsl_advanced_options_events_discrete_conditional) -Finally, discrete events' condition may be a boolean expression (consisting of parameters, species, variables, and the time variable). Let's say that we want to create an event which, if the concentration of `X` is below a threshold `1.0`, adds `1.0` units of `X` to the system, then we can use the following discrete event: -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - X < 1.0 => [X ~ X + 2.0] - end - d, X --> 0 -end -``` -If we simulate the system using the same conditions as for our [similar, continuous, example](@ref dsl_advanced_options_events_continuous) we get the same result: -```@example dsl_advanced_events -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -So, how is modelling this event as a discrete or continuous event different? There are four differences: -1) For continuous events, the simulation method finds the exact time point when the condition triggers. Discrete events are triggered at the first time step when the condition holds. -2) This discrete event will be triggered whenever `X < 1.0` holds, not just when the concentration of `X` passes the threshold. E.g. it will be triggered if the initial concentration of `X` is less than `1.0`. -3) Only the discrete event event can be used with jump simulations. -4) The discrete event can be used to create more advanced conditions. - -E.g. using (4), we can modify our system so that the event is only triggered when time is less than `5.0` (after which `X` decays towards `0`): -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - (X < 1.0) & (t < 5.0) => [X ~ X + 2.0] - end - d, X --> 0 -end - -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -!!!note - When we form composite boolean conditions for conditional discrete events, we use `&` to denote the AND operator (not `&&`, as this is currently not supported). - -!!!warn - Generally, discrete events including equality (`==`) will not be triggered. The reason is that the condition is only checked at the end of every time step. Hence, unless special precautions are taken to ensure that the simulator stops when the condition holds, it will unlikely be triggered. - - ## [Specifying non-time independent variables](@id dsl_advanced_options_ivs) -As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on a *time independent variable*, and potentially one or more *spatial independent variables*. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using the `@ivs` option. E.g. to use `τ` instead of `t` we can use +As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depend on a *time independent variable*, and potentially one or more *spatial independent variables*. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using the `@ivs` option. E.g. to use `τ` instead of `t` we can use ```@example dsl_advanced_ivs using Catalyst # hide rn = @reaction_network begin @@ -606,7 +364,7 @@ It is possible to designate several independent variables using `@ivs`. If so, t ```@example dsl_advanced_ivs rn = @reaction_network begin @ivs τ x - @species X(x) Y(x) + @species X(τ) Y(x) (p1,d1), 0 <--> X (p2,d2), 0 <--> Y end @@ -623,4 +381,41 @@ species(rn) ``` !!! note - Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying spatial independent variables is currently limited. \ No newline at end of file + Setting spatial independent variables is primarily intended for the modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying spatial independent variables is limited. + + +## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) +It is possible to supply reactions with *metadata*, containing some additional information of the reaction. A reaction's metadata follows after its declaration (first using the metadata's name, then a `=`, then its value) and is encapsulated by `[]` (where individual entries are separated by `,`). Here, we add a `description` metadata to the reactions of a birth-death process: +```@example dsl_advanced_reaction_metadata +using Catalyst # hide +bd_model = @reaction_network begin + p, 0 --> X, [description="A production reaction"] + d, X --> 0, [description="A degradation reaction"] +end +nothing # hide +``` + +When [bundling reactions](@ref dsl_description_reaction_bundling), reaction metadata can be bundled using the same rules as rates. Bellow we re-declare our birth-death process, but on a single line: +```@example dsl_advanced_reaction_metadata +bd_model = @reaction_network begin + (p,d), 0 --> X, ([description="A production reaction"], [description="A degradation reaction"]) +end +nothing # hide +``` + +Here we declare a model where we also provide a `misc` metadata (which can hold any quantity we require) to our birth reaction: +```@example dsl_advanced_reaction_metadata +bd_model = @reaction_network begin + p, 0 --> X, [description="A production reaction", misc=:value] + d, X --> 0, [description="A degradation reaction"] +end +nothing # hide +``` + +A reaction's metadata can be accessed using specific functions, e.g. `Catalyst.hasdescription` and `Catalyst.getdescription` can be used to check if a reaction have a description metadata, and to retrieve it, respectively: +```@example dsl_advanced_reaction_metadata +rx = @reaction p, 0 --> X, [description="A production reaction"] +Catalyst.getdescription(rx) +``` + +A list of all available reaction metadata can be found [here](@ref ref). \ No newline at end of file diff --git a/docs/src/02_model_creation/01_dsl_basics.md b/docs/src/model_creation/dsl_basics.md similarity index 88% rename from docs/src/02_model_creation/01_dsl_basics.md rename to docs/src/model_creation/dsl_basics.md index a230c2de00..924da97753 100644 --- a/docs/src/02_model_creation/01_dsl_basics.md +++ b/docs/src/model_creation/dsl_basics.md @@ -1,5 +1,5 @@ # [The Catalyst DSL - Introduction](@id dsl_description) -In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref ref) will describe some of the DSL's more advanced features. +In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called "*the Catalyst DSL*"). A [follow-up tutorial](@ref dsl_advanced_options) will describe some of the DSL's more advanced features. The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction) or [compositionally](@ref compositional_modeling)). A summary of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial will solely focus on model creation. @@ -9,7 +9,7 @@ using Catalyst ``` ### [Quick-start summary](@id dsl_description_quick_start) -The DSL is initiated through the `@reaction_network`, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a Michaelis-Menten enzyme kinetics system can be written as +The DSL is initiated through the `@reaction_network` macro, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a [Michaelis-Menten enzyme kinetics system](@ref ref) can be written as ```@example dsl_0 rn = @reaction_network begin (kB,kD), S + E <--> SE @@ -26,12 +26,12 @@ rn = @reaction_network begin 1.0, Y --> X end ``` -Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with `end`. Each reaction consists of +Here, you start with `@reaction_network begin`, next list all of the model's reactions, and finish with `end`. Each reaction consists of - A *rate*. - A (potentially empty) set of *substrates*. - A (potentially empty) set of *products*. -Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above example, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. In the second reaction, `Y` becomes `X` at rate `1.0`. +Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref dsl_description_symbols_arrows)). In the above example, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. In the second reaction, `Y` becomes `X` at rate `1.0`. Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). @@ -129,7 +129,7 @@ rn8 = @reaction_network begin k, Y --> X end ``` -Generally, using forward reactions is clearer than backwards ones, with the later generally not being used. +Generally, using forward reactions is clearer than backwards ones, with the latter typically never being used. ### [Bundling similar reactions on a single line](@id dsl_description_reaction_bundling_similar) There exist additional situations where models contain similar reactions (e.g. systems where all system components degrade at identical rates). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model where species `X` and `Y` both degrade at the rate `d`: @@ -139,13 +139,13 @@ rn8 = @reaction_network begin d, Y --> 0 end ``` -These share both the rates (`d`) and product (`0`), however, the substrates are different (`X` and `Y`). Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: +These share both their rates (`d`) and products (`0`), however, the substrates are different (`X` and `Y`). Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: ```@example dsl_1 rn8 = @reaction_network begin d, (X,Y) --> 0 end ``` -This declaration of the model is identical to the previous one. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: +This declaration of the model is identical to the previous one. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: ```@example dsl_1 rn9 = @reaction_network begin dX, X --> 0 @@ -187,7 +187,7 @@ rn12 = @reaction_network begin ((pX, pY, pZ),d), (0, Y0, Z0) <--> (X, Y, Z1+Z2) end ``` -However, like for the above model, bundling reactions too zealously can reduce (rather improve) a model's readability. +However, like for the above model, bundling reactions too zealously can reduce (rather than improve) a model's readability. ## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. @@ -218,10 +218,10 @@ latexify(rn_13_alt; form=:ode) ``` Here, while these models will generate identical ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. !!! warn - While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). + While `rn_13` and `rn_13_alt` will generate equivalent simulations, for jump simulations, the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). !!! danger - Catalyst automatically infers whether quantities appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). + Catalyst automatically infers whether quantities appearing in the DSL are species or parameters (as described [here](@ref dsl_advanced_options_declaring_species_and_parameters)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref dsl_advanced_options_declaring_species_and_parameters). A full example of how to do this for this example can be found [here](@ref ref). Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: ```@example dsl_1 @@ -252,7 +252,6 @@ end ### [Pre-defined functions](@id dsl_description_nonconstant_rates_available_functions) Two functions frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where `X` activates its own production through a Hill function can be created using: ```@example dsl_1 -custom_function(p1,p2,X) = (p1+E)/(p2+E) rn_18 = @reaction_network begin hill(X,v,K,n), 0 --> P d, X --> 0 @@ -283,6 +282,9 @@ rn_15 = @reaction_network begin end ``` +!!! warn + Jump simulations cannot be performed for models with time-dependent rates without additional considerations, which are discussed [here](@ref ref). + ## [Non-standard stoichiometries](@id dsl_description_stoichiometries) ### [Non-integer stoichiometries](@id dsl_description_stoichiometries_decimal) @@ -293,10 +295,10 @@ rn_16 = @reaction_network begin d, X --> 0 end ``` -It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the [`combinatoric_ratelaw = false`](@ref ref) option must be used. We note that non-integer stoichiometric coefficients does not make sense in most fields, however, this features is available for use for models where it does make sense. +It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the [`combinatoric_ratelaw = false`](@ref ref) option must be used. We note that non-integer stoichiometric coefficients do not make sense in most fields, however, this feature is available for use for models where it does make sense. ### [Parametric stoichiometries](@id dsl_description_stoichiometries_parameters) -It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: +It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` bind to form `Xn`: ```@example dsl_1 rn_17 = @reaction_network begin (kB,kD), n*X <--> Xn diff --git a/docs/src/model_creation/dsl_description.md b/docs/src/model_creation/dsl_description.md deleted file mode 100644 index e32bba7673..0000000000 --- a/docs/src/model_creation/dsl_description.md +++ /dev/null @@ -1,767 +0,0 @@ -# [The Reaction DSL](@id dsl_description) -This tutorial describes the syntax for building chemical reaction -network models using Catalyst's domain-specific language (DSL). Examples showing -how to both construct and solve ODE, SDE, and jump models are provided in [Basic -Chemical Reaction Network Examples](@ref basic_CRN_library). To learn more about the symbolic -[`ReactionSystem`](@ref)s generated by the DSL, and how to use them directly, see -the tutorial on [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction). - -We first load the `Catalyst` package, which is required for the code in this tutorial to run -```@example tut2 -using Catalyst -``` - -## [Basic syntax](@id basic_examples) - -The `@reaction_network` macro allows the (symbolic) specification of reaction -networks with a simple format. Its input is a set of chemical reactions, and -from them it generates a symbolic [`ReactionSystem`](@ref) reaction network -object. The `ReactionSystem` can be used as input to ModelingToolkit -`ODEProblem`, `NonlinearProblem`, `SteadyStateProblem`, `SDEProblem`, -`JumpProblem`, and more. `ReactionSystem`s can also be incrementally extended as -needed, allowing for programmatic construction of networks and network -composition. - -The basic syntax is: - -```@example tut2 -rn = @reaction_network begin - 2.0, X + Y --> XY - 1.0, XY --> Z1 + Z2 -end -``` -where each line of the [`@reaction_network`](@ref) macro corresponds to a -chemical reaction. Each reaction consists of a reaction rate (the expression on -the left-hand side of `,`), a set of substrates (the expression in-between `,` -and `-->`), and a set of products (the expression on the right-hand side of -`-->`). The substrates and the products may contain one or more reactants, -separated by `+`. The naming convention for these is the same as for normal -variables in Julia. - -The chemical reaction model is generated by the `@reaction_network` macro and -stored in the `rn` variable (a normal Julia variable, which does not need to be -called `rn`). It corresponds to a [`ReactionSystem`](@ref), a symbolic -representation of the chemical network. The generated `ReactionSystem` can be -converted to a symbolic differential equation model via -```@example tut2 -osys = convert(ODESystem, rn) -osys = complete(osys) -``` - -We can then convert the symbolic ODE model into a compiled, optimized -representation for use in the SciML ODE solvers by constructing an `ODEProblem`. -Creating an `ODEProblem` also requires our specifying the initial conditions for -the model. We do this by creating a mapping from each symbolic variable -representing a chemical species to its initial value -```@example tut2 -# define the symbolic variables -t = default_t() -@species X(t) Y(t) Z(t) XY(t) Z1(t) Z2(t) - -# create the mapping -u0 = [X => 1.0, Y => 1.0, XY => 1.0, Z1 => 1.0, Z2 => 1.0] -``` -Alternatively, we can create a mapping using Julia `Symbol`s for each variable, -and then convert them to a mapping involving symbolic variables like -```@example tut2 -u0 = symmap_to_varmap(rn, [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0]) -``` -Given the mapping, we can then create an `ODEProblem` from our symbolic `ODESystem` -```@example tut2 -tspan = (0.0, 1.0) # the time interval to solve on -oprob = ODEProblem(osys, u0, tspan, []) -``` - -Catalyst provides a shortcut to avoid having to explicitly `convert` to an -`ODESystem` and/or use `symmap_to_varmap`, allowing direct construction -of the `ODEProblem` like -```@example tut2 -u0 = [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0] -oprob = ODEProblem(rn, u0, tspan, []) -``` - -For more detailed examples, see the example implementations in the [Library of Basic Chemical Reaction Network Models](@ref basic_CRN_library) section. - -## Defining parameters and species -Numeric parameter values do not need to be set when the model is created, i.e. -Catalyst supports symbolic parameters too: -```@example tut2 -rn = @reaction_network begin - k1, X --> Y - k2, Y --> X -end -``` -All symbols that do not appear as a substrate or product in a reaction are -designated by Catalyst as a parameter (i.e. all symbols appearing only within -rate expressions and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). In this example `X` and `Y` -appear as a substrates and products, but neither `k1` nor `k2`. Hence `k1` and `k2` are -designated as parameters. Later in this tutorial, we will describe how to manually specify what should be -considered a species or parameter. - -## Production, Destruction, and Stoichiometry -Sometimes reactants are produced/destroyed from/to nothing. This can be -designated using either `0` or `∅`: -```@example tut2 -rn = @reaction_network begin - 2.0, 0 --> X - 1.0, X --> 0 -end -``` -If several molecules of the same reactant are involved in a reaction, the -stoichiometry of a reactant in a reaction can be set using a number. Here, two -molecules of species `X` form the dimer `X2`: -```@example tut2 -rn = @reaction_network begin - 1.0, 2X --> Y -end -``` -this corresponds to the differential equation: -```@example tut2 -convert(ODESystem, rn) -``` -Other numbers than 2 can be used, and parenthesis can be used to reuse the same -stoichiometry for several reactants: -```@example tut2 -rn = @reaction_network begin - 1.0, X + 2(Y + Z) --> W -end -``` -Note, one can explicitly multiply by integer coefficients too, i.e. -```@example tut2 -rn = @reaction_network begin - 1.0, X + 2*(Y + Z) --> W -end -``` - -## Arrow variants -A variety of Unicode arrows are accepted by the DSL in addition to `-->`. All of -these work: `>`, `→` `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, `⇁`. Backwards -arrows can also be used to write the reaction in the opposite direction. For -example, these reactions are equivalent: -```@example tut2 -rn = @reaction_network begin - 1.0, X + Y --> XY - 1.0, X + Y → XY - 1.0, XY ← X + Y - 1.0, XY <-- X + Y -end -``` - -## Bi-directional arrows for reversible reactions -Bi-directional arrows, including bidirectional Unicode arrows like ↔, can be -used to designate a reversible reaction. For example, these two models are -equivalent: -```@example tut2 -rn = @reaction_network begin - 2.0, X + Y --> XY - 2.0, X + Y <-- XY -end -``` -```@example tut2 -rn2 = @reaction_network begin - (2.0,2.0), X + Y <--> XY -end -``` - -If the reaction rates in the backward and forward directions are different, they -can be designated in the following way: -```@example tut2 -rn = @reaction_network begin - (2.0,1.0), X + Y <--> XY -end -``` -which is identical to -```@example tut2 -rn = @reaction_network begin - 2.0, X + Y --> XY - 1.0, X + Y <-- XY -end -``` - -## Combining several reactions in one line -Several similar reactions can be combined in one line by providing a tuple of -reaction rates and/or substrates and/or products. If several tuples are provided, -they must all be of identical length. These pairs of reaction networks are all -identical. - -Pair 1: -```@example tut2 -rn1 = @reaction_network begin - 1.0, S --> (P1,P2) -end -``` -```@example tut2 -rn2 = @reaction_network begin - 1.0, S --> P1 - 1.0, S --> P2 -end -``` -Pair 2: -```@example tut2 -rn1 = @reaction_network begin - (1.0,2.0), (S1,S2) --> P -end -``` -```@example tut2 -rn2 = @reaction_network begin - 1.0, S1 --> P - 2.0, S2 --> P -end -``` -Pair 3: -```@example tut2 -rn1 = @reaction_network begin - (1.0,2.0,3.0), (S1,S2,S3) --> (P1,P2,P3) -end -``` -```@example tut2 -rn2 = @reaction_network begin - 1.0, S1 --> P1 - 2.0, S2 --> P2 - 3.0, S3 --> P3 -end -``` -This can also be combined with bi-directional arrows, in which case separate -tuples can be provided for the backward and forward reaction rates. -These reaction networks are identical -```@example tut2 -rn1 = @reaction_network begin - (1.0,(1.0,2.0)), S <--> (P1,P2) -end -``` -```@example tut2 -rn2 = @reaction_network begin - 1.0, S --> P1 - 1.0, S --> P2 - 1.0, P1 --> S - 2.0, P2 --> S -end -``` - -## Variable reaction rates -Reaction rates do not need to be a single parameter or a number, but can also be -expressions depending on time or the current amounts of system species (when, for -example, one species can activate the production of another). For instance, this -is a valid notation: -```@example tut2 -rn = @reaction_network begin - 1.0, X --> ∅ - k*X, Y --> ∅ -end -``` -corresponding to the ODE model -```@example tut2 -convert(ODESystem,rn) -``` - -With respect to the corresponding mass action ODE model, this is actually -equivalent to the reaction system -```@example tut2 -rn = @reaction_network begin - 1.0, X --> ∅ - k, X + Y --> X -end -``` -```@example tut2 -convert(ODESystem,rn) -``` -!!! note - While the ODE models corresponding to the preceding two reaction systems are - identical, in the latter example the `Reaction` stored in `rn` will be classified as - [`ismassaction`](@ref) while in the former it will not, which can impact optimizations - used in generating `JumpSystem`s. For this reason, it is recommended to use the - latter representation when possible. - -Most expressions and functions are valid reaction rates, e.g.: -```@example tut2 -using SpecialFunctions -rn = @reaction_network begin - 2.0*X^2, 0 --> X + Y - t*gamma(Y), X --> ∅ - pi*X/Y, Y --> ∅ -end -``` -where here `t` always denotes Catalyst's time variable. Please note that many -user-defined functions can be called directly, but others will require -registration with Symbolics.jl ([see the faq](@ref user_functions)). - -## [Explicit specification of network species and parameters](@id dsl_description_explicit_species) -Recall that the `@reaction_network` macro automatically designates symbols used -in the macro as either parameters or species, with symbols that appear as a -substrate or product being species, and all other symbols becoming parameters -(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default -behavior for a given symbol. E.g one might want something to be considered as a -species, even if it only appears within a rate expression. In the following -network -```@example tut2 -rn = @reaction_network begin - k*X, Y --> 0 -end -``` -`X` (as well as `k`) will be considered a parameter. - -By using the `@species` and `@parameters` options within the `@reaction_network` -macro, one can manually declare that specified symbols should be considered a -species or parameter. E.g in: -```@example tut2 -rn = @reaction_network begin - @species X(t) Y(t) - k*X, Y --> 0 -end -``` -`X` and `Y` are set as species. Please note that when declaring species using -the `@species` option, their dependant variable (almost always `t`) also needs -to be designated. Similarly in -```@example tut2 -rn = @reaction_network begin - @parameters k - k*X, Y --> 0 -end -``` -both `X` and `k` will be considered as parameters. It is also possible to use -both options simultaneously, allowing users to fully specify which symbols are -species and/or parameters: -```@example tut2 -rn = @reaction_network begin - @species X(t) Y(t) - @parameters k - k*X, Y --> 0 -end -``` -Here, `X` and `Y` are designated as species and `k` as a parameter. - -The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: -```@example tut2 -rn = @reaction_network begin - @species X(t) - k*X, Y --> 0 -end -``` - -Finally, note that the `@species` and `@parameters` options can also be used in -`begin ... end` block form, allowing more formatted lists of species/parameters: -```@example tut2 -rn = @reaction_network begin - @parameters begin - d1 - d2 - end - @species begin - X1(t) - X2(t) - end - d2, X2 --> 0 - d1, X1 --> 0 -end -``` -This can be especially useful when declaring default values for clarity of model -specification (see the next section). - -## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) -When using the `@species` and ` @parameters` macros to declare species and/or -parameters, one can also provide default initial conditions for each species and -values for each parameter: -```@example tut2 -rn = @reaction_network begin - @species X(t)=1.0 - @parameters p=1.0 d=0.1 - p, 0 --> X - d, X --> ∅ -end -``` -This system can now be simulated without providing initial condition or -parameter vectors to the DifferentialEquations.jl solvers: -```@example tut2 -using DifferentialEquations, Plots -u0 = [] -tspan = (0.0, 10.0) -p = [] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -When providing default values, it is possible to do so for only a subset of the -species or parameters, in which case the rest can be specified when constructing -the problem type to solve: -```@example tut2 -rn = @reaction_network begin - @species X(t) - @parameters p=1.0 d - p, 0 --> X - d, X --> 0 -end - -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:d => .1] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -Finally, default values can be overridden by passing mapping vectors to the -DifferentialEquations.jl problem being constructed. Only those initial conditions -or parameters for which we want to change their value from the default will need to be passed -```@example tut2 -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1] # we change p to 2.0 -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -## Constant/fixed species -It is possible to fix the amount of a species in a reaction. Without fixing -a species, a reaction could look like -```@example tut2 -rn = @reaction_network begin - k, X + Y --> 0 -end -``` - -```@example tut2 -ode_sys = convert(ODESystem, rn) -``` - -```@example tut2 -equations(ode_sys) -``` - -Fixing a species could either be achieved by modifying the reaction specification -and specifying constant species explicitly as species as described -[above](@ref dsl_description_explicit_species), i.e., -```@example tut2 -rn = @reaction_network begin - @species X(t) - k * X, Y --> 0 -end -``` - -```@example tut2 -ode_sys = convert(ODESystem, rn) -``` - -```@example tut2 -equations(ode_sys) -``` - -The species can of course also just be used as parameter - using the same modification -of the reaction, i.e., -```@example tut2 -rn = @reaction_network begin - k * X, Y --> 0 -end -``` - -```@example tut2 -ode_sys = convert(ODESystem, rn) -``` - -```@example tut2 -equations(ode_sys) -``` - -The same result can also be achieved by declaring a species as fixed/constant -without having to change the reaction itself, i.e., -```@example tut2 -rn = @reaction_network begin - @parameters X [isconstantspecies = true] - k, X + Y --> 0 -end -``` - -```@example tut2 -ode_sys = convert(ODESystem, rn) -``` - -```@example tut2 -equations(ode_sys) -``` - -## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) -It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: -```@example tut2 -rn = @reaction_network begin - @parameters X0 - @species X(t)=X0 - p, 0 --> X - d, X --> ∅ -end -``` -We can now simulate the network without providing any initial conditions: -```@example tut2 -u0 = [] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1, :X0 => 1.0] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -## Naming the generated `ReactionSystem` -ModelingToolkit uses system names to allow for compositional and hierarchical -models. To specify a name for the generated `ReactionSystem` via the -[`@reaction_network`](@ref) macro, just place the name before `begin`: -```@example tut2 -rn = @reaction_network production_degradation begin - p, ∅ --> X - d, X --> ∅ -end -ModelingToolkit.nameof(rn) == :production_degradation -``` - -## Pre-defined functions -Hill functions and a Michaelis-Menten function are pre-defined and can be used -as rate laws. Below, the pair of reactions within `rn1` are equivalent, as are -the pair of reactions within `rn2`: -```@example tut2 -rn1 = @reaction_network begin - hill(X,v,K,n), ∅ --> X - v*X^n/(X^n+K^n), ∅ --> X -end -``` -```@example tut2 -rn2 = @reaction_network begin - mm(X,v,K), ∅ --> X - v*X/(X+K), ∅ --> X -end -``` -Repressor Hill (`hillr`) and Michaelis-Menten (`mmr`) functions are also -provided: -```@example tut2 -rn1 = @reaction_network begin - hillr(X,v,K,n), ∅ --> X - v*K^n/(X^n+K^n), ∅ --> X -end -``` -```@example tut2 -rn2 = @reaction_network begin - mmr(X,v,K), ∅ --> X - v*K/(X+K), ∅ --> X -end -``` - -Please see the API [Rate Laws](@ref api_rate_laws) section for more details. - -## Including non-species variables -Non-species unknown variables can be specified in the DSL using the `@variables` -macro. These are declared similarly to species. For example, -```@example tut2 -rn_with_volume = @reaction_network begin - @variables V(t) - k*V, 0 --> A -end -``` -creates a network with one species -```@example tut2 -species(rn_with_volume) -``` -and one non-species -```@example tut2 -nonspecies(rn_with_volume) -``` -giving two unknown variables, always internally ordered by species and then -nonspecies: -```@example tut2 -unknowns(rn_with_volume) -``` - -`rn_with_volume` could then be extended with constraint equations for how `V(t)` -evolves in time, see the [associated tutorial](@ref constraint_equations). - -## Specifying alternative time variables and/or extra independent variables -While the DSL defaults to allowing `t` as the time variable, one can use the -`@ivs` macro to specify an alternative independent variable. For example, to -make `s` the default time variable one can say -```@example tut2 -rn_with_s = @reaction_network begin - @ivs s - @variables V(s) - @species B(s) - k, A + V*B --> C -end -show(stdout, MIME"text/plain"(), rn_with_s) # hide -``` -where we see all unknowns are now functions of `s`. - -Similarly, if one wants unknowns to be functions of more than one independent -variable, for example to encode a spatial problem, one can list more than one -variable, i.e. `@ivs t x y`. Here the first listed independent variable is -always chosen to represent time. For example, -```@example tut2 -rn_with_many_ivs = @reaction_network begin - @ivs s x - @variables V1(s) V2(s,x) - @species A(s) B(s,x) - k, V1*A --> V2*B + C -end -show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide -``` -Here again `s` will be the time variable, and any inferred species, `C` in this -case, are made functions of both variables, i.e. `C(s, x)`. - -## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) -The DSL allows Julia variables to be interpolated for the network name, within -rate constant expressions, or for species/stoichiometry within reactions. Using -the lower-level symbolic interface we can then define symbolic variables and -parameters outside of the macro, which can then be used within expressions in -the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) -tutorial for details on the lower-level symbolic interface). For example, -```@example tut2 -t = default_t() -@parameters k α -@species A(t) -spec = A -par = α -rate = k*A -name = :network -rn = @reaction_network $name begin - $rate*B, 2*$spec + $par*B --> $spec + C - end -``` -As the parameters `k` and `α` were pre-defined and appeared via interpolation, -we did not need to declare them within the `@reaction_network` macro, -i.e. they are automatically detected as parameters: -```@example tut2 -parameters(rn) -``` -as are the species coming from interpolated variables -```@example tut2 -species(rn) -``` - -!!! note - When using interpolation, expressions like `2$spec` won't work; the - multiplication symbol must be explicitly included like `2*$spec`. - -## Including observables -Sometimes, one might want to include observable variables. These are variables that can be computed directly from the other system variables (rather than having their values implicitly given through some differential equation). These can be introduced through the `@observables` option. - -Let us consider a simple example where two species ($X$ and $Y$) are produced and degraded at constant rates. They can also bind, forming a complex ($XY$). If we want to access the total amount of $X$ in the system we can create an observable that denotes this quantity ($Xtot = X + XY$). Here, we create observables for the total amount of $X$ and $Y$: -```@example obs1 -using Catalyst # hide -rn = @reaction_network begin - @observables begin - Xtot ~ X + XY - Ytot ~ Y + XY - end - (pX,dX), 0 <--> X - (pY,dY), 0 <--> Y - (kB,kD), X + Y <--> XY -end -``` -The `@observables` option is followed by one line for each observable formula (enclosed by a `begin ... end` block). The left-hand sides indicate the observables' names, and the right-hand sides how their values are computed. The two sides are separated by a `~`. - -If we now simulate our model: -```@example obs1 -using DifferentialEquations # hide -u0 = [:X => 0.0, :Y => 0.0, :XY => 0.0] -tspan = (0.0, 10.0) -ps = [:pX => 1.0, :dX => 0.2, :pY => 1.0, :dY => 0.5, :kB => 1.0, :kD => 0.2] -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(oprob) -nothing # hide -``` -we can index the solution using our observables (just like for [other variables](@ref simulation_structure_interfacing_solutions)). E.g. we can receive a vector with all $Xtot$ values using -```@example obs1 -sol[:Xtot] -``` -similarly, we can plot the values of $Xtot$ and $Ytot$ using -```@example obs1 -using Plots -plot(sol; idxs = [rn.Xtot, rn.Ytot], label = ["Total X" "Total Y"]) -``` - -If we only wish to provide a single observable, the `begin ... end` block is note required. E.g., to record only the total amount of $X$ we can use: -```@example obs1 -using Catalyst # hide -rn = @reaction_network begin - @observables Xtot ~ X + XY - (pX,dX), 0 <--> X - (pY,dY), 0 <--> Y - (kB,kD), X + Y <--> XY -end -``` - -Finally, some general rules for creating observables: -- Observables can depend on any species, parameters, or variables, but not on other observables. -- All observables components appearing on the right side of the `~` must be declared somewhere (i.e., they cannot only appear as a part of the observables formula). -- Only a single `@observables` option block can be used in each `@reaction_network` call. -- The left-hand side of the observables expression must be a single symbol, indicating the observable's name. -- Metadata can, however, be provided, e.g through `@observables (Xtot, [description="Total amount of X"]) ~ X + XY`. -- The right-hand side of the observables expression can be any valid algebraic expression. -- Observables are (by default, but this can be changed) considered `variables` (and not `species`). This can be changed by e.g. pre-declaring them using the `@species` option: -```@example obs2 -using Catalyst # hide -rn = @reaction_network begin - @species Xtot(t) - @observables Xtot ~ X1 + X2 - (k1,k2), X1 <--> X2 -end -nothing # hide -``` - -## Incorporating (differential) equations into reaction network models -Some models cannot be purely described as reaction networks. E.g. consider the growth of a cell, where the rate of change in the cell's volume depends on some growth factor. Here, the cell's volume would be described by a normal ODE. Such equations can be incorporated into a model using the `@equations` option. Here, we create a model where a growth factor ($G$) is produced and degraded at a linear rates, and the rate of change in cell volume ($V$) is linear in the amount of growth factor: -```@example eqs1 -using Catalyst #hide -rn = @reaction_network begin - @equations begin - D(V) ~ G - end - (p,d), 0 <--> G -end -``` -Here, `D(V)` indicates the (time) derivative with respect to `D`. The differential equation left and right hand sides are separated by a `~`. The left-hand side should contain differential only, the right hand side can contain any algebraic expression. - -We can check the differential equation corresponding to this reaction network using latexify: -```@example eqs1 -using Latexify -latexify(rn; form=:ode) -``` -We can also simulate it using the normal syntax -```@example eqs1 -using DifferentialEquations, Plots # hide -u0 = [:G => 0.0, :V => 0.1] -ps = [:p => 1.0, :d => 0.5] -oprob = ODEProblem(rn, u0, (0.0, 1.0), ps) -sol = solve(oprob) -plot(sol) -``` -Here, growth is indefinite. To improve the model, [a callback](@ref advanced_simulations_callbacks) can be used to half the volume (cell division) once some threshold is reached. - -When creating differential equations this way, the subject of the differential is automatically inferred to be a variable, however, any component on the right-hand side must be declare somewhere in the macro. E.g. to add a scaling parameter ($k$), we must declare that $k$ is a parameter using the `@parameters` option: -```@example eqs1 -rn = @reaction_network begin - @parameters k - @equations begin - D(V) ~ k*G - end - (p,d), 0 <--> G -end -nothing #hide -``` -If the differential does not appear isolated on the lhs, its subject variable must also be explicitly declared (as it is not inferred for these cases). - -It is possible to add several equations to the model. In this case, each have a separate line. E.g. to keep track of a supply of nutrition ($N$) in the growth media, we can use: -```@example eqs1 -rn = @reaction_network begin - @equations begin - D(V) ~ G - D(N) ~ -G - end - (p,d), 0 <--> G -end -nothing #hide -``` - -When only a single equation is added, the `begin ... end` statement can be omitted. E.g., the first model can be declared equivalently using: -```@example eqs1 -rn = @reaction_network begin - @equations D(V) ~ G - (p,d), 0 <--> G -end -nothing # hide -``` From 766e31270c8700293dbf1850dfdb75ae092ed32b Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 16 May 2024 16:58:29 -0400 Subject: [PATCH 11/23] up --- docs/src/model_creation/dsl_advanced.md | 16 +++--- docs/src/model_creation/dsl_basics.md | 76 ++++++++++++------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/docs/src/model_creation/dsl_advanced.md b/docs/src/model_creation/dsl_advanced.md index ec678daeb4..85f3cb948f 100644 --- a/docs/src/model_creation/dsl_advanced.md +++ b/docs/src/model_creation/dsl_advanced.md @@ -4,23 +4,23 @@ Within the Catalyst DSL, each line can represent either *a reaction* or *an opti All option designations begin with a declaration starting with `@`, followed by its input. E.g. the `@observables` option allows for the generation of observables. Each option can only be used once within each use of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that do not involve using an option. As a first step, we import Catalyst (which is required to run the tutorial): -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions using Catalyst ``` ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) [Previously](@ref ref), we mentioned that the DSL automatically determines which symbols correspond to species and which to parameters. This is done by designating everything that appears as either a substrate or a product as a species, and all remaining quantities as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive form (`Pᵢ`) to its active form (`Pₐ`) is catalysed by an enzyme (`E`). Using the most natural description: -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ end ``` `E` (as well as `k`) will be considered a parameter, something we can confirm directly: -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions parameters(catalysis_sys) ``` If we want `E` to be considered a species, we can designate this using the `@species` option: -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions catalysis_sys = @reaction_network begin @species E(t) k*E, Pᵢ --> Pₐ @@ -31,7 +31,7 @@ parameters(catalysis_sys) When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-`t` dependant species is also possible](@ref ref)). Similarly, the `@parameters` option can be used to explicitly designate something as a parameter: -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions catalysis_sys = @reaction_network begin @parameters k k*E, Pᵢ --> Pₐ @@ -42,7 +42,7 @@ Here, while `k` is explicitly defined as a parameter, no information is provided While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occurs as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). Rather than listing all species/parameters on a single line after the options, a `begin ... end` block can be used (listing one species/parameter on each line). E.g. in the following example we use this notation to explicitly designate all species and parameters of the system: -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions catalysis_sys = @reaction_network begin @species begin E(t) @@ -57,7 +57,7 @@ end ``` A side-effect of using the `@species` and `@parameter` options is that they specify *the order in which the species and parameters are stored*. I.e. lets check the order of the parameters in the parameters in the following dimerisation model: -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions dimerisation = @reaction_network begin (p,d), 0 <--> X (kB,kD), 2X <--> X2 @@ -65,7 +65,7 @@ end parameters(dimerisation) ``` The default order is typically equal to the order with which the parameters (or species) are encountered in the DSL (this is, however, not guaranteed). If we specify the parameters using `@parameters`, the order used within the option is used instead: -```@example dsl_advanced_1 +```@example dsl_advanced_explicit_definitions dimerisation = @reaction_network begin @parameters kB kD p d (p,d), 0 <--> X diff --git a/docs/src/model_creation/dsl_basics.md b/docs/src/model_creation/dsl_basics.md index 924da97753..19057d2d65 100644 --- a/docs/src/model_creation/dsl_basics.md +++ b/docs/src/model_creation/dsl_basics.md @@ -4,13 +4,13 @@ In the [introduction to Catalyst](@ref introduction_to_catalyst) we described ho The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction) or [compositionally](@ref compositional_modeling)). A summary of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial will solely focus on model creation. Before we begin, we will first load the Catalyst package (which is required to run the code). -```@example dsl_1 +```@example dsl_basics_intro using Catalyst ``` ### [Quick-start summary](@id dsl_description_quick_start) The DSL is initiated through the `@reaction_network` macro, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a [Michaelis-Menten enzyme kinetics system](@ref ref) can be written as -```@example dsl_0 +```@example dsl_basics_intro rn = @reaction_network begin (kB,kD), S + E <--> SE kP, SE --> P + E @@ -20,7 +20,7 @@ Here, `<-->` is used to create a bi-directional reaction (with forward rate `kP` ## [Basic syntax](@id dsl_description_basic_syntax) The basic syntax of the DSL is -```@example dsl_1 +```@example dsl_basics rn = @reaction_network begin 2.0, X --> Y 1.0, Y --> X @@ -37,7 +37,7 @@ Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia ## [Defining parameters and species in the DSL](@id dsl_description_parameters_basics) Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you wish to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: -```@example dsl_1 +```@example dsl_basics rn1 = @reaction_network begin a, X --> Y b, Y --> X @@ -45,7 +45,7 @@ end ``` Here we have used single-character symbols to designate all species and parameters. Multi-character symbols, however, are also permitted. E.g. we could call the rates `kX` and `kY`: -```@example dsl_1 +```@example dsl_basics rn1 = @reaction_network begin kX, X --> Y kY, Y --> X @@ -58,14 +58,14 @@ Generally, anything that is a [permitted Julia variable name](@id https://docs.j ### [Reactions with multiple substrates or products](@id dsl_description_reactions_multiples) Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate, at rate `kD`, to form `XY`) we use: -```@example dsl_1 +```@example dsl_basics rn2 = @reaction_network begin kB, X + Y --> XY kD, XY --> X + Y end ``` Reactions can have any number of substrates and products, and their names do not need to have any relationship to each other, as demonstrated by the following mock model: -```@example dsl_1 +```@example dsl_basics rn3 = @reaction_network begin k, X + Y + Z --> A + B + C + D end @@ -73,7 +73,7 @@ end ### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created (in the first reaction) and degraded (in a second reaction), we use: -```@example dsl_1 +```@example dsl_basics rn4 = @reaction_network begin p, 0 --> X d, X --> 0 @@ -82,7 +82,7 @@ end ### [Reactions with non-unitary stoichiometries](@id dsl_description_reactions_stoichiometries) Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating its number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: -```@example dsl_1 +```@example dsl_basics rn5 = @reaction_network begin kB, 2X --> X2 kD, X2 --> 2X @@ -91,7 +91,7 @@ end Reactants whose stoichiometries are not defined are assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affecting the created model can be found [here](@ref ref). Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction twice, both with and without this notation: -```@example dsl_1 +```@example dsl_basics rn6 = @reaction_network begin k, 2X + 3(Y + 2Z) --> 5(V + W) k, 2X + 3Y + 6Z --> 5V + 5W @@ -103,14 +103,14 @@ nothing # hide ### [Bi-directional (or reversible) reactions](@id dsl_description_reaction_bundling_reversible) As is the case for the following two-state model: -```@example dsl_1 +```@example dsl_basics rn7 = @reaction_network begin k1, X1 --> X2 k2, X2 --> X1 end ``` it is common that reactions occur in both directions (so-called *bi-directional* reactions). Here, it is possible to bundle the reactions into a single line by using the `<-->` arrow. When we do this, the rate term must include two separate rates (one for each direction, these are enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: -```@example dsl_1 +```@example dsl_basics rn7 = @reaction_network begin (k1,k2), X1 <--> X2 end @@ -118,13 +118,13 @@ end Here, the first rate (`k1`) denotes the *forward rate* and the second rate (`k2`) the *backwards rate*. Catalyst also permits writing pure backwards reactions. These use identical syntax to forward reactions, but with the `<--` arrow: -```@example dsl_1 +```@example dsl_basics rn8 = @reaction_network begin k, X <-- Y end ``` Here, the substrate(s) are on the right-hand side and the product(s) are on the left-hand side. Hence, the above model can be written identically using: -```@example dsl_1 +```@example dsl_basics rn8 = @reaction_network begin k, Y --> X end @@ -133,41 +133,41 @@ Generally, using forward reactions is clearer than backwards ones, with the latt ### [Bundling similar reactions on a single line](@id dsl_description_reaction_bundling_similar) There exist additional situations where models contain similar reactions (e.g. systems where all system components degrade at identical rates). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model where species `X` and `Y` both degrade at the rate `d`: -```@example dsl_1 +```@example dsl_basics rn8 = @reaction_network begin d, X --> 0 d, Y --> 0 end ``` These share both their rates (`d`) and products (`0`), however, the substrates are different (`X` and `Y`). Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: -```@example dsl_1 +```@example dsl_basics rn8 = @reaction_network begin d, (X,Y) --> 0 end ``` This declaration of the model is identical to the previous one. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: -```@example dsl_1 +```@example dsl_basics rn9 = @reaction_network begin dX, X --> 0 dY, Y --> 0 end ``` This can be represented using: -```@example dsl_1 +```@example dsl_basics rn9 = @reaction_network begin (dX,dY), (X,Y) --> 0 end ``` It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions use the same rate $k$): -```@example dsl_1 +```@example dsl_basics rn10 = @reaction_network begin k, (X0,X1,X2,X3) --> (X1,X2,X3,X4) end ``` It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rates. These may then (or may not) indicate several rates. We exemplify this using the two following two (identical) networks, created with and without bundling. -```@example dsl_1 +```@example dsl_basics rn11 = @reaction_network begin kf, S --> P1 kf, S --> P2 @@ -175,14 +175,14 @@ rn11 = @reaction_network begin kb_2, P2 --> S end ``` -```@example dsl_1 +```@example dsl_basics rn11 = @reaction_network begin (kf, (kb_1, kb_2)), S <--> (P1,P2) end ``` Like when we designated stoichiometries, reaction bundling can be applied very generally to create some truly complicated reactions: -```@example dsl_1 +```@example dsl_basics rn12 = @reaction_network begin ((pX, pY, pZ),d), (0, Y0, Z0) <--> (X, Y, Z1+Z2) end @@ -193,27 +193,27 @@ However, like for the above model, bundling reactions too zealously can reduce ( So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on `A` and a parameter (`kP`). We model this through: -```@example dsl_1 +```@example dsl_basics rn_13 = @reaction_network begin d, A --> 0 kP*A, 0 --> P end ``` Here, `P`'s production rate will be reduced as `A` decays. We can [print the ODE this model produces with `Latexify`](@ref ref): -```@example dsl_1 +```@example dsl_basics using Latexify latexify(rn_13; form=:ode) ``` In this case, we can generate an equivalent model by instead adding `A` as both a substrate and a product to `P`'s production reaction: -```@example dsl_1 +```@example dsl_basics rn_13_alt = @reaction_network begin d, A --> 0 kp, A --> A + P end ``` We can confirm that this generates the same ODE: -```@example dsl_1 +```@example dsl_basics latexify(rn_13_alt; form=:ode) ``` Here, while these models will generate identical ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. @@ -224,7 +224,7 @@ Here, while these models will generate identical ODE, SDE, and jump simulations, Catalyst automatically infers whether quantities appearing in the DSL are species or parameters (as described [here](@ref dsl_advanced_options_declaring_species_and_parameters)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref dsl_advanced_options_declaring_species_and_parameters). A full example of how to do this for this example can be found [here](@ref ref). Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: -```@example dsl_1 +```@example dsl_basics rn_14 = @reaction_network begin 2.0 + X^2, 0 --> X + Y k1 + k2^k3, X --> ∅ @@ -234,14 +234,14 @@ end ### [Using functions in rates](@id dsl_description_nonconstant_rates_functions) It is possible for the rate to contain Julia functions. These can either be functions from Julia's standard library: -```@example dsl_1 +```@example dsl_basics rn_16 = @reaction_network begin d, A --> 0 kp*sqrt(A), 0 --> P end ``` or ones defined by the user: -```@example dsl_1 +```@example dsl_basics custom_function(p1, p2, X) = (p1 + X) / (p2 + X) rn_17 = @reaction_network begin d, A --> 0 @@ -251,7 +251,7 @@ end ### [Pre-defined functions](@id dsl_description_nonconstant_rates_available_functions) Two functions frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where `X` activates its own production through a Hill function can be created using: -```@example dsl_1 +```@example dsl_basics rn_18 = @reaction_network begin hill(X,v,K,n), 0 --> P d, X --> 0 @@ -267,7 +267,7 @@ Catalyst comes with the following predefined functions: ### [Time-dependant rates](@id dsl_description_nonconstant_rates_time) Previously we have assumed that the rates are independent of the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progresses, we can use: -```@example dsl_1 +```@example dsl_basics rn_14 = @reaction_network begin kp/(1 + t), 0 --> P d, P --> 0 @@ -275,7 +275,7 @@ end ``` Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to represent a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: -```@example dsl_1 +```@example dsl_basics rn_15 = @reaction_network begin A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P d, P --> 0 @@ -289,7 +289,7 @@ end ### [Non-integer stoichiometries](@id dsl_description_stoichiometries_decimal) Previously all stoichiometric constants have been integer numbers, however, decimal numbers are also permitted. Here we create a birth-death model where each production reaction produces 1.5 units of `X`: -```@example dsl_1 +```@example dsl_basics rn_16 = @reaction_network begin p, 0 --> 1.5X d, X --> 0 @@ -299,13 +299,13 @@ It is also possible to have non-integer stoichiometric coefficients for substrat ### [Parametric stoichiometries](@id dsl_description_stoichiometries_parameters) It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` bind to form `Xn`: -```@example dsl_1 +```@example dsl_basics rn_17 = @reaction_network begin (kB,kD), n*X <--> Xn end ``` Now we can designate the value of `n` through a parameter when we e.g. create an `ODEProblem`: -```@example dsl_1 +```@example dsl_basics u0 = [:X => 5.0, :Xn => 1.0] ps = [:kB => 1.0, :kD => 0.1, :n => 4] oprob = ODEProblem(rn_17, u0, (0.0, 1.0), ps) @@ -317,7 +317,7 @@ Julia permits any Unicode characters to be used in variable names, thus Catalyst ### [Using ∅ in degradation/production reactions](@id dsl_description_symbols_empty_set) Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅` symbol. E.g. the production/degradation system can alternatively be written as: -```@example dsl_1 +```@example dsl_basics rn4 = @reaction_network begin p, ∅ --> X d, X --> ∅ @@ -331,7 +331,7 @@ Catalyst uses `-->`, `<-->`, and `<--` to denote forward, bi-directional, and ba - `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. E.g. the production/degradation system can alternatively be written as: -```@example dsl_1 +```@example dsl_basics rn4 = @reaction_network begin p, ∅ → X d, X → ∅ @@ -346,7 +346,7 @@ A range of possible characters are available which can be incorporated into spec - Other symbols (e.g. `£`, `ℂ`, `▲`, and `♠`). An example of how this can be used to create a neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used to model a sigma factor V circuit in the bacteria *Bacillus subtilis*: -```@example dsl_1 +```@example dsl_basics σᵛ_model = @reaction_network begin v₀ + hill(σᵛ,v,K,n), ∅ → σᵛ + A kdeg, (σᵛ, A, Aσᵛ) → ∅ From 0f26c9bfe015e5664cda62448101839c47396ece Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 17 May 2024 10:35:16 -0400 Subject: [PATCH 12/23] add sections on parameter types and vector species/parameters --- docs/src/model_creation/dsl_advanced.md | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/docs/src/model_creation/dsl_advanced.md b/docs/src/model_creation/dsl_advanced.md index 85f3cb948f..8a1026bbae 100644 --- a/docs/src/model_creation/dsl_advanced.md +++ b/docs/src/model_creation/dsl_advanced.md @@ -228,6 +228,52 @@ end A common use-case for constant species is when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is the [Brusselator](https://en.wikipedia.org/wiki/Brusselator). +### [Designating parameter types](@id dsl_advanced_options_parameter_types) +Sometimes it is desired to designate that a parameter should have a specific [type](@ref ref). When supplying this parameter's value to e.g. an `ODEProblem`, that parameter will then be restricted to that specific type. Designating a type is done by appending the parameter with `::` followed by its type. E.g. in the following example we specify that the parameter `n` (the number of `X` molecules in the `Xn` polymer) must be an integer (`Int64`) +```@example dsl_advanced_parameter_types +using Catalyst # hide +polymerisation_network = @reaction_network begin + @parameters n::Int64 + (kB,kD), n*X <--> Xn +end +nothing # hide +``` +Generally, when simulating models with mixed parameter types, it is recommended to [declare parameter values as tuples, rather than vectors](@ref ref), e.g.: +```@example dsl_advanced_parameter_types +ps = (:kB => 0.2, :kD => 1.0, :n => 2) +nothing # hide +``` + +If a parameter has a type, metadata, and a default value, they are designated in the following order: +```@example dsl_advanced_parameter_types +polymerisation_network = @reaction_network begin + @parameters n::Int64 = 2 [description="Parameter n, which is an integer and defaults to the value 2."] + (kB,kD), n*X <--> Xn +end +nothing # hide +``` + +### [Vector-valued species or parameters](@id dsl_advanced_options_vector_variables) +Sometimes, one wishes to declare a large number of similar parameters or species. This can be done by *creating them as vectors*. E.g. below we create a [two-state system](@ref ref). However, instead of declaring `X1` and `X2` (and `k1` and `k2`) as separate entities, we declare them as vectors: +```@example dsl_advanced_vector_variables +using Catalyst # hide +two_state_model = @reaction_network begin + @parameters k[1:2] + @species X(t)[1:2] + (k[1],k[2]), X[1] <--> X[2] +end +``` +Now, we can also declare our initial conditions and parameter values as vectors as well: +```@example dsl_advanced_vector_variables +using OrdinaryDiffEq, Plots # hide +u0 = [:X => [0.0, 2.0]] +tspan = (0.0, 1.0) +ps = [:k => [1.0, 2.0]] +oprob = ODEProblem(two_state_model, u0, tspan, ps) +sol = solve(oprob) +plot(sol) +``` + ## [Naming reaction networks](@id dsl_advanced_options_naming) Each reaction network model has a name. It can be accessed using the `nameof` function. By default, some generic name is used: ```@example dsl_advanced_names From 45916ed74e49a39657f4cca8eb7c4de171885df6 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 30 Jan 2024 15:08:56 -0500 Subject: [PATCH 13/23] init --- .../dsl_advanced_options.md | 1 + .../catalyst_functionality/dsl_description.md | 847 ++++++++++++++++++ 2 files changed, 848 insertions(+) create mode 100644 docs/src/catalyst_functionality/dsl_advanced_options.md create mode 100644 docs/src/catalyst_functionality/dsl_description.md diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md new file mode 100644 index 0000000000..7d305b64ac --- /dev/null +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -0,0 +1 @@ +# [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) \ No newline at end of file diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md new file mode 100644 index 0000000000..6e84f5bd4f --- /dev/null +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -0,0 +1,847 @@ +# [The Catalyst DSL - Introduction](@id dsl_description) +In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` macro can be used to create chemical reaction network models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating chemical reaction network (CRN) models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSLs more advanced features. + +The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of teh various ways to create `ReactionSystems`s can be found [here](@ref ref). + +Before we begin the tutorial, we will first load the `Catalyst` package (which is required to run the code). +```@example dsl_1 +using Catalyst +``` + +### QUick-start summary + + +## [Basic syntax](@id dsl_description_basic_syntax) +The basic syntax of the DSL is +```@example dsl_1 +rn = @reaction_network begin + 2.0, X --> Y + 1.0, Y --> X +end +``` +Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with an `end`. Each reaction consists of +- A rate +- A (potentially empty) set of substrates. +- A (potentially empty) set of products. + +Each reaction line declares, in order, the rate, the substrate(s), and the products. The rate is separated from the substrate(s) by a `,`, ad the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. + +Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). + +## [Defining parameters and species in the DSL](@id dsl_description_parameters_basics) +Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you which to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: +```@example dsl_1 +rn1 = @reaction_network begin + a, X --> Y + b, Y --> X +end +``` + +Here we have used single-character symbols to designate all species and parameters. Multi-character symbols, however, are also permitted. E.g. we could call the rates `kX` and `kY`: +```@example dsl_1 +rn1 = @reaction_network begin + kX, X --> Y + kY, Y --> X +end +nothing # hide +``` +Generally, anything that is a [permitted Julia variable name](@id https://docs.julialang.org/en/v1/manual/variables/#man-allowed-variable-names) can be used to designate a species or parameter in Catalyst. + +## [Different types of reactions](@id dsl_description_reactions) + +### Reactions with multiple substrate(s) or product(s) +Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` binds (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: +```@example dsl_1 +rn2 = @reaction_network begin + kB, X + Y --> XY + kD, XY --> X + Y +end +``` +Reactions can have any number of substrates and products, and their names does not need to have any relationship to each other, as demonstrated by the following mock-model: +```@example dsl_1 +rn3 = @reaction_network begin + k, X + Y + Z --> A + B + C + D +end +``` + +### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) +Some reactions has no products, in which case the substrate(s) are degraded (ie.e removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions has no substrate(s), in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species $X$ is both created and degraded, we use: +```@example dsl_1 +rn4 = @reaction_network begin + p, 0 --> X + d, X --> 0 +end +``` + +### Reactions with non-unitary stoichiometries +Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which may dissociate back to two `X` copies) we use: +```@example dsl_1 +rn5 = @reaction_network begin + kB, 2X --> X2 + kD, X2 --> 2X +end +``` +Reactant which stoichiometry is not defined is assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affects the created model can be found [here](@ref ref). + +Stoichiometries can be combined with `( )` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: +```@example dsl_1 +rn6 = @reaction_network begin + k, 2X + 3(Y + 2Z) --> 5(V + W) + k, 2X + 3Y + 6Z --> 5V + 5W +end +nothing # hide +``` + +## [Bundling of similar reactions](@id dsl_description_reaction_bundling) + +### Bi-directional (or reversible) reactions +As is the case for the following two-state model: +```@example dsl_1 +rn7 = @reaction_network begin + k1, X1 --> X2 + k2, X2 --> X1 +end +``` +it is common that reactions occurs in both direction. Here, it is possible ot bundle the reactions into a single line by using the `<-->` arrow. When we do this, we the rate term includes bother the rates (enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: +```@example dsl_1 +rn7 = @reaction_network begin + (k1,k2), X1 <--> X2 +end +``` +Here, the first rate (`k1`) denotes the *forward rate* and the second rate (`k2`) the *backwards rate*. + +Catalyst also permits writing backwards reactions only. This uses the identical syntax to when forward reactions are created, but using the `<--` arrow: +```@example dsl_1 +rn8 = @reaction_network begin + k, X <-- Y +end +``` +Here, the substrate(s) are on the right-hand side and the product(s) on the left-hand side. Hence, the above model can be written identically using: +```@example dsl_1 +rn8 = @reaction_network begin + k, Y --> X +end +``` +Generally, using forward reactions is clearer than backwards ones, with the letter generally not being used. + +### Bundling similar reactions on a singe line +There exists several other situation where models contain similar reactions (e.g. the systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following mode, where species $X$ and $Y$ both degrades at the rate $d$: +```@example dsl_1 +rn8 = @reaction_network begin + d, X --> 0 + d, Y --> 0 +end +``` +These share both the rates and products, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression. However, we have to provide two separate substrate expressions: +```@example dsl_1 +rn8 = @reaction_network begin + d, (X,Y) --> 0 +end +``` +This declaration of the model is identical to the one previously.Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, does not make sense to use). I.e. if the tw reactions had different degradation rates: +```@example dsl_1 +rn9 = @reaction_network begin + dX, X --> 0 + dY, Y --> 0 +end +``` +we could represent this using: +```@example dsl_1 +rn9 = @reaction_network begin + (dX,dY), (X,Y) --> 0 +end +``` + +It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions uses the same rate $k$): +```@example dsl_1 +rn10 = @reaction_network begin + k, (X0,X1,X2,X3) --> (X1,X2,X3,X4) +end +``` + +It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rate(s). These may then (or may not) indicate several rates. We exemplify this using the two following (identical) networks, created with and without bundling. +```@example dsl_1 +rn11 = @reaction_network begin + kf, S --> P1 + kf, S --> P2 + kb_1, P1 --> S + kb_2, P2 --> S +end +``` +```@example dsl_1 +rn11 = @reaction_network begin + (kf, (kb_1, kb_2)), S <--> (P1,P2) +end +``` + +Like when we designated stoichiometries, reaction bundling can be applied very generally to create some truly complicated reactions: +```@example dsl_1 +rn12 = @reaction_network begin + ((pX, pY, pZ),d), (0, Y0, Z0) <--> (X, Y, Z1+Z2) +end +``` +However, as for the above model, bundling reactions too zealously can reduce (rather improve) the model's readability. + +## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) +So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. + +Let us consider a model with an activator ($A$, which degraded at a constant rate) and a protein ($P$). The production rate of $P$ depends both on $A$ and parameter ($kp$). We model this through: +```@example dsl_1 +rn_13 = @reaction_network begin + d, A --> 0 + kp*A, 0 --> P +end +``` +Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`]: +```@example dsl_1 +using Latexify +latexify(rn_13; form=:ode) +``` + +In this case, we can generate an equivalent model by instead adding $A$ as both a substrate and a product to $A$'s production reaction: +```@example dsl_1 +rn_13_alt = @reaction_network begin + d, A --> 0 + kp, A --> A + P +end +``` +We can confirm that this generate the same ODE: +```@example dsl_1 +latexify(rn_13_alt; form=:ode) +``` +Here, while the model will generate the same ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. +!!! warn + While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). + +!!! danger + Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This mean that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). + + +### Time-dependant rates + +### Using functions in rates + +### Using pre-declared Michaelis-Menten or Hill function rate + + +## [Using special symbols](@id dsl_description_symbols) +Julia permits any unicode characters to be used in variable names, thus Catalyst are able to use these as well. Below we describe some case where these to alter the appearance of models. No functionality is, however, tied to this. + +### [Using ∅ to denote degradation/production reactions](@id dsl_description_symbols_empty_set) +Previously, we described how `0` could be used to [denote degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system alternatively can be written as: +```@example dsl_1 +rn4 = @reaction_network begin + p, ∅ --> X + d, X --> ∅ +end +``` + +### Using special arrow symbols +Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representation of these arrows are available. Here, +- `>`, `→`, `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, and `⇁` can be used to represent forward reactions. +- `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent reversible reactions. +- `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. + +E.g. the production/degradation system alternatively can be written as: +```@example dsl_1 +rn4 = @reaction_network begin + p, ∅ → X + d, X → ∅ +end +``` + +### Using special symbols to denote species or parameters +A range of possible characters are available which can be incorporated into species and parameter names. This includes, but is not limited to: +- Greek letters (e.g `α`, `σ`, `τ`, and `Ω`). +- Superscript and subscript characters (to create e.g `k₁`, `k₂`, `Xₐ`, and `Xᴾ`). +- Non-latin, non-greek, letters (e.g. `ä`, `Д`, `س`, and `א`). +- Other symbols (e.g. `£`, `ℂ`, `▲`, and `♠`). + +An example of how this can be used to create neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used it the creation a model of the sigma factor V circuit in the bacteria *Bacillus subtilis*: +```@example dsl_1 +σᵛ_model = @reaction_network begin + v₀ + hill(σᵛ,v,K,n), ∅ → σᵛ + A + kdeg, (σᵛ, A, Aσᵛ) → ∅ + (kB,kD), A + σᵛ ↔ Aσᵛ + L, Aσᵛ → σᵛ +end +nothing # hide +``` + +This functionality can also be used to create less-serious models: +rn_13 = @reaction_network begin + 🍦, 😢 --> 😃 +end + +It should be noted that the following symbols are *not permitted* to be used as species or parameter names: +- `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)) +- `pi` and `π` (used in Julia to denote [`3.1415926535897...`](https://en.wikipedia.org/wiki/Pi)). +- `t` (used to denote the [time variable](@ref ref)). +- `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)) +- `im` (used in Julia to represent [complex numbers](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/#Complex-Numbers)). +- `nothing` ([used in Julia](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). +- `Γ` (used by Catalyst to represent [conserved quantities](@ref ref)). + + + + + + +## [Basic syntax](@id basic_examples) + +The `@reaction_network` macro allows the (symbolic) specification of reaction +networks with a simple format. Its input is a set of chemical reactions, and +from them it generates a symbolic [`ReactionSystem`](@ref) reaction network +object. The `ReactionSystem` can be used as input to ModelingToolkit +`ODEProblem`, `NonlinearProblem`, `SteadyStateProblem`, `SDEProblem`, +`JumpProblem`, and more. `ReactionSystem`s can also be incrementally extended as +needed, allowing for programmatic construction of networks and network +composition. + +The basic syntax is: + +```@example dsl_1 +rn = @reaction_network begin + 2.0, X + Y --> XY + 1.0, XY --> Z1 + Z2 +end +``` +where each line of the [`@reaction_network`](@ref) macro corresponds to a +chemical reaction. Each reaction consists of a reaction rate (the expression on +the left-hand side of `,`), a set of substrates (the expression in-between `,` +and `-->`), and a set of products (the expression on the right-hand side of +`-->`). The substrates and the products may contain one or more reactants, +separated by `+`. The naming convention for these is the same as for normal +variables in Julia. + +The chemical reaction model is generated by the `@reaction_network` macro and +stored in the `rn` variable (a normal Julia variable, which does not need to be +called `rn`). It corresponds to a [`ReactionSystem`](@ref), a symbolic +representation of the chemical network. The generated `ReactionSystem` can be +converted to a symbolic differential equation model via +```@example dsl_1 +osys = convert(ODESystem, rn) +``` + +We can then convert the symbolic ODE model into a compiled, optimized +representation for use in the SciML ODE solvers by constructing an `ODEProblem`. +Creating an `ODEProblem` also requires our specifying the initial conditions for +the model. We do this by creating a mapping from each symbolic variable +representing a chemical species to its initial value +```@example dsl_1 +# define the symbolic variables +@variables t +@species X(t) Y(t) Z(t) XY(t) Z1(t) Z2(t) + +# create the mapping +u0 = [X => 1.0, Y => 1.0, XY => 1.0, Z1 => 1.0, Z2 => 1.0] +``` +Alternatively, we can create a mapping using Julia `Symbol`s for each variable, +and then convert them to a mapping involving symbolic variables like +```@example dsl_1 +u0 = symmap_to_varmap(rn, [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0]) +``` +Given the mapping, we can then create an `ODEProblem` from our symbolic `ODESystem` +```@example dsl_1 +tspan = (0.0, 1.0) # the time interval to solve on +oprob = ODEProblem(osys, u0, tspan, []) +``` + +Catalyst provides a shortcut to avoid having to explicitly `convert` to an +`ODESystem` and/or use `symmap_to_varmap`, allowing direct construction +of the `ODEProblem` like +```@example dsl_1 +u0 = [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0] +oprob = ODEProblem(rn, u0, tspan, []) +``` + +For more detailed examples, see the [Basic Chemical Reaction Network +Examples](@ref basic_CRN_examples). + +## Defining parameters and species +Numeric parameter values do not need to be set when the model is created, i.e. +Catalyst supports symbolic parameters too: +```@example dsl_1 +rn = @reaction_network begin + k1, X --> Y + k2, Y --> X +end +``` +All symbols that do not appear as a substrate or product in a reaction are +designated by Catalyst as a parameter (i.e. all symbols appearing only within +rate expressions and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). In this example `X` and `Y` +appear as a substrates and products, but neither `k1` nor `k2`. Hence `k1` and `k2` are +designated as parameters. Later in this tutorial, we will describe how to manually specify what should be +considered a species or parameter. + +## Production, Destruction, and Stoichiometry +Sometimes reactants are produced/destroyed from/to nothing. This can be +designated using either `0` or `∅`: +```@example dsl_1 +rn = @reaction_network begin + 2.0, 0 --> X + 1.0, X --> 0 +end +``` +If several molecules of the same reactant are involved in a reaction, the +stoichiometry of a reactant in a reaction can be set using a number. Here, two +molecules of species `X` form the dimer `X2`: +```@example dsl_1 +rn = @reaction_network begin + 1.0, 2X --> Y +end +``` +this corresponds to the differential equation: +```@example dsl_1 +convert(ODESystem, rn) +``` +Other numbers than 2 can be used, and parenthesis can be used to reuse the same +stoichiometry for several reactants: +```@example dsl_1 +rn = @reaction_network begin + 1.0, X + 2(Y + Z) --> W +end +``` +Note, one can explicitly multiply by integer coefficients too, i.e. +```@example dsl_1 +rn = @reaction_network begin + 1.0, X + 2*(Y + Z) --> W +end +``` + +## Arrow variants +A variety of Unicode arrows are accepted by the DSL in addition to `-->`. All of +these work: `>`, `→` `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, `⇁`. Backwards +arrows can also be used to write the reaction in the opposite direction. For +example, these reactions are equivalent: +```@example dsl_1 +rn = @reaction_network begin + 1.0, X + Y --> XY + 1.0, X + Y → XY + 1.0, XY ← X + Y + 1.0, XY <-- X + Y +end +``` + +## Bi-directional arrows for reversible reactions +Bi-directional arrows, including bidirectional Unicode arrows like ↔, can be +used to designate a reversible reaction. For example, these two models are +equivalent: +```@example dsl_1 +rn = @reaction_network begin + 2.0, X + Y --> XY + 2.0, X + Y <-- XY +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + (2.0,2.0), X + Y <--> XY +end +``` + +If the reaction rates in the backward and forward directions are different, they +can be designated in the following way: +```@example dsl_1 +rn = @reaction_network begin + (2.0,1.0), X + Y <--> XY +end +``` +which is identical to +```@example dsl_1 +rn = @reaction_network begin + 2.0, X + Y --> XY + 1.0, X + Y <-- XY +end +``` + +Finally, Catalyst also + +## Combining several reactions in one line +Several similar reactions can be combined in one line by providing a tuple of +reaction rates and/or substrates and/or products. If several tuples are provided, +they must all be of identical length. These pairs of reaction networks are all +identical. + +Pair 1: +```@example dsl_1 +rn1 = @reaction_network begin + 1.0, S --> (P1,P2) +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S --> P1 + 1.0, S --> P2 +end +``` +Pair 2: +```@example dsl_1 +rn1 = @reaction_network begin + (1.0,2.0), (S1,S2) --> P +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S1 --> P + 2.0, S2 --> P +end +``` +Pair 3: +```@example dsl_1 +rn1 = @reaction_network begin + (1.0,2.0,3.0), (S1,S2,S3) --> (P1,P2,P3) +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S1 --> P1 + 2.0, S2 --> P2 + 3.0, S3 --> P3 +end +``` +This can also be combined with bi-directional arrows, in which case separate +tuples can be provided for the backward and forward reaction rates. +These reaction networks are identical +```@example dsl_1 +rn1 = @reaction_network begin + (1.0,(1.0,2.0)), S <--> (P1,P2) +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + 1.0, S --> P1 + 1.0, S --> P2 + 1.0, P1 --> S + 2.0, P2 --> S +end +``` + +## Variable reaction rates +Reaction rates do not need to be a single parameter or a number, but can also be +expressions depending on time or the current amounts of system species (when, for +example, one species can activate the production of another). For instance, this +is a valid notation: +```@example dsl_1 +rn = @reaction_network begin + 1.0, X --> ∅ + k*X, Y --> ∅ +end +``` +corresponding to the ODE model +```@example dsl_1 +convert(ODESystem,rn) +``` + +With respect to the corresponding mass action ODE model, this is actually +equivalent to the reaction system +```@example dsl_1 +rn = @reaction_network begin + 1.0, X --> ∅ + k, X + Y --> X +end +``` +```@example dsl_1 +convert(ODESystem,rn) +``` +!!! note + While the ODE models corresponding to the preceding two reaction systems are + identical, in the latter example the `Reaction` stored in `rn` will be classified as + [`ismassaction`](@ref) while in the former it will not, which can impact optimizations + used in generating `JumpSystem`s. For this reason, it is recommended to use the + latter representation when possible. + +Most expressions and functions are valid reaction rates, e.g.: +```@example dsl_1 +using SpecialFunctions +rn = @reaction_network begin + 2.0*X^2, 0 --> X + Y + t*gamma(Y), X --> ∅ + pi*X/Y, Y --> ∅ +end +``` +where here `t` always denotes Catalyst's time variable. Please note that many +user-defined functions can be called directly, but others will require +registration with Symbolics.jl ([see the faq](@ref user_functions)). + +## Explicit specification of network species and parameters +Recall that the `@reaction_network` macro automatically designates symbols used +in the macro as either parameters or species, with symbols that appear as a +substrate or product being species, and all other symbols becoming parameters +(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default +behavior for a given symbol. E.g one might want something to be considered as a +species, even if it only appears within a rate expression. In the following +network +```@example dsl_1 +rn = @reaction_network begin + k*X, Y --> 0 +end +``` +`X` (as well as `k`) will be considered a parameter. + +By using the `@species` and `@parameters` options within the `@reaction_network` +macro, one can manually declare that specified symbols should be considered a +species or parameter. E.g in: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + k*X, Y --> 0 +end +``` +`X` and `Y` are set as species. Please note that when declaring species using +the `@species` option, their dependant variable (almost always `t`) also needs +to be designated. Similarly in +```@example dsl_1 +rn = @reaction_network begin + @parameters k + k*X, Y --> 0 +end +``` +both `X` and `k` will be considered as parameters. It is also possible to use +both options simultaneously, allowing users to fully specify which symbols are +species and/or parameters: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + @parameters k + k*X, Y --> 0 +end +``` +Here, `X` and `Y` are designated as species and `k` as a parameter. + +The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + k*X, Y --> 0 +end +``` + +Finally, note that the `@species` and `@parameters` options can also be used in +`begin ... end` block form, allowing more formatted lists of species/parameters: +```@example dsl_1 +rn = @reaction_network begin + @parameters begin + d1 + d2 + end + @species begin + X1(t) + X2(t) + end + d2, X2 --> 0 + d1, X1 --> 0 +end +``` +This can be especially useful when declaring default values for clarity of model +specification (see the next section). + +## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) +When using the `@species` and ` @parameters` macros to declare species and/or +parameters, one can also provide default initial conditions for each species and +values for each parameter: +```@example dsl_1 +rn = @reaction_network begin + @species X(t)=1.0 + @parameters p=1.0 d=0.1 + p, 0 --> X + d, X --> ∅ +end +``` +This system can now be simulated without providing initial condition or +parameter vectors to the DifferentialEquations.jl solvers: +```@example dsl_1 +using DifferentialEquations, Plots +u0 = [] +tspan = (0.0, 10.0) +p = [] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +When providing default values, it is possible to do so for only a subset of the +species or parameters, in which case the rest can be specified when constructing +the problem type to solve: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + @parameters p=1.0 d + p, 0 --> X + d, X --> 0 +end + +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:d => .1] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +Finally, default values can be overridden by passing mapping vectors to the +DifferentialEquations.jl problem being constructed. Only those initial conditions +or parameters for which we want to change their value from the default will need to be passed +```@example dsl_1 +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1] # we change p to 2.0 +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) +It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: +```@example dsl_1 +rn = @reaction_network begin + @parameters X0 + @species X(t)=X0 + p, 0 --> X + d, X --> ∅ +end +``` +We can now simulate the network without providing any initial conditions: +```@example dsl_1 +u0 = [] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1, :X0 => 1.0] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## Naming the generated `ReactionSystem` +ModelingToolkit uses system names to allow for compositional and hierarchical +models. To specify a name for the generated `ReactionSystem` via the +[`@reaction_network`](@ref) macro, just place the name before `begin`: +```@example dsl_1 +rn = @reaction_network production_degradation begin + p, ∅ --> X + d, X --> ∅ +end +ModelingToolkit.nameof(rn) == :production_degradation +``` + +## Pre-defined functions +Hill functions and a Michaelis-Menten function are pre-defined and can be used +as rate laws. Below, the pair of reactions within `rn1` are equivalent, as are +the pair of reactions within `rn2`: +```@example dsl_1 +rn1 = @reaction_network begin + hill(X,v,K,n), ∅ --> X + v*X^n/(X^n+K^n), ∅ --> X +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + mm(X,v,K), ∅ --> X + v*X/(X+K), ∅ --> X +end +``` +Repressor Hill (`hillr`) and Michaelis-Menten (`mmr`) functions are also +provided: +```@example dsl_1 +rn1 = @reaction_network begin + hillr(X,v,K,n), ∅ --> X + v*K^n/(X^n+K^n), ∅ --> X +end +``` +```@example dsl_1 +rn2 = @reaction_network begin + mmr(X,v,K), ∅ --> X + v*K/(X+K), ∅ --> X +end +``` + +Please see the API [Rate Laws](@ref api_rate_laws) section for more details. + +## Including non-species variables +Non-species state variables can be specified in the DSL using the `@variables` +macro. These are declared similarly to species. For example, +```@example dsl_1 +rn_with_volume = @reaction_network begin + @variables V(t) + k*V, 0 --> A +end +``` +creates a network with one species +```@example dsl_1 +species(rn_with_volume) +``` +and one non-species +```@example dsl_1 +nonspecies(rn_with_volume) +``` +giving two state variables, always internally ordered by species and then +nonspecies: +```@example dsl_1 +states(rn_with_volume) +``` + +`rn_with_volume` could then be extended with constraint equations for how `V(t)` +evolves in time, see the [associated tutorial](@ref constraint_equations). + +## Specifying alternative time variables and/or extra independent variables +While the DSL defaults to allowing `t` as the time variable, one can use the +`@ivs` macro to specify an alternative independent variable. For example, to +make `s` the default time variable one can say +```@example dsl_1 +rn_with_s = @reaction_network begin + @ivs s + @variables V(s) + @species B(s) + k, A + V*B --> C +end +show(stdout, MIME"text/plain"(), rn_with_s) # hide +``` +where we see all states are now functions of `s`. + +Similarly, if one wants states to be functions of more than one independent +variable, for example to encode a spatial problem, one can list more than one +variable, i.e. `@ivs t x y`. Here the first listed independent variable is +always chosen to represent time. For example, +```@example dsl_1 +rn_with_many_ivs = @reaction_network begin + @ivs s x + @variables V1(s) V2(s,x) + @species A(s) B(s,x) + k, V1*A --> V2*B + C +end +show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide +``` +Here again `s` will be the time variable, and any inferred species, `C` in this +case, are made functions of both variables, i.e. `C(s, x)`. + +## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) +The DSL allows Julia variables to be interpolated for the network name, within +rate constant expressions, or for species/stoichiometry within reactions. Using +the lower-level symbolic interface we can then define symbolic variables and +parameters outside of the macro, which can then be used within expressions in +the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) +tutorial for details on the lower-level symbolic interface). For example, +```@example dsl_1 +@parameters k α +@variables t +@species A(t) +spec = A +par = α +rate = k*A +name = :network +rn = @reaction_network $name begin + $rate*B, 2*$spec + $par*B --> $spec + C + end +``` +As the parameters `k` and `α` were pre-defined and appeared via interpolation, +we did not need to declare them within the `@reaction_network` macro, +i.e. they are automatically detected as parameters: +```@example dsl_1 +parameters(rn) +``` +as are the species coming from interpolated variables +```@example dsl_1 +species(rn) +``` + +!!! note + When using interpolation, expressions like `2$spec` won't work; the + multiplication symbol must be explicitly included like `2*$spec`. From bab5c25908b97d9da0711889e94dd682546badc5 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 30 Jan 2024 16:46:45 -0500 Subject: [PATCH 14/23] save changes --- .../dsl_advanced_options.md | 280 +++++++- .../catalyst_functionality/dsl_description.md | 616 ++---------------- 2 files changed, 335 insertions(+), 561 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index 7d305b64ac..7af4fec0c7 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -1 +1,279 @@ -# [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) \ No newline at end of file +# [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) + +Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous tutorial](@ref dsl_description) described how to create reactions. This one will instead describe how to use options. These are typically used to supplant the model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. + +All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here], with most (but not all) being described in more detail below. + + This tutorial will also describe some additional advanced DSL features that does not include using an option. + +## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) +Previously, we mentioned that + +### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) + +### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) + +### [Specifying non-species variables](@id dsl_advanced_options_variables) + +### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) + +## [Setting reaction metadata](@id dsl_advanced_options_) + +## [Naming reaction networks](@id dsl_advanced_options_) + +## [Creating observables](@id dsl_advanced_options_) + +## [Creating events](@id dsl_advanced_options_) + +## [Specifying non-time independent variables](@id dsl_advanced_options_) + + +## Explicit specification of network species and parameters +Recall that the `@reaction_network` macro automatically designates symbols used +in the macro as either parameters or species, with symbols that appear as a +substrate or product being species, and all other symbols becoming parameters +(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default +behavior for a given symbol. E.g one might want something to be considered as a +species, even if it only appears within a rate expression. In the following +network +```@example dsl_1 +rn = @reaction_network begin + k*X, Y --> 0 +end +``` +`X` (as well as `k`) will be considered a parameter. + +By using the `@species` and `@parameters` options within the `@reaction_network` +macro, one can manually declare that specified symbols should be considered a +species or parameter. E.g in: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + k*X, Y --> 0 +end +``` +`X` and `Y` are set as species. Please note that when declaring species using +the `@species` option, their dependant variable (almost always `t`) also needs +to be designated. Similarly in +```@example dsl_1 +rn = @reaction_network begin + @parameters k + k*X, Y --> 0 +end +``` +both `X` and `k` will be considered as parameters. It is also possible to use +both options simultaneously, allowing users to fully specify which symbols are +species and/or parameters: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) Y(t) + @parameters k + k*X, Y --> 0 +end +``` +Here, `X` and `Y` are designated as species and `k` as a parameter. + +The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + k*X, Y --> 0 +end +``` + +Finally, note that the `@species` and `@parameters` options can also be used in +`begin ... end` block form, allowing more formatted lists of species/parameters: +```@example dsl_1 +rn = @reaction_network begin + @parameters begin + d1 + d2 + end + @species begin + X1(t) + X2(t) + end + d2, X2 --> 0 + d1, X1 --> 0 +end +``` +This can be especially useful when declaring default values for clarity of model +specification (see the next section). + +## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) +When using the `@species` and ` @parameters` macros to declare species and/or +parameters, one can also provide default initial conditions for each species and +values for each parameter: +```@example dsl_1 +rn = @reaction_network begin + @species X(t)=1.0 + @parameters p=1.0 d=0.1 + p, 0 --> X + d, X --> ∅ +end +``` +This system can now be simulated without providing initial condition or +parameter vectors to the DifferentialEquations.jl solvers: +```@example dsl_1 +using DifferentialEquations, Plots +u0 = [] +tspan = (0.0, 10.0) +p = [] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +When providing default values, it is possible to do so for only a subset of the +species or parameters, in which case the rest can be specified when constructing +the problem type to solve: +```@example dsl_1 +rn = @reaction_network begin + @species X(t) + @parameters p=1.0 d + p, 0 --> X + d, X --> 0 +end + +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:d => .1] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +Finally, default values can be overridden by passing mapping vectors to the +DifferentialEquations.jl problem being constructed. Only those initial conditions +or parameters for which we want to change their value from the default will need to be passed +```@example dsl_1 +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1] # we change p to 2.0 +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) +It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: +```@example dsl_1 +rn = @reaction_network begin + @parameters X0 + @species X(t)=X0 + p, 0 --> X + d, X --> ∅ +end +``` +We can now simulate the network without providing any initial conditions: +```@example dsl_1 +u0 = [] +tspan = (0.0, 10.0) +p = [:p => 2.0, :d => .1, :X0 => 1.0] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob) +plot(sol) +``` + +## Naming the generated `ReactionSystem` +ModelingToolkit uses system names to allow for compositional and hierarchical +models. To specify a name for the generated `ReactionSystem` via the +[`@reaction_network`](@ref) macro, just place the name before `begin`: +```@example dsl_1 +rn = @reaction_network production_degradation begin + p, ∅ --> X + d, X --> ∅ +end +ModelingToolkit.nameof(rn) == :production_degradation +``` + +## Including non-species variables +Non-species state variables can be specified in the DSL using the `@variables` +macro. These are declared similarly to species. For example, +```@example dsl_1 +rn_with_volume = @reaction_network begin + @variables V(t) + k*V, 0 --> A +end +``` +creates a network with one species +```@example dsl_1 +species(rn_with_volume) +``` +and one non-species +```@example dsl_1 +nonspecies(rn_with_volume) +``` +giving two state variables, always internally ordered by species and then +nonspecies: +```@example dsl_1 +states(rn_with_volume) +``` + +`rn_with_volume` could then be extended with constraint equations for how `V(t)` +evolves in time, see the [associated tutorial](@ref constraint_equations). + +## Specifying alternative time variables and/or extra independent variables +While the DSL defaults to allowing `t` as the time variable, one can use the +`@ivs` macro to specify an alternative independent variable. For example, to +make `s` the default time variable one can say +```@example dsl_1 +rn_with_s = @reaction_network begin + @ivs s + @variables V(s) + @species B(s) + k, A + V*B --> C +end +show(stdout, MIME"text/plain"(), rn_with_s) # hide +``` +where we see all states are now functions of `s`. + +Similarly, if one wants states to be functions of more than one independent +variable, for example to encode a spatial problem, one can list more than one +variable, i.e. `@ivs t x y`. Here the first listed independent variable is +always chosen to represent time. For example, +```@example dsl_1 +rn_with_many_ivs = @reaction_network begin + @ivs s x + @variables V1(s) V2(s,x) + @species A(s) B(s,x) + k, V1*A --> V2*B + C +end +show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide +``` +Here again `s` will be the time variable, and any inferred species, `C` in this +case, are made functions of both variables, i.e. `C(s, x)`. + +## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) +The DSL allows Julia variables to be interpolated for the network name, within +rate constant expressions, or for species/stoichiometry within reactions. Using +the lower-level symbolic interface we can then define symbolic variables and +parameters outside of the macro, which can then be used within expressions in +the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) +tutorial for details on the lower-level symbolic interface). For example, +```@example dsl_1 +@parameters k α +@variables t +@species A(t) +spec = A +par = α +rate = k*A +name = :network +rn = @reaction_network $name begin + $rate*B, 2*$spec + $par*B --> $spec + C + end +``` +As the parameters `k` and `α` were pre-defined and appeared via interpolation, +we did not need to declare them within the `@reaction_network` macro, +i.e. they are automatically detected as parameters: +```@example dsl_1 +parameters(rn) +``` +as are the species coming from interpolated variables +```@example dsl_1 +species(rn) +``` + +!!! note + When using interpolation, expressions like `2$spec` won't work; the + multiplication symbol must be explicitly included like `2*$spec`. diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index 6e84f5bd4f..c70d6a259c 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -1,14 +1,14 @@ # [The Catalyst DSL - Introduction](@id dsl_description) In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` macro can be used to create chemical reaction network models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating chemical reaction network (CRN) models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSLs more advanced features. -The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of teh various ways to create `ReactionSystems`s can be found [here](@ref ref). +The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial wil solely focus on options for model creation. Before we begin the tutorial, we will first load the `Catalyst` package (which is required to run the code). ```@example dsl_1 using Catalyst ``` -### QUick-start summary +### Quick-start summary ## [Basic syntax](@id dsl_description_basic_syntax) @@ -192,7 +192,7 @@ rn_13 = @reaction_network begin kp*A, 0 --> P end ``` -Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`]: +Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`](@ref ref): ```@example dsl_1 using Latexify latexify(rn_13; form=:ode) @@ -216,12 +216,65 @@ Here, while the model will generate the same ODE, SDE, and jump simulations, the !!! danger Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This mean that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). +Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: +```@example dsl_1 +rn_14 = @reaction_network begin + 2.0 + X^2, 0 --> X + Y + k1+k2^k3, X --> ∅ + pi*X/(sqrt(2)+Y), Y → ∅ +end +``` ### Time-dependant rates +Previously we have assumed that that the rates are independent on the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progress, we can use: +```@example dsl_1 +rn_14 = @reaction_network begin + kp/t, 0 --> P + d, P --> 0 +end +``` + +Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to representing a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: +```@example dsl_1 +rn_15 = @reaction_network begin + A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P + d, P --> 0 +end +``` ### Using functions in rates +It is possible for the rate to use Julia function. These can either be functions from Julia's standard library: +```@example dsl_1 +rn_16 = @reaction_network begin + d, A --> 0 + kp*sqrt(A), 0 --> P +end +``` +or ones defined by the user: +```@example dsl_1 +custom_function(p1,p2,X) = (p1+E)/(p2+E) +rn_17 = @reaction_network begin + d, A --> 0 + custom_function(k1,k2,E), 0 --> P +end +``` ### Using pre-declared Michaelis-Menten or Hill function rate +Two function frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where $X$ activates its own production through a hill function can be created using: +```@example dsl_1 +custom_function(p1,p2,X) = (p1+E)/(p2+E) +rn_18 = @reaction_network begin + hill(X,v,K,n), 0 --> P + d, X --> 0 +end +``` + +Catalyst comes with the following predefined functions: +- The Michaelis-Menten function: $mm(X,v,K) = v*X/(X + K)$. +- The repressive Michaelis-Menten function: $mmr(X,v,K) = v*K/(X + K)$. +- The Hill function: $hill(X,v,K,n) = v*(X^n)/(X^n + K^n)$. +- The repressive Hill function: $hillr(X,v,K,n) = v*(K^n)/(X^n + K^n)$. +- The activating/repressive Hill function: $hillar(X,Y,v,K,n) = v*(X^n)/(X^n + Y^n + K^n)$. ## [Using special symbols](@id dsl_description_symbols) @@ -287,561 +340,4 @@ It should be noted that the following symbols are *not permitted* to be used as -## [Basic syntax](@id basic_examples) - -The `@reaction_network` macro allows the (symbolic) specification of reaction -networks with a simple format. Its input is a set of chemical reactions, and -from them it generates a symbolic [`ReactionSystem`](@ref) reaction network -object. The `ReactionSystem` can be used as input to ModelingToolkit -`ODEProblem`, `NonlinearProblem`, `SteadyStateProblem`, `SDEProblem`, -`JumpProblem`, and more. `ReactionSystem`s can also be incrementally extended as -needed, allowing for programmatic construction of networks and network -composition. - -The basic syntax is: - -```@example dsl_1 -rn = @reaction_network begin - 2.0, X + Y --> XY - 1.0, XY --> Z1 + Z2 -end -``` -where each line of the [`@reaction_network`](@ref) macro corresponds to a -chemical reaction. Each reaction consists of a reaction rate (the expression on -the left-hand side of `,`), a set of substrates (the expression in-between `,` -and `-->`), and a set of products (the expression on the right-hand side of -`-->`). The substrates and the products may contain one or more reactants, -separated by `+`. The naming convention for these is the same as for normal -variables in Julia. - -The chemical reaction model is generated by the `@reaction_network` macro and -stored in the `rn` variable (a normal Julia variable, which does not need to be -called `rn`). It corresponds to a [`ReactionSystem`](@ref), a symbolic -representation of the chemical network. The generated `ReactionSystem` can be -converted to a symbolic differential equation model via -```@example dsl_1 -osys = convert(ODESystem, rn) -``` - -We can then convert the symbolic ODE model into a compiled, optimized -representation for use in the SciML ODE solvers by constructing an `ODEProblem`. -Creating an `ODEProblem` also requires our specifying the initial conditions for -the model. We do this by creating a mapping from each symbolic variable -representing a chemical species to its initial value -```@example dsl_1 -# define the symbolic variables -@variables t -@species X(t) Y(t) Z(t) XY(t) Z1(t) Z2(t) - -# create the mapping -u0 = [X => 1.0, Y => 1.0, XY => 1.0, Z1 => 1.0, Z2 => 1.0] -``` -Alternatively, we can create a mapping using Julia `Symbol`s for each variable, -and then convert them to a mapping involving symbolic variables like -```@example dsl_1 -u0 = symmap_to_varmap(rn, [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0]) -``` -Given the mapping, we can then create an `ODEProblem` from our symbolic `ODESystem` -```@example dsl_1 -tspan = (0.0, 1.0) # the time interval to solve on -oprob = ODEProblem(osys, u0, tspan, []) -``` - -Catalyst provides a shortcut to avoid having to explicitly `convert` to an -`ODESystem` and/or use `symmap_to_varmap`, allowing direct construction -of the `ODEProblem` like -```@example dsl_1 -u0 = [:X => 1.0, :Y => 1.0, :XY => 1.0, :Z1 => 1.0, :Z2 => 1.0] -oprob = ODEProblem(rn, u0, tspan, []) -``` - -For more detailed examples, see the [Basic Chemical Reaction Network -Examples](@ref basic_CRN_examples). - -## Defining parameters and species -Numeric parameter values do not need to be set when the model is created, i.e. -Catalyst supports symbolic parameters too: -```@example dsl_1 -rn = @reaction_network begin - k1, X --> Y - k2, Y --> X -end -``` -All symbols that do not appear as a substrate or product in a reaction are -designated by Catalyst as a parameter (i.e. all symbols appearing only within -rate expressions and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). In this example `X` and `Y` -appear as a substrates and products, but neither `k1` nor `k2`. Hence `k1` and `k2` are -designated as parameters. Later in this tutorial, we will describe how to manually specify what should be -considered a species or parameter. - -## Production, Destruction, and Stoichiometry -Sometimes reactants are produced/destroyed from/to nothing. This can be -designated using either `0` or `∅`: -```@example dsl_1 -rn = @reaction_network begin - 2.0, 0 --> X - 1.0, X --> 0 -end -``` -If several molecules of the same reactant are involved in a reaction, the -stoichiometry of a reactant in a reaction can be set using a number. Here, two -molecules of species `X` form the dimer `X2`: -```@example dsl_1 -rn = @reaction_network begin - 1.0, 2X --> Y -end -``` -this corresponds to the differential equation: -```@example dsl_1 -convert(ODESystem, rn) -``` -Other numbers than 2 can be used, and parenthesis can be used to reuse the same -stoichiometry for several reactants: -```@example dsl_1 -rn = @reaction_network begin - 1.0, X + 2(Y + Z) --> W -end -``` -Note, one can explicitly multiply by integer coefficients too, i.e. -```@example dsl_1 -rn = @reaction_network begin - 1.0, X + 2*(Y + Z) --> W -end -``` - -## Arrow variants -A variety of Unicode arrows are accepted by the DSL in addition to `-->`. All of -these work: `>`, `→` `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, `⇁`. Backwards -arrows can also be used to write the reaction in the opposite direction. For -example, these reactions are equivalent: -```@example dsl_1 -rn = @reaction_network begin - 1.0, X + Y --> XY - 1.0, X + Y → XY - 1.0, XY ← X + Y - 1.0, XY <-- X + Y -end -``` - -## Bi-directional arrows for reversible reactions -Bi-directional arrows, including bidirectional Unicode arrows like ↔, can be -used to designate a reversible reaction. For example, these two models are -equivalent: -```@example dsl_1 -rn = @reaction_network begin - 2.0, X + Y --> XY - 2.0, X + Y <-- XY -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - (2.0,2.0), X + Y <--> XY -end -``` - -If the reaction rates in the backward and forward directions are different, they -can be designated in the following way: -```@example dsl_1 -rn = @reaction_network begin - (2.0,1.0), X + Y <--> XY -end -``` -which is identical to -```@example dsl_1 -rn = @reaction_network begin - 2.0, X + Y --> XY - 1.0, X + Y <-- XY -end -``` - -Finally, Catalyst also - -## Combining several reactions in one line -Several similar reactions can be combined in one line by providing a tuple of -reaction rates and/or substrates and/or products. If several tuples are provided, -they must all be of identical length. These pairs of reaction networks are all -identical. - -Pair 1: -```@example dsl_1 -rn1 = @reaction_network begin - 1.0, S --> (P1,P2) -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S --> P1 - 1.0, S --> P2 -end -``` -Pair 2: -```@example dsl_1 -rn1 = @reaction_network begin - (1.0,2.0), (S1,S2) --> P -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S1 --> P - 2.0, S2 --> P -end -``` -Pair 3: -```@example dsl_1 -rn1 = @reaction_network begin - (1.0,2.0,3.0), (S1,S2,S3) --> (P1,P2,P3) -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S1 --> P1 - 2.0, S2 --> P2 - 3.0, S3 --> P3 -end -``` -This can also be combined with bi-directional arrows, in which case separate -tuples can be provided for the backward and forward reaction rates. -These reaction networks are identical -```@example dsl_1 -rn1 = @reaction_network begin - (1.0,(1.0,2.0)), S <--> (P1,P2) -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - 1.0, S --> P1 - 1.0, S --> P2 - 1.0, P1 --> S - 2.0, P2 --> S -end -``` - -## Variable reaction rates -Reaction rates do not need to be a single parameter or a number, but can also be -expressions depending on time or the current amounts of system species (when, for -example, one species can activate the production of another). For instance, this -is a valid notation: -```@example dsl_1 -rn = @reaction_network begin - 1.0, X --> ∅ - k*X, Y --> ∅ -end -``` -corresponding to the ODE model -```@example dsl_1 -convert(ODESystem,rn) -``` - -With respect to the corresponding mass action ODE model, this is actually -equivalent to the reaction system -```@example dsl_1 -rn = @reaction_network begin - 1.0, X --> ∅ - k, X + Y --> X -end -``` -```@example dsl_1 -convert(ODESystem,rn) -``` -!!! note - While the ODE models corresponding to the preceding two reaction systems are - identical, in the latter example the `Reaction` stored in `rn` will be classified as - [`ismassaction`](@ref) while in the former it will not, which can impact optimizations - used in generating `JumpSystem`s. For this reason, it is recommended to use the - latter representation when possible. - -Most expressions and functions are valid reaction rates, e.g.: -```@example dsl_1 -using SpecialFunctions -rn = @reaction_network begin - 2.0*X^2, 0 --> X + Y - t*gamma(Y), X --> ∅ - pi*X/Y, Y --> ∅ -end -``` -where here `t` always denotes Catalyst's time variable. Please note that many -user-defined functions can be called directly, but others will require -registration with Symbolics.jl ([see the faq](@ref user_functions)). - -## Explicit specification of network species and parameters -Recall that the `@reaction_network` macro automatically designates symbols used -in the macro as either parameters or species, with symbols that appear as a -substrate or product being species, and all other symbols becoming parameters -(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default -behavior for a given symbol. E.g one might want something to be considered as a -species, even if it only appears within a rate expression. In the following -network -```@example dsl_1 -rn = @reaction_network begin - k*X, Y --> 0 -end -``` -`X` (as well as `k`) will be considered a parameter. - -By using the `@species` and `@parameters` options within the `@reaction_network` -macro, one can manually declare that specified symbols should be considered a -species or parameter. E.g in: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) Y(t) - k*X, Y --> 0 -end -``` -`X` and `Y` are set as species. Please note that when declaring species using -the `@species` option, their dependant variable (almost always `t`) also needs -to be designated. Similarly in -```@example dsl_1 -rn = @reaction_network begin - @parameters k - k*X, Y --> 0 -end -``` -both `X` and `k` will be considered as parameters. It is also possible to use -both options simultaneously, allowing users to fully specify which symbols are -species and/or parameters: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) Y(t) - @parameters k - k*X, Y --> 0 -end -``` -Here, `X` and `Y` are designated as species and `k` as a parameter. - -The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) - k*X, Y --> 0 -end -``` - -Finally, note that the `@species` and `@parameters` options can also be used in -`begin ... end` block form, allowing more formatted lists of species/parameters: -```@example dsl_1 -rn = @reaction_network begin - @parameters begin - d1 - d2 - end - @species begin - X1(t) - X2(t) - end - d2, X2 --> 0 - d1, X1 --> 0 -end -``` -This can be especially useful when declaring default values for clarity of model -specification (see the next section). - -## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) -When using the `@species` and ` @parameters` macros to declare species and/or -parameters, one can also provide default initial conditions for each species and -values for each parameter: -```@example dsl_1 -rn = @reaction_network begin - @species X(t)=1.0 - @parameters p=1.0 d=0.1 - p, 0 --> X - d, X --> ∅ -end -``` -This system can now be simulated without providing initial condition or -parameter vectors to the DifferentialEquations.jl solvers: -```@example dsl_1 -using DifferentialEquations, Plots -u0 = [] -tspan = (0.0, 10.0) -p = [] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -When providing default values, it is possible to do so for only a subset of the -species or parameters, in which case the rest can be specified when constructing -the problem type to solve: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) - @parameters p=1.0 d - p, 0 --> X - d, X --> 0 -end - -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:d => .1] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -Finally, default values can be overridden by passing mapping vectors to the -DifferentialEquations.jl problem being constructed. Only those initial conditions -or parameters for which we want to change their value from the default will need to be passed -```@example dsl_1 -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1] # we change p to 2.0 -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) -It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: -```@example dsl_1 -rn = @reaction_network begin - @parameters X0 - @species X(t)=X0 - p, 0 --> X - d, X --> ∅ -end -``` -We can now simulate the network without providing any initial conditions: -```@example dsl_1 -u0 = [] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1, :X0 => 1.0] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) -``` - -## Naming the generated `ReactionSystem` -ModelingToolkit uses system names to allow for compositional and hierarchical -models. To specify a name for the generated `ReactionSystem` via the -[`@reaction_network`](@ref) macro, just place the name before `begin`: -```@example dsl_1 -rn = @reaction_network production_degradation begin - p, ∅ --> X - d, X --> ∅ -end -ModelingToolkit.nameof(rn) == :production_degradation -``` - -## Pre-defined functions -Hill functions and a Michaelis-Menten function are pre-defined and can be used -as rate laws. Below, the pair of reactions within `rn1` are equivalent, as are -the pair of reactions within `rn2`: -```@example dsl_1 -rn1 = @reaction_network begin - hill(X,v,K,n), ∅ --> X - v*X^n/(X^n+K^n), ∅ --> X -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - mm(X,v,K), ∅ --> X - v*X/(X+K), ∅ --> X -end -``` -Repressor Hill (`hillr`) and Michaelis-Menten (`mmr`) functions are also -provided: -```@example dsl_1 -rn1 = @reaction_network begin - hillr(X,v,K,n), ∅ --> X - v*K^n/(X^n+K^n), ∅ --> X -end -``` -```@example dsl_1 -rn2 = @reaction_network begin - mmr(X,v,K), ∅ --> X - v*K/(X+K), ∅ --> X -end -``` - -Please see the API [Rate Laws](@ref api_rate_laws) section for more details. - -## Including non-species variables -Non-species state variables can be specified in the DSL using the `@variables` -macro. These are declared similarly to species. For example, -```@example dsl_1 -rn_with_volume = @reaction_network begin - @variables V(t) - k*V, 0 --> A -end -``` -creates a network with one species -```@example dsl_1 -species(rn_with_volume) -``` -and one non-species -```@example dsl_1 -nonspecies(rn_with_volume) -``` -giving two state variables, always internally ordered by species and then -nonspecies: -```@example dsl_1 -states(rn_with_volume) -``` - -`rn_with_volume` could then be extended with constraint equations for how `V(t)` -evolves in time, see the [associated tutorial](@ref constraint_equations). - -## Specifying alternative time variables and/or extra independent variables -While the DSL defaults to allowing `t` as the time variable, one can use the -`@ivs` macro to specify an alternative independent variable. For example, to -make `s` the default time variable one can say -```@example dsl_1 -rn_with_s = @reaction_network begin - @ivs s - @variables V(s) - @species B(s) - k, A + V*B --> C -end -show(stdout, MIME"text/plain"(), rn_with_s) # hide -``` -where we see all states are now functions of `s`. - -Similarly, if one wants states to be functions of more than one independent -variable, for example to encode a spatial problem, one can list more than one -variable, i.e. `@ivs t x y`. Here the first listed independent variable is -always chosen to represent time. For example, -```@example dsl_1 -rn_with_many_ivs = @reaction_network begin - @ivs s x - @variables V1(s) V2(s,x) - @species A(s) B(s,x) - k, V1*A --> V2*B + C -end -show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide -``` -Here again `s` will be the time variable, and any inferred species, `C` in this -case, are made functions of both variables, i.e. `C(s, x)`. - -## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) -The DSL allows Julia variables to be interpolated for the network name, within -rate constant expressions, or for species/stoichiometry within reactions. Using -the lower-level symbolic interface we can then define symbolic variables and -parameters outside of the macro, which can then be used within expressions in -the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) -tutorial for details on the lower-level symbolic interface). For example, -```@example dsl_1 -@parameters k α -@variables t -@species A(t) -spec = A -par = α -rate = k*A -name = :network -rn = @reaction_network $name begin - $rate*B, 2*$spec + $par*B --> $spec + C - end -``` -As the parameters `k` and `α` were pre-defined and appeared via interpolation, -we did not need to declare them within the `@reaction_network` macro, -i.e. they are automatically detected as parameters: -```@example dsl_1 -parameters(rn) -``` -as are the species coming from interpolated variables -```@example dsl_1 -species(rn) -``` -!!! note - When using interpolation, expressions like `2$spec` won't work; the - multiplication symbol must be explicitly included like `2*$spec`. From 97fe3f0cc71681bd0c39a14d972abb2b0f64c9d3 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 30 Jan 2024 20:08:06 -0500 Subject: [PATCH 15/23] save progress --- .../dsl_advanced_options.md | 557 +++++++++++------- 1 file changed, 347 insertions(+), 210 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index 7af4fec0c7..aaf09c0edc 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -4,276 +4,413 @@ Within the Catalyst DSL, each line can represent either *a reaction* or *an opti All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here], with most (but not all) being described in more detail below. - This tutorial will also describe some additional advanced DSL features that does not include using an option. +This tutorial will also describe some additional advanced DSL features that does not include using an option. As a first step, we import Catalyst (which is required to run the tutorial): +```@example dsl_advanced_1 +using Catalyst +``` ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) -Previously, we mentioned that - -### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) - -### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) - -### [Specifying non-species variables](@id dsl_advanced_options_variables) +[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining symbols as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default +behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein $P$ from its inactive ($Pᵢ$) to its active ($Pₐ$) for for is catalysed by an enzyme $E$. Using the most natural description: +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + k*E, Pᵢ --> Pₐ +end +``` +`X` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want $E$ to be considered a species, we can designate this using the `@species` option: +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + @species E(t) + k*E, Pᵢ --> Pₐ +end +``` +!!! note + When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-time dependant species is also possible](@ref ref)). -### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) +Similarly, the `@parameters` option can be used to +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + @parameters k + k*E, Pᵢ --> Pₐ +end +``` +Here, while `k` is explicitly defined as a parameter, not information is provided about $E$. Hence, the default case will be used (setting $E$ to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by an extensive list of all species/parameters, or just a subset. -## [Setting reaction metadata](@id dsl_advanced_options_) +While designating something which would default to a parameter as a species is straightforward, the other direction (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). -## [Naming reaction networks](@id dsl_advanced_options_) +Rather than listing all species/parameters on a single line after the options, a `begin ... end` block can be used (listing one species/parameter on each line). E.g. in the following example we use this notation to explicitly designate all species and parameters of the system: +```@example dsl_advanced_1 +catalysis_sys = @reaction_network begin + @species begin + E(t) + Pᵢ(t) + Pₐ(t) + end + @parameters begin + k + end + k*E, Pᵢ --> Pₐ +end +``` -## [Creating observables](@id dsl_advanced_options_) +A side-effect of using the `@species` and `@parameter` options is that they specify *the order in which the species and parameters are stored*. I.e. lets check the order of the parameters in the parameters in the following dimerisation model: +```@example dsl_advanced_1 +dimerisation = @reaction_network begin + (p,d), 0 <--> X + (kB,kD), 2X <--> X2 +end +parameters(dimerisation) +``` +The default order is typically equal to the order with which the parameters (or species) are encountered in the DSL (this is, however, not guaranteed). If we specify the parameters using `@parameters`, the order used within the option is used instead: +```@example dsl_advanced_1 +dimerisation = @reaction_network begin + @parameters kB kD p d + (p,d), 0 <--> X + (kB,kD), 2X <--> X2 +end +parameters(dimerisation) +``` +!!! danger + Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary. -## [Creating events](@id dsl_advanced_options_) +The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either system, that knowledge can be transferred to the other one. -## [Specifying non-time independent variables](@id dsl_advanced_options_) +Generally, there are dour main reasons for specifying species/parameters using the `@species` and `@parameters` option: +1. To designate a symbol, that would otherwise have defaulted to a parameter, as a species. +2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). +3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). +1. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) +!!!! warn + Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *symbols that appear in reactions*. Until now this has not been relevant. However, this tutorial will demosntrate use cases where species/parameters, that are not part of reactions, are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). -## Explicit specification of network species and parameters -Recall that the `@reaction_network` macro automatically designates symbols used -in the macro as either parameters or species, with symbols that appear as a -substrate or product being species, and all other symbols becoming parameters -(i.e. those that only appear within a rate expression and/or as [stoichiometric coefficients](@ref parametric_stoichiometry)). Sometimes, one might want to manually override this default -behavior for a given symbol. E.g one might want something to be considered as a -species, even if it only appears within a rate expression. In the following -network -```@example dsl_1 +### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) +When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` and the desired default value). E.g here we set $X$'s default initial condition value to $1.0$, and $p$ and $d$'s default values to $1.0$ and $0.2$, respectively: +```@example dsl_advanced_defaults +using Catalyst # hide rn = @reaction_network begin - k*X, Y --> 0 + @species X(t)=1.0 + @parameters p=1.0 d=0.1 + (p,d), 0 <--> X end ``` -`X` (as well as `k`) will be considered a parameter. - -By using the `@species` and `@parameters` options within the `@reaction_network` -macro, one can manually declare that specified symbols should be considered a -species or parameter. E.g in: -```@example dsl_1 +Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` are set to empty vectors: +```@example dsl_advanced_defaults +using OrdinaryDiffEq, Plots +u0 = [] +tspan = (0.0, 10.0) +p = [] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` +It is still possible to provide values for some (or all) initial conditions/parameters in `u0` and `ps` (in which case these overrides the default values): +```@example dsl_advanced_defaults +u0 = [:X => 4.0] +p = [:d => 0.5] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` +It is also possible to declare a model with default values for only some initial conditions/parameters: +```@example dsl_advanced_defaults +using Catalyst # hide rn = @reaction_network begin - @species X(t) Y(t) - k*X, Y --> 0 + @species X(t)=1.0 + (p,d), 0 <--> X end + +tspan = (0.0, 10.0) +p = [:p => 1.0, :D => 0.2] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) ``` -`X` and `Y` are set as species. Please note that when declaring species using -the `@species` option, their dependant variable (almost always `t`) also needs -to be designated. Similarly in -```@example dsl_1 +API for checking the default values of a species or parameters can be found [here](@ref ref). + +### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) +In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of $X$ using the parameter $X₀$. +```@example dsl_advanced_defaults rn = @reaction_network begin - @parameters k - k*X, Y --> 0 + @species X(t)=X₀ + @parameters X₀ + (p,d), 0 <--> X end ``` -both `X` and `k` will be considered as parameters. It is also possible to use -both options simultaneously, allowing users to fully specify which symbols are -species and/or parameters: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) Y(t) - @parameters k - k*X, Y --> 0 +Please note that as the parameter $X₀$ does not occur as part of any reactions, Catalyst's DSL cannot infer whether it is a species or a parameter. This must hence be explicitly declared. We can now simulate our model while providing $X$'s value through the $X₀$ parameter: +```@example dsl_advanced_defaults +u0 = [] +p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` +It is still possible to designate $X$'s value in `u0`, in which case this overrides the default value. Please note that $X₀$ is still a parameter of the system, and its value must still be designated to simulate the model. +```@example dsl_advanced_defaults +u0 = [:X => 0.5] +p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] +oprob = ODEProblem(rn, u0, tspan, p) +sol = solve(oprob, Tsit5()) +plot(sol) +``` + +### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) +Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically ho this is done). Here we will introduce how to set metadata, and describe some common metadata types. + +Whenever a species/parameter is declared using the `@species`/`@parameters` options, it can be followed by a `[]` within which the metadata is given. Each metadata entry consists of teh metadata's name, followed by a `=`, followed by its value. E.g. the `description` metadata allows you to attach a `String` to a species/parameter. Here we create a simple model where we add descriptions to all species and parameters. +```@example dsl_advanced_metadata +using Catalyst # hide +two_state_system = @reaction_network begin + @species Xi(t) [description="The species X's inactive form"] Xa(t) [description="The species X's active form"] + @parameters kA [description="X's activation rate"] kD [description="X's deactivation rate"] + (ka,kD), Xi <--> Xa +end +``` +A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we remove some of the descriptions, and also add a [bounds metadata](@ref ref) to $kA$, +```@example dsl_advanced_metadata +two_state_system = @reaction_network begin + @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD [description="X's deactivation rate"] + (ka,kD), Xi <--> Xa end ``` -Here, `X` and `Y` are designated as species and `k` as a parameter. -The lists provided to the `@species` and `@parameters` options do not need to be extensive. Any symbol that appears in neither list will use the default option as determined by the macro. E.g. in the previous example, where we only want to change the default designation of `X` (making it a species rather than a parameter), we can simply write: -```@example dsl_1 -rn = @reaction_network begin - @species X(t) - k*X, Y --> 0 +It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kD$'s default value to $1.0$ we use +```@example dsl_advanced_metadata +two_state_system = @reaction_network begin + @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD = 1.0 [description="X's deactivation rate"] + (ka,kD), Xi <--> Xa end ``` -Finally, note that the `@species` and `@parameters` options can also be used in -`begin ... end` block form, allowing more formatted lists of species/parameters: -```@example dsl_1 -rn = @reaction_network begin +When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. the previous example is rewritten as +```@example dsl_advanced_metadata +two_state_system = @reaction_network begin @parameters begin - d1 - d2 + kA, [description="X's activation rate", bound=(0.01,10.0)] + kD = 1.0, [description="X's deactivation rate"] end - @species begin - X1(t) - X2(t) - end - d2, X2 --> 0 - d1, X1 --> 0 + (ka,kD), Xi <--> Xa +end +``` + +Each metadata has its own getter functions. E.g. we can get the description of the parameter $pA$ using `getdescription` (here we use [system indexing](@ref ref) to access the parameter): +```@example dsl_advanced_metadata +getdescription(two_state_system.kA) +``` + +It is not possible for the user to directly designate their own metadata. These have to first be added to Catalyst. Doing so is somewhat involved, and described in detail [here](). A full list of metadata that can be used for species and/or parameters can be found [here](@ref ref). + +### [Designating constant-valued/fixed species-parameters](@id dsl_advanced_options_constant_species) + +Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of a callback]@ref advanced_simulations_callbacks). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species $X$ is converted to $Xᴾ$ at rate $k$. By designating $X$ as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. +```@example dsl_advanced_constant_species +using Catalyst # hide +rn = @reaction_network begin + @parameters X [isconstantspecies=true] + k, X --> Xᴾ +end +``` +We can confirm that $X$ is the only species of the system: +```@example dsl_advanced_constant_species +species(rn) +``` +Here, the produced model is actually identical to if $X$ had simply been put as a parameter in the reaction's rate: +```@example dsl_advanced_constant_species +rn = @reaction_network begin + k*X, 0 --> Xᴾ end ``` -This can be especially useful when declaring default values for clarity of model -specification (see the next section). -## [Setting default values for initial conditions and parameters](@id dsl_description_defaults) -When using the `@species` and ` @parameters` macros to declare species and/or -parameters, one can also provide default initial conditions for each species and -values for each parameter: -```@example dsl_1 +A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is [the Brusselator](https://en.wikipedia.org/wiki/Brusselator). + +### [Specifying non-species variables](@id dsl_advanced_options_variables) +Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of reaction events. When they are converted to ODEs, the species are the variables of the system. However, Catalyst permits the creation of hybrid CRN models. These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it does not participate in reactions). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: +```@example dsl_advanced_variables +using Catalyst # hide rn = @reaction_network begin - @species X(t)=1.0 - @parameters p=1.0 d=0.1 - p, 0 --> X - d, X --> ∅ + @variables V(t) + (p,d), 0 <--> G end ``` -This system can now be simulated without providing initial condition or -parameter vectors to the DifferentialEquations.jl solvers: -```@example dsl_1 -using DifferentialEquations, Plots -u0 = [] +Note that $V$ (like species) is time-dependant, and (like species) must be declared as such when the `@variables` option is used. We can now simulate our model (remembering to provide a value for $V$ as well as $G$): +```@example dsl_advanced_variables +using OrdinaryDiffEq, Plots +u0 = [:G => 0.1, :V => 1.0] tspan = (0.0, 10.0) -p = [] +p = [:p => 1.0, :d => 0.5] oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) +sol = solve(oprob, Tsit5()) plot(sol) ``` +Here, we have not actually described how $V$ interacting with our model, or how its value may change. Primarily variables are declared as part of hybrid CRN/equation modelling, which is described in more detail [here](@ref ref). + +You can set metadata and default initial condition values for variables using the same syntax as used for parameters and species. -When providing default values, it is possible to do so for only a subset of the -species or parameters, in which case the rest can be specified when constructing -the problem type to solve: -```@example dsl_1 +You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknown` function can be used to return both. + +## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) +Reactions can also have metadata. This is described in detail [here](@ref ref). + +## [Naming reaction networks](@id dsl_advanced_options_naming) +Each reaction network model has a name. It can be accessed using the `nameof` function. By default, some generic name is used: +```@example dsl_advanced_names +using Catalyst # hide rn = @reaction_network begin - @species X(t) - @parameters p=1.0 d - p, 0 --> X - d, X --> 0 + (p,d), 0 <--> X end - -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:d => .1] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) +nameof(rn) +``` +A specific name can be given as an argument between the `@reaction_network` and the `begin`. E.g. to name a network `my_network` we can use: +```@example dsl_advanced_names +rn = @reaction_network my_network begin + (p,d), 0 <--> X +end +nameof(rn) ``` -Finally, default values can be overridden by passing mapping vectors to the -DifferentialEquations.jl problem being constructed. Only those initial conditions -or parameters for which we want to change their value from the default will need to be passed -```@example dsl_1 -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1] # we change p to 2.0 -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) +A consequence of generic names being used by default is that networks, even if seemingly identical, by default are not. E.g. +```@example dsl_advanced_names +rn1 = @reaction_network begin + (p,d), 0 <--> X +end +rn2 = @reaction_network begin + (p,d), 0 <--> X +end +nothing # hide +``` +Here, since the networks' names are different: +```@example dsl_advanced_names +nameof(rn1) == nameof(rn2) +``` +they are different +```@example dsl_advanced_names +rn1 == rn2 +``` +By designating the networks to have the same name, however, identity is achieved. +```@example dsl_advanced_names +rn1 = @reaction_network my_network begin + (p,d), 0 <--> X +end +rn2 = @reaction_network my_network begin + (p,d), 0 <--> X +end +rn1 == rn2 ``` -## [Setting initial conditions that depend on parameters](@id dsl_description_parametric_initial_conditions) -It is possible to set the initial condition of one (or several) species so that they depend on some system parameter. This is done in a similar way as default initial conditions, but giving the parameter instead of a value. When doing this, we also need to ensure that the initial condition parameter is a variable of the system: -```@example dsl_1 +## [Creating observables](@id dsl_advanced_options_observables) + +Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an equation). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first given the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. + +Let us consider a model where two species ($X$ and $Y$) can bind to form a complex ($XY$, which also can dissociate back into $X$ and $Y$). If we wish to create a representation for the total amount of $X$ and $Y$ in the system, we can do this by creating observables $Xtot$ and $Ytot$: +```@example dsl_advanced_observables +using Catalyst # hide rn = @reaction_network begin - @parameters X0 - @species X(t)=X0 - p, 0 --> X - d, X --> ∅ + @observables begin + Xtot ~ X + XY + Ytot ~ Y + XY + end + (kB,kD), X + Y <--> XY end ``` -We can now simulate the network without providing any initial conditions: -```@example dsl_1 -u0 = [] +We can now simulate our model using normal syntax (initial condition values for observables should not, and can not, be provided): +```@example dsl_advanced_observables +using OrdinaryDiffEq +u0 = [:X => 1.0, :Y => 2.0, :XY => 0.0] tspan = (0.0, 10.0) -p = [:p => 2.0, :d => .1, :X0 => 1.0] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob) -plot(sol) +ps = [:kB => 1.0, :kD => 1.5] +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(oprob, Tsit5()) +nothing # hide ``` -## Naming the generated `ReactionSystem` -ModelingToolkit uses system names to allow for compositional and hierarchical -models. To specify a name for the generated `ReactionSystem` via the -[`@reaction_network`](@ref) macro, just place the name before `begin`: -```@example dsl_1 -rn = @reaction_network production_degradation begin - p, ∅ --> X - d, X --> ∅ +Next, we can use [symbolic indexing](@ref ref) of our solution object, but with the observable as input. E.g. we can use +```@example dsl_advanced_observables +sol[:Xtot] +``` +to get a vector with teh value of $Xtot$ throughout the simulation. We can also use +```@example dsl_advanced_observables +using Plots +plot(sol; idxs = [:Xtot, :Ytot]) +``` +to plot the observables (rather than the species). + +Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex $Xn$ containing $n$ copies of $X$. Here, we create an observable describing the total number of $X$ molecules in the system: +```@example dsl_advanced_observables +using Catalyst # hide +rn = @reaction_network begin + @observables Xtot ~ X + n*XnXY + (kB,kD), n*X <--> Xn end -ModelingToolkit.nameof(rn) == :production_degradation +nothing # hide ``` +Note that, since we only have a single observable, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. -## Including non-species variables -Non-species state variables can be specified in the DSL using the `@variables` -macro. These are declared similarly to species. For example, -```@example dsl_1 -rn_with_volume = @reaction_network begin - @variables V(t) - k*V, 0 --> A +[Metadata](@ref ref) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref ref) to our observable we can do +```@example dsl_advanced_observables +using Catalyst # hide +rn = @reaction_network begin + @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY + (kB,kD), n*X <--> Xn end +nothing # hide ``` -creates a network with one species -```@example dsl_1 -species(rn_with_volume) -``` -and one non-species -```@example dsl_1 -nonspecies(rn_with_volume) -``` -giving two state variables, always internally ordered by species and then -nonspecies: -```@example dsl_1 -states(rn_with_volume) -``` - -`rn_with_volume` could then be extended with constraint equations for how `V(t)` -evolves in time, see the [associated tutorial](@ref constraint_equations). - -## Specifying alternative time variables and/or extra independent variables -While the DSL defaults to allowing `t` as the time variable, one can use the -`@ivs` macro to specify an alternative independent variable. For example, to -make `s` the default time variable one can say -```@example dsl_1 -rn_with_s = @reaction_network begin - @ivs s - @variables V(s) - @species B(s) - k, A + V*B --> C + +Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here $Xtot$ becomes a species: +```@example dsl_advanced_observables +using Catalyst # hide +rn = @reaction_network begin + @species Xtot(t) + @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY + (kB,kD), n*X <--> Xn end -show(stdout, MIME"text/plain"(), rn_with_s) # hide -``` -where we see all states are now functions of `s`. - -Similarly, if one wants states to be functions of more than one independent -variable, for example to encode a spatial problem, one can list more than one -variable, i.e. `@ivs t x y`. Here the first listed independent variable is -always chosen to represent time. For example, -```@example dsl_1 -rn_with_many_ivs = @reaction_network begin - @ivs s x - @variables V1(s) V2(s,x) - @species A(s) B(s,x) - k, V1*A --> V2*B + C +nothing # hide +``` + +Some final notes regarding observables: +- The right-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). +- All symbols appearing on the left-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). +- Observables may not depend on other observables. +- Observables have their [dependent variables](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. + +## [Creating events](@id dsl_advanced_options_events) + +## [Specifying non-time independent variables](@id dsl_advanced_options_ivs) + +As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on on time independent variable, and potentially one or more spatial independent variables. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using teh `@ivs` option. E.g. to use `s` instead of `t` we can use +```@example dsl_advanced_ivs +using Catalyst # hide +rn = @reaction_network my_network begin + @ivs s + (ka,kD), Xi <--> Xa end -show(stdout, MIME"text/plain"(), rn_with_many_ivs) # hide -``` -Here again `s` will be the time variable, and any inferred species, `C` in this -case, are made functions of both variables, i.e. `C(s, x)`. - -## [Interpolation of Julia variables](@id dsl_description_interpolation_of_variables) -The DSL allows Julia variables to be interpolated for the network name, within -rate constant expressions, or for species/stoichiometry within reactions. Using -the lower-level symbolic interface we can then define symbolic variables and -parameters outside of the macro, which can then be used within expressions in -the DSL (see the [Programmatic Construction of Symbolic Reaction Systems](@ref programmatic_CRN_construction) -tutorial for details on the lower-level symbolic interface). For example, -```@example dsl_1 -@parameters k α -@variables t -@species A(t) -spec = A -par = α -rate = k*A -name = :network -rn = @reaction_network $name begin - $rate*B, 2*$spec + $par*B --> $spec + C - end +nothing # hide +``` +We can confirm that $Xi$ and $Xa$ depend on `s` (and not `t`): +```@example dsl_advanced_ivs +species(rn) ``` -As the parameters `k` and `α` were pre-defined and appeared via interpolation, -we did not need to declare them within the `@reaction_network` macro, -i.e. they are automatically detected as parameters: -```@example dsl_1 -parameters(rn) + +It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default, time, variable, while the following one is considered spatial variables. If we want some species to depend on a non-time independent variable, this has to be explicitly declared: +```@example dsl_advanced_ivs +using Catalyst # hide +rn = @reaction_network my_network begin + @ivs t x + @species Y(s) + (p1,d1), 0 <--> X + (p2,d2), 0 <--> Y +end +species(rn) ``` -as are the species coming from interpolated variables -```@example dsl_1 +Finally, it is possible to have species which depends on several independent variables: +```@example dsl_advanced_ivs +using Catalyst # hide +rn = @reaction_network my_network begin + @ivs t x + @species Xi(t,x) Xa(t,x) + (ka,kD), Xi <--> Xa +end species(rn) ``` !!! note - When using interpolation, expressions like `2$spec` won't work; the - multiplication symbol must be explicitly included like `2*$spec`. + Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying indepdent variables is currently limited. \ No newline at end of file From 8ea57713350864718b367803a80265d1f11efc9f Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 18 Apr 2024 10:29:52 -0400 Subject: [PATCH 16/23] save progress --- .../dsl_advanced_options.md | 311 +++++++++++++++--- .../catalyst_functionality/dsl_description.md | 150 +++++---- 2 files changed, 337 insertions(+), 124 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index aaf09c0edc..d0edec4458 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -1,8 +1,7 @@ # [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) - Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous tutorial](@ref dsl_description) described how to create reactions. This one will instead describe how to use options. These are typically used to supplant the model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. -All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here], with most (but not all) being described in more detail below. +All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that does not include using an option. As a first step, we import Catalyst (which is required to run the tutorial): ```@example dsl_advanced_1 @@ -11,13 +10,13 @@ using Catalyst ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) [Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining symbols as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default -behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein $P$ from its inactive ($Pᵢ$) to its active ($Pₐ$) for for is catalysed by an enzyme $E$. Using the most natural description: +behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive (`Pᵢ`) to its active (`Pₐ`) is catalysed by an enzyme `E`. Using the most natural description: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ end ``` -`X` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want $E$ to be considered a species, we can designate this using the `@species` option: +`E` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want `E` to be considered a species, we can designate this using the `@species` option: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin @species E(t) @@ -27,16 +26,16 @@ end !!! note When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-time dependant species is also possible](@ref ref)). -Similarly, the `@parameters` option can be used to +Similarly, the `@parameters` option can be used to explicitly designate something as a parameter: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin @parameters k k*E, Pᵢ --> Pₐ end ``` -Here, while `k` is explicitly defined as a parameter, not information is provided about $E$. Hence, the default case will be used (setting $E$ to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by an extensive list of all species/parameters, or just a subset. +Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. -While designating something which would default to a parameter as a species is straightforward, the other direction (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). +While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). Rather than listing all species/parameters on a single line after the options, a `begin ... end` block can be used (listing one species/parameter on each line). E.g. in the following example we use this notation to explicitly designate all species and parameters of the system: ```@example dsl_advanced_1 @@ -71,11 +70,12 @@ end parameters(dimerisation) ``` !!! danger - Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary. + Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary, and also checks the order manually. -The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either system, that knowledge can be transferred to the other one. +!!! note + The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either approach, that knowledge can be transferred to the other one. -Generally, there are dour main reasons for specifying species/parameters using the `@species` and `@parameters` option: +Generally, there are four main reasons for specifying species/parameters using the `@species` and `@parameters` option: 1. To designate a symbol, that would otherwise have defaulted to a parameter, as a species. 2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). 3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). @@ -85,7 +85,7 @@ Generally, there are dour main reasons for specifying species/parameters using t Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *symbols that appear in reactions*. Until now this has not been relevant. However, this tutorial will demosntrate use cases where species/parameters, that are not part of reactions, are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). ### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) -When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` and the desired default value). E.g here we set $X$'s default initial condition value to $1.0$, and $p$ and $d$'s default values to $1.0$ and $0.2$, respectively: +When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` followed by the desired default value). E.g here we set `X`'s default initial condition value to $1.0$, and `p` and `d`'s default values to $1.0$ and $0.2$, respectively: ```@example dsl_advanced_defaults using Catalyst # hide rn = @reaction_network begin @@ -94,7 +94,7 @@ rn = @reaction_network begin (p,d), 0 <--> X end ``` -Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` are set to empty vectors: +Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` can be empty vectors: ```@example dsl_advanced_defaults using OrdinaryDiffEq, Plots u0 = [] @@ -129,7 +129,7 @@ plot(sol) API for checking the default values of a species or parameters can be found [here](@ref ref). ### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) -In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of $X$ using the parameter $X₀$. +In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of `X` using the parameter `X₀`. ```@example dsl_advanced_defaults rn = @reaction_network begin @species X(t)=X₀ @@ -137,7 +137,7 @@ rn = @reaction_network begin (p,d), 0 <--> X end ``` -Please note that as the parameter $X₀$ does not occur as part of any reactions, Catalyst's DSL cannot infer whether it is a species or a parameter. This must hence be explicitly declared. We can now simulate our model while providing $X$'s value through the $X₀$ parameter: +Please note that as the parameter `X₀` does not occur as part of any reactions, Catalyst's DSL cannot infer whether it is a species or a parameter. This must hence be explicitly declared. We can now simulate our model while providing `X`'s value through the `X₀` parameter: ```@example dsl_advanced_defaults u0 = [] p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] @@ -145,7 +145,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -It is still possible to designate $X$'s value in `u0`, in which case this overrides the default value. Please note that $X₀$ is still a parameter of the system, and its value must still be designated to simulate the model. +It is still possible to designate $X$'s value in `u0`, in which case this overrides the default value. ```@example dsl_advanced_defaults u0 = [:X => 0.5] p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] @@ -153,11 +153,12 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` +Please note that `X₀` is still a parameter of the system, and its value must still be designated to simulate the model. ### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) -Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically ho this is done). Here we will introduce how to set metadata, and describe some common metadata types. +Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically how this is done). Here we will introduce how to set metadata, and describe some common metadata types. -Whenever a species/parameter is declared using the `@species`/`@parameters` options, it can be followed by a `[]` within which the metadata is given. Each metadata entry consists of teh metadata's name, followed by a `=`, followed by its value. E.g. the `description` metadata allows you to attach a `String` to a species/parameter. Here we create a simple model where we add descriptions to all species and parameters. +Whenever a species/parameter is declared using the `@species`/`@parameters` options, it can be followed by a `[]` within which the metadata is given. Each metadata entry consists of the metadata's name, followed by a `=`, followed by its value. E.g. the `description` metadata allows you to attach a [`String`](https://docs.julialang.org/en/v1/base/strings/) to a species/parameter. Here we create a simple model where we add descriptions to all species and parameters. ```@example dsl_advanced_metadata using Catalyst # hide two_state_system = @reaction_network begin @@ -166,18 +167,18 @@ two_state_system = @reaction_network begin (ka,kD), Xi <--> Xa end ``` -A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we remove some of the descriptions, and also add a [bounds metadata](@ref ref) to $kA$, +A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we only provide a description for `kA`, for which we also provide a [bounds metadata](@ref https://docs.sciml.ai/ModelingToolkit/dev/basics/Variable_metadata/#Bounds), ```@example dsl_advanced_metadata two_state_system = @reaction_network begin - @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD [description="X's deactivation rate"] + @parameters kA [description="X's activation rate", bound=(0.01,10.0)] (ka,kD), Xi <--> Xa end ``` -It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kD$'s default value to $1.0$ we use +It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kA$'s default value to $1.0$ we use ```@example dsl_advanced_metadata two_state_system = @reaction_network begin - @parameters kA [description="X's activation rate", bound=(0.01,10.0)] kD = 1.0 [description="X's deactivation rate"] + @parameters kA=1.0 [description="X's activation rate", bound=(0.01,10.0)] (ka,kD), Xi <--> Xa end ``` @@ -193,7 +194,7 @@ two_state_system = @reaction_network begin end ``` -Each metadata has its own getter functions. E.g. we can get the description of the parameter $pA$ using `getdescription` (here we use [system indexing](@ref ref) to access the parameter): +Each metadata has its own getter functions. E.g. we can get the description of the parameter `kA` using `getdescription` (here we use [system indexing](@ref ref) to access the parameter): ```@example dsl_advanced_metadata getdescription(two_state_system.kA) ``` @@ -202,7 +203,7 @@ It is not possible for the user to directly designate their own metadata. These ### [Designating constant-valued/fixed species-parameters](@id dsl_advanced_options_constant_species) -Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of a callback]@ref advanced_simulations_callbacks). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species $X$ is converted to $Xᴾ$ at rate $k$. By designating $X$ as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. +Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of an event]@ref dsl_advanced_options_events). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species `X` is converted to `Xᴾ` at rate `k`. By designating `X` as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. ```@example dsl_advanced_constant_species using Catalyst # hide rn = @reaction_network begin @@ -224,7 +225,7 @@ end A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is [the Brusselator](https://en.wikipedia.org/wiki/Brusselator). ### [Specifying non-species variables](@id dsl_advanced_options_variables) -Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of reaction events. When they are converted to ODEs, the species are the variables of the system. However, Catalyst permits the creation of hybrid CRN models. These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it does not participate in reactions). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: +Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of *reaction events*. When they are converted to ODEs, the species become unknowns of the system. However, Catalyst permits the creation of [hybrid CRN models](@ref ref). These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it cannot plausibly be a reaction reactant). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: ```@example dsl_advanced_variables using Catalyst # hide rn = @reaction_network begin @@ -246,7 +247,7 @@ Here, we have not actually described how $V$ interacting with our model, or how You can set metadata and default initial condition values for variables using the same syntax as used for parameters and species. -You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknown` function can be used to return both. +You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknowns` function retrieves both. ## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) Reactions can also have metadata. This is described in detail [here](@ref ref). @@ -297,11 +298,12 @@ end rn1 == rn2 ``` -## [Creating observables](@id dsl_advanced_options_observables) +Setting names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. -Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an equation). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first given the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. +## [Creating observables](@id dsl_advanced_options_observables) +Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. -Let us consider a model where two species ($X$ and $Y$) can bind to form a complex ($XY$, which also can dissociate back into $X$ and $Y$). If we wish to create a representation for the total amount of $X$ and $Y$ in the system, we can do this by creating observables $Xtot$ and $Ytot$: +Let us consider a model where two species (`X` and `Y`) can bind to form a complex ($XY$, which also can dissociate back into `X` and `Y`). If we wish to create a representation for the total amount of `X` and `Y` in the system, we can do this by creating observables `Xtot` and `Ytot`: ```@example dsl_advanced_observables using Catalyst # hide rn = @reaction_network begin @@ -323,31 +325,30 @@ sol = solve(oprob, Tsit5()) nothing # hide ``` -Next, we can use [symbolic indexing](@ref ref) of our solution object, but with the observable as input. E.g. we can use +Next, we can use [symbolic indexing](@ref simulation_structure_interfacing) of our solution object, but with the observable as input. E.g. we can use ```@example dsl_advanced_observables sol[:Xtot] ``` -to get a vector with teh value of $Xtot$ throughout the simulation. We can also use +to get a vector with `Xtot`'s throughout the simulation. We can also use ```@example dsl_advanced_observables using Plots plot(sol; idxs = [:Xtot, :Ytot]) ``` to plot the observables (rather than the species). -Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex $Xn$ containing $n$ copies of $X$. Here, we create an observable describing the total number of $X$ molecules in the system: +Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: ```@example dsl_advanced_observables -using Catalyst # hide rn = @reaction_network begin - @observables Xtot ~ X + n*XnXY + @observables Xtot ~ X + n*Xn (kB,kD), n*X <--> Xn end nothing # hide ``` -Note that, since we only have a single observable, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. +!!! + If only a single observable is declared, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. -[Metadata](@ref ref) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref ref) to our observable we can do +[Metadata](@ref dsl_advanced_options_species_and_parameters_metadata) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref dsl_advanced_options_species_and_parameters_metadata) to our observable we can use ```@example dsl_advanced_observables -using Catalyst # hide rn = @reaction_network begin @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY (kB,kD), n*X <--> Xn @@ -355,9 +356,8 @@ end nothing # hide ``` -Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here $Xtot$ becomes a species: +Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here `Xtot` becomes a species: ```@example dsl_advanced_observables -using Catalyst # hide rn = @reaction_network begin @species Xtot(t) @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY @@ -367,35 +367,243 @@ nothing # hide ``` Some final notes regarding observables: -- The right-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). -- All symbols appearing on the left-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). +- The left-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). +- All symbols appearing on the right-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). - Observables may not depend on other observables. - Observables have their [dependent variables](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. ## [Creating events](@id dsl_advanced_options_events) +Sometimes one wishes to model events, describing things that can happen to it during a simulation. + - A chemical system where an amount of some species is added at a time point after the simulation's initiation. + - A simulation of a circadian rhythm, where light is turned on/off every 12 hours. + - A cell divides when some size variable reaches a certain threshold, halving the amount of each species in the system. + +Events are divided into *continuous* and *discrete* events, and these can be added directly to a system using the `continuous_events` and `discrete_events` options. Events can also be modelled through *callbacks*. These are different in that they are supplied in the simulation step (rather than on system creation), and generally provide more flexibility in how they may affect the system. Callbacks are described on a separate [page](@ref advanced_simulations_callbacks). + +The notation described below for creating continuous and discrete events is the same which is used in [ModelingToolkit to create events](https://docs.sciml.ai/ModelingToolkit/stable/basics/Events/), and which is used for [events for programmatic model creation](@ref ref). + +### [Continuous vs discrete events](@id dsl_advanced_options_events_continuous_vs_discrete) +Both continuous and discrete events combine some condition (for triggering the event) with some affect (describing their effects on the system). They differ in the following ways: +- They use slightly different notation. +- Discrete events' conditions are checked at *the end of* each simulation time step. For continuous events, the simulation instead finds the *exact time point* when the event is triggered at. +- Continuous events cannot be supplied to jump simulations. + +### [Continuous events](@id dsl_advanced_options_events_continuous) +Let us consider a simple system where species `X` degraded at a constant rate `d`. Next, we wish to add an event which adds `2.0` units of `X` whenever `X` reaches a critical threshold `1.0`. This can be done in the following manner: +```@example dsl_advanced_events +using Catalyst # hide +rn = @reaction_network begin + @continuous_events begin + X ~ 1.0 => [X ~ X + 2.0] + end + d, X --> 0 +end +nothing # hide +``` +Here, the `@continuous_events` option is followed by a `begin ... end` block. Next, each line corresponds to a separate event. Each event is created in the following manner: +- It combines a *condition* (denoting when the event will happen) with one (or several) *affects* (denoting what the event does to the system when it happens). +- The condition (left) and affect (right) are separated by a `=>`. +- The condition takes the form of an [equation](). Here, the event is triggered whenever the equation's two sides (separated by a `~`) are equal. +- The affect(s) are enclosed within `[]`. If there are multiple affects, these are separated by `,` (the example above contains a single affect). +- Each affect is a single equation that describes how a parameter, species, or [variable](@ref dsl_advanced_options_variables) is updated when the event is triggered. +- Each affect's equation's left-hand side must contain only the parameter/species/variable whose value should be updated. +- Each affect's equation's right-hand side is an expression describing its updated value. + +We can simulate the model we declared, just like any other model: +```@example dsl_advanced_events +using OrdinaryDiffEq, Plots +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +Inspecting the solution, we can confirm that whenever `X` reaches a value of `1.0`, `2.0` units of `X` is added to the system. + +In our example, we can also denote the critical quantities using parameters: +```@example dsl_advanced_events +rn = @reaction_network begin + @parameters X_thres X_add + @continuous_events begin + X ~ X_thres => [X ~ X + X_add] + end + d, X --> 0 +end +nothing # hide +``` +Here, since `X_thres` and `X_add` do not appear in any reactions, Catalyst cannot determine whether they are parameters or species. hence, they must be [explicitly designated as parameters by using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters). Next, these can be designated as any value, and supplied to the `ODEProblem`: +```@example dsl_advanced_events +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0, :X_thres => 0.5, :X_add => 3.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` + +As previously noted, each continuous event can have multiple affects. The following system has two components (`X` and `Y`, one being produced and one being degraded). When their concentrations are equal, a continuous events reduce the concentration of `X` while increasing the concentration of `Y`: +```@example dsl_advanced_events +rn = @reaction_network begin + @continuous_events begin + X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] + end + p, 0 --> X + d, Y --> 0 +end + +u0 = [:X => 1.0, :Y => 3.0] +tspan = (0.0, 10.0) +ps = [:p => 1.0, :d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` + +!!!warn + A single event (continuous or discrete) can update the value of either (one or several) species (and variables), or of (one or several) parameters. It is not possible for an event to update the values of both species/variables and parameters. + +In the above examples we have modelled a system with a single event. In these cases, the `begin end` block is not required, and the event can be provided on the same line as the `@continuous_events` option: +```@example dsl_advanced_events +rn = @reaction_network begin + @continuous_events X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] + p, 0 --> X + d, Y --> 0 +end +nothing # hide +``` + +### [Discrete events](@id dsl_advanced_options_events_discrete) +Just like [continuous events](dsl_advanced_options_events_continuous), discrete events combine a condition with one or more affect statements. Here, discrete events' affects are created identically to those for continuous events. Discrete events' conditions are different. There exist 3 different types of discrete events, each with a different type of condition. All three types are created using the `@discrete_events` option, and a single system can contain a mix of all types. The three types are: +- Preset-time discrete events. +- Periodic discrete events. +- Conditional discrete events. + +#### [Preset-time discrete events](@id dsl_advanced_options_events_discrete_presettime) +*Present-time events* are events that happen at specific time points. Here, the condition is a vector with all the time points at which an event is triggered. E.g. here we create a production/degradation loop, where `2.0` units of `X` is added at time points `3.0` and `7.0` +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + [3.0, 7.0] => [X ~ X + 2.0] + end + (p,d), 0 <--> X +end + +u0 = [:X => 0.1, :Y => 3.0] +tspan = (0.0, 10.0) +ps = [:p => 1.0, :d => 0.5] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` + +The preset time points can also be parameters (in which case, they have to be [designated as such using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters)): +```@example dsl_advanced_events +rn = @reaction_network begin + @parameters t1 t2 + @discrete_events begin + [t1, t2] => [X ~ X + 2.0] + end + (p,d), 0 <--> X +end +nothing +``` + +#### [Periodic discrete events](@id dsl_advanced_options_events_discrete_periodic) +When a discrete event's condition is a vector, a preset-time event is created. If it instead is a single value, a *periodic event* is created. These occur repeatedly throughout a simulation, with its period set by the affect term. E.g. here we create a system where `0.5` units of `X` is added every `1.0` time units. +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + 1.0 => [X ~ X + 0.5] + end + d, X --> 0 +end + +u0 = [:X => 1.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +Like for preset-time events, periodic events' affects may contain parameters. + +#### [Conditional discrete events](@id dsl_advanced_options_events_discrete_conditional) +Finally, discrete events' condition may be a boolean expression (consisting of parameters, species, variables, and the time variable). Let's say that we want to create an event which, if the concentration of `X` is below a threshold `1.0`, adds `1.0` units of `X` to the system, then we can use the following discrete event: +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + X < 1.0 => [X ~ X + 2.0] + end + d, X --> 0 +end +``` +If we simulate the system using the same conditions as for our [similar, continuous, example](@ref dsl_advanced_options_events_continuous) we get the same result: +```@example dsl_advanced_events +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +So, how is modelling this event as a discrete or continuous event different? There are four differences: +1) For continuous events, the simulation method finds the exact time point when the condition triggers. Discrete events are triggered at the first time step when the condition holds. +2) This discrete event will be triggered whenever `X < 1.0` holds, not just when the concentration of `X` passes the threshold. E.g. it will be triggered if the initial concentration of `X` is less than `1.0`. +3) Only the discrete event event can be used with jump simulations. +4) The discrete event can be used to create more advanced conditions. + +E.g. using (4), we can modify our system so that the event is only triggered when time is less than `5.0` (after which `X` decays towards `0`): +```@example dsl_advanced_events +rn = @reaction_network begin + @discrete_events begin + (X < 1.0) & (t < 5.0) => [X ~ X + 2.0] + end + d, X --> 0 +end + +u0 = [:X => 2.0] +tspan = (0.0, 10.0) +ps = [:d => 1.0] + +oprob = ODEProblem(rn, u0, tspan, ps) +sol = solve(ODEProblem, Tsit5()) +plot(sol) +``` +!!!note + When we form composite boolean conditions for conditional discrete events, we use `&` to denote the AND operator (not `&&`, as this is currently not supported). + +!!!warn + Generally, discrete events including equality (`==`) will not be triggered. The reason is that the condition is only checked at the end of every time step. Hence, unless special precautions are taken to ensure that the simulator stops when the condition holds, it will unlikely be triggered. + ## [Specifying non-time independent variables](@id dsl_advanced_options_ivs) -As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on on time independent variable, and potentially one or more spatial independent variables. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using teh `@ivs` option. E.g. to use `s` instead of `t` we can use +As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on a *time independent variable*, and potentially one or more *spatial independent variables*. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using the `@ivs` option. E.g. to use `τ` instead of `t` we can use ```@example dsl_advanced_ivs using Catalyst # hide -rn = @reaction_network my_network begin - @ivs s +rn = @reaction_network begin + @ivs τ (ka,kD), Xi <--> Xa end nothing # hide ``` -We can confirm that $Xi$ and $Xa$ depend on `s` (and not `t`): +We can confirm that `Xi` and `Xa` depend on `τ` (and not `t`): ```@example dsl_advanced_ivs species(rn) ``` -It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default, time, variable, while the following one is considered spatial variables. If we want some species to depend on a non-time independent variable, this has to be explicitly declared: +It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default (time) independent variable, while the following one is considered spatial independent variables. If we want some species to depend on a non-default independent variable, this has to be explicitly declared: ```@example dsl_advanced_ivs -using Catalyst # hide -rn = @reaction_network my_network begin - @ivs t x - @species Y(s) +rn = @reaction_network begin + @ivs τ x + @species X(x) Y(x) (p1,d1), 0 <--> X (p2,d2), 0 <--> Y end @@ -403,8 +611,7 @@ species(rn) ``` Finally, it is possible to have species which depends on several independent variables: ```@example dsl_advanced_ivs -using Catalyst # hide -rn = @reaction_network my_network begin +rn = @reaction_network begin @ivs t x @species Xi(t,x) Xa(t,x) (ka,kD), Xi <--> Xa @@ -413,4 +620,4 @@ species(rn) ``` !!! note - Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying indepdent variables is currently limited. \ No newline at end of file + Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying spatial independent variables is currently limited. \ No newline at end of file diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index c70d6a259c..2819704642 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -1,15 +1,22 @@ # [The Catalyst DSL - Introduction](@id dsl_description) -In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` macro can be used to create chemical reaction network models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating chemical reaction network (CRN) models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSLs more advanced features. +In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSL's more advanced features. -The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction)). A descriptions of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial wil solely focus on options for model creation. +The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction) or [compositionally](@ref compositional_modeling)). A summary of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial will solely focus on model creation. -Before we begin the tutorial, we will first load the `Catalyst` package (which is required to run the code). +Before we begin, we will first load the `Catalyst` package (which is required to run the code). ```@example dsl_1 using Catalyst ``` -### Quick-start summary - +### [Quick-start summary](@id dsl_description_quick_start) +The DSL is initiated through the `@reaction_network`, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a Michaelis-Menten enzyme kinetics system can be written as +```@example dsl_0 +rn = @reaction_network begin + (kB,kC), S + E <--> SE + kP, SE --> P + E +end +``` +Here, `<-->` is used to create a bi-directional reaction (with forward rate `kP` and backward rate `kD`). Next, the model (stored in the variable `rn`) can be used as input to various types of [simulations](@ref ref). ## [Basic syntax](@id dsl_description_basic_syntax) The basic syntax of the DSL is @@ -19,12 +26,12 @@ rn = @reaction_network begin 1.0, Y --> X end ``` -Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with an `end`. Each reaction consists of -- A rate -- A (potentially empty) set of substrates. -- A (potentially empty) set of products. +Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with `end`. Each reaction consists of +- A *rate*. +- A (potentially empty) set of *substrates*. +- A (potentially empty) set of *products*. -Each reaction line declares, in order, the rate, the substrate(s), and the products. The rate is separated from the substrate(s) by a `,`, ad the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. +Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). @@ -49,15 +56,15 @@ Generally, anything that is a [permitted Julia variable name](@id https://docs.j ## [Different types of reactions](@id dsl_description_reactions) -### Reactions with multiple substrate(s) or product(s) -Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` binds (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: +### [Reactions with multiple substrates or products](@id dsl_description_reactions_multiples) +Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: ```@example dsl_1 rn2 = @reaction_network begin kB, X + Y --> XY kD, XY --> X + Y end ``` -Reactions can have any number of substrates and products, and their names does not need to have any relationship to each other, as demonstrated by the following mock-model: +Reactions can have any number of substrates and products, and their names do not need to have any relationship to each other, as demonstrated by the following mock model: ```@example dsl_1 rn3 = @reaction_network begin k, X + Y + Z --> A + B + C + D @@ -65,7 +72,7 @@ end ``` ### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) -Some reactions has no products, in which case the substrate(s) are degraded (ie.e removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions has no substrate(s), in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species $X$ is both created and degraded, we use: +Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created and degraded, we use: ```@example dsl_1 rn4 = @reaction_network begin p, 0 --> X @@ -73,17 +80,17 @@ rn4 = @reaction_network begin end ``` -### Reactions with non-unitary stoichiometries -Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which may dissociate back to two `X` copies) we use: +### [Reactions with non-unitary stoichiometries](@id dsl_description_reactions_stoichiometries) +Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: ```@example dsl_1 rn5 = @reaction_network begin kB, 2X --> X2 kD, X2 --> 2X end ``` -Reactant which stoichiometry is not defined is assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affects the created model can be found [here](@ref ref). +Reactants whose stoichiometries are not defined are assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affecting the created model can be found [here](@ref ref). -Stoichiometries can be combined with `( )` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: +Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: ```@example dsl_1 rn6 = @reaction_network begin k, 2X + 3(Y + 2Z) --> 5(V + W) @@ -94,7 +101,7 @@ nothing # hide ## [Bundling of similar reactions](@id dsl_description_reaction_bundling) -### Bi-directional (or reversible) reactions +### [Bi-directional (or reversible) reactions](@id dsl_description_reaction_bundling_reversible) As is the case for the following two-state model: ```@example dsl_1 rn7 = @reaction_network begin @@ -102,7 +109,7 @@ rn7 = @reaction_network begin k2, X2 --> X1 end ``` -it is common that reactions occurs in both direction. Here, it is possible ot bundle the reactions into a single line by using the `<-->` arrow. When we do this, we the rate term includes bother the rates (enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: +it is common that reactions occur in both directions (so-called *bi-directional* reactions). Here, it is possible to bundle the reactions into a single line by using the `<-->` arrow. When we do this, the rate term must include two separate rates (one for each direction, these are enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: ```@example dsl_1 rn7 = @reaction_network begin (k1,k2), X1 <--> X2 @@ -110,13 +117,13 @@ end ``` Here, the first rate (`k1`) denotes the *forward rate* and the second rate (`k2`) the *backwards rate*. -Catalyst also permits writing backwards reactions only. This uses the identical syntax to when forward reactions are created, but using the `<--` arrow: +Catalyst also permits writing pure backwards reactions. These use identical syntax to forward reactions, but with the `<--` arrow: ```@example dsl_1 rn8 = @reaction_network begin k, X <-- Y end ``` -Here, the substrate(s) are on the right-hand side and the product(s) on the left-hand side. Hence, the above model can be written identically using: +Here, the substrate(s) are on the right-hand side and the product(s) are on the left-hand side. Hence, the above model can be written identically using: ```@example dsl_1 rn8 = @reaction_network begin k, Y --> X @@ -124,35 +131,35 @@ end ``` Generally, using forward reactions is clearer than backwards ones, with the letter generally not being used. -### Bundling similar reactions on a singe line -There exists several other situation where models contain similar reactions (e.g. the systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following mode, where species $X$ and $Y$ both degrades at the rate $d$: +### [Bundling similar reactions on a single line](@id dsl_description_reaction_bundling_similar) +There exist several other situations where models contain similar reactions (e.g. systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model, where species `X` and `Y` both degrade at the rate `d`: ```@example dsl_1 rn8 = @reaction_network begin d, X --> 0 d, Y --> 0 end ``` -These share both the rates and products, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression. However, we have to provide two separate substrate expressions: +These share both the rates and product, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: ```@example dsl_1 rn8 = @reaction_network begin d, (X,Y) --> 0 end ``` -This declaration of the model is identical to the one previously.Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, does not make sense to use). I.e. if the tw reactions had different degradation rates: +This declaration of the model is identical to the one previously. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: ```@example dsl_1 rn9 = @reaction_network begin dX, X --> 0 dY, Y --> 0 end ``` -we could represent this using: +This can be represented using: ```@example dsl_1 rn9 = @reaction_network begin (dX,dY), (X,Y) --> 0 end ``` -It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions uses the same rate $k$): +It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions use the same rate $k$): ```@example dsl_1 rn10 = @reaction_network begin k, (X0,X1,X2,X3) --> (X1,X2,X3,X4) @@ -185,27 +192,27 @@ However, as for the above model, bundling reactions too zealously can reduce (ra ## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. -Let us consider a model with an activator ($A$, which degraded at a constant rate) and a protein ($P$). The production rate of $P$ depends both on $A$ and parameter ($kp$). We model this through: +Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on $A$ and a parameter (`kP`). We model this through: ```@example dsl_1 rn_13 = @reaction_network begin d, A --> 0 - kp*A, 0 --> P + kP*A, 0 --> P end ``` -Here, $P$'s production rate will decay as $A$ is slowly removed from the system. We can [check the ODE this model produce by using `Latexify`](@ref ref): +Here, `P`'s production rate will decay as `A` is slowly removed from the system. We can [print the ODE this model produces by using `Latexify`](@ref ref): ```@example dsl_1 using Latexify latexify(rn_13; form=:ode) ``` -In this case, we can generate an equivalent model by instead adding $A$ as both a substrate and a product to $A$'s production reaction: +In this case, we can generate an equivalent model by instead adding `A` as both a substrate and a product to `P`'s production reaction: ```@example dsl_1 rn_13_alt = @reaction_network begin d, A --> 0 kp, A --> A + P end ``` -We can confirm that this generate the same ODE: +We can confirm that this generates the same ODE: ```@example dsl_1 latexify(rn_13_alt; form=:ode) ``` @@ -214,7 +221,7 @@ Here, while the model will generate the same ODE, SDE, and jump simulations, the While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). !!! danger - Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This mean that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). + Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: ```@example dsl_1 @@ -225,25 +232,8 @@ rn_14 = @reaction_network begin end ``` -### Time-dependant rates -Previously we have assumed that that the rates are independent on the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progress, we can use: -```@example dsl_1 -rn_14 = @reaction_network begin - kp/t, 0 --> P - d, P --> 0 -end -``` - -Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to representing a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: -```@example dsl_1 -rn_15 = @reaction_network begin - A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P - d, P --> 0 -end -``` - -### Using functions in rates -It is possible for the rate to use Julia function. These can either be functions from Julia's standard library: +### [Using functions in rates](@id dsl_description_nonconstant_rates_functions) +It is possible for the rate to contain Julia function. These can either be functions from Julia's standard library: ```@example dsl_1 rn_16 = @reaction_network begin d, A --> 0 @@ -252,15 +242,15 @@ end ``` or ones defined by the user: ```@example dsl_1 -custom_function(p1,p2,X) = (p1+E)/(p2+E) +custom_function(p1, p2, X) = (p1 + X) / (p2 + X) rn_17 = @reaction_network begin d, A --> 0 custom_function(k1,k2,E), 0 --> P end ``` -### Using pre-declared Michaelis-Menten or Hill function rate -Two function frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where $X$ activates its own production through a hill function can be created using: +### (@id dsl_description_nonconstant_rates_available_functions) +Two functions frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where `X` activates its own production through a Hill function can be created using: ```@example dsl_1 custom_function(p1,p2,X) = (p1+E)/(p2+E) rn_18 = @reaction_network begin @@ -270,18 +260,34 @@ end ``` Catalyst comes with the following predefined functions: -- The Michaelis-Menten function: $mm(X,v,K) = v*X/(X + K)$. -- The repressive Michaelis-Menten function: $mmr(X,v,K) = v*K/(X + K)$. -- The Hill function: $hill(X,v,K,n) = v*(X^n)/(X^n + K^n)$. -- The repressive Hill function: $hillr(X,v,K,n) = v*(K^n)/(X^n + K^n)$. -- The activating/repressive Hill function: $hillar(X,Y,v,K,n) = v*(X^n)/(X^n + Y^n + K^n)$. +- The Michaelis-Menten function: $mm(X,v,K) = v * X/(X + K)$. +- The repressive Michaelis-Menten function: $mmr(X,v,K) = v * K/(X + K)$. +- The Hill function: $hill(X,v,K,n) = v * (X^n)/(X^n + K^n)$. +- The repressive Hill function: $hillr(X,v,K,n) = v * (K^n)/(X^n + K^n)$. +- The activating/repressive Hill function: $hillar(X,Y,v,K,n) = v * (X^n)/(X^n + Y^n + K^n)$. +### [Time-dependant rates](@id dsl_description_nonconstant_rates_time) +Previously we have assumed that the rates are independent of the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progresses, we can use: +```@example dsl_1 +rn_14 = @reaction_network begin + kp/(1 + t), 0 --> P + d, P --> 0 +end +``` + +Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to represent a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: +```@example dsl_1 +rn_15 = @reaction_network begin + A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P + d, P --> 0 +end +``` ## [Using special symbols](@id dsl_description_symbols) -Julia permits any unicode characters to be used in variable names, thus Catalyst are able to use these as well. Below we describe some case where these to alter the appearance of models. No functionality is, however, tied to this. +Julia permits any Unicode characters to be used in variable names, thus Catalyst can use these as well. Below we describe some cases where this can be useful. No functionality is, however, tied to this. -### [Using ∅ to denote degradation/production reactions](@id dsl_description_symbols_empty_set) -Previously, we described how `0` could be used to [denote degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system alternatively can be written as: +### [Using ∅ in degradation/production reactions](@id dsl_description_symbols_empty_set) +Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system can alternatively be written as: ```@example dsl_1 rn4 = @reaction_network begin p, ∅ --> X @@ -289,8 +295,8 @@ rn4 = @reaction_network begin end ``` -### Using special arrow symbols -Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representation of these arrows are available. Here, +### [Using special arrow symbols](@id dsl_description_symbols_arrows) +Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representations of these arrows are available. Here, - `>`, `→`, `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, and `⇁` can be used to represent forward reactions. - `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent reversible reactions. - `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. @@ -303,14 +309,14 @@ rn4 = @reaction_network begin end ``` -### Using special symbols to denote species or parameters +### [Using special symbols to denote species or parameters](@id dsl_description_symbols_special) A range of possible characters are available which can be incorporated into species and parameter names. This includes, but is not limited to: - Greek letters (e.g `α`, `σ`, `τ`, and `Ω`). -- Superscript and subscript characters (to create e.g `k₁`, `k₂`, `Xₐ`, and `Xᴾ`). +- Superscript and subscript characters (to create e.g. `k₁`, `k₂`, `Xₐ`, and `Xᴾ`). - Non-latin, non-greek, letters (e.g. `ä`, `Д`, `س`, and `א`). - Other symbols (e.g. `£`, `ℂ`, `▲`, and `♠`). -An example of how this can be used to create neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used it the creation a model of the sigma factor V circuit in the bacteria *Bacillus subtilis*: +An example of how this can be used to create a neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used to model a sigma factor V circuit in the bacteria *Bacillus subtilis*: ```@example dsl_1 σᵛ_model = @reaction_network begin v₀ + hill(σᵛ,v,K,n), ∅ → σᵛ + A @@ -321,7 +327,7 @@ end nothing # hide ``` -This functionality can also be used to create less-serious models: +This functionality can also be used to create less serious models: rn_13 = @reaction_network begin 🍦, 😢 --> 😃 end @@ -329,10 +335,10 @@ end It should be noted that the following symbols are *not permitted* to be used as species or parameter names: - `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)) - `pi` and `π` (used in Julia to denote [`3.1415926535897...`](https://en.wikipedia.org/wiki/Pi)). -- `t` (used to denote the [time variable](@ref ref)). +- `t` (used to denote the [time variable](@ref dsl_description_nonconstant_rates_time)). - `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)) - `im` (used in Julia to represent [complex numbers](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/#Complex-Numbers)). -- `nothing` ([used in Julia](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). +- `nothing` (used in Julia to denote [nothing](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). - `Γ` (used by Catalyst to represent [conserved quantities](@ref ref)). From 15ef2f16bd947ecc9c9fc13cfc9c28ca522581fb Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 26 Apr 2024 17:30:19 -0400 Subject: [PATCH 17/23] save progress --- .../dsl_advanced_options.md | 85 ++++++++++--------- .../catalyst_functionality/dsl_description.md | 64 +++++++------- 2 files changed, 73 insertions(+), 76 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/catalyst_functionality/dsl_advanced_options.md index d0edec4458..7afb7a6427 100644 --- a/docs/src/catalyst_functionality/dsl_advanced_options.md +++ b/docs/src/catalyst_functionality/dsl_advanced_options.md @@ -1,30 +1,34 @@ # [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) -Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous tutorial](@ref dsl_description) described how to create reactions. This one will instead describe how to use options. These are typically used to supplant the model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. +Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous DSL tutorial](@ref dsl_description) described how to create reactions. This one will focus on options. These are typically used to supplant a model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. -All options are designated begins with a symbol starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each declaration of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. +All options designations begins with declaration starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each use of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that does not directly include using an option. -This tutorial will also describe some additional advanced DSL features that does not include using an option. As a first step, we import Catalyst (which is required to run the tutorial): +As a first step, we import Catalyst (which is required to run the tutorial): ```@example dsl_advanced_1 using Catalyst ``` ## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) -[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining symbols as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default -behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive (`Pᵢ`) to its active (`Pₐ`) is catalysed by an enzyme `E`. Using the most natural description: +[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining quantities as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive form (`Pᵢ`) to its active form (`Pₐ`) is catalysed by an enzyme (`E`). Using the most natural description: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ end ``` -`E` (as well as `k`) will be considered a parameter (this can be checked using e.g. `parameters(catalysis_sys)`). If we want `E` to be considered a species, we can designate this using the `@species` option: +`E` (as well as `k`) will be considered a parameter, something we can confirm directly: +```@example dsl_advanced_1 +parameters(catalysis_sys) +``` +If we want `E` to be considered a species, we can designate this using the `@species` option: ```@example dsl_advanced_1 catalysis_sys = @reaction_network begin @species E(t) k*E, Pᵢ --> Pₐ end +parameters(catalysis_sys) ``` !!! note - When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-time dependant species is also possible](@ref ref)). + When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-`t` dependant species is also possible](@ref ref)). Similarly, the `@parameters` option can be used to explicitly designate something as a parameter: ```@example dsl_advanced_1 @@ -33,7 +37,7 @@ catalysis_sys = @reaction_network begin k*E, Pᵢ --> Pₐ end ``` -Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a symbol cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. +Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a quantity cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). @@ -76,13 +80,13 @@ parameters(dimerisation) The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either approach, that knowledge can be transferred to the other one. Generally, there are four main reasons for specifying species/parameters using the `@species` and `@parameters` option: -1. To designate a symbol, that would otherwise have defaulted to a parameter, as a species. +1. To designate a quantity, that would otherwise have defaulted to a parameter, as a species. 2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). 3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). -1. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) +4. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) !!!! warn - Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *symbols that appear in reactions*. Until now this has not been relevant. However, this tutorial will demosntrate use cases where species/parameters, that are not part of reactions, are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). + Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *quantities that appear in reactions*. Until now this has not been relevant. However, this tutorial will demonstrate cases where species/parameters that are not part of reactions are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). ### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` followed by the desired default value). E.g here we set `X`'s default initial condition value to $1.0$, and `p` and `d`'s default values to $1.0$ and $0.2$, respectively: @@ -94,7 +98,7 @@ rn = @reaction_network begin (p,d), 0 <--> X end ``` -Next, if we simulate the model, we do not need to provide values for species/parameters which have default values. In this case all have default values, so both `u0` and `ps` can be empty vectors: +Next, if we simulate the model, we do not need to provide values for species or parameters that have default values. In this case all have default values, so both `u0` and `ps` can be empty vectors: ```@example dsl_advanced_defaults using OrdinaryDiffEq, Plots u0 = [] @@ -104,7 +108,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -It is still possible to provide values for some (or all) initial conditions/parameters in `u0` and `ps` (in which case these overrides the default values): +It is still possible to provide values for some (or all) initial conditions/parameters in `u0`/`ps` (in which case these overrides the default values): ```@example dsl_advanced_defaults u0 = [:X => 4.0] p = [:d => 0.5] @@ -126,7 +130,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -API for checking the default values of a species or parameters can be found [here](@ref ref). +API for checking the default values of species and parameters can be found [here](@ref ref). ### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of `X` using the parameter `X₀`. @@ -153,7 +157,7 @@ oprob = ODEProblem(rn, u0, tspan, p) sol = solve(oprob, Tsit5()) plot(sol) ``` -Please note that `X₀` is still a parameter of the system, and its value must still be designated to simulate the model. +Please note that `X₀` is still a parameter of the system, and as such its value must still be designated to simulate the model (even if it is not actually used). ### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically how this is done). Here we will introduce how to set metadata, and describe some common metadata types. @@ -162,12 +166,12 @@ Whenever a species/parameter is declared using the `@species`/`@parameters` opti ```@example dsl_advanced_metadata using Catalyst # hide two_state_system = @reaction_network begin - @species Xi(t) [description="The species X's inactive form"] Xa(t) [description="The species X's active form"] + @species Xi(t) [description="The X's inactive form"] Xa(t) [description="The X's active form"] @parameters kA [description="X's activation rate"] kD [description="X's deactivation rate"] (ka,kD), Xi <--> Xa end ``` -A metadata can be given to only a subset of a system's species/parameters. To give several metadata, separate each by a `,`. Here we only provide a description for `kA`, for which we also provide a [bounds metadata](@ref https://docs.sciml.ai/ModelingToolkit/dev/basics/Variable_metadata/#Bounds), +A metadata can be given to only a subset of a system's species/parameters, and a quantity can be given several metadata entries. To give several metadata, separate each by a `,`. Here we only provide a description for `kA`, for which we also provide a [bounds metadata](@ref https://docs.sciml.ai/ModelingToolkit/dev/basics/Variable_metadata/#Bounds), ```@example dsl_advanced_metadata two_state_system = @reaction_network begin @parameters kA [description="X's activation rate", bound=(0.01,10.0)] @@ -183,7 +187,7 @@ two_state_system = @reaction_network begin end ``` -When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. the previous example is rewritten as +When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. a version of the previous example can be written as ```@example dsl_advanced_metadata two_state_system = @reaction_network begin @parameters begin @@ -199,11 +203,11 @@ Each metadata has its own getter functions. E.g. we can get the description of t getdescription(two_state_system.kA) ``` -It is not possible for the user to directly designate their own metadata. These have to first be added to Catalyst. Doing so is somewhat involved, and described in detail [here](). A full list of metadata that can be used for species and/or parameters can be found [here](@ref ref). +It is not possible for the user to directly designate their own metadata. These have to first be added to Catalyst. Doing so is somewhat involved, and described in detail [here](@ref ref). A full list of metadata that can be used for species and/or parameters can be found [here](@ref ref). -### [Designating constant-valued/fixed species-parameters](@id dsl_advanced_options_constant_species) +### [Designating constant-valued/fixed species parameters](@id dsl_advanced_options_constant_species) -Catalyst enables the designation of parameters as `constantspecies`. These can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of an event]@ref dsl_advanced_options_events). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species `X` is converted to `Xᴾ` at rate `k`. By designating `X` as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. +Catalyst enables the designation of parameters as `constantspecies`. These parameters can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of an event]@ref ref). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species `X` is converted to `Xᴾ` at rate `k`. By designating `X` as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. ```@example dsl_advanced_constant_species using Catalyst # hide rn = @reaction_network begin @@ -211,20 +215,21 @@ rn = @reaction_network begin k, X --> Xᴾ end ``` -We can confirm that $X$ is the only species of the system: +We can confirm that $Xᴾ$ is the only species of the system: ```@example dsl_advanced_constant_species species(rn) ``` -Here, the produced model is actually identical to if $X$ had simply been put as a parameter in the reaction's rate: +Here, the produced model is actually identical to if $X$ had simply been a parameter in the reaction's rate: ```@example dsl_advanced_constant_species rn = @reaction_network begin k*X, 0 --> Xᴾ end ``` -A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is [the Brusselator](https://en.wikipedia.org/wiki/Brusselator). +A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is the [Brusselator](https://en.wikipedia.org/wiki/Brusselator). ### [Specifying non-species variables](@id dsl_advanced_options_variables) +--- MOVE THIS TO HYBRID SECTION --- Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of *reaction events*. When they are converted to ODEs, the species become unknowns of the system. However, Catalyst permits the creation of [hybrid CRN models](@ref ref). These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it cannot plausibly be a reaction reactant). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: ```@example dsl_advanced_variables using Catalyst # hide @@ -250,6 +255,7 @@ You can set metadata and default initial condition values for variables using th You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknowns` function retrieves both. ## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) +--- DISCUSS WHAT TO DO WITH THIS SECTION --- Reactions can also have metadata. This is described in detail [here](@ref ref). ## [Naming reaction networks](@id dsl_advanced_options_naming) @@ -277,16 +283,12 @@ end rn2 = @reaction_network begin (p,d), 0 <--> X end -nothing # hide +rn1 == rn2 ``` -Here, since the networks' names are different: +The reason can be confirmed by checking that their respective (randomly generated) names are different: ```@example dsl_advanced_names nameof(rn1) == nameof(rn2) ``` -they are different -```@example dsl_advanced_names -rn1 == rn2 -``` By designating the networks to have the same name, however, identity is achieved. ```@example dsl_advanced_names rn1 = @reaction_network my_network begin @@ -298,12 +300,12 @@ end rn1 == rn2 ``` -Setting names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. +Setting model names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. ## [Creating observables](@id dsl_advanced_options_observables) -Sometimes, one might want to include observable variables. These are variables which values that can be computed directly from the systems species, parameters, and variables (rather than having their values implicitly given by an reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. +Sometimes one might want to use observable variables. These are variables with values that can be computed directly from a system's state (rather than having their values implicitly given by reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. -Let us consider a model where two species (`X` and `Y`) can bind to form a complex ($XY$, which also can dissociate back into `X` and `Y`). If we wish to create a representation for the total amount of `X` and `Y` in the system, we can do this by creating observables `Xtot` and `Ytot`: +Let us consider a model where two species (`X` and `Y`) can bind to form a complex (`XY`, which also can dissociate back into `X` and `Y`). If we wish to create a representation for the total amount of `X` and `Y` in the system, we can do this by creating observables `Xtot` and `Ytot`: ```@example dsl_advanced_observables using Catalyst # hide rn = @reaction_network begin @@ -329,14 +331,14 @@ Next, we can use [symbolic indexing](@ref simulation_structure_interfacing) of o ```@example dsl_advanced_observables sol[:Xtot] ``` -to get a vector with `Xtot`'s throughout the simulation. We can also use +to get a vector with `Xtot`'s value throughout the simulation. We can also use ```@example dsl_advanced_observables using Plots plot(sol; idxs = [:Xtot, :Ytot]) ``` to plot the observables (rather than the species). -Observables can be defined using complicated expression containing species, parameters, and variables (but not other observables). in the following example (which uses a [parametric stoichiometry](@ref ref)) $X$ polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: +Observables can be defined using complicated expression containing species, parameters, and [variables](@ref ref) (but not other observables). In the following example (which uses a [parametric stoichiometry](@ref ref)) `X` polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: ```@example dsl_advanced_observables rn = @reaction_network begin @observables Xtot ~ X + n*Xn @@ -350,13 +352,13 @@ nothing # hide [Metadata](@ref dsl_advanced_options_species_and_parameters_metadata) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref dsl_advanced_options_species_and_parameters_metadata) to our observable we can use ```@example dsl_advanced_observables rn = @reaction_network begin - @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY + @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*Xn (kB,kD), n*X <--> Xn end nothing # hide ``` -Observables are by default considered [variables](@ref dsl_advanced_options_variables) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here `Xtot` becomes a species: +Observables are by default considered [variables](@ref ref) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here `Xtot` becomes a species: ```@example dsl_advanced_observables rn = @reaction_network begin @species Xtot(t) @@ -368,11 +370,12 @@ nothing # hide Some final notes regarding observables: - The left-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). -- All symbols appearing on the right-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). +- All quantities appearing on the right-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). - Observables may not depend on other observables. -- Observables have their [dependent variables](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. +- Observables have their [dependent variable(s)](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. ## [Creating events](@id dsl_advanced_options_events) +--- MOVE TO SEPARATE DOC PAGE --- Sometimes one wishes to model events, describing things that can happen to it during a simulation. - A chemical system where an amount of some species is added at a time point after the simulation's initiation. - A simulation of a circadian rhythm, where light is turned on/off every 12 hours. @@ -599,7 +602,7 @@ We can confirm that `Xi` and `Xa` depend on `τ` (and not `t`): species(rn) ``` -It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default (time) independent variable, while the following one is considered spatial independent variables. If we want some species to depend on a non-default independent variable, this has to be explicitly declared: +It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default (time) independent variable, while the following one(s) are considered spatial independent variable(s). If we want some species to depend on a non-default independent variable, this has to be explicitly declared: ```@example dsl_advanced_ivs rn = @reaction_network begin @ivs τ x @@ -609,7 +612,7 @@ rn = @reaction_network begin end species(rn) ``` -Finally, it is possible to have species which depends on several independent variables: +It is also possible to have species which depends on several independent variables: ```@example dsl_advanced_ivs rn = @reaction_network begin @ivs t x diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index 2819704642..ea89995ec0 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -1,9 +1,9 @@ # [The Catalyst DSL - Introduction](@id dsl_description) -In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref) will describe some of the DSL's more advanced features. +In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref ref) will describe some of the DSL's more advanced features. The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction) or [compositionally](@ref compositional_modeling)). A summary of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial will solely focus on model creation. -Before we begin, we will first load the `Catalyst` package (which is required to run the code). +Before we begin, we will first load the Catalyst package (which is required to run the code). ```@example dsl_1 using Catalyst ``` @@ -12,7 +12,7 @@ using Catalyst The DSL is initiated through the `@reaction_network`, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a Michaelis-Menten enzyme kinetics system can be written as ```@example dsl_0 rn = @reaction_network begin - (kB,kC), S + E <--> SE + (kB,kD), S + E <--> SE kP, SE --> P + E end ``` @@ -31,12 +31,12 @@ Here, you start with `@reaction_network begin`, next list all of the model's rea - A (potentially empty) set of *substrates*. - A (potentially empty) set of *products*. -Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above examples, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. +Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above example, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. In the second reaction, `Y` becomes `X` at rate `1.0`. Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). ## [Defining parameters and species in the DSL](@id dsl_description_parameters_basics) -Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you which to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: +Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you wish to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: ```@example dsl_1 rn1 = @reaction_network begin a, X --> Y @@ -57,7 +57,7 @@ Generally, anything that is a [permitted Julia variable name](@id https://docs.j ## [Different types of reactions](@id dsl_description_reactions) ### [Reactions with multiple substrates or products](@id dsl_description_reactions_multiples) -Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate at, rate `kD`, to form `XY`) we use: +Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate, at rate `kD`, to form `XY`) we use: ```@example dsl_1 rn2 = @reaction_network begin kB, X + Y --> XY @@ -72,7 +72,7 @@ end ``` ### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) -Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created and degraded, we use: +Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created (in the first reaction) and degraded (in a second reaction), we use: ```@example dsl_1 rn4 = @reaction_network begin p, 0 --> X @@ -81,7 +81,7 @@ end ``` ### [Reactions with non-unitary stoichiometries](@id dsl_description_reactions_stoichiometries) -Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating the number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: +Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating its number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: ```@example dsl_1 rn5 = @reaction_network begin kB, 2X --> X2 @@ -90,7 +90,7 @@ end ``` Reactants whose stoichiometries are not defined are assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affecting the created model can be found [here](@ref ref). -Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction, both with and without this notation: +Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction twice, both with and without this notation: ```@example dsl_1 rn6 = @reaction_network begin k, 2X + 3(Y + 2Z) --> 5(V + W) @@ -129,23 +129,23 @@ rn8 = @reaction_network begin k, Y --> X end ``` -Generally, using forward reactions is clearer than backwards ones, with the letter generally not being used. +Generally, using forward reactions is clearer than backwards ones, with the later generally not being used. ### [Bundling similar reactions on a single line](@id dsl_description_reaction_bundling_similar) -There exist several other situations where models contain similar reactions (e.g. systems where all system components degrade at the same rate). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model, where species `X` and `Y` both degrade at the rate `d`: +There exist additional situations where models contain similar reactions (e.g. systems where all system components degrade at identical rates). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model where species `X` and `Y` both degrade at the rate `d`: ```@example dsl_1 rn8 = @reaction_network begin d, X --> 0 d, Y --> 0 end ``` -These share both the rates and product, however, the substrates are different. Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: +These share both the rates (`d`) and product (`0`), however, the substrates are different (`X` and `Y`). Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: ```@example dsl_1 rn8 = @reaction_network begin d, (X,Y) --> 0 end ``` -This declaration of the model is identical to the one previously. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: +This declaration of the model is identical to the previous one. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: ```@example dsl_1 rn9 = @reaction_network begin dX, X --> 0 @@ -166,7 +166,7 @@ rn10 = @reaction_network begin end ``` -It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rate(s). These may then (or may not) indicate several rates. We exemplify this using the two following (identical) networks, created with and without bundling. +It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rates. These may then (or may not) indicate several rates. We exemplify this using the two following two (identical) networks, created with and without bundling. ```@example dsl_1 rn11 = @reaction_network begin kf, S --> P1 @@ -187,19 +187,19 @@ rn12 = @reaction_network begin ((pX, pY, pZ),d), (0, Y0, Z0) <--> (X, Y, Z1+Z2) end ``` -However, as for the above model, bundling reactions too zealously can reduce (rather improve) the model's readability. +However, like for the above model, bundling reactions too zealously can reduce (rather improve) a model's readability. ## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. -Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on $A$ and a parameter (`kP`). We model this through: +Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on `A` and a parameter (`kP`). We model this through: ```@example dsl_1 rn_13 = @reaction_network begin d, A --> 0 kP*A, 0 --> P end ``` -Here, `P`'s production rate will decay as `A` is slowly removed from the system. We can [print the ODE this model produces by using `Latexify`](@ref ref): +Here, `P`'s production rate will be reduced as `A` decays. We can [print the ODE this model produces with `Latexify`](@ref ref): ```@example dsl_1 using Latexify latexify(rn_13; form=:ode) @@ -216,24 +216,24 @@ We can confirm that this generates the same ODE: ```@example dsl_1 latexify(rn_13_alt; form=:ode) ``` -Here, while the model will generate the same ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. +Here, while these models will generate identical ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. !!! warn While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). !!! danger - Catalyst automatically infers whether variables appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). + Catalyst automatically infers whether quantities appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: ```@example dsl_1 rn_14 = @reaction_network begin 2.0 + X^2, 0 --> X + Y - k1+k2^k3, X --> ∅ - pi*X/(sqrt(2)+Y), Y → ∅ + k1 + k2^k3, X --> ∅ + pi * X/(sqrt(2) + Y), Y → ∅ end ``` ### [Using functions in rates](@id dsl_description_nonconstant_rates_functions) -It is possible for the rate to contain Julia function. These can either be functions from Julia's standard library: +It is possible for the rate to contain Julia functions. These can either be functions from Julia's standard library: ```@example dsl_1 rn_16 = @reaction_network begin d, A --> 0 @@ -249,7 +249,7 @@ rn_17 = @reaction_network begin end ``` -### (@id dsl_description_nonconstant_rates_available_functions) +### [Pre-defined functions](@id dsl_description_nonconstant_rates_available_functions) Two functions frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where `X` activates its own production through a Hill function can be created using: ```@example dsl_1 custom_function(p1,p2,X) = (p1+E)/(p2+E) @@ -287,7 +287,7 @@ end Julia permits any Unicode characters to be used in variable names, thus Catalyst can use these as well. Below we describe some cases where this can be useful. No functionality is, however, tied to this. ### [Using ∅ in degradation/production reactions](@id dsl_description_symbols_empty_set) -Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅`. E.g. the production/degradation system can alternatively be written as: +Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅` symbol. E.g. the production/degradation system can alternatively be written as: ```@example dsl_1 rn4 = @reaction_network begin p, ∅ --> X @@ -296,12 +296,12 @@ end ``` ### [Using special arrow symbols](@id dsl_description_symbols_arrows) -Catalyst uses `-->`, `<-->`, and `<--` to denote forward, reversible, and backwards reactions, respectively. Several unicode representations of these arrows are available. Here, +Catalyst uses `-->`, `<-->`, and `<--` to denote forward, bi-directional, and backwards reactions, respectively. Several unicode representations of these arrows are available. Here, - `>`, `→`, `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, and `⇁` can be used to represent forward reactions. -- `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent reversible reactions. +- `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent bi-directional reactions. - `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. -E.g. the production/degradation system alternatively can be written as: +E.g. the production/degradation system can alternatively be written as: ```@example dsl_1 rn4 = @reaction_network begin p, ∅ → X @@ -333,17 +333,11 @@ rn_13 = @reaction_network begin end It should be noted that the following symbols are *not permitted* to be used as species or parameter names: -- `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)) - `pi` and `π` (used in Julia to denote [`3.1415926535897...`](https://en.wikipedia.org/wiki/Pi)). +- `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)). - `t` (used to denote the [time variable](@ref dsl_description_nonconstant_rates_time)). -- `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)) +- `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)). - `im` (used in Julia to represent [complex numbers](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/#Complex-Numbers)). - `nothing` (used in Julia to denote [nothing](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). - `Γ` (used by Catalyst to represent [conserved quantities](@ref ref)). - - - - - - From dfa831e954645e731724dad99f9d279ee40e85d4 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 2 May 2024 16:40:30 -0400 Subject: [PATCH 18/23] save progress. Add stuff on non-standard stoichiometries. --- .../catalyst_functionality/dsl_description.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index ea89995ec0..dce49c3037 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -283,6 +283,33 @@ rn_15 = @reaction_network begin end ``` +## [Non-standard stoichiometries](@id dsl_description_stoichiometries) + +### [Non-integer stoichiometries](@id dsl_description_stoichiometries_decimal) +Previously all stoichiometric constants have been integer numbers, however, decimal numbers are also permitted. Here we create a birth-death model where each production reaction produces 1.5 units of `X`: +```@example dsl_1 +rn_16 = @reaction_network begin + p, 0 --> 1.5X + d, X --> 0 +end +``` +It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the `combinatoric_ratelaw = false` option must be used. + +### [Parametric stoichiometries](@id dsl_description_stoichiometries_decimal) +It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: +```@example dsl_1 +rn_17 = @reaction_network begin + (kB,kD), n*X <--> Xn +end +``` +Now we can designate the value of `n` through a parameter when we e.g. create an `ODEProblem`: +```@example dsl_1 +u0 = [:X => 5.0, :Xn => 1.0] +ps = [:kB => 1.0, :kD => 0.1, :n => 4] +oprob = ODEProblem(rn, u0, (0.0, 1.0), ps) +nothing # hide +``` + ## [Using special symbols](@id dsl_description_symbols) Julia permits any Unicode characters to be used in variable names, thus Catalyst can use these as well. Below we describe some cases where this can be useful. No functionality is, however, tied to this. From f4131bfe4b360062edc6d56140e608c20207fba6 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 2 May 2024 16:40:51 -0400 Subject: [PATCH 19/23] save progress --- docs/src/catalyst_functionality/dsl_description.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index dce49c3037..9f82386d74 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -295,7 +295,7 @@ end ``` It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the `combinatoric_ratelaw = false` option must be used. -### [Parametric stoichiometries](@id dsl_description_stoichiometries_decimal) +### [Parametric stoichiometries](@id dsl_description_stoichiometries_parameters) It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: ```@example dsl_1 rn_17 = @reaction_network begin From 620dd45699666c2fc7f3ad8e935a15b9eba113e8 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 2 May 2024 16:49:41 -0400 Subject: [PATCH 20/23] save progress --- docs/src/catalyst_functionality/dsl_description.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/catalyst_functionality/dsl_description.md index 9f82386d74..a230c2de00 100644 --- a/docs/src/catalyst_functionality/dsl_description.md +++ b/docs/src/catalyst_functionality/dsl_description.md @@ -293,7 +293,7 @@ rn_16 = @reaction_network begin d, X --> 0 end ``` -It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the `combinatoric_ratelaw = false` option must be used. +It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the [`combinatoric_ratelaw = false`](@ref ref) option must be used. We note that non-integer stoichiometric coefficients does not make sense in most fields, however, this features is available for use for models where it does make sense. ### [Parametric stoichiometries](@id dsl_description_stoichiometries_parameters) It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: @@ -306,7 +306,7 @@ Now we can designate the value of `n` through a parameter when we e.g. create an ```@example dsl_1 u0 = [:X => 5.0, :Xn => 1.0] ps = [:kB => 1.0, :kD => 0.1, :n => 4] -oprob = ODEProblem(rn, u0, (0.0, 1.0), ps) +oprob = ODEProblem(rn_17, u0, (0.0, 1.0), ps) nothing # hide ``` From 00029df8295b24203eb2854bd91b287c7830c924 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 9 May 2024 13:17:06 -0400 Subject: [PATCH 21/23] move to new doc structure --- .../dsl_description.md => 02_model_creation/01_dsl_basics.md} | 0 .../02_dsl_advanced.md} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/src/{catalyst_functionality/dsl_description.md => 02_model_creation/01_dsl_basics.md} (100%) rename docs/src/{catalyst_functionality/dsl_advanced_options.md => 02_model_creation/02_dsl_advanced.md} (100%) diff --git a/docs/src/catalyst_functionality/dsl_description.md b/docs/src/02_model_creation/01_dsl_basics.md similarity index 100% rename from docs/src/catalyst_functionality/dsl_description.md rename to docs/src/02_model_creation/01_dsl_basics.md diff --git a/docs/src/catalyst_functionality/dsl_advanced_options.md b/docs/src/02_model_creation/02_dsl_advanced.md similarity index 100% rename from docs/src/catalyst_functionality/dsl_advanced_options.md rename to docs/src/02_model_creation/02_dsl_advanced.md From ba8189246316bc78ebd58e18d71f23de9566e42b Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 16 May 2024 16:42:05 -0400 Subject: [PATCH 22/23] rebase --- docs/src/02_model_creation/01_dsl_basics.md | 370 ----------- docs/src/02_model_creation/02_dsl_advanced.md | 626 ------------------ 2 files changed, 996 deletions(-) delete mode 100644 docs/src/02_model_creation/01_dsl_basics.md delete mode 100644 docs/src/02_model_creation/02_dsl_advanced.md diff --git a/docs/src/02_model_creation/01_dsl_basics.md b/docs/src/02_model_creation/01_dsl_basics.md deleted file mode 100644 index a230c2de00..0000000000 --- a/docs/src/02_model_creation/01_dsl_basics.md +++ /dev/null @@ -1,370 +0,0 @@ -# [The Catalyst DSL - Introduction](@id dsl_description) -In the [introduction to Catalyst](@ref introduction_to_catalyst) we described how the `@reaction_network` [macro](https://docs.julialang.org/en/v1/manual/metaprogramming/#man-macros) can be used to create chemical reaction network (CRN) models. This macro enables a so-called [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) for creating CRN models. This tutorial will give a basic introduction on how to create Catalyst models using this macro (from now onwards called the "*Catalyst DSL*"). A [follow-up tutorial](@ref ref) will describe some of the DSL's more advanced features. - -The Catalyst DSL generates a [`ReactionSystem`](@ref) (the [julia structure](https://docs.julialang.org/en/v1/manual/types/#Composite-Types) Catalyst uses to represent CRN models). These can be created through alternative methods (e.g. [programmatically](@ref programmatic_CRN_construction) or [compositionally](@ref compositional_modeling)). A summary of the various ways to create `ReactionSystems`s can be found [here](@ref ref). [Previous](@ref ref) and [following](@ref ref) tutorials describe how to simulate models once they have been created using the DSL. This tutorial will solely focus on model creation. - -Before we begin, we will first load the Catalyst package (which is required to run the code). -```@example dsl_1 -using Catalyst -``` - -### [Quick-start summary](@id dsl_description_quick_start) -The DSL is initiated through the `@reaction_network`, which is followed by one line for each reaction. Each reaction consists of a *rate*, followed lists first of the substrates and next of the products. E.g. a Michaelis-Menten enzyme kinetics system can be written as -```@example dsl_0 -rn = @reaction_network begin - (kB,kD), S + E <--> SE - kP, SE --> P + E -end -``` -Here, `<-->` is used to create a bi-directional reaction (with forward rate `kP` and backward rate `kD`). Next, the model (stored in the variable `rn`) can be used as input to various types of [simulations](@ref ref). - -## [Basic syntax](@id dsl_description_basic_syntax) -The basic syntax of the DSL is -```@example dsl_1 -rn = @reaction_network begin - 2.0, X --> Y - 1.0, Y --> X -end -``` -Here, you start with `@reaction_network begin`, next list all of the model's reaction, and finish with `end`. Each reaction consists of -- A *rate*. -- A (potentially empty) set of *substrates*. -- A (potentially empty) set of *products*. - -Each reaction line declares, in order, the rate, the substrate(s), and the product(s). The rate is separated from the substrate(s) by a `,`, and the substrate(s) from the production by a `-->` (other arrows, however, are [also possible](@ref ref)). In the above example, our model consists of two reactions. In the first one, `X` (the single substrate) becomes `Y` (the single product) at rate `2.0`. In the second reaction, `Y` becomes `X` at rate `1.0`. - -Finally, `rn = ` is used to store the model in the variable `rn` (a normal Julia variable, which does not need to be called `rn`). - -## [Defining parameters and species in the DSL](@id dsl_description_parameters_basics) -Typically, the rates are not constants, but rather parameters (which values can be set e.g. at [the beginning of each simulation](@ref ref)). To set parametric rates, simply use whichever symbol you wish to represent your parameter with. E.g. to set the above rates to `a` and `b`, we use: -```@example dsl_1 -rn1 = @reaction_network begin - a, X --> Y - b, Y --> X -end -``` - -Here we have used single-character symbols to designate all species and parameters. Multi-character symbols, however, are also permitted. E.g. we could call the rates `kX` and `kY`: -```@example dsl_1 -rn1 = @reaction_network begin - kX, X --> Y - kY, Y --> X -end -nothing # hide -``` -Generally, anything that is a [permitted Julia variable name](@id https://docs.julialang.org/en/v1/manual/variables/#man-allowed-variable-names) can be used to designate a species or parameter in Catalyst. - -## [Different types of reactions](@id dsl_description_reactions) - -### [Reactions with multiple substrates or products](@id dsl_description_reactions_multiples) -Previously, our reactions have had a single substrate and a single product. However, reactions with multiple substrates and/or products are possible. Here, all the substrates (or products) are listed and separated by a `+`. E.g. to create a model where `X` and `Y` bind (at rate `kB`) to form `XY` (which then can dissociate, at rate `kD`, to form `XY`) we use: -```@example dsl_1 -rn2 = @reaction_network begin - kB, X + Y --> XY - kD, XY --> X + Y -end -``` -Reactions can have any number of substrates and products, and their names do not need to have any relationship to each other, as demonstrated by the following mock model: -```@example dsl_1 -rn3 = @reaction_network begin - k, X + Y + Z --> A + B + C + D -end -``` - -### [Reactions with degradation or production](@id dsl_description_reactions_degradation_and_production) -Some reactions have no products, in which case the substrate(s) are degraded (i.e. removed from the system). To denote this, set the reaction's right-hand side to `0`. Similarly, some reactions have no substrates, in which case the product(s) are produced (i.e. added to the system). This is denoted by setting the left-hand side to `0`. E.g. to create a model where a single species `X` is both created (in the first reaction) and degraded (in a second reaction), we use: -```@example dsl_1 -rn4 = @reaction_network begin - p, 0 --> X - d, X --> 0 -end -``` - -### [Reactions with non-unitary stoichiometries](@id dsl_description_reactions_stoichiometries) -Reactions may include multiple copies of the same reactant (i.e. a substrate or a product). To specify this, the reactant is preceded by a number indicating its number of copies (also called the reactant's *stoichiometry*). E.g. to create a model where two copies of `X` dimerise to form `X2` (which then dissociate back to two `X` copies) we use: -```@example dsl_1 -rn5 = @reaction_network begin - kB, 2X --> X2 - kD, X2 --> 2X -end -``` -Reactants whose stoichiometries are not defined are assumed to have stoichiometry `1`. Any integer number can be used, furthermore, [decimal numbers and parameters can also be used as stoichiometries](@ref ref). A discussion of non-unitary (i.e. not equal to `1`) stoichiometries affecting the created model can be found [here](@ref ref). - -Stoichiometries can be combined with `()` to define them for multiple reactants. Here, the following (mock) model declares the same reaction twice, both with and without this notation: -```@example dsl_1 -rn6 = @reaction_network begin - k, 2X + 3(Y + 2Z) --> 5(V + W) - k, 2X + 3Y + 6Z --> 5V + 5W -end -nothing # hide -``` - -## [Bundling of similar reactions](@id dsl_description_reaction_bundling) - -### [Bi-directional (or reversible) reactions](@id dsl_description_reaction_bundling_reversible) -As is the case for the following two-state model: -```@example dsl_1 -rn7 = @reaction_network begin - k1, X1 --> X2 - k2, X2 --> X1 -end -``` -it is common that reactions occur in both directions (so-called *bi-directional* reactions). Here, it is possible to bundle the reactions into a single line by using the `<-->` arrow. When we do this, the rate term must include two separate rates (one for each direction, these are enclosed by a `()` and separated by a `,`). I.e. the two-state model can be declared using: -```@example dsl_1 -rn7 = @reaction_network begin - (k1,k2), X1 <--> X2 -end -``` -Here, the first rate (`k1`) denotes the *forward rate* and the second rate (`k2`) the *backwards rate*. - -Catalyst also permits writing pure backwards reactions. These use identical syntax to forward reactions, but with the `<--` arrow: -```@example dsl_1 -rn8 = @reaction_network begin - k, X <-- Y -end -``` -Here, the substrate(s) are on the right-hand side and the product(s) are on the left-hand side. Hence, the above model can be written identically using: -```@example dsl_1 -rn8 = @reaction_network begin - k, Y --> X -end -``` -Generally, using forward reactions is clearer than backwards ones, with the later generally not being used. - -### [Bundling similar reactions on a single line](@id dsl_description_reaction_bundling_similar) -There exist additional situations where models contain similar reactions (e.g. systems where all system components degrade at identical rates). Reactions which share either rates, substrates, or products can be bundled into a single line. Here, the parts which are different for the reactions are written using `(,)` (containing one separate expression for each reaction). E.g., let us consider the following model where species `X` and `Y` both degrade at the rate `d`: -```@example dsl_1 -rn8 = @reaction_network begin - d, X --> 0 - d, Y --> 0 -end -``` -These share both the rates (`d`) and product (`0`), however, the substrates are different (`X` and `Y`). Hence, the reactions can be bundled into a single line using the common rate and product expression while providing separate substrate expressions: -```@example dsl_1 -rn8 = @reaction_network begin - d, (X,Y) --> 0 -end -``` -This declaration of the model is identical to the previous one. Reactions can share any subset of the rate, substrate, and product expression (the cases where they share all, or none, however, do not make sense to use). I.e. if the two reactions also have different degradation rates: -```@example dsl_1 -rn9 = @reaction_network begin - dX, X --> 0 - dY, Y --> 0 -end -``` -This can be represented using: -```@example dsl_1 -rn9 = @reaction_network begin - (dX,dY), (X,Y) --> 0 -end -``` - -It is possible to use bundling for any number of reactions. E.g. in the following model we bundle the conversion of a species $X$ between its various forms (where all reactions use the same rate $k$): -```@example dsl_1 -rn10 = @reaction_network begin - k, (X0,X1,X2,X3) --> (X1,X2,X3,X4) -end -``` - -It is possible to combine bundling with bi-directional reactions. In this case, the rate is first split into the forward and backwards rates. These may then (or may not) indicate several rates. We exemplify this using the two following two (identical) networks, created with and without bundling. -```@example dsl_1 -rn11 = @reaction_network begin - kf, S --> P1 - kf, S --> P2 - kb_1, P1 --> S - kb_2, P2 --> S -end -``` -```@example dsl_1 -rn11 = @reaction_network begin - (kf, (kb_1, kb_2)), S <--> (P1,P2) -end -``` - -Like when we designated stoichiometries, reaction bundling can be applied very generally to create some truly complicated reactions: -```@example dsl_1 -rn12 = @reaction_network begin - ((pX, pY, pZ),d), (0, Y0, Z0) <--> (X, Y, Z1+Z2) -end -``` -However, like for the above model, bundling reactions too zealously can reduce (rather improve) a model's readability. - -## [Non-constant reaction rates](@id dsl_description_nonconstant_rates) -So far we have assumed that all reaction rates are constant (being either a number of a parameter). Non-constant rates that depend on one (or several) species are also possible. More generally, the rate can be any valid expression of parameters and species. - -Let us consider a model with an activator (`A`, which degraded at a constant rate) and a protein (`P`). The production rate of `P` depends both on `A` and a parameter (`kP`). We model this through: -```@example dsl_1 -rn_13 = @reaction_network begin - d, A --> 0 - kP*A, 0 --> P -end -``` -Here, `P`'s production rate will be reduced as `A` decays. We can [print the ODE this model produces with `Latexify`](@ref ref): -```@example dsl_1 -using Latexify -latexify(rn_13; form=:ode) -``` - -In this case, we can generate an equivalent model by instead adding `A` as both a substrate and a product to `P`'s production reaction: -```@example dsl_1 -rn_13_alt = @reaction_network begin - d, A --> 0 - kp, A --> A + P -end -``` -We can confirm that this generates the same ODE: -```@example dsl_1 -latexify(rn_13_alt; form=:ode) -``` -Here, while these models will generate identical ODE, SDE, and jump simulations, the chemical reaction network models themselves are not equivalent. Generally, as pointed out in the two notes below, using the second form is preferable. -!!! warn - While `rn_13` and `rn_13_alt` will generate equivalent simulations, for [jump simulations](@ref ref), the first model will have [reduced performance](@ref ref) (which generally are more performant when rates are constant). - -!!! danger - Catalyst automatically infers whether quantities appearing in the DSL are species or parameters (as described [here](@ref ref)). Generally, anything that does not appear as a reactant is inferred to be a parameter. This means that if you want to model a reaction activated by a species (e.g. `kp*A, 0 --> P`), but that species does not occur as a reactant, it will be interpreted as a parameter. This can be handled by [manually declaring the system species](@ref ref). A full example of how to do this for this example can be found [here](@ref ref). - -Above we used a simple example where the rate was the product of a species and a parameter. However, any valid Julia expression of parameters, species, and values can be used. E.g the following is a valid model: -```@example dsl_1 -rn_14 = @reaction_network begin - 2.0 + X^2, 0 --> X + Y - k1 + k2^k3, X --> ∅ - pi * X/(sqrt(2) + Y), Y → ∅ -end -``` - -### [Using functions in rates](@id dsl_description_nonconstant_rates_functions) -It is possible for the rate to contain Julia functions. These can either be functions from Julia's standard library: -```@example dsl_1 -rn_16 = @reaction_network begin - d, A --> 0 - kp*sqrt(A), 0 --> P -end -``` -or ones defined by the user: -```@example dsl_1 -custom_function(p1, p2, X) = (p1 + X) / (p2 + X) -rn_17 = @reaction_network begin - d, A --> 0 - custom_function(k1,k2,E), 0 --> P -end -``` - -### [Pre-defined functions](@id dsl_description_nonconstant_rates_available_functions) -Two functions frequently used within systems biology are the [*Michaelis-Menten*](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) and [*Hill*](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)) functions. These are pre-defined in Catalyst and can be called using `mm(X,v,K)` and `hill(X,v,K,n)`. E.g. a self-activation loop where `X` activates its own production through a Hill function can be created using: -```@example dsl_1 -custom_function(p1,p2,X) = (p1+E)/(p2+E) -rn_18 = @reaction_network begin - hill(X,v,K,n), 0 --> P - d, X --> 0 -end -``` - -Catalyst comes with the following predefined functions: -- The Michaelis-Menten function: $mm(X,v,K) = v * X/(X + K)$. -- The repressive Michaelis-Menten function: $mmr(X,v,K) = v * K/(X + K)$. -- The Hill function: $hill(X,v,K,n) = v * (X^n)/(X^n + K^n)$. -- The repressive Hill function: $hillr(X,v,K,n) = v * (K^n)/(X^n + K^n)$. -- The activating/repressive Hill function: $hillar(X,Y,v,K,n) = v * (X^n)/(X^n + Y^n + K^n)$. - -### [Time-dependant rates](@id dsl_description_nonconstant_rates_time) -Previously we have assumed that the rates are independent of the [time variable, $t$](@ref ref). However, time-dependent reactions are also possible. Here, simply use `t` to represent the time variable. E.g., to create a production/degradation model where the production rate decays as time progresses, we can use: -```@example dsl_1 -rn_14 = @reaction_network begin - kp/(1 + t), 0 --> P - d, P --> 0 -end -``` - -Like previously, `t` can be part of any valid expression. E.g. to create a reaction with a cyclic rate (e.g. to represent a [circadian system](https://en.wikipedia.org/wiki/Circadian_rhythm)) we can use: -```@example dsl_1 -rn_15 = @reaction_network begin - A*(sin(2π*f*t - ϕ)+1)/2, 0 --> P - d, P --> 0 -end -``` - -## [Non-standard stoichiometries](@id dsl_description_stoichiometries) - -### [Non-integer stoichiometries](@id dsl_description_stoichiometries_decimal) -Previously all stoichiometric constants have been integer numbers, however, decimal numbers are also permitted. Here we create a birth-death model where each production reaction produces 1.5 units of `X`: -```@example dsl_1 -rn_16 = @reaction_network begin - p, 0 --> 1.5X - d, X --> 0 -end -``` -It is also possible to have non-integer stoichiometric coefficients for substrates. However, in this case the [`combinatoric_ratelaw = false`](@ref ref) option must be used. We note that non-integer stoichiometric coefficients does not make sense in most fields, however, this features is available for use for models where it does make sense. - -### [Parametric stoichiometries](@id dsl_description_stoichiometries_parameters) -It is possible for stoichiometric coefficients to be parameters. E.g. here we create a generic polymerisation system where `n` copies of `X` binds to form `Xn`: -```@example dsl_1 -rn_17 = @reaction_network begin - (kB,kD), n*X <--> Xn -end -``` -Now we can designate the value of `n` through a parameter when we e.g. create an `ODEProblem`: -```@example dsl_1 -u0 = [:X => 5.0, :Xn => 1.0] -ps = [:kB => 1.0, :kD => 0.1, :n => 4] -oprob = ODEProblem(rn_17, u0, (0.0, 1.0), ps) -nothing # hide -``` - -## [Using special symbols](@id dsl_description_symbols) -Julia permits any Unicode characters to be used in variable names, thus Catalyst can use these as well. Below we describe some cases where this can be useful. No functionality is, however, tied to this. - -### [Using ∅ in degradation/production reactions](@id dsl_description_symbols_empty_set) -Previously, we described how `0` could be used to [create degradation or production reactions](@ref dsl_description_reactions_degradation_and_production). Catalyst permits the user to instead use the `∅` symbol. E.g. the production/degradation system can alternatively be written as: -```@example dsl_1 -rn4 = @reaction_network begin - p, ∅ --> X - d, X --> ∅ -end -``` - -### [Using special arrow symbols](@id dsl_description_symbols_arrows) -Catalyst uses `-->`, `<-->`, and `<--` to denote forward, bi-directional, and backwards reactions, respectively. Several unicode representations of these arrows are available. Here, -- `>`, `→`, `↣`, `↦`, `⇾`, `⟶`, `⟼`, `⥟`, `⥟`, `⇀`, and `⇁` can be used to represent forward reactions. -- `↔`, `⟷`, `⇄`, `⇆`, `⇌`, `⇋`, , and `⇔` can be used to represent bi-directional reactions. -- `<`, `←`, `↢`, `↤`, `⇽`, `⟵`, `⟻`, `⥚`, `⥞`, `↼`, , and `↽` can be used to represent backwards reactions. - -E.g. the production/degradation system can alternatively be written as: -```@example dsl_1 -rn4 = @reaction_network begin - p, ∅ → X - d, X → ∅ -end -``` - -### [Using special symbols to denote species or parameters](@id dsl_description_symbols_special) -A range of possible characters are available which can be incorporated into species and parameter names. This includes, but is not limited to: -- Greek letters (e.g `α`, `σ`, `τ`, and `Ω`). -- Superscript and subscript characters (to create e.g. `k₁`, `k₂`, `Xₐ`, and `Xᴾ`). -- Non-latin, non-greek, letters (e.g. `ä`, `Д`, `س`, and `א`). -- Other symbols (e.g. `£`, `ℂ`, `▲`, and `♠`). - -An example of how this can be used to create a neat-looking model can be found in [Schwall et al. (2021)](https://www.embopress.org/doi/full/10.15252/msb.20209832) where it was used to model a sigma factor V circuit in the bacteria *Bacillus subtilis*: -```@example dsl_1 -σᵛ_model = @reaction_network begin - v₀ + hill(σᵛ,v,K,n), ∅ → σᵛ + A - kdeg, (σᵛ, A, Aσᵛ) → ∅ - (kB,kD), A + σᵛ ↔ Aσᵛ - L, Aσᵛ → σᵛ -end -nothing # hide -``` - -This functionality can also be used to create less serious models: -rn_13 = @reaction_network begin - 🍦, 😢 --> 😃 -end - -It should be noted that the following symbols are *not permitted* to be used as species or parameter names: -- `pi` and `π` (used in Julia to denote [`3.1415926535897...`](https://en.wikipedia.org/wiki/Pi)). -- `ℯ` (used in Julia to denote [Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)). -- `t` (used to denote the [time variable](@ref dsl_description_nonconstant_rates_time)). -- `∅` ([used for production/degradation reactions](@ref dsl_description_symbols_empty_set)). -- `im` (used in Julia to represent [complex numbers](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/#Complex-Numbers)). -- `nothing` (used in Julia to denote [nothing](https://docs.julialang.org/en/v1/base/constants/#Core.nothing)). -- `Γ` (used by Catalyst to represent [conserved quantities](@ref ref)). - diff --git a/docs/src/02_model_creation/02_dsl_advanced.md b/docs/src/02_model_creation/02_dsl_advanced.md deleted file mode 100644 index 7afb7a6427..0000000000 --- a/docs/src/02_model_creation/02_dsl_advanced.md +++ /dev/null @@ -1,626 +0,0 @@ -# [The Catalyst DSL - Advanced Features and Options](@id dsl_advanced_options) -Within the Catalyst DSL, each line can represent either *a reaction* or *an option*. The [previous DSL tutorial](@ref dsl_description) described how to create reactions. This one will focus on options. These are typically used to supplant a model with additional information. Examples include the declaration of initial condition/parameter default values, or the creation observables or events. - -All options designations begins with declaration starting with `@`, followed by its input. E.g. the `@observables` options allows for the generation of observables. Each option can only be used once within each use of `@reaction_network`. A full list of options can be found [here](@ref ref), with most (but not all) being described in more detail below. This tutorial will also describe some additional advanced DSL features that does not directly include using an option. - -As a first step, we import Catalyst (which is required to run the tutorial): -```@example dsl_advanced_1 -using Catalyst -``` - -## [Explicit specification of network species and parameters](@id dsl_advanced_options_declaring_species_and_parameters) -[Previously](@ref ref), we mentioned that the DSL automatically determines which symbols corresponds to species and which to parameters. This is done by designating everything that appear as either a substrate or a product as a species, and all remaining quantities as parameters (i.e. those only appearing within rates or [stoichiometric constants](@ref ref)). Sometimes, one might want to manually override this default behaviour for a given symbol. I.e. consider the following model, where the conversion of a protein `P` from its inactive form (`Pᵢ`) to its active form (`Pₐ`) is catalysed by an enzyme (`E`). Using the most natural description: -```@example dsl_advanced_1 -catalysis_sys = @reaction_network begin - k*E, Pᵢ --> Pₐ -end -``` -`E` (as well as `k`) will be considered a parameter, something we can confirm directly: -```@example dsl_advanced_1 -parameters(catalysis_sys) -``` -If we want `E` to be considered a species, we can designate this using the `@species` option: -```@example dsl_advanced_1 -catalysis_sys = @reaction_network begin - @species E(t) - k*E, Pᵢ --> Pₐ -end -parameters(catalysis_sys) -``` -!!! note - When declaring species using the `@species` option, the species symbol must be followed by `(t)`. The reason is that species are time-dependent variables, and this time-dependency must be explicitly specified ([designation of non-`t` dependant species is also possible](@ref ref)). - -Similarly, the `@parameters` option can be used to explicitly designate something as a parameter: -```@example dsl_advanced_1 -catalysis_sys = @reaction_network begin - @parameters k - k*E, Pᵢ --> Pₐ -end -``` -Here, while `k` is explicitly defined as a parameter, no information is provided about `E`. Hence, the default case will be used (setting `E` to a parameter). The `@species` and `@parameter` options can be used simultaneously (although a quantity cannot be declared *both* as a species and a parameter). They may be followed by a full list of all species/parameters, or just a subset. - -While designating something which would default to a parameter as a species is straightforward, the reverse (creating a parameter which occur as a substrate or product) is more involved. This is, however, possible, and described [here](@ref dsl_advanced_options_constant_species). - -Rather than listing all species/parameters on a single line after the options, a `begin ... end` block can be used (listing one species/parameter on each line). E.g. in the following example we use this notation to explicitly designate all species and parameters of the system: -```@example dsl_advanced_1 -catalysis_sys = @reaction_network begin - @species begin - E(t) - Pᵢ(t) - Pₐ(t) - end - @parameters begin - k - end - k*E, Pᵢ --> Pₐ -end -``` - -A side-effect of using the `@species` and `@parameter` options is that they specify *the order in which the species and parameters are stored*. I.e. lets check the order of the parameters in the parameters in the following dimerisation model: -```@example dsl_advanced_1 -dimerisation = @reaction_network begin - (p,d), 0 <--> X - (kB,kD), 2X <--> X2 -end -parameters(dimerisation) -``` -The default order is typically equal to the order with which the parameters (or species) are encountered in the DSL (this is, however, not guaranteed). If we specify the parameters using `@parameters`, the order used within the option is used instead: -```@example dsl_advanced_1 -dimerisation = @reaction_network begin - @parameters kB kD p d - (p,d), 0 <--> X - (kB,kD), 2X <--> X2 -end -parameters(dimerisation) -``` -!!! danger - Generally, Catalyst and the SciML ecosystem *does not* guarantee that parameter and species order is preserved throughout various operations on the model. Writing programs that depend on these orders is *strongly discouraged*. There are, however, some legacy packages which still depends on order (one example is provided [here](@ref ref)). In these situations, this might be useful. However, in these cases, it is recommended that the user is extra wary, and also checks the order manually. - -!!! note - The syntax of the `@species` and `@parameters` options is identical to that used by the `@species` and `@parameters` macros [used in programmatic modelling in Catalyst](@ref programmatic_CRN_construction) (for e.g. designating metadata or initial conditions). Hence, if one have learnt how to specify species/parameters using either approach, that knowledge can be transferred to the other one. - -Generally, there are four main reasons for specifying species/parameters using the `@species` and `@parameters` option: -1. To designate a quantity, that would otherwise have defaulted to a parameter, as a species. -2. To designate default values for parameters/species initial conditions (described [here](@ref dsl_advanced_options_default_vals)). -3. To designate metadata for species/parameters (described [here](@ref dsl_advanced_options_species_and_parameters_metadata)). -4. To designate a species or parameters that does not occur in reactions, but are still part of the model (e.g a [parametric initial condition](@ref dsl_advanced_options_parametric_initial_conditions)) - -!!!! warn - Catalyst's DSL automatically infer species and parameters from the input. However, it only does so for *quantities that appear in reactions*. Until now this has not been relevant. However, this tutorial will demonstrate cases where species/parameters that are not part of reactions are used. These *must* be designated using either the `@species` or `@parameters` options (or the `@variables` option, which is described [later](@ref dsl_advanced_options_variables)). - -### [Setting default values for species and parameters](@id dsl_advanced_options_default_vals) -When declaring species/parameters using the `@species` and `@parameters` options, one can also assign them default values (by appending them with `=` followed by the desired default value). E.g here we set `X`'s default initial condition value to $1.0$, and `p` and `d`'s default values to $1.0$ and $0.2$, respectively: -```@example dsl_advanced_defaults -using Catalyst # hide -rn = @reaction_network begin - @species X(t)=1.0 - @parameters p=1.0 d=0.1 - (p,d), 0 <--> X -end -``` -Next, if we simulate the model, we do not need to provide values for species or parameters that have default values. In this case all have default values, so both `u0` and `ps` can be empty vectors: -```@example dsl_advanced_defaults -using OrdinaryDiffEq, Plots -u0 = [] -tspan = (0.0, 10.0) -p = [] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) -plot(sol) -``` -It is still possible to provide values for some (or all) initial conditions/parameters in `u0`/`ps` (in which case these overrides the default values): -```@example dsl_advanced_defaults -u0 = [:X => 4.0] -p = [:d => 0.5] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) -plot(sol) -``` -It is also possible to declare a model with default values for only some initial conditions/parameters: -```@example dsl_advanced_defaults -using Catalyst # hide -rn = @reaction_network begin - @species X(t)=1.0 - (p,d), 0 <--> X -end - -tspan = (0.0, 10.0) -p = [:p => 1.0, :D => 0.2] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) -plot(sol) -``` -API for checking the default values of species and parameters can be found [here](@ref ref). - -### [Setting parametric initial conditions](@id dsl_advanced_options_parametric_initial_conditions) -In the previous section, we designated default values for species' initial conditions and parameters. However, the right-hand side of the designation accepts any valid expression (not only numeric values). While this can be used to set up some advanced default values, the most common use-case is to designate a species's initial condition as a parameter. E.e. in the following example we represent the initial condition of `X` using the parameter `X₀`. -```@example dsl_advanced_defaults -rn = @reaction_network begin - @species X(t)=X₀ - @parameters X₀ - (p,d), 0 <--> X -end -``` -Please note that as the parameter `X₀` does not occur as part of any reactions, Catalyst's DSL cannot infer whether it is a species or a parameter. This must hence be explicitly declared. We can now simulate our model while providing `X`'s value through the `X₀` parameter: -```@example dsl_advanced_defaults -u0 = [] -p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) -plot(sol) -``` -It is still possible to designate $X$'s value in `u0`, in which case this overrides the default value. -```@example dsl_advanced_defaults -u0 = [:X => 0.5] -p = [:X₀ => 1.0, :p => 1.0, :d => 0.5] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) -plot(sol) -``` -Please note that `X₀` is still a parameter of the system, and as such its value must still be designated to simulate the model (even if it is not actually used). - -### [Designating metadata for species and parameters](@id dsl_advanced_options_species_and_parameters_metadata) -Catalyst permits the user to define *metadata* for species and parameters. This permits the user to assign additional information to these, which can be used for a variety of purposes. Some Catalyst features depend on using metadata (with each such case describing specifically how this is done). Here we will introduce how to set metadata, and describe some common metadata types. - -Whenever a species/parameter is declared using the `@species`/`@parameters` options, it can be followed by a `[]` within which the metadata is given. Each metadata entry consists of the metadata's name, followed by a `=`, followed by its value. E.g. the `description` metadata allows you to attach a [`String`](https://docs.julialang.org/en/v1/base/strings/) to a species/parameter. Here we create a simple model where we add descriptions to all species and parameters. -```@example dsl_advanced_metadata -using Catalyst # hide -two_state_system = @reaction_network begin - @species Xi(t) [description="The X's inactive form"] Xa(t) [description="The X's active form"] - @parameters kA [description="X's activation rate"] kD [description="X's deactivation rate"] - (ka,kD), Xi <--> Xa -end -``` -A metadata can be given to only a subset of a system's species/parameters, and a quantity can be given several metadata entries. To give several metadata, separate each by a `,`. Here we only provide a description for `kA`, for which we also provide a [bounds metadata](@ref https://docs.sciml.ai/ModelingToolkit/dev/basics/Variable_metadata/#Bounds), -```@example dsl_advanced_metadata -two_state_system = @reaction_network begin - @parameters kA [description="X's activation rate", bound=(0.01,10.0)] - (ka,kD), Xi <--> Xa -end -``` - -It is possible to add both default values and metadata to a parameter/species. In this case, first provide the default value, next the metadata. I.e. to in the above example set $kA$'s default value to $1.0$ we use -```@example dsl_advanced_metadata -two_state_system = @reaction_network begin - @parameters kA=1.0 [description="X's activation rate", bound=(0.01,10.0)] - (ka,kD), Xi <--> Xa -end -``` - -When designating metadata for species/parameters in `begin ... end` blocks the syntax changes slight. Here, a `,` must be inserted before the metadata (but after any potential default value). I.e. a version of the previous example can be written as -```@example dsl_advanced_metadata -two_state_system = @reaction_network begin - @parameters begin - kA, [description="X's activation rate", bound=(0.01,10.0)] - kD = 1.0, [description="X's deactivation rate"] - end - (ka,kD), Xi <--> Xa -end -``` - -Each metadata has its own getter functions. E.g. we can get the description of the parameter `kA` using `getdescription` (here we use [system indexing](@ref ref) to access the parameter): -```@example dsl_advanced_metadata -getdescription(two_state_system.kA) -``` - -It is not possible for the user to directly designate their own metadata. These have to first be added to Catalyst. Doing so is somewhat involved, and described in detail [here](@ref ref). A full list of metadata that can be used for species and/or parameters can be found [here](@ref ref). - -### [Designating constant-valued/fixed species parameters](@id dsl_advanced_options_constant_species) - -Catalyst enables the designation of parameters as `constantspecies`. These parameters can be used as species in reactions, however, their values are not changed by the reaction and remain constant throughout the simulation (unless changed by e.g. the [occurrence of an event]@ref ref). Practically, this is done by setting the parameter's `isconstantspecies` metadata to `true`. Here, we create a simple reaction where the species `X` is converted to `Xᴾ` at rate `k`. By designating `X` as a constant species parameter, we ensure that its quantity is unchanged by the occurrence of the reaction. -```@example dsl_advanced_constant_species -using Catalyst # hide -rn = @reaction_network begin - @parameters X [isconstantspecies=true] - k, X --> Xᴾ -end -``` -We can confirm that $Xᴾ$ is the only species of the system: -```@example dsl_advanced_constant_species -species(rn) -``` -Here, the produced model is actually identical to if $X$ had simply been a parameter in the reaction's rate: -```@example dsl_advanced_constant_species -rn = @reaction_network begin - k*X, 0 --> Xᴾ -end -``` - -A common use-case for constant species are when modelling systems where some species are present in such surplus that their amounts the reactions' effect on it is negligible. A system which is commonly modelled this way is the [Brusselator](https://en.wikipedia.org/wiki/Brusselator). - -### [Specifying non-species variables](@id dsl_advanced_options_variables) ---- MOVE THIS TO HYBRID SECTION --- -Chemical reaction network (CRN) models (which Catalyst creates) described how *species* are affected by the occurrence of *reaction events*. When they are converted to ODEs, the species become unknowns of the system. However, Catalyst permits the creation of [hybrid CRN models](@ref ref). These describe phenomenons which can only partially be modelled using CRNs. An example may be a bacterium. Here, we can use a CRN to model some internal system (e.g. controlling its growth rate). However, we might also want to model the bacterium's volume. Here, the volume cannot be considered a species (as it cannot plausibly be a reaction reactant). Instead, we should model it as a normal variable. Here, Catalyst provides the `@variables` option for adding non-species variables to the system. E.g. to create a model where a single growth factor ($G$) is produced and degraded, and where we also have a single volume variables ($V$) we can use: -```@example dsl_advanced_variables -using Catalyst # hide -rn = @reaction_network begin - @variables V(t) - (p,d), 0 <--> G -end -``` -Note that $V$ (like species) is time-dependant, and (like species) must be declared as such when the `@variables` option is used. We can now simulate our model (remembering to provide a value for $V$ as well as $G$): -```@example dsl_advanced_variables -using OrdinaryDiffEq, Plots -u0 = [:G => 0.1, :V => 1.0] -tspan = (0.0, 10.0) -p = [:p => 1.0, :d => 0.5] -oprob = ODEProblem(rn, u0, tspan, p) -sol = solve(oprob, Tsit5()) -plot(sol) -``` -Here, we have not actually described how $V$ interacting with our model, or how its value may change. Primarily variables are declared as part of hybrid CRN/equation modelling, which is described in more detail [here](@ref ref). - -You can set metadata and default initial condition values for variables using the same syntax as used for parameters and species. - -You can use the `variables` and `species` functions to retrieve a model's variables and species, respectively. The `unknowns` function retrieves both. - -## [Setting reaction metadata](@id dsl_advanced_options_reaction_metadata) ---- DISCUSS WHAT TO DO WITH THIS SECTION --- -Reactions can also have metadata. This is described in detail [here](@ref ref). - -## [Naming reaction networks](@id dsl_advanced_options_naming) -Each reaction network model has a name. It can be accessed using the `nameof` function. By default, some generic name is used: -```@example dsl_advanced_names -using Catalyst # hide -rn = @reaction_network begin - (p,d), 0 <--> X -end -nameof(rn) -``` -A specific name can be given as an argument between the `@reaction_network` and the `begin`. E.g. to name a network `my_network` we can use: -```@example dsl_advanced_names -rn = @reaction_network my_network begin - (p,d), 0 <--> X -end -nameof(rn) -``` - -A consequence of generic names being used by default is that networks, even if seemingly identical, by default are not. E.g. -```@example dsl_advanced_names -rn1 = @reaction_network begin - (p,d), 0 <--> X -end -rn2 = @reaction_network begin - (p,d), 0 <--> X -end -rn1 == rn2 -``` -The reason can be confirmed by checking that their respective (randomly generated) names are different: -```@example dsl_advanced_names -nameof(rn1) == nameof(rn2) -``` -By designating the networks to have the same name, however, identity is achieved. -```@example dsl_advanced_names -rn1 = @reaction_network my_network begin - (p,d), 0 <--> X -end -rn2 = @reaction_network my_network begin - (p,d), 0 <--> X -end -rn1 == rn2 -``` - -Setting model names is primarily useful for [hierarchical modelling](@ref ref), where network names are appended to the display name of subnetworks' species and parameters. - -## [Creating observables](@id dsl_advanced_options_observables) -Sometimes one might want to use observable variables. These are variables with values that can be computed directly from a system's state (rather than having their values implicitly given by reactions or equations). Observables can be designated using the `@observables` option. Here, the `@observables` option is followed by a `begin ... end` block with one line for each observable. Each line first gives the observable, followed by a `~` (*not* a `=`!), followed by an expression describing how to compute it. - -Let us consider a model where two species (`X` and `Y`) can bind to form a complex (`XY`, which also can dissociate back into `X` and `Y`). If we wish to create a representation for the total amount of `X` and `Y` in the system, we can do this by creating observables `Xtot` and `Ytot`: -```@example dsl_advanced_observables -using Catalyst # hide -rn = @reaction_network begin - @observables begin - Xtot ~ X + XY - Ytot ~ Y + XY - end - (kB,kD), X + Y <--> XY -end -``` -We can now simulate our model using normal syntax (initial condition values for observables should not, and can not, be provided): -```@example dsl_advanced_observables -using OrdinaryDiffEq -u0 = [:X => 1.0, :Y => 2.0, :XY => 0.0] -tspan = (0.0, 10.0) -ps = [:kB => 1.0, :kD => 1.5] -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(oprob, Tsit5()) -nothing # hide -``` - -Next, we can use [symbolic indexing](@ref simulation_structure_interfacing) of our solution object, but with the observable as input. E.g. we can use -```@example dsl_advanced_observables -sol[:Xtot] -``` -to get a vector with `Xtot`'s value throughout the simulation. We can also use -```@example dsl_advanced_observables -using Plots -plot(sol; idxs = [:Xtot, :Ytot]) -``` -to plot the observables (rather than the species). - -Observables can be defined using complicated expression containing species, parameters, and [variables](@ref ref) (but not other observables). In the following example (which uses a [parametric stoichiometry](@ref ref)) `X` polymerises to form a complex `Xn` containing `n` copies of `X`. Here, we create an observable describing the total number of `X` molecules in the system: -```@example dsl_advanced_observables -rn = @reaction_network begin - @observables Xtot ~ X + n*Xn - (kB,kD), n*X <--> Xn -end -nothing # hide -``` -!!! - If only a single observable is declared, the `begin .. end` block is not required and the observable can be declared directly after the `@observables` option. - -[Metadata](@ref dsl_advanced_options_species_and_parameters_metadata) can be supplied to an observable directly after the its declaration (but before its formula). If so, the metadata must be separated from the observable with a `,`, and the observable plus the metadata encapsulated by `()`. E.g. to add a [description metadata](@ref dsl_advanced_options_species_and_parameters_metadata) to our observable we can use -```@example dsl_advanced_observables -rn = @reaction_network begin - @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*Xn - (kB,kD), n*X <--> Xn -end -nothing # hide -``` - -Observables are by default considered [variables](@ref ref) (not species). To designate them as a species, they can be pre-declared using the `@species` option. I.e. Here `Xtot` becomes a species: -```@example dsl_advanced_observables -rn = @reaction_network begin - @species Xtot(t) - @observables (Xtot, [description="The total amount of X in the system."]) ~ X + n*XnXY - (kB,kD), n*X <--> Xn -end -nothing # hide -``` - -Some final notes regarding observables: -- The left-hand side of the observable declaration must contain a single symbol only (with the exception of metadata, which can also be supplied). -- All quantities appearing on the right-hand side must be declared elsewhere within the `@reaction_network` call (either by being part of a reaction, or through the `@species`, `@parameters`, or `@variables` options). -- Observables may not depend on other observables. -- Observables have their [dependent variable(s)](@ref ref) automatically assigned as the union of the dependent variables of the species and variables on which it depends. - -## [Creating events](@id dsl_advanced_options_events) ---- MOVE TO SEPARATE DOC PAGE --- -Sometimes one wishes to model events, describing things that can happen to it during a simulation. - - A chemical system where an amount of some species is added at a time point after the simulation's initiation. - - A simulation of a circadian rhythm, where light is turned on/off every 12 hours. - - A cell divides when some size variable reaches a certain threshold, halving the amount of each species in the system. - -Events are divided into *continuous* and *discrete* events, and these can be added directly to a system using the `continuous_events` and `discrete_events` options. Events can also be modelled through *callbacks*. These are different in that they are supplied in the simulation step (rather than on system creation), and generally provide more flexibility in how they may affect the system. Callbacks are described on a separate [page](@ref advanced_simulations_callbacks). - -The notation described below for creating continuous and discrete events is the same which is used in [ModelingToolkit to create events](https://docs.sciml.ai/ModelingToolkit/stable/basics/Events/), and which is used for [events for programmatic model creation](@ref ref). - -### [Continuous vs discrete events](@id dsl_advanced_options_events_continuous_vs_discrete) -Both continuous and discrete events combine some condition (for triggering the event) with some affect (describing their effects on the system). They differ in the following ways: -- They use slightly different notation. -- Discrete events' conditions are checked at *the end of* each simulation time step. For continuous events, the simulation instead finds the *exact time point* when the event is triggered at. -- Continuous events cannot be supplied to jump simulations. - -### [Continuous events](@id dsl_advanced_options_events_continuous) -Let us consider a simple system where species `X` degraded at a constant rate `d`. Next, we wish to add an event which adds `2.0` units of `X` whenever `X` reaches a critical threshold `1.0`. This can be done in the following manner: -```@example dsl_advanced_events -using Catalyst # hide -rn = @reaction_network begin - @continuous_events begin - X ~ 1.0 => [X ~ X + 2.0] - end - d, X --> 0 -end -nothing # hide -``` -Here, the `@continuous_events` option is followed by a `begin ... end` block. Next, each line corresponds to a separate event. Each event is created in the following manner: -- It combines a *condition* (denoting when the event will happen) with one (or several) *affects* (denoting what the event does to the system when it happens). -- The condition (left) and affect (right) are separated by a `=>`. -- The condition takes the form of an [equation](). Here, the event is triggered whenever the equation's two sides (separated by a `~`) are equal. -- The affect(s) are enclosed within `[]`. If there are multiple affects, these are separated by `,` (the example above contains a single affect). -- Each affect is a single equation that describes how a parameter, species, or [variable](@ref dsl_advanced_options_variables) is updated when the event is triggered. -- Each affect's equation's left-hand side must contain only the parameter/species/variable whose value should be updated. -- Each affect's equation's right-hand side is an expression describing its updated value. - -We can simulate the model we declared, just like any other model: -```@example dsl_advanced_events -using OrdinaryDiffEq, Plots -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -Inspecting the solution, we can confirm that whenever `X` reaches a value of `1.0`, `2.0` units of `X` is added to the system. - -In our example, we can also denote the critical quantities using parameters: -```@example dsl_advanced_events -rn = @reaction_network begin - @parameters X_thres X_add - @continuous_events begin - X ~ X_thres => [X ~ X + X_add] - end - d, X --> 0 -end -nothing # hide -``` -Here, since `X_thres` and `X_add` do not appear in any reactions, Catalyst cannot determine whether they are parameters or species. hence, they must be [explicitly designated as parameters by using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters). Next, these can be designated as any value, and supplied to the `ODEProblem`: -```@example dsl_advanced_events -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0, :X_thres => 0.5, :X_add => 3.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` - -As previously noted, each continuous event can have multiple affects. The following system has two components (`X` and `Y`, one being produced and one being degraded). When their concentrations are equal, a continuous events reduce the concentration of `X` while increasing the concentration of `Y`: -```@example dsl_advanced_events -rn = @reaction_network begin - @continuous_events begin - X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] - end - p, 0 --> X - d, Y --> 0 -end - -u0 = [:X => 1.0, :Y => 3.0] -tspan = (0.0, 10.0) -ps = [:p => 1.0, :d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` - -!!!warn - A single event (continuous or discrete) can update the value of either (one or several) species (and variables), or of (one or several) parameters. It is not possible for an event to update the values of both species/variables and parameters. - -In the above examples we have modelled a system with a single event. In these cases, the `begin end` block is not required, and the event can be provided on the same line as the `@continuous_events` option: -```@example dsl_advanced_events -rn = @reaction_network begin - @continuous_events X ~ Y => [X ~ X - 1.0, Y ~ Y + 1.0] - p, 0 --> X - d, Y --> 0 -end -nothing # hide -``` - -### [Discrete events](@id dsl_advanced_options_events_discrete) -Just like [continuous events](dsl_advanced_options_events_continuous), discrete events combine a condition with one or more affect statements. Here, discrete events' affects are created identically to those for continuous events. Discrete events' conditions are different. There exist 3 different types of discrete events, each with a different type of condition. All three types are created using the `@discrete_events` option, and a single system can contain a mix of all types. The three types are: -- Preset-time discrete events. -- Periodic discrete events. -- Conditional discrete events. - -#### [Preset-time discrete events](@id dsl_advanced_options_events_discrete_presettime) -*Present-time events* are events that happen at specific time points. Here, the condition is a vector with all the time points at which an event is triggered. E.g. here we create a production/degradation loop, where `2.0` units of `X` is added at time points `3.0` and `7.0` -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - [3.0, 7.0] => [X ~ X + 2.0] - end - (p,d), 0 <--> X -end - -u0 = [:X => 0.1, :Y => 3.0] -tspan = (0.0, 10.0) -ps = [:p => 1.0, :d => 0.5] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` - -The preset time points can also be parameters (in which case, they have to be [designated as such using the `@parameters` option](@ref dsl_advanced_options_declaring_species_and_parameters)): -```@example dsl_advanced_events -rn = @reaction_network begin - @parameters t1 t2 - @discrete_events begin - [t1, t2] => [X ~ X + 2.0] - end - (p,d), 0 <--> X -end -nothing -``` - -#### [Periodic discrete events](@id dsl_advanced_options_events_discrete_periodic) -When a discrete event's condition is a vector, a preset-time event is created. If it instead is a single value, a *periodic event* is created. These occur repeatedly throughout a simulation, with its period set by the affect term. E.g. here we create a system where `0.5` units of `X` is added every `1.0` time units. -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - 1.0 => [X ~ X + 0.5] - end - d, X --> 0 -end - -u0 = [:X => 1.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -Like for preset-time events, periodic events' affects may contain parameters. - -#### [Conditional discrete events](@id dsl_advanced_options_events_discrete_conditional) -Finally, discrete events' condition may be a boolean expression (consisting of parameters, species, variables, and the time variable). Let's say that we want to create an event which, if the concentration of `X` is below a threshold `1.0`, adds `1.0` units of `X` to the system, then we can use the following discrete event: -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - X < 1.0 => [X ~ X + 2.0] - end - d, X --> 0 -end -``` -If we simulate the system using the same conditions as for our [similar, continuous, example](@ref dsl_advanced_options_events_continuous) we get the same result: -```@example dsl_advanced_events -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -So, how is modelling this event as a discrete or continuous event different? There are four differences: -1) For continuous events, the simulation method finds the exact time point when the condition triggers. Discrete events are triggered at the first time step when the condition holds. -2) This discrete event will be triggered whenever `X < 1.0` holds, not just when the concentration of `X` passes the threshold. E.g. it will be triggered if the initial concentration of `X` is less than `1.0`. -3) Only the discrete event event can be used with jump simulations. -4) The discrete event can be used to create more advanced conditions. - -E.g. using (4), we can modify our system so that the event is only triggered when time is less than `5.0` (after which `X` decays towards `0`): -```@example dsl_advanced_events -rn = @reaction_network begin - @discrete_events begin - (X < 1.0) & (t < 5.0) => [X ~ X + 2.0] - end - d, X --> 0 -end - -u0 = [:X => 2.0] -tspan = (0.0, 10.0) -ps = [:d => 1.0] - -oprob = ODEProblem(rn, u0, tspan, ps) -sol = solve(ODEProblem, Tsit5()) -plot(sol) -``` -!!!note - When we form composite boolean conditions for conditional discrete events, we use `&` to denote the AND operator (not `&&`, as this is currently not supported). - -!!!warn - Generally, discrete events including equality (`==`) will not be triggered. The reason is that the condition is only checked at the end of every time step. Hence, unless special precautions are taken to ensure that the simulator stops when the condition holds, it will unlikely be triggered. - - -## [Specifying non-time independent variables](@id dsl_advanced_options_ivs) - -As [described elsewhere](@ref ref), Catalyst's `ReactionSystem` models depends on a *time independent variable*, and potentially one or more *spatial independent variables*. By default, the independent variable `t` is used. We can declare another independent variable (which is automatically used as the default one) using the `@ivs` option. E.g. to use `τ` instead of `t` we can use -```@example dsl_advanced_ivs -using Catalyst # hide -rn = @reaction_network begin - @ivs τ - (ka,kD), Xi <--> Xa -end -nothing # hide -``` -We can confirm that `Xi` and `Xa` depend on `τ` (and not `t`): -```@example dsl_advanced_ivs -species(rn) -``` - -It is possible to designate several independent variables using `@ivs`. If so, the first one is considered the default (time) independent variable, while the following one(s) are considered spatial independent variable(s). If we want some species to depend on a non-default independent variable, this has to be explicitly declared: -```@example dsl_advanced_ivs -rn = @reaction_network begin - @ivs τ x - @species X(x) Y(x) - (p1,d1), 0 <--> X - (p2,d2), 0 <--> Y -end -species(rn) -``` -It is also possible to have species which depends on several independent variables: -```@example dsl_advanced_ivs -rn = @reaction_network begin - @ivs t x - @species Xi(t,x) Xa(t,x) - (ka,kD), Xi <--> Xa -end -species(rn) -``` - -!!! note - Setting spatial independent variables is primarily intended for modelling of spatial systems on continuous domains. Catalyst's support for this is currently under development. Hence, the utility of specifying spatial independent variables is currently limited. \ No newline at end of file From d64c2f6ea9428e34d89f367ba5365be271869b1a Mon Sep 17 00:00:00 2001 From: Torkel Date: Sat, 25 May 2024 11:30:39 -0400 Subject: [PATCH 23/23] rebase fix --- docs/pages.jl | 2 +- .../examples/basic_CRN_examples.md | 274 ------------------ 2 files changed, 1 insertion(+), 275 deletions(-) delete mode 100644 docs/src/model_creation/examples/basic_CRN_examples.md diff --git a/docs/pages.jl b/docs/pages.jl index 0f31fdcbe2..4d25ecacdf 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -18,7 +18,7 @@ pages = Any[ "model_creation/network_analysis.md", "model_creation/chemistry_related_functionality.md", "Model creation examples" => Any[ - "model_creation/examples/basic_CRN_examples.md", + "model_creation/examples/basic_CRN_library.md", "model_creation/examples/programmatic_generative_linear_pathway.md", "model_creation/examples/hodgkin_huxley_equation.md", "model_creation/examples/smoluchowski_coagulation_equation.md" diff --git a/docs/src/model_creation/examples/basic_CRN_examples.md b/docs/src/model_creation/examples/basic_CRN_examples.md deleted file mode 100644 index 762761cbbc..0000000000 --- a/docs/src/model_creation/examples/basic_CRN_examples.md +++ /dev/null @@ -1,274 +0,0 @@ -# [Library of Basic Chemical Reaction Network Models](@id basic_CRN_library) -Below we will present various simple and established chemical reaction network (CRN) models. Each model is given some brief background, implemented using the `@reaction_network` DSL, and basic simulations are performed. - -## Birth-death process -The birth-death process is one of the simplest possible CRN models. It consists of a single component ($X$) which is both produced and degraded at linear rates: -```@example crn_library_birth_death -using Catalyst -bd_process = @reaction_network begin - (p,d), ∅ <--> X -end -``` -Next we define simulation conditions. Note that the initial condition is integer-valued (required to perform jump simulations). -```@example crn_library_birth_death -u0 = [:X => 1] -tspan = (0.0, 10.0) -ps = [:p => 1, :d => 0.2] -``` -We can now simulate our model using all three interpretations. First we perform a reaction rate equation-based ODE simulation: -```@example crn_library_birth_death -using OrdinaryDiffEq -oprob = ODEProblem(bd_process, u0, tspan, ps) -osol = solve(oprob, Tsit5()) -nothing # hide -``` -Next, a chemical Langevin equation-based SDE simulation: -```@example crn_library_birth_death -using StochasticDiffEq -sprob = SDEProblem(bd_process, u0, tspan, ps) -ssol = solve(sprob, ImplicitEM()) -nothing # hide -``` -Next, a stochastic chemical kinetics-based jump simulation: -```@example crn_library_birth_death -using JumpProcesses -dprob = DiscreteProblem(bd_process, u0, tspan, ps) -jprob = JumpProblem(bd_process, dprob, Direct()) -jsol = solve(jprob, SSAStepper()) -nothing # hide -``` -Finally, we plot the results: -```@example crn_library_birth_death -using Plots -oplt = plot(osol; title = "Reaction rate equation (ODE)") -splt = plot(ssol; title = "Chemical Langevin equation (SDE)") -jplt = plot(jsol; title = "Stochastic chemical kinetics (Jump)") -plot(oplt, splt, jplt; size=(800,700), layout = (3,1)) -``` - -## Michaelis-Menten enzyme kinetics -[Michaelis-Menten enzyme kinetics](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics) is a simple description of an enzyme ($E$) transforming a substrate ($S$) into a product ($P$). Under certain assumptions it can be simplified to a singe function (a Michaelis-Menten function) and used as a reaction rate. Here we instead present the full system model: -```@example crn_library_michaelis_menten -using Catalyst -mm_system = @reaction_network begin - kB, S + E --> SE - kD, SE --> S + E - kP, SE --> P + E -end -``` -Next, we perform ODE, SDE, and jump simulations of the model: -```@example crn_library_michaelis_menten -u0 = [:S => 301, :E => 100, :SE => 0, :P => 0] -tspan = (0., 100.) -ps = [:kB => 0.00166, :kD => 0.0001, :kP => 0.1] - -using OrdinaryDiffEq -oprob = ODEProblem(mm_system, u0, tspan, ps) -osol = solve(oprob, Tsit5()) - -using StochasticDiffEq -sprob = SDEProblem(mm_system, u0, tspan, ps) -ssol = solve(sprob, ImplicitEM()) - -using JumpProcesses -dprob = DiscreteProblem(mm_system, u0, tspan, ps) -jprob = JumpProblem(mm_system, dprob, Direct()) -jsol = solve(jprob, SSAStepper()) - -using Plots -oplt = plot(osol; title = "Reaction rate equation (ODE)") -splt = plot(ssol; title = "Chemical Langevin equation (SDE)") -jplt = plot(jsol; title = "Stochastic chemical kinetics (Jump)") -plot(oplt, splt, jplt; size=(800,700), layout = (3,1)) -``` - -## SIR infection model -The [SIR model](https://en.wikipedia.org/wiki/Compartmental_models_in_epidemiology#The_SIR_model) is the simplest model of the spread of an infectious disease. While the real system is very different from the chemical and cellular processes typically modelled with CRNs, it (and several other epidemiological systems) can be modelled using the same CRN formalism. The SIR model consists of three species: susceptible ($S$), infected ($I$), and removed ($R$) individuals, and two reaction events: infection and recovery. -```@example crn_library_sir -using Catalyst -sir_model = @reaction_network begin - α, S + I --> 2I - β, I --> R -end -``` -First we perform a deterministic ODE simulation: -```@example crn_library_sir -using OrdinaryDiffEq, Plots -u0 = [:S => 99, :I => 1, :R => 0] -tspan = (0.0, 500.0) -ps = [:α => 0.001, :β => 0.01] - -# Solve ODEs. -oprob = ODEProblem(sir_model, u0, tspan, ps) -osol = solve(oprob, Tsit5()) -plot(osol; title = "Reaction rate equation (ODE)") -``` -Next we perform 3 different Jump simulations. Note that for the stochastic model, the occurrence of a outbreak is not certain. Rather, there is a possibility that it fizzles out without a noteworthy peak. -```@example crn_library_sir -using JumpProcesses -dprob = DiscreteProblem(sir_model, u0, tspan, ps) -jprob = JumpProblem(sir_model, dprob, Direct()) - -jsol1 = solve(jprob, SSAStepper()) -jsol2 = solve(jprob, SSAStepper()) -jsol3 = solve(jprob, SSAStepper()) -jsol1 = solve(jprob, SSAStepper(); seed=1) # hide -jsol2 = solve(jprob, SSAStepper(); seed=2) # hide -jsol3 = solve(jprob, SSAStepper(); seed=3) # hide - -jplt1 = plot(jsol1; title = "Outbreak") -jplt2 = plot(jsol2; title = "Outbreak") -jplt3 = plot(jsol3; title = "No outbreak") -plot(jplt1, jplt2, jplt3; size=(800,700), layout = (3,1)) -``` - -## The Wilhelm model -The Wilhelm model was introduced in [*Wilhelm (2009)*](https://bmcsystbiol.biomedcentral.com/articles/10.1186/1752-0509-3-90) as the smallest CRN model (with constant rates) that exhibits bistability. -```@example crn_library_wilhelm -wilhelm_model = @reaction_network begin - k1, Y --> 2X - k2, 2X --> X + Y - k3, X + Y --> Y - k4, X --> 0 -end -``` -We can simulate the model for two different initial conditions, demonstrating the existence of two different stable steady states. -```@example crn_library_wilhelm -using OrdinaryDiffEq, Plots -u0_1 = [:X => 1.5, :Y => 0.5] -u0_2 = [:X => 2.5, :Y => 0.5] -tspan = (0., 10.) -ps = [:k1 => 8.0, :k2 => 2.0, :k3 => 1.0, :k4 => 1.5] - -oprob1 = ODEProblem(wilhelm_model, u0_1, tspan, ps) -oprob2 = ODEProblem(wilhelm_model, u0_2, tspan, ps) -osol1 = solve(oprob1, Tsit5()) -osol2 = solve(oprob2, Tsit5()) -oplt1 = plot(osol1; idxs = :X, label = "X(0) = 1.5") -oplt2 = plot!(osol2; idxs = :X, label = "X(0) = 2.5", yguide = "X", size = (800,700)) -``` - -## Simple self-activation loop -The simplest self-activation loop consist of a single species (here called $X$) which activates its own production. If its production rate is modelled with a hill function with $n>1$, the system may exhibit bistability. -```@example crn_library_self_activation -using Catalyst -sa_loop = @reaction_network begin - v₀ + hill(X,v,K,n), ∅ --> X - d, X --> ∅ -end -``` -A simple example of such a loop is a transcription factor which activates its own gene. Here, $v₀$ represents a basic transcription rate (leakage) in the absence of the transcription factor. - -We simulate the self-activation loop from a single initial condition using both deterministic (ODE) and stochastic (jump) simulations. We note that while the deterministic simulation reaches a single steady state, the stochastic one switches between two different states. -```@example crn_library_self_activation -using OrdinaryDiffEq, Plots -u0 = [:X => 4] -tspan = (0.0, 1000.0) -ps = [:v₀ => 0.1, :v => 2.0, :K => 10.0, :n => 2, :d => 0.1] - -oprob = ODEProblem(sa_loop, u0, tspan, ps) -osol = solve(oprob, Tsit5()) - -dprob = DiscreteProblem(sa_loop, u0, tspan, ps) -jprob = JumpProblem(sa_loop, dprob, Direct()) -jsol = solve(jprob, SSAStepper()) -jsol = solve(jprob, SSAStepper(); seed = 2091) # hide - -plot(osol; label = "Reaction rate equation (ODE)") -plot!(jsol; label = "Stochastic chemical kinetics (Jump)", yguide = "X", size = (800,600)) -``` - -## The Brusselator -The [Brusselator](https://en.wikipedia.org/wiki/Brusselator) is a well known (theoretical) CRN model able to produce oscillations (its name is a portmanteau of "Brussels" and "oscillator"). -```@example crn_library_brusselator -using Catalyst -brusselator = @reaction_network begin - A, ∅ --> X - 1, 2X + Y --> 3X - B, X --> Y - 1, X --> ∅ -end -``` -It is generally known to (for reaction rate equation-based ODE simulations) produce oscillations when $B > 1 + A^2$. However, this results is based on models generated when *combinatorial adjustment of rates is not performed*. Since Catalyst automatically perform these adjustments, and one reaction contain a stoichiometric constant $>1$, the threshold will be different. Here, we trial two different values of $B$. In both cases, $B < 1 + A^2$, however, in he second case the system is able to generate oscillations. -```@example crn_library_brusselator -using OrdinaryDiffEq, Plots -u0 = [:X => 1.0, :Y => 1.0] -tspan = (0., 50.) -ps1 = [:A => 1.0, :B => 1.0] -ps2 = [:A => 1.0, :B => 1.8] - -oprob1 = ODEProblem(brusselator, u0, tspan, ps1) -oprob2 = ODEProblem(brusselator, u0, tspan, ps2) -osol1 = solve(oprob1, Rodas5P()) -osol2 = solve(oprob2, Rodas5P()) -oplt1 = plot(osol1; title = "No Oscillation") -oplt2 = plot(osol2; title = "Oscillation") - -plot(oplt1, oplt2; layout = (1,2), size(800,700)) -``` - -## The repressilator -The repressilator was introduced in [*Elowitz & Leibler (2000)*](https://www.nature.com/articles/35002125) as a simple system that is able to generate oscillations (most notably, they demonstrated this both in a model and in a synthetic in vivo implementation in *Escherichia col*). It consists of three genes, repressing each other in a cycle. Here, we will implement it using three species ($X$, $Y$, and $Z$) which production rates are (repressing) [Hill functions](https://en.wikipedia.org/wiki/Hill_equation_(biochemistry)). -```@example crn_library_brusselator -using Catalyst -repressilator = @reaction_network begin - hillr(Z,v,K,n), ∅ --> X - hillr(X,v,K,n), ∅ --> Y - hillr(Y,v,K,n), ∅ --> Z - d, (X, Y, Z) --> ∅ -end -``` -Whether it oscillates or not depends on its parameter values. Here, we will perform deterministic (ODE) simulations for two different values of $K$, showing that it oscillates for one value and not the other one. Next, we will perform stochastic (SDE) simulations for both $K$ values, showing that the stochastic model is able to sustain oscillations in both cases. This is an example of the phenomena of *noise-induced oscillation*. -```@example crn_library_brusselator -using OrdinaryDiffEq, StochasticDiffEq, Plots -u0 = [:X => 50.0, :Y => 15.0, :Z => 15.0] -tspan = (0., 200.) -ps1 = [:v => 10.0, :K => 20.0, :n => 3, :d => 0.1] -ps2 = [:v => 10.0, :K => 50.0, :n => 3, :d => 0.1] - -oprob1 = ODEProblem(repressilator, u0, tspan, ps1) -oprob2 = ODEProblem(repressilator, u0, tspan, ps2) -osol1 = solve(oprob1, Tsit5()) -osol2 = solve(oprob2, Tsit5()) -oplt1 = plot(osol1; title = "Oscillation (ODE, K = 20)") -oplt2 = plot(osol2; title = "No oscillation (ODE, K = 50)") - -sprob1 = SDEProblem(repressilator, u0, tspan, ps1) -sprob2 = SDEProblem(repressilator, u0, tspan, ps2) -ssol1 = solve(sprob1, ImplicitEM()) -ssol2 = solve(sprob2, ImplicitEM()) -ssol1 = solve(sprob1, ImplicitEM(); seed = 1) # hide -ssol2 = solve(sprob2, ImplicitEM(); seed = 100) # hide -splt1 = plot(ssol1; title = "Oscillation (SDE, K = 20)") -splt2 = plot(ssol2; title = "Oscillation (SDE, K = 50)") - -plot(oplt1, oplt2, splt1, splt2; layout = (2,2), size = (800,600)) -``` - -## The Willamowski–Rössler model -The Willamowski–Rössler model was introduced in [*Willamowski & Rössler (1979)*](https://www.degruyter.com/document/doi/10.1515/zna-1980-0308/html?lang=en) as an example of a simple CRN model which exhibits [*chaotic behaviours*](https://en.wikipedia.org/wiki/Chaos_theory). This means that small changes in initial conditions can produce relatively large changes in the system's trajectory. -```@example crn_library_chaos -using Catalyst -wr_model = @reaction_network begin - k1, 2X --> 3X - k2, X --> 2X - k3, Z + 2X --> 2Z - k4, Y + X --> 2Y - k5, Y --> ∅ - k6, 2Z --> ∅ - k7, Z --> ∅ -end -``` -Here we first simulate the model for a single initial conditions, showing in both time-state space and phase space how how it reaches a [*strange attractor*](https://www.dynamicmath.xyz/strange-attractors/). -```@example crn_library_chaos -using OrdinaryDiffEq, Plots -u0 = [:X => 1.5, :Y => 1.5, :Z => 1.5] -tspan = (0.0, 50.0) -p = [:k1 => 2.1, :k2 => 0.7, :k3 => 2.9, :k4 => 1.1, :k5 => 1.0, :k6 => 0.5, :k7 => 2.7] -oprob = ODEProblem(wr_model, u0, tspan, p) -sol = solve(oprob, Rodas5P()) - -plt1 = plot(sol; title = "Time-state space") -plt2 = plot(sol; idxs = (:X, :Y, :Z), title = "Phase space") -plot(plt1, plt2; layout = (1,2), size = (800,400)) -``` \ No newline at end of file