Skip to content

Commit 2d4668d

Browse files
committed
feat: add ContinuousCLock
A ContinuousClock ticks at every solver step
1 parent bc53299 commit 2d4668d

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

src/clock.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,23 @@ function Base.:(==)(c1::Clock, c2::Clock)
124124
end
125125

126126
is_concrete_time_domain(x) = x isa Union{AbstractClock, Continuous}
127+
128+
"""
129+
ContinuousClock <: AbstractClock
130+
ContinuousClock([t]; dt)
131+
132+
A clock that ticks at each solver step. This clock does generally not have equidistant tick intervals, instead, the tick interval depends on the adaptive step-size slection of the continuous solver, as well as any continuous event handling. If adaptivity of the solver is turned off and there are no continuous events, the tick interval will be given by the fixed solver time step `dt`.
133+
"""
134+
struct ContinuousClock <: AbstractClock
135+
"Independent variable"
136+
t::Union{Nothing, Symbolic}
137+
"Period"
138+
ContinuousClock(t::Union{Num, Symbolic}) = new(value(t))
139+
end
140+
ContinuousClock() = ContinuousClock(nothing)
141+
142+
sampletime(c) = nothing
143+
Base.hash(c::ContinuousClock, seed::UInt) = seed 0x953d7b9a18874b91
144+
function Base.:(==)(c1::ContinuousClock, c2::ContinuousClock)
145+
((c1.t === nothing || c2.t === nothing) || isequal(c1.t, c2.t))
146+
end

src/systems/diffeqs/abstractodesystem.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,10 @@ function DiffEqBase.ODEProblem{iip, specialize}(sys::AbstractODESystem, u0map =
10531053
discrete_cbs = map(affects, clocks, svs) do affect, clock, sv
10541054
if clock isa Clock
10551055
PeriodicCallback(DiscreteSaveAffect(affect, sv), clock.dt)
1056+
elseif clock isa ContinuousClock
1057+
affect = DiscreteSaveAffect(affect, sv)
1058+
DiscreteCallback(Returns(true), affect,
1059+
initialize = (c, u, t, integrator) -> affect(integrator))
10561060
else
10571061
error("$clock is not a supported clock type.")
10581062
end

test/clock.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,42 @@ y = res.y[:]
477477
# y ~ x(k-1) + kp * u
478478
# Instead. This should be equivalent to the above, but gve me an error when I tried
479479
end
480+
481+
## Test continuous clock
482+
483+
c = ModelingToolkit.ContinuousClock(t)
484+
485+
@mtkmodel Counter begin
486+
@variables begin
487+
count(t) = 0
488+
u(t) = 0
489+
end
490+
@equations begin
491+
count(k + 1) ~ Sample(c)(u)
492+
end
493+
end
494+
495+
@mtkmodel FirstOrder begin
496+
@variables begin
497+
x(t) = 0
498+
end
499+
@equations begin
500+
D(x) ~ -x + sin(t)
501+
end
502+
end
503+
504+
@mtkmodel FirstOrderWithStepCounter begin
505+
@components begin
506+
counter = Counter()
507+
fo = FirstOrder()
508+
end
509+
@equations begin
510+
counter.u ~ fo.x
511+
end
512+
end
513+
514+
@mtkbuild model = FirstOrderWithStepCounter()
515+
prob = ODEProblem(model, [], (0.0, 10.0))
516+
sol = solve(prob, Tsit5(), kwargshandle = KeywordArgSilent)
517+
518+
@test sol.prob.kwargs[:disc_saved_values][1].t == sol.t[1:2:end] # Test that the discrete-tiem system executed at every step of the continuous solver. The solver saves each time step twice, one state value before discrete affect and one after.

0 commit comments

Comments
 (0)