Problems with passing an array in fortran [closed] - fortran

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I'm making a function call to a fortran subroutine:
CALL EGFpar(T_e,X_e,Y_e,cpi(1:nx,1:Nspec),WEG,IWEG)
Here IWEG is initialized with integer values in an initialization routine.
IWEG and WEG are declared in an include file:
INTEGER LIWEGmax, LWEGMAX
PARAMETER (LIWEGmax=400, LWEGMAX=6000000)
INTEGER IWEG(LIWEGmax)
DOUBLE PRECISION WEG(LWEGmax)
COMMON/EGINI/ IWEG, WEG
While debugging with VSCode, I can see the values I have initialized it with.
However, when I step into EGFpar, all the passed arrays have their initialized values except IWEG.
IWEG has nothing. Not zeros, but just blank. The subroutine EGFpar is given below
C-----------------------------------------------------------------------
SUBROUTINE EGFPAR ( NP, T, X, Y, CP, WEG, IWEG )
C-----------------------------------------------------------------------
C
C This subroutine initializes the thermomolecular
C parameters that are needed in order to evaluate
C the transport linear systems.
C The parameters that have to be evaluated depend on the transport
C coefficients that will be subsequently computed, as indicated
C by JFLAG. This flag has been initialized when calling EGINI.
C
C Input
C -----
C NP number of nodes
C T(NP) temperature
C X(NP,NS) species mole fractions
C Y(NP,NS) species mass fractions
C CP(NP,NS) species heat capacities at constant pressure
C per unit mass
C WEG double precision work array for EGLIB
C IWEG integer work array for EGLIB
C
C-----------------------------------------------------------------------
IMPLICIT DOUBLE PRECISION (A-H,O-Z)
IMPLICIT INTEGER (I-N)
DIMENSION WEG(*), IWEG(*)
C-----------------------------------------------------------------------
INCLUDE 'eg.cmn'
C-----------------------------------------------------------------------
CALL LFPAR ( NP, JFLAG, WEG(IEGPA),
& T, WEG(IDLT1), WEG(IDLT2), WEG(IDLT3),
& WEG(IDLT4), WEG(IDLT5), WEG(IDLT6),
& WEG(IEGRU), NS, WEG(IAAA), WEG(IBBB),
& WEG(IEGWT), WEG(IBIN), WEG(IETA),
& WEG(IETALG), WEG(ICTAIJ), WEG(IFITA),
& WEG(ICINT), CP, WEG(ICXI),
& WEG(IEPSIJ), WEG(IEGEPS), WEG(IEGCFD), WEG(IEGCFE),
& WEG(IEGZRT), WEG(IEGDIP), IWEG(IEGLIN) )
C-----------------------------------------------------------------------
CALL EGFXY ( NP, NS, WEG(IXTR), WEG(IYTR), WEG(IAUX), X, Y )
C-----------------------------------------------------------------------
RETURN
END
What is going wrong here?

The subroutine call
CALL EGFpar(T_e,X_e,Y_e,cpi(1:nx,1:Nspec),WEG,IWEG)
misses its first integer argument NP and should rather be
CALL EGFpar(NP, T_e,X_e,Y_e,cpi(1:nx,1:Nspec),WEG,IWEG)
This error could have been captured by the compiler if you would had an explicit interface.
This can be done in various ways:
manually defining an interface block
in modern Fortran by placing the procedure in a module
placing the procedure in a contains section (e.g. sub-program, sub-subroutine)
Check out this answer for more in-depth info about explicit interfaces.

Related

Converting Integer to Double Precision in fortran [duplicate]

This question already has an answer here:
Real value after assignment different from the real expression
(1 answer)
Closed 1 year ago.
I am seeing a strange problem, when I convert a large int to float, the values are not the same,
Here's a test program that replicates the problem
program test
integer a
a = 135000011
b = dble(a)
write(*,*) a, b
end
This prints
135000011 135000012
What is happening? How can I circumvent this?
I have found the error and how to fix this?
Since the value of b is not declared, fortran implicitly assumes its to be real, so to mitigate and correct the issue b should be declared as double precision
full program here
program test
integer a
double precision b
a = 135000011
b = dble(a)
write(*,*) a, b
end
This prints
135000011 135000011.00000

Floating point math accuracy, c++ vs fortran [duplicate]

This question already has answers here:
Different precision in C++ and Fortran
(2 answers)
Closed 2 years ago.
I have tried to implement a recursive function, in both C++ and Fortran, which calculates the value of the n'th Legendre polynomial, at x. In Fortran I have
recursive function legendre(n, x) result(p)
integer, intent(in) :: n
real(8), intent(in) :: x
real(8) :: p
if(n == 0) then
p = 1.0
else if (n == 1) then
p = x
else
p = (2.0*real(n,dp)-1.0)*x*legendre(n-1,x)-(real(n,dp)-1.0)*legendre(n-2,x)
p = p / real(n,dp)
end if
end function legendre
and then in C++ I have
double legendre(int n, double x) {
double p;
if(n == 0) return 1.0;
else if(n == 1) return x;
else {
p = (2.0*(double)n - 1.0)*x*legendre(n-1,x)-((double)n - 1.0)*legendre(n-2,x);
p /= (double)n;
return p;
}
}
These two functions seem to be exactly the same to me, both using double precission, but the result from the Fortran function is substantial different from the C++ result. For example,
legendre(7,-0.2345) = 0.28876207107499049178814404296875 according to WolframAlpha. The two codes above, when compiled with no optimizations produce
Fortran : 0.28876206852410113
C++ : 0.28876207107499052285
I know that the answers should not be the same due to floating point arithmetic, but the difference in value here for double precision seems somewhat large to me. What is the reason that the Fortran value is so far off from the other two ?
Although the variables in your FORTRAN function are defined as double-precision (8 bytes), the constants you have specified are default (single-precision, 4-byte) values.
According to this discussion, that means the arithmetic is performed to single-precision accuracy:
Even if the variable that you are assigning the result to is defined
to be DP, the Fortran standard requires that the arithmetic on the
constants be performed using SP. That is because you are using default
real constants, since you do not have any kind type parameter at the
end of the constants. By rule, default real is SP.
And, further on in the same discussion:
...Starting with Fortran 90, published in June 1991, this practice of
"promoting" SP constants to DP is prohibited.
So, in order to force double-precision maths, specify the constants as DP: instead of, for example, 1.0, specify 1.0D0 (and so forth for the others).
Thanks to Adrian's response, I was able to fix the problem. Nothing was wrong with the code in the Fortran function, the issue was the value of x which I was passing to it. Even though in the main program, I had defined x as real(8) and latter assigned it the value with a simple
x = -0.2345
which I thought should be double precision. It should actually be
x = -0.2345_dp
This results in the two functions having the same answer. I believe it is likely due to the reason that Adrian pointed out.

Maximum value of 64 bit floating point number for overflow detection

I have a seemingly simple problem: I want to detect whether a floating point addition in Fortran will overflow by doing something like the following:
real*8 :: a, b, c
a = ! some value
b = ! some value
if (b > DOUBLE_MAX - a) then
! handle overflow
else
c = a + b
The problem is that I don't know what DOUBLE_MAX should be. I'm aware of how floating point numbers are represented according to IEEE 754 but the largest value representable by a double precision floating point number seems to be too large for a variable of type real*8 (i.e. if I try to assign 1.7976931348623157e+308 to such a variable gfortran complains). C and C++ have predefined constants/generic functions for this purpose but I couldn't find a Fortran equivalent.
Note: I'm aware that real*8 is not really part of the standard but there seems to be no other way to reliably specify that a floating point number should use the double precision format.
Something like this?
real(REAL64) function func( a, b )
use, intrinsic :: iso_fortran_env, only: REAL64, INT64
use, intrinsic :: ieee_arithmetic, only: ieee_value, ieee_set_flag, IEEE_OVERFLOW, IEEE_QUIET_NAN
implicit none
real(REAL64), intent(in) :: a, b
real(REAL64), parameter :: MAX64 = huge(0.0_REAL64)
if ( b > MAX64-a ) then
! Set IEEE_OVERFLOW flag and return NaN
call ieee_set_flag(IEEE_OVERFLOW,.true.)
func = ieee_value(func,IEEE_QUIET_NAN)
else
func = a + b
end if
return
end function func
All I could find for intrinsic ieee_exceptions module is:
https://github.com/gcc-mirror/gcc/blob/master/libgfortran/ieee/ieee_exceptions.F90
For setting NaN value see post.
There are likely better ways to detect overflow, but the precise answer to your question is to use the huge function. HUGE(a) returns the largest possible number representable by the type a.

gfortran doesn't compile real variables

I have written a simple program in Fortran90 to calculate the area of a triangle. The user enters the three sides of the triangle, and then the program outputs the area. Simple enough.
MODULE Triangle_Operations
IMPLICIT NONE
CONTAINS
FUNCTION Area(x,y,z)
REAL :: Area ! function type
REAL, INTENT( IN ) :: x, y, z
REAL :: theta, height
theta = ACOS((x**2+y**2-z**2)/(2.0*x*y))
height = x*SIN(theta); Area = 0.5*y*height
END FUNCTION Area
END MODULE Triangle_Operations
PROGRAM Triangle
USE Triangle_Operations
IMPLICIT NONE
REAL :: a, b, c, Area
PRINT *, 'Welcome, please enter the &
&lengths of the 3 sides.'
READ *, a, b, c
PRINT *, 'Triangle''s area: ', Area(a,b,c)
END PROGRAM Triangle
When I compile this with gfortran gfortran triangle1.f90, this is the error I receive:
triangle1.f90:16.25:
REAL :: a, b, c, Area
1
triangle1.f90:14.8:
USE Triangle_Operations
2
Error: Symbol 'area' at (1) conflicts with symbol from module 'triangle_operations', use-associated at (2)
triangle1.f90:19.13:
READ *, a, b, c
1
Error: Symbol 'a' at (1) has no IMPLICIT type
triangle1.f90:19.16:
READ *, a, b, c
1
Error: Symbol 'b' at (1) has no IMPLICIT type
triangle1.f90:19.19:
READ *, a, b, c
1
Error: Symbol 'c' at (1) has no IMPLICIT type
Why exactly would there be an error thrown for variables a,b,c? I have explicitly defined these as reals.
The problem is that you've defined Area twice -- once in your main program and once in the module you import and the names conflict. You likely think you need to define Area in the main program as a holdover from earlier (darker) times when calling a function without an explicit interface. In modern Fortran, modules provide interfaces automatically and the statement use Triangle_operations is enough.
To fix your problem, remove the delcaration of Area from your main program, e.g. turn
REAL :: a, b, c, Area
into
REAL :: a, b, c
The subsequent errors in your compile output are a result of the first error concerning Area. That entire line gets invalidated so the type declarations of a, b, and c are not processed and this cause the compiler to complain about missing types the next time they are encountered. These errors will go away once you make the fix suggested above.
If your intent had been to have a variable named Area in your main program to store the result of the module's function call, you could rename the module symbol, e.g.
use triangle_operations, triangleArea => Area
and then do this:
real a, b, c, Area
Area = triangleArea(a,b,c)
in your main program.

gfortran error: zgesvd in lapack

I was doing an svd decomposition of a square matrix A, with A=U S Vdag, and in the fortran code, the line reads
lwork = -1
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
lwork = int(work(1));deallocate(work); allocate(work(lwork))
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
When I compiled with gfortran, it went through with no error or warning. However when I run the program, it shows error with message:
" ** On entry to ZGESVD parameter number 11 had an illegal value "
I could not figure out what went wrong.
For reference, the definitions of the parameters:
type cmatrix
integer(4) d
complex(8), allocatable :: m(:,:)
end type
type (cmatrix) A,U,Vdag
allocate(A%m(dim,dim),U%m(dim,dim),Vdag%m(dim,dim))
A%d = dim; U%m = dim; Vdag%d = dim
real(8) S(dim)
Thanks in advance!
Xiaoyu
p.s. It should be mentioned that such a program runs smoothly when compiled with ifort, but gfortran gives an runtime error as shown above
--- Problem solved!
It seems that the issue lies in how ifortran and gfortran allocates memory. I defined in the code USV type which:
type USV
integer is_alloc
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
When initializing by
type(USV) Test_usv(:)
allocate(Test_usv(3)),
the value of is_alloc is 0 using intel fortran compiler, while arbitrary number for gfortran. I need to use this value as a criterion for allocating U V matrices:
if (is_alloc.eq.0) then
allocate(U%m(dim,dim))
end if
The fundamental problem is not a difference between ifort and gfortran. Your approach to the initialization of the variables isn't valid Fortran. Unless you initialize a variable with a declaration, assignment statement, etc., its value is undefined. One way to fix this would be to add a default initialization to the type definition:
type USV
integer is_alloc = 0
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
Another approach would be to not track the allocation status yourself and to rely upon the intrinsic function that Fortran provides for this purpose:
if (.NOT. allocated (U%m) ) allocate(U%m(dim,dim))
P.S. Best practice is not to rely upon specific numeric values for kinds. The kind values are arbitrary and are not necessarily the number of bytes of the type. Some compilers use the number of bytes, others don't. One method to specify the number of bytes and to have portable code is to use types provided by the ISO Fortran environment:
use, intrinsic :: ISO_FORTRAN_ENV
integer(int32) :: d
real(real64), allocatable :: S(:)
The types are named after the number of bits. A list of the available types is in the "Intrinsic Modules" chapter of the gfortran manual.