Failure to handle clock inference in Dymola - modelica

The following model in Dymola gives an error,
model Test1
Real outvar;
Real outvarc;
Real internal;
parameter Real Tp=0.1;
equation
when Clock(Tp) then
internal = time;
end when;
outvar = hold(outvarc);
algorithm
if (firstTick(internal)) then
outvarc := 1;
else
outvarc := previous(outvarc);
outvarc := outvarc + 1;
end if;
end Test1;
If I modify the variable internal as follows then the model works.
model Test2
Real outvar;
Real outvarc;
Boolean internal(start=true);
parameter Real Tp=0.1;
equation
when Clock(Tp) then
internal = false;
end when;
outvar = hold(outvarc);
algorithm
if (firstTick(internal)) then
outvarc := 1;
else
outvarc := previous(outvarc);
outvarc := outvarc + 1;
end if;
end Test2;
Is there any explanation why model Test1 is giving an error?

It fails in Dymola 2022x and earlier.
In Dymola 2023 and later it gives the warning:
The sub clock, BaseClock_0.SubClock_1, includes pre, time, or
non-clocked when, but no solver method is specified. This is not
correct according to the specification and it could indicate that it
was clocked by mistake. The warning can be disabled by setting
Advanced.Translation.WarnForMissingSolverMethod=false;
That suggests that the simplest solution would be:
when Clock(Tp) then
internal = sample(time);
end when;
Basically time is not a clocked variable. (Yes, I'm aware that time in clocked partitions is messier than this suggests.)

Here is a suggestion for a little more compact code of the overall model. It gives a bit different results as it uses the "discrete time" internal to make equations discrete/clocked automatically, but I guess the would be the use-case anyways (otherwise sampling time does not make a lot of sense).
model Test3
parameter Real Tp=0.1;
discrete Real internal; // "discrete" is optional in Dymola, but helps understanding
discrete Real outvarc(start=1, fixed=true); // setting an initial value
Real outvar;
equation
internal = sample(time, Clock(Tp)); // sampling time with a locally defined clock and store it to "internal"
outvarc = previous(outvarc)+internal; // using a sampled/clocked variable (internal) makes the equation discrete
outvar = hold(outvarc); // converting to a continuous variable from a clocked one
end Test3;
Suggestions for further improvements welcome...

Related

Too many equations for discrete state machine variables in when statements in Modelica

I have a contrived Modelica model in which a have a state machine variable manipulated by multiple when statements:
model WhenExample
type State = enumeration(first, second, third);
State state;
initial equation
state = State.first;
equation
when sample(0, 1) then
state = State.second;
end when;
when sample(0, 3) then
state = State.third;
end when;
end WhenExample;
When compiling under OpenModelica OMC, I get the following error:
[1] 16:46:39 Symbolic Error
Too many equations, over-determined system. The model has 2 equation(s) and 1 variable(s).
This kinda makes sense as I do have two equations for my single state variable. However, those equations only apply at discrete points in time, right?
Do I need to make sure all "manipulations" of a particular variable happen only in a single when statement?
See Modelica Spec, section 8.5 Events and Synchronization:
https://www.modelica.org/documents/ModelicaSpec33Revision1.pdf
Just before section 8.6 there is an example that should help you.
Some code based on that is given below:
model WhenExample
parameter Integer multiplySample = 3;
Boolean fastSample, slowSample;
Integer ticks(start=0);
type State = enumeration(first, second, third);
State state(start = State.first);
equation
fastSample = sample(0,1);
algorithm
when fastSample then
ticks := if pre(ticks) < multiplySample then pre(ticks)+1 else 0;
slowSample := pre(ticks) == 0;
state := State.second;
end when;
when slowSample then
state := State.third;
end when;
end WhenExample;

issue generating triangular wave function in Modelica

I'm trying to create a model where one Modelica variable is a triangular wave of another variable. First I tried the floor() function as below:
model test1
final constant Real pi=2*Modelica.Math.asin(1.0);
parameter Real b = 1;
parameter Real a = 1;
Real x,p,u;
equation
if sign(sin(x*pi/b))>=0 then
p=a*(x-b*floor(x/b));
else
p=a*(b-(x-b*floor(x/b)));
end if;
x=time;
u = floor(x/b);
end test1
(x=time; is arbitrary so the model compiles)
but the result is weird, as you can see below
zoom in:
somehow 0.005 seconds before the next step floor function behaves unexpectedly and becomes a linear function ending by the next value.
then I tried the ceil() function. everything seemed right till I realised the same problem happens with ceil() function at other values (e.g. x=13)
I would appreciate if you could:
help me understand why this "glitch" happens and if it is intentional by design or a bug?
how I can fix this?
are there any alternatives to create a triangular wave function?
P.S. I am using this "wave function" to model the interaction between two jagged bodies"
If you are allowed to utilize the Modelica Standard Library, you can build up a parametrized, time-based zigzag signal using the CombiTimeTable block with linear interpolation and periodic extrapolation. For example,
model Test4
parameter Real a=2 "Amplitude";
parameter Real b=3 "Period";
Real y=zigzag.y[1] "Zigzag";
Modelica.Blocks.Sources.CombiTimeTable zigzag(
table=[0,0;b/4,a;b/4,a;b/2,0;b/2,0;3*b/4,-a;3*b/4,-a;b,0],
extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic)
annotation(Placement(transformation(extent={{-80,60},{-60,80}})));
Modelica.Blocks.Sources.Trapezoid trapezoid(
amplitude=2*a,
rising=b/2,
width=0,
falling=b/2,
period=b,
offset=-a)
annotation(Placement(transformation(extent={{-80,25},{-60,45}})));
annotation(uses(Modelica(version="3.2.2")));
end Test4;
I don't have an explanation for the glitches in your simulation.
However, I would take another approach to the sawtooth function: I see it as an integrator integrating +1 and -1 upwards and downwards. The integration time determines the amplitude and period of the sawtooth function.
The pictures below show an implementation using MSL blocks and one using code. The simulation results below are the same for both implementations.
Best regards,
Rene Just Nielsen
Block diagram:
Code:
model test3
parameter Real a=2 "amplitude";
parameter Real b=3 "period";
Real u, y;
initial equation
u = 1;
y = 0;
equation
4*a/b*u = der(y);
when y > a then
u = -1;
elsewhen y < -a then
u = 1;
end when;
end test3;
Simulation result:
The problem I guess is due to floating point representation and events not occurring at exact times.
Consider x-floor(x) and 1-(x-floor(x)) at time=0.99, they are 0.99 and 0.01; at time=1.00 they are 0.0 and 1.0, which causes your problems.
For a=b=1, you can use the following equation for p:
p=min(mod(x,2),2-mod(x,2));. You can even add noEvent to it, and you can consider the signal continuous (but not differentiable).
model test
parameter Real b = 1;
parameter Real a = 3;
Real x, p;
equation
p = 2*a*min(1 / b * mod(x, b ),1 - 1/b * mod(x, b));
x = time;
end test;
My first advise would be to remove the sign-function, since there is no benefit of doing sign(foo)>=0 compared to foo>=0.
Interesting enough that seems to fix the problem in Dymola - and I assume also in OpenModelica:
model test1 "almost original"
final constant Real pi=2*Modelica.Math.asin(1.0);
parameter Real b = 1;
parameter Real a = 1;
Real x,p,u;
equation
if sin(x*pi/b)>=0 then
p=a*(x-b*floor(x/b));
else
p=a*(b-(x-b*floor(x/b)));
end if;
x=time;
u = floor(x/b);
end test1;
Now I only have to explain that - and the reason is that sin(x*pi/b) is slightly out of sync with the floor-function, but if you use sin(x*pi/b)>=0 that is within the root-finding epsilon and nothing strange happen.
When you use sign(sin(x*pi/b))>=0 that is no longer possible, instead of having sin(x*pi/b) an epsilon below zero it is now -1, and instead of epsilon above zero it is 1.
The real solution is thus slightly more complicated:
model test2 "working"
parameter Real b = 1;
parameter Real a = 1;
Real x,p,u;
Real phase=mod(x,b*2);
equation
if phase<b then
p=a/b*phase;
else
p=a-a/b*(phase-b);
end if;
x=time;
u = floor(x/b);
end test2;
which was improved based on a suggested solution:
model test3 "almost working"
parameter Real b = 1;
parameter Real a = 1;
Real x,p,u;
equation
if mod(x,2*b)<b then
p=a/b*mod(x,b);
else
p=a-a/b*mod(x,b);
end if;
x=time;
u = floor(x/b);
end test3;
The key point in this solution, test2, is that there is only one problematic event generating expression mod(x,2*b) - and the < will not get out of sync with this.
In practice test3 will almost certainly also work, but in unlikely cases the event generation might get out of sync between mod(x,2*b) and mod(x,b); with unknown consequences.
Note that all three examples are now modified to generate output that looks similar.

Dynamically switching connect in Modelica

Assume I have a large connector involving all kinds of base types (Real, Integer, String, Boolean). How can I switch connections based on state events?
I would like to do something like this:
model switch
input ComplicatedConnector icon[2];
output ComplicatedConnector ocon;
input Real x;
equation
if x >= 0 then
connect(ocon, icon[1]);
else
connect(ocon, icon[2]);
end if;
end switch;
This does not work. How can it be properly expressed in Modelica?
Answer based on comment by Adrian Pop.
model switch
input ComplicatedConnector icon[2];
output ComplicatedConnector ocon;
input Real x;
ComplicatedConnector con;
initial equation
con = icon[1];
equation
connect(ocon, con);
when x >= 0 then
con := icon[1];
end when;
when x < 0 then
con := icon[2];
end when;
end switch;
Update: The model above is wrong because ocon outputs the initial value of icon[1] forever if no event occurs which is not what you would expect from a switch. Note that this is not due to a wrong answer but due to my false interpretation of the answer. The following model is based on the answer by Michael Tiller.
model switch
input ComplicatedConnector icon[2];
output ComplicatedConnector ocon;
input Real x;
Integer k;
initial equation
k = 1;
equation
ocon = icon[k];
when x >= 0 then
k := 1;
elsewhen x < 0 then
k := 2;
end when;
end switch;
Is not possible. You can only switch them based on a parameter known at compile time (also known as structural parameter). The condition in the if equation containing connects needs to be a parameter expression.
Note that connect statements are equations. You can expand them out yourself. They exist mainly to avoid "bookkeeping" errors for generating boilerplate equations. So what I suggest you do is simply take your switch model and expand each connect into equations. The it should work.

Modelica: Calculating a variable using both der() and explicit declaration

I apologize for the poor title, but I found it hard to describe the problem in a comprehensible way.
What I want to do is to solve an ODE, but I don't want to start integrating at time = 0. I want the initial value, i.e. the starting point of the integration, to be accessible for changes until the integration starts. I'll try to illustrate this with a piece of code:
model testModel "A test"
parameter Real startTime = 10 "Starting time of integration";
parameter Real a = 0.1 "Some constant";
Real x;
input Real x_init = 3;
initial equation
x = x_init;
equation
if time <= startTime then
x = x_init;
else
der(x) = -a*x;
end if;
end testModel;
Notice that x_init is declared as input, and can be changed continuously. This code yields an error message, and as far as I can tell, this is due to the fact that I have declared x as both der(x) = and x =. The error message is:
Error: Singular inconsistent scalar system for der(x) = ( -(if time <= 10 then x-x_init else a*x))/((if time <= 10 then 0.0 else 1.0)) = -1e-011/0
I thought about writing
der(x) = 0
instead of
x = init_x
in the if-statement, which will avoid the error message. The problem in such an approach, however, is that I lose the ability to modify the x_init, i.e. the starting point of the integration, before the integration starts. Lets say, for instance, that x_init changes from 3 to 4 at time = 7.
Is there a work-around to perform what I want? Thanks.
(I'm gonna use this to simulate several submodels as part of a network, but the submodels are not going to be initiated at the same time, hence the startTime-variable and the ability to change the initial condition before integration.)
Suggested solution: I've tried out the following:
when time >= startTime
reinit(x,x_init);
end when;
in combination with the der(x) = 0 alternative. This seems to work. Other suggestions are welcome.
If your input is differentiable, this should work:
model testModel "A test"
parameter Real startTime = 10 "Starting time of integration";
parameter Real a = 0.1 "Some constant";
Real x;
input Real x_init = 3;
initial equation
x = x_init;
equation
if time <= startTime then
der(x) = der(x_init);
else
der(x) = -a*x;
end if;
end testModel;
Otherwise, I suspect the best you could do would be to have your x variable be a very fast first-order tracker before startTime.
The fundamental issue here is that you are trying to model a variable index DAE. None of the Modelica tools I'm aware of support variable index systems like this.

model a periodic time-varing real variable in OpenModelica

What I want to model is a periodic time-varing real variable, the following code can not be simulated. Does somebody have suggestion?
class try
discrete Real x(start = 1);
algorithm
when sample(0,4) then
x := 1; // reinit(x, 1) also does not work
end when;
equation
der(x) = 1;
end try;
All the error message is listed as follows:
Translation 18:32:29 0:0-0:0 Internal error Transformation Module failed!
Translation 18:32:29 0:0-0:0 Internal error BackendDAETransform.reduceIndexDummyDer failed!
Translation 18:32:29 0:0-0:0 Internal error BackendDAETransform.selectDummyState: no state to select
Symbolic 18:32:29 10:3-10:13 Model is structurally singular, error found sorting equations 0.0 = 1.0;
for variables
The problem is that if you want the variable x to be continue between the sampling time istants you have to remove the discrete keyword, this will work fine:
class try
Real x(start = 1);
algorithm
when sample(0,4) then
reinit(x, 1);
end when;
equation
der(x) = 1;
end try;
Ciao,
Marco