Skip to content

Commit 44866ab

Browse files
authored
Merge pull request #110 from JuliaControl/nicenames
Improvements to workflow with named systems
2 parents 6e9eabe + 9e6a979 commit 44866ab

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed

src/hinfinity_design.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ function _scalematrix(A::AbstractMatrix; method = :QR)
566566
elseif method === :SVD
567567
return _coordinatetransformsvd(A)
568568
else
569-
error("The method $method is not supported, use 'QR' or 'SVD' instad.")
569+
error("The method $method is not supported, use `:QR` or `:SVD` instead.")
570570
end
571571
end
572572

src/named_systems2.jl

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ function Base.promote_rule(::Type{NamedStateSpace{TE, StateSpace{TE, T1}}}, ::Ty
6464
NamedStateSpace{TE, StateSpace{TE, promote_type(T1,eltype(MT))}}
6565
end
6666

67+
function Base.promote_rule(::Type{U}, ::Type{NamedStateSpace{T, S}}) where
68+
{T, TF, U<:TransferFunction{<:Any, TF} , S<:AbstractStateSpace{T}}
69+
inner = promote_type(U,S)
70+
NamedStateSpace{T, inner}
71+
end
72+
6773

6874

6975
function Base.convert(::Type{NamedStateSpace{T, S}}, s::U) where {T, S <: AbstractStateSpace, U <: AbstractStateSpace}
@@ -82,6 +88,11 @@ function Base.convert(::Type{NamedStateSpace{T, S}}, s::NamedStateSpace{T, U}) w
8288
NamedStateSpace{T,typeof(sys)}(sys, s.x, s.u, s.y, s.name)
8389
end
8490

91+
function Base.convert(::Type{NamedStateSpace{T, S}}, s::U) where {T, S <: AbstractStateSpace, U <: TransferFunction}
92+
s2 = Base.convert(S, s)
93+
named_ss(s2, x = gensym("x"), u = gensym("u"), y = gensym("y"))
94+
end
95+
8596
# function Base.convert(::Type{TransferFunction{TE, S}}, s::U) where {TE, S, U <: NamedStateSpace{TE}}
8697
# convert(TransferFunction{TE, S}, s.sys)
8798
# end
@@ -288,6 +299,34 @@ function Base.:*(s1::NamedStateSpace{T, S}, s2::Number) where {T <: CS.TimeEvolu
288299
)
289300
end
290301

302+
function Base.:*(s1::AbstractMatrix, s2::NamedStateSpace{T, S}) where {T <: CS.TimeEvolution, S}
303+
if isdiag(s1)
304+
return NamedStateSpace{T,S}(
305+
s1*s2.sys,
306+
s2.x,
307+
s2.u,
308+
[Symbol(string(y)*"_scaled") for y in s2.y],
309+
isempty(s2.name) ? "" : s2.name*"_scaled",
310+
)
311+
else
312+
return *(promote(s1, s2)...)
313+
end
314+
end
315+
316+
function Base.:*(s1::NamedStateSpace{T, S}, s2::AbstractMatrix) where {T <: CS.TimeEvolution, S}
317+
if isdiag(s2)
318+
return NamedStateSpace{T,S}(
319+
s1.sys*s2,
320+
s1.x,
321+
[Symbol(string(u)*"_scaled") for u in s1.u],
322+
s1.y,
323+
isempty(s1.name) ? "" : s1.name*"_scaled",
324+
)
325+
else
326+
return *(promote(s1, s2)...)
327+
end
328+
end
329+
291330
function Base.:/(s::NamedStateSpace{T, S}, n::Number) where {T <: CS.TimeEvolution, S}
292331
s*(1/n)
293332
end
@@ -324,7 +363,9 @@ end
324363
"""
325364
measure(s::NamedStateSpace, names)
326365
327-
Return a system with specified states as measurement outputs.
366+
Return a system with specified state variables as measurement outputs.
367+
368+
See also [`add_output`](@ref).
328369
"""
329370
function measure(s::NamedStateSpace, names)
330371
inds = names2indices(names, s.x)
@@ -801,6 +842,27 @@ function CS.append(systems::NamedStateSpace...; kwargs...)
801842
end
802843

803844

845+
"""
846+
add_output(sys::NamedStateSpace, C2::AbstractArray, D2 = 0; y)
847+
848+
Add outputs to `sys` corresponding to the output matrix `C2` and the feedthrough matrix `D2` to the system `sys`.
849+
850+
# Arguments:
851+
- `y`: The names used for the new outputs. If not provided, the names will be generated automatically.
852+
853+
See also [`measure`](@ref) for a simpler way to output state variables.
854+
"""
855+
function CS.add_output(sys::NamedStateSpace, C2::AbstractArray, D2=0; y = [Symbol("y_$i") for i in (1:size(C2, 1)) .+ sys.ny])
856+
T = promote_type(CS.numeric_type(sys), eltype(C2), eltype(D2))
857+
A,B,C,D = ssdata(sys)
858+
D3 = D2 == 0 ? zeros(T, size(C2, 1), sys.nu) : D2
859+
x = sys.x
860+
u = sys.u
861+
y = [sys.y; y]
862+
named_ss(ss(A, B, [C; C2], [D; D3]), sys.timeevol; x, u, y)
863+
end
864+
865+
804866
function CS.minreal(sys::NamedStateSpace, args...; kwargs...)
805867
msys = minreal(sys.sys, args...; kwargs...)
806868
named_ss(msys; sys.u, sys.y, sys.name)

test/test_named_systems2.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ end
139139
G = measure(s1, :x)
140140
@test G.C == ones(1, 1)
141141
@test G.y == [:x]
142+
143+
@test add_output(s1, [0.2]) isa NamedStateSpace
144+
@test add_output(s1, [0.2], y=[:hej]).y[2] === :hej
145+
142146
end
143147

144148
G1 = ss(1,1,1,0)
@@ -189,6 +193,27 @@ s2 = named_ss(G2, x = [:z], u = [:u1], y=[:y2])
189193
G1 = named_ss(ssrand(1,1,1, Ts=1), "G1")
190194
G2 = named_ss(ssrand(1,1,1, Ts=1), "G2")
191195
gangoffourplot(G1, G2) # tests some convert methods for I to discrete
196+
197+
198+
G1 = named_ss(ssrand(1,1,1), "G1")
199+
# Scalars
200+
@test_nowarn G1*1
201+
@test_nowarn 1*G1
202+
203+
# Transfer function
204+
@test (G1*tf(1, [1,1])).sys == (G1*ss(tf(1, [1,1]))).sys
205+
@test (tf(1, [1,1])*G1).sys == (ss(tf(1, [1,1]))*G1).sys
206+
207+
# Matrix
208+
@test (G1*ones(1,1)).sys == (G1*ss(ones(1,1))).sys
209+
@test (ones(1,1)*G1).sys == (ss(ones(1,1))*G1).sys
210+
211+
# if the matrix is diagonal, the names are `u_scaled`
212+
@test endswith(string((G1*ones(1,1)).u[]), "_scaled")
213+
214+
# If the matrix is not diagonal, the names are generic
215+
G1 = named_ss(ssrand(1,2,1), "G1")
216+
@test !endswith(string((G1*ones(2,2)).u[1]), "_scaled")
192217
end
193218

194219

0 commit comments

Comments
 (0)