|
1 | | -@data Clocks begin |
2 | | - ContinuousClock |
3 | | - struct PeriodicClock |
4 | | - dt::Union{Nothing, Float64, Rational{Int}} |
5 | | - phase::Float64 = 0.0 |
6 | | - end |
7 | | - SolverStepClock |
8 | | -end |
| 1 | +# Clock system implementation |
| 2 | +# |
| 3 | +# When Moshi is available as an extension, it provides advanced @data/@match functionality. |
| 4 | +# When Moshi is not loaded, we fall back to simple struct-based implementations. |
9 | 5 |
|
10 | | -# for backwards compatibility |
11 | | -const TimeDomain = Clocks.Type |
12 | | -using .Clocks: ContinuousClock, PeriodicClock, SolverStepClock |
13 | | -const Continuous = ContinuousClock() |
14 | | -(clock::TimeDomain)() = clock |
15 | | - |
16 | | -Base.Broadcast.broadcastable(d::TimeDomain) = Ref(d) |
17 | | - |
18 | | -""" |
19 | | - Clock(dt) |
20 | | - Clock() |
21 | | -
|
22 | | -The default periodic clock with tick interval `dt`. If `dt` is left unspecified, it will |
23 | | -be inferred (if possible). |
24 | | -""" |
25 | | -Clock(dt::Union{<:Rational, Float64}; phase = 0.0) = PeriodicClock(dt, phase) |
26 | | -Clock(dt; phase = 0.0) = PeriodicClock(convert(Float64, dt), phase) |
27 | | -Clock(; phase = 0.0) = PeriodicClock(nothing, phase) |
28 | | - |
29 | | -@doc """ |
30 | | - SolverStepClock |
31 | | -
|
32 | | -A clock that ticks at each solver step (sometimes referred to as "continuous sample time"). |
33 | | -This clock **does generally not have equidistant tick intervals**, instead, the tick |
34 | | -interval depends on the adaptive step-size selection of the continuous solver, as well as |
35 | | -any continuous event handling. If adaptivity of the solver is turned off and there are no |
36 | | -continuous events, the tick interval will be given by the fixed solver time step `dt`. |
37 | | -
|
38 | | -Due to possibly non-equidistant tick intervals, this clock should typically not be used with |
39 | | -discrete-time systems that assume a fixed sample time, such as PID controllers and digital |
40 | | -filters. |
41 | | -""" SolverStepClock |
42 | | - |
43 | | -isclock(c::TimeDomain) = @match c begin |
44 | | - PeriodicClock() => true |
45 | | - _ => false |
46 | | -end |
47 | | - |
48 | | -issolverstepclock(c::TimeDomain) = @match c begin |
49 | | - SolverStepClock() => true |
50 | | - _ => false |
51 | | -end |
52 | | - |
53 | | -iscontinuous(c::TimeDomain) = @match c begin |
54 | | - ContinuousClock() => true |
55 | | - _ => false |
56 | | -end |
57 | | - |
58 | | -is_discrete_time_domain(c::TimeDomain) = !iscontinuous(c) |
59 | | - |
60 | | -# workaround for https://github.com/Roger-luo/Moshi.jl/issues/43 |
61 | | -isclock(::Any) = false |
62 | | -issolverstepclock(::Any) = false |
63 | | -iscontinuous(::Any) = false |
64 | | -is_discrete_time_domain(::Any) = false |
65 | | - |
66 | | -function first_clock_tick_time(c, t0) |
67 | | - @match c begin |
68 | | - PeriodicClock(dt) => ceil(t0 / dt) * dt |
69 | | - SolverStepClock() => t0 |
70 | | - ContinuousClock() => error("ContinuousClock() is not a discrete clock") |
71 | | - end |
72 | | -end |
73 | | - |
74 | | -struct IndexedClock{I} |
75 | | - clock::TimeDomain |
76 | | - idx::I |
77 | | -end |
78 | | - |
79 | | -Base.getindex(c::TimeDomain, idx) = IndexedClock(c, idx) |
80 | | - |
81 | | -function canonicalize_indexed_clock(ic::IndexedClock, sol::AbstractTimeseriesSolution) |
82 | | - c = ic.clock |
83 | | - |
84 | | - return @match c begin |
85 | | - PeriodicClock(dt) => ceil(sol.prob.tspan[1] / dt) * dt .+ (ic.idx .- 1) .* dt |
86 | | - SolverStepClock() => begin |
87 | | - ssc_idx = findfirst(eachindex(sol.discretes)) do i |
88 | | - !isa(sol.discretes[i].t, AbstractRange) |
89 | | - end |
90 | | - sol.discretes[ssc_idx].t[ic.idx] |
91 | | - end |
92 | | - ContinuousClock() => sol.t[ic.idx] |
93 | | - end |
94 | | -end |
| 6 | +# Include fallback implementations (always available) |
| 7 | +include("clock_fallback.jl") |
0 commit comments