zekeriyasari / Causal.jl

Causal.jl - A modeling and simulation framework adopting causal modeling approach.
https://zekeriyasari.github.io/Causal.jl/dev/
Other
115 stars 7 forks source link

Different ETA when using custom type #32

Closed ValentinKaisermayer closed 4 years ago

ValentinKaisermayer commented 4 years ago

Consider the following two very similar implementations. First with a custom type that derives of AbstractODESystem:

using Jusdl, Plots

@def_ode_system  struct Sys{RH,RO,ST,IP,OP} <: AbstractODESystem 
    righthandside::RH = function ode(dx, x, u, t)
        dx[1] = x[2]
        dx[2] = x[3]
        dx[3] = -200. * x[1] - 230. * x[2] - 31. * x[3] + u[1](t)
    end    
    readout::RO = (x, u, t) -> 500. * x[1]
    state::ST = [0., 0., 0.]
    input::IP = Inport() 
    output::OP = Outport() 
end

model = Model(clock=Clock(0, 0.01, 10.))
addnode!(model, StepGenerator(delay=1.), label=:step)
addnode!(model, Sys(), label=:sys)
addnode!(model, Writer(input=Inport(2)), label=:writer)
addbranch!(model, :step => :sys, 1 => 1)
addbranch!(model, :step => :writer, 1 => 1)
addbranch!(model, :sys => :writer, 1 => 2)

sim = simulate!(model)

# Read and plot data 
t, x = read(getnode(model, :writer).component)
plot(t, x[:, 1], label="r(t)", xlabel="t")
plot!(t, x[:, 2], label="y(t)", xlabel="t")

The simulation will not really work. The estimated ETA is 06:28.

[ Info: 2020-08-18T08:24:05.463 Started simulation...
[ Info: 2020-08-18T08:24:05.85 Inspecting model...
[ Info: 2020-08-18T08:24:05.919 Done.
[ Info: 2020-08-18T08:24:05.924 Initializing the model...
[ Info: 2020-08-18T08:24:08.82 Done...
[ Info: 2020-08-18T08:24:08.822 Running the simulation...
Progress:   0%|█                                                                                                                                                                                 |  ETA: 0:06:28

Now the same system but implemented using ODESystem:

using Jusdl, Plots

function ode(dx, x, u, t)
    dx[1] = x[2]
    dx[2] = x[3]
    dx[3] = -200. * x[1] - 230. * x[2] - 31. * x[3] + u[1](t)
end

model = Model(clock=Clock(0, 0.01, 10.))
addnode!(model, StepGenerator(delay=1.), label=:step)
addnode!(model, ODESystem(righthandside=ode, readout=(x, u, t) -> 500 * x[1], state=[0., 0., 0.], input=Inport(1), output=Outport(1)), label=:sys)
addnode!(model, Writer(input=Inport(2)), label=:writer)
addbranch!(model, :step => :sys, 1 => 1)
addbranch!(model, :step => :writer, 1 => 1)
addbranch!(model, :sys => :writer, 1 => 2)

sim = simulate!(model)

# Read and plot data 
t, x = read(getnode(model, :writer).component)
plot(t, x[:, 1], label="r(t)", xlabel="t")
plot!(t, x[:, 2], label="y(t)", xlabel="t")

This one is really fast:

[ Info: 2020-08-18T08:30:03.41 Started simulation...
[ Info: 2020-08-18T08:30:19.505 Inspecting model...
[ Info: 2020-08-18T08:30:19.561 Done.
[ Info: 2020-08-18T08:30:19.563 Initializing the model...
[ Info: 2020-08-18T08:30:22.452 Done...
[ Info: 2020-08-18T08:30:22.454 Running the simulation...
Progress: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| Time: 0:00:02
[ Info: 2020-08-18T08:30:24.534 Done...
[ Info: 2020-08-18T08:30:24.536 Terminating the simulation...
[ Info: 2020-08-18T08:30:24.733 Done.

However, I can not understand the problem and what the difference between the first and second implementation is.

zekeriyasari commented 4 years ago

The Sys must be mutable.

@def_ode_system  mutable struct Sys{RH,RO,ST,IP,OP} <: AbstractODESystem 
    righthandside::RH = function ode(dx, x, u, t)
        dx[1] = x[2]
        dx[2] = x[3]
        dx[3] = -200. * x[1] - 230. * x[2] - 31. * x[3] + u[1](t)
    end    
    readout::RO = (x, u, t) -> 500. * x[1]
    state::ST = [0., 0., 0.]
    input::IP = Inport() 
    output::OP = Outport() 
end

Then, the simulation works without a problem.

The problem was that during the run stage of the simulation Sys cannot be mutated(its current time, state, etc.) and the task corresponding to Sys component fails, which causes the simulation to stop. By the way, you can use troubleshoot function to see the errors thrown during the simulation.

zekeriyasari commented 4 years ago

You may also prefer describing the model as

@defmodel model begin
    @nodes begin
        step = StepGenerator(delay=0.)
        sys = Sys() 
        writer = Writer(input=Inport(2))
    end
    @branches begin
        step[1] => sys[1]
        step[1] => writer[1]
        sys[1] => writer[2]
    end
end

which, I think, looks simpler.

ValentinKaisermayer commented 4 years ago

The Sys must be mutable.

Thank you! I see ODESystem is defined in this way but otherwise the doc strings do not say anything about it.

zekeriyasari commented 4 years ago

My fault ! The docstring of @def_ode_system will completely be revised.

ValentinKaisermayer commented 4 years ago

Sry to bother you again... I have added a simple control loop to the model:

using Jusdl, Plots

@def_ode_system mutable struct Sys{RH,RO,ST,IP,OP} <: AbstractODESystem 
    righthandside::RH = function ode(dx, x, u, t)
        dx[1] = x[2]
        dx[2] = x[3]
        dx[3] = -200. * x[1] - 230. * x[2] - 31. * x[3] + u[1](t)
    end    
    readout::RO = (x, u, t) -> 500. * x[1]
    state::ST = [0., 0., 0.]
    input::IP = Inport() 
    output::OP = Outport() 
end

@def_ode_system mutable struct PIController{RH,RO,ST,IP,OP} <: AbstractODESystem 
    kp::Float64 = 1.
    Tn::Float64 = 0.1
    righthandside::RH = (dx, x, u, t, kp = kp, Tn = Tn) -> (dx[1] = kp / Tn * u[1](t))    
    readout::RO = (x, u, t, kp = kp) -> kp * u[1](t) + x[1]
    state::ST = [0.]
    input::IP = Inport() 
    output::OP = Outport() 
end

# Construct the model 
model = Model(clock=Clock(0., 0.01, 10.))
addnode!(model, StepGenerator(delay=1.), label=:step)
addnode!(model, Adder(signs=(+, -)), label=:sum)
addnode!(model, PIController(kp=0.2, Tn=0.58), label=:pi)
addnode!(model, Sys(), label=:sys)
addnode!(model, Writer(input=Inport(2)), label=:writer)
addbranch!(model, :step => :sum, 1 => 1)
addbranch!(model, :sys => :sum, 1 => 2)
addbranch!(model, :sum => :pi, 1 => 1)
addbranch!(model, :step => :writer, 1 => 1)
addbranch!(model, :sys => :writer, 1 => 2)

# troubleshoot(model)
sim = simulate!(model)

# Read and plot data 
t, x = read(getnode(model, :writer).component)
plot(t, x[:, 1], label="r(t)", xlabel="t")
plot!(t, x[:, 2], label="y(t)", xlabel="t")

But it gets stuck when running the simulation (I do not even get the progressbar). However, troubleshoot(model) does not reveal any errors (I guess because it errors anyway)

julia> troubleshoot(model)
ERROR: MethodError: no method matching istaskfailed(::Nothing)
zekeriyasari commented 4 years ago

The block diagram of the model you try to simulate is as follows.

model

But the problem is input port of Sys and output port of PI is not terminated. In Jusdl, all the ports of the components should be terminated.

ValentinKaisermayer commented 4 years ago

My fault! I missed the connection of PI and Sys.

zekeriyasari commented 4 years ago

Besides, I added some extra checks to inspect stage of the simulation. To use them, please check out the master branch.

]add Jusdl#master
ValentinKaisermayer commented 4 years ago

Thx!

zekeriyasari commented 4 years ago

In case the simulation gets stuck and you want to see the problem, then you stop the execution by Ctrl+c first and then call troubleshoot(model).