Skip to content

Commit 2b1f525

Browse files
fix: remove ambiguities in namespacing of analysis points
1 parent 5097335 commit 2b1f525

File tree

3 files changed

+48
-61
lines changed

3 files changed

+48
-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: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ using ModelingToolkit, ModelingToolkitStandardLibrary.Blocks
22
using OrdinaryDiffEq, LinearAlgebra
33
using Test
44
using ModelingToolkit: t_nounits as t, D_nounits as D, AnalysisPoint, AbstractSystem
5+
import ModelingToolkit as MTK
56
using Symbolics: NAMESPACE_SEPARATOR
67

78
@testset "AnalysisPoint is lowered to `connect`" begin
@@ -69,26 +70,21 @@ sys = ODESystem(eqs, t, systems = [P, C], name = :hej)
6970
@test norm(sol.u[end]) < 1e-6 # This fails without the feedback through C
7071
end
7172

72-
@testset "get_sensitivity - $name" for (name, sys, ap) in [
73+
test_cases = [
7374
("inner", sys, sys.plant_input),
7475
("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))
76+
("inner - Symbol", sys, :plant_input),
77+
("nested - Symbol", nested_sys, nameof(sys.plant_input))
7878
]
79+
80+
@testset "get_sensitivity - $name" for (name, sys, ap) in test_cases
7981
matrices, _ = get_sensitivity(sys, ap)
8082
@test matrices.A[] == -2
8183
@test matrices.B[] * matrices.C[] == -1 # either one negative
8284
@test matrices.D[] == 1
8385
end
8486

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-
]
87+
@testset "get_comp_sensitivity - $name" for (name, sys, ap) in test_cases
9288
matrices, _ = get_comp_sensitivity(sys, ap)
9389
@test matrices.A[] == -2
9490
@test matrices.B[] * matrices.C[] == 1 # both positive or negative
@@ -104,13 +100,7 @@ S = sensitivity(P, C) # or feedback(1, P*C)
104100
T = comp_sensitivity(P, C) # or feedback(P*C)
105101
=#
106102

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-
]
103+
@testset "get_looptransfer - $name" for (name, sys, ap) in test_cases
114104
matrices, _ = get_looptransfer(sys, ap)
115105
@test matrices.A[] == -1
116106
@test matrices.B[] * matrices.C[] == -1 # either one negative
@@ -125,13 +115,7 @@ C = -1
125115
L = P*C
126116
=#
127117

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-
]
118+
@testset "open_loop - $name" for (name, sys, ap) in test_cases
135119
open_sys, (du, u) = open_loop(sys, ap)
136120
matrices, _ = linearize(open_sys, [du], [u])
137121
@test matrices.A[] == -1
@@ -146,13 +130,14 @@ eqs = [connect(P.output, :plant_output, C.input)
146130
sys = ODESystem(eqs, t, systems = [P, C], name = :hej)
147131
@named nested_sys = ODESystem(Equation[], t; systems = [sys])
148132

149-
@testset "get_sensitivity - $name" for (name, sys, ap) in [
133+
test_cases = [
150134
("inner", sys, sys.plant_input),
151135
("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))
136+
("inner - Symbol", sys, :plant_input),
137+
("nested - Symbol", nested_sys, nameof(sys.plant_input))
155138
]
139+
140+
@testset "get_sensitivity - $name" for (name, sys, ap) in test_cases
156141
matrices, _ = get_sensitivity(sys, ap)
157142
@test matrices.A[] == -2
158143
@test matrices.B[] * matrices.C[] == -1 # either one negative
@@ -163,15 +148,19 @@ end
163148
("inner", sys, sys.plant_input, sys.plant_output),
164149
("nested", nested_sys, nested_sys.hej.plant_input, nested_sys.hej.plant_output)
165150
]
151+
inputname = Symbol(join(
152+
MTK.namespace_hierarchy(nameof(inputap))[2:end], NAMESPACE_SEPARATOR))
153+
outputname = Symbol(join(
154+
MTK.namespace_hierarchy(nameof(outputap))[2:end], NAMESPACE_SEPARATOR))
166155
@testset "input - $(typeof(input)), output - $(typeof(output))" for (input, output) in [
167156
(inputap, outputap),
168-
(nameof(inputap), outputap),
169-
(inputap, nameof(outputap)),
170-
(nameof(inputap), nameof(outputap)),
157+
(inputname, outputap),
158+
(inputap, outputname),
159+
(inputname, outputname),
171160
(inputap, [outputap]),
172-
(nameof(inputap), [outputap]),
173-
(inputap, [nameof(outputap)]),
174-
(nameof(inputap), [nameof(outputap)])
161+
(inputname, [outputap]),
162+
(inputap, [outputname]),
163+
(inputname, [outputname])
175164
]
176165
matrices, _ = linearize(sys, input, output)
177166
# 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)