less than or equal not equal less than or equal in modelica? - modelica

I tried a simple example with OpenModelica v1.20.0, but I find a confusing results.
The code is as follows:
model test
Boolean state1;
Boolean state2;
Real f;
equation
f = if time<1 then 0.5 else if time<3 then 0.4 else if time<5 then 0.3 else if time<7 then 0.4 else 0.5;
state1 = f<=0.4;
state2 = f<0.4 or f==0.4;
end test;
And the corresponding result is as follows:
Obviously, the result of state1(<=) is not equal state2(< or ==), and state1 is not a desired result.
Why?

There are some things to consider in Modelica:
The <, <=, >, and >= are not necessarily taken strictly but generate events. However, the state1 should ideally still be true from 1 to 7s where f<=0.4, and false otherwise; https://specification.modelica.org/maint/3.5/equations.html#events-and-synchronization So simulating that in Dymola generates
The == is not legal for reals outside of functions, so state2 cannot be defined like that https://specification.modelica.org/maint/3.5/operators-and-expressions.html#equality-relational-and-logical-operators
The reason I write ideally is that it is not always possible to guarantee that relations are handled literally at events. That was specified in Modelica up to version 3.2; but did not work in all cases in practice and has thus later been removed.

I suspect floating-point issues, although behaviour remains weird.
I fixed a translation warning about using == with Reals outside a function (you should see that too), but behaviour is the same as in your plot.
I also tried to make sure all comparisons use the same parameter 0.4, to avoid floating-point inaccuracies between the different 0.4 literals, and scaled f with a nominal attribute.
Even using <= for both state1 (in the equation section) and state2 (set by a function) does not produce the same plot for both state1 and state2.
model test
Boolean state1;
Boolean state2;
parameter Real threshold = 0.4;
Real f(nominal=0.5);
function compare
input Real a;
input Real b;
output Boolean res;
algorithm
res := a < b or a == b;
//res := a <= b; // Even this does not produce the same result as state1
end compare;
equation
f = if time<1 then 0.5 else if time<3 then threshold else if time<5 then 0.3 else if time<7 then threshold else 0.5;
state1 = f<=threshold;
state2 = compare(f, threshold);
annotation(
experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-06, Interval = 0.02));
end test;
Edit: this seems connected to event resolution, I opened a bug report about the behaviour of the <= operator at https://github.com/OpenModelica/OpenModelica/issues/9140
For now, you can work around it by using state1 = noEvent(f<=threshold);, instead. This seems to work correctly in OM, too.
Edit/Resolution: The issue has been clarified with a comment -- you should probably not write Modelica like that. A less synthetic example might help solve the underlying modelling issue you have.

Related

How can I generate complex events in modelica for when statement?

I tryed to use this code:
Real x,y;
Boolean trigger(start = true)
when x < y and trigger then
trigger = false;
end when;
I want to generate event for "when" only once. But my code doesn't work.
How can I generate complex events in modelica for when statement?
In Dymola you get the following error message:
The computational causality analysis requires the variables trigger
to be solved from the equation: when x < y and trigger then trigger =
false; end when;
however, the when condition also depends on the unknowns.
You may be able to cut the loop by putting 'pre' around these
references in the when condition.
Thus the solution would be:
Real x,y;
Boolean trigger(start = true) ;
equation
when x < y and pre(trigger) then
trigger = false;
end when;
As you see this is quite simple (and simulates in Dymola), but I haven't checked it in OpenModelica.
The problem you seem to be hitting is the first error message Internal error BackendDAETransform.analyseStrongComponentBlock failed (Sorry - Support for Discrete Equation Systems is not yet implemented). This seems to be https://trac.openmodelica.org/OpenModelica/ticket/1232, and I think it is caused by redefining part of your condition variable within the when statement.
You can work around this with reinit. See also the Bouncing ball example and the reference. It needs to act on a state variable, that's why I put the der(trigger) in there.
model test_when
Real trigger(start = 1.0, fixed = true);
equation
der(trigger) = 0;
when trigger > 0.5 and time > 5 then
reinit(trigger, 0);
end when;
annotation(
experiment(StartTime = 0, StopTime = 10, Tolerance = 1e-06, Interval = 0.02));
end test_when;
Probably there is a nicer way to achieve this. Anybody else got input on this?
You can check the compilation log (Statistics - events) to confirm that only one event was fired.

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.

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.

Implied Volatility in Matlab

I'm trying to calculate the implied volatility using the Black-Scholes formula in Matlab (2012b), but somehow have problems with some strike prices.
For instance blsimpv(1558,1440,0.0024,(1/12),116.4) will return NaN.
I thought it probably would be some problem with the function and therefore searched the internet for some other matlab scripts and customized it to my custom needs, but unfortunately I'm still not able to return a valid implied volatility.
function sigma=impvol(C,S,K,r,T)
%F=S*exp((r).*T);
%G=C.*exp(r.*T);
%alpha=log(F./K)./sqrt(T);
%beta=0.5*sqrt(T);
%a=beta.*(F+K);
%b=sqrt(2*pi)*(0.5*(F-K)-G);
%c=alpha.*(F-K);
%disc=max(0,b.^2-4*a.*c);
%sigma0=(-b+sqrt(disc))./(2*a);
i=-1000;
while i<=5000
sigma0=i/1000;
sigma=NewtonMethod(sigma0);
if sigma<=10 && sigma>=-10
fprintf('This is sigma %f',sigma)
end
i=i+1;
end
end
function s1=NewtonMethod(s0)
s1=s0;
count=0;
f=#(x) call(S,K,r,x,T)-C;
fprime=#(x) call_vega(S,K,r,x,T);
max_count=1e4;
while max(abs(f(s1)))>1e-7 && count<max_count
count=count+1;
s0=s1;
s1=s0-f(s0)./fprime(s0);
end
end
end
function d=d1(S,K,r,sigma,T)
d=(log(S./K)+(r+sigma.^2*0.5).*(T))./(sigma.*sqrt(T));
end
function d=d2(S,K,r,sigma,T)
d=(log(S./K)+(r-sigma.^2*0.5).*(T))./(sigma.*sqrt(T));
end
function p=Phi(x)
p=0.5*(1.+erf(x/sqrt(2)));
end
function p=PhiPrime(x)
p=exp(-0.5*x.^2)/sqrt(2*pi);
end
function c=call(S,K,r,sigma,T)
c=S.*Phi(d1(S,K,r,sigma,T))-K.*exp(-r.*(T)).*Phi(d2(S,K,r,sigma,T));
end
function v=call_vega(S,K,r,sigma,T)
v=S.*PhiPrime(d1(S,K,r,sigma,T)).*sqrt(T);
end
Running impvol(116.4,1558,1440,0.0024,(1/12)) will however unfortunately return the value 'Inf'. There somehow is a problem with the Newton-Rhapson method not converging but I am kind of clueless how to solve this. Does anyone know how to solve this problem or know some other way how to calculate the implied volatility?
Thank u in advance already for your help!
Kind regards,
Henk
I would definitely suggest this code: Fast Matrixwise Black-Scholes Implied Volatility
It is able to compute the entire surface in one shot and - my experience - I found it much more reliable than blsimpv() or impvol() which are other functions implemented in matlab.
Newton-Rhapson method does not work well for implied volatility. You should use the bisection method (not sure how it is used in Matlab). It is described in http://en.wikipedia.org/wiki/Bisection_method. For completeness, it works this way:
1) Pick an arbitrary high (impossible) volatility like high=200%/year.
2) Pick lowest possible volatility (low=0%).
2a) Calculate option premium for 0% volatility, if actual premium is lower than that, it means negative volatility (which is "impossible").
3) While implied volatility is not found:
3.1) If "high" and "low" are very near (e.g. equal up to 5th decimal), either one is your implied volatility. If not...
3.2) Calculate average between "high" and "low". avg=(high+low)/2
3.3) Calculate option premium for avg volatility.
3.4) If actual premium is higher then p(avg), make min=avg, because implied volatility must lie between avg and max.
3.4a) If actual premium is lower than p(avg), make max=avg, because implied vol must lie between min and avg.
The main disvantage of bisect is that you have to pick a maximum value, so your function won't find implied volatilities bigger than that. But something like 200%/year should be high enough for real-world usage.
I use yet another method more like Newton's method, hence not limited to a range, since vega is a derivative, but with a "linearization" fix to avoid hunting and failure due to small vegas:
def implied_volatility(type, premium, S, K, r, s_dummy, t):
if S <= 0.000001 or K <= 0.000001 or t <= 0.000001 or premium <= 0.000001:
return 0.0
s = 0.35
for cycle in range(0, 120):
ext_premium = type(S, K, r, s, t)
if abs(premium - ext_premium) < 0.005:
return s
ext_vega = type.vega(S, K, r, s, t)
# print S, K, r, s, t, premium, ext_premium, ext_vega
if ext_vega < 0.0000001:
# Avoids zero division if stuck
ext_vega = 0.0000001
s_new = s - (ext_premium - premium) / ext_vega
if abs(s_new - s) > 1.00:
# estimated s is too different from previous;
# it is better to go linearly, since
# vega is too small to give a good guess
if s_new > s:
s += 1.0
else:
s -= 1.0
else:
s = s_new
if s < 0.0:
# No volatility < 0%
s = 0.0001
if s > 99.99:
# No point calculating volatilities > 9999%/year
return 100.0
return 0.0
Still, I think that bisect is your best bet.
I created a simple function that conducts a sort of trial and error calculation if the output from blsimpv is NaN. This slows down the computation time significantly for me but it always gives me a desirable result.
The function is shown to be used below
BSIVC(t,i)= blsimpv(S(t,i),K,r,tau(t),HestonCiter(t,i))
if isnan(BSIVC(t,i));
BSIVC(t,i)= secondIVcalc(HestonCiter(t,i),S(t,i),K,r,q,tau(t))
end
The function itself is described below:
function IV= secondIVcalc(HestonC,S,K,r,q,T)
lowCdif = 1;
a=0;
while lowCdif>0.0001
a= a+0.00001
lowCdif = HestonC - BSCprice(S,K,r,q,a,T);
end
IV= a;
end
Please note that BSCprice is not an in-built function in matlab.
Just to make the code clearer-
BSCprice is of the format BSCprice(Underlying Asset Price, Strike Price, interest rate, dividend yield, implied vol, time to maturity).

testing value of iteration in matlab for loop fails?

I'm trying to test the value of an iteration variable in a for loop and I'm not getting what I expect so I'm assuming that I'm misunderstanding something about the way matlab works and/or I'm doing something horribly wrong....
Can someone explain why the if statement in this code doesn't test true when x hits 0.2?:
start = -1;
stop = 1;
interval = 0.01;
for x = start:interval:stop
if x == 0.20
disp('it worked')
end
end
But this code does test true:
start = 0;
stop = 1;
interval = 0.01;
for x = start:interval:stop
if x == 0.20
disp('it worked')
end
end
I tried a bunch of different starting values and they seem to be random as to whether they work or not....Why should changing the starting value change the output?
I also see a similar inconsistency if I change the tested value (ie. 0.2 to 0.8 or something else)
What am I missing?
You are testing a floating point number with ==. Note that -1+120*.01==0.2 is false since they are not equal in the floating point representation. 0.01*20==0.2 happens to be true. Instead, use a tolerance e.g. if abs(x-0.20)<1e-10.