Skip to content

Commit c23b553

Browse files
authored
factor out causal simplification (#71)
* factor out causal simplification * add tests * import fix * more import
1 parent 60ae91e commit c23b553

File tree

4 files changed

+102
-27
lines changed

4 files changed

+102
-27
lines changed

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ DataInterpolations = "3, 4, 5, 6, 7"
2020
ModelingToolkit = "9.61"
2121
ModelingToolkitStandardLibrary = "2"
2222
MonteCarloMeasurements = "1.1"
23-
OrdinaryDiffEq = "6"
2423
RobustAndOptimalControl = "0.4.14"
2524
Symbolics = "5, 6"
2625
UnPack = "1"
2726
julia = "1.6"
2827

2928
[extras]
3029
ControlSystems = "a6e380b2-a6ca-5380-bf3e-84a91bcd477e"
31-
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
30+
OrdinaryDiffEqNonlinearSolve = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8"
31+
OrdinaryDiffEqRosenbrock = "43230ef6-c299-4910-a778-202eb28ce4ce"
3232
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3333

3434
[targets]
35-
test = ["Test", "ControlSystems", "OrdinaryDiffEq"]
35+
test = ["Test", "ControlSystems", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqNonlinearSolve"]

src/ode_system.jl

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -199,29 +199,14 @@ function RobustAndOptimalControl.named_ss(
199199
out
200200
end
201201
end
202-
ny = length(outputs)
203202
matrices, ssys = ModelingToolkit.linearize(sys, inputs, outputs; kwargs...)
204203
symstr(x) = Symbol(x isa AnalysisPoint ? x.name : string(x))
205204
unames = symstr.(inputs)
206-
fm(x) = convert(Matrix{Float64}, x)
207205
if nu > 0 && size(matrices.B, 2) == 2nu
208-
nx = size(matrices.A, 1)
209-
# This indicates that input derivatives are present
210-
duinds = findall(any(!iszero, eachcol(matrices.B[:, nu+1:end])))
211-
= matrices.B[:, duinds .+ nu]
212-
ndu = length(duinds)
213-
B = matrices.B[:, 1:nu]
214-
Iu = duinds .== (1:nu)'
215-
E = [I(nx) -B̄; zeros(ndu, nx+ndu)]
216-
217-
Ae = cat(matrices.A, -I(ndu), dims=(1,2))
218-
Be = [B; Iu]
219-
Ce = [fm(matrices.C) zeros(ny, ndu)]
220-
De = fm(matrices.D[:, 1:nu])
221-
dsys = dss(Ae, E, Be, Ce, De)
222-
lsys = ss(RobustAndOptimalControl.DescriptorSystems.dss2ss(dsys)[1])
223-
# unames = [unames; Symbol.("der_" .* string.(unames))]
224-
# sys = ss(matrices...)
206+
# This indicates that input derivatives are present
207+
duinds = findall(any(!iszero, eachcol(matrices.B[:, nu+1:end]))) .+ nu
208+
u2du = (1:nu) .=> duinds # This maps inputs to their derivatives
209+
lsys = causal_simplification(matrices, u2du)
225210
else
226211
lsys = ss(matrices...)
227212
end
@@ -234,6 +219,28 @@ function RobustAndOptimalControl.named_ss(
234219
)
235220
end
236221

222+
function causal_simplification(sys, u2duinds::Vector{Pair{Int, Int}})
223+
nx = size(sys.A, 1)
224+
ny = size(sys.C, 1)
225+
ndu = length(u2duinds)
226+
nu = size(sys.B, 2) - ndu
227+
u_with_du_inds = first.(u2duinds)
228+
duinds = last.(u2duinds)
229+
= sys.B[:, duinds]
230+
B = sys.B[:, 1:nu]
231+
Iu = u_with_du_inds .== (1:nu)'
232+
E = [I(nx) -B̄; zeros(ndu, nx+ndu)]
233+
234+
fm(x) = convert(Matrix{Float64}, x)
235+
236+
Ae = cat(sys.A, -I(ndu), dims=(1,2))
237+
Be = [B; Iu]
238+
Ce = [fm(sys.C) zeros(ny, ndu)]
239+
De = fm(sys.D[:, 1:nu])
240+
dsys = dss(Ae, E, Be, Ce, De)
241+
ss(RobustAndOptimalControl.DescriptorSystems.dss2ss(dsys)[1])
242+
end
243+
237244
for f in [:sensitivity, :comp_sensitivity, :looptransfer]
238245
fnn = Symbol("get_named_$f")
239246
fn = Symbol("get_$f")

test/test_ODESystem.jl

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using ControlSystemsMTK,
2-
ControlSystemsBase, ModelingToolkit, OrdinaryDiffEq, RobustAndOptimalControl
2+
ControlSystemsBase, ModelingToolkit, RobustAndOptimalControl
33
import ModelingToolkitStandardLibrary.Blocks as Blocks
4+
using OrdinaryDiffEqNonlinearSolve, OrdinaryDiffEqRosenbrock
45
conn = ModelingToolkit.connect
56
connect = ModelingToolkit.connect
67
## Test SISO (single input, single output) system
@@ -165,7 +166,7 @@ x0 = Pair[
165166
p = Pair[]
166167

167168
prob = ODEProblem(simplified_sys, x0, (0.0, 20.0), p, jac = true)
168-
sol = solve(prob, OrdinaryDiffEq.Rodas5(), saveat = 0:0.01:20)
169+
sol = solve(prob, Rodas5(), saveat = 0:0.01:20)
169170
if isinteractive()
170171
@show sol.retcode
171172
plot(sol, layout = length(unknowns(simplified_sys)) + 1)
@@ -177,7 +178,7 @@ if isinteractive()
177178
end
178179

179180
## Double-mass model in MTK
180-
using ModelingToolkit, OrdinaryDiffEq, LinearAlgebra
181+
using ModelingToolkit, OrdinaryDiffEqRosenbrock, LinearAlgebra
181182
using ModelingToolkitStandardLibrary.Mechanical.Rotational
182183
using ModelingToolkitStandardLibrary.Blocks: Sine
183184
using ModelingToolkit: connect
@@ -280,4 +281,70 @@ Sn = get_named_sensitivity(sys_outer, [sys_outer.inner.plant_input, sys_outer.in
280281
P = named_ss(ssrand(1,1,1), u=:jörgen, y=:solis)
281282
@named Pode = ODESystem(P)
282283
ModelingToolkit.isconnector(Pode.jörgen)
283-
ModelingToolkit.isconnector(Pode.solis)
284+
ModelingToolkit.isconnector(Pode.solis)
285+
286+
287+
288+
## Test causal simplification
289+
using LinearAlgebra
290+
using ModelingToolkit
291+
using ModelingToolkitStandardLibrary
292+
using ModelingToolkitStandardLibrary.Blocks
293+
using ModelingToolkitStandardLibrary.Mechanical.MultiBody2D
294+
using ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition
295+
using Test
296+
297+
using ControlSystemsMTK
298+
using ControlSystemsMTK.ControlSystemsBase: sminreal, minreal, poles
299+
connect = ModelingToolkit.connect
300+
301+
@independent_variables t
302+
D = Differential(t)
303+
304+
@named link1 = Link(; m = 0.2, l = 10, I = 1, g = -9.807)
305+
@named cart = TranslationalPosition.Mass(; m = 1, s = 0)
306+
@named fixed = TranslationalPosition.Fixed()
307+
@named force = Force(use_support = false)
308+
309+
eqs = [connect(link1.TX1, cart.flange)
310+
connect(cart.flange, force.flange)
311+
connect(link1.TY1, fixed.flange)]
312+
313+
@named model = ODESystem(eqs, t, [], []; systems = [link1, cart, force, fixed])
314+
lin_outputs = [cart.s, cart.v, link1.A, link1.dA]
315+
lin_inputs = [force.f.u]
316+
317+
# => nothing to remove extra defaults
318+
op = Dict(cart.s => 10, cart.v => 0, link1.A => -pi/2, link1.dA => 0, force.f.u => 0, link1.x1 => nothing, link1.y1 => nothing, link1.x2 => nothing, link1.x_cm => nothing)
319+
G = named_ss(model, lin_inputs, lin_outputs; allow_symbolic = true, op,
320+
allow_input_derivatives = true, zero_dummy_der = true)
321+
G = sminreal(G)
322+
@info "minreal"
323+
G = minreal(G)
324+
@info "poles"
325+
ps = poles(G)
326+
327+
@test minimum(abs, ps) < 1e-6
328+
@test minimum(abs, complex(0, 1.3777260367206716) .- ps) < 1e-10
329+
330+
lsys, syss = linearize(model, lin_inputs, lin_outputs, op = op,
331+
allow_input_derivatives = true)
332+
lsyss, sysss = ModelingToolkit.linearize_symbolic(model, lin_inputs, lin_outputs;
333+
allow_input_derivatives = true)
334+
335+
dummyder = setdiff(unknowns(sysss), unknowns(model))
336+
# op2 = merge(ModelingToolkit.guesses(model), op, Dict(x => 0.0 for x in dummyder))
337+
op2 = merge(ModelingToolkit.defaults(syss), op)
338+
op2[link1.fy1] = -op2[link1.g] * op2[link1.m]
339+
op2[cart.f] = 0
340+
341+
@test substitute(lsyss.A, op2) lsys.A
342+
# We cannot pivot symbolically, so the part where a linear solve is required
343+
# is not reliable.
344+
@test substitute(lsyss.B, op2)[1:6, 1] lsys.B[1:6, 1]
345+
@test substitute(lsyss.C, op2) lsys.C
346+
@test substitute(lsyss.D, op2) lsys.D
347+
348+
@test G.nx == 4
349+
@test G.nu == length(lin_inputs)
350+
@test G.ny == length(lin_outputs)

test/test_batchlin.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using ControlSystemsMTK, ModelingToolkit, RobustAndOptimalControl, MonteCarloMeasurements
2+
using OrdinaryDiffEqNonlinearSolve, OrdinaryDiffEqRosenbrock
23
using ModelingToolkit: getdefault
34
unsafe_comparisons(true)
45

@@ -44,7 +45,7 @@ using DataInterpolations
4445
# code = SymbolicControlSystems.print_c_array(stdout, Ps, xs, "gain_scheduled_controller", struct_name="hej", struct_type="kaj")
4546

4647
## Simulate
47-
using OrdinaryDiffEq, ControlSystemsBase
48+
using OrdinaryDiffEqRosenbrock, ControlSystemsBase
4849
import ModelingToolkitStandardLibrary.Blocks
4950

5051
@named fb = Blocks.Add(k2=-1)

0 commit comments

Comments
 (0)