Linearization

A nonlinear dynamical system with state (differential and algebraic) $x$ and input signals $u$

\[M \dot x = f(x, u)\]

can be linearized using the function linearize to produce a linear statespace system on the form

\[\begin{aligned} \dot x &= Ax + Bu\\ y &= Cx + Du \end{aligned}\]

The linearize function expects the user to specify the inputs $u$ and the outputs $u$ using the syntax shown in the example below:

Example

using ModelingToolkit
@variables t x(t)=0 y(t)=0 u(t)=0 r(t)=0
@parameters kp = 1
D = Differential(t)

eqs = [u ~ kp * (r - y) # P controller
       D(x) ~ -x + u    # First-order plant
       y ~ x]           # Output equation

@named sys = ODESystem(eqs, t)
matrices, simplified_sys = linearize(sys, [r], [y]) # Linearize from r to y
matrices
(A = [-2.0;;], B = [1.0;;], C = [1.0;;], D = [0.0;;])

The named tuple matrices contains the matrices of the linear statespace representation, while simplified_sys is an ODESystem that, amongst other things, indicates the state order in the linear system through

using ModelingToolkit: inputs, outputs
[states(simplified_sys); inputs(simplified_sys); outputs(simplified_sys)]
3-element Vector{Any}:
 x(t)
 r(t)
 y(t)

Operating point

The operating point to linearize around can be specified with the keyword argument op like this: op = Dict(x => 1, r => 2).

Batch linearization and algebraic variables

If linearization is to be performed around multiple operating points, the simplification of the system has to be carried out a single time only. To facilitate this, the lower-level function ModelingToolkit.linearization_function is available. This function further allows you to obtain separate Jacobians for the differential and algebraic parts of the model. For ODE models without algebraic equations, the statespace representation above is available from the output of linearization_function as A, B, C, D = f_x, f_u, h_x, h_u.

Input derivatives

Physical systems are always proper, i.e., they do not differentiate causal inputs. However, ModelingToolkit allows you to model non-proper systems, such as inverse models, and may sometimes fail to find a realization of a proper system on proper form. In these situations, linearize may throw an error mentioning

Input derivatives appeared in expressions (-g_z\g_u != 0)

This means that to simulate this system, some order of derivatives of the input is required. To allow linearize to proceed in this situation, one may pass the keyword argument allow_input_derivatives = true, in which case the resulting model will have twice as many inputs, $2n_u$, where the last $n_u$ inputs correspond to $\dot u$.

If the modeled system is actually proper (but MTK failed to find a proper realization), further numerical simplification can be applied to the resulting statespace system to obtain a proper form. Such simplification is currently available in the experimental package ControlSystemsMTK.

Tools for linear analysis

ModelingToolkitStandardLibrary contains a set of tools for more advanced linear analysis. These can be used to make it easier to work with and analyze causal models, such as control and signal-processing systems.

ModelingToolkit.linearizeFunction
(; A, B, C, D), simplified_sys = linearize(sys, inputs, outputs;    t=0.0, op = Dict(), allow_input_derivatives = false, kwargs...)
(; A, B, C, D)                 = linearize(simplified_sys, lin_fun; t=0.0, op = Dict(), allow_input_derivatives = false)

Return a NamedTuple with the matrices of a linear statespace representation on the form

\[\begin{aligned} ẋ &= Ax + Bu\\ y &= Cx + Du \end{aligned}\]

The first signature automatically calls linearization_function internally, while the second signature expects the outputs of linearization_function as input.

op denotes the operating point around which to linearize. If none is provided, the default values of sys are used.

If allow_input_derivatives = false, an error will be thrown if input derivatives ($u̇$) appear as inputs in the linearized equations. If input derivatives are allowed, the returned B matrix will be of double width, corresponding to the input [u; u̇].

See also linearization_function which provides a lower-level interface, and ModelingToolkit.reorder_states.

See extended help for an example.

The implementation and notation follows that of "Linear Analysis Approach for Modelica Models", Allain et al. 2009

Extended help

This example builds the following feedback interconnection and linearizes it from the input of F to the output of P.


  r ┌─────┐       ┌─────┐     ┌─────┐
───►│     ├──────►│     │  u  │     │
    │  F  │       │  C  ├────►│  P  │ y
    └─────┘     ┌►│     │     │     ├─┬─►
                │ └─────┘     └─────┘ │
                │                     │
                └─────────────────────┘
using ModelingToolkit
@variables t
function plant(; name)
    @variables x(t) = 1
    @variables u(t)=0 y(t)=0
    D = Differential(t)
    eqs = [D(x) ~ -x + u
           y ~ x]
    ODESystem(eqs, t; name = name)
end

function ref_filt(; name)
    @variables x(t)=0 y(t)=0
    @variables u(t)=0 [input=true]
    D = Differential(t)
    eqs = [D(x) ~ -2 * x + u
           y ~ x]
    ODESystem(eqs, t, name = name)
end

function controller(kp; name)
    @variables y(t)=0 r(t)=0 u(t)=0
    @parameters kp = kp
    eqs = [
        u ~ kp * (r - y),
    ]
    ODESystem(eqs, t; name = name)
end

@named f = ref_filt()
@named c = controller(1)
@named p = plant()

connections = [f.y ~ c.r # filtered reference to controller reference
               c.u ~ p.u # controller output to plant input
               p.y ~ c.y]

@named cl = ODESystem(connections, t, systems = [f, c, p])

lsys, ssys = linearize(cl, [f.u], [p.x])
desired_order =  [f.x, p.x]
lsys = ModelingToolkit.reorder_states(lsys, states(ssys), desired_order)

@assert lsys.A == [-2 0; 1 -2]
@assert lsys.B == [1; 0;;]
@assert lsys.C == [0 1]
@assert lsys.D[] == 0
source
ModelingToolkit.linearization_functionFunction
lin_fun, simplified_sys = linearization_function(sys::AbstractSystem, inputs, outputs; simplify = false, kwargs...)

Return a function that linearizes system sys. The function linearize provides a higher-level and easier to use interface.

lin_fun is a function (variables, p, t) -> (; f_x, f_z, g_x, g_z, f_u, g_u, h_x, h_z, h_u), i.e., it returns a NamedTuple with the Jacobians of f,g,h for the nonlinear sys (technically for simplified_sys) on the form

\[ẋ = f(x, z, u) 0 = g(x, z, u) y = h(x, z, u)\]

where x are differential states, z algebraic states, u inputs and y outputs. To obtain a linear statespace representation, see linearize. The input argument variables is a vector defining the operating point, corresponding to states(simplified_sys) and p is a vector corresponding to the parameters of simplified_sys. Note: all variables in inputs have been converted to parameters in simplified_sys.

The simplified_sys has undergone structural_simplify and had any occurring input or output variables replaced with the variables provided in arguments inputs and outputs. The states of this system also indicates the order of the states that holds for the linearized matrices.

Arguments:

  • sys: An ODESystem. This function will automatically apply simplification passes on sys and return the resulting simplified_sys.
  • inputs: A vector of variables that indicate the inputs of the linearized input-output model.
  • outputs: A vector of variables that indicate the outputs of the linearized input-output model.
  • simplify: Apply simplification in tearing.
  • kwargs: Are passed on to find_solvables!

See also linearize which provides a higher-level interface.

source