RODESystem
Construction of RODESystem
A RODESystem
is represented by the state function
\[\begin{array}{l} dx = f(x, u, t, W) \end{array}\]
and the output function
\[ y = g(x, u, t)\]
where $t$ is the time, $x \in R^n$ is the state, $u \in R^p$ and $y \in R^m$ is output of the system. Therefore to construct a RODESystem
, we need to define statefunc
and outputfunc
with the corresponding syntax,
function statefunc(dx, x, u, t)
dx .= ... # Update dx
end
and
function outputfunc(x, u, t)
y = ... # Compute y
return y
end
As an example, consider the system with the state function
\[ \begin{array}{l} dx_1 = 2 x_1 sin(W_1 - W_2) \\ dx_2 = -2 x_2 cos(W_1 + W_2) \end{array}\]
and with the output function
\[ y = x\]
That is, all the state variable are taken as output. The statefunc
and the outputfunc
is defined as,
julia> function statefunc(dx, x, u, t, W)
dx[1] = 2x[1]*sin(W[1] - W[2])
dx[2] = -2x[2]*cos(W[1] + W[2])
end
statefunc (generic function with 1 method)
julia> outputfunc(x, u, t) = x
outputfunc (generic function with 1 method)
To construct the RODESystem
, we need to specify the initial condition and time.
julia> x0 = [1., 1.]
2-element Array{Float64,1}:
1.0
1.0
julia> t = 0.
0.0
Note from statefunc
, the system has not any input, i.e. input is nothing, and has an output with a dimension of 1.
julia> input = nothing
julia> output = Outport(2)
2-element Outport{Outpin{Float64}}:
Outpin(eltype:Float64, isbound:false)
Outpin(eltype:Float64, isbound:false)
We are ready to construct the system
julia> ds = RODESystem(righthandside=statefunc, readout=outputfunc, state=x0, input=input, output=output, solverkwargs=(dt=0.01,))
RODESystem(righthandside:statefunc, readout:outputfunc, state:[1.0, 1.0], t:0.0, input:nothing, output:Outport(numpins:2, eltype:Outpin{Float64}))
Note that ds
has a solver to solve its state function statefunc
which is random differential equation. To solve its statefunc
, the step size of the solver must be specified. See Random Differential Equtions
of DifferentialEquations
package.
Basic Operation of RODESystem
When a RODESystem
is triggered from its trigger
link, it read the current time from its trigger
link, reads its input (if available, i.e. its input is not nothing), solves its state function, computes its output value and writes its output value its output
bus (again, if available, i.e., its output bus is not nothing). To drive a RODESystem
, it must be launched
. Let us continue with ds
constructed in the previous section.
julia> iport, trg, hnd = Inport(2), Outpin(), Inpin{Bool}()
(Inport(numpins:2, eltype:Inpin{Float64}), Outpin(eltype:Float64, isbound:false), Inpin(eltype:Bool, isbound:false))
julia> connect!(ds.output, iport)
2-element Array{Link{Float64},1}:
Link(state:open, eltype:Float64, isreadable:false, iswritable:false)
Link(state:open, eltype:Float64, isreadable:false, iswritable:false)
julia> connect!(trg, ds.trigger)
Link(state:open, eltype:Float64, isreadable:false, iswritable:false)
julia> connect!(ds.handshake, hnd)
Link(state:open, eltype:Bool, isreadable:false, iswritable:false)
julia> task = launch(ds)
Task (runnable) @0x00007f1fd1dde4a0
julia> task2 = @async while true
all(take!(iport) .=== NaN) && break
end
Task (runnable) @0x00007f1fd08e5600
When launched, ds
is ready to be driven. We can drive ds
by drive(ds, t)
or put!(ds.trigger, t)
where t
is the time until which we will drive ds
.
julia> put!(trg, 1.)
When triggered, ds
read the time t
from its trigger
link, solved its differential equation, computed its value and writes its output value to its output
bus. To signal that, the evolution is succeeded, ds
writes true
to its handshake
link which must be taken to further drive ds
. (approve!(ds)
) can also be used.
julia> take!(hnd)
true
We can continue to drive ds
.
julia> for t in 2. : 10.
put!(trg, t)
take!(hnd)
end
After each evolution, ds
writes its current output value to its output
bus.
julia> [outbuf(pin.link.buffer) for pin in iport]
2-element Array{Array{Float64,1},1}:
[9.536488051310913, 2.046849298015438, 3.8897947784807307, 3.694042354973167, 14.149497021758512, 14.865360944963289, 46.43003538928599, 13.723252688576622, 4.974400108269335, 1.4521684144644695 … 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
[0.0003846347205336689, 0.0011255749006023582, 0.00030492388188520675, 0.00013676892261963925, 0.0004967323269456172, 0.0013339329886276146, 0.0035696178931756634, 0.006885898820284732, 0.04434468568361265, 0.3073083632039139 … 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
When launched, a task
was constructed which still running. As long as no exception is thrown during the evolution of ds
, the state of task
is running which implies ds
can be driven.
julia> task
Task (runnable) @0x00007f1fd1dde4a0
julia> task2
Task (runnable) @0x00007f1fd08e5600
To terminate the task
safely, ds
should be terminated safely.
julia> put!(trg, NaN)
julia> put!(ds.output, [NaN, NaN])
2-element Array{Float64,1}:
NaN
NaN
Note that the state of task
is done
which implies the task
has been terminated safely.
julia> task
Task (done) @0x00007f1fd1dde4a0
julia> task2
Task (done) @0x00007f1fd08e5600
Full API
Causal.@def_rode_system
— Macro@def_rode_system ex
where ex
is the expression to define to define a new AbstractRODESystem component type. The usage is as follows:
@def_rode_system mutable struct MyRODESystem{T1,T2,T3,...,TN,OP,RH,RO,ST,IP,OP} <: AbstractRODESystem
param1::T1 = param1_default # optional field
param2::T2 = param2_default # optional field
param3::T3 = param3_default # optional field
⋮
paramN::TN = paramN_default # optional field
righthandside::RH = righthandside_function # mandatory field
readout::RO = readout_function # mandatory field
state::ST = state_default # mandatory field
input::IP = input_default # mandatory field
output::OP = output_default # mandatory field
end
Here, MyRODESystem
has N
parameters. MyRODESystem
is represented by the righthandside
and readout
function. state
, input
and output
is the initial state, input port and output port of MyRODESystem
.
righthandside
must have the signature
function righthandside((dx, x, u, t, W, args...; kwargs...)
dx .= .... # update dx
end
and readout
must have the signature
function readout(x, u, t)
y = ...
return y
end
New RODE system must be a subtype of AbstractRODESystem
to function properly.
Example
julia> @def_rode_system mutable struct MySystem{RH, RO, IP, OP} <: AbstractRODESystem
A::Matrix{Float64} = [2. 0.; 0 -2]
righthandside::RH = (dx, x, u, t, W) -> (dx .= A * x * W)
readout::RO = (x, u, t) -> x
state::Vector{Float64} = rand(2)
input::IP = nothing
output::OP = Outport(2)
end
julia> ds = MySystem();
Causal.RODESystem
— Typemutable struct RODESystem{RH, RO, ST<:(AbstractArray{var"#s301",1} where var"#s301"<:Real), IP<:(Union{var"#s300", var"#s299"} where var"#s299"<:Nothing where var"#s300"<:Inport), OP<:(Union{var"#s298", var"#s297"} where var"#s297"<:Nothing where var"#s298"<:Outport), var"253", var"254", var"255", Symbol, var"256", Float64, var"257", var"258", var"259", var"260", var"261", var"262"} <: AbstractRODESystem
Constructs a generic RODE system
Fields
righthandside::Any
Right-hand-side function
readout::Any
Readout function
state::AbstractArray{var"#s301",1} where var"#s301"<:Real
State
input::Union{var"#s300", var"#s299"} where var"#s299"<:Nothing where var"#s300"<:Inport
Input. Expected to be an
Inport
orNothing
output::Union{var"#s298", var"#s297"} where var"#s297"<:Nothing where var"#s298"<:Outport
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.MultiplicativeNoiseLinearSystem
— Typemutable struct MultiplicativeNoiseLinearSystem{T1<:(AbstractArray{var"#s301",2} where var"#s301"<:Real), RH, RO, ST<:(AbstractArray{var"#s300",1} where var"#s300"<:Real), IP<:(Union{var"#s299", var"#s298"} where var"#s298"<:Nothing where var"#s299"<:Inport), OP<:(Union{var"#s297", var"#s296"} where var"#s296"<:Nothing where var"#s297"<:Outport), var"253", var"254", var"255", Symbol, var"256", Float64, var"257", var"258", var"259", var"260", var"261", var"262"} <: AbstractRODESystem
Constructs a MultiplicativeNoiseLinearSystem
with the dynamics
\[\begin{array}{l} \dot{x} = A x W \end{array} where `W` is the noise process.\]
Fields
A::AbstractArray{var"#s301",2} where var"#s301"<:Real
A
righthandside::Any
Right-hand-side function
readout::Any
Readout function
state::AbstractArray{var"#s300",1} where var"#s300"<:Real
State
input::Union{var"#s299", var"#s298"} where var"#s298"<:Nothing where var"#s299"<:Inport
Input. Expected to be an
Inport
orNothing
output::Union{var"#s297", var"#s296"} where var"#s296"<:Nothing where var"#s297"<:Outport
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