Skip to content

Commit b6985e0

Browse files
authored
Merge pull request #137 from baggepinnen/mv_lin
make linearize work for multiple analysis points
2 parents 4544313 + a8a4e1e commit b6985e0

File tree

2 files changed

+50
-39
lines changed

2 files changed

+50
-39
lines changed

src/Blocks/analysis_points.jl

+41-38
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,13 @@ function namespaced_ap_match(x, ns, ap_names0, loop_openings)
183183
end
184184
end
185185

186-
function get_perturbation_var(x::Num)
186+
function get_perturbation_var(x::Num, prefix = "d")
187187
@variables d(t) = 0
188-
@set! d.val.f.name = Symbol("d_$(x)")
188+
@set! d.val.f.name = Symbol("$(prefix)_$(x)")
189189
d
190190
end
191-
function get_perturbation_var(x)
192-
get_perturbation_var.(x)
191+
function get_perturbation_var(x, args...)
192+
get_perturbation_var.(x, args...)
193193
end
194194

195195
function _check_and_sort!(ap_names, aps, namespaces, multiplicities)
@@ -367,49 +367,52 @@ function open_loop(sys, ap_name::Symbol; ground_input = false, kwargs...)
367367
end
368368

369369
function ModelingToolkit.linearization_function(sys::ModelingToolkit.AbstractSystem,
370-
input_name::Symbol, output_name::Symbol;
370+
input_name::SymOrVec, output_name::SymOrVec;
371371
loop_openings = nothing,
372372
kwargs...)
373373
t = get_iv(sys)
374374
@variables u(t)=0 [input = true]
375375
@variables y(t)=0 [output = true]
376-
find_input = namespaced_ap_match(input_name, loop_openings)
377-
find_output = namespaced_ap_match(output_name, loop_openings)
378-
find = (x, ns) -> find_input(x, ns) || find_output(x, ns)
376+
find = namespaced_ap_match([input_name; output_name], loop_openings)
379377

380-
namespace_u = Ref{Union{Nothing, Symbol}}(nothing)
381-
namespace_y = Ref{Union{Nothing, Symbol}}(nothing)
382-
apr_u = Ref{Union{Nothing, AnalysisPoint}}(nothing)
383-
apr_y = Ref{Union{Nothing, AnalysisPoint}}(nothing)
384-
replace = let u = u, y = y, namespace_u = namespace_u, apr_u = apr_u,
385-
namespace_y = namespace_y, apr_y = apr_y
378+
u = []
379+
y = []
380+
namespace_u = []
381+
namespace_y = []
382+
aps_u = []
383+
aps_y = []
384+
multiplicities_u = Int[]
385+
multiplicities_y = Int[]
386386

387-
function (ap, ns)
388-
if namespaced_ap_match(ap, ns, input_name, nothing)
389-
namespace_u[] = ns # Save the namespace to make it available for renamespace below
390-
apr_u[] = ap
391-
[ap_var(ap.out) ~ ap_var(ap.in) + u], u
392-
#input.in.u ~ 0] # We only need to ground one of the ends, hence not including this equation
393-
elseif namespaced_ap_match(ap, ns, output_name, nothing)
394-
namespace_y[] = ns # Save the namespace to make it available for renamespace below
395-
apr_y[] = ap
396-
[ap_var(ap.in) ~ y
397-
ap_var(ap.out) ~ ap_var(ap.in)], y
398-
else # loop opening
399-
[ap_var(ap.out) ~ 0], []
400-
end
387+
replace = function (ap, ns)
388+
if namespaced_ap_match(ap, ns, input_name, nothing)
389+
push!(namespace_u, ns) # Save the namespace to make it available for renamespace below
390+
push!(aps_u, ap)
391+
ui = get_perturbation_var(ap_var(ap.out), "u")
392+
push!(multiplicities_u, length(ui)) # one ap may yield several new vars
393+
append!(u, ui)
394+
[ap_var(ap.out) .~ ap_var(ap.in) + ui;], ui
395+
#input.in.u ~ 0] # We only need to ground one of the ends, hence not including this equation
396+
elseif namespaced_ap_match(ap, ns, output_name, nothing)
397+
push!(namespace_y, ns) # Save the namespace to make it available for renamespace below
398+
push!(aps_y, ap)
399+
yi = get_perturbation_var(ap_var(ap.in), "y")
400+
push!(multiplicities_y, length(yi))
401+
append!(y, yi)
402+
[ap_var(ap.in) .~ yi;
403+
ap_var(ap.out) .~ ap_var(ap.in)], yi
404+
else # loop opening
405+
[ap_var(ap.out) .~ 0;], []
401406
end
402407
end
408+
403409
sys = expand_connections(sys, find, replace)
404-
(ap = apr_u[]) === nothing && error("Did not find analysis point $input_name")
405-
(ap = apr_y[]) === nothing && error("Did not find analysis point $output_name")
406-
if (ns = namespace_u[]) !== nothing
407-
u = ModelingToolkit.renamespace(ns, u)
408-
end
409-
if (ns = namespace_y[]) !== nothing
410-
y = ModelingToolkit.renamespace(ns, y)
411-
end
412-
ModelingToolkit.linearization_function(sys, [u], [y]; kwargs...)
410+
permutation_u = _check_and_sort!(input_name, aps_u, namespace_u, multiplicities_u)
411+
permutation_y = _check_and_sort!(output_name, aps_y, namespace_y, multiplicities_y)
412+
413+
yn = ModelingToolkit.renamespace.(namespace_u, y) # permutation applied in _check_and_sort
414+
un = ModelingToolkit.renamespace.(namespace_y, u)
415+
ModelingToolkit.linearization_function(sys, un, yn; kwargs...)
413416
end
414417

415418
# Add a method to get_sensitivity that accepts the name of an AnalysisPoint
@@ -438,7 +441,7 @@ end
438441
439442
Linearize a system between two analysis points. To get a loop-transfer function, see [`get_looptransfer`](@ref)
440443
"""
441-
function ModelingToolkit.linearize(sys, input_name::Symbol, output_name::Symbol;
444+
function ModelingToolkit.linearize(sys, input_name::SymOrVec, output_name::SymOrVec;
442445
loop_openings = nothing, kwargs...)
443446
lin_fun, ssys = linearization_function(sys, input_name, output_name; loop_openings,
444447
kwargs...)

test/Blocks/test_analysis_points.jl

+9-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ D = [0.0 0.0; 0.0 0.0]
273273
@named K = Blocks.StateSpace(A, B, C, D)
274274
Kss = CS.ss(A, B, C, D)
275275

276-
eqs = [connect(P.output, K.input)
276+
eqs = [connect(P.output, :plant_output, K.input)
277277
connect(K.output, :plant_input, P.input)]
278278
sys = ODESystem(eqs, t, systems = [P, K], name = :hej)
279279

@@ -293,6 +293,10 @@ matrices, _ = Blocks.get_looptransfer(sys, :plant_input)
293293
L = Kss * Pss
294294
@test CS.tf(CS.ss(matrices...)) CS.tf(L)
295295

296+
matrices, _ = linearize(sys, :plant_input, :plant_output)
297+
G = CS.feedback(Pss, Kss, pos_feedback = true)
298+
@test CS.tf(CS.ss(matrices...)) CS.tf(G)
299+
296300
## Multiple analysis points ====================================================
297301
@named P = FirstOrder(k = 1, T = 1)
298302
@named C = Gain(1)
@@ -353,3 +357,7 @@ L = CS.ss(matrices...) |> sminreal
353357
@test tf(L[2, 2]) tf(0)
354358
@test sminreal(L[1, 2]) ss(-1)
355359
@test tf(L[2, 1]) tf(Ps)
360+
361+
matrices, _ = linearize(sys_outer, [:inner_plant_input], [:inner_plant_output])
362+
G = CS.ss(matrices...) |> sminreal
363+
@test tf(G) tf(CS.feedback(Ps, Cs))

0 commit comments

Comments
 (0)