Skip to content

Commit b7a9196

Browse files
authored
Merge pull request #110 from baggepinnen/analysis_points
Add analysis points
2 parents a8bb688 + c90780a commit b7a9196

File tree

7 files changed

+572
-1
lines changed

7 files changed

+572
-1
lines changed

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ Symbolics = "4.9"
1717
julia = "1.6"
1818

1919
[extras]
20+
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
2021
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
2122
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
2223
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2324

2425
[targets]
25-
test = ["OrdinaryDiffEq", "SafeTestsets", "Test"]
26+
test = ["OrdinaryDiffEq", "SafeTestsets", "Test", "ControlSystemsBase"]

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[deps]
2+
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
23
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
34
IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
45
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"

docs/pages.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ pages = [
1111
"Magnetic Components" => "API/magnetic.md",
1212
"Mechanical Components" => "API/mechanical.md",
1313
"Thermal Components" => "API/thermal.md",
14+
"Linear Analysis" => "API/linear_analysis.md",
1415
],
1516
]

docs/src/API/linear_analysis.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Linear Analysis
2+
3+
!!! danger "Experimental"
4+
The interface described here is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
5+
6+
Linear analysis refers to the process of linearizing a nonlinear model and analysing the resulting linear dynamical system. To facilitate linear analysis, ModelingToolkitStandardLibrary provides the concept of an [`AnalysisPoint`](@ref), which can be inserted in-between two causal blocks (such as those from the `Blocks` sub module). Once a model containing analysis points is built, several operations are available:
7+
8+
- [`get_sensitivity`](@ref) get the [sensitivity function (wiki)](https://en.wikipedia.org/wiki/Sensitivity_(control_systems)), $S(s)$, as defined in the field of control theory.
9+
- [`get_comp_sensitivity`](@ref) get the complementary sensitivity function $T(s) : S(s)+T(s)=1$.
10+
- [`get_looptransfer`](@ref) get the (open) loop-transfer function where the loop starts and ends in the analysis point.
11+
- [`linearize`](@ref) can be called with two analysis points denoting the input and output of the linearized system. Parts of the model not appearing between the input and output will be removed.
12+
- [`open_loop`](@ref) return a new (nonlinear) system where the loop has been broken in the analysis point, i.e., the connection the analysis point usually implies has been removed.
13+
14+
An analysis point can be created explicitly using the constructor [`AnalysisPoint`](@ref), or automatically when connecting two causal components using `connect`:
15+
```julia
16+
connect(comp1.output, :analysis_point_name, comp2.input)
17+
```
18+
19+
Of the above mentioned functions, all except for [`open_loop`](@ref) return the output of [`ModelingToolkit.linearize`](@ref), which is
20+
```julia
21+
matrices, simplified_sys = linearize(...)
22+
# matrices = (; A, B, C, D)
23+
```
24+
i.e., `matrices` is a named tuple containing the matrices of a linear state-space system on the form
25+
```math
26+
\begin{aligned}
27+
\dot x &= Ax + Bu\\
28+
y &= Cx + Du
29+
\end{aligned}
30+
```
31+
32+
## Example
33+
The following example builds a simple closed-loop system with a plant $P$ and a controller $C$. Two analysis points are inserted, one before and one after $P$. We then derive a number of sensitivity functions and show the corresponding code using the package ControlSystemBase.jl
34+
35+
```@example LINEAR_ANALYSIS
36+
using ModelingToolkitStandardLibrary.Blocks, ModelingToolkit
37+
@named P = FirstOrder(k=1, T=1) # A first-order system with pole in -1
38+
@named C = Gain(-1) # A P controller
39+
t = ModelingToolkit.get_iv(P)
40+
eqs = [
41+
connect(P.output, :plant_output, C.input) # Connect with an automatically created analysis point called :plant_output
42+
connect(C.output, :plant_input, P.input) # Connect with an automatically created analysis point called :plant_input
43+
]
44+
sys = ODESystem(eqs, t, systems=[P,C], name=:feedback_system)
45+
46+
matrices_S = get_sensitivity(sys, :plant_input)[1] # Compute the matrices of a state-space representation of the (input)sensitivity function.
47+
matrices_T = get_comp_sensitivity(sys, :plant_input)[1]
48+
```
49+
Continued linear analysis and design can be performed using ControlSystemsBase.jl.
50+
We create `ControlSystemsBase.StateSpace` objects using
51+
```@example LINEAR_ANALYSIS
52+
using ControlSystemsBase, Plots
53+
S = ss(matrices_S...)
54+
T = ss(matrices_T...)
55+
bodeplot([S, T], lab=["S" "" "T" ""])
56+
```
57+
58+
The sensitivity functions obtained this way should be equivalent to the ones obtained with the code below
59+
60+
```@example LINEAR_ANALYSIS_CS
61+
using ControlSystemsBase
62+
P = tf(1.0, [1, 1]) |> ss
63+
C = 1 # Negative feedback assumed in ControlSystems
64+
S = sensitivity(P, C) # or feedback(1, P*C)
65+
T = comp_sensitivity(P, C) # or feedback(P*C)
66+
```
67+
68+
We may also derive the loop-transfer function $L(s) = P(s)C(s)$ using
69+
70+
```@example LINEAR_ANALYSIS
71+
matrices_L = get_looptransfer(sys, :plant_output)[1]
72+
L = ss(matrices_L...)
73+
```
74+
which is equivalent to the following with ControlSystems
75+
```@example LINEAR_ANALYSIS_CS
76+
L = P*(-C) # Add the minus sign to build the negative feedback into the controller
77+
```
78+
79+
80+
To obtain the transfer function between two analysis points, we call `linearize`
81+
```@example LINEAR_ANALYSIS
82+
matrices_P = linearize(sys, :plant_input, :plant_output)[1]
83+
```
84+
this particular transfer function should be equivalent to the linear system `P`, i.e., equivalent to this call
85+
```@example LINEAR_ANALYSIS
86+
@unpack input, output = P # To get the correct namespace
87+
linearize(P, [input.u], [output.u])[1]
88+
```
89+
90+
## Index
91+
```@index
92+
Pages = ["linear_analysis.md"]
93+
```
94+
95+
```@autodocs
96+
Modules = [ModelingToolkitStandardLibrary.Blocks]
97+
Pages = ["Blocks/analysis_points.jl"]
98+
Order = [:function, :type]
99+
Private = false
100+
```

src/Blocks/Blocks.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ export Integrator, Derivative, FirstOrder, SecondOrder, StateSpace
2626
export PI, LimPI, PID, LimPID
2727
include("continuous.jl")
2828

29+
export AnalysisPoint, expand_analysis_points, get_sensitivity, get_comp_sensitivity,
30+
get_looptransfer, open_loop
31+
include("analysis_points.jl")
32+
2933
end

0 commit comments

Comments
 (0)