Skip to content

Commit 12bc1ba

Browse files
fix: remove ambiguities in namespacing of analysis points
1 parent 0a46cfd commit 12bc1ba

File tree

3 files changed

+47
-61
lines changed

3 files changed

+47
-61
lines changed

src/systems/analysis_points.jl

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,10 @@ function modify_nested_subsystem(
314314
return fn(root)
315315
end
316316
# ignore the name of the root
317-
if nameof(root) == hierarchy[1]
318-
hierarchy = @view hierarchy[2:end]
317+
if nameof(root) != hierarchy[1]
318+
error("The name of the root system $(nameof(root)) must be included in the name passed to `modify_nested_subsystem`")
319319
end
320+
hierarchy = @view hierarchy[2:end]
320321

321322
# recursive helper function which does the searching and modification
322323
function _helper(sys::AbstractSystem, i::Int)
@@ -722,13 +723,14 @@ end
722723
723724
A utility function to get the "canonical" form of a list of analysis points. Always returns
724725
a list of values. Any value that cannot be turned into an `AnalysisPoint` (i.e. isn't
725-
already an `AnalysisPoint` or `Symbol`) is simply wrapped in an array.
726+
already an `AnalysisPoint` or `Symbol`) is simply wrapped in an array. `Symbol` names of
727+
`AnalysisPoint`s are namespaced with `sys`.
726728
"""
727-
canonicalize_ap(ap::Symbol) = [AnalysisPoint(ap)]
728-
canonicalize_ap(ap::AnalysisPoint) = [ap]
729-
canonicalize_ap(ap) = [ap]
730-
function canonicalize_ap(aps::Vector)
731-
mapreduce(canonicalize_ap, vcat, aps; init = [])
729+
canonicalize_ap(sys::AbstractSystem, ap::Symbol) = [AnalysisPoint(renamespace(sys, ap))]
730+
canonicalize_ap(sys::AbstractSystem, ap::AnalysisPoint) = [ap]
731+
canonicalize_ap(sys::AbstractSystem, ap) = [ap]
732+
function canonicalize_ap(sys::AbstractSystem, aps::Vector)
733+
mapreduce(Base.Fix1(canonicalize_ap, sys), vcat, aps; init = [])
732734
end
733735

734736
"""
@@ -737,7 +739,7 @@ end
737739
Given a list of analysis points, break the connection for each and set the output to zero.
738740
"""
739741
function handle_loop_openings(sys::AbstractSystem, aps)
740-
for ap in canonicalize_ap(aps)
742+
for ap in canonicalize_ap(sys, aps)
741743
sys, (outvar,) = apply_transformation(Break(ap, true), sys)
742744
if Symbolics.isarraysymbolic(outvar)
743745
push!(get_eqs(sys), outvar ~ zeros(size(outvar)))
@@ -776,7 +778,7 @@ All other keyword arguments are forwarded to `linearization_function`.
776778
function get_linear_analysis_function(
777779
sys::AbstractSystem, transform, aps; system_modifier = identity, loop_openings = [], kwargs...)
778780
sys = handle_loop_openings(sys, loop_openings)
779-
aps = canonicalize_ap(aps)
781+
aps = canonicalize_ap(sys, aps)
780782
dus = []
781783
us = []
782784
for ap in aps
@@ -860,9 +862,7 @@ result of `apply_transformation`.
860862
with any required further modifications peformed.
861863
"""
862864
function open_loop(sys, ap::Union{Symbol, AnalysisPoint}; system_modifier = identity)
863-
if ap isa Symbol
864-
ap = AnalysisPoint(ap)
865-
end
865+
ap = only(canonicalize_ap(sys, ap))
866866
tf = LoopTransferTransform(ap)
867867
sys, vars = apply_transformation(tf, sys)
868868
return system_modifier(sys), vars
@@ -871,9 +871,9 @@ end
871871
function linearization_function(sys::AbstractSystem,
872872
inputs::Union{Symbol, Vector{Symbol}, AnalysisPoint, Vector{AnalysisPoint}},
873873
outputs; loop_openings = [], system_modifier = identity, kwargs...)
874-
loop_openings = Set(map(nameof, canonicalize_ap(loop_openings)))
875-
inputs = canonicalize_ap(inputs)
876-
outputs = canonicalize_ap(outputs)
874+
loop_openings = Set(map(nameof, canonicalize_ap(sys, loop_openings)))
875+
inputs = canonicalize_ap(sys, inputs)
876+
outputs = canonicalize_ap(sys, outputs)
877877

878878
input_vars = []
879879
for input in inputs
@@ -897,7 +897,7 @@ function linearization_function(sys::AbstractSystem,
897897
push!(output_vars, output_var)
898898
end
899899

900-
sys = handle_loop_openings(sys, collect(loop_openings))
900+
sys = handle_loop_openings(sys, map(AnalysisPoint, collect(loop_openings)))
901901

902902
return linearization_function(system_modifier(sys), input_vars, output_vars; kwargs...)
903903
end

test/analysis_points.jl

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -69,26 +69,21 @@ sys = ODESystem(eqs, t, systems = [P, C], name = :hej)
6969
@test norm(sol.u[end]) < 1e-6 # This fails without the feedback through C
7070
end
7171

72-
@testset "get_sensitivity - $name" for (name, sys, ap) in [
72+
test_cases = [
7373
("inner", sys, sys.plant_input),
7474
("nested", nested_sys, nested_sys.hej.plant_input),
75-
("inner - nonamespace", sys, :plant_input),
76-
("inner - Symbol", sys, nameof(sys.plant_input)),
77-
("nested - Symbol", nested_sys, nameof(nested_sys.hej.plant_input))
75+
("inner - Symbol", sys, :plant_input),
76+
("nested - Symbol", nested_sys, nameof(sys.plant_input))
7877
]
78+
79+
@testset "get_sensitivity - $name" for (name, sys, ap) in test_cases
7980
matrices, _ = get_sensitivity(sys, ap)
8081
@test matrices.A[] == -2
8182
@test matrices.B[] * matrices.C[] == -1 # either one negative
8283
@test matrices.D[] == 1
8384
end
8485

85-
@testset "get_comp_sensitivity - $name" for (name, sys, ap) in [
86-
("inner", sys, sys.plant_input),
87-
("nested", nested_sys, nested_sys.hej.plant_input),
88-
("inner - nonamespace", sys, :plant_input),
89-
("inner - Symbol", sys, nameof(sys.plant_input)),
90-
("nested - Symbol", nested_sys, nameof(nested_sys.hej.plant_input))
91-
]
86+
@testset "get_comp_sensitivity - $name" for (name, sys, ap) in test_cases
9287
matrices, _ = get_comp_sensitivity(sys, ap)
9388
@test matrices.A[] == -2
9489
@test matrices.B[] * matrices.C[] == 1 # both positive or negative
@@ -104,13 +99,7 @@ S = sensitivity(P, C) # or feedback(1, P*C)
10499
T = comp_sensitivity(P, C) # or feedback(P*C)
105100
=#
106101

107-
@testset "get_looptransfer - $name" for (name, sys, ap) in [
108-
("inner", sys, sys.plant_input),
109-
("nested", nested_sys, nested_sys.hej.plant_input),
110-
("inner - nonamespace", sys, :plant_input),
111-
("inner - Symbol", sys, nameof(sys.plant_input)),
112-
("nested - Symbol", nested_sys, nameof(nested_sys.hej.plant_input))
113-
]
102+
@testset "get_looptransfer - $name" for (name, sys, ap) in test_cases
114103
matrices, _ = get_looptransfer(sys, ap)
115104
@test matrices.A[] == -1
116105
@test matrices.B[] * matrices.C[] == -1 # either one negative
@@ -125,13 +114,7 @@ C = -1
125114
L = P*C
126115
=#
127116

128-
@testset "open_loop - $name" for (name, sys, ap) in [
129-
("inner", sys, sys.plant_input),
130-
("nested", nested_sys, nested_sys.hej.plant_input),
131-
("inner - nonamespace", sys, :plant_input),
132-
("inner - Symbol", sys, nameof(sys.plant_input)),
133-
("nested - Symbol", nested_sys, nameof(nested_sys.hej.plant_input))
134-
]
117+
@testset "open_loop - $name" for (name, sys, ap) in test_cases
135118
open_sys, (du, u) = open_loop(sys, ap)
136119
matrices, _ = linearize(open_sys, [du], [u])
137120
@test matrices.A[] == -1
@@ -146,13 +129,14 @@ eqs = [connect(P.output, :plant_output, C.input)
146129
sys = ODESystem(eqs, t, systems = [P, C], name = :hej)
147130
@named nested_sys = ODESystem(Equation[], t; systems = [sys])
148131

149-
@testset "get_sensitivity - $name" for (name, sys, ap) in [
132+
test_cases = [
150133
("inner", sys, sys.plant_input),
151134
("nested", nested_sys, nested_sys.hej.plant_input),
152-
("inner - nonamespace", sys, :plant_input),
153-
("inner - Symbol", sys, nameof(sys.plant_input)),
154-
("nested - Symbol", nested_sys, nameof(nested_sys.hej.plant_input))
135+
("inner - Symbol", sys, :plant_input),
136+
("nested - Symbol", nested_sys, nameof(sys.plant_input))
155137
]
138+
139+
@testset "get_sensitivity - $name" for (name, sys, ap) in test_cases
156140
matrices, _ = get_sensitivity(sys, ap)
157141
@test matrices.A[] == -2
158142
@test matrices.B[] * matrices.C[] == -1 # either one negative
@@ -163,15 +147,19 @@ end
163147
("inner", sys, sys.plant_input, sys.plant_output),
164148
("nested", nested_sys, nested_sys.hej.plant_input, nested_sys.hej.plant_output)
165149
]
150+
inputname = Symbol(join(
151+
MTK.namespace_hierarchy(nameof(inputap))[2:end], NAMESPACE_SEPARATOR))
152+
outputname = Symbol(join(
153+
MTK.namespace_hierarchy(nameof(outputap))[2:end], NAMESPACE_SEPARATOR))
166154
@testset "input - $(typeof(input)), output - $(typeof(output))" for (input, output) in [
167155
(inputap, outputap),
168-
(nameof(inputap), outputap),
169-
(inputap, nameof(outputap)),
170-
(nameof(inputap), nameof(outputap)),
156+
(inputname, outputap),
157+
(inputap, outputname),
158+
(inputname, outputname),
171159
(inputap, [outputap]),
172-
(nameof(inputap), [outputap]),
173-
(inputap, [nameof(outputap)]),
174-
(nameof(inputap), [nameof(outputap)])
160+
(inputname, [outputap]),
161+
(inputap, [outputname]),
162+
(inputname, [outputname])
175163
]
176164
matrices, _ = linearize(sys, input, output)
177165
# Result should be the same as feedpack(P, 1), i.e., the closed-loop transfer function from plant input to plant output

test/downstream/analysis_points.jl

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ import ControlSystemsBase as CS
6464
prob = ODEProblem(sys, unknowns(sys) .=> 0.0, (0.0, 4.0))
6565
sol = solve(prob, Rodas5P(), reltol = 1e-6, abstol = 1e-9)
6666

67-
matrices, ssys = linearize(closed_loop, AnalysisPoint(:r), AnalysisPoint(:y))
67+
matrices, ssys = linearize(closed_loop, :r, :y)
6868
lsys = ss(matrices...) |> sminreal
6969
@test lsys.nx == 8
7070

@@ -129,13 +129,11 @@ end
129129
t,
130130
systems = [P_inner, feedback, ref])
131131

132-
P_not_broken, _ = linearize(sys_inner, AnalysisPoint(:u), AnalysisPoint(:y))
132+
P_not_broken, _ = linearize(sys_inner, :u, :y)
133133
@test P_not_broken.A[] == -2
134-
P_broken, _ = linearize(sys_inner, AnalysisPoint(:u), AnalysisPoint(:y),
135-
loop_openings = [AnalysisPoint(:u)])
134+
P_broken, _ = linearize(sys_inner, :u, :y, loop_openings = [:u])
136135
@test P_broken.A[] == -1
137-
P_broken, _ = linearize(sys_inner, AnalysisPoint(:u), AnalysisPoint(:y),
138-
loop_openings = [AnalysisPoint(:y)])
136+
P_broken, _ = linearize(sys_inner, :u, :y, loop_openings = [:y])
139137
@test P_broken.A[] == -1
140138

141139
Sinner = sminreal(ss(get_sensitivity(sys_inner, :u)[1]...))
@@ -154,10 +152,10 @@ end
154152
t,
155153
systems = [P_outer, sys_inner])
156154

157-
Souter = sminreal(ss(get_sensitivity(sys_outer, sys_inner.u)[1]...))
155+
Souter = sminreal(ss(get_sensitivity(sys_outer, sys_outer.sys_inner.u)[1]...))
158156

159157
Sinner2 = sminreal(ss(get_sensitivity(
160-
sys_outer, sys_inner.u, loop_openings = [:y2])[1]...))
158+
sys_outer, sys_outer.sys_inner.u, loop_openings = [:y2])[1]...))
161159

162160
@test Sinner.nx == 1
163161
@test Sinner == Sinner2
@@ -268,7 +266,7 @@ end
268266
@test tf(L[2, 1]) tf(Ps)
269267

270268
matrices, _ = linearize(
271-
sys_outer, [sys_outer.inner.plant_input], [sys_inner.plant_output])
269+
sys_outer, [sys_outer.inner.plant_input], [nameof(sys_inner.plant_output)])
272270
G = CS.ss(matrices...) |> sminreal
273271
@test tf(G) tf(CS.feedback(Ps, Cs))
274272
end

0 commit comments

Comments
 (0)