I am building a model with two periodic clocks in which more than one component reads its own
sample period with SampleTime(). Each such component is written clock-agnostically and is
instantiated on whichever clock the model connects it to: one on a 1 ms sensing clock, another on
a 5 ms control clock. Clock inference rejects the model, reporting one partition with two clocks.
The two partitions share no variable. The only thing they have in common is the SampleTime()
term, and it appears in the list of involved variables:
using ModelingToolkit
using ModelingToolkit: t_nounits as t
kf = ShiftIndex(Clock(0.001))
ks = ShiftIndex(Clock(0.005))
# Two discrete partitions that share no variable, each accumulating its own sample time.
function build(slow_period)
@variables xf(t) = 0.0 xs(t) = 0.0
@named sys = System([xf(kf) ~ xf(kf - 1) + SampleTime(),
xs(ks) ~ xs(ks - 1) + slow_period], t)
return sys
end
for (what, slow_period) in (("SampleTime() in both partitions", SampleTime()),
("SampleTime() in one partition", 0.005))
try
mtkcompile(build(slow_period))
println(what, " -> compiles")
catch e
println(what, " -> ", sprint(showerror, e))
end
end
SampleTime() in both partitions -> ArgumentError: Found clock partition with multiple associated
clocks. Involved variables: [xf(t), xs(t), Shift(t, -1)(xf(t)), Shift(t, -1)(xs(t)),
ModelingToolkitBase.SampleTime(nothing)()]. Involved clocks:
[PeriodicClock(0.001, 0.0), PeriodicClock(0.005, 0.0)].
SampleTime() in one partition -> HybridSystemNotSupportedException: Discrete systems with
multiple clocks are not supported with the standard MTK compiler.
The second line is the control: with the slow partition's period given as a number, inference
completes and the model gets as far as code generation, where it meets the standard compiler's
unrelated multi-clock limitation. (My real model goes through SynchToolkit's compile_lustre pass
instead, and compiles once the second SampleTime() is removed.) So the difference between the
two runs is only whether inference separates the two partitions.
What makes this look like the term rather than the model:
SampleTime()'s own docstring defines it per equation -- "time sampled at the inferred clock for
that equation".
- The substitution honours that:
substitute_sample_time
(ModelingToolkitTearing/src/clock_inference/interface.jl) walks the equations with their
eq_domain and substitutes each equation's own period, so by the time a period is needed the
per-partition answer is already available.
- Inference itself does not: it builds its graph over
fullvars
(ModelingToolkitTearing/src/clock_inference/clock_inference.jl), and SampleTime() is a single
nullary term sitting in there like any other variable, so it is one node shared by every equation
that mentions it. Two such equations on two clocks therefore form one connected component with
two clock vertices, which is what the error reports.
The practical effect is that a model may contain at most one SampleTime() if it has more than one
clock, which is a restriction I could not find documented. It is also hard to trace from the error:
the message lists the whole merged partition, so in a real model it names a few dozen variables
belonging to components that have nothing to do with each other, and mentions neither the term nor
the two components that use it. Localising it in my own model took a bisect over the structural
parameters that switch the second use on.
Versions: ModelingToolkit 11.42.0, ModelingToolkitBase 1.69.0, ModelingToolkitTearing 1.20.6,
Julia 1.13.0.
I am building a model with two periodic clocks in which more than one component reads its own
sample period with
SampleTime(). Each such component is written clock-agnostically and isinstantiated on whichever clock the model connects it to: one on a 1 ms sensing clock, another on
a 5 ms control clock. Clock inference rejects the model, reporting one partition with two clocks.
The two partitions share no variable. The only thing they have in common is the
SampleTime()term, and it appears in the list of involved variables:
The second line is the control: with the slow partition's period given as a number, inference
completes and the model gets as far as code generation, where it meets the standard compiler's
unrelated multi-clock limitation. (My real model goes through SynchToolkit's
compile_lustrepassinstead, and compiles once the second
SampleTime()is removed.) So the difference between thetwo runs is only whether inference separates the two partitions.
What makes this look like the term rather than the model:
SampleTime()'s own docstring defines it per equation -- "time sampled at the inferred clock forthat equation".
substitute_sample_time(
ModelingToolkitTearing/src/clock_inference/interface.jl) walks the equations with theireq_domainand substitutes each equation's own period, so by the time a period is needed theper-partition answer is already available.
fullvars(
ModelingToolkitTearing/src/clock_inference/clock_inference.jl), andSampleTime()is a singlenullary term sitting in there like any other variable, so it is one node shared by every equation
that mentions it. Two such equations on two clocks therefore form one connected component with
two clock vertices, which is what the error reports.
The practical effect is that a model may contain at most one
SampleTime()if it has more than oneclock, which is a restriction I could not find documented. It is also hard to trace from the error:
the message lists the whole merged partition, so in a real model it names a few dozen variables
belonging to components that have nothing to do with each other, and mentions neither the term nor
the two components that use it. Localising it in my own model took a bisect over the structural
parameters that switch the second use on.
Versions: ModelingToolkit 11.42.0, ModelingToolkitBase 1.69.0, ModelingToolkitTearing 1.20.6,
Julia 1.13.0.