diff --git a/docs/src/man/creating_systems.md b/docs/src/man/creating_systems.md index b4cc75dcf..2767b930b 100644 --- a/docs/src/man/creating_systems.md +++ b/docs/src/man/creating_systems.md @@ -268,19 +268,6 @@ P = ssrand(2,3,1) # A random 2×3 MIMO system sys_array = getindex.(Ref(P), 1:P.ny, (1:P.nu)') ``` -### Creating arrays with different types of systems -When calling `hcat/vcat`, Julia automatically tries to promote the types to the smallest common supertype, this means that creating an array with one continuous and one discrete-time system fails -```@example MIMO -P_cont = ssrand(2,3,1) -P_disc = ssrand(2,3,1, Ts=1) -@test_throws ErrorException [P_cont, P_disc] # ERROR: Sampling time mismatch -``` -You can explicitly tell Julia that you want a particular supertype, e.g, -```@example MIMO -StateSpace[P_cont, P_disc] -``` -The type `StateSpace` is abstract, since the type parameters are not specified. - ## Demo systems The module `ControlSystemsBase.DemoSystems` contains a number of demo systems demonstrating different kinds of dynamics. diff --git a/lib/ControlSystemsBase/src/ControlSystemsBase.jl b/lib/ControlSystemsBase/src/ControlSystemsBase.jl index 3a523f7f7..60d25b9a6 100644 --- a/lib/ControlSystemsBase/src/ControlSystemsBase.jl +++ b/lib/ControlSystemsBase/src/ControlSystemsBase.jl @@ -237,7 +237,7 @@ function __init__() print(io, " for automatic discretization (applicable to systems without delays or nonlinearities only).") end plots_id = Base.PkgId(UUID("91a5bcdd-55d7-5caf-9e0b-520d859cae80"), "Plots") - if nameof(exc.f) === :plot && parentmodule(argtypes[1]) == @__MODULE__() && !haskey(Base.loaded_modules, plots_id) + if exc.f !== nothing && nameof(exc.f) === :plot && parentmodule(argtypes[1]) == @__MODULE__() && !haskey(Base.loaded_modules, plots_id) printstyled(io, "\nPlotting is not available unless Plots.jl is loaded manually. Call `using Plots` before plotting.", color=:green, bold=true) elseif (exc.f == /) && argtypes[2] <: DelayLtiSystem print(io, "A delayed system can not be inverted. Consider use of the function `feedback`.") diff --git a/lib/ControlSystemsBase/src/connections.jl b/lib/ControlSystemsBase/src/connections.jl index 9d3511352..b002214c5 100644 --- a/lib/ControlSystemsBase/src/connections.jl +++ b/lib/ControlSystemsBase/src/connections.jl @@ -138,6 +138,16 @@ end Base.typed_hcat(::Type{S}, X::Number...) where {S<:LTISystem} = hcat(convert.(S, X)...) Base.typed_hcat(::Type{S}, X::Union{AbstractArray{<:Number,1}, AbstractArray{<:Number,2}}...) where {S<:LTISystem} = hcat(convert.(S, X)...) +## Mixed-type array creation +# When creating an array of systems, an error may be thrown if the default Base.vect is called that tries to promote all systems to a common type. E.g., when using non-proper transfer functions and statespace systems. We thus opt out of the conversion with the method below +function Base.vect(X::LTISystem...) + LTISystem[X...] +end + +function Base.vect(X::T...) where T <: LTISystem + T[X...] +end + """ add_input(sys::AbstractStateSpace, B2::AbstractArray, D2 = 0) diff --git a/lib/ControlSystemsBase/src/types/StateSpace.jl b/lib/ControlSystemsBase/src/types/StateSpace.jl index a58967956..ee3c89a5f 100644 --- a/lib/ControlSystemsBase/src/types/StateSpace.jl +++ b/lib/ControlSystemsBase/src/types/StateSpace.jl @@ -248,7 +248,13 @@ end ## Approximate ## function isapprox(sys1::ST1, sys2::ST2; kwargs...) where {ST1<:AbstractStateSpace,ST2<:AbstractStateSpace} fieldnames(ST1) == fieldnames(ST2) || (return false) - return all(isapprox(getfield(sys1, f), getfield(sys2, f); kwargs...) for f in fieldnames(ST1)) + return all(fieldnames(ST1)) do f + if fieldtype(ST1, f) <: Union{Number, AbstractArray{<:Number}, LTISystem} + isapprox(getfield(sys1, f), getfield(sys2, f); kwargs...) + else + getfield(sys1, f) == getfield(sys2, f) + end + end end ## ADDITION ## diff --git a/lib/ControlSystemsBase/src/types/TransferFunction.jl b/lib/ControlSystemsBase/src/types/TransferFunction.jl index ff2b2fbe7..37ddeee5b 100644 --- a/lib/ControlSystemsBase/src/types/TransferFunction.jl +++ b/lib/ControlSystemsBase/src/types/TransferFunction.jl @@ -105,7 +105,7 @@ isrational(::TransferFunction) = true ##################################################################### ## EQUALITY ## -function ==(G1::TransferFunction, G2::TransferFunction) +function ==(G1::T, G2::T) where T<:TransferFunction fields = (:timeevol, :ny, :nu, :matrix) for field in fields if getproperty(G1, field) != getproperty(G2, field) @@ -115,6 +115,8 @@ function ==(G1::TransferFunction, G2::TransferFunction) return true end +==(G1::TransferFunction, G2::TransferFunction) = ==(promote(G1,G2)...) + ## Approximate ## function isapprox(G1::TransferFunction, G2::TransferFunction; kwargs...) G1, G2 = promote(G1, G2) diff --git a/lib/ControlSystemsBase/src/types/conversion.jl b/lib/ControlSystemsBase/src/types/conversion.jl index c43808f2c..8c8b9e39e 100644 --- a/lib/ControlSystemsBase/src/types/conversion.jl +++ b/lib/ControlSystemsBase/src/types/conversion.jl @@ -136,8 +136,8 @@ function siso_tf_to_ss(T::Type, f::SisoRational) num0, den0 = numvec(f), denvec(f) # Normalize the numerator and denominator to allow realization of transfer functions # that are proper, but not strictly proper - num = num0 / den0[1] - den = den0 / den0[1] + num = num0 ./ den0[1] + den = den0 ./ den0[1] N = length(den) - 1 # The order of the rational function f diff --git a/lib/ControlSystemsBase/test/test_connections.jl b/lib/ControlSystemsBase/test/test_connections.jl index c58c9265f..7a34969df 100644 --- a/lib/ControlSystemsBase/test/test_connections.jl +++ b/lib/ControlSystemsBase/test/test_connections.jl @@ -148,7 +148,15 @@ P = ss(-1.0, 2.0, 3.0, 4.0) @test [2.5 P 3.5] == ss(-1.0, [0.0 2.0 0.0], 3.0, [2.5 4.0 3.5]) @test [2.5; P; 3.5] == ss(-1.0, 2.0, [0.0; 3.0; 0.0], [2.5; 4.0; 3.5]) - +# Test vector creation +v = [ssrand(1,1,1), tf(1)] +@test v isa Vector{LTISystem} +@test v[1] isa StateSpace{Continuous, Float64} +@test v[2] isa TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}} + +# Test vector creation +v = [tf(1), tf(1)] +@test v isa Vector{TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}}} # Combination tfRational and sisoZpk Czpk_111 = zpk([-2],[-5],1) diff --git a/lib/ControlSystemsBase/test/test_zpk.jl b/lib/ControlSystemsBase/test/test_zpk.jl index 719a44e8e..955eb5cbc 100644 --- a/lib/ControlSystemsBase/test/test_zpk.jl +++ b/lib/ControlSystemsBase/test/test_zpk.jl @@ -153,7 +153,7 @@ k = 0.3 @test eltype(fill(zpk(1,0.005)/zpk(2, 0.005),2)) <: TransferFunction @test eltype(fill(zpk(1)+1,2)) <: TransferFunction -@test eltype([tf(1,1), zpk(1,1)]) <: TransferFunction +@test eltype([tf(1,1), zpk(1,1)]) <: LTISystem zpk(tf([1 2; 3 4])) == zpk([1 2; 3 4])