Upgrading from f77 to f90
Upgrading from f77 to f90
Charles Karney
PPPL
2000-01-17
INTRODUCTION
f90 was a large change to the specification of Fortran (relative to f77)
and it has taken a long time for high quality f90 compilers to appear on
all platforms.
Now you may (nearly) assume that f90 is available wherever f77 is
supported. Since f90 includes f77 as a subset it is relatively easy to
make the transition to using f90.
An important advantage to f90 for existing f77 programs is to make your
code more portable since f90 provides standard ways of doing what required
extensions in f77.
Reference: Cooper Redwine, Upgrading to Fortran 90, (Springer, 1995)
OVERVIEW FOR NEW FEATURES
* Standardized existing non-standard extensions
I/O: STATUS='REPLACE', POSITION='APPEND', NAMELIST, INCLUDE
Intrinsics: SYSTEM_CLOCK, DATE_AND_TIME, FLOOR, CEILING, RANDOM_NUMBER
Memory allocation
Specification of precision
* Arrays
array syntax
assumed shape arrays
Intrinsics: DOT_PRODUCT, TRANSPOSE, MATMUL
* Control constructs
CASE, DO WHILE, EXIT (from DO loops)
* Free form and other syntactic sugar (<, <=, ==, /=, >, >=)
* Interface specifications
* Modules
* Derived types
* Pointers
MEMORY ALLOCATION
The easy case (automatic array)
subroutine subr(a,n)
implicit none
integer n
real a(n)
real work(n,n) ! memory is allocated at subroutine entry
... ! and deallocated at subroutine exit
end subroutine subr
The next case (allocatable array)
program prog
implicit none
real, allocatable, dimension(:,:) :: work
integer :: n
...
read(...) n ! Figure out the size of the arrays
allocate(work(0:n-1,0:n-1))
...
deallocate(work)
stop
end program prog
PRECISION
integer, parameter :: default_prec=kind(0.0) ! default precision
integer, parameter :: wp=selected_real_kind(12) ! at least 12 digits
Literals:
3.14159e0_wp
Declaration:
real(wp) :: pi
real(kind(1.0d0)) :: dx ! These two are identical
double precision :: dx
Conversion
x=real(i,wp)
z=cmplx(x,y,xp)
Notes:
if z is complex, REAL(z) and AIMAG(z) preserve precision
if x is a real type, then REAL(x) converts to default precision
CMPLX(x,y) converts to default precision
There's also a "kind" for integer variables
ARRAY SYNTAX
Whole arrays or subarrays may be used with standard operators and intrinsic
functions. Requires arrays to be the same shape. Operations are all
element-wise.
real, dimension(m,n) :: x, y
real, dimension(m,m) :: z
real :: a
...
x=2 ! Assignment of scalar
y=x ! Array assignment
x(3:5,:)=(/ 1, 2, 3 /) ! A literal for a 1d array
x=y+sin(x) ! Array operations
y=x*y ! Element-wise multiplication
z=matmul(x,transpose(y)) ! Matrix multiplication
a=dot_product(z(1,:),z(:,1)) ! Vector dot product
where (x < 0) y=1 ! Masked assignment
Read the book
for more general array constructors
for more information on WHERE
for information on RESHAPE
ASSUMED SHAPE ARRAYS
subroutine sub(x)
implicit none
real, dimension(:,:) :: x
! Only the rank (and the lower bounds) are specified
integer :: i,j
do j=lbound(x,2),ubound(x,2)
do i=lbound(x,1),ubound(x,1)
x(i,j)=...
end do
end do
! or else use array operations
x=x+1
end subroutine sub
SIZE(x,1) = UBOUND(x,1)-LBOUND(x,1)+1
SIZE(x) returns total number of elements
UBOUND(x) and LBOUND(x) return 1d arrays of bounds
PASSING SUBARRAYS TO SUBROUTINES (F77 AND F90)
implicit none
real x(m,n)
interface
subroutine sub77(x,m,m1,n,...)
integer m,m1,n
real, dimension(m1,n) :: x ! assumed SIZE
end subroutine sub77
subroutine sub90(x,...)
real, dimension(:,:) :: x ! assumed SHAPE
end subroutine sub90
end interface
call sub90(x(1:ma,1:na), ...) ! f90-style
call sub77(x(1,1),ma,m,na, ...) ! f77-style with array element
call sub77(x,ma,m,na, ...) ! f77-style with array name
call sub77(x(1:ma,1:na),ma,ma,na, ...) ! inefficient
! since requires a COPY (see below)
Notes:
meaning of sub90 call is clear
sub77 call is verbose and obscure
(is the first arg a scalar or an array?)
interface REQUIRED for sub90
sub77 convention is REQUIRED when invoking f77 libraries
(blas, nag, lapack)
NON-CONTIGUOUS ARRAYS
It is possible to include a stride on arrays
a(1:n:2)
indicates the odd numbered elements of a. Similarly
x(4,:)
is a one-dimensional array denoting the 4th row of a two-dimensional array.
In both these cases, the array elements are not CONTIGUOUS.
Passing these array slices to subroutines whose dummy arguments are
assumed shape arrays is efficient.
However, non-contiguous arrays must be copied to a temporary contiguous
array when passed as an assumed size array.
Sometimes arrays are copied unnecessarily -- complain when this happens.
INTERFACES
In the declaration part of a subroutine (or preferably within a module)
interface
subroutine abc(x,y,x)
declaration statements from definition of abc
end subroutine abc
...
end interface
Advantages:
compile-time checking of subroutine calls
Required for
subroutines with assumed shape arrays
subroutines with optional arguments
generic procedures
use of keyword calling
functions that return arrays, etc.
INTENT
When a subprogram declares variables, the programmer can specify
INTENT(IN)
INTENT(OUT)
INTENT(INOUT) ! The default
The calling routine can be told of these intents via an interface block.
Allows the compiler to perform error checking both when the subprogram is
compiled and where the subprogram is invoked.
In addition, the compiler may be able to produce more efficient code (both
for the subroutine and its callers).
MODULES
A COMMON block is a mechanism of sharing data of predetermined extents.
A MODULE is an extension which allows sharing of allocatable arrays, data
initializations, definitions of parameters, derived types, interfaces,
subroutines.
Example
program testrng
use rng
implicit none
integer, parameter :: double=selected_real_kind(12)
integer, parameter :: single=selected_real_kind(6)
type(rng_state) :: state ! derived type rng_state
integer, dimension(rng_s) :: seed ! parameter rng_s
real(double),dimension(1234) :: xa
real(single),dimension(2,3,5) :: ya
call rng_set_seed(seed) ! generic interface for rng_set_seed
print *, rng_print_seed(seed) ! return type for rng_print_seed
call rng_set_seed(seed,(/1776,07,04,-300,12,0,0,0/))
call rng_set_seed(seed,123456789)
call rng_set_seed(seed,'123456789')
call rng_init(seed,state)
call rng_number(state,xa) ! generic interface for rng_number
call rng_number(state,ya(2,:,::2))
...
end program testrng
Module defines:
derived type: rng_state
parameter: rng_s
generic interfaces: rng_set_seed, rng_number
function result: rng_print_seed
Module definition:
module rng
implicit none
integer, parameter :: rng_double=selected_real_kind(12)
integer, parameter :: rng_single=selected_real_kind(6)
integer, parameter :: rng_k=100, rng_s=8, rng_c=34
type :: rng_state
real(rng_double), dimension(0:rng_k-1) :: array
integer :: index
end type rng_state
interface rng_number
subroutine rng_number_d0(state,x)
implicit none
real(rng_double), intent(out) :: x
type(rng_state), intent(inout) :: state
end subroutine rng_number_d0
...
end interface
...
interface
function rng_print_seed(seed)
implicit none
integer, dimension(rng_s), intent(in) :: seed
character(len=rng_c) :: rng_print_seed
end function rng_print_seed
end interface
end module rng
FORTRAN 90 features to simplify library APIs
Module inclusion is simple and general
use rng
Passing arrays is simplified since array dimensions need not be passed to
subroutines.
Generic interface allows related functions to be grouped together.
Arguments can be made optional and given default values.
Actual arguments to subroutine can be specified by keyword
call ode(FUNCTION=f,METHOD='RK',TOLERANCE=1.0e-12,...)
Generic interface and optional arguments allow features to be added to a
library, without the need to change existing codes (a recompile is needed
though).
Definitions of derived types may be extended without the need to change
existing codes.
Interfaces ensure that subroutines are called correctly.
COMPILING
Convention is
prog.f is f77 or f90 in fixed format.
prog.f90 is f90 in free format
Suggest putting module definitions into a separate file (or files), e.g.,
suppose the module rng is defined in the file
rngmod.f90
and that module rng is used in testrng.f90
Compiling this file with
f90 -c rngmod.f90
f90 -c -em rngmod.f90 ! -em is needed on Cray
produces rngmod.o and
rng.mod or RNG.mod
Tell f90 to look in the current directory "." for module files with the
make rule
testrng.o: testrng.f90 rng.o
f90 -M. -c $< ! Sun
f90 -p. -c -em $< ! Cray
f90 -I. -c $< ! Digital, SGI, NAG, Portland Group
The dependency on rng.o causes the module to be compiled first.
Finally the program is built by linking together all the .o files:
testrng: testrng.o rng.o
f90 -o $@ $^
OTHER OBSERVATIONS
On most platforms you can link together f77-compiled and f90-compiled
objects files. However:
On Sun, use f90 to do the linking and include
-lf77compat
With PGI compilers, f77 and f90 don't inter-operate. SO use f90 instead of
f77.
object files produced by different f90-compilers on a given platform (e.g.,
NAG and PGI compilers under Linux) don't inter-operate because
.mod files are incompatible
the run time libraries (for I/O etc.) are incompatible
There's no clearly established convention about where the .mod files for a
specific package belong. Possibilities are
-M/usr/local/include
-M/usr/local/include/<package>
Original accessible as: http://charles.karney.info/misc/f90-sem.txt
Print with: enscript --margins=::72:72 -B -r -f Courier14
Charles Karney (2000-01-17)
Back to index.