In Modelica version 3.3, new features were introduced to address concerns about non-deterministic discrete behavior [Elmqvist]. In this section, we’ll present some examples of how these issues presented themselves before version 3.3 and show how these new features help address them.
To start, consider the following model:
model IndependentSampling "Sampling independently"
Real x "Sampled at 10Hz via one method";
Real y "Sampled at 10Hz via another method";
Real e "Error between x and y";
Real next_time "Next sample for y";
equation
when sample(0,0.1) then
x = time;
end when;
when {initial(), time>pre(next_time)} then
y = time;
next_time = pre(next_time)+0.1;
end when;
e = x-y;
end IndependentSampling;
If you look carefully, you will see that x
and y
are both
computed at discrete times. Furthermore, they are both sampled
initially at the start of the simulation and then again every 0.1
seconds. But the question is, are they really identical? To help
address this question, we include the variable e
which measures
the difference between them.
Simulating this model, we get the following trajectories for x
and
y
. Of course, they look identical. But in order to really
determine if there are any differences between them, let’s plot the
error value, e
:
Now, let’s consider the following model:
model SynchronizedSampling "A simple way to synchronize sampling"
Integer tick "A clock counter";
Real x, y;
Real e "Error between x and y";
equation
when sample(0,0.1) then
tick = pre(tick)+1;
end when;
when change(tick) then
x = time;
end when;
when change(tick) then
y = time;
end when;
e = x-y;
end SynchronizedSampling;
Here, we set up a common signal that triggers the assignment to both
variables. In this way, we can be sure that when the tick
signal
becomes true, both x
and y
will be assigned a value. Sure
enough, if we run this model, we see that the error is always zero:
This kind of approach, where each signal is sampled based on a common “tick” (or clock), is a good way to avoid determinism issues. However, what about cases where you have one signal that samples at a higher rate than another, but you know that at certain times they should be sampled together? Consider the following example:
model SubsamplingWithIntegers "Use integers to implement subsampling"
Integer tick "Clock counter";
Real x, y, z;
equation
when sample(0,0.1) then
tick = pre(tick)+1;
end when;
when change(tick) then
x = time;
end when;
when change(tick) then
y = time;
end when;
when mod(tick-1,2)==0 then
z = time;
end when;
end SubsamplingWithIntegers;
In this case, the variable tick
is a counter. Every time it
changes, we update the values of x
and y
. So this much is
identical to the previous models. However, we added a third signal,
z
, that is sampled only when the value of tick
is odd. So
x
and y
are sampled twice as often. But every time z
is
updated, we can be sure that x
and y
are updated at exactly
the same time. Simulating this model gives us:
This is the approach taken in Modelica prior to version 3.3. But version 3.3 introduced some new features that allow us to more easily express these situations.
Consider the following model:
model SamplingWithClocks "Using clocks to sub and super sample"
Real x, y, z, w;
equation
x = sample(time, Clock(1,10));
y = sample(time, Clock(1,10));
z = subSample(x, 2);
w = superSample(x, 3);
end SamplingWithClocks;
Now, instead of relying on a when
statement, we use an enhanced
version of the sample
function where the first argument is an
expression to evaluate to determine the sampled value and the second
argument is used to tell us when to evaluate it. Let’s work through
these lines one by one and discuss them. First we have:
x = sample(time, Clock(1,10));
Note that we have done away with the 0.1
. We no longer see any
mention of the clock interval as a real number. Instead, we use the
Clock
operator to the define clock interval for x
as a
rational number. This is important because it allows us to do exact
comparisons between clocks. This brings us to the next line:
y = sample(time, Clock(1,10));
Again, we see the rational representation of the clock. What this
means, in practice, is that the Modelica compiler can know for certain
that these two clocks, x
and y
, are identical because they are
defined in terms of integer quantities which allow exact comparison.
This means that when executing a simulation, we can know for certain
that these two clocks will trigger simultaneously.
If we wanted to create a clock that was exactly half as slow as
x
, we can use the subSample
operator to accomplish this. We
see this in the definition of z
:
z = subSample(x, 2);
Behind the scenes, the Modelica compiler can reason about these
clocks. It knows that the x
clock triggers every
of a second. Using the information provided by
the subSample
operator the Modelica compiler can therefore deduce
that z
triggers every of a second.
Conceptually, this means that z
could also have been defined as:
z = sample(time, Clock(2,10));
But by defining z
using the subSample
operator and defining it
with respect to x
we ensure that z
is always triggering at
half the frequency of x
regardless of how x
is defined.
In a similar way, we can define another clock, w
that triggers 3 times as
frequently as x
by using the superSample
operator:
w = superSample(x, 3);
Again, we could have defined w
directly using sample
with:
w = sample(time, Clock(1,30));
But by using superSample
, we can ensure that w
is always
sampling three times as fast as x
and six times as fast as z
(since z
is also defined with respect to x
).
The synchronous clock features in Modelica are relatively new. As such, they are not yet supported by all Modelica compilers. To learn more about these synchronous features and their applications see [Elmqvist] and/or the Modelica Specification, version 3.3 or later.
[Elmqvist] | (1, 2) “Fundamentals of Synchronous Control in Modelica”, Hilding Elmqvist, Martin Otter and Sven-Erik Mattsson http://www.ep.liu.se/ecp/076/001/ecp12076001.pdf |