Skip to content

Commit 9952972

Browse files
authored
Merge pull request #1912 from SciML/myb_fb/clock
Add TimeDomain, Clock, and their inference
2 parents b6b511c + 994478b commit 9952972

File tree

9 files changed

+593
-7
lines changed

9 files changed

+593
-7
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ version = "8.30.0"
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
88
ArrayInterfaceCore = "30b0a656-2188-435a-8636-2ec0e6a096e2"
99
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
10+
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
1011
ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
1112
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
1213
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
@@ -48,6 +49,7 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
4849
AbstractTrees = "0.3, 0.4"
4950
ArrayInterfaceCore = "0.1.1"
5051
Combinatorics = "1"
52+
Compat = "3.42, 4"
5153
ConstructionBase = "1"
5254
DataStructures = "0.17, 0.18"
5355
DiffEqBase = "6.103.0"

src/ModelingToolkit.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ $(DocStringExtensions.README)
33
"""
44
module ModelingToolkit
55
using DocStringExtensions
6+
using Compat
67
using AbstractTrees
78
using DiffEqBase, SciMLBase, ForwardDiff, Reexport
89
using SciMLBase: StandardODEProblem, StandardNonlinearProblem, handle_varmap
@@ -145,8 +146,11 @@ include("systems/sparsematrixclil.jl")
145146
include("systems/discrete_system/discrete_system.jl")
146147
include("systems/validation.jl")
147148
include("systems/dependency_graphs.jl")
149+
include("clock.jl")
150+
include("discretedomain.jl")
148151
include("systems/systemstructure.jl")
149152
using .SystemStructures
153+
include("systems/clock_inference.jl")
150154

151155
include("debugging.jl")
152156
include("systems/alias_elimination.jl")
@@ -214,4 +218,10 @@ export @variables, @parameters
214218
export @named, @nonamespace, @namespace, extend, compose, complete
215219
export debug_system
216220

221+
export Continuous, Discrete, sampletime, input_timedomain, output_timedomain
222+
#export has_discrete_domain, has_continuous_domain
223+
#export is_discrete_domain, is_continuous_domain, is_hybrid_domain
224+
export Sample, Hold, Shift, ShiftIndex
225+
export Clock #, InferredDiscrete,
226+
217227
end # module

src/clock.jl

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
abstract type TimeDomain end
2+
abstract type AbstractDiscrete <: TimeDomain end
3+
4+
Base.Broadcast.broadcastable(d::TimeDomain) = Ref(d)
5+
6+
struct Inferred <: TimeDomain end
7+
struct InferredDiscrete <: AbstractDiscrete end
8+
struct Continuous <: TimeDomain end
9+
10+
const UnknownDomain = Union{Nothing, Inferred, InferredDiscrete}
11+
const InferredDomain = Union{Inferred, InferredDiscrete}
12+
13+
Symbolics.option_to_metadata_type(::Val{:timedomain}) = TimeDomain
14+
15+
"""
16+
is_continuous_domain(x::Sym)
17+
Determine if variable `x` is a continuous-time variable.
18+
"""
19+
is_continuous_domain(x::Sym) = getmetadata(x, TimeDomain, false) isa Continuous
20+
21+
"""
22+
is_discrete_domain(x::Sym)
23+
Determine if variable `x` is a discrete-time variable.
24+
"""
25+
is_discrete_domain(x::Sym) = getmetadata(x, TimeDomain, false) isa Discrete
26+
27+
# is_discrete_domain(x::Sym) = isvarkind(Discrete, x)
28+
29+
has_continuous_domain(x::Sym) = is_continuous_domain(x)
30+
has_discrete_domain(x::Sym) = is_discrete_domain(x)
31+
32+
function get_time_domain(x)
33+
if istree(x) && operation(x) isa Operator
34+
output_timedomain(x)
35+
else
36+
getmetadata(x, TimeDomain, nothing)
37+
end
38+
end
39+
get_time_domain(x::Num) = get_time_domain(value(x))
40+
41+
"""
42+
has_time_domain(x::Sym)
43+
Determine if variable `x` has a time-domain attributed to it.
44+
"""
45+
function has_time_domain(x::Union{Sym, Term})
46+
# getmetadata(x, Continuous, nothing) !== nothing ||
47+
# getmetadata(x, Discrete, nothing) !== nothing
48+
getmetadata(x, TimeDomain, nothing) !== nothing
49+
end
50+
has_time_domain(x::Num) = has_time_domain(value(x))
51+
has_time_domain(x) = false
52+
53+
for op in [Differential, Difference]
54+
@eval input_timedomain(::$op, arg = nothing) = Continuous()
55+
@eval output_timedomain(::$op, arg = nothing) = Continuous()
56+
end
57+
58+
"""
59+
has_discrete_domain(x)
60+
true if `x` contains discrete signals (`x` may or may not contain continuous-domain signals). `x` may be an expression or equation.
61+
See also [`is_discrete_domain`](@ref)
62+
"""
63+
has_discrete_domain(x) = hasshift(x) || hassample(x) || hashold(x)
64+
65+
"""
66+
has_continuous_domain(x)
67+
true if `x` contains continuous signals (`x` may or may not contain discrete-domain signals). `x` may be an expression or equation.
68+
See also [`is_continuous_domain`](@ref)
69+
"""
70+
has_continuous_domain(x) = hasderiv(x) || hasdiff(x) || hassample(x) || hashold(x)
71+
72+
"""
73+
is_hybrid_domain(x)
74+
true if `x` contains both discrete and continuous-domain signals. `x` may be an expression or equation.
75+
"""
76+
is_hybrid_domain(x) = has_discrete_domain(x) && has_continuous_domain(x)
77+
78+
"""
79+
is_discrete_domain(x)
80+
true if `x` contains only discrete-domain signals.
81+
See also [`has_discrete_domain`](@ref)
82+
"""
83+
is_discrete_domain(x) = has_discrete_domain(x) && !has_continuous_domain(x)
84+
85+
"""
86+
is_continuous_domain(x)
87+
true if `x` contains only continuous-domain signals.
88+
See also [`has_continuous_domain`](@ref)
89+
"""
90+
is_continuous_domain(x) = !has_discrete_domain(x) && has_continuous_domain(x)
91+
92+
struct ClockInferenceException <: Exception
93+
msg::Any
94+
end
95+
96+
function Base.showerror(io::IO, cie::ClockInferenceException)
97+
print(io, "ClockInferenceException: ", cie.msg)
98+
end
99+
100+
abstract type AbstractClock <: AbstractDiscrete end
101+
102+
"""
103+
Clock <: AbstractClock
104+
Clock(t; dt)
105+
The default periodic clock with independent variables `t` and tick interval `dt`.
106+
If `dt` is left unspecified, it will be inferred (if possible).
107+
"""
108+
struct Clock <: AbstractClock
109+
"Independent variable"
110+
t::Any
111+
"Period"
112+
dt::Any
113+
Clock(t, dt = nothing) = new(value(t), dt)
114+
end
115+
116+
sampletime(c) = isdefined(c, :dt) ? c.dt : nothing

0 commit comments

Comments
 (0)