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