Skip to content

Commit

Permalink
Added methods to re-simulate a model.
Browse files Browse the repository at this point in the history
  • Loading branch information
zekeriya.sari committed Apr 29, 2021
1 parent a57b196 commit cdac1e1
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 18 deletions.
2 changes: 2 additions & 0 deletions src/Causal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,10 @@ export @defmodel,
Branch,
addnode!,
getnode,
getcomponent,
addbranch!,
getbranch,
getlinks,
deletebranch!,
signalflow,
troubleshoot
Expand Down
8 changes: 8 additions & 0 deletions src/connections/link.jl
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,11 @@ end
Reconstructst the channel of `link` is its channel is closed.
"""
refresh!(l::Link{T}) where {T} = (l.channel = Channel{T}(); l)

"""
$(SIGNATURES)
Cleans the data on `link`.
"""
clean!(link::Link) = (clean!(link.buffer); link)

106 changes: 88 additions & 18 deletions src/models/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ Node(component:Gain(gain:1.0, input:Inport(numpins:1, eltype:Inpin{Float64}), ou
getnode(model::Model, idx::Int) = model.nodes[idx]
getnode(model::Model, label) = filter(node -> node.label === label, model.nodes)[1]

"""
$(SIGNATURES)
Returns the component of `model` corresponding to `specifier` that can be either index or label of the component.
"""
getcomponent(model::Model, specifier) = getnode(model, specifier).component

function register(taskmanager, component)
triggerport, handshakeport = taskmanager.triggerport, taskmanager.handshakeport
triggerpin, handshakepin = Outpin(), Inpin{Bool}()
Expand Down Expand Up @@ -145,6 +152,13 @@ getbranch(model::Model, nodepair::Pair{Int, Int}) = filter(branch -> branch.node
getbranch(model::Model, nodepair::Pair{Symbol, Symbol}) =
getbranch(model, getnode(model, nodepair.first).idx => getnode(model, nodepair.second).idx)

"""
$(SIGNATURES)
Returns the links of `model` corresponding to the `pair` which can be a pair of integers or symbols to specify the source and destination nodes of the branch.
"""
getlinks(model::Model, pair) = getbranch(model, nodepair).links

"""
deletebranch!(model::Model, branch::Branch)
Expand Down Expand Up @@ -413,53 +427,108 @@ function checkchannels(model)
end

##### Model initialization

"""
initialize!(model::Model)
$(SIGNATURES)
Initializes `model` by launching component task for each of the component of `model`. The pairs component and component tasks are recordedin the task manager of the `model`. The `model` clock is [`set!`](@ref) and the files of [`Writer`](@ref) are openned.
"""
function initialize!(model::Model)
taskmanager = model.taskmanager
pairs = taskmanager.pairs
nodes = model.nodes

# NOTE: Tasks to make the components be triggerable are launched here.
# The important point here is that the simulation should be cancelled if an error is thrown in any of the tasks
# launched here. This is done by binding the task to the chnnel of the trigger link of the component. Hrence the
# lifetime of the channel of the link connecting the component to the taskmanger is determined by the lifetime of
# the task launched for the component. To cancel the simulation and report the stacktrace the task is `fetch`ed.
bind!(model)

# Turn on clock model clock if it is running.
initclock!(model)

# Clean the model
clean!(model)

# Open the files, GUI's for sink components.
opensinks!(model)
end

# Find the link connecting `component` to `taskmanager`.
function whichlink(taskmanager, component)
tpin = component.trigger
tport = taskmanager.triggerport
# NOTE: `component` must be connected to `taskmanager` by a single link which is checked by `only`
# `outpin.links` must have just a single link which checked by `only`
outpin = filter(pin -> isconnected(pin, tpin), tport) |> only
outpin.links |> only
end

# Bind tasks to connections
function bind!(model::Model)
taskmanager = model.taskmanager
pairs = taskmanager.pairs
nodes = model.nodes
for node in nodes
component = node.component
link = whichlink(taskmanager, component) # Link connecting the component to taskmanager.
task = launch(component) # Task launched to make `componnent` be triggerable.
bind(link.channel, task) # Bind the task to the channel of the link.
pairs[component] = task
end
model
end

# Turn on clock model clock if it is running.
# Open AbstractSink components
function opensinks!(model::Model)
foreach(node -> open(node.component), filter(node->isa(node.component, AbstractSink), model.nodes))
model
end

# Initialze model clock
function initclock!(model::Model)
if isoutoftime(model.clock)
msg = "Model clock is out of time. Its current time $(model.clock.t) should be less than its final time "
msg *= "$(model.clock.tf). Resettting the model clock to its defaults."
@warn msg
set!(model.clock)
end
isrunning(model.clock) || set!(model.clock)

# Open the files, GUI's for sink components.
foreach(node -> open(node.component), filter(node->isa(node.component, AbstractSink), model.nodes))
model
end

# Return the model back.
"""
$(SIGNATURES)
Cleans the buffers of the links of the connections, internal buffers of components, current time of dynamical systems.
"""
function clean!(model::Model)
cleanbranches!(model)
cleannodes!(model)
resetdynamicalsystems!(model)
model
end

function cleanbranches!(model)
foreach(branch -> foreach(link -> clean!(link), branch.links), model.branches)
model
end

function cleannodes!(model)
foreach(node -> cleancomponent!(node.component), model.nodes)
model
end

# Find the link connecting `component` to `taskmanager`.
function whichlink(taskmanager, component)
tpin = component.trigger
tport = taskmanager.triggerport
# NOTE: `component` must be connected to `taskmanager` by a single link which is checked by `only`
# `outpin.links` must have just a single link which checked by `only`
outpin = filter(pin -> isconnected(pin, tpin), tport) |> only
outpin.links |> only
function cleancomponent!(comp::T) where T
foreach(name -> (field = getfield(comp, name); field isa Buffer && clean!(field)), fieldnames(T))
comp
end

function resetdynamicalsystems!(model)
t = model.clock.ti
for comp in filter(comp -> comp isa AbstractDynamicSystem, getfield.(model.nodes, :component))
comp.t != t && (comp.t = t)
reinit!(comp.integrator)
comp.input === nothing || (interp = comp.interpolant; clean!(interp.timebuf); clean!(interp.databuf))
end
model
end

##### Model running
Expand Down Expand Up @@ -517,6 +586,7 @@ function terminate!(model::Model)
end


##### Model simulation.
function _simulate(sim::Simulation, reportsim::Bool, withbar::Bool, breakpoints::Vector{Int})
model = sim.model
@siminfo "Started simulation..."
Expand Down
12 changes: 12 additions & 0 deletions src/utilities/buffer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,15 @@ end
Returns all elements in `buf`. See also: [`content`](@ref)
"""
snapshot(buf::Buffer) = outbuf(buf)

"""
$(SIGNATURES)
Cleans the contents of `buf`.
"""
function clean!(buf::Buffer)
buf.internals[1] .= 0.
buf.internals[2] .= 0.
buf.index = 1
buf
end

0 comments on commit cdac1e1

Please sign in to comment.