Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

delay*tf being recognized as improper even if it is not. #830

Closed
freemin7 opened this issue Apr 22, 2023 · 10 comments
Closed

delay*tf being recognized as improper even if it is not. #830

freemin7 opened this issue Apr 22, 2023 · 10 comments

Comments

@freemin7
Copy link
Contributor

⌃ [a6e380b2] ControlSystems v1.7.1
plot(step(delay(5)*((s+1))/((s+2)*(s+0.5))))
ERROR: System is improper, a state-space representation is impossible
Stacktrace:
 [1] error(s::String)
   @ Base .\error.jl:35
 [2] convert(::Type{StateSpace{Continuous, Float64}}, G::TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}}; balance::Bool)
   @ ControlSystemsBase C:\Users\joto\.julia\packages\ControlSystemsBase\MHo5M\src\types\conversion.jl:92
 [3] convert
   @ C:\Users\joto\.julia\packages\ControlSystemsBase\MHo5M\src\types\conversion.jl:90 [inlined]
 [4] convert
   @ C:\Users\joto\.julia\packages\ControlSystemsBase\MHo5M\src\types\DelayLtiSystem.jl:62 [inlined]
 [5] _promote
   @ .\promotion.jl:336 [inlined]
 [6] promote
   @ .\promotion.jl:359 [inlined]
 [7] *(sys1::DelayLtiSystem{Float64, Float64}, sys2::TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}})
   @ ControlSystemsBase C:\Users\joto\.julia\packages\ControlSystemsBase\MHo5M\src\types\Lti.jl:4
 [8] top-level scope
   @ REPL[28]:1

While both plot(step(delay(5))) and plot(step(((s+1))/((s+2)*(s+0.5)))) are proper and do plot.

@freemin7 freemin7 changed the title delay being recognized as improper even if it is not. delay*tf being recognized as improper even if it is not. Apr 22, 2023
@baggepinnen
Copy link
Member

You need parenthesis around the Tf due to the Julia operator precedence

@freemin7
Copy link
Contributor Author

freemin7 commented Apr 23, 2023

I am not sure what you mean but t(step((delay(5))*((s+1))/((s+2)*(s+0.5)))) did fail with the same error

@albheim
Copy link
Member

albheim commented Apr 24, 2023

The problem us that the multiplication of the delay with the nominator happens first, creating a temporary value that is invalid. So putting parenthesis to force the division in the tf to happen before the delay multiplication should help.

I believe this happens since the delay is represented using state space it will try to promote the denominator to state space before multiplying, which does not work.

@baggepinnen
Copy link
Member

c * b / a is in julia parsed as (c*a) / b)

julia> Meta.@lower c * b / a
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1%1 = c * b
│   %2 = %1 / a
└──      return %2
))))

@baggepinnen
Copy link
Member

julia> delay(5)*((s+1)/((s+2)*(s+0.5)))
DelayLtiSystem{Float64, Float64}

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

Continuous-time state-space model

Delays: [5.0]

@freemin7
Copy link
Contributor Author

The problem us that the multiplication of the delay with the nominator happens first, creating a temporary value that is invalid. So putting parenthesis to force the division in the tf to happen before the delay multiplication should help.

There are plenty of algorithms in control engineering where improper transfer functions play an important role as intermidiary steps. That those are not supported when the delays are involved together with the unhelpfull error messages makes me quiet unhappy that the issue is remarked as resolved.

I realise that that would require a new type of object to be handled properly something like a partial fraction decomposition with power of the delays (and their possible cross terms?) as a basis could work

@baggepinnen
Copy link
Member

baggepinnen commented Apr 25, 2023

It would of course be good to have better support for non-proper systems, but it requires a descriptor-system type, something we don't have at the moment. See DescriptorSystems.jl for such an implementation, albeit without support for delays.

@freemin7
Copy link
Contributor Author

In my naive understanding if transfer function with delays are not converted into state space models eagerly all the existing machinery should mostly work fine. Atleast in my superficial reading Descriptor Systems allows handling of DAEs. I don't immediatly see how having them would allow for a more straightforward implementation of this functionality. Addition, multiplication and division of transfer function handling of delays are obvious, however handling the infinite roots delays introduces might complicate things.

However i get the impression that this does not look like a fruitful approach to you. Let me know if i am mistaken. Also thank you for that reference to descriptor systems. DEAs came up today and i was not away of that fomralism.

@baggepinnen
Copy link
Member

baggepinnen commented Apr 26, 2023

Descriptor systems can represent non-proper systems, we use this in ControlSystemsMTK, see an example here.

I vastly prefer state-space realizations for several reasons, polynomial models have very poor numerics, even 6-8 order systems suffer from numerical roundoff when represented as rational functions with polynomials. The transfer-function approach would also have a hard time representing things like feedback(P) = $\dfrac{P}{1 + P}$ if $P$ contains delays. For that, you'd have to be able to represent polynomials not only in $s$, but also in $e^{sL_i} \quad \forall \quad i$. Even if you can represent such a system, how would you simulate it? You can kind-of simulate them using the Fourier-transform, but to properly simulate them in the time domain you need a delayed state-space representation.

All this to say that I think implementing a transfer-function type with delay support would have a very low ratio of "inspiration / transpiration", or put in other words, poor bang for the buck. Implementing <: LTISystem types is rather tedious and has a lot of corner cases that must be handled.


Related:

Transfer functions can be represented symbolically as well

using Symbolics
@variables s

julia> P = exp(-s) * (s+3) / ((s+1)*(s+2))
((3 + s)*exp(-s)) / ((1 + s)*(2 + s))

julia> P / (1 + P)
((3 + s)*exp(-s)) / ((1 + s)*(1 + ((3 + s)*exp(-s)) / ((1 + s)*(2 + s)))*(2 + s))

julia> P / (1 + P) |> simplify
((3 + s)*exp(-s)) / (2 + s^2 + 3s + 3exp(-s) + s*exp(-s))

can represent a much broader class of non-rational transfer functions than only time delays. You can also evaluate the frequency-response of a general transfer function as a regular julia function

tf(s) = exp(-sqrt(s))
tf(3.14im)

So unless you need to simulate the system, there are several options that allow you to compute the frequency response of arbitrarily complicated non-rational transfer functions.

@baggepinnen
Copy link
Member

I added a better (hopefully) error message

julia> delay(5)*((s+1))/((s+2)*(s+0.5))
ERROR: The transfer function is not proper and can not be converted to a DelayLtiSystem type.
If you tried to form the system `exp(sL) * B / A` where `B / A` is proper, add parenthesis to make it `exp(sL) * (B / A)`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants