Closed freemin7 closed 1 year ago
You need parenthesis around the Tf due to the Julia operator precedence
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
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.
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
))))
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]
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
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.
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.
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.
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)`.
While both
plot(step(delay(5)))
andplot(step(((s+1))/((s+2)*(s+0.5))))
are proper and do plot.