Skip to content

Commit f7da682

Browse files
fixup! docs: add and update docstrings for analysis points and transformations
1 parent 7f42ef0 commit f7da682

File tree

1 file changed

+148
-12
lines changed

1 file changed

+148
-12
lines changed

src/systems/analysis_points.jl

+148-12
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,85 @@
11
"""
22
$(TYPEDEF)
3-
$(TYPEDSIGNATURES)
3+
AnalysisPoint(input, name::Symbol, outputs::Vector)
44
55
Create an AnalysisPoint for linear analysis. Analysis points can be created by calling
66
77
```
88
connect(in, :ap_name, out...)
99
```
1010
11-
Where `in` is the input to the connection, and `out...` are the outputs. In the context of
12-
ModelingToolkitStandardLibrary.jl, `in` is a `RealOutput` connector and `out...` are all
13-
`RealInput` connectors. All involved connectors are required to either have an unknown named
11+
Where `in` is the input to the connection, and `out...` are the outputs. All involved
12+
connectors (input and outputs) are required to either have an unknown named
1413
`u` or a single unknown, all of which should have the same size.
14+
15+
See also [`get_sensitivity`](@ref), [`get_comp_sensitivity`](@ref), [`get_looptransfer`](@ref), [`open_loop`](@ref)
16+
17+
# Fields
18+
19+
$(TYPEDFIELDS)
20+
21+
# Example
22+
23+
```julia
24+
using ModelingToolkit
25+
using ModelingToolkitStandardLibrary.Blocks
26+
using ModelingToolkit: t_nounits as t
27+
28+
@named P = FirstOrder(k = 1, T = 1)
29+
@named C = Gain(; k = -1)
30+
t = ModelingToolkit.get_iv(P)
31+
32+
eqs = [connect(P.output, C.input)
33+
connect(C.output, :plant_input, P.input)]
34+
sys = ODESystem(eqs, t, systems = [P, C], name = :feedback_system)
35+
36+
matrices_S, _ = get_sensitivity(sys, :plant_input) # Compute the matrices of a state-space representation of the (input) sensitivity function.
37+
matrices_T, _ = get_comp_sensitivity(sys, :plant_input)
38+
```
39+
40+
Continued linear analysis and design can be performed using ControlSystemsBase.jl.
41+
Create `ControlSystemsBase.StateSpace` objects using
42+
43+
```julia
44+
using ControlSystemsBase, Plots
45+
S = ss(matrices_S...)
46+
T = ss(matrices_T...)
47+
bodeplot([S, T], lab = ["S" "T"])
48+
```
49+
50+
The sensitivity functions obtained this way should be equivalent to the ones obtained with the code below
51+
52+
```julia
53+
using ControlSystemsBase
54+
P = tf(1.0, [1, 1])
55+
C = 1 # Negative feedback assumed in ControlSystems
56+
S = sensitivity(P, C) # or feedback(1, P*C)
57+
T = comp_sensitivity(P, C) # or feedback(P*C)
58+
```
1559
"""
1660
struct AnalysisPoint
61+
"""
62+
The input to the connection. In the context of ModelingToolkitStandardLibrary.jl,
63+
this is a `RealOutput` connector.
64+
"""
1765
input::Any
66+
"""
67+
The name of the analysis point.
68+
"""
1869
name::Symbol
70+
"""
71+
The outputs of the connection. In the context of ModelingToolkitStandardLibrary.jl,
72+
these are all `RealInput` connectors.
73+
"""
1974
outputs::Union{Nothing, Vector{Any}}
2075
end
2176

2277
AnalysisPoint() = AnalysisPoint(nothing, Symbol(), nothing)
78+
"""
79+
$(TYPEDSIGNATURES)
80+
81+
Create an `AnalysisPoint` with the given name, with no input or outputs specified.
82+
"""
2383
AnalysisPoint(name::Symbol) = AnalysisPoint(nothing, name, nothing)
2484

2585
Base.nameof(ap::AnalysisPoint) = ap.name
@@ -78,9 +138,35 @@ function Symbolics.connect(in, ap::AnalysisPoint, outs...)
78138
end
79139

80140
"""
81-
$(TYPEDSIGNATURES)
141+
connect(output_connector, ap_name::Symbol, input_connector; verbose = true)
142+
connect(output_connector, ap::AnalysisPoint, input_connector; verbose = true)
143+
144+
Connect `output_connector` and `input_connector` with an [`AnalysisPoint`](@ref) inbetween.
145+
The incoming connection `output_connector` is expected to be an output connector (for
146+
example, `ModelingToolkitStandardLibrary.Blocks.RealOutput`), and vice versa.
82147
83-
Create an `AnalysisPoint` connection connecting `in` to `outs...`.
148+
*PLEASE NOTE*: The connection is assumed to be *causal*, meaning that
149+
150+
```julia
151+
@named P = FirstOrder(k = 1, T = 1)
152+
@named C = Gain(; k = -1)
153+
connect(C.output, :plant_input, P.input)
154+
```
155+
156+
is correct, whereas
157+
158+
```julia
159+
connect(P.input, :plant_input, C.output)
160+
```
161+
162+
typically is not (unless the model is an inverse model).
163+
164+
# Arguments:
165+
166+
- `output_connector`: An output connector
167+
- `input_connector`: An input connector
168+
- `ap`: An explicitly created [`AnalysisPoint`](@ref)
169+
- `ap_name`: If a name is given, an [`AnalysisPoint`](@ref) with the given name will be created automatically.
84170
"""
85171
function Symbolics.connect(in::AbstractSystem, name::Symbol, out, outs...)
86172
return AnalysisPoint() ~ AnalysisPoint(in, name, [out; collect(outs)])
@@ -724,12 +810,6 @@ for f in [:get_sensitivity, :get_comp_sensitivity, :get_looptransfer]
724810
sys, ap, args...; loop_openings, system_modifier, kwargs...)
725811
ModelingToolkit.linearize(ssys, lin_fun; kwargs...), ssys
726812
end
727-
@eval @doc """
728-
$(TYPEDSIGNATURES)
729-
730-
Call `$($utility_fun)` and perform the linearization. All keyword arguments are
731-
forwarded to `$($utility_fun)` and subsequently `linearize`.
732-
""" $f
733813
end
734814

735815
"""
@@ -785,3 +865,59 @@ function linearization_function(sys::AbstractSystem,
785865

786866
return linearization_function(system_modifier(sys), input_vars, output_vars; kwargs...)
787867
end
868+
869+
@doc """
870+
get_sensitivity(sys, ap::AnalysisPoint; kwargs)
871+
get_sensitivity(sys, ap_name::Symbol; kwargs)
872+
873+
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`.
874+
875+
!!! danger "Experimental"
876+
877+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
878+
879+
# Arguments:
880+
881+
- `kwargs`: Are sent to `ModelingToolkit.linearize`
882+
883+
See also [`get_comp_sensitivity`](@ref), [`get_looptransfer`](@ref).
884+
""" get_sensitivity
885+
886+
@doc """
887+
get_comp_sensitivity(sys, ap::AnalysisPoint; kwargs)
888+
get_comp_sensitivity(sys, ap_name::Symbol; kwargs)
889+
890+
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`.
891+
892+
!!! danger "Experimental"
893+
894+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
895+
896+
# Arguments:
897+
898+
- `kwargs`: Are sent to `ModelingToolkit.linearize`
899+
900+
See also [`get_sensitivity`](@ref), [`get_looptransfer`](@ref).
901+
""" get_comp_sensitivity
902+
903+
@doc """
904+
get_looptransfer(sys, ap::AnalysisPoint; kwargs)
905+
get_looptransfer(sys, ap_name::Symbol; kwargs)
906+
907+
Compute the (linearized) loop-transfer function in analysis point `ap`, from `ap.out` to `ap.in`.
908+
909+
!!! info "Negative feedback"
910+
911+
Feedback loops often use negative feedback, and the computed loop-transfer function will in this case have the negative feedback included. Standard analysis tools often assume a loop-transfer function without the negative gain built in, and the result of this function may thus need negation before use.
912+
913+
914+
!!! danger "Experimental"
915+
916+
The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning.
917+
918+
# Arguments:
919+
920+
- `kwargs`: Are sent to `ModelingToolkit.linearize`
921+
922+
See also [`get_sensitivity`](@ref), [`get_comp_sensitivity`](@ref), [`open_loop`](@ref).
923+
""" get_looptransfer

0 commit comments

Comments
 (0)