 Synchronous Systems¶

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