Skip to content

Commit 0c919b9

Browse files
YingboMabaggepinnen
andcommitted
WIP: use the new MTK find and replace functionality to implement AP
Co-authored-by: Fredrik Bagge Carlson <baggepinnen@gmail.com>
1 parent b7a9196 commit 0c919b9

File tree

2 files changed

+47
-31
lines changed

2 files changed

+47
-31
lines changed

src/Blocks/analysis_points.jl

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,23 @@ using ModelingToolkit: get_eqs, vars, @set!, get_iv
33
Base.@kwdef mutable struct AnalysisPoint
44
in = nothing
55
out = nothing
6-
name::Symbol
6+
name::Symbol = :nothing
77
end
88

9+
Base.nameof(ap::AnalysisPoint) = ap.name
10+
911
"""
1012
AnalysisPoint(in, out, name::Symbol)
1113
AnalysisPoint(in, out; name::Symbol)
1214
AnalysisPoint(name::Symbol)
1315
14-
Create an AnalysisPoint for linear analysis. Analysis points can also be created automatically by calling
16+
Create an AnalysisPoint for linear analysis. Analysis points can also be created automatically by calling
1517
```
1618
connect(in, :ap_name, out)
1719
```
1820
1921
!!! danger "Experimental"
20-
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
22+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
2123
2224
# Arguments:
2325
- `in`: A connector of type [`RealOutput`](@ref).
@@ -63,6 +65,10 @@ AnalysisPoint(name) = AnalysisPoint(; name)
6365

6466
Base.show(io::IO, ap::AnalysisPoint) = show(io, MIME"text/plain"(), ap)
6567
function Base.show(io::IO, ::MIME"text/plain", ap::AnalysisPoint)
68+
if ap.in === nothing
69+
print(io, "0")
70+
return
71+
end
6672
if get(io, :compact, false)
6773
print(io, "AnalysisPoint($(ap.in.u), $(ap.out.u); name=$(ap.name))")
6874
else
@@ -94,11 +100,13 @@ The incoming connection `in` is expected to be of type [`RealOutput`](@ref), and
94100
function ModelingToolkit.connect(in, ap::AnalysisPoint, out)
95101
ap.in = in
96102
ap.out = out
97-
return 0 ~ ap
103+
return AnalysisPoint() ~ ap
98104
end
99105

106+
ModelingToolkit.get_systems(ap::AnalysisPoint) = (ap.in, ap.out)
100107
function ModelingToolkit.connect(in, ap_name::Symbol, out)
101-
return 0 ~ AnalysisPoint(in, out, ap_name)
108+
ap = AnalysisPoint(in, out, ap_name)
109+
return AnalysisPoint() ~ ap
102110
end
103111

104112
function ModelingToolkit.vars(ap::AnalysisPoint; op = Differential)
@@ -135,12 +143,14 @@ function expand_analysis_points(sys)
135143
sys
136144
end
137145

146+
#=
138147
function ModelingToolkit.namespace_expr(ap::AnalysisPoint, sys, n = nameof(sys)) where {T}
139-
in = ModelingToolkit.renamespace(n, ap.in)
140-
out = ModelingToolkit.renamespace(n, ap.out)
148+
in = ap.in === nothing ? ap.in : ModelingToolkit.renamespace(n, ap.in)
149+
out = ap.out === nothing ? ap.out : ModelingToolkit.renamespace(n, ap.out)
141150
name = Symbol(n, :_, ap.name)
142151
AnalysisPoint(in, out, name)
143152
end
153+
=#
144154

145155
function Base.:(==)(ap1::AnalysisPoint, ap2::AnalysisPoint)
146156
return ap1.in == ap2.in && ap1.out == ap2.out # Name doesn't really matter if inputs and outputs are the same
@@ -153,29 +163,36 @@ end
153163
Compute the sensitivity function in analysis point `ap`. The sensitivity function is obtained by introducing an infinitesimal perturbation `d` at the input of `ap`, linearizing the system and computing the transfer function between `d` and the output of `ap`.
154164
155165
!!! danger "Experimental"
156-
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
166+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
157167
158168
# Arguments:
159169
- `kwargs`: Are sent to `ModelingToolkit.linearize`
160170
161171
See also [`get_comp_sensitivity`](@ref), [`get_looptransfer`](@ref).
162172
"""
163-
function get_sensitivity(sys, ap::AnalysisPoint; kwargs...)
164-
sys = ModelingToolkit.flatten(sys) # To get namespacing right
173+
function get_sensitivity(sys, ap_name::Symbol; kwargs...)
174+
find = let ap_name = ap_name
175+
x -> x isa AnalysisPoint && nameof(x) === ap_name
176+
end
165177
t = get_iv(sys)
166178
@variables d(t) = 0 # Perturbation serving as input to sensivity transfer function
167-
found = false
168-
new_eqs = map(equations(sys)) do eq
169-
eq.rhs == ap || (return eq)
170-
found = true
171-
ap.out.u ~ ap.in.u + d # This assumes that the connector has an internal vaiable named u
179+
namespace = Ref{Union{Nothing, Symbol}}(nothing)
180+
this_ap = Ref{Union{Nothing, AnalysisPoint}}(nothing)
181+
replace = let d = d, namespace = namespace, this_ap = this_ap
182+
(ap, ns) -> begin
183+
namespace[] = ns
184+
this_ap[] = ap
185+
(ap.out.u ~ ap.in.u + d), d
186+
end
172187
end
173-
found || error("Did not find analysis point $ap")
174-
@set! sys.eqs = new_eqs
175-
@set! sys.states = [states(sys); d]
176-
@set! sys.defaults = merge(ModelingToolkit.defaults(sys), Dict(d => 0))
177-
sys = expand_analysis_points(sys) # Any remaining analysis points are removed by this
178-
ModelingToolkit.linearize(sys, [d], [ap.out.u]; kwargs...)
188+
sys = expand_connections(sys, find, replace)
189+
(ap = this_ap[]) === nothing && error("Did not find analysis point $ap")
190+
u = ap.out.u
191+
if (ns = namespace[]) !== nothing
192+
d = ModelingToolkit.renamespace(d, ns)
193+
u = ModelingToolkit.renamespace(u, ns)
194+
end
195+
ModelingToolkit.linearize(sys, [d], [u]; kwargs...)
179196
end
180197

181198
"""
@@ -185,7 +202,7 @@ end
185202
Compute the complementary sensitivity function in analysis point `ap`. The complementary sensitivity function is obtained by introducing an infinitesimal perturbation `d` at the output of `ap`, linearizing the system and computing the transfer function between `d` and the input of `ap`.
186203
187204
!!! danger "Experimental"
188-
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
205+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
189206
190207
# Arguments:
191208
- `kwargs`: Are sent to `ModelingToolkit.linearize`
@@ -218,7 +235,7 @@ end
218235
Compute the (linearized) loop-transfer function in analysis point `ap`, from `ap.out` to `ap.in`.
219236
220237
!!! danger "Experimental"
221-
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
238+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
222239
223240
# Arguments:
224241
- `kwargs`: Are sent to `ModelingToolkit.linearize`
@@ -249,7 +266,7 @@ Open the loop at analysis point `ap` by breaking the connection through `ap`.
249266
`open_sys` will have `u ~ ap.out` as input and `y ~ ap.in` as output.
250267
251268
!!! danger "Experimental"
252-
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
269+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
253270
254271
# Arguments:
255272
- `kwargs`: Are sent to `ModelingToolkit.linearize`
@@ -307,12 +324,10 @@ function ModelingToolkit.linearize(sys, input::AnalysisPoint, output::AnalysisPo
307324
ModelingToolkit.linearize(sys, [u], [y]; kwargs...)
308325
end
309326

310-
# Add a method to get_sensitivity that accepts the name of an AnalysisPoint
327+
# Add a method to get_sensitivity that accepts the name of an AnalysisPoint
311328
for f in [:get_sensitivity, :get_comp_sensitivity, :get_looptransfer, :open_loop]
312-
@eval function $f(sys, ap_name::Symbol, args...; kwargs...)
313-
ap = find_analysis_point(sys, ap_name)
314-
ap === nothing && error("Failed to find an analysis point named $ap_name")
315-
$f(sys, ap, args...; kwargs...)
329+
@eval function $f(sys, ap::AnalysisPoint, args...; kwargs...)
330+
$f(sys, nameof(ap), args...; kwargs...)
316331
end
317332
end
318333

test/Blocks/test_analysis_points.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Test, LinearAlgebra
12
using ModelingToolkit
23
using ModelingToolkitStandardLibrary.Blocks
34
using OrdinaryDiffEq
@@ -119,8 +120,8 @@ eqs = [connect(r.output, F.input)
119120
sys_outer = ODESystem(eqs, t, systems = [F, sys_inner, r], name = :outer)
120121

121122
# test first that the structural_simplify ∘ expand_analysis_points works correctly
122-
ssys = structural_simplify(expand_analysis_points(sys_outer))
123-
# ssys = structural_simplify((sys_outer))
123+
#ssys = structural_simplify(expand_analysis_points(sys_outer))
124+
ssys = structural_simplify(sys_outer)
124125
prob = ODEProblem(ssys, [P.x => 1], (0, 10))
125126
# sol = solve(prob, Rodas5())
126127
# plot(sol)

0 commit comments

Comments
 (0)