error in if-statement, requires scalar logical expression - if-statement

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

Problems with do while implementation

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.

How to implement factorial function into code?

So I am using the taylor series to calculate sin(0.75) in fortran 90 up until a certain point, so I need to run it in a do while loop (until my condition is met). This means I will need to use a factorial, here's my code:
program taylor
implicit none
real :: x = 0.75
real :: y
integer :: i = 3
do while (abs(y - sin(0.75)) > 10.00**(-7))
i = i + 2
y = x - ((x**i)/fact(i))
print *, y
end do
end program taylor
Where i've written fact(i) is where i'll need the factorial. Unfortunately, Fortran doesn't have an intrinsic ! function. How would I implement the function in this program?
Thanks.
The following simple function answers your question. Note how it returns a real, not an integer. If performance is not an issue, then this is fine for the Taylor series.
real function fact(n)
integer, intent(in) :: n
integer :: i
if (n < 0) error stop 'factorial is singular for negative integers'
fact = 1.0
do i = 2, n
fact = fact * i
enddo
end function fact
But the real answer is that Fortran 2008 does have an intrinsic function for the factorial: the Gamma function. For a positive integer n, it is defined such that Gamma(n+1) == fact(n).
(I can imagine the Gamma function is unfamiliar. It's a generalization of the factorial function: Gamma(x) is defined for all complex x, except non-positive integers. The offset in the definition is for historical reasons and unnecessarily confusing it you ask me.)
In some cases you may want to convert the output of the Gamma function to an integer. If so, make sure you use "long integers" via INT(Gamma(n+1), kind=INT64) with the USE, INTRINSIC :: ISO_Fortran_env declaration. This is a precaution against factorials becoming quite large. And, as always, watch out for mixed-mode arithmetic!
Here's another method to compute n! in one line using only inline functions:
product((/(i,i=1,n)/))
Of course i must be declared as an integer beforehand. It creates an array that goes from 1 to n and takes the product of all components. Bonus: It even works gives the correct thing for n = 0.
You do NOT want to use a factorial function for your Taylor series. That would meant computing the same terms over and over. You should just multiply the factorial variable in each loop iteration. Don't forget to use real because the integer will overflow quickly.
See the answer under the question of your schoolmate Program For Calculating Sin Using Taylor Expansion Not Working?
Can you write the equation which gives factorial?
It may look something like this
PURE FUNCTION Bang(N)
IMPLICIT NONE
INTEGER, INTENT(IN) :: N
INTEGER :: I
INTEGER :: Bang
Bang = N
IF(N == 2) THEN
Bang = 2
ELSEIF(N == 1) THEN
Bang = 1
ELSEIF(N < 1) THEN
WRITE(*,*)'Error in Bang function N=',N
STOP
ELSE
DO I = (N-1), 2, -1
Bang = Bang * I
ENDDO
ENDIF
RETURN
END FUNCTION Bang

SIGFPE error with gfortran 4.8.5 handling

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).

IF clause at (1) requires a scalar LOGICAL expression [duplicate]

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.

Translation from Maple to C++

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);