I want to change a large Fortran 90 program to use double precision instead of single precision. I thought the easiest way to do this is to add the following flag to the compiler:
-fdefault-real-8
However, this does not seem to update the MPI commands. For example, I need to change commands like this:
CALL MPI_RECV(x, n, MPI_REAL, rankstart, tag, comm, stat, ierr)
to
CALL MPI_RECV(x, n, MPI_REAL8, rankstart, tag, comm, stat, ierr)
Do you know if there is a compiler flag to change the default value of MPI_REAL to MPI_REAL8? If not, do you know of another way I can easily change the precision of the program without having the manually adjust all the commands in the code myself?
You can use the same flag when configuring your MPI library for compilation. In that case, provided the library is programmed properly, the default real should correspond to the default kind you chose.
The library may request that the default integers and logicals are of the same storage size as the new enlarged real.
Be aware that MPI is an external library. It is compiled separately. It will NOT react to flags that you supply to the compiler when compiling your program. It only knows about the flags that were used to compile the library.
The easiest way to change the precision in a Fortran 90 program is to use a working kind parameter
integer, parameter :: rp = kind(1.d0)
and to declare your real variables as
real(rp) :: x
You can later change the parameter value to any other value as shown at Fortran 90 kind parameter
Related
I want to specify as type of a subroutine a floating point value (real) of 8 bytes precision.
I have read here that the modern way to do it would be:
real(real64), intent(out) :: price_open(length)
However, iso_fortran_env is not supported by f2py (same as it does not support iso_c_bindings either).
I get errors of this type:
94 | real(kind=real64) price_open(length)
| 1
Error: Parameter 'real64' at (1) has not been declared or is a variable, which does not reduce to a constant expression
The link referenced before states that using kind would be the proper way if iso_fortran_env is not available and that real*8 shall be avoided.
I have been using real(8) is that equivalent to using kinds? If not, what shall I use?
What is wrong with real*8 if I want to always enforce 8 bytes floating point values?
You say you are specifically interested in interoperability with C. However, iso_c_binding, nor iso_fortran_env are supported. These modules have constants that help you to set the right kind constant for a given purpose. The one from iso_fortran_env would NOT be the right one to choose anyway, it would be c_double.
If these constants meant to help you in your choice are not available, you are on your own. Now you can choose any other method freely.
It is completely legitimate to use just kind(1.d0) and just check that the connection to C works. Automake had been doing that for ages.
Or use selected_real_kind(), it does not matter, it is really up to you. Just check that double in C ended up being the same numeric type.
The traditional thing in automatic build processes was to do many tests about which (mainly C) constant ended up having which value. You just need to check that double precision in Fortran does indeed correspond to double in C. It is very likely, but just check it. You can have a macro that changes the choice if not, but probably it is a needless work untill you actually meet such a system.
I am trying to switch to use mpi for some old fortran codes I have. I got some strange errors when compiling the code.
Error: There is no specific subroutine for the generic 'mpi_type_indexed' at (1)
when I try to switch to 'use mpi' in the code. If I use 'include 'mpif.h'' then the program got compiled and is able to run correctly.
I have written a compact example to verify the program. Both the code and my example are compiled under gcc/8.1.0 and openmpi/3.1.2.
program bt
use mpi
implicit none
!include 'mpif.h'
contains
subroutine read_me()
implicit none
integer :: my_n, my_disp, my_type
integer :: ierr
my_n = 2
my_disp = 4
call MPI_Type_indexed(1, my_n, my_disp, MPI_INTEGER, my_type, ierr)
end subroutine
end program
compile it with no flag: mpif90 bt.F90
With use mpi committed and include 'mpif.h' uncommitted, everything works fine.
With use mpi uncommitted and include 'mpif.h' committed, I got error says
bt.F90:23:67:
call MPI_Type_indexed(1, my_n, my_disp, MPI_INTEGER, my_type, ierr)
1
Error: There is no specific subroutine for the generic 'mpi_type_indexed' at (1)
As indicated in the comments the "problem" that has occurred is that because you have used the module rather than the include file an interface is now in scope, and the compiler can now detect that you are trying to call MPI_Type_indexed with incorrect arguments, as the 2nd and 3rd arguments should be arrays - take a look at https://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node79.htm#Node79 to see what the interface should be.
Looking at your example it looks as though the original author was assuming that a scalar and a 1 element array are the same thing - this is not the case as the former is rank 0 and the later rank 1. I say this as the first argument specifies how big the arrays should be, and in your case this has the value 1. Thus the 2nd and 3rd arguments should be single element arrays, rather than the scalars you have. The simplest solution, as these arguments are Intent( In ), is to put them in square brackets acting as an array constructor
call MPI_Type_indexed(1, [ my_n ], [ my_disp ], MPI_INTEGER, my_type, ierr)
I've always been using the -fdefault-real-8 option of gfortran to automatically promote every single REAL declared anywhere in the program to double precision, along with any constant, e.g. 1.23. If I ever wanted to switch back to single precision, I only had to remove that option and recompile, without changing a single character in the source code.
At a point I started using ISO_FORTRAN_ENV module, since it allows me to use constants like INPUT|OUTPUT|ERROR_UNIT, as well as IOSTAT_END and IOSTAT_EOR and others (which seemed to be a good and easy move in the direction of portability, am I wrong?). From then on, I've been seeing and ignoring the following warning
Warning: Use of the NUMERIC_STORAGE_SIZE named constant from intrinsic module ISO_FORTRAN_ENV at (1) is incompatible with option -fdefault-real-8
since such incompatibility seems to have no effect so far.
Now I'd like to get rid of this warning if it is possible and worth it.
If I correctly understood, to avoid this warning I should give up on -fdefault-real-8 option and change every REAL to REAL(real64) and/or to REAL(dp) (provided that, in the latter case, the statement USE, INTRINSIC :: ISO_FORTRAN_ENV, dp => real64 is put in that unit), which is not a difficult task for sed or vim.
Nevertheless, it seems to me that this change wouldn't be the same as using -fdefault-real-8 option, since all constants would stay single precision as long as I don't add d0 to them.
Assumed the -fdefault-real-8 option is removed and ISO_FORTRAN_ENV is used anywhere, is there any way to make any constant across the program behave as each had d0 suffix?
Whether or not this is possible, have I correctly extrapolated that I can put the following lines in a single module which is used by all others program units, each of which can then use dp as kind type parameter?
USE, INTRINSIC :: ISO_FORTRAN_ENV
INTEGER, PARAMETER :: dp = real64
I would prefer this way since I could switch to real32 or real128 or whatever by changing only that line.
If you just want to silence the warning and you do not care about the implications -fdefault-real-8 has on storage association and some Fortran standard requirements, just do not import NUMERIC_STORAGE_SIZE from the module. For example,
USE, INTRINSIC :: ISO_FORTRAN_ENV, only: INPUT_UNIT,OUTPUT_UNIT,ERROR_UNIT
Assumed the -fdefault-real-8 option is removed and ISO_FORTRAN_ENV is used anywhere, is there any way to make any constant across the program behave as each had d0 suffix?
No.
By the way, d0 is exactly the same as double precision, so that doesn't fixate much either, since the meaning of double precision is allowed to vary as much as real.
Whether or not this is possible, have I correctly extrapolated that I can put the following lines in a single module which is used by all others program units, each of which can then use dp as kind type parameter?
Yes. That is a common practice.
If I want to use the 64-bit interface I can specify the -i8 compiler Flag for ifort or -fdefault-integer-8 for gfortran.
In MPI however MPI_INTEGER is defined as a fixed 32 bit integer: https://www.ibm.com/support/knowledgecenter/SSFK3V_2.3.0/com.ibm.cluster.pe.v2r3.pe400.doc/am106_pmd.htm
If I have a simple call such as:
MPI_Bcast(buffer, count, MPI_DATATYPE, root, MPI_COMM_WORLD, ierr)
How can I pass MPI_DATATYPE such that it takes the default value? I.e. MPI_INTEGER8 if -i8 is set or MPI_INTEGER4 if not?
I was considering doing it trough a constant, but I don't know what the type of MPI_DATATYPE is. Is it integer(4) just like MPI_COMM_WORLD is?
Edit: I just realized, that different MPI implementations behave differently:
program main
use mpi
integer(4) :: sz
integer(4) :: ierr
call MPI_Init(ierr)
call MPI_Type_size(MPI_INTEGER4, sz, ierr)
write (* ,* ) "4 = ", sz
call MPI_Type_size(MPI_INTEGER8, sz, ierr)
write (* ,* ) "8 = ", sz
call MPI_Type_size(MPI_INTEGER, sz, ierr)
write (* ,* ) "? = ", sz
call MPI_Finalize(ierr)
end program main
IntelMPI:
> $ ./bla.x
4 = 4
8 = 8
? = 4
OpenMPI:
> $ ./bla.x
4 = 4
8 = 8
? = 8
"In MPI however MPI_INTEGER is defined as a fixed 32 bit integer" It is not. That only is true for that particular MPI library you link to. If you link true MPI answer, link to the official MPI specifications.
If you want to use special flags changing the default behaviour so drastically, you would have to compile the MPI library to know about that flags. The implementation may or may not support such a change, but theoretically it should be possible.
There are tutorials on the web that show such compilation for certain MPI implementations. Use your search engine to find links as http://diracprogram.org/doc/release-12/installation/int64/mpi.html http://linuxtoolkit.blogspot.cz/2013/08/building-openmpi-libraries-for-64-bit.html and others.
If you want to be portable without caring about the default integer the compiler decided to use today and needing to have the MPI library synchronized with that, just use integer variables with some fixed storage size like integer(int32) and integer(int64). Those kind constants are defined in the iso_fortran_env module.
Or do not use/change these ugly flags so that you can be sure that the MPI library has the same settings as the compiler's default.
I have this simple code which uses DGEMM routine for matrix multiplication
program check
implicit none
real(8),dimension(2,2)::A,B,C
A(1,1)=4.5
A(1,2)=4.5
A(2,1)=4.5
A(2,2)=4.5
B(1,1)=2.5
B(1,2)=2.5
B(2,1)=2.5
B(2,2)=2.5
c=0.0
call DGEMM('n','n',2,2,2,1.00,A,2,B,2,0.00,C,2)
print *,C(1,1)
print *,C(1,2)
print *,C(2,1)
print *,C(2,2)
end program check
now when i compile this code with command
gfortran -o check check.f90 -lblas
I get some random garbage values. But when I add
-fdefault-real-8
to the compiling options I get correct values.
But since it is not a good way of variable declaration in Fortran. So I used the iso_fortran_env intrinsic module and added two lines to the code
use iso_fortran_env
real(kind=real32),dimension(2,2)::A,B,C
and compiled with
gfortran -o check check.f90 -lblas
Again I got wrong output .
Where I'm erring in this code?
I'm on 32bit linux and using GCC
DGEMM expects double precision values for ALPHA and BETA.
Without further options, you are feeding single precision floats to LAPACK - hence the garbage.
Using -fdefault-real-8 you force every float specified to be double precision by default, and DGEMM is fed correctly.
In your case, the call should be:
call DGEMM('n','n',2,2,2,1.00_8,A,2,B,2,0.00_8,C,2)
which specifies the value for alpha to be 1 as a float of kind 8, and zero of kind 8 for beta.
If you want to perform the matrix-vector product in single precision, use SGEMM.
Note that this is highly compiler-specific, you should consider using REAL32/REAL64 from the ISO_Fortran_env module instead (also for the declaration of A, B, and C).