Skip to content

Commit 33d4ecc

Browse files
committedJul 29, 2024
refactor: use clock from SciMLBase, fix tests
1 parent e7917b8 commit 33d4ecc

13 files changed

+287
-204
lines changed
 

‎Project.toml

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
2020
DomainSets = "5b8099bc-c8ec-5219-889f-1d9e522a28bf"
2121
DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821"
2222
ExprTools = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
23+
Expronicon = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636"
2324
FindFirstFunctions = "64ca27bc-2ba2-4a57-88aa-44e436879224"
2425
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
2526
FunctionWrappersWrappers = "77dc65aa-8811-40c2-897b-53d922fa7daf"
@@ -81,6 +82,7 @@ DocStringExtensions = "0.7, 0.8, 0.9"
8182
DomainSets = "0.6, 0.7"
8283
DynamicQuantities = "^0.11.2, 0.12, 0.13"
8384
ExprTools = "0.1.10"
85+
Expronicon = "0.8"
8486
FindFirstFunctions = "1"
8587
ForwardDiff = "0.10.3"
8688
FunctionWrappersWrappers = "0.1"

‎docs/src/tutorials/SampledData.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ A clock can be seen as an *event source*, i.e., when the clock ticks, an event i
1616
- [`Hold`](@ref)
1717
- [`ShiftIndex`](@ref)
1818

19-
When a continuous-time variable `x` is sampled using `xd = Sample(x, dt)`, the result is a discrete-time variable `xd` that is defined and updated whenever the clock ticks. `xd` is *only defined when the clock ticks*, which it does with an interval of `dt`. If `dt` is unspecified, the tick rate of the clock associated with `xd` is inferred from the context in which `xd` appears. Any variable taking part in the same equation as `xd` is inferred to belong to the same *discrete partition* as `xd`, i.e., belonging to the same clock. A system may contain multiple different discrete-time partitions, each with a unique clock. This allows for modeling of multi-rate systems and discrete-time processes located on different computers etc.
19+
When a continuous-time variable `x` is sampled using `xd = Sample(dt)(x)`, the result is a discrete-time variable `xd` that is defined and updated whenever the clock ticks. `xd` is *only defined when the clock ticks*, which it does with an interval of `dt`. If `dt` is unspecified, the tick rate of the clock associated with `xd` is inferred from the context in which `xd` appears. Any variable taking part in the same equation as `xd` is inferred to belong to the same *discrete partition* as `xd`, i.e., belonging to the same clock. A system may contain multiple different discrete-time partitions, each with a unique clock. This allows for modeling of multi-rate systems and discrete-time processes located on different computers etc.
2020

2121
To make a discrete-time variable available to the continuous partition, the [`Hold`](@ref) operator is used. `xc = Hold(xd)` creates a continuous-time variable `xc` that is updated whenever the clock associated with `xd` ticks, and holds its value constant between ticks.
2222

@@ -34,7 +34,7 @@ using ModelingToolkit
3434
using ModelingToolkit: t_nounits as t
3535
@variables x(t) y(t) u(t)
3636
dt = 0.1 # Sample interval
37-
clock = Clock(t, dt) # A periodic clock with tick rate dt
37+
clock = Clock(dt) # A periodic clock with tick rate dt
3838
k = ShiftIndex(clock)
3939
4040
eqs = [
@@ -99,7 +99,7 @@ may thus be modeled as
9999
```julia
100100
t = ModelingToolkit.t_nounits
101101
@variables y(t) [description = "Output"] u(t) [description = "Input"]
102-
k = ShiftIndex(Clock(t, dt))
102+
k = ShiftIndex(Clock(dt))
103103
eqs = [
104104
a2 * y(k) + a1 * y(k - 1) + a0 * y(k - 2) ~ b2 * u(k) + b1 * u(k - 1) + b0 * u(k - 2)
105105
]
@@ -128,10 +128,10 @@ requires specification of the initial condition for both `x(k-1)` and `x(k-2)`.
128128
Multi-rate systems are easy to model using multiple different clocks. The following set of equations is valid, and defines *two different discrete-time partitions*, each with its own clock:
129129

130130
```julia
131-
yd1 ~ Sample(t, dt1)(y)
132-
ud1 ~ kp * (Sample(t, dt1)(r) - yd1)
133-
yd2 ~ Sample(t, dt2)(y)
134-
ud2 ~ kp * (Sample(t, dt2)(r) - yd2)
131+
yd1 ~ Sample(dt1)(y)
132+
ud1 ~ kp * (Sample(dt1)(r) - yd1)
133+
yd2 ~ Sample(dt2)(y)
134+
ud2 ~ kp * (Sample(dt2)(r) - yd2)
135135
```
136136

137137
`yd1` and `ud1` belong to the same clock which ticks with an interval of `dt1`, while `yd2` and `ud2` belong to a different clock which ticks with an interval of `dt2`. The two clocks are *not synchronized*, i.e., they are not *guaranteed* to tick at the same point in time, even if one tick interval is a rational multiple of the other. Mechanisms for synchronization of clocks are not yet implemented.
@@ -148,7 +148,7 @@ using ModelingToolkit: t_nounits as t
148148
using ModelingToolkit: D_nounits as D
149149
dt = 0.5 # Sample interval
150150
@variables r(t)
151-
clock = Clock(t, dt)
151+
clock = Clock(dt)
152152
k = ShiftIndex(clock)
153153
154154
function plant(; name)

‎src/ModelingToolkit.jl

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ using SciMLStructures
4343
using Compat
4444
using AbstractTrees
4545
using DiffEqBase, SciMLBase, ForwardDiff
46-
using SciMLBase: StandardODEProblem, StandardNonlinearProblem, handle_varmap
46+
using SciMLBase: StandardODEProblem, StandardNonlinearProblem, handle_varmap, TimeDomain,
47+
PeriodicClock, Clock, SolverStepClock, Continuous
4748
using Distributed
4849
import JuliaFormatter
4950
using MLStyle
@@ -272,6 +273,6 @@ export debug_system
272273
#export has_discrete_domain, has_continuous_domain
273274
#export is_discrete_domain, is_continuous_domain, is_hybrid_domain
274275
export Sample, Hold, Shift, ShiftIndex, sampletime, SampleTime
275-
export Clock #, InferredDiscrete,
276+
export Clock, SolverStepClock, TimeDomain
276277

277278
end # module

‎src/clock.jl

+33-68
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
1-
abstract type TimeDomain end
2-
abstract type AbstractDiscrete <: TimeDomain end
1+
module InferredClock
32

4-
Base.Broadcast.broadcastable(d::TimeDomain) = Ref(d)
3+
export InferredTimeDomain
54

6-
struct Inferred <: TimeDomain end
7-
struct InferredDiscrete <: AbstractDiscrete end
8-
struct Continuous <: TimeDomain end
5+
using Expronicon.ADT: @adt, @match
6+
using SciMLBase: TimeDomain
97

10-
Symbolics.option_to_metadata_type(::Val{:timedomain}) = TimeDomain
8+
@adt InferredTimeDomain begin
9+
Inferred
10+
InferredDiscrete
11+
end
12+
13+
Base.Broadcast.broadcastable(x::InferredTimeDomain) = Ref(x)
14+
15+
end
16+
17+
using .InferredClock
18+
19+
struct VariableTimeDomain end
20+
Symbolics.option_to_metadata_type(::Val{:timedomain}) = VariableTimeDomain
21+
22+
is_concrete_time_domain(::TimeDomain) = true
23+
is_concrete_time_domain(_) = false
1124

1225
"""
1326
is_continuous_domain(x)
@@ -16,15 +29,15 @@ true if `x` contains only continuous-domain signals.
1629
See also [`has_continuous_domain`](@ref)
1730
"""
1831
function is_continuous_domain(x)
19-
issym(x) && return getmetadata(x, TimeDomain, false) isa Continuous
32+
issym(x) && return getmetadata(x, VariableTimeDomain, false) == Continuous
2033
!has_discrete_domain(x) && has_continuous_domain(x)
2134
end
2235

2336
function get_time_domain(x)
2437
if iscall(x) && operation(x) isa Operator
2538
output_timedomain(x)
2639
else
27-
getmetadata(x, TimeDomain, nothing)
40+
getmetadata(x, VariableTimeDomain, nothing)
2841
end
2942
end
3043
get_time_domain(x::Num) = get_time_domain(value(x))
@@ -37,14 +50,14 @@ Determine if variable `x` has a time-domain attributed to it.
3750
function has_time_domain(x::Symbolic)
3851
# getmetadata(x, Continuous, nothing) !== nothing ||
3952
# getmetadata(x, Discrete, nothing) !== nothing
40-
getmetadata(x, TimeDomain, nothing) !== nothing
53+
getmetadata(x, VariableTimeDomain, nothing) !== nothing
4154
end
4255
has_time_domain(x::Num) = has_time_domain(value(x))
4356
has_time_domain(x) = false
4457

4558
for op in [Differential]
46-
@eval input_timedomain(::$op, arg = nothing) = Continuous()
47-
@eval output_timedomain(::$op, arg = nothing) = Continuous()
59+
@eval input_timedomain(::$op, arg = nothing) = Continuous
60+
@eval output_timedomain(::$op, arg = nothing) = Continuous
4861
end
4962

5063
"""
@@ -83,12 +96,17 @@ true if `x` contains only discrete-domain signals.
8396
See also [`has_discrete_domain`](@ref)
8497
"""
8598
function is_discrete_domain(x)
86-
if hasmetadata(x, TimeDomain) || issym(x)
87-
return getmetadata(x, TimeDomain, false) isa AbstractDiscrete
99+
if hasmetadata(x, VariableTimeDomain) || issym(x)
100+
return is_discrete_time_domain(getmetadata(x, VariableTimeDomain, false))
88101
end
89102
!has_discrete_domain(x) && has_continuous_domain(x)
90103
end
91104

105+
sampletime(c) = @match c begin
106+
PeriodicClock(dt, _...) => dt
107+
_ => nothing
108+
end
109+
92110
struct ClockInferenceException <: Exception
93111
msg::Any
94112
end
@@ -97,57 +115,4 @@ function Base.showerror(io::IO, cie::ClockInferenceException)
97115
print(io, "ClockInferenceException: ", cie.msg)
98116
end
99117

100-
abstract type AbstractClock <: AbstractDiscrete end
101-
102-
"""
103-
Clock <: AbstractClock
104-
Clock([t]; dt)
105-
106-
The default periodic clock with independent variables `t` and tick interval `dt`.
107-
If `dt` is left unspecified, it will be inferred (if possible).
108-
"""
109-
struct Clock <: AbstractClock
110-
"Independent variable"
111-
t::Union{Nothing, Symbolic}
112-
"Period"
113-
dt::Union{Nothing, Float64}
114-
Clock(t::Union{Num, Symbolic}, dt = nothing) = new(value(t), dt)
115-
Clock(t::Nothing, dt = nothing) = new(t, dt)
116-
end
117-
Clock(dt::Real) = Clock(nothing, dt)
118-
Clock() = Clock(nothing, nothing)
119-
120-
sampletime(c) = isdefined(c, :dt) ? c.dt : nothing
121-
Base.hash(c::Clock, seed::UInt) = hash(c.dt, seed 0x953d7a9a18874b90)
122-
function Base.:(==)(c1::Clock, c2::Clock)
123-
((c1.t === nothing || c2.t === nothing) || isequal(c1.t, c2.t)) && c1.dt == c2.dt
124-
end
125-
126-
is_concrete_time_domain(x) = x isa Union{AbstractClock, Continuous}
127-
128-
"""
129-
SolverStepClock <: AbstractClock
130-
SolverStepClock()
131-
SolverStepClock(t)
132-
133-
A clock that ticks at each solver step (sometimes referred to as "continuous sample time"). This clock **does generally not have equidistant tick intervals**, instead, the tick interval depends on the adaptive step-size selection 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`.
134-
135-
Due to possibly non-equidistant tick intervals, this clock should typically not be used with discrete-time systems that assume a fixed sample time, such as PID controllers and digital filters.
136-
"""
137-
struct SolverStepClock <: AbstractClock
138-
"Independent variable"
139-
t::Union{Nothing, Symbolic}
140-
"Period"
141-
SolverStepClock(t::Union{Num, Symbolic}) = new(value(t))
142-
end
143-
SolverStepClock() = SolverStepClock(nothing)
144-
145-
Base.hash(c::SolverStepClock, seed::UInt) = seed 0x953d7b9a18874b91
146-
function Base.:(==)(c1::SolverStepClock, c2::SolverStepClock)
147-
((c1.t === nothing || c2.t === nothing) || isequal(c1.t, c2.t))
148-
end
149-
150-
struct IntegerSequence <: AbstractClock
151-
t::Union{Nothing, Symbolic}
152-
IntegerSequence(t::Union{Num, Symbolic}) = new(value(t))
153-
end
118+
struct IntegerSequence end

‎src/discretedomain.jl

+29-24
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ $(TYPEDEF)
8585
Represents a sample operator. A discrete-time signal is created by sampling a continuous-time signal.
8686
8787
# Constructors
88-
`Sample(clock::TimeDomain = InferredDiscrete())`
89-
`Sample([t], dt::Real)`
88+
`Sample(clock::Union{TimeDomain, InferredTimeDomain} = InferredDiscrete)`
89+
`Sample(dt::Real)`
9090
9191
`Sample(x::Num)`, with a single argument, is shorthand for `Sample()(x)`.
9292
@@ -100,16 +100,23 @@ julia> using Symbolics
100100
101101
julia> t = ModelingToolkit.t_nounits
102102
103-
julia> Δ = Sample(t, 0.01)
103+
julia> Δ = Sample(0.01)
104104
(::Sample) (generic function with 2 methods)
105105
```
106106
"""
107107
struct Sample <: Operator
108108
clock::Any
109-
Sample(clock::TimeDomain = InferredDiscrete()) = new(clock)
110-
Sample(t, dt::Real) = new(Clock(t, dt))
109+
Sample(clock::Union{TimeDomain, InferredTimeDomain} = InferredDiscrete) = new(clock)
110+
end
111+
112+
function Sample(arg::Real)
113+
arg = unwrap(arg)
114+
if symbolic_type(arg) == NotSymbolic()
115+
Sample(Clock(arg))
116+
else
117+
Sample()(arg)
118+
end
111119
end
112-
Sample(x) = Sample()(x)
113120
(D::Sample)(x) = Term{symtype(x)}(D, Any[x])
114121
(D::Sample)(x::Num) = Num(D(value(x)))
115122
SymbolicUtils.promote_symtype(::Sample, x) = x
@@ -176,15 +183,18 @@ julia> x(k) # no shift
176183
x(t)
177184
178185
julia> x(k+1) # shift
179-
Shift(t, 1)(x(t))
186+
Shift(1)(x(t))
180187
```
181188
"""
182189
struct ShiftIndex
183-
clock::TimeDomain
190+
clock::Union{InferredTimeDomain, TimeDomain, IntegerSequence}
184191
steps::Int
185-
ShiftIndex(clock::TimeDomain = Inferred(), steps::Int = 0) = new(clock, steps)
186-
ShiftIndex(t::Num, dt::Real, steps::Int = 0) = new(Clock(t, dt), steps)
187-
ShiftIndex(t::Num, steps::Int = 0) = new(IntegerSequence(t), steps)
192+
function ShiftIndex(
193+
clock::Union{TimeDomain, InferredTimeDomain, IntegerSequence} = Inferred, steps::Int = 0)
194+
new(clock, steps)
195+
end
196+
ShiftIndex(dt::Real, steps::Int = 0) = new(Clock(dt), steps)
197+
ShiftIndex(::Num, steps::Int) = new(IntegerSequence(), steps)
188198
end
189199

190200
function (xn::Num)(k::ShiftIndex)
@@ -197,18 +207,13 @@ function (xn::Num)(k::ShiftIndex)
197207
args = Symbolics.arguments(vars[]) # args should be one element vector with the t in x(t)
198208
length(args) == 1 ||
199209
error("Cannot shift an expression with multiple independent variables $x.")
200-
t = args[]
201-
if hasfield(typeof(clock), :t)
202-
isequal(t, clock.t) ||
203-
error("Independent variable of $xn is not the same as that of the ShiftIndex $(k.t)")
204-
end
205210

206211
# d, _ = propagate_time_domain(xn)
207212
# if d != clock # this is only required if the variable has another clock
208213
# xn = Sample(t, clock)(xn)
209214
# end
210215
# QUESTION: should we return a variable with time domain set to k.clock?
211-
xn = setmetadata(xn, TimeDomain, k.clock)
216+
xn = setmetadata(xn, VariableTimeDomain, k.clock)
212217
if steps == 0
213218
return xn # x(k) needs no shift operator if the step of k is 0
214219
end
@@ -221,37 +226,37 @@ Base.:-(k::ShiftIndex, i::Int) = k + (-i)
221226
"""
222227
input_timedomain(op::Operator)
223228
224-
Return the time-domain type (`Continuous()` or `Discrete()`) that `op` operates on.
229+
Return the time-domain type (`Continuous` or `InferredDiscrete`) that `op` operates on.
225230
"""
226231
function input_timedomain(s::Shift, arg = nothing)
227232
if has_time_domain(arg)
228233
return get_time_domain(arg)
229234
end
230-
InferredDiscrete()
235+
InferredDiscrete
231236
end
232237

233238
"""
234239
output_timedomain(op::Operator)
235240
236-
Return the time-domain type (`Continuous()` or `Discrete()`) that `op` results in.
241+
Return the time-domain type (`Continuous` or `InferredDiscrete`) that `op` results in.
237242
"""
238243
function output_timedomain(s::Shift, arg = nothing)
239244
if has_time_domain(arg)
240245
return get_time_domain(arg)
241246
end
242-
InferredDiscrete()
247+
InferredDiscrete
243248
end
244249

245-
input_timedomain(::Sample, arg = nothing) = Continuous()
250+
input_timedomain(::Sample, arg = nothing) = Continuous
246251
output_timedomain(s::Sample, arg = nothing) = s.clock
247252

248253
function input_timedomain(h::Hold, arg = nothing)
249254
if has_time_domain(arg)
250255
return get_time_domain(arg)
251256
end
252-
InferredDiscrete() # the Hold accepts any discrete
257+
InferredDiscrete # the Hold accepts any discrete
253258
end
254-
output_timedomain(::Hold, arg = nothing) = Continuous()
259+
output_timedomain(::Hold, arg = nothing) = Continuous
255260

256261
sampletime(op::Sample, arg = nothing) = sampletime(op.clock)
257262
sampletime(op::ShiftIndex, arg = nothing) = sampletime(op.clock)

‎src/systems/abstractsystem.jl

+30-5
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,17 @@ function SymbolicIndexingInterface.parameter_observed(sys::AbstractSystem, sym)
571571
return obsfn
572572
end
573573

574+
function has_observed_with_lhs(sys, sym)
575+
has_observed(sys) || return false
576+
if has_index_cache(sys) && (ic = get_index_cache(sys)) !== nothing
577+
return any(isequal(sym), ic.observed_syms)
578+
else
579+
return any(isequal(sym), [eq.lhs for eq in observed(sys)])
580+
end
581+
end
582+
574583
function _all_ts_idxs!(ts_idxs, ::NotSymbolic, sys, sym)
575-
if is_variable(sys, sym)
584+
if is_variable(sys, sym) || is_independent_variable(sys, sym)
576585
push!(ts_idxs, ContinuousTimeseries())
577586
elseif is_timeseries_parameter(sys, sym)
578587
push!(ts_idxs, timeseries_parameter_index(sys, sym).timeseries_idx)
@@ -585,17 +594,33 @@ for traitT in [
585594
]
586595
@eval function _all_ts_idxs!(ts_idxs, ::$traitT, sys, sym)
587596
allsyms = vars(sym; op = Symbolics.Operator)
588-
foreach(allsyms) do s
589-
_all_ts_idxs!(ts_idxs, sys, s)
597+
for s in allsyms
598+
s = unwrap(s)
599+
if is_variable(sys, s) || is_independent_variable(sys, s) ||
600+
has_observed_with_lhs(sys, s)
601+
push!(ts_idxs, ContinuousTimeseries())
602+
elseif is_timeseries_parameter(sys, s)
603+
push!(ts_idxs, timeseries_parameter_index(sys, s).timeseries_idx)
604+
end
590605
end
591606
end
592607
end
608+
function _all_ts_idxs!(ts_idxs, ::ScalarSymbolic, sys, sym::Symbol)
609+
if has_index_cache(sys) && (ic = get_index_cache(sys)) !== nothing
610+
return _all_ts_idxs!(ts_idxs, sys, ic.symbol_to_variable[sym])
611+
elseif is_variable(sys, sym) || is_independent_variable(sys, sym) ||
612+
any(isequal(sym), [getname(eq.lhs) for eq in observed(sys)])
613+
push!(ts_idxs, ContinuousTimeseries())
614+
elseif is_timeseries_parameter(sys, sym)
615+
push!(ts_idxs, timeseries_parameter_index(sys, s).timeseries_idx)
616+
end
617+
end
593618
function _all_ts_idxs!(ts_idxs, ::NotSymbolic, sys, sym::AbstractArray)
594-
foreach(sym) do s
619+
for s in sym
595620
_all_ts_idxs!(ts_idxs, sys, s)
596621
end
597622
end
598-
_all_ts_idxs!(ts_idxs, sys, sym) = _all_ts_idxs!(ts_idxs, NotSymbolic(), sys, sym)
623+
_all_ts_idxs!(ts_idxs, sys, sym) = _all_ts_idxs!(ts_idxs, symbolic_type(sym), sys, sym)
599624

600625
function SymbolicIndexingInterface.get_all_timeseries_indexes(sys::AbstractSystem, sym)
601626
if !is_time_dependent(sys)

‎src/systems/clock_inference.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ end
88
function ClockInference(ts::TransformationState)
99
@unpack structure = ts
1010
@unpack graph = structure
11-
eq_domain = TimeDomain[Continuous() for _ in 1:nsrcs(graph)]
12-
var_domain = TimeDomain[Continuous() for _ in 1:ndsts(graph)]
11+
eq_domain = TimeDomain[Continuous for _ in 1:nsrcs(graph)]
12+
var_domain = TimeDomain[Continuous for _ in 1:ndsts(graph)]
1313
inferred = BitSet()
1414
for (i, v) in enumerate(get_fullvars(ts))
1515
d = get_time_domain(v)
@@ -151,7 +151,7 @@ function split_system(ci::ClockInference{S}) where {S}
151151
get!(clock_to_id, d) do
152152
cid = (cid_counter[] += 1)
153153
push!(id_to_clock, d)
154-
if d isa Continuous
154+
if d == Continuous
155155
continuous_id[] = cid
156156
end
157157
cid

‎src/systems/diffeqs/abstractodesystem.jl

+19-19
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ function process_DEProblem(constructor, sys::AbstractODESystem, u0map, parammap;
825825
# ModelingToolkit.get_tearing_state(sys) !== nothing => Requires structural_simplify first
826826
if sys isa ODESystem && build_initializeprob &&
827827
(((implicit_dae || !isempty(missingvars)) &&
828-
all(isequal(Continuous()), ci.var_domain) &&
828+
all(==(Continuous), ci.var_domain) &&
829829
ModelingToolkit.get_tearing_state(sys) !== nothing) ||
830830
!isempty(initialization_equations(sys))) && t !== nothing
831831
if eltype(u0map) <: Number
@@ -1011,14 +1011,12 @@ function DiffEqBase.ODEProblem{iip, specialize}(sys::AbstractODESystem, u0map =
10111011
affects, clocks = ModelingToolkit.generate_discrete_affect(
10121012
sys, dss...; eval_expression, eval_module)
10131013
discrete_cbs = map(affects, clocks) do affect, clock
1014-
if clock isa Clock
1015-
PeriodicCallback(affect, clock.dt;
1014+
@match clock begin
1015+
PeriodicClock(dt, _...) => PeriodicCallback(affect, dt;
10161016
final_affect = true, initial_affect = true)
1017-
elseif clock isa SolverStepClock
1018-
DiscreteCallback(Returns(true), affect,
1017+
&SolverStepClock => DiscreteCallback(Returns(true), affect,
10191018
initialize = (c, u, t, integrator) -> affect(integrator))
1020-
else
1021-
error("$clock is not a supported clock type.")
1019+
_ => error("$clock is not a supported clock type.")
10221020
end
10231021
end
10241022
if cbs === nothing
@@ -1112,14 +1110,15 @@ function DiffEqBase.DDEProblem{iip}(sys::AbstractODESystem, u0map = [],
11121110
u0 = h(p, tspan[1])
11131111
cbs = process_events(sys; callback, eval_expression, eval_module, kwargs...)
11141112
if has_discrete_subsystems(sys) && (dss = get_discrete_subsystems(sys)) !== nothing
1115-
affects, clocks, svs = ModelingToolkit.generate_discrete_affect(
1113+
affects, clocks = ModelingToolkit.generate_discrete_affect(
11161114
sys, dss...; eval_expression, eval_module)
1117-
discrete_cbs = map(affects, clocks, svs) do affect, clock, sv
1118-
if clock isa Clock
1119-
PeriodicCallback(DiscreteSaveAffect(affect, sv), clock.dt;
1115+
discrete_cbs = map(affects, clocks) do affect, clock
1116+
@match clock begin
1117+
PeriodicClock(dt, _...) => PeriodicCallback(affect, dt;
11201118
final_affect = true, initial_affect = true)
1121-
else
1122-
error("$clock is not a supported clock type.")
1119+
&SolverStepClock => DiscreteCallback(Returns(true), affect,
1120+
initialize = (c, u, t, integrator) -> affect(integrator))
1121+
_ => error("$clock is not a supported clock type.")
11231122
end
11241123
end
11251124
if cbs === nothing
@@ -1174,14 +1173,15 @@ function DiffEqBase.SDDEProblem{iip}(sys::AbstractODESystem, u0map = [],
11741173
u0 = h(p, tspan[1])
11751174
cbs = process_events(sys; callback, eval_expression, eval_module, kwargs...)
11761175
if has_discrete_subsystems(sys) && (dss = get_discrete_subsystems(sys)) !== nothing
1177-
affects, clocks, svs = ModelingToolkit.generate_discrete_affect(
1176+
affects, clocks = ModelingToolkit.generate_discrete_affect(
11781177
sys, dss...; eval_expression, eval_module)
1179-
discrete_cbs = map(affects, clocks, svs) do affect, clock, sv
1180-
if clock isa Clock
1181-
PeriodicCallback(DiscreteSaveAffect(affect, sv), clock.dt;
1178+
discrete_cbs = map(affects, clocks) do affect, clock
1179+
@match clock begin
1180+
PeriodicClock(dt, _...) => PeriodicCallback(affect, dt;
11821181
final_affect = true, initial_affect = true)
1183-
else
1184-
error("$clock is not a supported clock type.")
1182+
&SolverStepClock => DiscreteCallback(Returns(true), affect,
1183+
initialize = (c, u, t, integrator) -> affect(integrator))
1184+
_ => error("$clock is not a supported clock type.")
11851185
end
11861186
end
11871187
if cbs === nothing

‎src/systems/index_cache.jl

+106-21
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct IndexCache
3232
constant_idx::ParamIndexMap
3333
dependent_idx::ParamIndexMap
3434
nonnumeric_idx::ParamIndexMap
35+
observed_syms::Set{Union{Symbol, BasicSymbolic}}
3536
discrete_buffer_sizes::Vector{Vector{BufferTemplate}}
3637
tunable_buffer_sizes::Vector{BufferTemplate}
3738
constant_buffer_sizes::Vector{BufferTemplate}
@@ -48,16 +49,21 @@ function IndexCache(sys::AbstractSystem)
4849
let idx = 1
4950
for sym in unks
5051
usym = unwrap(sym)
52+
rsym = renamespace(sys, usym)
5153
sym_idx = if Symbolics.isarraysymbolic(sym)
5254
reshape(idx:(idx + length(sym) - 1), size(sym))
5355
else
5456
idx
5557
end
5658
unk_idxs[usym] = sym_idx
59+
unk_idxs[rsym] = sym_idx
5760
if hasname(sym) && (!iscall(sym) || operation(sym) !== getindex)
5861
name = getname(usym)
62+
rname = getname(rsym)
5963
unk_idxs[name] = sym_idx
64+
unk_idxs[rname] = sym_idx
6065
symbol_to_variable[name] = sym
66+
symbol_to_variable[rname] = sym
6167
end
6268
idx += length(sym)
6369
end
@@ -71,18 +77,41 @@ function IndexCache(sys::AbstractSystem)
7177
if idxs == idxs[begin]:idxs[end]
7278
idxs = reshape(idxs[begin]:idxs[end], size(idxs))
7379
end
80+
rsym = renamespace(sys, arrsym)
7481
unk_idxs[arrsym] = idxs
82+
unk_idxs[rsym] = idxs
7583
if hasname(arrsym)
7684
name = getname(arrsym)
85+
rname = getname(rsym)
7786
unk_idxs[name] = idxs
87+
unk_idxs[rname] = idxs
7888
symbol_to_variable[name] = arrsym
89+
symbol_to_variable[rname] = arrsym
7990
end
8091
end
8192
end
8293

94+
observed_syms = Set{Union{Symbol, BasicSymbolic}}()
8395
for eq in observed(sys)
84-
if symbolic_type(eq.lhs) != NotSymbolic() && hasname(eq.lhs)
85-
symbol_to_variable[getname(eq.lhs)] = eq.lhs
96+
if symbolic_type(eq.lhs) != NotSymbolic()
97+
sym = eq.lhs
98+
ttsym = default_toterm(sym)
99+
rsym = renamespace(sys, sym)
100+
rttsym = renamespace(sys, ttsym)
101+
push!(observed_syms, sym)
102+
push!(observed_syms, ttsym)
103+
push!(observed_syms, rsym)
104+
push!(observed_syms, rttsym)
105+
if hasname(sym) && (!iscall(sym) || operation(sym) !== getindex)
106+
symbol_to_variable[getname(sym)] = eq.lhs
107+
symbol_to_variable[getname(ttsym)] = eq.lhs
108+
symbol_to_variable[getname(rsym)] = eq.lhs
109+
symbol_to_variable[getname(rttsym)] = eq.lhs
110+
push!(observed_syms, getname(sym))
111+
push!(observed_syms, getname(ttsym))
112+
push!(observed_syms, getname(rsym))
113+
push!(observed_syms, getname(rttsym))
114+
end
86115
end
87116
end
88117

@@ -109,26 +138,40 @@ function IndexCache(sys::AbstractSystem)
109138

110139
for inp in inps
111140
inp = unwrap(inp)
141+
ttinp = default_toterm(inp)
142+
rinp = renamespace(sys, inp)
143+
rttinp = renamespace(sys, ttinp)
112144
is_parameter(sys, inp) ||
113145
error("Discrete subsystem $i input $inp is not a parameter")
114146
disc_clocks[inp] = i
115-
disc_clocks[default_toterm(inp)] = i
147+
disc_clocks[ttinp] = i
148+
disc_clocks[rinp] = i
149+
disc_clocks[rttinp] = i
116150
if hasname(inp) && (!iscall(inp) || operation(inp) !== getindex)
117151
disc_clocks[getname(inp)] = i
118-
disc_clocks[default_toterm(inp)] = i
152+
disc_clocks[getname(ttinp)] = i
153+
disc_clocks[getname(rinp)] = i
154+
disc_clocks[getname(rttinp)] = i
119155
end
120156
insert_by_type!(disc_buffers[i], inp)
121157
end
122158

123159
for sym in unknowns(disc_sys)
124160
sym = unwrap(sym)
161+
ttsym = default_toterm(sym)
162+
rsym = renamespace(sys, sym)
163+
rttsym = renamespace(sys, ttsym)
125164
is_parameter(sys, sym) ||
126165
error("Discrete subsystem $i unknown $sym is not a parameter")
127166
disc_clocks[sym] = i
128-
disc_clocks[default_toterm(sym)] = i
167+
disc_clocks[ttsym] = i
168+
disc_clocks[rsym] = i
169+
disc_clocks[rttsym] = i
129170
if hasname(sym) && (!iscall(sym) || operation(sym) !== getindex)
130171
disc_clocks[getname(sym)] = i
131-
disc_clocks[getname(default_toterm(sym))] = i
172+
disc_clocks[getname(ttsym)] = i
173+
disc_clocks[getname(rsym)] = i
174+
disc_clocks[getname(rttsym)] = i
132175
end
133176
insert_by_type!(disc_buffers[i], sym)
134177
end
@@ -138,21 +181,31 @@ function IndexCache(sys::AbstractSystem)
138181
# FIXME: This shouldn't be necessary
139182
eq.rhs === -0.0 && continue
140183
sym = eq.lhs
184+
ttsym = default_toterm(sym)
185+
rsym = renamespace(sys, sym)
186+
rttsym = renamespace(sys, ttsym)
141187
if iscall(sym) && operation(sym) == Shift(t, 1)
142188
sym = only(arguments(sym))
143189
end
144190
disc_clocks[sym] = i
145-
disc_clocks[sym] = i
146-
disc_clocks[default_toterm(sym)] = i
191+
disc_clocks[ttsym] = i
192+
disc_clocks[rsym] = i
193+
disc_clocks[rttsym] = i
147194
if hasname(sym) && (!iscall(sym) || operation(sym) !== getindex)
148195
disc_clocks[getname(sym)] = i
149-
disc_clocks[getname(default_toterm(sym))] = i
196+
disc_clocks[getname(ttsym)] = i
197+
disc_clocks[getname(rsym)] = i
198+
disc_clocks[getname(rttsym)] = i
150199
end
151200
end
152201
end
153202

154203
for par in inputs[continuous_id]
155204
is_parameter(sys, par) || error("Discrete subsystem input is not a parameter")
205+
par = unwrap(par)
206+
ttpar = default_toterm(par)
207+
rpar = renamespace(sys, par)
208+
rttpar = renamespace(sys, ttpar)
156209
iscall(par) && operation(par) isa Hold ||
157210
error("Continuous subsystem input is not a Hold")
158211
if haskey(disc_clocks, par)
@@ -163,6 +216,9 @@ function IndexCache(sys::AbstractSystem)
163216
haskey(disc_clocks, sym) ||
164217
error("Variable $par not part of a discrete subsystem")
165218
disc_clocks[par] = disc_clocks[sym]
219+
disc_clocks[ttpar] = disc_clocks[sym]
220+
disc_clocks[rpar] = disc_clocks[sym]
221+
disc_clocks[rttpar] = disc_clocks[sym]
166222
insert_by_type!(disc_buffers[disc_clocks[sym]], par)
167223
end
168224
end
@@ -172,13 +228,21 @@ function IndexCache(sys::AbstractSystem)
172228
for affect in affs
173229
if affect isa Equation
174230
is_parameter(sys, affect.lhs) || continue
175-
176-
disc_clocks[affect.lhs] = user_affect_clock
177-
disc_clocks[default_toterm(affect.lhs)] = user_affect_clock
178-
if hasname(affect.lhs) &&
179-
(!iscall(affect.lhs) || operation(affect.lhs) !== getindex)
180-
disc_clocks[getname(affect.lhs)] = user_affect_clock
181-
disc_clocks[getname(default_toterm(affect.lhs))] = user_affect_clock
231+
sym = affect.lhs
232+
ttsym = default_toterm(sym)
233+
rsym = renamespace(sys, sym)
234+
rttsym = renamespace(sys, ttsym)
235+
236+
disc_clocks[sym] = user_affect_clock
237+
disc_clocks[ttsym] = user_affect_clock
238+
disc_clocks[rsym] = user_affect_clock
239+
disc_clocks[rttsym] = user_affect_clock
240+
if hasname(sym) &&
241+
(!iscall(sym) || operation(sym) !== getindex)
242+
disc_clocks[getname(sym)] = user_affect_clock
243+
disc_clocks[getname(ttsym)] = user_affect_clock
244+
disc_clocks[getname(rsym)] = user_affect_clock
245+
disc_clocks[getname(rttsym)] = user_affect_clock
182246
end
183247
buffer = get!(disc_buffers, user_affect_clock, Dict{Any, Set{BasicSymbolic}}())
184248
insert_by_type!(buffer, affect.lhs)
@@ -188,11 +252,18 @@ function IndexCache(sys::AbstractSystem)
188252
is_parameter(sys, disc) ||
189253
error("Expected discrete variable $disc in callback to be a parameter")
190254
disc = unwrap(disc)
255+
ttdisc = default_toterm(disc)
256+
rdisc = renamespace(sys, disc)
257+
rttdisc = renamespace(sys, ttdisc)
191258
disc_clocks[disc] = user_affect_clock
192-
disc_clocks[default_toterm(disc)] = user_affect_clock
259+
disc_clocks[ttdisc] = user_affect_clock
260+
disc_clocks[rdisc] = user_affect_clock
261+
disc_clocks[rttdisc] = user_affect_clock
193262
if hasname(disc) && (!iscall(disc) || operation(disc) !== getindex)
194263
disc_clocks[getname(disc)] = user_affect_clock
195-
disc_clocks[getname(default_toterm(disc))] = user_affect_clock
264+
disc_clocks[getname(ttdisc)] = user_affect_clock
265+
disc_clocks[getname(rdisc)] = user_affect_clock
266+
disc_clocks[getname(rttdisc)] = user_affect_clock
196267
end
197268
buffer = get!(
198269
disc_buffers, user_affect_clock, Dict{Any, Set{BasicSymbolic}}())
@@ -267,13 +338,22 @@ function IndexCache(sys::AbstractSystem)
267338
buffer_sizes = BufferTemplate[]
268339
for (i, (T, buf)) in enumerate(buffers)
269340
for (j, p) in enumerate(buf)
341+
ttp = default_toterm(p)
342+
rp = renamespace(sys, p)
343+
rttp = renamespace(sys, ttp)
270344
idxs[p] = (i, j)
271-
idxs[default_toterm(p)] = (i, j)
345+
idxs[ttp] = (i, j)
346+
idxs[rp] = (i, j)
347+
idxs[rttp] = (i, j)
272348
if hasname(p) && (!iscall(p) || operation(p) !== getindex)
273349
idxs[getname(p)] = (i, j)
350+
idxs[getname(ttp)] = (i, j)
351+
idxs[getname(rp)] = (i, j)
352+
idxs[getname(rttp)] = (i, j)
274353
symbol_to_variable[getname(p)] = p
275-
idxs[getname(default_toterm(p))] = (i, j)
276-
symbol_to_variable[getname(default_toterm(p))] = p
354+
symbol_to_variable[getname(ttp)] = p
355+
symbol_to_variable[getname(rp)] = p
356+
symbol_to_variable[getname(rttp)] = p
277357
end
278358
end
279359
push!(buffer_sizes, BufferTemplate(T, length(buf)))
@@ -293,6 +373,7 @@ function IndexCache(sys::AbstractSystem)
293373
const_idxs,
294374
dependent_idxs,
295375
nonnumeric_idxs,
376+
observed_syms,
296377
disc_buffer_sizes,
297378
tunable_buffer_sizes,
298379
const_buffer_sizes,
@@ -306,6 +387,10 @@ function SymbolicIndexingInterface.is_variable(ic::IndexCache, sym)
306387
return check_index_map(ic.unknown_idx, sym) !== nothing
307388
end
308389

390+
function SymbolicIndexingInterface.is_variable(ic::IndexCache, sym::Symbol)
391+
return check_index_map(ic.unknown_idx, sym) !== nothing
392+
end
393+
309394
function SymbolicIndexingInterface.variable_index(ic::IndexCache, sym)
310395
return check_index_map(ic.unknown_idx, sym)
311396
end

‎src/systems/parameter_buffer.jl

+8-9
Original file line numberDiff line numberDiff line change
@@ -604,16 +604,15 @@ function SciMLBase.create_parameter_timeseries_collection(
604604

605605
for (i, partition) in enumerate(ps.discrete)
606606
clock = id_to_clock[i]
607-
if clock isa Clock
608-
ts = tspan[1]:(clock.dt):tspan[2]
609-
push!(buffers, DiffEqArray(NestedGetIndex{typeof(partition)}[], ts, (1, 1)))
610-
elseif clock isa SolverStepClock
611-
push!(buffers,
607+
@match clock begin
608+
PeriodicClock(dt, _...) => begin
609+
ts = tspan[1]:(dt):tspan[2]
610+
push!(buffers, DiffEqArray(NestedGetIndex{typeof(partition)}[], ts, (1, 1)))
611+
end
612+
&SolverStepClock => push!(buffers,
612613
DiffEqArray(NestedGetIndex{typeof(partition)}[], eltype(tspan)[], (1, 1)))
613-
elseif clock isa Continuous
614-
continue
615-
else
616-
error("Unhandled clock $clock")
614+
&Continuous => continue
615+
_ => error("Unhandled clock $clock")
617616
end
618617
end
619618

‎src/systems/systemstructure.jl

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import ..ModelingToolkit: isdiffeq, var_from_nested_derivative, vars!, flatten,
88
isparameter, isconstant,
99
independent_variables, SparseMatrixCLIL, AbstractSystem,
1010
equations, isirreducible, input_timedomain, TimeDomain,
11+
InferredTimeDomain,
1112
VariableType, getvariabletype, has_equations, ODESystem
1213
using ..BipartiteGraphs
1314
import ..BipartiteGraphs: invview, complete
@@ -331,7 +332,7 @@ function TearingState(sys; quick_cancel = false, check = true)
331332
!isdifferential(var) && (it = input_timedomain(var)) !== nothing
332333
set_incidence = false
333334
var = only(arguments(var))
334-
var = setmetadata(var, TimeDomain, it)
335+
var = setmetadata(var, VariableTimeDomain, it)
335336
@goto ANOTHER_VAR
336337
end
337338
end
@@ -660,7 +661,7 @@ function structural_simplify!(state::TearingState, io = nothing; simplify = fals
660661
@set! sys.defaults = merge(ModelingToolkit.defaults(sys),
661662
Dict(v => 0.0 for v in Iterators.flatten(inputs)))
662663
end
663-
ps = [setmetadata(sym, TimeDomain, get(time_domains, sym, Continuous()))
664+
ps = [setmetadata(sym, VariableTimeDomain, get(time_domains, sym, Continuous))
664665
for sym in get_ps(sys)]
665666
@set! sys.ps = ps
666667
else

‎test/clock.jl

+41-41
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ dt = 0.1
1414
@parameters kp
1515
# u(n + 1) := f(u(n))
1616

17-
eqs = [yd ~ Sample(t, dt)(y)
17+
eqs = [yd ~ Sample(dt)(y)
1818
ud ~ kp * (r - yd)
1919
r ~ 1.0
2020

@@ -70,35 +70,35 @@ sss, = ModelingToolkit._structural_simplify!(
7070
@test equations(sss) == [D(x) ~ u - x]
7171
sss, = ModelingToolkit._structural_simplify!(deepcopy(tss[1]), (inputs[1], ()))
7272
@test isempty(equations(sss))
73-
d = Clock(t, dt)
73+
d = Clock(dt)
7474
k = ShiftIndex(d)
75-
@test observed(sss) == [yd(k + 1) ~ Sample(t, dt)(y); r(k + 1) ~ 1.0;
75+
@test observed(sss) == [yd(k + 1) ~ Sample(dt)(y); r(k + 1) ~ 1.0;
7676
ud(k + 1) ~ kp * (r(k + 1) - yd(k + 1))]
7777

78-
d = Clock(t, dt)
78+
d = Clock(dt)
7979
# Note that TearingState reorders the equations
80-
@test eqmap[1] == Continuous()
80+
@test eqmap[1] == Continuous
8181
@test eqmap[2] == d
8282
@test eqmap[3] == d
8383
@test eqmap[4] == d
84-
@test eqmap[5] == Continuous()
85-
@test eqmap[6] == Continuous()
84+
@test eqmap[5] == Continuous
85+
@test eqmap[6] == Continuous
8686

8787
@test varmap[yd] == d
8888
@test varmap[ud] == d
8989
@test varmap[r] == d
90-
@test varmap[x] == Continuous()
91-
@test varmap[y] == Continuous()
92-
@test varmap[u] == Continuous()
90+
@test varmap[x] == Continuous
91+
@test varmap[y] == Continuous
92+
@test varmap[u] == Continuous
9393

9494
@info "Testing shift normalization"
9595
dt = 0.1
9696
@variables x(t) y(t) u(t) yd(t) ud(t)
9797
@parameters kp
98-
d = Clock(t, dt)
98+
d = Clock(dt)
9999
k = ShiftIndex(d)
100100

101-
eqs = [yd ~ Sample(t, dt)(y)
101+
eqs = [yd ~ Sample(dt)(y)
102102
ud ~ kp * yd + ud(k - 2)
103103

104104
# plant (time continuous part)
@@ -171,10 +171,10 @@ eqs = [yd ~ Sample(t, dt)(y)
171171

172172
eqs = [
173173
# controller (time discrete part `dt=0.1`)
174-
yd1 ~ Sample(t, dt)(y)
175-
ud1 ~ kp * (Sample(t, dt)(r) - yd1)
176-
yd2 ~ Sample(t, dt2)(y)
177-
ud2 ~ kp * (Sample(t, dt2)(r) - yd2)
174+
yd1 ~ Sample(dt)(y)
175+
ud1 ~ kp * (Sample(dt)(r) - yd1)
176+
yd2 ~ Sample(dt2)(y)
177+
ud2 ~ kp * (Sample(dt2)(r) - yd2)
178178

179179
# plant (time continuous part)
180180
u ~ Hold(ud1) + Hold(ud2)
@@ -183,24 +183,24 @@ eqs = [yd ~ Sample(t, dt)(y)
183183
@named sys = ODESystem(eqs, t)
184184
ci, varmap = infer_clocks(sys)
185185

186-
d = Clock(t, dt)
187-
d2 = Clock(t, dt2)
186+
d = Clock(dt)
187+
d2 = Clock(dt2)
188188
#@test get_eq_domain(eqs[1]) == d
189189
#@test get_eq_domain(eqs[3]) == d2
190190

191191
@test varmap[yd1] == d
192192
@test varmap[ud1] == d
193193
@test varmap[yd2] == d2
194194
@test varmap[ud2] == d2
195-
@test varmap[r] == Continuous()
196-
@test varmap[x] == Continuous()
197-
@test varmap[y] == Continuous()
198-
@test varmap[u] == Continuous()
195+
@test varmap[r] == Continuous
196+
@test varmap[x] == Continuous
197+
@test varmap[y] == Continuous
198+
@test varmap[u] == Continuous
199199

200200
@info "test composed systems"
201201

202202
dt = 0.5
203-
d = Clock(t, dt)
203+
d = Clock(dt)
204204
k = ShiftIndex(d)
205205
timevec = 0:0.1:4
206206

@@ -240,16 +240,16 @@ eqs = [yd ~ Sample(t, dt)(y)
240240

241241
ci, varmap = infer_clocks(cl)
242242

243-
@test varmap[f.x] == Clock(t, 0.5)
244-
@test varmap[p.x] == Continuous()
245-
@test varmap[p.y] == Continuous()
246-
@test varmap[c.ud] == Clock(t, 0.5)
247-
@test varmap[c.yd] == Clock(t, 0.5)
248-
@test varmap[c.y] == Continuous()
249-
@test varmap[f.y] == Clock(t, 0.5)
250-
@test varmap[f.u] == Clock(t, 0.5)
251-
@test varmap[p.u] == Continuous()
252-
@test varmap[c.r] == Clock(t, 0.5)
243+
@test varmap[f.x] == Clock(0.5)
244+
@test varmap[p.x] == Continuous
245+
@test varmap[p.y] == Continuous
246+
@test varmap[c.ud] == Clock(0.5)
247+
@test varmap[c.yd] == Clock(0.5)
248+
@test varmap[c.y] == Continuous
249+
@test varmap[f.y] == Clock(0.5)
250+
@test varmap[f.u] == Clock(0.5)
251+
@test varmap[p.u] == Continuous
252+
@test varmap[c.r] == Clock(0.5)
253253

254254
## Multiple clock rates
255255
@info "Testing multi-rate hybrid system"
@@ -260,10 +260,10 @@ eqs = [yd ~ Sample(t, dt)(y)
260260

261261
eqs = [
262262
# controller (time discrete part `dt=0.1`)
263-
yd1 ~ Sample(t, dt)(y)
263+
yd1 ~ Sample(dt)(y)
264264
ud1 ~ kp * (r - yd1)
265265
# controller (time discrete part `dt=0.2`)
266-
yd2 ~ Sample(t, dt2)(y)
266+
yd2 ~ Sample(dt2)(y)
267267
ud2 ~ kp * (r - yd2)
268268

269269
# plant (time continuous part)
@@ -273,8 +273,8 @@ eqs = [yd ~ Sample(t, dt)(y)
273273

274274
@named cl = ODESystem(eqs, t)
275275

276-
d = Clock(t, dt)
277-
d2 = Clock(t, dt2)
276+
d = Clock(dt)
277+
d2 = Clock(dt2)
278278

279279
ci, varmap = infer_clocks(cl)
280280
@test varmap[yd1] == d
@@ -331,8 +331,8 @@ eqs = [yd ~ Sample(t, dt)(y)
331331
using ModelingToolkitStandardLibrary.Blocks
332332

333333
dt = 0.05
334-
d = Clock(t, dt)
335-
k = ShiftIndex()
334+
d = Clock(dt)
335+
k = ShiftIndex(d)
336336

337337
@mtkmodel DiscretePI begin
338338
@components begin
@@ -362,7 +362,7 @@ eqs = [yd ~ Sample(t, dt)(y)
362362
output = RealOutput()
363363
end
364364
@equations begin
365-
output.u ~ Sample(t, dt)(input.u)
365+
output.u ~ Sample(dt)(input.u)
366366
end
367367
end
368368

@@ -474,7 +474,7 @@ eqs = [yd ~ Sample(t, dt)(y)
474474

475475
## Test continuous clock
476476

477-
c = ModelingToolkit.SolverStepClock(t)
477+
c = ModelingToolkit.SolverStepClock
478478
k = ShiftIndex(c)
479479

480480
@mtkmodel CounterSys begin

‎test/parameter_dependencies.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,10 @@ end
157157
dt = 0.1
158158
@variables x(t) y(t) u(t) yd(t) ud(t) r(t) z(t)
159159
@parameters kp kq
160-
d = Clock(t, dt)
160+
d = Clock(dt)
161161
k = ShiftIndex(d)
162162

163-
eqs = [yd ~ Sample(t, dt)(y)
163+
eqs = [yd ~ Sample(dt)(y)
164164
ud ~ kp * (r - yd) + kq * z
165165
r ~ 1.0
166166
u ~ Hold(ud)

0 commit comments

Comments
 (0)
Please sign in to comment.