JuliaControl / ControlSystems.jl

A Control Systems Toolbox for Julia
https://juliacontrol.github.io/ControlSystems.jl/stable/
Other
509 stars 85 forks source link

`ssdata` & storage format of systems? #857

Closed B-LIE closed 1 year ago

B-LIE commented 1 year ago

I'm puzzled by the following:

> using ControlSystems

> sys = tf([2,1],[5,6,1])

> ssdata(sys)
(nothing, nothing, nothing, nothing)

> ssdata(ss(sys))
([0.0 0.5; -0.4 -1.2], [0.0; 1.0;;], [0.4 0.4], [0.0;;])
albheim commented 1 year ago

Yes, there are a few different ways the systems are stored. TransferFunctions constsis of a matrix of either SisoTf types storing two polynomials or SisoZpk storing a gain and a vector each for pole/zero locations. Then there is StateSpace storing four matrices, and some other versions of special state spaces as well.

The extra ;; is (I think) from normal matrix plotting since maybe version 1.8?

julia> (rand(1, 1), rand(2, 1), rand(1, 2))
([0.1785620036939919;;], [0.6089651884889287; 0.7791505974417174;;], [0.494261101514058 0.47886582862763305])

It denotes that there is a second dimension when it is not otherwise clear from the data, i.e. for a 2x1 size matrix to distinguish it from a 2-length vector.

baggepinnen commented 1 year ago

Why was this puzzling? Transfer functions and statespace models have quite different properties when it comes to computation and it makes sense to be able to represent both. For one, transfer functions can represent non-proper systems, whereas statespace models (without mass matrix) can not. The computations involved in computing, e.g., frequency responses are also vastly different for the two different representations.

baggepinnen commented 1 year ago

I also encourage you to actually look at the source code, it would trivially answer this kind of question, a big benefit of using open-source software ;)

B-LIE commented 1 year ago

Why was this puzzling?

"Puzzling" for one with background in MATLAB's ControlToolbox - where ssdata responds with A,B,C,D matrices irrespective of which constructor was used. Also, my impression is that numerically speaking, it is better with storing models in State Space form, so I thought that was done. The documentation of ssdata uses generic sys as argument.

But you are right - I shouldn't assume that ControlSystems.jl has exactly the same use as MATLAB's toolbox.

As you say, storing the system as a transfer function has some advantages in that transfer functions can handle infinite dimensional systems (such as with the delay) function. The delay is, of course, a representation of the simple advection equation $\frac{\partial x}{\partial t} = -v\frac{\partial x}{\partial z}$ with $z \in [0,L]$ leading to Laplace transform $\exp{(-\tau s)}$ with $\tau = L/v$. Many other fancy transfer functions can be found which are not fractions of polynomials, such as in various heat exchangers. It would be cool if ControlSystems could allow for such types by multiplying a transfer function with other functions (such as those given by linear PDEs), but there is perhaps not a big "market" for this :-o.

Anyways, my confusion was due to MATLAB experience.

B-LIE commented 1 year ago

I also encourage you to actually look at the source code

I tried to open the ControlSystems.jl source code in GitHub, but didn't really find ssdata.

However, utility function Base.functionloc found it. [I've had experience in the past where Base.functionloc gives an error message, though.]

baggepinnen commented 1 year ago

I can recommend the function dump to inspect the structure of any composed object

julia> H = tf(1, [1,1]);

julia> dump(H)
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}}
  matrix: Array{ControlSystemsBase.SisoRational{Int64}}((1, 1))
    1: ControlSystemsBase.SisoRational{Int64}
      num: Polynomials.Polynomial{Int64, :x}
        coeffs: Array{Int64}((1,)) [1]
      den: Polynomials.Polynomial{Int64, :x}
        coeffs: Array{Int64}((2,)) [1, 1]
  timeevol: Continuous Continuous()

The macro @edit is also useful in locating the source of the code that is being called

julia> @edit ssdata(H)

As a side, we represent time-delay systems as an LFT of a statespace system with a time-delay block:

julia> tf(1, [1,1]) * delay(2.0)
DelayLtiSystem{Float64, Float64}

P: StateSpace{Continuous, Float64}
A = 
 -1.0
B = 
 0.0  1.0
C = 
 1.0
 0.0
D = 
 0.0  0.0
 1.0  0.0

Continuous-time state-space model

Delays: [2.0]

you can read more about this in the paper https://github.com/JuliaControl/CDC2021/blob/master/CDC2021.pdf

baggepinnen commented 1 year ago

Also, my impression is that numerically speaking, it is better with storing models in State Space form, so I thought that was done.

For many things yes, but not for everything, we thus allow the user to choose the representation. We also store zpk models as separate arrays of zeros poles and gain, which is preferable in some situations.

We have a whole page in the documentation dedicated to these choices https://juliacontrol.github.io/ControlSystems.jl/stable/man/numerical/