Skip to content

Commit 75d5545

Browse files
committed
add option to simplify causality in a different way
1 parent dafbbfc commit 75d5545

File tree

2 files changed

+61
-16
lines changed

2 files changed

+61
-16
lines changed

src/ode_system.jl

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,19 +154,22 @@ end
154154

155155

156156
"""
157-
RobustAndOptimalControl.named_ss(sys::ModelingToolkit.AbstractTimeDependentSystem, inputs, outputs; kwargs...)
157+
RobustAndOptimalControl.named_ss(sys::ModelingToolkit.AbstractTimeDependentSystem, inputs, outputs; descriptor=true, kwargs...)
158158
159159
Convert an `ODESystem` to a `NamedStateSpace` using linearization. `inputs, outputs` are vectors of variables determining the inputs and outputs respectively. See docstring of `ModelingToolkit.linearize` for more info on `kwargs`.
160160
161-
This method automatically converts systems that MTK has failed to produce a proper form for into a proper linear statespace system. Learn more about how that is done here:
161+
If `descriptor = true` (default), this method automatically converts systems that MTK has failed to produce a proper form for into a proper linear statespace system using the method described here:
162162
https://juliacontrol.github.io/ControlSystemsMTK.jl/dev/#Internals:-Transformation-of-non-proper-models-to-proper-statespace-form
163+
If `descriptor = false`, the system is instead converted to a statespace realization using `sys[:,uinds] + sys[:,duinds]*tf('s')`, which tends to result in a larger realization on which the user may want to call `minreal(sys, tol)` with a carefully selected tolerance.
164+
163165
164166
See also [`ModelingToolkit.linearize`](@ref) which is the lower-level function called internally. The functions [`get_named_sensitivity`](@ref), [`get_named_comp_sensitivity`](@ref), [`get_named_looptransfer`](@ref) similarily provide convenient ways to compute sensitivity functions while retaining signal names in the same way as `named_ss`. The corresponding lower-level functions `get_sensitivity`, `get_comp_sensitivity` and `get_looptransfer` are available in ModelingToolkitStandardLibrary.Blocks and are documented in [MTKstdlib: Linear analysis](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/linear_analysis/).
165167
"""
166168
function RobustAndOptimalControl.named_ss(
167169
sys::ModelingToolkit.AbstractTimeDependentSystem,
168170
inputs,
169171
outputs;
172+
descriptor = true,
170173
kwargs...,
171174
)
172175

@@ -206,7 +209,7 @@ function RobustAndOptimalControl.named_ss(
206209
# This indicates that input derivatives are present
207210
duinds = findall(any(!iszero, eachcol(matrices.B[:, nu+1:end]))) .+ nu
208211
u2du = (1:nu) .=> duinds # This maps inputs to their derivatives
209-
lsys = causal_simplification(matrices, u2du)
212+
lsys = causal_simplification(matrices, u2du; descriptor)
210213
else
211214
lsys = ss(matrices...)
212215
end
@@ -219,26 +222,33 @@ function RobustAndOptimalControl.named_ss(
219222
)
220223
end
221224

222-
function causal_simplification(sys, u2duinds::Vector{Pair{Int, Int}})
225+
function causal_simplification(sys, u2duinds::Vector{Pair{Int, Int}}; descriptor=true)
226+
fm(x) = convert(Matrix{Float64}, x)
223227
nx = size(sys.A, 1)
224228
ny = size(sys.C, 1)
225229
ndu = length(u2duinds)
226230
nu = size(sys.B, 2) - ndu
227231
u_with_du_inds = first.(u2duinds)
228232
duinds = last.(u2duinds)
229-
= sys.B[:, duinds]
230233
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)
234+
= sys.B[:, duinds]
235+
D = sys.D[:, 1:nu]
236+
= sys.D[:, duinds]
237+
iszero(fm(D̄)) || error("Nonzero feedthrough matrix from input derivative not supported")
238+
if descriptor
239+
Iu = u_with_du_inds .== (1:nu)'
240+
E = [I(nx) -B̄; zeros(ndu, nx+ndu)]
235241

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+
Ae = cat(sys.A, -I(ndu), dims=(1,2))
243+
# Ae[1:nx, nx+1:end] .= B
244+
Be = [B; Iu]
245+
Ce = [fm(sys.C) zeros(ny, ndu)]
246+
De = fm(D)
247+
dsys = dss(Ae, E, Be, Ce, De)
248+
return ss(RobustAndOptimalControl.DescriptorSystems.dss2ss(dsys)[1])
249+
else
250+
ss(sys.A, B, sys.C, D) + ss(sys.A, B̄, sys.C, D̄)*tf('s')
251+
end
242252
end
243253

244254
for f in [:sensitivity, :comp_sensitivity, :looptransfer]

test/test_ODESystem.jl

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ using ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition
295295
using Test
296296

297297
using ControlSystemsMTK
298-
using ControlSystemsMTK.ControlSystemsBase: sminreal, minreal, poles
298+
using ControlSystemsMTK.ControlSystemsBase: sminreal, minreal, poles, ss, tf
299299
connect = ModelingToolkit.connect
300300

301301
@independent_variables t
@@ -348,3 +348,38 @@ op2[cart.f] = 0
348348
@test G.nx == 4
349349
@test G.nu == length(lin_inputs)
350350
@test G.ny == length(lin_outputs)
351+
352+
## Test difficult `named_ss` simplification
353+
using ControlSystemsMTK, ControlSystemsBase, RobustAndOptimalControl
354+
lsys = (A = [0.0 2.778983834717109e8 1.4122312296634873e6 0.0; 0.0 0.0 0.0 0.037848975765016724; 0.0 24.837541148074962 0.12622006230897712 0.0; -0.0 -4.620724819774693 -0.023481719514324866 -0.6841991610512456], B = [-5.042589978197361e8 0.0; -0.0 0.0; -45.068824982602656 -0.0; 8.384511049369085 54.98555939873381], C = [0.0 0.0 0.954929658551372 0.0], D = [0.0 0.0])
355+
356+
# lsys = (A = [-0.0075449237853825925 1.6716817118020731e-6 0.0; 1864.7356343162514 -0.4131578457122937 0.0; 0.011864343330426718 -2.6287085638214332e-6 0.0], B = [0.0 0.0; 0.0 52566.418015009294; 0.0 0.3284546792274811], C = [1.4683007399899215e8 0.0 0.0], D = [-9.157636303058283e7 0.0])
357+
358+
G = ControlSystemsMTK.causal_simplification(lsys, [1=>2])
359+
G2 = ControlSystemsMTK.causal_simplification(lsys, [1=>2], descriptor=false)
360+
G2 = minreal(G2, 1e-12)
361+
362+
@test dcgain(G, 1e-5)[] dcgain(G2, 1e-5)[] rtol=1e-3
363+
@test freqresp(G, 1)[] freqresp(G2, 1)[]
364+
@test freqresp(G, 10)[] freqresp(G2, 10)[]
365+
366+
z = 0.462726166562343204837317130554462562
367+
368+
@test minimum(abs, tzeros(G) .- z) < sqrt(eps())
369+
@test minimum(abs, tzeros(G2) .- z) < sqrt(eps())
370+
371+
# using GenericSchur
372+
373+
Gb = balance_statespace(G)[1]
374+
Gb = minreal(Gb, 1e-8)
375+
@test Gb.nx == 2
376+
@test minimum(abs, tzeros(Gb) .- z) < sqrt(eps())
377+
378+
w = exp10.(LinRange(-12, 2, 2000))
379+
ControlSystemsBase.bodeplot([G, G2, minreal(G, 1e-8)], w)
380+
381+
382+
##
383+
384+
# S = schur(A,B)
385+
# V = eigvecs(S)

0 commit comments

Comments
 (0)