I am converting a Fortran file using f2c. The file contains the definition of a MODULE, and I am unable to convert it. The error is Error on line 1 of machine.f: unclassifiable statement (starts "modulemach")
The file is pasted below:
MODULE MACHINE
IMPLICIT NONE
SAVE
! Machine dependant constants
integer, parameter :: kind_io4 = 4, kind_io8 = 8 , kind_ior = 8
&, kind_evod = 8, kind_dbl_prec = 8
&, kind_rad = selected_real_kind(13,60) !the '60' maps to 64-bit real
&, kind_phys = selected_real_kind(13,60) ! the '60' maps to 64-bit real
&, kind_REAL = 8 ! used in cmp_comm
&, kind_INTEGER = 4 ! -,,-
real(kind=kind_evod), parameter :: mprec = 1.e-12 ! machine precision to restrict dep
END MODULE MACHINE
Any help will be appreciated!
Thanks
I've used f2c on a few occasions only, and don't have much experience with it, but isn't it a tool for converting Fortran 77 code to C?
Modules are a Fortran feature that were introduced in Fortran 90, and they are not part of Fortran 77 (along with portable precision, e.g. KIND and some other stuff), and I don't know if C has an analog for those features. Somebody will surely clarify me on that.
Related
I have a data file, some of the numbers are really big, like 1E252. How do I read data into Fortran as 1D252. I've tried declaring the variable as double and kind=16, but when its read in as 1E252, it errors.
Unfortunately, the easy solution, to parse the data file and convert all Es to Ds won't work because it also needs to be compatible with python.
Is there something more elegant than this?
program test
Integer :: i
real (kind = 8) :: another_test
Character(len=20) :: char_arr
char_arr = "1.10E+223"
Do i = 1,20
If (char_arr(i:i) == "E") then
char_arr(i:i) = "D"
EndIf
EndDo
Read(char_arr, *) another_test
Write(*,*) another_test
end program test
This isn't a full answer, mainly because I haven't been able to reproduce your premise. (I'm at my home computer, I only have access to gfortran v7.3.0 on Windows 10/cygwin.)
This code compiled perfectly fine and printed the correct value:
program large_float
use iso_fortran_env, only: real64
implicit none
character(len=20) :: strrep
real(kind=real64) :: val
strrep = '1.10E+223'
read (strrep, *) val
print *, val
end program large_float
Now you can see what I do differently to you is this: Instead of the ambiguous kind=8 I use the intrinsic iso_fortran_env to get the correct kind for 64 bit floating values.
I would strongly recommend to use this syntax, as it is compiler independent1 and you will know exactly what you get.
1iso_fotran_env was added I think in Fortran 2003, but I don't know of any Fortran compiler that doesn't recognise it. If you get an error, you can still use the selected_real_kind:
integer, parameter :: real64 = selected_real_kind(R=300)
I have a Fortran program where I specify the kind of the numeric data types in an attempt to retain a minimum level of precision, regardless of what compiler is used to build the program. For example:
integer, parameter :: rsp = selected_real_kind(4)
...
real(kind=rsp) :: real_var
The problem is that I have used MPI to parallelize the code and I need to make sure the MPI communications are specifying the same type with the same precision. I was using the following approach to stay consistent with the approach in my program:
call MPI_Type_create_f90_real(4,MPI_UNDEFINED,rsp_mpi,mpi_err)
...
call MPI_Send(real_var,1,rsp_mpi,dest,tag,MPI_COMM_WORLD,err)
However, I have found that this MPI routine is not particularly well-supported for different MPI implementations, so it's actually making my program non-portable. If I omit the MPI_Type_create routine, then I'm left to rely on the standard MPI_REAL and MPI_DOUBLE_PRECISION data types, but what if that type is not consistent with what selected_real_kind picks as the real type that will ultimately be passed around by MPI? Am I stuck just using the standard real declaration for a datatype, with no kind attribute and, if I do that, am I guaranteed that MPI_REAL and real are always going to have the same precision, regardless of compiler and machine?
UPDATE:
I created a simple program that demonstrates the issue I see when my internal reals have higher precision than what is afforded by the MPI_DOUBLE_PRECISION type:
program main
use mpi
implicit none
integer, parameter :: rsp = selected_real_kind(16)
integer :: err
integer :: rank
real(rsp) :: real_var
call MPI_Init(err)
call MPI_Comm_rank(MPI_COMM_WORLD,rank,err)
if (rank.eq.0) then
real_var = 1.123456789012345
call MPI_Send(real_var,1,MPI_DOUBLE_PRECISION,1,5,MPI_COMM_WORLD,err)
else
call MPI_Recv(real_var,1,MPI_DOUBLE_PRECISION,0,5,MPI_COMM_WORLD,&
MPI_STATUS_IGNORE,err)
end if
print *, rank, real_var
call MPI_Finalize(err)
end program main
If I build and run with 2 cores, I get:
0 1.12345683574676513672
1 4.71241976735884452383E-3998
Now change the 16 to a 15 in selected_real_kind and I get:
0 1.1234568357467651
1 1.1234568357467651
Is it always going to be safe to use selected_real_kind(15) with MPI_DOUBLE_PRECISION no matter what machine/compiler is used to do the build?
Use the Fortran 2008 intrinsic STORAGE_SIZE to determine the number bytes that each number requires and send as bytes. Note that STORAGE_SIZE returns the size in bits, so you will need to divide by 8 to get the size in bytes.
This solution works for moving data but does not help you use reductions. For that you will have to implement a user-defined reduction operation. If that's important to you, I will update my answer with the details.
For example:
program main
use mpi
implicit none
integer, parameter :: rsp = selected_real_kind(16)
integer :: err
integer :: rank
real(rsp) :: real_var
call MPI_Init(err)
call MPI_Comm_rank(MPI_COMM_WORLD,rank,err)
if (rank.eq.0) then
real_var = 1.123456789012345
call MPI_Send(real_var,storage_size(real_var)/8,MPI_BYTE,1,5,MPI_COMM_WORLD,err)
else
call MPI_Recv(real_var,storage_size(real_var)/8,MPI_BYTE,0,5,MPI_COMM_WORLD,&
MPI_STATUS_IGNORE,err)
end if
print *, rank, real_var
call MPI_Finalize(err)
end program main
I confirmed that this change corrects the problem and the output I see is:
0 1.12345683574676513672
1 1.12345683574676513672
Not really an answer, but we have the same problem and use something like this:
!> Number of digits for single precision numbers
integer, parameter, public :: single_prec = 6
!> Number of digits for double precision numbers
integer, parameter, public :: double_prec = 15
!> Number of digits for extended double precision numbers
integer, parameter, public :: xdble_prec = 18
!> Number of digits for quadruple precision numbers
integer, parameter, public :: quad_prec = 33
integer, parameter, public :: rk_prec = double_prec
!> The kind to select for default reals
integer, parameter, public :: rk = selected_real_kind(rk_prec)
And then have an initialization routine where we do:
!call mpi_type_create_f90_real(rk_prec, MPI_UNDEFINED, rk_mpi, iError)
!call mpi_type_create_f90_integer(long_prec, long_k_mpi, iError)
! Workaround shitty MPI-Implementations.
select case(rk_prec)
case(single_prec)
rk_mpi = MPI_REAL
case(double_prec)
rk_mpi = MPI_DOUBLE_PRECISION
case(quad_prec)
rk_mpi = MPI_REAL16
case default
write(*,*) 'unknown real type specified for mpi_type creation'
end select
long_k_mpi = MPI_INTEGER8
While this is not nice, it works reasonably well, and seems to be usable on Cray, IBM BlueGene and conventional Linux Clusters.
Best thing to do is push sites and vendors to properly support this in MPI. As far as I know it has been fixed in OpenMPI and planned to be fixed in MPICH by 3.1.1. See OpenMPI Tickets 3432 and 3435 as well as MPICH Tickets 1769 and 1770.
How about:
integer, parameter :: DOUBLE_PREC = kind(0.0d0)
integer, parameter :: SINGLE_PREC = kind(0.0e0)
integer, parameter :: MYREAL = DOUBLE_PREC
if (MYREAL .eq. DOUBLE_PREC) then
MPIREAL = MPI_DOUBLE_PRECISION
else if (MYREAL .eq. SINGLE_PREC) then
MPIREAL = MPI_REAL
else
print *, "Erorr: Can't figure out MPI precision."
STOP
end if
and use MPIREAL instead of MPI_DOUBLE_PRECISION from then on.
program PEU3
integer(kind=7) :: num = 600851475143
integer(kind=7) :: pf, counter
This is a section of my fortran code. Num is very large, so I tried to set it to kind = 7, but for some reason It's still throwing Error 217 - Integer(Kind=3) Constant out of range, for the line declaring num, even though I've declared that num ought to be kind 7. I've been at it for a while and can't understand for the life of me why it wouldn't be working. Help would be greatly appreciated. My IDE is Plato, with silverfrost compiler, if it's relevant.
Notice how the error is "Constant out of range", not "Variable out of range". The Constant in the line
integer(kind=7) :: num = 600851475143
is the actual number: 600851475143. By default, your compiler seems to want to store constants as 32 bit integers, and that number is too large for that.
The simplest solution would be to tell the compiler to store that constant as the same kind as the num, so something along these lines:
integer(kind=7) :: num = 600851475143_7
That trailing underscore tells the compiler to store the constant as an integer of kind 7.
BUT
I need to point out that what number corresponds to which kind is compiler and machine dependent. Which means like that, the code would not be easily transferable.
So please use one of these constructs:
For newer compilers (Fortran 2008 compliant), use the intrinsic iso_fortran_env module:
program PEU3
use iso_fortran_env, only: int64
implicit none
integer(kind=int64) :: num = 600851475143_int64
For older compilers, you can use the selected_int_kind method to find out the best integer kind. It takes a single parameter: The maximum number of (base 10) digits to store. (Though technically, if you pass it the number 12, it would only guarantee the numbers between -10^12 ... 10^12, so you'd have to pass 13 to be certain that your number can be stored.)
integer, parameter :: largeint = selected_int_kind(13)
integer(kind=largeint) :: num = 600851475143_largeint
Both of these methods are more easily readable and compiler-independent, so much easier to port to a new system.
I need to convert a parameter from integer(kind=8) to integer(kind=4) in Fortran, is there any (simple) way of doing this?
This parameter is an input number, and if this number is greater than 2^31-1 (the limit of a 4-byte integer), the program will always ask for a smaller number (so it can "fit" inside those 4 bytes), so I think that this shouldn't be a problem.
To create integer of any kind use
result = int(source, kind=result_kind)
so you can do
result = int(source, 4)
source can be any number, including an integer of any kind.
Note that kind=8 does not mean 8 bytes and kind=4 does not mean 4 bytes. There are compilers which do not have kinds 4 and 8 at all. These numbers are not portable. Do not use them. See Fortran: integer*4 vs integer(4) vs integer(kind=4) for more details.
As Vladimir F's answer notes, the intrinsic function int returns an integer value of desired kind int(i,kind=kind).
When an expression of a certain kind is required (such as in a procedure argument list) this is quite useful:
call sub_with_int_i1_arg(INT(int_i2, kind=i1))
However, intrinsic assignment to an integer already provides conversion if required:
integer, parameter :: kind1=7, kind2=23
integer(kind1) :: i
integer(kind2) :: j = 85
! The intrinsic assignment
i = j
! is equivalent to
i = INT(j,KIND(i))
! which here is also
i = INT(j,kind1)
end
The intrinsic huge may be useful in determining whether the range of i is large enough:
if (ABS(j).le.HUGE(i)) then
i = j
else
error stop "Oh noes :("
end if
As Steve Lionel commented about the draft, Fortran 2018 introduced the intrinsic function out_of_range which also tests such cases:
if (.not.OUT_OF_RANGE(j,i)) then
i = j
else
error stop "Oh noes :("
end if
However, even in early 2022 it's not wise to rely on implementation of this function.
How can I read and write to the standard input, output and error streams stdin, stdout and stderr in Fortran? I've heard writing to stderr, for example, used to be write(5, fmt=...), with 5 the unit for stderr, and I know the way to write to stdout is to use write(*, fmt=...).
How do I read and write to the standard input and output units with the ifort compiler?
Compiler version:
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 10.0 Build 20070426 Package ID: l_fc_p_10.0.023 Copyright (C) 1985-2007 Intel Corporation. All rights reserved
If you have a Fortran 2003 compiler, the intrinsic module iso_fortran_env defines the variables input_unit, output_unit and error_unit which point to standard in, standard out and standard error respectively.
I tend to use something like
#ifdef f2003
use, intrinsic :: iso_fortran_env, only : stdin=>input_unit, &
stdout=>output_unit, &
stderr=>error_unit
#else
#define stdin 5
#define stdout 6
#define stderr 0
#endif
in my input/output routines. Although this of course means preprocessing your source file (to do this with ifort, use the -fpp flag when compiling your source code or change the source file extension from .f to .F or from .f90 to .F90).
An alternative to this would be to write your own, non-intrinsic, iso_fortran_env module (if you don't have a Fortran 2003 compiler), as discussed here (this link has died since this answer was posted). In this example they use a module:
module iso_fortran_env
! Nonintrinsic version for Lahey/Fujitsu Fortran for Linux.
! See Subclause 13.8.2 of the Fortran 2003 standard.
implicit NONE
public
integer, parameter :: Character_Storage_Size = 8
integer, parameter :: Error_Unit = 0
integer, parameter :: File_Storage_Size = 8
integer, parameter :: Input_Unit = 5
integer, parameter :: IOSTAT_END = -1
integer, parameter :: IOSTAT_EOR = -2
integer, parameter :: Numeric_Storage_Size = 32
integer, parameter :: Output_Unit = 6
end module iso_fortran_env
As noted in other answers, 0, 5 and 6 are usually stderr, stdin and stdout (this is true for ifort on Linux) but this is not defined by the Fortran standard. Using the iso_fortran_env module is the correct way to portably write to these units.
The Fortran standard doesn't specify which units numbers correspond to stdin/out/err. The usual convention, followed by e.g. gfortran, is that stderr=0, stdin=5, stdout=6.
If your compiler supports the F2003 ISO_FORTRAN_ENV intrinsic module, that module contains the constants INPUT_UNIT, OUTPUT_UNIT, and ERROR_UNIT allowing the programmer to portably retrieve the unit numbers for the preconnected units.
It's actually 0 for stderr. 5 is stdin, 6 is stdout.
For example:
PROGRAM TEST
WRITE(0,*) "Error"
WRITE(6,*) "Good"
END PROGRAM TEST
Gives:
./a.out
Error
Good
while
./a.out 2> /dev/null
Good
I would store a PARAMETER that is STDERR = 0 to make it portable, so if you hit a platform that is different you can just change the parameter.
This example was compiled and run with ifort 12.1.1.256, 11.1.069, 11.1.072 and 11.1.073.
The standard way to write to stdout in Fortran is to put an asterisk instead of the unit number, i.e.,
WRITE(*,fmt) something
or to simply use
PRINT fmt,something
Similarly, the standard way to read from stdin is
READ(*,fmt) something
There is no standard way to write to stderr unless you use ERROR_UNIT from the ISO_FORTRAN_ENV module, which requires Fortran 2003 or later.
Unit numbers 0, 5 and 6 will certainly work in the ifort compiler (and also in some other Fortran compilers), but keep in mind they are compiler-dependent.