Within a subroutine I try to create a statement, however it will only work if I enter a number directly, as soon as I replace the number with a variable, it will give the error:
||Error: IF clause requires a scalar LOGICAL expression|
In this example var is a real number between 0 and 1.
if ( var%type3 < 0.5) then
test = 1
end if
where the type3 component is declared as
real, dimension(1,1) :: type3
Does someone maybe know what we are doing wrong. Because the error does not give us any clues which part of the statement is wrong.
You try to use a DIMENSION(1, 1) type as a REAL.
You shoul add (1, 1) to access to yout REAL contained in the DIMENSION(1, 1)
Use :
IF ( var%type3(1, 1) < 0.5 ) THEN
print *, 'IT WORKS'
END IF
Exemple to get this error :
MODULE vardef
TYPE vartype
REAL :: type3(1, 1)
END TYPE vartype
END MODULE vardef
PROGRAM test
USE vardef
TYPE(vartype) var
var%type3(1, 1) = 0
IF ( var%type3 < 0.5 ) THEN
print *, 'IT WORKS'
END IF
RETURN
END PROGRAM test
From your comment var%type3 is a real, dimension(1,1). This isn't scalar, and var%type3 < 0.5 will be an array of the same shape.
As the error message states, the test condition for the if should be scalar logical. Depending on what you want to do your test condition can be one of a non-exhaustive list:
var%type3(1,1) < 0.5
ALL(var%type3 < 0.5)
ANY(var%type3 < 0.5)
The first case seems natural as it is a scalar condition, but I leave the others as you may well be expanding to cases where it isn't a (1,1) array.
Related
I need to check if the imaginary part is very small and set it to zero if it is in order to eliminate some floating point errors that result in very small non-zero imaginary parts when it should be zero.
My code is as follows:
kz2 = SQRT((n2*(2.0*PI*eta))**2 - kxarray(p)**2)
kz1 = SQRT((n1*(2.0*PI*eta))**2 - kxarray(p)**2)
if (aimag(kz2) < 0.0005) then
kz2 = (REAL(kz2),0.0)
end if
if (aimag(kz1) < 0.0005) then
kz1 = (REAL(kz1), 0.0)
end if
Unfortunately the compiler just returns:
gaussian1.f90:122.18:
kz2 = (REAL(kz2),0.0)
1
Error: Expected a right parenthesis in expression at (1)
gaussian1.f90:126.18:
kz1 = (REAL(kz1), 0.0)
1
Error: Expected a right parenthesis in expression at (1)
Any advice would be greatly appreciated - am I even going about this problem the right way?
UPDATE: I managed to avoid the problem by using:
if (aimag(kz2) < 0.0005) then
kz2 = real(kz2)
end if
if (aimag(kz1) < 0.0005) then
kz1 = real(kz1)
end if
But what would I do if I wanted to set the imaginary part to a non-zero amount?
In Fortran 2008 there are even more possibilities. You can access real and imaginary parts as derived type components, e.g.
a = c%re
b%im = 5
So, to set imaginary part of z to zero in new compilers you can try z%im = 0 .
I think you are looking for the CMPLX function, which converts real or integer arguments to a complex number. So it you example you should be able to do something like this:
kz1 = cmplx(real(kz1), 0.)
The (1.0,1.0) style parenthesis notation you have tried is only valid for constant values, not forming a complex number from the values held in variables.
I am having problems with a do while implementation for a sine taylor series. Editing the do loop to do bb = 1, 10 , 2 gives an expected result well within the margin of error, however when running the desired implementation of the do loop (do while(abs(sineseries) - accuracy > 0), will always give an answer equal to 1. So I have narrowed the possibilities down to the do while loop implementation being faulty.
program taylor
implicit none
real :: x
real :: sineseries, nfactsine
real, parameter :: accuracy = 1.e-10
integer :: signum, bb
nfactsine = 1
signum = 1
write(*,*) "Write your input value"
read(*,*) x
sineseries = 0
do while(abs(sineseries) - accuracy > 0)
sineseries = sineseries + (signum*x**bb)/nfactsine
nfactsine = nfactsine*(bb+1)*(bb+2)
signum = -signum
end do
write(*,*) sineseries, sin(x)
end program taylor
The two types of loops are not doing the same thing.
In the loop
do bb=1, 10, 2
...
end do
you have loop control with variable bb. This variable takes the values 1, 3, ..., 9 at iterations as the loop proceeds.
The do while does not have this control: you must replicate the increment of bb manually:
bb=1
do while (...)
...
bb=bb+2
end do
As Pierre de Buyl commented, you also have an error in the termination condition for the indefinite iteration count. The condition initially evaluates as false, so the loop body isn't executed even once.
I am using a computational fluid dynamics software that is compiled with gfortran version 4.8.5 on Ubuntu 16.04 LTS. The software can be compiled with either single precision or double precision and the -O3 optimization option. As I do not have the necessary computational resources to run the CFD software on double precision I am compiling it with single precision and the following options
ffpe-trap=invalid,zero,overflow
I am getting a SIGFPE error on a line of code that contains the asin function-
INTEGER, PARAMETER :: sp = SELECTED_REAL_KIND( 6, 37) !< single precision
INTEGER, PARAMETER :: wp = sp
REAL(KIND=wp) zsm(:,:)
ela(i,j) = ASIN(zsm(ip,jp))
In other words the inverse sin function and this code is part of a doubly nested FOR loop with jp and ip as the indices. Currently the software staff is unable to help me for various other reasons and so I am trying to debug this on my own. The SIGFPE error is only being observed in the single precision compilation not double precision compilation.
I have inserted the following print statements in my code prior to the line of code that is failing i.e. the asin function call. Would this help me with unraveling the problem that I am facing ? This piece of code is executed for every time step and it is occurring after a series of time steps. Alternatively what other steps can I do to help me fix this problem ? Would adding "precision" to the compiler flag help ?
if (zsm(ip,jp) >= 1.0 .or. zsm(ip,jp) <= -1.0) then
print *,zsm(ip,jp),ip,jp
end if
EDIT
I took a look at this answer Unexpected behavior of asin in R and I am wondering whether I could do something similar in fortran i.e. by using the max function. If it goes below -1 or greater than 1 then round it off in the proper manner. How can I do it with gfortran using the max function ?
On my desktop the following program executes with no problems(i.e. it has the ability to handle signed zeros properly) and so I am guessing the SIGFPE error occurs with either the argument greater than 1 or less than -1.
program testa
real a,x
x = -0.0000
a = asin(x)
print *,a
end program testa
We have min and max functions in Fortran, so I think we can use the same method as in the linked page, i.e., asin( max(-1.0,min(1.0,x) ). I have tried the following test with gfortran-4.8 & 7.1:
program main
implicit none
integer, parameter :: sp = selected_real_kind( 6, 37 )
integer, parameter :: wp = sp
! integer, parameter :: wp = kind( 0.0 )
! integer, parameter :: wp = kind( 0.0d0 )
real(wp) :: x, a
print *, "Input x"
read(*,*) x
print *, "x =", x
print *, "equal to 1 ? :", x == 1.0_wp
print *, asin( x )
print *, asin( max( -1.0_wp, min( 1.0_wp, x ) ) )
end
which gives with wp = sp (or wp = kind(0.0) on my computer)
$ ./a.out
Input x
1.00000001
x = 1.00000000
equal to 1 ? : T
1.57079625 (<- 1.5707964 for gfortran-4.8)
1.57079625
$ ./a.out
Input x
1.0000001
x = 1.00000012
equal to 1 ? : F
NaN
1.57079625
and with wp = kind(0.0d0)
$ ./a.out
Input x
1.0000000000000001
x = 1.0000000000000000
equal to 1 ? : T
1.5707963267948966
1.5707963267948966
$ ./a.out
Input x
1.000000000000001
x = 1.0000000000000011
equal to 1 ? : F
NaN
1.5707963267948966
If it is necessary to modify a lot of asin(x) and the program relies on a C or Fortran preprocessor, it may be convenient to define some macro like
#define clamp(x) max(-1.0_wp,min(1.0_wp,x))
and use it as asin( clamp(x) ). If we want to remove such a modification, we can simply change the definition of clamp() as #define clamp(x) (x). Another approach may be to define some asin2(x) function that limits x to [-1,1] and replace the built-in asin by asin2 (either as a macro or a Fortran function).
Within a subroutine I try to create a statement, however it will only work if I enter a number directly, as soon as I replace the number with a variable, it will give the error:
||Error: IF clause requires a scalar LOGICAL expression|
In this example var is a real number between 0 and 1.
if ( var%type3 < 0.5) then
test = 1
end if
where the type3 component is declared as
real, dimension(1,1) :: type3
Does someone maybe know what we are doing wrong. Because the error does not give us any clues which part of the statement is wrong.
You try to use a DIMENSION(1, 1) type as a REAL.
You shoul add (1, 1) to access to yout REAL contained in the DIMENSION(1, 1)
Use :
IF ( var%type3(1, 1) < 0.5 ) THEN
print *, 'IT WORKS'
END IF
Exemple to get this error :
MODULE vardef
TYPE vartype
REAL :: type3(1, 1)
END TYPE vartype
END MODULE vardef
PROGRAM test
USE vardef
TYPE(vartype) var
var%type3(1, 1) = 0
IF ( var%type3 < 0.5 ) THEN
print *, 'IT WORKS'
END IF
RETURN
END PROGRAM test
From your comment var%type3 is a real, dimension(1,1). This isn't scalar, and var%type3 < 0.5 will be an array of the same shape.
As the error message states, the test condition for the if should be scalar logical. Depending on what you want to do your test condition can be one of a non-exhaustive list:
var%type3(1,1) < 0.5
ALL(var%type3 < 0.5)
ANY(var%type3 < 0.5)
The first case seems natural as it is a scalar condition, but I leave the others as you may well be expanding to cases where it isn't a (1,1) array.
Hey,
So I have a maple program which does bisection method and I have to convert it to C++. I tried converting it according to what the code generation help on the maple forums said but it kept throwing out errors. I would appreciate some help in this.
Thanks,
Here is the code for maple
Use the bisection method to solve the following mathematical problem:
a. smallest positive root of equation
f(x):=evalf(1/x-evalf(Pi)*cos(evalf(Pi)*x));
with delta = 10^-5 and eps = 10^-6
plot(f(x),x=.05..10.0);
From graph above we can conclude that given equation has smallest positive real root located between 0.0 and 2.0
To get their values with accuracy required we invoke bisection method with root isolation interval (0.01,2.0):
Bisect:=proc(funct_equation,ai,bi,Mi,epsfi,deltaxi) local k,M,a,b,u,v,w,c,e,epsf,deltax,feq, notsolved: M:=Mi: feq:=funct_equation: a:=ai: b:=bi: epsf:=epsfi: deltax:=deltaxi: notsolved:=true: u:=evalf(subs(x=a,feq)): v:=evalf(subs(x=b,feq)): printf("a=%+9.6f %+12.6e\nb=%+9.6f %+12.6e\n\n",a,u,b,v); e:=b-a; if (sign(u)<>sign(v)) then printf(" n x f\n"); for k from 1 by 1 while (k<M and notsolved) do:
e:=0.5*e;
c:=a+e;
w:=evalf(subs(x=c,feq)):
printf("%2d %+9.6f %+12.6e\n",k,c,w);
if (abs(e)<deltax or abs(w)<epsf) then
notsolved:=false:
else
if (sign(w) <> sign(u)) then
b:=c: v:=w:
else
a:=c: u:=w:
fi:
fi: od: printf("Root = %+9.6f function = %+12.6e\n",0.5*(a+b),evalf(subs(x=0.5*(a+b),feq))); fi: end: with(plots):
Warning, the name change coords has been redefined
Bisect(f(x),0.01,2.0,30,1.0e-6,1.0e-5):
You won't need that subs call, if you keep your feq as a procedure.
restart:
Bisect:=proc(func::procedure,ai,bi,Mi,epsfi,deltaxi)
local k::integer,
M::integer,
a,b,u,v,
w::float,
c,e,
epsf::float,
deltax,
notsolved;
M:=Mi:
a:=ai: b:=bi: epsf:=epsfi:
deltax:=deltaxi: notsolved:=true:
u:=func(a);
v:=func(b);
printf("a=%+9.6f %+12.6e\nb=%+9.6f %+12.6e\n\n",a,u,b,v);
e:=b-a;
if (sign(u)<>sign(v)) then
printf(" n x f\n");
for k from 1 by 1 while (k<M and notsolved) do
e:=0.5*e;
c:=a+e;
w:=func(c);
printf("%2d %+9.6f %+12.6e\n",k,c,w);
if (abs(e)<deltax or abs(w)<epsf) then
notsolved:=false:
else
if (sign(w) <> sign(u)) then
b:=c: v:=w:
else
a:=c: u:=w:
fi:
fi:
od:
printf("Root = %+9.6f function = %+12.6e\n",0.5*(a+b),func(0.5*(a+b),feq));
fi:
0.5*(a+b);
end:
with(plots):
f:=subs(Pi=evalf[16](Pi),proc(x::float) 1/x-Pi*cos(Pi*x); end proc);
Bisect(f,0.01,2.0,30,1.0e-6,1.0e-5);
f(%);
CodeGeneration[C](f);
CodeGeneration[C](Bisect);
Also, if you start with an expression for f, you can always turn it into an operator (a sort of procedure, but which too can be code-generated) using the unapply command.
For example, I could also have created the procedure f in the following ways. (Note that one of these produces a default 10-digits approximation to Pi in the generated C code, and the other a 16-digit approximation.)
f_expression := 1/x-Pi*cos(Pi*x);
f:=unapply(f_expression, [x::float]);
CodeGeneration[C](f);
f:=subs(Pi=evalf[16](Pi),unapply(f_expression, [x::float]));
CodeGeneration[C](f);