Modelica - iterator with exception? - modelica

Slightly generalised example:
How do I make a for loop with exceptions when I define model equations?
The following works:
model Test
Real[9] q;
equation
q[2] = 1.2;
q[4] = 1.4;
for i in {1,3,5,6,7,8,9} loop
q[i] = 0;
end for;
end Test;
But I would rather like to write something like:
model Test
Real[9] q;
equation
q[2] = 1.2;
q[4] = 1.4;
for i in 1:9 and not in {2,4} loop
q[i] = 0;
end for;
end Test;
Is this possible?

This should be possible, as long as you make sure to have an equation for every unknown.
Probably not the perfect solution, but actually pretty readable:
model LoopException
Real[9] q;
equation
q[2] = 1.2;
q[4] = 1.4;
for i in 1:9 loop
if Modelica.Math.Vectors.find(i, {2, 4}) == 0 then
q[i] = 0;
end if;
end for;
end LoopException;
As an alternative, you could also try to write an "Array Constructor with Iterators" (Modelica Language Spec. Sec. 10.4.1), but that probably gets a bit messy...

With 2 helper functions you can easily emulate what you want:
model Test
Real[9] q;
equation
q[2] = 1.2;
q[4] = 1.4;
for i in allExcept(1:9, {2,4}) loop
q[i] = 0;
end for;
end Test;
Here are the functions you need for that:
function contains
"Check if vector v contains any element with value e"
input Integer vec[:];
input Integer e;
output Boolean result;
algorithm
result := false;
for v in vec loop
if v == e then
result := true;
break;
end if;
end for;
end contains;
function allExcept
"Return all elements of vector v which are not part of vector ex"
input Integer all[:];
input Integer ex[:];
output Integer vals[size(all, 1) - size(ex, 1)];
protected
Integer i=1;
algorithm
for v in all loop
if not contains(ex, v) then
vals[i] := v;
i := i + 1;
end if;
end for;
end allExcept;
Note that Modelica tools usually need to know the size of vectors during translation, especially here when you generate equations. Hence, the following line is required:
output Integer vals[size(all, 1) - size(ex, 1)];
It will fail when all or ex contain duplicate elements.
Therefore the model will not translate, if you try something like
for i in allExcept(1:3, {2, 2}) loop

Related

Continuous fraction approximation of pi

While studying for Matlab, I came up with this problem: given an integer 1<d<15 find the smallest p,q (positive integers) such that abs(p/q-pi)<=10^-d.
So here's my attempt: I first thought that I need to bound p,q in order to create loops, so I put as input data some M,Nupper bounds for p,q respectively. So here is my algorithm:
M=input('Give an upper bound for p:')
N=input('Give an upper bound for q:')
d=input('Give a positive integer between 1 and 15:')
for q=1:N
for p=1:M
if abs(pi-p/q)<=10^(-d)
break
end
end
end
What is wrong about it?
Thank you in advance.
The problem lies in the the way you chose to terminate the for loops: break only stops the inner loop. Try this code instead, and check the value of p and q at which the execution stops:
M=input('Give an upper bound for p:')
N=input('Give an upper bound for q:')
d=input('Give a positive integer between 1 and 15:')
for q=1:N
for p=1:M
if abs(pi-p/q)<=10^(-d)
[p,q]
error('Found!') % Arrest the program when condition is met
end
end
end
which gives the following output:
Of course, there are better ways of capturing all the possible pairs of integers that meet the condition (e.g. by using disp instead of error). That goes beyond the scope of this answer, but I shall provide a couple of examples here:
clear; clc;
M=input('Give an upper bound for p:')
N=input('Give an upper bound for q:')
d=input('Give a positive integer between 1 and 15:')
pairs = []; % p,q pairs are stored here
k = 1; % counter
for q=1:N
for p=1:M
if abs(pi-p/q)<=10^(-d)
pairs(k,1) = p;
pairs(k,2) = q;
k = k + 1; % increment the counter
end
end
end
The script above will end quietly: the (p,q) pairs will be stored in the pair matrix.
The following one will print directly the pairs:
clear; clc;
M=input('Give an upper bound for p:')
N=input('Give an upper bound for q:')
d=input('Give a positive integer between 1 and 15:')
for q=1:N
for p=1:M
if abs(pi-p/q)<=10^(-d)
out = sprintf("(%d, %d)", p,q);
disp(out);
end
end
end
For the sake of the experiment, and following up on #Cris Luengo's comment, here's a slightly more elaborate version of the script: the for loops are encapsulated in a dedicated function and a while loop keeps good track of the progress and populates the res matrix with the (p,q) pairs:
clear; clc;
M=input('Give an upper bound for p:');
N=input('Give an upper bound for q:');
d=input('Give a positive integer between 1 and 15:');
close_to_pi = #(px,qx) abs(pi-px/qx)<=10^(-d); % returns true/false
p = 1; q = 1;
count = 0;
res = nan(N*M,2) ; % (p,q) pairs are stored here; preallocate for speed!
tic % starts the timer
while (q <= N)
[p,q, found] = approx_pi(p, q, N, M, close_to_pi);
if found
count = count + 1;
res(count,:) = [p,q]; % populates output var
end
if p<M % we aren't done with p
p = p + 1;
else % reset p and increment q
p = 1;
q = q + 1;
end
end
res(isnan(res(:,1)),:) = []; % gets rid of the empty elements
disp(count)
toc % stops the timer and prints elapsed time
function [p, q, found] = approx_pi(p0, q0, N, M, fun)
for q=q0:N
for p=p0:M
if fun(p,q)
found = 1;
return
end % if
end % for p
p0 = 1;
end % for q
found = 0;
end % approx_pi
If you are interested in continuous fraction approximations of pi, try rat(pi, Tol) where Tol is the tolerance. Further details here.

Continuous variable used in clocked when clause is automatically discretized

model test
import Modelica.Constants.pi;
Real f;
discrete Real g;
Clock clk=Clock(0.1);
equation
f = sin(pi*time);
when Clock(0.1) then
if f >= 0 then
g = (sin(pi*time)) - 0.1;
else
g = (sin(pi*time)) + 0.1;
end if;
end when;
end test;
f is assigned as a continuous function. I want to sample the value of g depended on f, but f also be changed to a discrete value. Is there anything wrong ?
The clock partitioning sees f as being used directly inside the when Clock and thus f is also seen as a clocked variable.
Use sample(f) if that is not desired:
model test
import Modelica.Constants.pi;
Real f;
discrete Real g;
Clock clk=Clock(0.1);
equation
f = sin(pi*time);
when Clock(0.1) then
if sample(f) >= 0 then
g = (sin(pi*time)) - 0.1;
else
g = (sin(pi*time)) + 0.1;
end if;
end when;
end test;
See also: Failure to handle clock inference in Dymola

Brace indexing is not supported for variables of this type

When I run the code below I got this error Brace indexing is not supported for variables of this type.
function [R, Q] = rq_givens(A)
Q = { eye(size(A,2)) };
R = { A };
I =eye(size(A,1));
Qs={ };
k=1;
for i=1:size(A,2)
for j= size(A,1):-1:i+1
y= -A(j,i);
x= A(i,i);
alpha = atan(y/x);
c = cos(alpha);
s= sin(alpha);
temp = I;
temp(i,i)=c;
temp(i,j)=-s;
temp(j,i)=s;
temp(j,j)=c;
A = temp * A;
Qs{k} = temp;
k=k+1;
end
end
Q=I;
for i=k-1:-1:1
Q = Q*Qs{i};
end
Q= Q';
R= A;
end
It's an assignment that I am doing so all I can do is to change the function above,
The code to call the function is below and must stay the same.
A = randn(6,4);
[R,Q] = rq_givens(A)
for i = 1:length(R)
disp("Q orthonormal?")
Q{i}*Q{i}'
Q{i}'*Q{i}
disp("R upper triangular?")
R{i}
end
R{end}*Q{end} - A % Equal ?
Data types of R, Q are changed to numeric type from cell type inside function call.
At the second line, R is cell array, but the last line of function "rq_given" changes the type of R to numeric matrix.
Thus, R{i} is invalid. Similar issue can be seen with Q also.

Generating two random time depedant veariables with different sample periods

Following this question, I'm trying to generate two time-dependent random functions omega1 and tau using this example. The difference is that I need to have two different sample periods of 0.05 and 0.17 for omega1 and tau respectively. I just duplicated the parts I thought would do the job:
model testData
extends Modelica.Icons.Example;
import Modelica.Math.Random.Generators;
import Modelica.Math.Random.Utilities;
parameter Real k = 50.0;
parameter Real J = 0.001;
Real theta1;
Real theta2;
Real omega2;
parameter Modelica.SIunits.Period samplePeriod1 = 0.05;
parameter Integer globalSeed1 = 30020;
parameter Integer localSeed1 = 614657;
output Real omega1;
parameter Modelica.SIunits.Period samplePeriod2 = 0.17;
parameter Integer globalSeed2 = 30020;
parameter Integer localSeed2 = 614657;
output Real tau;
protected
discrete Integer state1024[33](each start=0, each fixed = true);
algorithm
when initial() then
state1024 := Generators.Xorshift1024star.initialState(localSeed1, globalSeed1);
omega1 := 0;
elsewhen sample(0, samplePeriod1) then
(omega1, state1024) := Generators.Xorshift1024star.random(pre(state1024));
omega1 := (omega1 - 0.5) * 13;
end when;
when initial() then
state1024 := Generators.Xorshift1024star.initialState(localSeed2, globalSeed2);
omega1 := 0;
elsewhen sample(0, samplePeriod2) then
(tau, state1024) := Generators.Xorshift1024star.random(pre(state1024));
tau := (tau - 0.5) * 3;
end when;
public
parameter Integer id1 = Utilities.initializeImpureRandom(globalSeed1);
discrete Real rImpure1;
Integer iImpure1;
parameter Integer id2 = Utilities.initializeImpureRandom(globalSeed2);
discrete Real rImpure2;
Integer iImpure2;
algorithm
when initial() then
rImpure1 := 0;
iImpure1 := 0;
elsewhen sample(0, samplePeriod1) then
rImpure1 := Utilities.impureRandom(id=id1);
iImpure1 := Utilities.impureRandomInteger(
id=id1,
imin=-1234,
imax=2345);
end when;
when initial() then
rImpure2 := 0;
iImpure2 := 0;
elsewhen sample(0, samplePeriod2) then
rImpure2 := Utilities.impureRandom(id=id2);
iImpure2 := Utilities.impureRandomInteger(
id=id2,
imin=-1234,
imax=2345);
end when;
initial equation
theta1 = 0;
theta2 = 0;
der(theta2) = 0;
equation
der(theta1) = omega1;
der(theta2) = omega2;
J * der(omega2) = tau + k * (theta1 - theta2);
annotation(experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-6, Interval = 0.02));
end testData;
however I get the error messages:
Symbolic Error
The given system is mixed-determined. [index > 3]
Please checkout the option "--maxMixedDeterminedIndex".
Translation Error
No system for the symbolic initialization was generated
I would appreciate if you could help me know what is the problem and how I can solve it.
P.S. considering that this code is apparantly compiling fine on Dymola, this could be a problem with OpenModelica. So I'm adding th JModelica tag in the case those guys can help me know if this compiles over there or not.
You have omega1 := 0; in two when initial()statements. Replace it by tau := 0; in the second one and the example will work.
I recommend to cleanup your code a bit. I found various smaller issues and needless code lines.
everything related to the impure random numbers can be removed
localSeed2 and globalSeed2 are useless when they are initialized like the other seed variables
state1024 is initialized at 3 different places (even though it works with OpenModelica): with start values and fixed=true and in two different when initial() statements
omega2 and tau2 don't need to be outputs. The Tool determines by itself what it has to compute.
And finally: Modelica models are a lot easier to debug and understand if existing blocks and physical components are used instead of writing lengthy code in a single class. Your model can also be built graphically with blocks from Modelica.Blocks.Noise and components from Modelica.Mechanics.Rotational.
Below is an updated version of your code with units, only one section for initialization and removed algorithm section (not necessary anymore due to the additional variables rand_omega and rand_tau).
model testData2
extends Modelica.Icons.Example;
import Modelica.Math.Random.Generators;
import Modelica.Math.Random.Utilities;
import SI = Modelica.SIunits;
parameter SI.RotationalSpringConstant k = 50.0;
parameter SI.Inertia J = 0.001;
parameter SI.Period samplePeriod_tau = 0.17;
parameter SI.Period samplePeriod_omega = 0.05;
parameter Integer globalSeed = 30020;
parameter Integer localSeed_tau = 614657;
parameter Integer localSeed_omega = 45613;
SI.Angle theta1, theta2;
SI.AngularVelocity omega1, omega2, rand_omega;
SI.Torque tau, rand_tau;
protected
discrete Integer state1024_tau[33];
discrete Integer state1024_omega[33];
initial equation
state1024_omega = Generators.Xorshift1024star.initialState(localSeed_omega, globalSeed);
state1024_tau = Generators.Xorshift1024star.initialState(localSeed_tau, globalSeed);
theta1 = 0;
theta2 = 0;
der(theta2) = 0;
equation
when sample(0, samplePeriod_omega) then
(rand_omega, state1024_omega) = Generators.Xorshift1024star.random(pre(state1024_omega));
end when;
when sample(0, samplePeriod_tau) then
(rand_tau, state1024_tau) = Generators.Xorshift1024star.random(pre(state1024_tau));
end when;
der(theta1) = omega1;
der(theta2) = omega2;
omega1 = (rand_omega - 0.5) * 13;
tau = (rand_tau - 0.5) * 3;
J * der(omega2) = 0 + k * (theta1 - theta2);
annotation(experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-6, Interval = 0.02));
end testData2;

Gauss-Seidel Method in MATLAB

I am trying to implement the Gauss-Seidel method in MATLAB. But there are two major mistakes in my code, and I could not fix them:
My code converges very well on small matrices, but it never converges on large matrices.
The code makes redundant iterations. How can I prevent from redundant iterations?
Gauss-Seidel Method on wikipedia.
N=5;
A=rand(N,N);
b=rand(N,1);
x = zeros(N,1);
sum = 0;
xold = x;
tic
for n_iter=1:1000
for i = 1:N
for j = 1:N
if (j ~= i)
sum = sum + (A(i,j)/A(i,i)) * xold(j);
else
continue;
end
end
x(i) = -sum + b(i)/A(i,i);
sum = 0;
end
if(abs(x(i)-xold(j))<0.001)
break;
end
xold = x;
end
gs_time=toc;
prompt1='Gauss-Seidel Method Time';
prompt2='x Matrix';
disp(prompt2);
disp(x);
disp(prompt1);
disp(gs_time);
First off, a generality. The Gauß-Seidel and Jacobi methods only apply to diagonally dominant matrices, not generic random ones. So to get correct test examples, you need to actually constructively ensure that condition, for instance via
A = rand(N,N)+N*eye(N)
or similar.
Else the method will diverge towards infinity in some or all components.
Now to some other strangeness in your implementation. What does
if(abs(x(i)-xold(j))<0.001)
mean? Note that this instruction is outside the loops where i and j are the iteration variables, so potentially, the index values are undefined. By inertia they will accidentally both have the value N, so this criterion makes at least a little sense.
What you want to test is some norm of the difference of the vectors as a whole, thus using sum(abs(x-xold))/N or max(abs(x-xold)). On the right side you might want to multiply with the same norm construction applied to x so that the test is for the relative error, taking the scale of the problem into account.
By the instructions in the given code, you are implementing the Jacobi iteration, computing all the updates first and then advancing the iteration vector. For the Gauß-Seidel variant you would need to replace the single components in-place, so that newly computed values are immediately used.
Also, you could shorten/simplify the inner loop
xold = x;
for i = 1:N
sum = b(i);
for j = 1:N
if (j ~= i)
sum = sum - A(i,j) * x(j);
end
end
x(i) = sum/A(i,i);
end
err = norm(x-xold)
or even shorter using the language features of matlab
xold = x
for i = 1:N
J = [1:(i-1) (i+1):N];
x(i) = ( b(i) - A(i,J)*x(J) )/A(i,i);
end
err = norm(x-xold)
%Gauss-seidal method for three equations
clc;
x1=0;
x2=0;
x3=0;
m=input('Enter number of iteration');
for i=1:1:m
x1(i+1)=(-0.01-0.52*x2(i)-x3(i))/0.3
x2(i+1)=0.67-1.9*x3(i)-0.5*x1(i+1)
x3(i+1)=(0.44-0.1*x1(i+1)-0.3*x2(i+1))/0.5
er1=abs((x1(i+1)-x1(i))/x1(i+1))*100
er2=abs((x2(i+1)-x2(i))/x2(i+1))*100
er3=abs((x3(i+1)-x3(i))/x3(i+1))*100
if er1<=0.01
er2<=0.01
er3<=0.01
break;
end
end