Passing array length to function - fortran

I'm sorry if this is a trivial question. My fortran-fu is poor.
Is there a way in Fortran to pass the array length? Using common (which, from what I gather is equivalent to global) is an option as well. What I want is in the main program to call a function with an array. For example (This is typed in, not copy pasted from anywhere)
program prog
integer num
double precision x(num),v
double precision test
....
v=test(x,num)
....
function test(x,num)
double precision test
integer num
double precision x(num)
test=0.0d0
....
return
end
This won't compile since num is not a constant. The important thing is to know what the array size I'm passing is.
Edit: I'm using the GNU Fortran 95 compiler.
Edit2: I tried High Performance Mark's solution without luck:
program prog
integer v
parameter (v=10)
double precision x(v),test,k
k=test(x)
write (*,*) size(x)
stop
end
function test(x)
double precision, dimension(:),intent(in) :: x
double precision test
write (*,*) size(x)
test = 0.0d0
return
end
The output should be two lines where 10 is written. Instead I got this:
/scpc:niels: #$ f95 err.f
/scpc:niels: #$ ./a.out
0
10
/scpc:niels: #$

Fortran arrays 'know' how long they are, you shouldn't need to pass the array and its length as different arguments. (Unless, that is, you are interfacing with old Fortran codes.) Today you'd write something like this
function test(arr)
real, dimension(:), intent(in) :: arr
...
integer :: arrsize
...
arrsize = size(arr)
...
If you have to interface to old code in which array sizes are passed you can make calls like this
call old_fortran_subr(array, size(array, 1), other_arguments)
Oh, and while I'm writing, have nothing to do with common in any code you write from scratch, it's a (rightly) deprecated feature from the 70's and earlier. Instead use module variables.

OK, there's a lot going on here, especially as the Fortran style you are using is a little archaic. Let's do it by steps ...
Firstly make sure you always use implicit none
Secondly if you know the size of the array a priori you can use a symbolic constant to denote its size. You do this via a parameter:
Program prog
Implicit None ! ALWAYS USE THIS
Integer, Parameter :: num = 36
Double Precision x( num )
Double Precision test
Double Precision v
Call Random_number( x )
v = test( x, num )
Write( *, * ) v
End Program prog
Function test( x, num )
Implicit None ! ALWAYS USE THIS
Double Precision test
Integer num
Double Precision x( num )
Integer i
test = 0.0d0
Do i = 1, num
test = test + x( i ) * x( i )
End Do
End Function test
[luser#cromer stackoverflow]$ gfortran -O -std=f95 -Wall -Wextra -pedantic func.f90
[luser#cromer stackoverflow]$ ./a.out
12.129812171430215
Note how num is set to be 36, but the parameter bit means I can not change its value - it is a constant and so can be used to set the size of arrays.
And that is how things stood until 1990. Then a number of things came into the language which change the answer. Most directly connected to your question are allocatable arrays, which allow you to specify the size of the array at run time, and assumed shape arrays, which make passing arrays to subprograms simpler. However a whole slew of other things came in and I suggest you have a look in a book to learn about them - the new language is much more expressive and safer than the old. As an example I would write the above nowadays as something like
[luser#cromer stackoverflow]$ cat func.f90
Module numbers_module
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
End Module numbers_module
Module funcs_module
Use numbers_module
Implicit None
Public :: test
Private
Contains
Function test( x ) Result( sum_sq )
Implicit None ! ALWAYS USE THIS
Real( wp ) :: sum_sq
Real( wp ), Dimension( : ), Intent( In ) :: x
sum_sq = Sum( x * x )
End Function test
End Module funcs_module
Program prog
Use numbers_module
Use funcs_module
Implicit None ! ALWAYS USE THIS
Real( wp ), Dimension( : ), Allocatable :: x
Real( wp ) :: v
Integer :: num
Write( *, * ) 'How many elements ?'
Read ( *, * ) num
Allocate( x( 1:num ) )
Call Random_number( x )
v = test( x )
Write( *, * ) v
End Program prog
[luser#cromer stackoverflow]$ gfortran -O -std=f95 -Wall -Wextra -pedantic func.f90
[luser#cromer stackoverflow]$ ./a.out
How many elements ?
39
14.151818513394156
If you decide to go this way make sure you understand why to use this method an interface to test needs to be in scope at the calling point - and to do this read a book.
Oh. Common. Just Say No.

Implicit variable declaration was used in earlier versions of Fortran. By this method of variable could simply be used without declaring it explicitly. One statement such as IMPLICIT REAL(A-H, O-Z ) would declare all real variables beginning with letters A-H and O-Z. This is now considered to be too loose and explicit type declaration such as is required by the more powerful language C is to be preferred. My first language was FORTRAN IV but the second language I learned was QBasic which like C requires that all variables be declared explicitly before use.

Related

Fortran EQUIVALENCE statement with array length from subroutine input

I'm modernizing some old Fortran code and I cannot get rid of an equivalence statement somewhere (long story short: it's mixed use is so convoluted it'd take too much work to convert everything).
I need the length of the EQUIVALENCEd arrays to depend on some input, like the following code:
program test_equivalence
implicit none
type :: t1
integer :: len = 10
end type t1
type(t1) :: o1
call eqv_int(o1%len)
call eqv(o1)
return
contains
subroutine eqv_int(len)
integer, intent(in) :: len
integer :: iwork(len*2)
real(8) :: rwork(len)
equivalence(iwork,rwork)
print *, 'LEN = ',len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv_int
subroutine eqv(o1)
type(t1), intent(in) :: o1
integer :: iwork(o1%len*2)
real(8) :: rwork(o1%len)
equivalence(iwork,rwork)
print *, 'LEN = ',o1%len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv
end program test_equivalence
This program will create 0-length arrays with gfortran 9.2.0. This is the output:
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
The same code will return Array 'rwork' at (1) with non-constant bounds cannot be an EQUIVALENCE object when compiled with gfortran 5.3.0, the warning disappears since gfortran 6.2.0, but the size of the arrays is always 0. So maybe compiler bug?
The source code is indeed not a valid Fortran program. To be specific, it violates the numbered constraint C8106 of Fortran 2018:
An equivalence-object shall not be a designator with a base object that is .. an automatic data object ..
Being a numbered constraint, the compiler must be capable of detecting this violation. If hasn't such a capability this is a deficiency in the compiler (a bug). Being "capable" doesn't mean doing so by default, so please look carefully to see whether there are options which do lead to this detection. Someone familiar with the internals of GCC can give further detail here.
As the source isn't a valid Fortran program, the compiler is allowed to consider the arrays of size zero if it has skipped the violation detection.

Why do I have to specify implicitly for a double precision return value of a function in Fortran?

I am new to Fortran and I am trying on the common block. My code is simple
program main
implicit double precision (p)
real * 8 :: x, y
common /yvalue/ y
x = 3d0
y = 3d0
print *, power(x)
end program main
function power(x)
implicit none
real * 8 :: power
real * 8 :: x, y
common /yvalue/ y
power = x ** y
end function power
It works but if I comment out the second line, which implicitly declares variables starting with p to be double precision, the compiler complains the following
Error: Return type mismatch of function ‘power’ at (1) (REAL(4)/REAL(8))
I do get the point that the return value power is by default a single precision variable, but why declaring power as double precision in the function is not enough? And why writing real * 8 power in main would not work either?
When a procedure (function or subroutine) that you are trying to invoke in your code lays outside the body of your program and also is not part of any module, it's named an external function (or subroutine).
Fortran is a statically-typed language, so the types of all variables and functions must be known at compile-time. So, if you want to reference an external function in your program, there must be a way for the program to know its return type. You have 3 (bad) options for this, and I'll list them, starting from the worst:
WORST: Rely on an implicit-typing rule that happens to match the return type of the external function with the type associated with its identifier in the caller (as you did in your sample).
Why you shouldn't do that? Because it is cancer. It makes the meaning of the code obscure, you can't know what this name reference to. It may even look just like an array variable in some circumstances, instead of a function. Also, the compiler doesn't check argument conformance in this case, so if you don't have specific compiler options turned on, the code will fail at runtime, or worse, will give wrong results. Moreover, implicit-typing is very very rarely useful these days, most of the time it's an ask for trouble. Always use implicit none!
As you noted, by the default rules of implicit-typing, any variable with a name starting with p will be default real type (in your compiler, it is real(4)). As you declared the function result as real*8, that your compiler interpret as real(8) (see final note), the error arises.
BAD: Declare the function's name and type in the caller's specification area.
You'd do that just like you'd declare a variable, like this:
program main
implicit none
real*8 :: x, y, power
By the way, the attribute external may be applied to external procedures like yours. More than giving some properties to the procedure (can be passed as an actual argument, disambiguation from intrinsic procedures), it would make the origin of the identifier clearer.
program main
implicit none
real*8 :: x, y, power
external :: power
Why you shouldn't do that? There is no argument checking by the compiler either. This severely limits your options for communicating to external functions: the arguments cannot be assumed-shape, assumed-rank, polymorphic, parameterized, coarray, or be declared on the callee side as allocatable, optional, pointer, target, asynchronous, volatile or value; the return type cannot be an array, or pointer, or allocatable; the function cannot be passed as argument, be elemental and, if pure, can't be used in such contexts. And the reason for all this is the lack of an explicit interface.
ACCEPTABLE: Specify an interface for your external function in the caller.
Like this:
program main
implicit none
interface
real*8 function power(y)
real*8 :: y
end function
end interface
This way, the compiler is able to know all details of declaration and all the restrictions I mentioned won't apply. Total freedom and code clarity!
Why you shouldn't do that? Because there is a better way, that is using modules! Well, it's totally ok to do this in contexts were you can't go for modules, e.g. when working with already existent large old code. The downside is that, you have almost the same code in two different places, and they must always match.
Bonus: BETTER: Use modules.
program main
use :: aux_module
implicit none
real*8 :: x, y
common /yvalue/ y
x = 3d0
y = 3d0
print *, power(x)
end
module aux_module
implicit none
contains
function power(x)
real*8 :: power
real*8 :: x, y
common /yvalue/ y
power = x ** y
end
end
Why you should definitely do that? Because with modules, interfaces are automatically and implicitly available (less code duplication, no restrictions); modules can be recompiled separately and updated without breaking code. Also, you can declare shared variables in the scope of the module and avoid using common declarations. An even better version of your code would be:
program main
use aux_module
implicit none
real*8 :: x
x = 3d0
y = 3d0
print *, power(x)
end
module aux_module
implicit none
real*8 :: y
contains
function power(x)
real*8 :: power
real*8 :: x
power = x ** y
end
end
There is even the option to include your functions directly into your program, after contains. This is recommended only if you don't plan to reuse this function in other program units. #IanBush's answer covers this case.
Final note: take a look on this answer to see why the syntax real*8 is non-standard and should be avoided.
As stated in the comments simply declaring the function in not only its own scope but also the scope that it is called will solve your problem. However I also want to discourage you from using common, implicit typing, and the completely non-standard real*8. As such here is a version of your program in a more modern dialect
ian#eris:~/work/stackoverflow$ cat power.f90
Program power_program
Implicit None
Integer, Parameter :: wp = Selected_real_kind( 14, 70 )
Real( wp ) :: x, y
x = 3.0_wp
y = 3.0_wp
! Return type and kind of the function power in scope
! due to the implicit interface
Write( *, '( 3( a, 1x, f0.6, 1x ) )' ) &
'x =', x, 'y = ', y, 'x**y = ', power( x, y )
Contains
Pure Function power( x, y ) Result( r )
Real( wp ) :: r
Real( wp ), Intent( In ) :: x
Real( wp ), Intent( In ) :: y
r = x ** y
End Function power
End Program power_program
ian#eris:~/work/stackoverflow$ gfortran -std=f2003 -Wall -Wextra -O power.f90
ian#eris:~/work/stackoverflow$ ./a.out
x = 3.000000 y = 3.000000 x**y = 27.000000
ian#eris:~/work/stackoverflow$

Pass arrays from C/C++ to Fortran and return a calculated array

I am trying to pass an array from C/C++ into a Fortran 2003 module and get the calculated values back into C/C++. I've been able to pass and return single values (scalars) just fine, but getting an array back and forth is proving difficult. I've found many threads on scalar values and I've been successful at making those work.
I've modeled my array based functions after my working scalar functions.
I am using gcc/gfortran.
Here's the Fortran module (ConvertUnitsLib.f03).
module ConvertUnitsLib
use :: iso_c_binding ! for C/C++ interop
real(c_double), bind(c) :: degF, degC
public DegCtoF
contains
!
! Convert temperature degrees Celsius Fahrenheit
!
real(kind = c_double) function DegCtoF(degC) result(degF) &
& bind(c, name = "DegCtoF")
real(c_double), intent(in), dimension(:) :: degC
real(c_double), dimension(size(degC)) :: degF
do i = 1, size(degC)
degF(i) = ( degC(i) * 1.8 ) + 32
end do
end function DegCtoF
! End of module
end module ConvertUnitsLib
And the C/C++, (CFort.cpp)
#include <stdio.h>
#ifdef __cplusplus
extern"C" {
#endif
double DegCtoF(double *[]);
#ifdef __cplusplus
}
#endif
/**********************************************************************/
int main(int argc, char *argv[])
{
printf("C/C++ and Fortran together!\n");
double DegreesC[2] = {32, 64};
double DegreesF[2];
DegreesF = DegCtoF(&DegreesC);
printf("%3.1f [C] = %3.1f [F]\n", DegreesC, DegreesF );
return 0;
}
And last but not least, the Makefile
# C++ directives
CC=g++
CFLAGS=-std=c++11
# Fortran directives
FC=gfortran
FFLAGS=-std=f2003
all: clean
$(FC) $(FFLAGS) -c -fcheck=all ConvertUnitsLib.f03
$(CC) $(CFLAGS) -c CFort.cpp
$(FC) $(FFLAGS) ConvertUnitsLib.o CFort.o -o convert
clean:
rm -f *.o
rm -f *.mod
Under the rules of current Fortran (Fortran 2008, but this is the same for when C interoperability was introduced in Fortran 2003), a Fortran procedure is not interoperable with C if it has an assumed shape dummy argument (other restrictions also apply). In your code degC, the dummy argument in the function DegCtoF, declared as
real(c_double), intent(in), dimension(:) :: degC
is such a thing.
So, under F2003 you cannot have such an interoperable function. Which is where things get tricky.
In the proposed draft for F2015 (based on the ISO TS29113 Further Interoperability of Fortran with C) such a thing is interoperable. And this syntax is (I think) supported by recent versions of gcc which is why the code is not rejected by gfortran.
(TS) Standardized interoperation with such a procedure with an assumed shape argument, however, requires using the C descriptor described in ISO_Fortran_binding.h on the C side which is not implemented in gcc. To do such interaction instead requires understanding the gcc array descriptor directly.
But you're in luck. In your case you don't really need to use an assumed shape dummy argument: you can use an explicit shape dummy argument and such interoperation is part of F2003. All you need to do is pass the size of the array.
Either way, an interoperable function must return a scalar result, so you'll also want to move to a subroutine, as given in the answer by innoSPG.
Finally, I'll mention your use of
real(c_double), bind(c) :: degF, degC
in the module.
These are interoperable global variables (through linkage association). You don't reference these variables in the Fortran code: the dummy and the function result are not these things.
In this simple case from the above, and the other answer, one will happily have a subroutine like
subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF')
...
end subroutine
but this is perhaps a good opportunity to describe the use of the C descriptor from ISO_Fortran_binding.h. Note, though, that in the immediate term gfortran does not support this approach.
Consider the Fortran source
subroutine DegCtoF(degC, degF) bind(c,name='DegCtoF')
use, intrinsic :: iso_c_binding, only : c_double
implicit none
real(c_double), intent(in), dimension(:) :: degC
real(c_double), intent(out), dimension(*) :: degF
degF(1:SIZE(degC)) = degC*1.8+32
end subroutine DegCtoF
(for simplicity I'm going to assume that the memory management of degF is done all on the C side - naturally one could extend beyond the assumed size array). For this subroutine to be interoperable the argument corresponding to degC must be a pointer to CFI_cdesc_t.
Take the C code (with size magic numbers)
#include "ISO_Fortran_binding.h"
#include <stdio.h>
void DegCtoF(CFI_cdesc_t*, double*);
int main(int argc, char *argv[])
{
printf("C and Fortran together!\n");
CFI_CDESC_T(1) DegreesC_Fdesc;
CFI_index_t extent[1] = {2};
CFI_rank_t rank = 1;
double DegreesC[2] = {32, 64};
double DegreesF[2];
CFI_establish((CFI_cdesc_t*)&DegreesC_Fdesc, &DegreesC, CFI_attribute_other,
CFI_type_double, 2*sizeof(double), rank, extent);
DegCtoF((CFI_cdesc_t*)&DegreesC_Fdesc, DegreesF);
printf("%3.1f [C] = %3.1f [F]\n", DegreesC[0], DegreesF[0] );
printf("%3.1f [C] = %3.1f [F]\n", DegreesC[1], DegreesF[1] );
return 0;
}
Here CFI_establish establishes a suitable C descriptor DegreesC_Fdesc which can correspond to the assumed shape Fortran dummy argument. Inside the Fortran subroutine there is no problem at all assessing the size of the incoming array.
Before francescalus confirms it, I was going to say that from what I know that was a little bit old, the interoperability does not permit what you are trying to do with arrays.
In addition, some good habits are always critical when coding. For example using implicit none in fortran to force the declaration of all variables before they are used. The use of named constant when the language permits it, for example the 2 that you are using as array size in fortran.
Below is a modified version of your code that should do something like what you want to achieve.
//Fortran
module ConvertUnitsLib
use :: iso_c_binding ! for C/C++ interop
!real(c_double), bind(c) :: degF, degC
implicit none
public DegCtoF
contains
!
! Convert temperature degrees Celsius Fahrenheit
!
subroutine DegCtoF(degC, degF, n)&
bind(c, name = "DegCtoF")
integer, intent(in) :: n
real(c_double), intent(in), dimension(n) :: degC
real(c_double), intent(out), dimension(n) :: degF
integer :: i
do i = 1, n
degF(i) = ( degC(i) * 1.8 ) + 32
end do
end subroutine DegCtoF
// C++
#include <stdio.h>
#ifdef __cplusplus
extern"C" {
#endif
double DegCtoF(double [], double [], const int *);
#ifdef __cplusplus
}
#endif
/**********************************************************************/
int main(int argc, char *argv[])
{
const int N = 2;
printf("C/C++ and Fortran together!\n");
double DegreesC[N] = {32, 64};
double DegreesF[N];
DegCtoF(DegreesC, DegreesF, &N);
for(int i = 0; i<N; i++){
printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i] );
}
return 0;
}

Eliminating hidden copies in Fortran

I wanted to ask some Fortran gurus about this issues I have with an up to date version of the Cray Compiler. I have several warnings that although they do not affect correctness, they will probably do for performance. The warning is:
This argument produces a copy in to a temporary variable.
Here is one of the situations where I get this warning. Within the same file (fem.f90) and module:
call fem( array_local( i, : ), pcor, arcol, inder, &
^
ftn-1438 crayftn: CAUTION FFEM, File = fem.f90, Line = 676, Column = 31
This argument produces a copy in to a temporary variable.
The routine FFEM from where array_local is called looks like:
--------------------------
subroutine ffem( alow, pcor, arcol, inder, iflag )
integer , intent( in ) :: alow(3), pcor(3)
real, intent( in ) :: inder,arcol
integer, intent( out ) :: iflag
integer:: array_local(5,3)
! within in a loop
call fem( array_local( i, : ), pcor, arcol, inder, &
..........
--------------------------
And here is fem subroutine:
--------------------------
subroutine fem (ac, pc, rc, id, flag )
integer, intent( in ) :: ac(3)
.......
--------------------------
I cannot find the way to get rid of that copy in which will definitely slow down my code. I was wondering, does anyone know why this happen, and how can I fix it?
If array_local must be defined as it is and you cannot define it as (5,3) as brady shows, you can consider an assumed shape dummy argument
subroutine fem (ac, pc, rc, id, flag )
integer, intent( in ) :: ac(:)
array passed there can be non-contiguous and still there is no copy, ac will be non-contiguous (strided) too.
You need explicit interfaces for that. That is best achieved by placing the subroutines in a module.
The feasibility of this change depends on your use of array_local elsewhere, but you could swap the order:
integer:: array_local(3,5)
and then call fem:
call fem( array_local( :, i ), pcor, arcol, inder, &
This will allow the compiler to simply send a reference to the appropriate part of the array_local array since columns are ordered contiguously in memory.

How are numeric types managed in fortran 90

I have an issue that hopefully would not affect my numerical calculations in Fortran 90.
The thing is that I have an array declared as
Real(r8), Allocatable :: matKBody(:)
By my hand, initializing such array I done it like
allocate(matKBody(1:Nk), STAT=ierr)
If (ierr /= 0) Stop
matKBody(:) = ( 0.0_r8, 0.0_r8)
which is a way to initialize a complex array. I noticed this error but I was surprised
that it doesn't matter if I initialize such array in this way or using the 'correct' statement:
matKBody(:) = 0.0_r8
...
... ! Do some stuff with the array
...
Deallocate( matKBody, STAT=ierr )
If (ierr /= 0) Stop
If I print both arrays they give me the correct initialization , i.e. both initialize
real numbers.
Why fortran (or the compiler) is not aware of such kind of things?
(I used ifort for compilation).
Full example:
program test_convert
use, intrinsic :: ISO_FORTRAN_ENV
Real(real64), Allocatable :: matKBody(:)
allocate (matKBody (1:10) )
matKBody(:) = ( 0.0_real64, 0.0_real64)
end program test_convert
Compiled with gfortran with options: -O2 -fimplicit-none -Wall -Wline-truncation -Wcharacter-truncation -Wsurprising -Waliasing -Wimplicit-interface -Wunused-parameter -fcheck=all -std=f2008 -pedantic -fbacktrace
Output from gfortran:
matKBody(:) = ( 0.0_real64, 0.0_real64)
1
Warning: Possible change of value in conversion from COMPLEX(8) to REAL(8) at (1)
So some compiler are "aware" of such things and will tell you about them if you request it to.
As discussed in the comments, Fortran provides automatic conversion between types upon assignment. So this is not an error, but gfortran at least will provide a warning so that the programmer can check whether they intended the conversion. You can suppress the warning and state your intent to cause a conversion via:
matKBody(:) = real ( ( 0.0_real64, 0.0_real64), real64 )
(This is just an example, since writing = 0.0_real64 is so much simpler for this particular assignment.)