DDESystem
Construction of DDESystem
A DDESystem
is represented by the following state equation
\[ \dot{x} = f(x, h, u, t) \quad t \geq t_0\]
where $t$ is the time, $x$ is the value of the state
, $u$ is the value of the input
. $h$ is the history function for which
\[ x(t) = h(t) \quad t \leq t_0\]
and by the output equation
\[ y = g(x, u, t) \]
where $y$ is the value of the output
.
As an example, consider a system with the state equation
\[ \begin{array}{l} \dot{x} = -x(t - \tau) \quad t \geq 0 \\ x(t) = 1. -\tau \leq t \leq 0 \\ \end{array}\]
First, we define the history function histfunc
,
julia> const out = zeros(1)
1-element Vector{Float64}:
0.0
julia> histfunc(out, u, t) = (out .= 1.);
Note that histfunc
mutates a vector out
. This mutation is for performance reasons
. Next the state function can be defined
julia> function statefunc(dx, x, h, u, t)
h(out, u, t - tau) # Update out vector
dx[1] = out[1] + x[1]
end
statefunc (generic function with 1 method)
and let us take all the state variables as outputs. Thus, the output function is
julia> outputfunc(x, u, t) = x
outputfunc (generic function with 1 method)
Next, we need to define the history
for the system. History is defined by specifying a history function, and the type of the lags. There may be two different lag: constant lags which are independent of the state variable $x$ and the dependent lags which are mainly the functions of the state variable $x$. Note that for this example, the have constant lags. Thus,
julia> tau = 1
1
julia> conslags = [tau]
1-element Vector{Int64}:
1
At this point, we are ready to construct the system.
julia> ds = DDESystem(righthandside=statefunc, history=histfunc, readout=outputfunc, state=[1.], input=nothing, output=Outport(), constlags=conslags, depslags=nothing)
ERROR: MethodError: no method matching FastBroadcast.BroadcastCharacteristics()
The applicable method may be too new: running in world age 47354, while current world is 95794.
Closest candidates are:
FastBroadcast.BroadcastCharacteristics() at /juliateam/.julia/packages/FastBroadcast/2JngL/src/FastBroadcast.jl:111 (method too new to be called from this world context.)
FastBroadcast.BroadcastCharacteristics(!Matched::Expr, !Matched::Vector{Symbol}, !Matched::Bool, !Matched::Bool) at /juliateam/.julia/packages/FastBroadcast/2JngL/src/FastBroadcast.jl:106 (method too new to be called from this world context.)
FastBroadcast.BroadcastCharacteristics(!Matched::Any, !Matched::Any, !Matched::Any, !Matched::Any) at /juliateam/.julia/packages/FastBroadcast/2JngL/src/FastBroadcast.jl:106 (method too new to be called from this world context.)
Basic Operation of DDESystem
The basis operaiton of DDESystem
is the same as those of other dynamical systems. When triggered from its trigger
link, the DDESystem
reads its time from its trigger
link, reads input, solves its differential equation, computes its output and writes the computed output to its output
bus. To drive DDESystem
, we must first launch it,
julia> iport, trg, hnd = Inport(), Outpin(), Inpin{Bool}()
(Inport(numpins:1, eltype:Inpin{Float64}), Outpin(eltype:Float64, isbound:false), Inpin(eltype:Bool, isbound:false))
julia> connect!(ds.output, iport)
ERROR: UndefVarError: ds not defined
julia> connect!(trg, ds.trigger)
ERROR: UndefVarError: ds not defined
julia> connect!(ds.handshake, hnd)
ERROR: UndefVarError: ds not defined
julia> task = launch(ds)
ERROR: UndefVarError: ds not defined
julia> task2 = @async while true
all(take!(iport) .=== NaN) && break
end
Task (failed) @0x00007fee0edb9270
MethodError: no method matching take!(::Missing)
Closest candidates are:
take!(!Matched::IOBuffer) at iobuffer.jl:385
take!(!Matched::Base.GenericIOBuffer) at iobuffer.jl:370
take!(!Matched::Channel) at channels.jl:383
...
Stacktrace:
[1] take!(pin::Inpin{Float64})
@ Causal ~/.julia/packages/Causal/vOCIT/src/connections/pin.jl:111
[2] _broadcast_getindex_evalf
@ ./broadcast.jl:648 [inlined]
[3] _broadcast_getindex
@ ./broadcast.jl:621 [inlined]
[4] getindex
@ ./broadcast.jl:575 [inlined]
[5] macro expansion
@ ./broadcast.jl:984 [inlined]
[6] macro expansion
@ ./simdloop.jl:77 [inlined]
[7] copyto!
@ ./broadcast.jl:983 [inlined]
[8] copyto!
@ ./broadcast.jl:936 [inlined]
[9] copy
@ ./broadcast.jl:908 [inlined]
[10] materialize
@ ./broadcast.jl:883 [inlined]
[11] take!(inport::Inport{Inpin{Float64}})
@ Causal ~/.julia/packages/Causal/vOCIT/src/connections/port.jl:186
[12] macro expansion
@ ./none:2 [inlined]
[13] (::Main.ex-dde_system_ex.var"#1#2")()
@ Main.ex-dde_system_ex ./task.jl:406
When launched, ds
is drivable. To drive ds
, we can use the syntax drive(ds, t)
or put!(ds.trigger, t)
where t
is the time until which ds
is to be driven.
julia> put!(trg, 1.)
ERROR: MethodError: no method matching iterate(::Missing)
Closest candidates are:
iterate(!Matched::Union{LinRange, StepRangeLen}) at range.jl:664
iterate(!Matched::Union{LinRange, StepRangeLen}, !Matched::Int64) at range.jl:664
iterate(!Matched::T) where T<:Union{Base.KeySet{var"#s79", var"#s78"} where {var"#s79", var"#s78"<:Dict}, Base.ValueIterator{var"#s77"} where var"#s77"<:Dict} at dict.jl:693
...
When driven, ds
reads the time t
from its trigger
link, (since its input is nothing
, ds
does nothing during its input reading stage), solves its differential equation, computes output and writes the value of its output to its output
bus. To signify, the step was taken with success, ds
writes true
to its handshake
which must be read to further drive ds
. For this, we can use the syntax approve!(ds)
or take!(ds.handshake)
.
julia> take!(hnd)
ERROR: MethodError: no method matching take!(::Missing)
Closest candidates are:
take!(!Matched::IOBuffer) at iobuffer.jl:385
take!(!Matched::Base.GenericIOBuffer) at iobuffer.jl:370
take!(!Matched::Channel) at channels.jl:383
...
We can continue to drive ds
.
julia> for t in 2. : 10.
put!(trg, t)
take!(hnd)
end
ERROR: MethodError: no method matching iterate(::Missing)
Closest candidates are:
iterate(!Matched::Union{LinRange, StepRangeLen}) at range.jl:664
iterate(!Matched::Union{LinRange, StepRangeLen}, !Matched::Int64) at range.jl:664
iterate(!Matched::T) where T<:Union{Base.KeySet{var"#s79", var"#s78"} where {var"#s79", var"#s78"<:Dict}, Base.ValueIterator{var"#s77"} where var"#s77"<:Dict} at dict.jl:693
...
When launched, we constructed a task
whose state is running
which implies that ds
can be driven.
julia> task
ERROR: UndefVarError: task not defined
julia> task2
Task (failed) @0x00007fee0edb9270
MethodError: no method matching take!(::Missing)
Closest candidates are:
take!(!Matched::IOBuffer) at iobuffer.jl:385
take!(!Matched::Base.GenericIOBuffer) at iobuffer.jl:370
take!(!Matched::Channel) at channels.jl:383
...
Stacktrace:
[1] take!(pin::Inpin{Float64})
@ Causal ~/.julia/packages/Causal/vOCIT/src/connections/pin.jl:111
[2] _broadcast_getindex_evalf
@ ./broadcast.jl:648 [inlined]
[3] _broadcast_getindex
@ ./broadcast.jl:621 [inlined]
[4] getindex
@ ./broadcast.jl:575 [inlined]
[5] macro expansion
@ ./broadcast.jl:984 [inlined]
[6] macro expansion
@ ./simdloop.jl:77 [inlined]
[7] copyto!
@ ./broadcast.jl:983 [inlined]
[8] copyto!
@ ./broadcast.jl:936 [inlined]
[9] copy
@ ./broadcast.jl:908 [inlined]
[10] materialize
@ ./broadcast.jl:883 [inlined]
[11] take!(inport::Inport{Inpin{Float64}})
@ Causal ~/.julia/packages/Causal/vOCIT/src/connections/port.jl:186
[12] macro expansion
@ ./none:2 [inlined]
[13] (::Main.ex-dde_system_ex.var"#1#2")()
@ Main.ex-dde_system_ex ./task.jl:406
As long as the state of the task
is running
, ds
can be driven. To terminate task
safely, we need to terminate the ds
.
julia> put!(trg, NaN)
ERROR: MethodError: no method matching iterate(::Missing)
Closest candidates are:
iterate(!Matched::Union{LinRange, StepRangeLen}) at range.jl:664
iterate(!Matched::Union{LinRange, StepRangeLen}, !Matched::Int64) at range.jl:664
iterate(!Matched::T) where T<:Union{Base.KeySet{var"#s79", var"#s78"} where {var"#s79", var"#s78"<:Dict}, Base.ValueIterator{var"#s77"} where var"#s77"<:Dict} at dict.jl:693
...
Note that the state of task
is done
which implies that ds
is not drivable any more.
Note that the output values of ds
is written to its output
bus.
julia> iport[1].link.buffer
ERROR: type Missing has no field buffer
Full API
Causal.@def_dde_system
— Macro@def_dde_system ex
where ex
is the expression to define to define a new AbstractDDESystem component type. The usage is as follows:
@def_dde_system mutable struct MyDDESystem{T1,T2,T3,...,TN,OP,RH,RO,ST,IP,OP} <: AbstractDDESystem
param1::T1 = param1_default # optional field
param2::T2 = param2_default # optional field
param3::T3 = param3_default # optional field
⋮
paramN::TN = paramN_default # optional field
constlags::CL = constlags_default # mandatory field
depslags::DL = depslags_default # mandatory field
righthandside::RH = righthandside_function # mandatory field
history::HST = history_function # mandatory field
readout::RO = readout_function # mandatory field
state::ST = state_default # mandatory field
input::IP = input_defauult # mandatory field
output::OP = output_default # mandatory field
end
Here, MyDDESystem
has N
parameters. MyDDESystem
is represented by the righthandside
and readout
function. state
, input
and output
is the state, input port and output port of MyDDESystem
.
righthandside
must have the signature
function righthandside(dx, x, u, t, args...; kwargs...)
dx .= .... # update dx
end
and readout
must have the signature
function readout(x, u, t)
y = ...
return y
end
New DDE system must be a subtype of AbstractDDESystem
to function properly.
Example
julia> _delay_feedback_system_cache = zeros(1)
1-element Array{Float64,1}:
0.0
julia> _delay_feedback_system_tau = 1.
1.0
julia> _delay_feedback_system_constlags = [1.]
1-element Array{Float64,1}:
1.0
julia> _delay_feedback_system_history(cache, u, t) = (cache .= 1.)
_delay_feedback_system_history (generic function with 1 method)
julia> function _delay_feedback_system_rhs(dx, x, h, u, t,
cache=_delay_feedback_system_cache, τ=_delay_feedback_system_tau)
h(cache, u, t - τ) # Update cache
dx[1] = cache[1] + x[1]
end
_delay_feedback_system_rhs (generic function with 3 methods)
julia> @def_dde_system mutable struct MyDDESystem{RH, HST, RO, IP, OP} <: AbstractDDESystem
constlags::Vector{Float64} = _delay_feedback_system_constlags
depslags::Nothing = nothing
righthandside::RH = _delay_feedback_system_rhs
history::HST = _delay_feedback_system_history
readout::RO = (x, u, t) -> x
state::Vector{Float64} = rand(1)
input::IP = nothing
output::OP = Outport(1)
end
julia> ds = MyDDESystem();
Causal.DDESystem
— Typemutable struct DDESystem{CL, DL, RH, HST, RO, ST<:(AbstractVector{var"#s16875"} where var"#s16875"<:Real), IP<:(Union{var"#s16874", var"#s16873"} where {var"#s16874"<:Inport, var"#s16873"<:Nothing}), OP<:(Union{var"#s16872", var"#s16632"} where {var"#s16872"<:Outport, var"#s16632"<:Nothing}), var"4004", var"4005", var"4006", Symbol, var"4007", Float64, var"4008", var"4009", var"4010", var"4011", var"4012", var"4013"} <: AbstractDDESystem
Construct a generic DDE system
Fields
constlags::Any
Constant lags
depslags::Any
Dependent lags
righthandside::Any
Right-hand-side function
history::Any
History function
readout::Any
Readout function
state::AbstractVector{var"#s16875"} where var"#s16875"<:Real
State
input::Union{var"#s16874", var"#s16873"} where {var"#s16874"<:Inport, var"#s16873"<:Nothing}
Input. Expected to be an
Inport
orNothing
output::Union{var"#s16872", var"#s16632"} where {var"#s16872"<:Outport, var"#s16632"<:Nothing}
Output port
trigger::Any
handshake::Any
callbacks::Any
name::Any
id::Any
t::Any
modelargs::Any
modelkwargs::Any
solverargs::Any
solverkwargs::Any
alg::Any
integrator::Any
Causal.DelayFeedbackSystem
— Typemutable struct DelayFeedbackSystem{CL<:(AbstractVector{var"#s16875"} where var"#s16875"<:Real), RH, HST, RO, ST<:(AbstractVector{var"#s16874"} where var"#s16874"<:Real), IP<:(Union{var"#s16873", var"#s16872"} where {var"#s16873"<:Inport, var"#s16872"<:Nothing}), OP<:(Union{var"#s16632", var"#s16631"} where {var"#s16632"<:Outport, var"#s16631"<:Nothing}), var"4004", var"4005", var"4006", Symbol, var"4007", Float64, var"4008", var"4009", var"4010", var"4011", var"4012", var"4013"} <: AbstractDDESystem
Constructs DelayFeedbackSystem
Fields
constlags::AbstractVector{var"#s16875"} where var"#s16875"<:Real
Constant lags
depslags::Nothing
Dependent lags
righthandside::Any
Right-hand-side function
history::Any
History function
readout::Any
Readout function
state::AbstractVector{var"#s16874"} where var"#s16874"<:Real
State
input::Union{var"#s16873", var"#s16872"} where {var"#s16873"<:Inport, var"#s16872"<:Nothing}
Input, Expected to be an
Inport
ofNothing
output::Union{var"#s16632", var"#s16631"} where {var"#s16632"<:Outport, var"#s16631"<:Nothing}
Output port
trigger::Any
handshake::Any
callbacks::Any
name::Any
id::Any
t::Any
modelargs::Any
modelkwargs::Any
solverargs::Any
solverkwargs::Any
alg::Any
integrator::Any