9696
9797Connect `output_connector` and `input_connector` with an [`AnalysisPoint`](@ref) inbetween.
9898The incoming connection `output_connector` is expected to be of type [`RealOutput`](@ref), and vice versa.
99- NOTE: The connection is assumed to be *causal*, meaning that
99+
100+ *PLEASE NOTE*: The connection is assumed to be *causal*, meaning that
100101```
101102connect(C.output, :plant_input, P.input)
102103```
@@ -152,24 +153,40 @@ function Base.:(==)(ap1::AnalysisPoint, ap2::AnalysisPoint)
152153 return ap1. in == ap2. in && ap1. out == ap2. out # Name doesn't really matter if inputs and outputs are the same
153154end
154155
155- function get_sensitivity_function (sys, ap_name:: Symbol ; kwargs... )
156- find = function (x, ns)
157- x isa AnalysisPoint || return false
158- if ns === nothing
159- nameof (x) === ap_name
160- else
161- Symbol (ns, :_ , nameof (x)) === ap_name
162- end
156+ function namespaced_ap_match (ap_name, loop_openings)
157+ (x, ns) -> namespaced_ap_match (x, ns, ap_name, loop_openings)
158+ end # 🍛
159+
160+ """
161+ namespaced_ap_match(x, ns, ap_name, loop_openings)
162+
163+ Returns true if `x` is an AnalysisPoint and matches either `ap_name` or any of the loop openings if namedspaced with `ns`.
164+ """
165+ function namespaced_ap_match (x, ns, ap_name, loop_openings)
166+ x isa AnalysisPoint || return false
167+ if ns === nothing
168+ nameof (x) === ap_name || (loop_openings != = nothing && nameof (x) ∈ loop_openings)
169+ else
170+ xx = Symbol (ns, :_ , nameof (x))
171+ xx === ap_name || (loop_openings != = nothing && xx ∈ loop_openings)
163172 end
173+ end
174+
175+ function get_sensitivity_function (sys, ap_name:: Symbol ; loop_openings = nothing , kwargs... )
176+ find = namespaced_ap_match (ap_name, loop_openings)
164177 t = get_iv (sys)
165178 @variables d (t) = 0 # Perturbation serving as input to sensitivity transfer function
166179 namespace = Ref {Union{Nothing, Symbol}} (nothing )
167180 apr = Ref {Union{Nothing, AnalysisPoint}} (nothing )
168181 replace = let d = d, namespace = namespace, apr = apr
169182 function (ap, ns)
170- namespace[] = ns # Save the namespace to make it available for renamespace below
171- apr[] = ap
172- (ap. out. u ~ ap. in. u + d), d
183+ if namespaced_ap_match (ap, ns, ap_name, nothing )
184+ namespace[] = ns
185+ apr[] = ap
186+ (ap. out. u ~ ap. in. u + d), d
187+ else # loop opening
188+ [ap. out. u ~ 0 ], []
189+ end
173190 end
174191 end
175192 sys = expand_connections (sys, find, replace)
@@ -182,24 +199,22 @@ function get_sensitivity_function(sys, ap_name::Symbol; kwargs...)
182199 ModelingToolkit. linearization_function (sys, [d], [u]; kwargs... )
183200end
184201
185- function get_comp_sensitivity_function (sys, ap_name:: Symbol ; kwargs... )
186- find = function (x, ns)
187- x isa AnalysisPoint || return false
188- if ns === nothing
189- nameof (x) === ap_name
190- else
191- Symbol (ns, :_ , nameof (x)) === ap_name
192- end
193- end
202+ function get_comp_sensitivity_function (sys, ap_name:: Symbol ; loop_openings = nothing ,
203+ kwargs... )
204+ find = namespaced_ap_match (ap_name, loop_openings)
194205 t = get_iv (sys)
195206 @variables d (t) = 0 # Perturbation serving as input to sensitivity transfer function
196207 namespace = Ref {Union{Nothing, Symbol}} (nothing )
197208 apr = Ref {Union{Nothing, AnalysisPoint}} (nothing )
198209 replace = let d = d, namespace = namespace, apr = apr
199210 function (ap, ns)
200- namespace[] = ns # Save the namespace to make it available for renamespace below
201- apr[] = ap
202- (ap. out. u + d ~ ap. in. u), d
211+ if namespaced_ap_match (ap, ns, ap_name, nothing )
212+ namespace[] = ns
213+ apr[] = ap
214+ (ap. out. u + d ~ ap. in. u), d
215+ else # loop opening
216+ [ap. out. u ~ 0 ], []
217+ end
203218 end
204219 end
205220 sys = expand_connections (sys, find, replace)
@@ -212,23 +227,20 @@ function get_comp_sensitivity_function(sys, ap_name::Symbol; kwargs...)
212227 ModelingToolkit. linearization_function (sys, [d], [u]; kwargs... )
213228end
214229
215- function get_looptransfer_function (sys, ap_name:: Symbol ; kwargs... )
216- find = function (x, ns)
217- x isa AnalysisPoint || return false
218- if ns === nothing
219- nameof (x) === ap_name
220- else
221- Symbol (ns, :_ , nameof (x)) === ap_name
222- end
223- end
230+ function get_looptransfer_function (sys, ap_name:: Symbol ; loop_openings = nothing , kwargs... )
231+ find = namespaced_ap_match (ap_name, loop_openings)
224232 t = get_iv (sys)
225233 namespace = Ref {Union{Nothing, Symbol}} (nothing )
226234 apr = Ref {Union{Nothing, AnalysisPoint}} (nothing )
227235 replace = let namespace = namespace, apr = apr
228236 function (ap, ns)
229- namespace[] = ns # Save the namespace to make it available for renamespace below
230- apr[] = ap
231- (0 ~ 0 ), nothing
237+ if namespaced_ap_match (ap, ns, ap_name, nothing )
238+ namespace[] = ns
239+ apr[] = ap
240+ (0 ~ 0 ), nothing
241+ else # loop opening
242+ [ap. out. u ~ 0 ], []
243+ end
232244 end
233245 end
234246 sys = expand_connections (sys, find, replace)
@@ -258,15 +270,8 @@ Open the loop at analysis point `ap` by breaking the connection through `ap`.
258270
259271See also [`get_sensitivity`](@ref), [`get_comp_sensitivity`](@ref), [`get_looptransfer`](@ref).
260272"""
261- function open_loop (sys, ap_name:: Symbol ; kwargs... )
262- find = function (x, ns)
263- x isa AnalysisPoint || return false
264- if ns === nothing
265- nameof (x) === ap_name
266- else
267- Symbol (ns, :_ , nameof (x)) === ap_name
268- end
269- end
273+ function open_loop (sys, ap_name:: Symbol ; ground_input = false , kwargs... )
274+ find = namespaced_ap_match (ap_name, nothing )
270275 t = get_iv (sys)
271276 @variables u (t)= 0 [input = true ]
272277 @variables y (t)= 0 [output = true ]
@@ -276,58 +281,62 @@ function open_loop(sys, ap_name::Symbol; kwargs...)
276281 function (ap, ns)
277282 namespace[] = ns # Save the namespace to make it available for renamespace below
278283 apr[] = ap
279- [ap. out. u ~ u, ap. in. u ~ y], [u, y]
284+ if ground_input
285+ [ap. out. u ~ 0 , ap. in. u ~ y], [y]
286+ else
287+ [ap. out. u ~ u, ap. in. u ~ y], [u, y]
288+ end
280289 end
281290 end
282- if (ns = namespace[]) != = nothing
283- y = ModelingToolkit. renamespace (ns, y)
284- u = ModelingToolkit. renamespace (ns, u)
285- end
291+ sys = expand_connections (sys, find, replace)
286292 (ap = apr[]) === nothing && error (" Did not find analysis point $ap_name " )
287293 sys
288294end
289295
290296function ModelingToolkit. linearization_function (sys:: ModelingToolkit.AbstractSystem ,
291297 input_name:: Symbol , output_name:: Symbol ;
298+ loop_openings = nothing ,
292299 kwargs... )
293- find = function (x, ns)
294- x isa AnalysisPoint || return false
295- if ns === nothing
296- nameof (x) ∈ (input_name, output_name)
297- else
298- Symbol (ns, :_ , nameof (x)) ∈ (input_name, output_name)
299- end
300- end
300+ t = get_iv (sys)
301+ @variables u (t)= 0 [input = true ]
302+ @variables y (t)= 0 [output = true ]
303+ find_input = namespaced_ap_match (input_name, loop_openings)
304+ find_output = namespaced_ap_match (output_name, loop_openings)
305+ find = (x, ns) -> find_input (x, ns) || find_output (x, ns)
301306
302307 namespace_u = Ref {Union{Nothing, Symbol}} (nothing )
303308 namespace_y = Ref {Union{Nothing, Symbol}} (nothing )
304309 apr_u = Ref {Union{Nothing, AnalysisPoint}} (nothing )
305310 apr_y = Ref {Union{Nothing, AnalysisPoint}} (nothing )
306- replace = let u = u, y = y, namespace_u = namespace_u, apr_u = apr_u, namespace_y = namespace_y, apr_y = apr_y
311+ replace = let u = u, y = y, namespace_u = namespace_u, apr_u = apr_u,
312+ namespace_y = namespace_y, apr_y = apr_y
313+
307314 function (ap, ns)
315+ if namespaced_ap_match (ap, ns, input_name, nothing )
308316 namespace_u[] = ns # Save the namespace to make it available for renamespace below
309317 apr_u[] = ap
310318 [ap. out. u ~ ap. in. u + u], u
311319 # input.in.u ~ 0] # We only need to ground one of the ends, hence not including this equation
320+ elseif namespaced_ap_match (ap, ns, output_name, nothing )
312321 namespace_y[] = ns # Save the namespace to make it available for renamespace below
313322 apr_y[] = ap
314323 [ap. in. u ~ y
315324 ap. out. u ~ ap. in. u], y
316- else
317- error ( " This should never happen " )
325+ else # loop opening
326+ [ap . out . u ~ 0 ], []
318327 end
319328 end
320329 end
321330 sys = expand_connections (sys, find, replace)
322331 (ap = apr_u[]) === nothing && error (" Did not find analysis point $input_name " )
323332 (ap = apr_y[]) === nothing && error (" Did not find analysis point $output_name " )
324333 if (ns = namespace_u[]) != = nothing
325- inputs[ 1 ] = ModelingToolkit. renamespace (ns, inputs[ 1 ]) # u
334+ u = ModelingToolkit. renamespace (ns, u)
326335 end
327336 if (ns = namespace_y[]) != = nothing
328- outputs[ 1 ] = ModelingToolkit. renamespace (ns, outputs[ 1 ]) # y
337+ y = ModelingToolkit. renamespace (ns, y)
329338 end
330- ModelingToolkit. linearization_function (sys, inputs, outputs ; kwargs... )
339+ ModelingToolkit. linearization_function (sys, [u], [y] ; kwargs... )
331340end
332341
333342# Add a method to get_sensitivity that accepts the name of an AnalysisPoint
344353
345354# Methods above are implemented in terms of linearization_function, the method below creates wrappers for linearize
346355for f in [:get_sensitivity , :get_comp_sensitivity , :get_looptransfer ]
347- @eval function $f (sys, ap:: Symbol , args... ; kwargs... )
348- lin_fun, ssys = $ (Symbol (string (f) * " _function" ))(sys, ap, args... ; kwargs... )
356+ @eval function $f (sys, ap:: Symbol , args... ; loop_openings = nothing , kwargs... )
357+ lin_fun, ssys = $ (Symbol (string (f) * " _function" ))(sys, ap, args... ; loop_openings,
358+ kwargs... )
349359 ModelingToolkit. linearize (ssys, lin_fun; kwargs... ), ssys
350360 end
351361end
355365
356366Linearize a system between two analysis points. To get a loop-transfer function, see [`get_looptransfer`](@ref)
357367"""
358- function ModelingToolkit. linearize (sys, input_name:: Symbol , output_name:: Symbol ; kwargs... )
359- lin_fun, ssys = linearization_function (sys, input_name, output_name; kwargs... )
368+ function ModelingToolkit. linearize (sys, input_name:: Symbol , output_name:: Symbol ;
369+ loop_openings = nothing , kwargs... )
370+ lin_fun, ssys = linearization_function (sys, input_name, output_name; loop_openings,
371+ kwargs... )
360372 ModelingToolkit. linearize (ssys, lin_fun; kwargs... ), ssys
361373end
362374
0 commit comments