Skip to content

Commit d4e9165

Browse files
Merge pull request #995 from AayushSabharwal/as/abstract-clock
refactor: allow new clocks to be defined in downstream packages
2 parents 5edf434 + b82ca64 commit d4e9165

File tree

2 files changed

+73
-10
lines changed

2 files changed

+73
-10
lines changed

src/SciMLBase.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import ADTypes: ADTypes, AbstractADType
2525
import Accessors: @set, @reset, @delete, @insert
2626
using Moshi.Data: @data
2727
using Moshi.Match: @match
28+
import Moshi.Derive: @derive
2829
import StaticArraysCore
2930
import Adapt: adapt_structure, adapt
3031

src/clock.jl

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
@data Clocks begin
1+
abstract type AbstractClock end
2+
3+
@data Clocks<:AbstractClock begin
24
ContinuousClock
35
struct PeriodicClock
46
dt::Union{Nothing, Float64, Rational{Int}}
57
phase::Float64 = 0.0
68
end
79
SolverStepClock
10+
struct EventClock
11+
id::Symbol
12+
end
813
end
914

15+
@derive Clocks[Show, Hash, Eq]
16+
1017
# for backwards compatibility
11-
const TimeDomain = Clocks.Type
12-
using .Clocks: ContinuousClock, PeriodicClock, SolverStepClock
18+
const TimeDomain = AbstractClock
19+
using .Clocks: ContinuousClock, PeriodicClock, SolverStepClock, EventClock
1320
const Continuous = ContinuousClock()
1421
(clock::TimeDomain)() = clock
1522

@@ -40,44 +47,99 @@ discrete-time systems that assume a fixed sample time, such as PID controllers a
4047
filters.
4148
""" SolverStepClock
4249

43-
isclock(c::TimeDomain) = @match c begin
50+
isclock(c::Clocks.Type) = @match c begin
4451
PeriodicClock() => true
4552
_ => false
4653
end
54+
isclock(::TimeDomain) = false
4755

48-
issolverstepclock(c::TimeDomain) = @match c begin
56+
issolverstepclock(c::Clocks.Type) = @match c begin
4957
SolverStepClock() => true
5058
_ => false
5159
end
60+
issolverstepclock(::TimeDomain) = false
5261

53-
iscontinuous(c::TimeDomain) = @match c begin
62+
iscontinuous(c::Clocks.Type) = @match c begin
5463
ContinuousClock() => true
5564
_ => false
5665
end
66+
iscontinuous(::TimeDomain) = false
67+
68+
iseventclock(c::Clocks.Type) = @match c begin
69+
EventClock() => true
70+
_ => false
71+
end
72+
iseventclock(::TimeDomain) = false
5773

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

6076
# workaround for https://github.com/Roger-luo/Moshi.jl/issues/43
6177
isclock(::Any) = false
6278
issolverstepclock(::Any) = false
6379
iscontinuous(::Any) = false
80+
iseventclock(::Any) = false
6481
is_discrete_time_domain(::Any) = false
6582

66-
function first_clock_tick_time(c, t0)
83+
# public
84+
function first_clock_tick_time(c::Clocks.Type, t0)
6785
@match c begin
6886
PeriodicClock(dt) => ceil(t0 / dt) * dt
6987
SolverStepClock() => t0
7088
ContinuousClock() => error("ContinuousClock() is not a discrete clock")
89+
EventClock() => error("Event clocks do not have a defined first tick time.")
90+
_ => error("Unimplemented for clock $c")
7191
end
7292
end
7393

74-
struct IndexedClock{I}
75-
clock::TimeDomain
94+
function first_clock_tick_time(c::TimeDomain, _)
95+
error("Unimplemented for clock $c")
96+
end
97+
98+
# public
99+
"""
100+
$(TYPEDEF)
101+
102+
A struct representing the operation of indexing a clock to obtain a subset of the time
103+
points at which it ticked. The actual list of time points depends on the tick instances
104+
on which the clock was ticking, and can be obtained via `canonicalize_indexed_clock`
105+
by providing a timeseries solution object.
106+
107+
For example, `IndexedClock(PeriodicClock(0.1), 3)` refers to the third time that
108+
`PeriodicClock(0.1)` ticked. If the simulation started at `t = 0`, then this would be
109+
`t = 0.2`. Similarly, `IndexedClock(PeriodicClock(0.1), [1, 5])` refers to `t = 0.0`
110+
and `t = 0.4` in this context.
111+
112+
# Fields
113+
114+
$(TYPEDFIELDS)
115+
"""
116+
struct IndexedClock{C <: AbstractClock, I}
117+
"""
118+
The clock being indexed. A subtype of `SciMLBase.AbstractClock`
119+
"""
120+
clock::C
121+
"""
122+
The subset of indexes being referred to. This can be an integer, an array of integers,
123+
a range or `Colon()` to refer to all the points that the clock ticked.
124+
"""
76125
idx::I
77126
end
78127

79-
Base.getindex(c::TimeDomain, idx) = IndexedClock(c, idx)
128+
# public
129+
"""
130+
$(TYPEDSIGNATURES)
131+
132+
Return a `SciMLBase.IndexedClock` representing the subset of the time points that the clock
133+
ticked indicated by `idx`.
134+
"""
135+
Base.getindex(c::AbstractClock, idx) = IndexedClock(c, idx)
80136

137+
# public
138+
"""
139+
$(TYPEDSIGNATURES)
140+
141+
Return the time points in the interval
142+
"""
81143
function canonicalize_indexed_clock(ic::IndexedClock, sol::AbstractTimeseriesSolution)
82144
c = ic.clock
83145

0 commit comments

Comments
 (0)