Skip to content

Commit 233a743

Browse files
committed
add symbolic linearization function
1 parent db52605 commit 233a743

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

src/systems/abstractsystem.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,28 @@ function linearization_function(sys::AbstractSystem, inputs,
12871287
return lin_fun, sys
12881288
end
12891289

1290+
function linearize_symbolic(sys::AbstractSystem, inputs,
1291+
outputs; simplify = false,
1292+
kwargs...)
1293+
sys, diff_idxs, alge_idxs, input_idxs = io_preprocessing(sys, inputs, outputs; simplify,
1294+
kwargs...)
1295+
sts = states(sys)
1296+
t = get_iv(sys)
1297+
p = parameters(sys)
1298+
1299+
fun = generate_function(sys, sts, p; expression = Val{false})[1]
1300+
dx = fun(sts, p, t)
1301+
1302+
A = Symbolics.jacobian(dx, sts)
1303+
B = Symbolics.jacobian(dx, inputs)
1304+
1305+
h = build_explicit_observed_function(sys, outputs)
1306+
y = h(sts, p, t)
1307+
C = Symbolics.jacobian(y, sts)
1308+
D = Symbolics.jacobian(y, inputs)
1309+
(; A, B, C, D), sys
1310+
end
1311+
12901312
function markio!(state, orig_inputs, inputs, outputs; check = true)
12911313
fullvars = state.fullvars
12921314
inputset = Dict{Any, Bool}(i => false for i in inputs)

test/linearize.jl

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,22 @@ connections = [f.y ~ c.r # filtered reference to controller reference
6868

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

71-
lsys, ssys = linearize(cl, [f.u], [p.x])
71+
lsys0, ssys = linearize(cl, [f.u], [p.x])
7272
desired_order = [f.x, p.x]
73-
lsys = ModelingToolkit.reorder_states(lsys, states(ssys), desired_order)
73+
lsys = ModelingToolkit.reorder_states(lsys0, states(ssys), desired_order)
7474

7575
@test lsys.A == [-2 0; 1 -2]
7676
@test lsys.B == reshape([1, 0], 2, 1)
7777
@test lsys.C == [0 1]
7878
@test lsys.D[] == 0
7979

80+
## Symbolic linearization
81+
lsyss, _ = ModelingToolkit.linearize_symbolic(cl, [f.u], [p.x])
82+
83+
@test substitute(lsyss.A, ModelingToolkit.defaults(cl)) == lsys.A
84+
@test substitute(lsyss.B, ModelingToolkit.defaults(cl)) == lsys.B
85+
@test substitute(lsyss.C, ModelingToolkit.defaults(cl)) == lsys.C
86+
@test substitute(lsyss.D, ModelingToolkit.defaults(cl)) == lsys.D
8087
##
8188
using ModelingToolkitStandardLibrary.Blocks: LimPID
8289
k = 400
@@ -86,16 +93,24 @@ Nd = 10
8693
@named pid = LimPID(; k, Ti, Td, Nd)
8794

8895
@unpack reference, measurement, ctr_output = pid
89-
lsys, ssys = linearize(pid, [reference.u, measurement.u], [ctr_output.u])
96+
lsys0, ssys = linearize(pid, [reference.u, measurement.u], [ctr_output.u])
9097
@unpack int, der = pid
9198
desired_order = [int.x, der.x]
92-
lsys = ModelingToolkit.reorder_states(lsys, states(ssys), desired_order)
99+
lsys = ModelingToolkit.reorder_states(lsys0, states(ssys), desired_order)
93100

94101
@test lsys.A == [0 0; 0 -10]
95102
@test lsys.B == [2 -2; 10 -10]
96103
@test lsys.C == [400 -4000]
97104
@test lsys.D == [4400 -4400]
98105

106+
lsyss, _ = ModelingToolkit.linearize_symbolic(pid, [reference.u, measurement.u],
107+
[ctr_output.u])
108+
109+
@test substitute(lsyss.A, ModelingToolkit.defaults(pid)) == lsys.A
110+
@test substitute(lsyss.B, ModelingToolkit.defaults(pid)) == lsys.B
111+
@test substitute(lsyss.C, ModelingToolkit.defaults(pid)) == lsys.C
112+
@test substitute(lsyss.D, ModelingToolkit.defaults(pid)) == lsys.D
113+
99114
# Test with the reverse desired state order as well to verify that similarity transform and reoreder_states really works
100115
lsys = ModelingToolkit.reorder_states(lsys, states(ssys), reverse(desired_order))
101116

@@ -151,6 +166,12 @@ lsys, ssys = linearize(sat, [u], [y])
151166
@test isempty(lsys.C)
152167
@test lsys.D[] == 1
153168

169+
@test_skip lsyss, _ = ModelingToolkit.linearize_symbolic(sat, [u], [y]) # Code gen replaces ifelse with if statements causing symbolic evaluation to fail
170+
# @test substitute(lsyss.A, ModelingToolkit.defaults(sat)) == lsys.A
171+
# @test substitute(lsyss.B, ModelingToolkit.defaults(sat)) == lsys.B
172+
# @test substitute(lsyss.C, ModelingToolkit.defaults(sat)) == lsys.C
173+
# @test substitute(lsyss.D, ModelingToolkit.defaults(sat)) == lsys.D
174+
154175
# outside the linear region the derivative is 0
155176
lsys, ssys = linearize(sat, [u], [y]; op = Dict(u => 2))
156177
@test isempty(lsys.A) # there are no differential variables in this system

0 commit comments

Comments
 (0)