From c916e70eeb198a63064b3b1e7b501701e78658b9 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 18 Apr 2024 12:36:10 -0400 Subject: [PATCH] save progress --- .../advanced_intro_to_catalyst.md | 162 +++++++++++------- 1 file changed, 104 insertions(+), 58 deletions(-) diff --git a/docs/src/introduction_to_catalyst/advanced_intro_to_catalyst.md b/docs/src/introduction_to_catalyst/advanced_intro_to_catalyst.md index 5a4eb4e25d..dc920cb386 100644 --- a/docs/src/introduction_to_catalyst/advanced_intro_to_catalyst.md +++ b/docs/src/introduction_to_catalyst/advanced_intro_to_catalyst.md @@ -1,5 +1,5 @@ # [How Catalyst works and advanced introduction](@id advanced_intro_to_catalyst) -We have [previously](@ref introduction_to_catalyst) introduced Catalyst, and how to use it to create and simulate simple chemical reaction network (CRN) models. Here, we provide a more in-depth introduction. We will explain how Catalyst is constructed (i.e. what packages it builds on and how), provide a comprehensive overview of the ways Catalyst models can be created, and describe how these are different. +We have [previously](@ref introduction_to_catalyst) introduced Catalyst, and how to use it to create and simulate simple chemical reaction network (CRN) models. Here, we provide a more in-depth introduction. We will explain how Catalyst is constructed (i.e. what packages it builds on and how), provide a comprehensive overview of the different ways Catalyst models can be created, and describe how these are different. This advanced introduction is not required for understanding the remaining documentation, nor for using any of Catalyst's features. It is not expected that new users will read it. Instead, it aims to provide additional context and understanding for more experienced users. Various section here are also used as references at later part of the documentation (for users who wants additional insight). For those who expect to use Catalyst extensively, we believe that, at some point, reading this tutorial will be useful. @@ -8,13 +8,13 @@ This advanced introduction is not required for understanding the remaining docum Catalyst is built on top of the domain-agnostic modelling package [ModelingToolkit.jl](https://github.com/SciML/ModelingToolkit.jl). ModelingToolkit is in turn built on the computer algebraic system [Symbolics.jl](https://github.com/JuliaSymbolics/Symbolics.jl). Here we will introduce these packages, and how the feed into Catalyst. ### [The Symbolics.jl computer algebraic system](@id advanced_intro_to_catalyst_background_symbolics) -Symbolics is a Julia package for representing, and doing computations on, symbolic expressions. It allow us to declare symbolic variables that can be used to construct equations. Prinarily this is done through the `@variables` macro: +Symbolics is a Julia package for representing, and doing computations on, symbolic expressions. It allow us to declare symbolic variables that can be used to construct equations. Primarily this is done through the `@variables` macro: ```@example advanced_intro_to_catalyst_symbolics using Symbolics @variables x y nothing # hide ``` -Here, both `x` and `y` becomes variables in the current scope, e.g. we can do +Here, both `x` and `y` becomes Julia variables in the current scope, e.g. we can do ```@example advanced_intro_to_catalyst_symbolics x ``` @@ -39,23 +39,26 @@ reactions(rn)[1].rate ``` Especially when large ODEs are formed, this can remove redundant terms, making the ODE faster to evaluate, thus increasing simulation performance. -Another feature of Symbolics.jl is that it differentiate equations: +Another feature of Symbolics is that it can differentiate equations: ```@example advanced_intro_to_catalyst_symbolics Symbolics.derivative(eq, x) ``` -This is used internally to symbolically compute system Jacobians. As explained in a [later section](@ref ref) this can sometimes be advantageous for simulations. +This is used internally to symbolically compute system Jacobians. As explained in a [later section](@ref ref) this can be used to improve simulation performance. ### [The ModelingToolkit.jl intermediary representation and modelling package](@id advanced_intro_to_catalyst_background_mtk) ModelingToolkit.jl is a package with two separate functionalities: -- It provides an interface for building such mathematical models. +- It provides an interface for building mathematical models from symbolic equations. - It implements an intermediary representation for representing such mathematical models. We will start with describing the first point, by showing how ModelingToolkit can be used to build a ODE model. First we need to designate the variables and parameters of the model. This is done in a similar manner as we defined variables using symbolics, however, with two differences: - ODEs (typically) describe the evolution of its variables with time. The variables are thus *time dependent* and must be declared as functions of the time *independent variable*, $t$ (dependency on non-time variables is possible and [discussed later]()). - Parameters must be declared using the `@parameters` macro (to distinguish them from the variables). + +Here, we import `t` (the time independent variable) and `D` (the differential with respect to `t`), declares the variable `X` and the parameters `p` and `d`: ```@example advanced_intro_to_catalyst_mtk using ModelingToolkit +using ModelingToolkit: t_nounits as t, D_nounits as D @variables X(t) @parameters p d ``` @@ -75,22 +78,18 @@ Here, the derivative with anything with respect to $t$ is defined using the `D` ```@example advanced_intro_to_catalyst_mtk diff_eq = D(X) ~ p - d*X ``` -Finally, to be able to use our differential equation for any purpose (e.g. to make simulations), we must first encapsulate it within an `ODESystem`. This takes vector listing all differential equations, variables, and parameters. It also requires designating the independent variable (here `t`) as the second argument: -```@example advanced_intro_to_catalyst_mtk -osys = ODESystem([diff_eq], t, [X], [p, d]; name = :simple_ODE) -``` -The `ODESystem` also have a name (this is a required argument), which can be found using the `nameof` function: -```@example advanced_intro_to_catalyst_mtk -nameof(osys) -``` -Sometimes the creation of an `ODESystem` is prepended with the `@named` macro. This automatically gives the system the same name as the variable it is assigned to: +!!! note + When we form symbolic equations (e.g. here setting `D(X)` equal to `p - d*X`), `~` is used as the equivalence operator. Next, `=` is used in the normal Julia fashion to assign the equation `D(X) ~ p - d*X` to the Julia variable `diff_eq`. + +Finally, to be able to use our differential equation for any purpose (e.g. to make simulations), we must first encapsulate it within an `ODESystem`. This takes vector listing all differential equations. It also requires designating the independent variable (here `t`) as the second argument: ```@example advanced_intro_to_catalyst_mtk -@named osys = ODESystem([diff_eq], t, [X], [p, d]) -nameof(osys) +@mtkbuild osys = ODESystem([diff_eq], t) ``` +Here, declarations of ModelingToolkit systems are prepended with an `@mtkbuild` statement. This have two effects: +- It sets a *system name* (which is equal to the variable it is stored in, in this case `osys`). System names are discussed in more detail [here](@ref ref). +- It applied the `structural_simplify` function to ths system (described in more detail [here](https://docs.sciml.ai/ModelingToolkit/stable/systems/NonlinearSystem/#Transformations)). -Finally, `ODESystem`s can passed to e.g. `ODEProblems` to create simulations. This is very similar to how it is done for Catalyst, however, we can actually use the variables and parameter themselves to designate their values in the initial condition and parameter values vectors: - +Finally, `ODESystem`s can be passed to e.g. `ODEProblems` to create simulations. This is very similar to how it is done for Catalyst. Here, however, we can use the actually variables and parameter themselves to designate their values in the initial condition and parameter values vectors: ```@example advanced_intro_to_catalyst_mtk using OrdinaryDiffEq, Plots @@ -104,7 +103,7 @@ plot(sol) ``` ### [System types and their conversions](@id advanced_intro_to_catalyst_background_systems) -We have previously noted that Catalyst CRN models (generated by e.g. `@reaction_network`) are stored in so-called `ReactionSystem` structures. Above we also noted that ModelingToolkit's `ODESystem` can be used as input to `ODEProblem`s in the same manner as Catalyst's `ReactionSystem`. What happens is that `ReactionSystem`s can be converted to `ODESystem`: +We have previously noted that Catalyst CRN models (generated by e.g. the `@reaction_network` DSL) are stored in so-called `ReactionSystem` structures. These can (just like ModelingToolkit's `ODESystem`s) be used as input to `ODEProblem`s. Internally, what actually happens here is that Catalyst's `ReactionSystem`s are converted to ModelingToolkit `ODESystems`, which are used for the actual simulation. To demonstrate we carry out the same process manually. First we use the `convert` function to convert our `REactionSystem` to an `ODESystem`: ```@example advanced_intro_to_catalyst_mtk using Catalyst rsys = @reaction_network begin @@ -112,19 +111,21 @@ rsys = @reaction_network begin end osys_catalyst = convert(ODESystem, rsys) ``` -Here, when a `ReactionSystem` is supplied to a `ODEProblem`, it is internally converted to a `ODESystem`, which is then used to perform the simulation. Indeed, we can simulate our converted `REactionSystem` and get the same result as when we simulated an `ODESystem` directly. +Here we have implemented the same model (a production/degradation loop) using both Catalyst and ModelingToolkit. Next, we simulate `osys_catalyst` in the exact same way as `osys` was simulated. ```@example advanced_intro_to_catalyst_mtk oprob_catalyst = ODEProblem(osys_catalyst, u0, tspan, ps) sol_catalyst = solve(oprob_catalyst, Tsit5()) sol == sol_catalyst ``` +Here we also confirm that the output simulation is identical ModelingToolkit implements a wide range of [system types](https://docs.sciml.ai/ModelingToolkit/stable/basics/AbstractSystem/), enabling to to model a wide range of systems. This include e.g.: +- [`ODESystem`s](https://docs.sciml.ai/ModelingToolkit/stable/systems/ODESystem/) to represent ODEs. - [`SDESystem`s](https://docs.sciml.ai/ModelingToolkit/stable/systems/SDESystem/) to represent SDEs. - [`JumpSystem`s](https://docs.sciml.ai/ModelingToolkit/stable/systems/JumpSystem/) to represent jump processes. - [`NonlinearSystem`s](https://docs.sciml.ai/ModelingToolkit/stable/systems/NonlinearSystem/) to represent systems of nonlinear equations. -The advantage of representing a model as a CRN is that from this representation there is an ubiquitous conversion to ODEs (via the [reaction rate equation](@ref ref)), SDEs (via the [chemical Langevin equation](@ref ref)), and jump processes (via the [stochastic chemical kinetics](@ref ref)). In the same manner, the advantage of representing a model as a Catalyst `ReactionSystem` is that these can ubiquitous be converted to `ODESystem`s, `SDESystem`s, `JumpSystem`s, and `NonlinearSystem`s (the last one primarily used to find the steady states of the ODE form). I.e. all of these are possible: +The advantage of representing a model as a CRN is that this representation can be unambiguously converted to ODEs (via the [reaction rate equation](@ref ref)), SDEs (via the [chemical Langevin equation](@ref ref)), and jump processes (via the [stochastic chemical kinetics](@ref ref)). In the same manner, the advantage of representing a model as a Catalyst `ReactionSystem` is that these can unambiguously be converted to `ODESystem`s, `SDESystem`s, `JumpSystem`s, and `NonlinearSystem`s (the last one primarily used to find the steady states of the ODE form). I.e. all of these are possible: ```@example advanced_intro_to_catalyst_mtk ssys = convert(SDESystem, rsys) jsys = convert(JumpSystem, rsys) @@ -136,17 +137,17 @@ with it next being possible to pass these into their respective problem types, e using StochasticDiffEq sprob = SDEProblem(ssys, u0, tspan, ps) ssol = solve(sprob, ImplicitEM()) -nothing # hide +plot(ssol) ``` -Practically, in all these cases, the `ReactionSystem` can be passed directly into the desired problem (and the conversion being carried out automatically). +Practically, in all these cases, the `ReactionSystem` can be passed directly into the desired problem (where the conversion is carried out automatically). ### [ModelingToolkit as an intermediary representation](@id advanced_intro_to_catalyst_background_ir) -Above we have demonstrated ModelingToolkit's second feature, that it provides an *intermediary representation* (IR) for representing mathematical models (as e.g. `ODESystem`s). Here, a large number of packages for building mathematical models in the Julia programming languages (e.g. Catalyst, [OrbitalTrajectories.jl](https://github.com/dpad/OrbitalTrajectories.jl), and [NBodySimulator.jl](https://github.com/SciML/NBodySimulator.jl)) builds their models using ModelingToolkit's IRs. Next, a large number of packages mode model simulation and analysis (e.g. [DifferentialEquations.jl](https://github.com/SciML/DifferentialEquations.jl), [NonlinearSolve.jl](https://github.com/SciML/NonlinearSolve.jl), [StructuralIdentifiability.jl](https://github.com/SciML/StructuralIdentifiability.jl), and [PEtab.jl](https://github.com/sebapersson/PEtab.jl)) ensures that their packages also are compatible with the IRs. +Above we have indirectly demonstrated ModelingToolkit's second feature, that it provides an *intermediary representation* (IR) for representing mathematical models (as e.g. `ODESystem`s). Here, a large number of packages for building mathematical models in the Julia programming languages (e.g. Catalyst, [OrbitalTrajectories.jl](https://github.com/dpad/OrbitalTrajectories.jl), and [NBodySimulator.jl](https://github.com/SciML/NBodySimulator.jl)) builds their models using ModelingToolkit's IRs. Next, a large number of packages mode model simulation and analysis (e.g. [DifferentialEquations.jl](https://github.com/SciML/DifferentialEquations.jl), [NonlinearSolve.jl](https://github.com/SciML/NonlinearSolve.jl), [StructuralIdentifiability.jl](https://github.com/SciML/StructuralIdentifiability.jl), and [PEtab.jl](https://github.com/sebapersson/PEtab.jl)) ensures that their packages also are compatible with these IRs. -This is the primary way Catalyst is able to support such a large number of features. By utilising the ModelingToolkit IR, we automatically receive support from one of the largest ecosystems for mathematical modelling, while requiring only minimal implementation within Catalyst itself. +This is the primary way Catalyst is able to support such a large number of features. By linking Catalyst to the ModelingToolkit IR, its models is automatically supported from one of the largest ecosystems for mathematical modelling, while requiring only minimal implementation within Catalyst itself. -## [Types of modelling in Catalyst](@id advanced_intro_to_catalyst_modelling_approaches) -There are three different ways of building Catalyst `ReactionSystem` models. The first two are distinct from each other, while the third one can build on top of either (or both) of these. Here we will briefly describe all three approached (with more details of each described in separate sections of this documentation). +## [Types of Catalyst modelling approached](@id advanced_intro_to_catalyst_modelling_approaches) +There are three main ways of building Catalyst `ReactionSystem` models. The first two are distinct from each other, while the third one can build on top of either (or both) of these. Here we will briefly describe all three approached (with more details of each described in separate sections of this documentation). ### [DSL-based modelling](@id advanced_intro_to_catalyst_modelling_approaches_DSL) Catalyst implements a *domain-specific language* (DSL) through its `@reaction_network` macro. This enables the writing of CRN models in their native chemical reaction form. here we use it to create a simple [catalysis model]: @@ -158,10 +159,10 @@ rs_dsl = @reaction_network rs begin kP, SE --> P + E end ``` -A throughout description of how to model CRNs using the DSL is provided [here](@ref ref), with some of its more advanced options being described [here](@ref ref). +A throughout description of how to model CRNs using the DSL is provided [here](@ref ref), with some of its more advanced options being described [here](@ref ref). The DSL is the most frequent modelling approach used throughout this documentation. Its primary advantage is that it enables models to be created quickly, and using code which is highly human-interpretable. ### [Programmatic modelling](@id advanced_intro_to_catalyst_modelling_approaches_programmatic) -Previously we showed how to programmatically create `ODESystems` [using ModelingToolkit](@ref advanced_intro_to_catalyst_background_mtk). Catalyst have a similar interface for creating `ReactionSystem`s. Here, we first designate the species and parameters of our model (note that we use the `@species` macro instead of the `@variables` macro, which is similar, but specifically designate that the created variables are *species*). +Previously we showed how to programmatically create `ODESystems` [using ModelingToolkit](@ref advanced_intro_to_catalyst_background_mtk). Catalyst have a similar interface for creating `ReactionSystem`s. Here, we first designate the species and parameters of our model (note that we use the `@species` macro instead of the `@variables` macro, which is similar, but specifically designate that the created [variables are *species*](@ref ref)). ```@example advanced_intro_to_catalyst_modelling_approaches @species S(t) E(t) SE(t) P(t) @parameters kB kD kP @@ -184,13 +185,13 @@ The resulting `ReactionSystem` is identical to the one we created directly using ```@example advanced_intro_to_catalyst_modelling_approaches rs_dsl == rs_prog ``` -This equality requires the two systems to have the same `name`, here `:rs`. This is provided to the DSL using [the initial argument to @reaction_network](@ref ref) a to the programmatic construction using the `name` keyword. +This equality requires the two systems to have the same `name` (here, `:rs`). The name is provided to the DSL using [the initial argument to @reaction_network](@ref ref) and to the programmatic constructor using the `name` keyword. -Note that we here directly use symbolic variables. This can be used to form arbitrary expressions as rates (just like we did when we [described Symbolics.jl](@ref @advanced_intro_to_catalyst_background_symbolics)). I.e. to make a reaction directly dependent on `P` we can write +Note that we here directly use symbolic variables. This can be used to form arbitrary expressions as rates (just like we did when we [described Symbolics](@ref @advanced_intro_to_catalyst_background_symbolics)). I.e. to make a reaction directly dependent on `P` we can write ```@example advanced_intro_to_catalyst_modelling_approaches Reaction(kP*P, [S], [P]) ``` -More details on programmatic model construction (e.g. how to use non-unitary stoichiometries) can be found [here](@ref programmatic_CRN_construction). +More details on programmatic model construction can be found [here](@ref programmatic_CRN_construction). While programmatic modelling typically is a bit more laborious than DSL-based, it also gives the user more control over the modelling process. ### [Hierarchical and compositional modelling](@id advanced_intro_to_catalyst_modelling_approaches_hierarchical) The final type of model construction is hierarchical (or compositional) modelling. This involves first creating smaller models, and then composing them together to create a single, more complex model. The component models can be created using either DSL-based or programmatic modelling. @@ -218,10 +219,10 @@ Now, we can use the `compose` function to combine all three models into a single @named model = compose(model, [nucleus, cytoplasm]) ``` -Hierarchical modelling can be used to model [system spread across several different compartments](@ref ref), to simplify the creation of [models with repetitive components](). More details on hierarchical and compositional modelling is provided [here](@ref compositional_modeling). +Hierarchical modelling can be used to model [system spread across several different compartments](@ref ref), to simplify the creation of [models with repetitive components](@ref ref). MHierarchical and compositional modelling is described in more detail [here](@ref compositional_modeling). ## [Designating species and parameters](@id advanced_intro_to_catalyst_designations) -Frequently when working with Catalyst, we need to represent a species or parameter (or variables, when [mixed CRN-equation based models are used]()). Primarily this happens when we set the initial conditions and parameter values of a simulation, but it can also be when we [designate which species to plot](@ref ref) or [updates a parameter during a simulation](@ref ref). There are a three different ways to represent species and parameters, each with their pros and cons. Hence, we will give a short overview of them here. +Frequently when working with Catalyst, we need to represent a species or parameter (or variables, when [mixed CRN-equation based models are used](@ref ref)). Primarily this happens when we set the initial conditions and parameter values of a simulation, but it is also relevant when we [designate which species to plot](@ref ref) or [updates a parameter during a simulation](@ref ref). There are a three different ways to represent species and parameters, each with their pros and cons. Here we will provide a short overview of each. ### [Symbol-based designation](@id advanced_intro_to_catalyst_designations_syms) Throughout this documentation, Symbol-based representation is used for parameters and species. Here, to represent $X$, the corresponding [`Symbol`](@ref ref), `:X` is used (a `Symbol` is created by pre-pending an expression with `:`). E.g. to simulate the following model: @@ -250,8 +251,10 @@ There are two cases where `Symbol`-based designations cannot be used: - When using [hierarchical/compositional modelling](advanced_intro_to_catalyst_modelling_approaches_hierarchical). Here, it cannot be inferred the value of $X$ in which submodel `:X` designates (i.e. its value in the nucleus or the cytosol). How to deal with this problem is described [three sections down](@ref advanced_intro_to_catalyst_designations_sys_symb). - When needing to form an algebraic expression of species/parameters (as used e.g. [here](@ref petab_parameter_fitting_intro_observables)). Potential workaround is to use one of the following two approaches for designating species/parameters (often, the quantity of interest can also be [declared as an *observable*](@ref ref)). +Generally, Symbol-based designates is used when [models are created using the DSL](@ref advanced_intro_to_catalyst_modelling_approaches_DSL). + ### [Symbolics-based designation](@id advanced_intro_to_catalyst_designations_symbolics) -Previously, we showed how model creation using ModelingToolkit involves [explicitly declaring variables and parameters, which then can be used to designate their values](@ref advanced_intro_to_catalyst_background_mtk).We also showed how Catalyst `ReactionSystem`s can be [created programmatically, in a similar way to how ModelingToolkit models are created](@ref advanced_intro_to_catalyst_modelling_approaches_programmatic). Here, just like for ModelingToolkit, we can represent species and parameters using their respective Symbolics variables directly: +Previously, we showed how model creation using ModelingToolkit involves [explicitly declaring variables and parameters, which then can be used to designate their values](@ref advanced_intro_to_catalyst_background_mtk). We also showed how Catalyst `ReactionSystem`s can be [created programmatically, in a similar way to how ModelingToolkit models are created](@ref advanced_intro_to_catalyst_modelling_approaches_programmatic). Here, just like for ModelingToolkit, we can represent species and parameters using their respective Symbolics variables directly: ```@example advanced_intro_to_catalyst_designations @variables t @species X(t) @@ -321,7 +324,10 @@ Next, we can designate a variable through `rs.X1` (first the system to which it X1 = @unpack X1 = rs isequal(rs.X1, X1) ``` -Here, indirect symbolic representation can be used exactly like symbolic representation to perform a simulation: +!!! note + In Julia, equality is normally checked using the `==` operator. However, when applied to Symbolic expressions, this will instead form a new symbolic expression. Hence, `rs.X1 == X1` cannot be used, and the `isequal` function has to be used instead. + +Indirect symbolic representation can be used exactly like symbolic representation to perform a simulation: ```@example advanced_intro_to_catalyst_designations u0_inds = [rs.X => 1.0] ps_inds = [rs.p => 1.0, rs.d => 0.2] @@ -329,6 +335,10 @@ oprob_inds = ODEProblem(rs, u0_inds, tspan, ps_inds) sol_inds = solve(oprob_inds, Tsit5()) sol_inds == sol_symb == sol_syms ``` +or to interface with e.g. solution objects. +```@example advanced_intro_to_catalyst_designations +sol[2rs.X + rs.p] == 2*sol[rs.X] .+ 1.0 +``` Using indirect symbolic representation is required when using compositional modelling. E.g. let us consider our [nucleus/cytoplasm model from previously](@ref advanced_intro_to_catalyst_modelling_approaches_hierarchical): ```@example advanced_intro_to_catalyst_designations @@ -384,8 +394,10 @@ sol = solve(oprob, Tsit5()) sol[rs.X1 + rs.X2] ``` +Indirect symbolic designation is primarily used in tandem with hierarchical modelling, and [discussed in more details in that section of the documentation](@ref ref). + ## Other low-level features -Finally, we describe some additional features of Catalyst. These are features that can be skipped, but for which some additional understanding of can be useful. +Finally, we describe some additional aspects of Catalyst. Understanding these is not required for working with Catalyst, however, it can be useful for certain workflows. ### [Interpolating variables into the DSL](@id advanced_intro_to_catalyst_interpolation) @@ -396,6 +408,7 @@ 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 advanced_intro_to_catalyst_interpolation +using Catalyst @parameters k α @variables t @species A(t) @@ -410,11 +423,11 @@ rn = @reaction_network $name begin 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 +```@example advanced_intro_to_catalyst_interpolation parameters(rn) ``` as are the species coming from interpolated variables -```@example tut2 +```@example advanced_intro_to_catalyst_interpolation species(rn) ``` @@ -423,15 +436,35 @@ species(rn) multiplication symbol must be explicitly included like `2*$spec`. ### [System completeness](@id advanced_intro_to_catalyst_completeness) -Both Catalyst and ModelingToolkit creates various *systems*, which then are used as input to *problems*. Especially ModelingToolkit (but [sometimes also Catalyst](@ref ref)) constructs models by composing systems together. Here, *completeness* is a termed used to designate that a model is ready for analysis. The important part here is that: +Both Catalyst and ModelingToolkit creates *systems*, which are used as input to *problems*. Especially ModelingToolkit (but [sometimes also Catalyst](@ref ref)) constructs models by composing systems together. Here, *completeness* is a termed used to designate that a model has been finalised. The important part here is that: - Complete models cannot be composed with other systems. -- Incomplete models should not be used for for analysis. +- Incomplete models should not be used for for simulation (or other forms of analysis). -Catalyst models are by default *complete* when they are created. This means that they can be provided to e.g. a `ODESystem` directly. This, however, means that they cannot be used for composition. To do so, the component model must be designated as *incomplete* when it is created (described [here](@ref ref)). Once a model have been composed of incomplete blocks, it can be marked as complete by applying the `complete` function. It is worth noting that ModelingToolkit (which is primarily designed for compositional modelling) works the other way. Models are, by default, incomplete, and must always be designated as complete before they are used. +Model completeness depends on how they are created: +- Models created using the `@reaction_network` DSL are *complete*. +- To create *incomplete models using the DSL*, use the [`@network_component` macro](@ref ref). +- Models created programmatically are *incomplete*. +- Models generated through the `compose` (and `extend`) functions are *incomplete. + +It is possible to check whenever a model is complete through the `Catalyst.iscomplete` function: +```@example advanced_intro_to_catalyst_interpolation +using Catalyst +rs = @network_components begin + (p,d), 0 <--> X +end +Catalyst.iscomplete(rs) +``` +A complete model can be generated from an incomplete model through the `complete` function: +```@example advanced_intro_to_catalyst_interpolation +rs = complete(rs) +Catalyst.iscomplete(rs) +``` + +All systems created through ModelingToolkit are incomplete (until marked as complete). However, if a ModelingToolkit system declaration is pre-appended with `@mtkbuild`, it will be marked as complete on creation. Finally, while Catalyst use the `complete` function to generate complete models, ModelingToolkit uses the `structural_simplify` function (which in addition to marking completeness, performs some additional system manipulations which are not relevant to Catalyst). ### [Independent variables](@id advanced_intro_to_catalyst_indep_vars) -A system consists of *dependent* and *independent* variables. Here, dependent variables are the typical variables of the system, while the independent are what they depend on. Most (CRN) systems only have a single independent variable, *time*. Next, its dependent variables (typically species) depend on (are function of) this independent variables (that is why we declare them as e.g. `X(t)` in programmatic modelling). Non-time independent variables are typically spatial variables. Here, a species's value could depend both on time and its spatial position in teh system (e.g. if you model the concentration of a chemical in a reactor with spatial patterns). +A differential equation model consists of *dependent* and *independent* variables. Here, dependent variables are the quantities which behaviour is described by our model, while the independent variables are variable these depend on.Most (CRN) systems only have a single independent variable, *time*. Next, its dependent variables (typically species) depend on (are function of) this independent variables (that is why we declare them as e.g. `X(t)` in programmatic modelling). Non-time independent variables exists, and these describe spatial dimensions. A species's value can depend on both time and one (or several) spatial dimension(s) (e.g. the concentration of a species `X` may depend on time and on its spatial location in a chemical reactor). It is possible to designate non-time independent variables for both dsl-based and programmatic modelling: ```@example advanced_intro_to_catalyst_indep_vars @@ -439,36 +472,36 @@ using Catalyst # In the DSL. @reaction_network begin - @ivs s - @species X(t,s) + @ivs t x + @species X(t,x) (p,d), 0 <--> X end # Programmatically. -@variables s -@species X(t,s) +@variables t x +@species X(t,x) @parameters p d rxs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing) ] -ReactionSystem(rxs, t; sivs=s) +ReactionSystem(rxs, t; sivs=[x]) ``` A `ReactionSystem`'s primary (time) independent variable can be retrieved using `get_iv(rsys)`, and its spatial ones using `get_sivs(rsys)`. -Currently, Catalyst primarily supports spatial modelling on discrete spatial structures, where spatial independent variables are not used. Hence, this feature is not required for any current Catalyst feature. +Currently, Catalyst primarily supports spatial modelling on discrete spatial structures (where spatial independent variables are not used). Hence, the declaration of spatial independent variables are of limited use. -## [Variable and reaction metadata](@id advanced_intro_to_catalyst_metadata) +## [Variable, reaction, and system metadata](@id advanced_intro_to_catalyst_metadata) ### [Variable metadata](@id advanced_intro_to_catalyst_metadata_variables) -It is possible to attach metadata to symbolic variables. The use of this depend on the metadata, and some may simply be a convenience for the user. E.g. the most general metadata, `description` allows you to attach an arbitrary string to a variable. Metadata is attached when a variable is created by adding it within square brackets after the variable in question. +It is possible to attach metadata to symbolic variables. The use of this depend on the metadata, and some may simply be a convenience for the user. E.g. the `description` allows you to attach an arbitrary string to a variable. Metadata is attached when a variable is created by adding it within square brackets after the variable in question. ```@example advanced_intro_to_catalyst_indep_metadata using Catalyst t = ModelingToolkit.time_iv @species X(t) [description="My protein of interest"] nothing # hide ``` -When several variables are created, you only put square bracket with metadata after those variables you are interested in +Several variables can be declared while metadata is only attached to a subset of them. ```@example advanced_intro_to_catalyst_indep_metadata @parameters p [description="The production rate of X"] d nothing # hide @@ -478,21 +511,24 @@ Metadata cannot be updated, however, it is possible to override variables with n @species X(t) [description="My protein of very high interest"] nothing # hide ``` -Each metadata have its own implementation, and it is not possible to use custom metadata (unless they implement it themselves). Each metadata also have a specific getter. I.e. to retieve the description we use +Each metadata have its own implementation, and it is not possible to use custom metadata (unless they implement it themselves). Each metadata also have a specific getter. I.e. to retrieve the description we use ```@example advanced_intro_to_catalyst_indep_metadata getdescription(X) ``` +A list of metadata supported by ModelingToolkit (and thus by Catalyst) can be found [here](https://docs.sciml.ai/ModelingToolkit/stable/basics/Variable_metadata/). + ### [Reaction metadata](@id advanced_intro_to_catalyst_metadata_reactions_) Reactions also have metadata. When created using the DSL, a reaction's metadata is given after the reactions (separated from it by a comma): ```@example advanced_intro_to_catalyst_indep_metadata +using Catalyst @reaction_network begin p, ∅ --> X, [description="Production of X"] d, X --> ∅ end ``` -Here, it is possible to attach metadata to [bundled reactions](@ref ref). Here, we add parentheses around the metadata blocks just as we do for the rates: +It is possible to attach metadata to [bundled reactions](@ref ref). Here, we add parentheses around the metadata blocks just as we do for the rates: ```@example advanced_intro_to_catalyst_indep_metadata rs = @reaction_network begin (p,d), ∅ <--> X, ([description="Production of X"], [description="Degradation of X"]) @@ -507,11 +543,21 @@ t = ModelinGToolkit.time_iv @species X(t) @parameters p d rxs = [ - Reaction(p, nothing, [X]; metadata = "Production of X") - Reaction(d, [X], nothing; metadata = "Degradation of X") + Reaction(p, nothing, [X]; metadata = [description => "Production of X"]) + Reaction(d, [X], nothing; metadata = [description => "Degradation of X"]) ] rs = ReactionSystem(rxs, t) nothing # hide ``` -Reaction metadata can be accessed using the `hasmetadata`, `getmetadata`, and `getallmetadata` functions, which are described [here](@ref ref). Unlike for variables, users can specify and use arbitrary metadata for their reactions. \ No newline at end of file +While arbitrary symbolic variables metadata cannot be used without special implementation, this is possible for reactions. Here, any descriptor can be used, and any value assigned to it. Reaction metadata can be accessed using the `hasmetadata`, `getmetadata`, and `getallmetadata` functions, which are described [here](@ref ref). + +Finally, all `ReactionSystem`s have a `metadata` field which can be assigned any arbitrary value. E.g. +```@example advanced_intro_to_catalyst_indep_metadata +my_metadata = Dict([:description => "A reaction system"]) +rs = ReactionSystem(rxs, t; metadata = my_metadata) +``` +A system's metadata can be accessed through the `ModelingToolkit.getmetadata` function: +```@example advanced_intro_to_catalyst_indep_metadata +ModelingToolkit.getmetadata(rs) +```