I'm trying to implement a DFT on my own because the FFT example in OpenModelica is not working for me and I can't figure out why.
But I'm already stuck sampling a sine function and assigning the sampled values to a buffer array.
This is why I tried to make it even simpler and just assign a counter variable "iTick" to the array which still doesn't work.
See the basic example.
Can anyone tell me why this is not working and how I can actually assign a value to the array when using the sample() function ??
block DFT
import Modelica.Constants.pi;
parameter Integer N = 360 "Total number of samples";
Integer iTick;
Real y_buf[N];
algorithm
when sample(0, 0.1) then
iTick :=iTick + 1;
if iTick >= 1 and iTick <= N then
y_buf[iTick] := iTick;
end if;
end when;
end DFT;
[358] 14:56:15 Symbolisch Warnung
The linear system:
1 : $PRE.y_buf[2] = y_buf[2]
2 : y_buf[2] = $PRE.y_buf[2]
[
-1.0 , 1.0 ;
1.0 , -1.0
]
*
[
y_buf[2] ;
$PRE.y_buf[2]
]
=
[
0.0 ;
0.0
]
might be structurally or numerically singular for variable $PRE.y_buf[2] since U(2,2) = 0.0. It might be hard to solve. Compilation continues anyway.
[359] 14:56:15 Symbolisch Warnung
The linear system:
1 : $PRE.y_buf[1] = y_buf[1]
2 : y_buf[1] = $PRE.y_buf[1]
[
-1.0 , 1.0 ;
1.0 , -1.0
]
*
[
y_buf[1] ;
$PRE.y_buf[1]
]
=
[
0.0 ;
0.0
]
might be structurally or numerically singular for variable $PRE.y_buf[1] since U(2,2) = 0.0. It might be hard to solve. Compilation continues anyway.
[360] 14:56:15 Übersetzung Warnung
Assuming fixed start value for the following 2 variables:
y_buf[360]:DISCRETE(fixed = false ) type: Real [360]
iTick:DISCRETE(fixed = false ) type: Integer
After a long search and try and error I found out that the magic word "discrete" solves my problem! I can't yet explain why, but see below the working example:
model Test
import Modelica.Constants.pi;
parameter Integer N = 360 "Total number of samples";
Integer iTick(start=0, fixed=true);
discrete Real y_buf[N](start=fill(0,N), fixed=fill(true, N));
algorithm
when sample(0, 0.1) then
iTick :=iTick + 1;
if iTick >= 1 and iTick <= N then
y_buf[iTick] := iTick;
end if;
end when;
end Test;
Hopefully this is a help to someone!
Your "Symbolisch Warnung" disappears once you initialize iTick and y_buf. However, the code still does not work. OpenModelica simulates it, but the items of y_buf are never updated.
This issue might be related to this question where the delay operator is not working in algorithm sections. Therefore, I suggest a similar workaround: Try to avoid the algorithm section. With an equation section and proper initialization, your minimal example could look as follows:
block DFT
import Modelica.Constants.pi;
parameter Integer N = 360 "Total number of samples";
Integer iTick(start=0, fixed=true);
Real y_buf[N](start=fill(0, N), fixed=fill(true, N));
equation
when sample(0, 0.1) then
iTick = pre(iTick) + 1;
end when;
for i in 1:N loop
when iTick >= i then
y_buf[i] = iTick;
end when;
end for;
end DFT;
Related
I'm trying to simulate Coulomb friction in Modelica. The basic concept is to check if relative velocity speed between to surfaces is less than a constant and the external force which tried to slid surfaces against each other is less than maximum static friction force (normalForce * staticFrictionCoefficient) then the friction force is equal to negative of the external shear force. otherwise, the friction force is equal to the kinetic friction force (normalForce * kineticFrictionCoefficient)in the opposite direction of the sliding direction.
I implemented this concept in Modelica as below:
function coulombFriction
input Real relVel;
input Real shearForce;
input Real normalForce;
input Real statfricco;
input Real kinfricco;
output Real fricForce;
algorithm
if relVel==0 and abs(shearForce)<statfricco*normalForce then
fricForce:=shearForce;
else
fricForce:=kinfricco*normalForce*sign(relVel);
end if;
end coulombFriction;
but when I call this function from a model as below:
model fricexample_1
extends coulombFriction;
//parameters
parameter Real kco=0.3;
parameter Real sco=0.4;
parameter Real nfo=1.0;
Real sfo;
Real ffo;
Real x;
Real v;
initial equation
x=0;
v=0;
equation
v=der(x);
der(v)=sfo-ffo;
sfo=time;
ffo=coulombFriction(relVel=v, shearForce=sfo, normalForce=nfo, statfricco=sco, kinfricco=kco);
end fricexample_1;
I see the error:
Translation Error
post-optimization module findZeroCrossings (simulation) failed.
If I remove the abs function from the defined function, it solves the compiling problem, but the model is wrong! I would appreciate if you could help me know:
how can I solve this problem?
how to model friction otherwise?
You can use noEvent on the conditions that might generate events in the function. Note that you don't need to extend the model with the function.
It should actually not work (to extend a model from a function), but it seems we don't check for it.
The model that compiles for me is below:
package Friction
function coulombFriction
input Real relVel;
input Real shearForce;
input Real normalForce;
input Real statfricco;
input Real kinfricco;
output Real fricForce;
algorithm
if noEvent(relVel==0) and noEvent(abs(shearForce)<statfricco*normalForce) then
fricForce:=shearForce;
else
fricForce:=kinfricco*normalForce*sign(relVel);
end if;
end coulombFriction;
model fricexample_1
//parameters
parameter Real kco=0.3;
parameter Real sco=0.4;
parameter Real nfo=1.0;
Real sfo;
Real ffo;
Real x;
Real v;
initial equation
x = 0;
v = 0;
equation
v = der(x);
der(v) = sfo-ffo;
sfo = time;
ffo = coulombFriction(relVel=v, shearForce=sfo, normalForce=nfo, statfricco=sco, kinfricco=kco);
end fricexample_1;
end Friction;
Your model does work with the 1.11 release. The issue is the extends coulombFriction; statement. Once you removed it, it should work fine even without the noEvent calls:
package Friction
function coulombFriction
input Real relVel;
input Real shearForce;
input Real normalForce;
input Real statfricco;
input Real kinfricco;
output Real fricForce;
algorithm
if relVel==0 and abs(shearForce)<statfricco*normalForce then
fricForce:=shearForce;
else
fricForce:=kinfricco*normalForce*sign(relVel);
end if;
end coulombFriction;
model fricexample_1
parameter Real kco=0.3;
parameter Real sco=0.4;
parameter Real nfo=1.0;
Real sfo;
Real ffo;
Real x;
Real v;
initial equation
x = 0;
v = 0;
equation
v = der(x);
der(v) = sfo-ffo;
sfo = time;
ffo = coulombFriction(relVel=v, shearForce=sfo, normalForce=nfo, statfricco=sco, kinfricco=kco);
end fricexample_1;
end Friction;
I'd recommend to reuse the friction state machine available in the Modelica Standard Library. An example, that works in OpenModelica and other tools, is given by https://github.com/dzimmer/ZimmersModelicaTutorial/blob/master/Tutorial2015/BaseComponents/Friction/IdealDryFriction.mo.
Actually, the model I have developed above for Columb friction is wrong. Thanks to this post I could find the correct version:
package friction1D
final constant Real eps=1.e-15 "Biggest number such that 1.0 + eps = 1.0";
function sgn
input Real inputVar;
output Real outputVar;
algorithm
if noEvent(inputVar < 0) then
outputVar := -1;
else
outputVar := 1;
end if;
end sgn;
function coulombFriction
input Real relVel;
input Real shearForce;
input Real normalForce;
input Real statfricco;
input Real kinfricco;
output Real fricForce;
algorithm
if noEvent(abs(relVel) < eps) and noEvent(abs(shearForce) < statfricco * normalForce) then
fricForce := shearForce;
else
fricForce := kinfricco * normalForce * sgn(relVel);
end if;
end coulombFriction;
model fricexample_1
//parameters
parameter Real kco = 0.3;
parameter Real sco = 0.4;
parameter Real nfo = 1.0;
parameter Real mass = 1.0;
Real sfo;
Real ffo;
Real x;
Real v;
initial equation
x = 0;
v = 0;
algorithm
sfo := 0.7 * sin(time);
ffo := coulombFriction(relVel = v, shearForce = sfo, normalForce = nfo, statfricco = sco, kinfricco = kco);
equation
v = der(x);
mass * der(v) = sfo - ffo;
annotation(
experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-8, Interval = 0.02),
__OpenModelica_simulationFlags(lv = "LOG_STATS", outputFormat = "mat", s = "dassl"));
end fricexample_1;
end friction1D;
This Question is in continuation to a previous one asked Matlab : Plot of entropy vs digitized code length
I want to calculate the entropy of a random variable that is discretized version (0/1) of a continuous random variable x. The random variable denotes the state of a nonlinear dynamical system called as the Tent Map. Iterations of the Tent Map yields a time series of length N.
The code should exit as soon as the entropy of the discretized time series becomes equal to the entropy of the dynamical system. It is known theoretically that the entropy of the system is log_2(2). The code exits but the frst 3 values of the entropy array are erroneous - entropy(1) = 1, entropy(2) = NaN and entropy(3) = NaN. I am scratching my head as to why this is happening and how I can get rid of it. Please help in correcting the code. THank you.
clear all
H = log(2)
threshold = 0.5;
x(1) = rand;
lambda(1) = 1;
entropy(1,1) = 1;
j=2;
tol=0.01;
while(~(abs(lambda-H)<tol))
if x(j - 1) < 0.5
x(j) = 2 * x(j - 1);
else
x(j) = 2 * (1 - x(j - 1));
end
s = (x>=threshold);
p_1 = sum(s==1)/length(s);
p_0 = sum(s==0)/length(s);
entropy(:,j) = -p_1*log2(p_1)-(1-p_1)*log2(1-p_1);
lambda = entropy(:,j);
j = j+1;
end
plot( entropy )
It looks like one of your probabilities is zero. In that case, you'd be trying to calculate 0*log(0) = 0*-Inf = NaN. The entropy should be zero in this case, so you you can just check for this condition explicitly.
Couple side notes: It looks like you're declaring H=log(2), but your post says the entropy is log_2(2). p_0 is always 1 - p_1, so you don't have to count everything up again. Growing the arrays dynamically is inefficient because matlab has to re-copy the entire contents at each step. You can speed things up by pre-allocating them (only worth it if you're going to be running for many timesteps).
I wanna find a root for the following function with an error less than 0.05%
f= 3*x*tan(x)=1
In the MatLab i've wrote that code to do so:
clc,close all
syms x;
x0 = 3.5
f= 3*x*tan(x)-1;
df = diff(f,x);
while (1)
x1 = 1 / 3*tan(x0)
%DIRV.. z= tan(x0)^2/3 + 1/3
er = (abs((x1 - x0)/x1))*100
if ( er <= 0.05)
break;
end
x0 = x1;
pause(1)
end
But It keeps running an infinite loop with error 200.00 I dunno why.
Don't use while true, as that's usually uncalled for and prone to getting stuck in infinite loops, like here. Simply set a limit on the while instead:
while er > 0.05
%//your code
end
Additionally, to prevent getting stuck in an infinite loop you can use an iteration counter and set a maximum number of iterations:
ItCount = 0;
MaxIt = 1e5; %// maximum 10,000 iterations
while er > 0.05 & ItCount<MaxIt
%//your code
ItCount=ItCount+1;
end
I see four points of discussion that I'll address separately:
Why does the error seemingly saturate at 200.0 and the loop continue infinitely?
The fixed-point iterator, as written in your code, is finding the root of f(x) = x - tan(x)/3; in other words, find a value of x at which the graphs of x and tan(x)/3 cross. The only point where this is true is 0. And, if you look at the value of the iterants, the value of x1 is approaching 0. Good.
The bad news is that you are also dividing by that value converging toward 0. While the value of x1 remains finite, in a floating point arithmetic sense, the division works but may become inaccurate, and er actually goes NaN after enough iterations because x1 underflowed below the smallest denormalized number in the IEEE-754 standard.
Why is er 200 before then? It is approximately 200 because the value of x1 is approximately 1/3 of the value of x0 since tan(x)/3 locally behaves as x/3 a la its Taylor Expansion about 0. And abs(1 - 3)*100 == 200.
Divisions-by-zero and relative orders-of-magnitude are why it is sometimes best to look at the absolute and relative error measures for both the values of the independent variable and function value. If need be, even putting an extremely (relatively) small finite, constant value in the denominator of the relative calculation isn't entirely a bad thing in my mind (I remember seeing it in some numerical recipe books), but that's just a band-aid for robustness's sake that typically hides a more serious error.
This convergence is far different compared to the Newton-Raphson iterations because it has absolutely no knowledge of slope and the fixed-point iteration will converge to wherever the fixed-point is (forgive the minor tautology), assuming it does converge. Unfortunately, if I remember correctly, fixed-point convergence is only guaranteed if the function is continuous in some measure, and tan(x) is not; therefore, convergence is not guaranteed since those pesky poles get in the way.
The function it appears you want to find the root of is f(x) = 3*x*tan(x)-1. A fixed-point iterator of that function would be x = 1/(3*tan(x)) or x = 1/3*cot(x), which is looking for the intersection of 3*tan(x) and 1/x. However, due to point number (2), those iterators still behave badly since they are discontinuous.
A slightly different iterator x = atan(1/(3*x)) should behave a lot better since small values of x will produce a finite value because atan(x) is continuous along the whole real line. The only drawback is that the domain of x is limited to the interval (-pi/2,pi/2), but if it converges, I think the restriction is worth it.
Lastly, for any similar future coding endeavors, I do highly recommend #Adriaan's advice. If would like a sort of compromise between the styles, most of my iterative functions are written with a semantic variable notDone like this:
iter = 0;
iterMax = 1E4;
tol = 0.05;
notDone = 0.05 < er & iter < iterMax;
while notDone
%//your code
iter = iter + 1;
notDone = 0.05 < er & iter < iterMax;
end
You can add flags and all that jazz, but that format is what I frequently use.
I believe that the code below achieves what you are after using Newton's method for the convergence. Please leave a comment if I have missed something.
% find x: 3*x*tan(x) = 1
f = #(x) 3*x*tan(x)-1;
dfdx = #(x) 3*tan(x)+3*x*sec(x)^2;
tolerance = 0.05; % your value?
perturbation = 1e-2;
converged = 1;
x = 3.5;
f_x = f(x);
% Use Newton s method to find the root
count = 0;
err = 10*tolerance; % something bigger than tolerance to start
while (err >= tolerance)
count = count + 1;
if (count > 1e3)
converged = 0;
disp('Did not converge.');
break;
end
x0 = x;
dfdx_x = dfdx(x);
if (dfdx_x ~= 0)
% Avoid division by zero
f_x = f(x);
x = x - f_x/dfdx_x;
else
% Perturb x and go back to top of while loop
x = x + perturbation;
continue;
end
err = (abs((x - x0)/x))*100;
end
if (converged)
disp(['Converged to ' num2str(x,'%10.8e') ' in ' num2str(count) ...
' iterations.']);
end
I am creating a Max Per Interval block in Wolfram System Modeler.
To make it easy for me to explain, I just set the Max value to 10.
block HighWaterMarkPerInterval
extends Modelica.Blocks.Interfaces.SISO;
protected
Integer index;
Real currentMax;
Real endTimes[1, 45] = [30812532.2, 32037805, 33265581.8, 34493233.8, 35720861.5, 36948483, 38176307.7, 39426940.6, 40654485.4, 41882212.1, 43109672.7, 44337076, 45564265.7, 46793039.6, 48045130.9, 50749960.3, 52040090.6, 53558507.7, 54814537.3, 56331978.2, 57587753.3, 59105952.9, 60362517.8, 61879307.8, 63136031.5, 64363411.4, 65590464.3, 67738027.40000001, 84725789.8, 87831338.09999999, 89030965.40000001, 90258821.8, 91486663.5, 92714210.3, 93941727.7, 95166770.3, 97283519, 99434222.90000001, 100658067.1, 102807019, 104030032.7, 106179193, 107402090, 109550214.2, 110771545.3];
algorithm
if endTimes[1, index] < time then
index := pre(index) + 1;
currentMax := 0;
else
currentMax := 10; // Constant to until I get logic working
end if;
initial algorithm
index := 0;
equation
y = currentMax;
end HighWaterMarkPerInterval;
When ran, index increments to infinity right off the bat. I figure there is something wrong with my logic, but I can't figure it.
The code is supposed to check to see if we are still in the interval time, and when we cross over into the next interval time it sets the "currentMax" value to zero. Which will reset the Max value I've implemented in another block.
Any help would be appreciated. Thanks.
EDIT: Code section form example.
model HighWaterMarkPerInterval
annotation(Diagram(coordinateSystem(extent = {{-148.5, -105}, {148.5, 105}}, preserveAspectRatio = true, initialScale = 0.1, grid = {5, 5})));
extends Modelica.Blocks.Interfaces.SISO;
Modelica.Blocks.Math.Max maxblock(u1 = currentMax, u2 = u);
Real flybyEnds[1, 45] = [30813151,32038322,33266015, truncated for space saving...];
Integer index;
Real currentMax;
initial equation
index = 1;
currentMax = 0;
algorithm
// When we are in the interval continually grab max block output and output currentMax
when {time>=flybyEnds[1, index-1], time <=flybyEnds[1,index]} then
currentMax := pre(maxblock.y);
y := currentMax;
end when;
// When we move to the next interval reset current max and move to the next interval
when time > flybyEnds[1, index] then
currentMax := 0;
index := pre(index) + 1;
end when;
end HighWaterMarkPerInterval;
You need to use when, not if. You can find a discussion about both and the differences between them in Modelica by Example.
This issue has also been discussed on SO both here and here.
Here is an example (completely untested, but it shows the basic idea):
model HighWaterMarkPerInterval
extends Modelica.Blocks.Interfaces.SISO;
parameter Modelica.SIunits.Time sample_rate=3600;
Real flybyEnds[45] = {30813151,32038322,33266015,...};
Integer index;
Real currentMax;
initial algorithm
// Specify the first time we are interested in...
index := 1;
algorithm
// At the start of the simulation, the initial max for the current
// interval [0,30813151] is whatever u is. The initial output value
// is also the initial value for u
when initial() then
currentMax := u
y := u;
end when;
// Check at some sample rate (faster than the flyby interval!)
// if u > currentMax...
when sample(sample_rate, sample_rate) then
// New currentMax is the larger of either currentMax or u
// when the sample took place
currentMax := max(pre(currentMax), pre(u));
end when;
// At the end of the "flyby", record the maximum found since
// the last flyby and specify the next flyby index.
when time>=flybyEnd[index] then
// New output is the value of currentMax from this interval
y := pre(currentMax);
// Now reset currentMax
currentMax := pre(u);
// Increment index up to the length of flybyEnd
index := min(pre(index)+1, size(flybyEnd,1));
end when;
end HighWaterMarkPerInterval;
I try to compile the following code with Dymola:
class abc
import Modelica.SIunits;
parameter SIunits.Time delta_t=0.5;
constant Real a[:]={4,2,6,-1,3,5,7,4,-3,-6};
Real x;
Integer j(start=1);
Integer k=size(a, 1);
algorithm
when {(sample(0, delta_t) and j < k),j == 1} then
x := a[j];
j := j + 1;
end when;
end abc;
and for time = 0 the variable j starts with 2. But it should start with j = 1.
Does anybody have an idea for this problem?
Keep in mind that sample(x,y) means that sample is true at x+i*y where i starts at zero. Which is to say that sample(0, ...) becomes true at time=0.
Since j starts at 1 and k is presumably more than 1, it doesn't seem unexpected to me that sample(0, delta_t) and j<k should become true at the start of the simulation.
I suspect what you want is:
class abc
import Modelica.SIunits;
parameter SIunits.Time delta_t=0.5;
constant Real a[:]={4,2,6,-1,3,5,7,4,-3,-6};
Real x;
Integer j(start=1);
Integer k=size(a, 1);
algorithm
when {(sample(delta_t, delta_t) and j < k),j == 1} then
x := a[pre(j)];
j := pre(j) + 1;
end when;
end abc;
I don't really see the point of the j==1 condition. It is true at the outset which means it doesn't "become" true then. And since j is never decremented, I don't see why it should ever return to the value 1 once it increments for the first time.
Note that I added a pre around the right-hand side values for j. If this were in an
equation section, I'm pretty sure the pre would be required. Since it is an algorithm section, it is mainly to document the intent of the code. It also makes the code robust to switching from equation to algorithm section.
Of course, there is an event at time = 0 triggered by the expression sample(0, delta_t) and j<k which becomes true.
But in older versions of Dymola there is an bug with the initialization of discrete variables. For instance even if you remove sample(0.0, delta_t) and j<k in dymola74, j will become 2 at time=0. The issue was that the pre values of when clauses, where not initialized correct. As far as I know this is corrected at least in the version FD1 2013.