Internal Details

This is a page for detailing some of the inner workings to help future contributors to the library.

Observables and Variable Elimination

In the variable “elimination” algorithms, what is actually done is that variables are removed from being unknowns and equations are moved into the observed category of the system. The observed equations are explicit algebraic equations which are then substituted out to completely eliminate these variables from the other equations, allowing the system to act as though these variables no longer exist.

However, a user may want to interact with such variables, for example, plotting their output. For this reason, these relationships are stored, and are then used to generate the observed equation found in the SciMLFunction interface, so that sol[x] lazily reconstructs the observed variable when necessary. In this sense, there is an equivalence between observables and the variable elimination system.

The procedure for variable elimination inside structural_simplify is

  1. ModelingToolkit.initialize_system_structure.
  2. ModelingToolkit.alias_elimination. This step moves equations into observed(sys).
  3. ModelingToolkit.dae_index_lowering by means of pantelides! (if the system is an ODESystem).
  4. ModelingToolkit.tearing.

Preparing a system for simulation

Before a simulation or optimization can be performed, the symbolic equations stored in an AbstractSystem must be converted into executable code. This step typically occurs after the simplification explained above, and is performed when an instance of a SciMLBase.AbstractSciMLProblem, such as a ODEProblem, is constructed. The call chain typically looks like this, with the function names in the case of an ODESystem indicated in parentheses

  1. Problem constructor (ODEProblem)
  2. Build an DEFunction (process_DEProblem -> ODEFunction
  3. Write actual executable code (generate_function or generate_custom_function)

Apart from generate_function, which generates the dynamics function, ODEFunction also builds functions for observed equations (build_explicit_observed_function) and Jacobians (generate_jacobian) etc. These are all stored in the ODEFunction.

Creating an MTKParameters object

It may be useful to create a parameter object without creating the problem. For this purpose, the MTKParameters constructor is exposed as public API.

ModelingToolkit.MTKParametersType
function MTKParameters(sys::AbstractSystem, p, u0 = Dict(); t0 = nothing)

Create an MTKParameters object for the system sys. p (u0) are symbolic maps from parameters (unknowns) to their values. The values can also be symbolic expressions, which are evaluated given the values of other parameters/unknowns. u0 is only required if the values of parameters depend on the unknowns. t0 is the initial time, for time- dependent systems. It is only required if the symbolic expressions also use the independent variable of the system.

This requires that complete has been called on the system (usually via structural_simplify or @mtkbuild) and the keyword split = true was passed (which is the default behavior).

source