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.