Skip to content

refactor: allow new clocks to be defined in downstream packages #995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SciMLBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import ADTypes: ADTypes, AbstractADType
import Accessors: @set, @reset, @delete, @insert
using Moshi.Data: @data
using Moshi.Match: @match
import Moshi.Derive: @derive
import StaticArraysCore
import Adapt: adapt_structure, adapt

Expand Down
64 changes: 59 additions & 5 deletions src/clock.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
@data Clocks begin
abstract type AbstractClock end

@data Clocks<:AbstractClock begin
ContinuousClock
struct PeriodicClock
dt::Union{Nothing, Float64, Rational{Int}}
phase::Float64 = 0.0
end
SolverStepClock
struct EventClock
id::Symbol
end
end

@derive Clocks[Show]

# for backwards compatibility
const TimeDomain = Clocks.Type
using .Clocks: ContinuousClock, PeriodicClock, SolverStepClock
using .Clocks: ContinuousClock, PeriodicClock, SolverStepClock, EventClock
const Continuous = ContinuousClock()
(clock::TimeDomain)() = clock

Expand Down Expand Up @@ -55,29 +62,76 @@ iscontinuous(c::TimeDomain) = @match c begin
_ => false
end

iseventclock(c::TimeDomain) = @match c begin
EventClock() => true
_ => false
end

is_discrete_time_domain(c::TimeDomain) = !iscontinuous(c)

# workaround for https://github.com/Roger-luo/Moshi.jl/issues/43
isclock(::Any) = false
issolverstepclock(::Any) = false
iscontinuous(::Any) = false
iseventclock(::Any) = false
is_discrete_time_domain(::Any) = false

# public
function first_clock_tick_time(c, t0)
@match c begin
PeriodicClock(dt) => ceil(t0 / dt) * dt
SolverStepClock() => t0
ContinuousClock() => error("ContinuousClock() is not a discrete clock")
EventClock() => error("Event clocks do not have a defined first tick time.")
_ => error("Unimplemented for clock $c")
end
end

struct IndexedClock{I}
clock::TimeDomain
# public
"""
$(TYPEDEF)

A struct representing the operation of indexing a clock to obtain a subset of the time
points at which it ticked. The actual list of time points depends on the tick instances
on which the clock was ticking, and can be obtained via `canonicalize_indexed_clock`
by providing a timeseries solution object.

For example, `IndexedClock(PeriodicClock(0.1), 3)` refers to the third time that
`PeriodicClock(0.1)` ticked. If the simulation started at `t = 0`, then this would be
`t = 0.2`. Similarly, `IndexedClock(PeriodicClock(0.1), [1, 5])` refers to `t = 0.0`
and `t = 0.4` in this context.

# Fields

$(TYPEDFIELDS)
"""
struct IndexedClock{C <: AbstractClock, I}
"""
The clock being indexed. A subtype of `SciMLBase.AbstractClock`
"""
clock::C
"""
The subset of indexes being referred to. This can be an integer, an array of integers,
a range or `Colon()` to refer to all the points that the clock ticked.
"""
idx::I
end

Base.getindex(c::TimeDomain, idx) = IndexedClock(c, idx)
# public
"""
$(TYPEDSIGNATURES)

Return a `SciMLBase.IndexedClock` representing the subset of the time points that the clock
ticked indicated by `idx`.
"""
Base.getindex(c::AbstractClock, idx) = IndexedClock(c, idx)

# public
"""
$(TYPEDSIGNATURES)

Return the time points in the interval
"""
function canonicalize_indexed_clock(ic::IndexedClock, sol::AbstractTimeseriesSolution)
c = ic.clock

Expand Down
Loading