From 387acad2318895b55068607127e016ea4b401564 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Mon, 6 Jan 2025 14:09:18 +0100 Subject: [PATCH 1/3] add promotion for TF<->named_ss --- src/hinfinity_design.jl | 2 +- src/named_systems2.jl | 11 +++++++++++ test/test_named_systems2.jl | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/hinfinity_design.jl b/src/hinfinity_design.jl index a16ba4b0..7f8bc86c 100644 --- a/src/hinfinity_design.jl +++ b/src/hinfinity_design.jl @@ -566,7 +566,7 @@ function _scalematrix(A::AbstractMatrix; method = :QR) elseif method === :SVD return _coordinatetransformsvd(A) else - error("The method $method is not supported, use 'QR' or 'SVD' instad.") + error("The method $method is not supported, use `:QR` or `:SVD` instead.") end end diff --git a/src/named_systems2.jl b/src/named_systems2.jl index a6d9aee7..028e2141 100644 --- a/src/named_systems2.jl +++ b/src/named_systems2.jl @@ -64,6 +64,12 @@ function Base.promote_rule(::Type{NamedStateSpace{TE, StateSpace{TE, T1}}}, ::Ty NamedStateSpace{TE, StateSpace{TE, promote_type(T1,eltype(MT))}} end +function Base.promote_rule(::Type{U}, ::Type{NamedStateSpace{T, S}}) where + {T, TF, U<:TransferFunction{<:Any, TF} , S<:AbstractStateSpace{T}} + inner = promote_type(U,S) + NamedStateSpace{T, inner} +end + 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 NamedStateSpace{T,typeof(sys)}(sys, s.x, s.u, s.y, s.name) end +function Base.convert(::Type{NamedStateSpace{T, S}}, s::U) where {T, S <: AbstractStateSpace, U <: TransferFunction} + s2 = Base.convert(S, s) + named_ss(s2, x = gensym("x"), u = gensym("u"), y = gensym("y")) +end + # function Base.convert(::Type{TransferFunction{TE, S}}, s::U) where {TE, S, U <: NamedStateSpace{TE}} # convert(TransferFunction{TE, S}, s.sys) # end diff --git a/test/test_named_systems2.jl b/test/test_named_systems2.jl index e786fa7e..b5e65937 100644 --- a/test/test_named_systems2.jl +++ b/test/test_named_systems2.jl @@ -189,6 +189,17 @@ s2 = named_ss(G2, x = [:z], u = [:u1], y=[:y2]) G1 = named_ss(ssrand(1,1,1, Ts=1), "G1") G2 = named_ss(ssrand(1,1,1, Ts=1), "G2") gangoffourplot(G1, G2) # tests some convert methods for I to discrete + + + G1 = named_ss(ssrand(1,1,1), "G1") + # Scalars + @test_nowarn G1*1 + @test_nowarn 1*G1 + + # Transfer function + @test (G1*tf(1, [1,1])).sys == (G1*ss(tf(1, [1,1]))).sys + @test (tf(1, [1,1])*G1).sys == (ss(tf(1, [1,1]))*G1).sys + end From f8f90374e54ea58751f414daa5d66188fefb8099 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Mon, 6 Jan 2025 14:10:08 +0100 Subject: [PATCH 2/3] scaled names for matrix multiplication --- src/named_systems2.jl | 28 ++++++++++++++++++++++++++++ test/test_named_systems2.jl | 10 ++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/named_systems2.jl b/src/named_systems2.jl index 028e2141..6cf4f3fe 100644 --- a/src/named_systems2.jl +++ b/src/named_systems2.jl @@ -299,6 +299,34 @@ function Base.:*(s1::NamedStateSpace{T, S}, s2::Number) where {T <: CS.TimeEvolu ) end +function Base.:*(s1::AbstractMatrix, s2::NamedStateSpace{T, S}) where {T <: CS.TimeEvolution, S} + if isdiag(s1) + return NamedStateSpace{T,S}( + s1*s2.sys, + s2.x, + s2.u, + [Symbol(string(y)*"_scaled") for y in s2.y], + isempty(s2.name) ? "" : s2.name*"_scaled", + ) + else + return *(promote(s1, s2)...) + end +end + +function Base.:*(s1::NamedStateSpace{T, S}, s2::AbstractMatrix) where {T <: CS.TimeEvolution, S} + if isdiag(s2) + return NamedStateSpace{T,S}( + s1.sys*s2, + s1.x, + [Symbol(string(u)*"_scaled") for u in s1.u], + s1.y, + isempty(s1.name) ? "" : s1.name*"_scaled", + ) + else + return *(promote(s1, s2)...) + end +end + function Base.:/(s::NamedStateSpace{T, S}, n::Number) where {T <: CS.TimeEvolution, S} s*(1/n) end diff --git a/test/test_named_systems2.jl b/test/test_named_systems2.jl index b5e65937..d9050fc4 100644 --- a/test/test_named_systems2.jl +++ b/test/test_named_systems2.jl @@ -200,6 +200,16 @@ s2 = named_ss(G2, x = [:z], u = [:u1], y=[:y2]) @test (G1*tf(1, [1,1])).sys == (G1*ss(tf(1, [1,1]))).sys @test (tf(1, [1,1])*G1).sys == (ss(tf(1, [1,1]))*G1).sys + # Matrix + @test (G1*ones(1,1)).sys == (G1*ss(ones(1,1))).sys + @test (ones(1,1)*G1).sys == (ss(ones(1,1))*G1).sys + + # if the matrix is diagonal, the names are `u_scaled` + @test endswith(string((G1*ones(1,1)).u[]), "_scaled") + + # If the matrix is not diagonal, the names are generic + G1 = named_ss(ssrand(1,2,1), "G1") + @test !endswith(string((G1*ones(2,2)).u[1]), "_scaled") end From 9e6a979df05019a345ddcc1d99ba3a97ed15cac7 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Mon, 6 Jan 2025 14:13:45 +0100 Subject: [PATCH 3/3] `add_output` for named ss --- src/named_systems2.jl | 25 ++++++++++++++++++++++++- test/test_named_systems2.jl | 4 ++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/named_systems2.jl b/src/named_systems2.jl index 6cf4f3fe..47d97ea9 100644 --- a/src/named_systems2.jl +++ b/src/named_systems2.jl @@ -363,7 +363,9 @@ end """ measure(s::NamedStateSpace, names) -Return a system with specified states as measurement outputs. +Return a system with specified state variables as measurement outputs. + +See also [`add_output`](@ref). """ function measure(s::NamedStateSpace, names) inds = names2indices(names, s.x) @@ -840,6 +842,27 @@ function CS.append(systems::NamedStateSpace...; kwargs...) end +""" + add_output(sys::NamedStateSpace, C2::AbstractArray, D2 = 0; y) + +Add outputs to `sys` corresponding to the output matrix `C2` and the feedthrough matrix `D2` to the system `sys`. + +# Arguments: +- `y`: The names used for the new outputs. If not provided, the names will be generated automatically. + +See also [`measure`](@ref) for a simpler way to output state variables. +""" +function CS.add_output(sys::NamedStateSpace, C2::AbstractArray, D2=0; y = [Symbol("y_$i") for i in (1:size(C2, 1)) .+ sys.ny]) + T = promote_type(CS.numeric_type(sys), eltype(C2), eltype(D2)) + A,B,C,D = ssdata(sys) + D3 = D2 == 0 ? zeros(T, size(C2, 1), sys.nu) : D2 + x = sys.x + u = sys.u + y = [sys.y; y] + named_ss(ss(A, B, [C; C2], [D; D3]), sys.timeevol; x, u, y) +end + + function CS.minreal(sys::NamedStateSpace, args...; kwargs...) msys = minreal(sys.sys, args...; kwargs...) named_ss(msys; sys.u, sys.y, sys.name) diff --git a/test/test_named_systems2.jl b/test/test_named_systems2.jl index d9050fc4..1917d735 100644 --- a/test/test_named_systems2.jl +++ b/test/test_named_systems2.jl @@ -139,6 +139,10 @@ end G = measure(s1, :x) @test G.C == ones(1, 1) @test G.y == [:x] + + @test add_output(s1, [0.2]) isa NamedStateSpace + @test add_output(s1, [0.2], y=[:hej]).y[2] === :hej + end G1 = ss(1,1,1,0)