# 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.linearize`

— Function```
(; 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
```

`ModelingToolkit.linearization_function`

— Function`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.