Introduction to Modern Fortran

History

Fortran Development

  • First version 1954
  • FORTRAN 66
  • FORTRAN 77
  • Fortran 90 – Backwards compatible
  • (The F programming language)
  • Fortran 95
  • Fortran 2003, (2004)
  • Fortran 2008, (2010)
  • Coming Fortran 2018 (Minor revision)

Pascal

program tpk(input,output);
var     
  i : integer;
  y : real;
  a : array [0..10] of real;

function f ( t : real) : real;
begin
  f := sqrt(abs(t)) + 5*t*t*t
end;

begin
  for i := 0 to 10 do read(a[i]);
  for i := 10 downto 0 do
  begin
    y := f(a[i]);
    if y  >  400 then 
      writeln(i,' TOO LARGE')
    else 
      writeln(i,y);
    end;
  end;
end.

C

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double f (double t)
{
   double temp;
   temp = sqrt(fabs(t)) + 5*pow(t,3);
   return temp;
}

int main()
{
   int i;
   double y;
   double a[11];
   for ( i = 0; i <= 10; ++i)
      scanf("%lf", &a[i]);
   for ( i = 10; i >= 0; i = i - 1 ) {
      y = f(a[i]);
      if ( y > 400 ) {
         printf(" %d",i); 
         printf(" TOO LARGE\n");
      }
      else {
         printf(" %d",i); 
         printf(" %lf",y); 
         printf(" \n");
      }
   }
   return 0; 
}

FORTRAN 0

        DIMENSION A(11)
        READ A
2       DO 3,8,11 J=1,11
3       I=11-J
        Y=SQRT(ABS(A(I+1)))+5*A(I+1)**3
        IF (400>=Y) 8,4
4       PRINT I,999.
        GOTO 2
8       PRINT I,Y
11      STOP

FORTRAN I

C       FORTRAN I STYLE
        FUNF(T)=SQRTF(ABSF(T))+5.0*T**3
        DIMENSION A(11)
    1   FORMAT(6F12.4)
        READ 1,A
        DO 10 J=1,11
        I=11-J
        Y=FUNF(A(I+1))
        IF(400.0-Y)4,8,8
    4   PRINT 5,I
    5   FORMAT(I10,10H TOO LARGE)
        GOTO 10
    8   PRINT 9,I,Y
    9   FORMAT(I10,F12.7)
   10   CONTINUE
        STOP 52525

FORTRAN I

C       FORTRAN IV STYLE
        DIMENSION A(11)
        FUN(T) = SQRT(ABS(T)) + 5.0*T**3
        READ (5,1) A
    1   FORMAT(5F10.2)
        DO 10 J = 1, 11
                I = 11 - J
                Y = FUN(A(I+1))
                IF (400.0-Y) 4, 8, 8
    4                    WRITE (6,5) I
    5                    FORMAT(I10, 10H TOO LARGE)
                GO TO 10
    8                    WRITE(6,9) I, Y
                         FORMAT(I10, F12.6)
   10     CONTINUE
        STOP
        END

FORTRAN IV / Fortran 66

C       FORTRAN IV STYLE
        DIMENSION A(11)
        FUN(T) = SQRT(ABS(T)) + 5.0*T**3
        READ (5,1) A
    1   FORMAT(5F10.2)
        DO 10 J = 1, 11
                I = 11 - J
                Y = FUN(A(I+1))
                IF (400.0-Y) 4, 8, 8
    4                    WRITE (6,5) I
    5                    FORMAT(I10, 10H TOO LARGE)
                GO TO 10
    8                    WRITE(6,9) I, Y
                         FORMAT(I10, F12.6)
   10     CONTINUE
        STOP
        END

FORTRAN 77

        PROGRAM TPK
C       FORTRAN 77 STYLE
        REAL A(0:10)
        READ (5,*) A
        DO 10 I = 10, 0, -1
                Y = FUN(A(I))
                IF ( Y .LT. 400) THEN
                        WRITE(6,9) I, Y
    9                   FORMAT(I10, F12.6)
                ELSE
                        WRITE (6,5) I
    5                   FORMAT(I10,' TOO LARGE')
                ENDIF
   10   CONTINUE
        END 

        REAL FUNCTION FUN(T)
        REAL T
        FUN = SQRT(ABS(T)) + 5.0*T**3
        END

Fortran 90/95

program sample1

	integer, parameter :: ap=selected_real_kind(15,300)
	real(kind=ap) :: x, y
	real(kind=ap) :: k(20,20)

	x = 6.0_ap
	y = 0.25_ap

	write(*,*) x
	write(*,*) y
	write(*,*) ap

    call myproc(k)

	write(*,*) k(1,1)

contains

subroutine myproc(k)
	
	integer, parameter :: ap=selected_real_kind(15,300)
	real(kind=ap) :: k(20,20)

	k=0.0_ap
	k(1,1) = 42.0_ap

	return

end subroutine myproc

end program sample1

Source form

Source form

Free form Fixed form
Source format Max 132 characters Code in positions 7-72, line numbers in position 2-5
Comments ! anywhere on a line !, C or * at position 1
Continuation & at end of line character at position 6 on continuation line
Multiple statements / line ; between statements On statement per row

Example of Fortran code

program example

	implicit none

	integer, parameter :: 	ap=selected_real_kind(15,300)
	real(ap) :: x,y
	real(ap) :: K(20,20)

	x = 6.0_ap   ! This is line comment

	y =               &
		0.25_ap + &
		0.25_ap

	write(*,*) x; write(*,*) y
	write(*,*) ap

	call myproc(K)

	stop

contains 

    subroutine myproc(K)

        real(ap) :: K(:,:)

        K = 0.0_ap

    end subroutine 

end program example

Program structure

program program-name]
[specication statements]
[executable statements]
[contains]
[subroutines]
end [program [program-name]]

Compiling and running Fortran code

What is a program

  • Executable code - machine code
  • Links to system libraries for interacting with operating system and file system
  • Links to external libraries

How to create a program

  • Handcoding machine code (HARD)
  • Writing assembler source code
    • compiling source to machine code (hard)
  • Using Fortran/C/C++ or any other higher language
    • compiling source to machine code (easier)
  • Using Python
    • compiles and executes each line

Making an executable

  • Write source code
  • Compile source code to object files (unlinked machine code)
  • Link object files and libraries to an executable

Source (.f90)

Source (.f90)

Source (.f90)

Object code (.o)

Object code (.o)

Object code (.o)

Executable

Libraries (.so)

Getting out our tools

$ ml foss/2019a
$ ml

Currently Loaded Modules:
  1) GCCcore/8.2.0      8) libpciaccess/0.14
  2) binutils/2.31.1    9) hwloc/1.11.11
  3) GCC/8.2.0-2.31.1  10) OpenMPI/3.1.3
  4) zlib/1.2.11       11) OpenBLAS/0.3.5
  5) numactl/2.0.12    12) FFTW/3.3.8
  6) XZ/5.2.4          13) ScaLAPACK/2.0.2-OpenBLAS-0.3.5
  7) libxml2/2.9.8     14) foss/2019a
  
$ gfortran --version
GNU Fortran (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Loading required modules

Compiling Fortran code

  • Write source code - use .f90 extension
  • Compile a Fortran source to an object file with:
    • gfortran -c mysource.f90
      • generates the object file mysource.o
  • multiple source files can be given
    • gfortran -c source1.f90 source2.f90
      • generates the object files source1.o and source2.o

Linking Fortran code

  • Linking is done using
    • gfortran mysource.o
      • generates an executable a.out
  • Executable can be given a name
    • gfortran mysource.o -o myexec
      • generates an executable myexec

Compiling and linking in a single step

  • Compiling and linking can be done in a single step
    • gfortran mysource.f90 -o myexec
  • Multiple source files can be compiled in a single step
    • gfortran mysource.f90 source1.f90 source2.f90 -o myexec

Variables

Naming of variables

  • 1-31 alphanumeric characters (letters, numbers and underscore)
  • Variable names can consist of both upper case and lower case characters.
  • Variables are case insensitive A and a are references to the same variable
  • First character can't be a number

Valid variable names

Invalid variable names

1a First char a number
a thing Contains a space
_ only non-alphanumeric char
a
a_thing
x1
mass
q123
time_of_flight

Variable types

  • integer, integers
  • real, floating point numbers
  • complex, complex numbers
  • logical, boolean variables
  • character, strings and characters

Variable declaration

type [[,attribute]... ::] entity-list

Variable declaration

integer :: a ! Scalar integer variable
real :: b ! Scalar floating point variable
logical :: flag ! boolean variable

real :: D(10) ! Floating point array consisting of 10 elements
real :: K(20,20) ! Floating point array of 20x20 elements

integer, dimension(10) :: C ! Integer array of 10 elements

character :: ch ! Character
character, dimension(60) :: chv ! Array of characters
character(len=80) :: line ! Character string
character(len=80) :: lines(60) ! Array of strings

Constants

integer, parameter :: A = 5 ! Integer constant
real :: C(A)                ! Floating point array where
                            ! the number of elements is
                            ! specified by A

Variable precision

  • Precision can be specfied as an extra parameter for the data type
  • Precision specifiers dependent on compiler and system architecture
  • special directives can be used to specify precision independent of architecture
    • selected_real_kind
    • selected_int_kind

Variable precision

real(8) :: A
real(4) :: B
integer(4) :: I
integer, parameter :: ap = selected_real_kind(15,300)
real(kind=ap) :: X,Y

Using selected_real_kind

explicit precision specifiers

selected_real_kind

integer, parameter :: ap = selected_real_kind(15,300)

significant decimals

10^{-300}
10^{300}

to

! variable declaration with precision specifier

X = 6.2_rk

Example

program constants

    implicit none
    
    integer, parameter :: ap = selected_real_kind(15,300)
    real(ap) :: pi1, pi2

    pi1 = 3.141592653589793
    pi2 = 3.141592653589793_ap
    
    write(*,*) 'pi1 = ', pi1
    write(*,*) 'pi2 = ', pi2
    
    stop

end program constants
pi1 = 3.14159274101257
pi2 = 3.14159265358979

General type rule

  • By default variables does not have to be declared in Fortran
  • The general type rule defines
    • Variables starting with I..N are integer
    • Other variables are real by default
  • This can lead to difficult bugs and should be avoided
  • By placing the directive implicit none in the beginning of a program or module forces declaration of all variables.

Variable assignment

Variables are assigned with the equal operator (=)

real(rk) a = 42.0_rk
integer(ik) b = 42_ik
logical c = .false.
d = .true.
character first_name = 'Jan'
last_name = "Johansson"
company_name1 = "McDonald's"
company_name2 = 'McDonald''s'

Defined and undefined variables

  • A variable is said to be undefined until it has been assigned a value
    • real :: speed ! variable speed exists, but is undefined
  • An undefined variable must not be referenced
  • When a variable is assigned a value it is considered defined
    • speed = 42.0 ! variable speed is defined
    • It can now be referenced by other expressions
  • An array is said to be defined when all elements have been assigned values

Derived data types

  • In some cases it can be benificial to define your own datatypes
    • Derived data types
  • Can contain
    • Standard variable types
    • Arrays
    • Other derived data types
  • A derived data type is defined by a type-statement block listing the included data members and type
  • Declared using the type keyword
    • type([data type]) :: variable 

Derived data types

program derived1

    use mf_utils

    implicit none

    type particle
        real(dp) :: x
        real(dp) :: y
        real(dp) :: z
        real(dp) :: m
    end type particle

    type(particle) :: p

    p % x = 0.0_dp
    p % y = 1.0_dp
    p % z = 2.0_dp
    p % m = 3.0_dp

    print*, p % x
    print*, p % y
    print*, p % z
    print*, p % m

end program derived1

data type definition

variable declaration

assignment using % operator

accessing values using % operator

Derived data types

program derived2

    use mf_utils

    implicit none

    type data_record
        real(dp) :: a
        integer  :: b
        logical  :: c
        character(10) :: name
    end type data_record

    type(data_record) :: d

    d % a = 0.0_dp
    d % b = 1
    d % c = .true.
    d % name = 'fortran'

    print*, d % a
    print*, d % b
    print*, d % c
    print*, d % name

end program derived2

mixed data types

Operators and expressions

Arithmetic operators

** power to
* multiplication
/ division
+ addition
- subtraction

Order of operations

  1. Parentesis ( )
  2. Operations with **
  3. Operations with * or /
  4. Operations with + or -
c = a+b/2   ! is equivalent to a + (b/2)
c = (a+b)/2 ! in this case (a + b) is evaluated and then /2

Relational operators

< .lt. less than
<= .le. less than or equal to
> .gt. greater than
>= .ge. greater than or equal to
== .eq. equal to
/= .ne. not equal to

Logical operators

.and. and
.or. or
.not. not

Integer expressions

Results of divisions will be truncated towards 0

6/3 = 2

8/3 = 2

-8/3 = -2

WARNING

2**3 = 8

2**(-3) = 1/(2**3) = 0

Mixed mode expressions

  • Numeric expressions with operands of differing datatypes
  • Weaker of to datatypes will be coerced to the stronger one
  • Result will be of the stronger type
real :: a
integer :: i
real :: b

b = a*i

Coerced to real

Array expressions

  • Intrinsic operations can also apply to arrays
  • Same shape arrays are assumed
  • One or more operands can be scalar
    • scalar values ”broadcast” to array operands
  • Order of array operations is not specified in the standard
    • Enabling efficient execution on a vector or parallel computer

Array expressions

real, dimension(10,20) :: a, b

real, dimension(5) :: v

a/b          ! Array of shape (10,20), with elements a(i,j)/b(i,j)

v+1.         ! Array of shape (5), with elements v(i) + 1.0

5/v+a(1:5,5) ! Array of shape (5), with elements 5/v(i) + a(i,5)

a.eq.b       ! .true. if a(i,j)==b(i,j) and .false. otherwise

Arrays and matrices

Arrays and matrices

  • Important concepts in scientific and technical applications
  • Efficient datatype in Fortran
  • Can be statically or dynamically allocated (Fortran 90)
  • Supports advanced indexing, slicing and assignment
integer, parameter :: rk = selected_real_kind(15,300)

real(rk), dimension(20,20) :: K ! Matrix 20x20 elements
real(rk) :: Ke(8,8)             ! Matrix 8x8 elements
real(rk) :: fe(6)               ! Array with 6 elements

Array indices

  • By default starting index of an array is 1
  • Can be redefined
! Array with indices 
! 
! [-3, -2, -1, 0, 1, 2, 3]

real(rk) :: idx(-3:3) 

Array assignment

Individual elements

K(5,6) = 5.0

Entire array

K = 5.0

Multiple values in single assignment

real(rk) :: v(5) ! Array with 5 elements

v = (/ 1.0, 2.0, 3.0, 4.0, 5.0 /)

Assigning slices

program index_notation
    
    implicit none

    real :: A(4,4)
    real :: B(4)
    real :: C(4)

    B = A(2,:) ! Assigns B the values of row 2 in A
    C = A(:,1) ! Assigns C the values of column 1 in A

    stop

end program index_notation

ex4

Assigning slices

! Assign row 5 in matrix K the values 1, 2, 3, 4, 5

K(5,:) = (/ 1.0, 2.0, 3.0, 4.0, 5.0 /)

! Assign the array v the values 5, 4, 3, 2, 1

v = (/ 5.0, 4.0, 3.0, 2.0, 1.0 /)

Using slices rows and columns can be assigned in single statements

Array storage

  • Arrays (1, 2 .. more dimensions) are always stored contiguous in memory
  • twodimensional arrays are stored columnwise in memory

Array storage

real :: A(16)
real :: A(8,2)
real :: A(2,8)

Assigning slices

! Assign row 5 in matrix K the values 1, 2, 3, 4, 5

K(5,:) = (/ 1.0, 2.0, 3.0, 4.0, 5.0 /)

! Assign the array v the values 5, 4, 3, 2, 1

v = (/ 5.0, 4.0, 3.0, 2.0, 1.0 /)

Arrays and derived data types

program derived2

    use mf_utils

    implicit none

    type data_record
        real(dp) :: a
        integer  :: b
        logical  :: c
        character(10) :: name
    end type data_record

    type(data_record) :: d(5)
    integer :: i

    do i=1,5
        d(i) % a = 0.0_dp
        d(i) % b = i
        d(i) % c = .true.
        d(i) % name = 'fortran'
    end do

    do i=1,5
        print*, d(i) % b
    end do

end program derived2

derived data type array

Conditional statements

if-statements

if-statement

if (scalar-logical-expr) then
        block
end if

if (scalar-logical-expr) statement

if (scalar-logical-expr1) then
         block1
else if (scalar-logical-expr2) then
         block2
else
         block3
end if

if-statement

program logic

    implicit none
    integer :: x
    logical :: flag
    
    write(*,*) 'Enter an integer value.'
    read(*,*) x
    
    flag = .FALSE.
    if (x>1000) then
        x = 1000
        flag = .TRUE.
    end if

    if (x<0) then
        x = 0
        flag = .TRUE.
    end if

    if (flag) then
        write(*,'(a,I4)') 'Corrected value = ', x
    else
        write(*,'(a,I4)') 'Value = ', x
    end if

end program logic

select statement

select statement

select case (expr)
        case selector
                block
end select

select case (expr)
        case selector
                block
        case default
                block
end select

select statement

program case_sample
    integer :: value
    write(*,*) 'Enter a value'
    read(*,*) value
    
    select case (value)
        case (:0)
            write(*,*) 'Greater than one.'
        case (1)
            write(*,*) 'Number one!'
        case (2:9)
            write(*,*) 'Between 2 and 9.'
        case (10)
            write(*,*) 'Number 10!'
        case (11:41)
            write(*,*) 'Less than 42 but greater than 10.'
        case (42)
            write(*,*) 'Meaning of life or perhaps 6*7.'
        case (43:)
            write(*,*) 'Greater than 42.'
        case default
            write(*,*) 'This should never happen!'
    end select

end program case_sample

Repetitive statements

do-statements

do i = 1, 10
    j = j + i
end do
do i = 2, 30, 4
    j = j + i
end do

start-value

end-value

start-value

end-value

step

loop-variable is not allowed to be modified inside a do-statement

ex11

do-statements

do i=1, 1000
    if (i>50) then
        exit
    else if (i<10) then
        cycle
    end if
    print *,i
end do

Exit do-statement and execute next executable statement after loop.

Continue to next iteration

ex11

do-statements

10
11
12
.

.

.
47
48
49
50

do while statements

do while (scalar-logical-expr)

        block
end do

Execute until a certain condition is fulfilled

do while statements

x = 0.0
do while (x<1.05)
    f = sin(x)
    x = x + 0.1
    write(*,*) x, f
end do
  0.100000001       0.00000000    
  0.200000003       9.98334214E-02
  0.300000012      0.198669329    
  0.400000006      0.295520216    
  0.500000000      0.389418334    
  0.600000024      0.479425550    
  0.700000048      0.564642489    
  0.800000072      0.644217730    
  0.900000095      0.717356145    
   1.00000012      0.783326983    
   1.10000014      0.841471076 

Built-in functions

Mathematical functions

Mathematical functions

Conversion functions

ex24

ex23

Vector- and matrix functions

ex24

ex23

Vector- and matrix functions

real(rk), dimension(20,20) :: A, B, C

C = A/B     ! Division Cij = Aij/Bij
C = sqrt(A) ! Square root Cij = sqrt(Aij)

Functions and operators can be used with arrays.

Example

A stiffness matrix

Fortran implementation

program function_sample
implicit none

integer, parameter :: rk = selected_real_kind(15,300)

integer :: i, j

real(rk) :: x1, x2, y1, y2, z1, z2
real(rk) :: nx, ny, nz
real(rk) :: L, E, A
real(rk) :: Kel(2,2)
real(rk) :: Ke(6,6)
real(rk) :: G(2,6)

! Initiate scalar values

E = 1.0_rk
A = 1.0_rk
x1 = 0.0_rk
x2 = 1.0_rk
y1 = 0.0_rk
y2 = 1.0_rk
z1 = 0.0_rk
z2 = 1.0_rk

Fortran implementation

! Calcuate directional cosines

L = sqrt( (x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2 )
nx = (x2-x1)/L
ny = (y2-y1)/L
nz = (z2-z1)/L

! Calucate local stiffness matrix

Kel(1,:) = (/ 1.0_ap , -1.0_ap /)
Kel(2,:) = (/ -1.0_ap, 1.0_ap /)
Kel = Kel * (E*A/L)

G(1,:) = (/ nx, ny, nz, 0.0_ap, 0.0_ap, 0.0_ap /)
G(2,:) = (/ 0.0_ap, 0.0_ap, 0.0_ap, nx, ny, nz /)

! Calculate transformed stiffness matrix

Ke = matmul(matmul(transpose(G),Kel),G)

! Print matrix

do i=1,6
    write(*,'(6G10.3)') (Ke(i,j), j=1,6)
end do

end program function_sample

Fortran implementation

0.1925 0.1925 0.1925 -.1925 -.1925 -.1925
0.1925 0.1925 0.1925 -.1925 -.1925 -.1925
0.1925 0.1925 0.1925 -.1925 -.1925 -.1925
-.1925 -.1925 -.1925 0.1925 0.1925 0.1925
-.1925 -.1925 -.1925 0.1925 0.1925 0.1925
-.1925 -.1925 -.1925 0.1925 0.1925 0.1925

Program units and subroutines

Main program

[program program-name]

  [specification statements]

  [executable statements]

[contains

  internal-subprograms]

end [program [program-name]]

The end-statement signals the end of the program-unit and also terminates the program execution

A Fortran program can only have one main program

The stop-statement

  • Stops program execution
  • Can be used in all program units
  • "Well-behaved" subroutines should return control to the main program, where error handling and stop-statements are executed
  • A stop-statement can contain stop codes
    • Number or strings
  • Bad coding practice in to have stop-codes in subroutines and functions.

Subroutines

  • External subroutines
    • Placed in separate source files. Implicit interface.
    • Can be implemented in other languages
  • Internal subroutines
    • subroutines contained inside program units, external subroutines and in module subroutines
  • Module subroutine
    • subroutine contained in a module

Subroutines

Subroutines

subroutine subroutine-name[([dummy-argument-list])]

        [argument-declaration]

        ...

        return

end subroutine [subroutine-name]

A subroutine in Fortran has the following syntax

Subroutines

  • All variables passed to a subroutine are passed by reference
    • A arguments to the subroutine refers to the actual variable in the calling code.
  • Subroutine arguments must be declared in the subroutine
  • A subroutine is called using the call statement
  • Control is returned to the calling routine at the end of the subroutine or by calling return.
    • Several return statements can exists in a subroutine

Subroutines

subroutine myproc(a,B,C)
    
    implicit none 

    integer :: a 
    real, dimension(a,*) :: B 
    real, dimension(a) :: C 

    ... 

    return 

end subroutine
integer :: a 
real, dimension(20,02) :: B 
real, dimension(10) :: C 

call myproc(a,B,C)

Dimensions of arrays need to be passed in the call, so that they can be declared correctly in the subroutine.

 

Last dimension not needed to be passed (*) as it is not needed to reference an array.

Functions

type function function-name([dummy-argument-list])

        [argument-declaration]

        ...

        function-name = return-value

        ...

        return

end function function-name

A subroutine in Fortran has the following syntax

Subroutine with a return value

Function

real function f(x) 

    real :: x 

    f = sin(x) 

    return 

end function f
real :: x, y

y = f(x)

Modules

  • Packages
    • global data
    • derived types and associated operations
    • subprograms
    • interface blocks
    • namelist definitions
  • Package everything associated with some kind of task
    • interval arithmetic
    • FEM elements of a certain type …

Modules

module module-name

        [specification statements]

[contains

        module-subprograms]

end [module [program-name]]

module constants
    integer, parameter :: ik6 = selected_int_kind(6)
    integer, parameter :: rk = selected_real_kind(15,300)
end module constants 
use constants

integer(ik6) :: myint
real(rk) :: myreal

Modules

module truss

    integer ...
    real, private ...
    public :: bar2e

contains
	
    subroutine bar2e(...)
	...
	return
    end subroutine bar2e

end module truss

Variables and subroutines can be made private and public (default) using the private and public attributes

Modules

program main

    use truss

    call bar2e(...)

end program main

All public variables, subroutines and datatypes from truss now available in program unit

Arguments of subroutines

  • Paramters passed as reference
    • Changing a parameter in a subroutine affects variable in calling routine
    • Quicker subroutine calls
    • Undesired “sideeffects”
  • Declaration of parameters
    • Don’t have to be the same as the calling routine
    • Last dimension can be left out for arrays.

Subroutines example

program sub_declaration1

    real(8), dimension(4,4) :: A
    integer, dimension(3) :: v
	
    A = 0.0_8
    v = 0
	
    call dowork(A,4,4,v,3)
	
    print *, A(4,4)
    print *, v(3)
	
end program sub_declaration1 
subroutine dowork(A,rows,cols,v,elements)

    integer :: rows, cols, elements
    real(8), dimension(rows*cols) :: A
    integer, dimension(elements)  :: v
	
    A = 42.0_8
    v = 42

    return

end subroutine dowork 

Subroutine interfaces

  • 2 flavors
    • external/implicit interface
    • Internal/explicit interface
  • Implicit (Fortran 77)
    • Separately compiled .f90-files
    • Compiler does not have all information
  • Explicit (Fortran 9x / 200x)
    • Declared in modules, program contain-section or with interface declaration
    • Compiler has all information on the subroutine interface

Subroutine in Fortran 90/95/2003

  • Arrays and vectors can be declared with  (:,:) or (:) dimensions
  • The explicit interface gives the compiler all information to determine the sizes of the parameters
  • Shorter subroutine parameter lists
  • Actual dimensions of the arrays can be queried using the size(array, dimension) function.

Implicit interface

program test	

    real(8), dimension(20,30) :: A
	
    A = 0.0_8
	
    call mysub(A, 20, 30)
	
end program test
subroutine mysub(A,rows,cols)

    integer :: rows, cols
    real(8), dimension(rows,cols) :: A
	
    A = 42.0_8
	
    print *, 'rows = ', size(A,1)
    print *, 'cols = ', size(A,2)
	
    return
	
end subroutine mysub

ex12,13

Module interface

program test	

    use utils
	
    real(8), dimension(20,30) :: A
	
    A = 0.0_8
	
    call mysub2(A)
	
end program test
module utils

contains

subroutine mysub2(A)

    real(8), dimension(:,:) :: A
	
    A = 42.0_8
	
    print *, 'rows = ', size(A,1)
    print *, 'cols = ', size(A,2)

    return
	
end subroutine mysub2

end module utils

Assumed shape array

ex14

Internal subroutine

program test	

    real(8), dimension(20,30) :: A
	
    A = 0.0_8
	
    call mysub3(A)
	
contains 

subroutine mysub3(A)

    real(8), dimension(:,:) :: A
	
    A = 42.0_8
	
    print *, 'rows = ', size(A,1)
    print *, 'cols = ', size(A,2)
	
end subroutine mysub3
	
end program test

Assumed shape array

Explicit interface

As no module or program unit is used interface must be declared explicitely here

program test	

    interface 
        subroutine mysub4(A)
            real(8), dimension(:,:) :: A
        end subroutine mysub4
    end interface

    real(8), dimension(20,30) :: A
	
    A = 0.0_8
	
    call mysub4(A)
	
end program test
subroutine mysub4(A)

    real(8), dimension(:,:) :: A
	
    A = 42.0_8
	
    print *, 'rows = ', size(A,1)
    print *, 'cols = ', size(A,2)
	
    return
	
end subroutine mysub4

ex16

Automatic variables

  • Temporary variables in subroutines
  • Created on the stack
  • Automatically allocated to desired size
  • Must fit in the stack

Automatic variables

program automatic_arrays

    real(8), dimension(20,30) :: A
	
    call mysub(A)
	
contains

subroutine mysub(A)

    real(8), dimension(:,:) :: A
    integer, dimension(size(A,1),size(A,2)) :: B
	
    B = 0
	
    print *, "rows = ", size(B,1)
    print *, "cols = ", size(B,2)
	
end subroutine mysub
	
end program automatic_arrays

B is automatically allocated. size() can be used to query size. More on this later.

Array features

Allocatable arrays

  • Size of arrays not always known at compile time
  • Declared using the allocatable attribute
  • The array rank is defined at declarations, but bounds undefined until array has been allocated
  • Allocate-statements used to allocate the array
  • ”Work arrays” where used in Fortran 77 as a replacement for allocatable arrays
  • Deallocated with deallocate-statement
  • Allocatable arrays automatically deallocated when returning from a function or subroutine

Allocatable arrays

real, dimension(:), allocatable :: f
real, dimension(:,:), allocatable :: K

allocate(f(20))     ! or allocate(f(20), K(20,20)
allocate(K(20,20))

deallocate(f)       ! or deallocate(f, K)
deallocate(K)

ex18

Array subobjects

  • Used to extract fields/arrays from other arrays
  • Can be used as parameters in subroutine calls
  • Compare Matlab’s index notation

Array subobjects

program array_subobjects

    integer, dimension(5,10) :: A
    integer :: i, j
	
    do i=1,5
        do j=1,10
            A(i,j) = (i-1)*10 + j
        end do
    end do

    print *, "Entire array:"	
    call writeArray(A)

    print *, "A(1, 5:10)"
    call writeVector(A(1, 5:10))

    print *, "A(1:5, 5:10)"
    call writeArray(A(1:5, 5:10))
	
    print *, "A(1:5, 5:10)"
    call writeArray(A(1:5, 5:10))

    print *, "A((/1,3/), (/2,4/))"
    call writeArray(A( (/1,3/), (/2,4/) ))

end program array_subobjects

ex22

Array subobjects

 Entire array:
 1 2 3 4 5 6 7 8 9 10
 11 12 13 14 15 16 17 18 19 20
 21 22 23 24 25 26 27 28 29 30
 31 32 33 34 35 36 37 38 39 40
 41 42 43 44 45 46 47 48 49 50
 A(1, 5:10)
 5 6 7 8 9 10
 A(1:5, 5:10)
 5 6 7 8 9 10
 15 16 17 18 19 20
 25 26 27 28 29 30
 35 36 37 38 39 40
 45 46 47 48 49 50
 A(1:5, 5:10)
 5 6 7 8 9 10
 15 16 17 18 19 20
 25 26 27 28 29 30
 35 36 37 38 39 40
 45 46 47 48 49 50
 A((/1,3/), (/2,4/))
 2 4
 22 24

Vector and matrix multiplication

  • Matrix multiplication
    • matmul(A,B), where A and B are matrices
  • Scalarproduct
    • dot_product(a,b), where a and b are vectors

ex24

Array subobjects

program matrix_multiply

    real(8), dimension(4,4) :: A
    real(8), dimension(4,8) :: B
    real(8), dimension(4,8) :: C
	
    call randomMatrix(A)
    call randomMatrix(B)
    call randomMatrix(C)
	
    C = matmul(A,B)
	
    print *, "A = "
    call writeArray(A)
    print *, "B = "
    call writeArray(B)
    print *, "C = "
    call writeArray(C)

end program matrix_multiply

Array subobjects

 A =
.984    .700    .275    .661
.810    .910    .304    .484
.985    .347    .548    .614
.971    .882    .129    .929
 B =
.763    .534    .711E-01.404    .731    .636    .879    .689
.786    .231    .238    .951    .621    .528    .733    .588E-01
.466    .302    .209    .294    .598    .461    .911    .343
.690E-01.619    .305    .326E-01.438    .202    .617    .851
 C =
1.48    1.18    .496    1.17    1.61    1.26    2.04    1.38
1.51    1.03    .486    1.30    1.55    1.23    1.96    1.13
1.32    1.15    .455    .909    1.53    1.19    2.00    1.41
1.56    1.34    .589    1.30    1.74    1.33    2.19    1.56

Reduction routines

  • all
    • True if all element = .true.
  • any
    • True if any element = .true.
  • maxval/minval
    • Max/min value in the field
  • product
    • Product of all elements in the field
  • sum
    • Sum of all elements in the field

ex25

Reduction routines

print *, 'Product of A = ', product(A)
print *, 'Sum of A     = ', sum(A)
print *, 'Max of A     = ', maxval(A)
print *, 'Min of A     = ', minval(A)
 A =
.984    .700    .275    .661
.810    .910    .304    .484
.985    .347    .548    .614
.971    .882    .129    .929

Product of A =  0.00016095765496870103
Sum of A     =  10.534961942117661
Max of A     =  0.9853920286986977
Min of A     =  0.12899155798368156

Information routines

  • lbound
    • Returns lower field index
  • ubound
    • Return upper field index
  • shape
    • Returns the matrix shape in an array [rows, columns]
  • size
    • Returns the size of matrix in a specific  dimension

ex26

Input and output

Reading and writing

  • Reading/writing from files is done using the READ/WRITE-statements
  • One row for each READ/WRITE-statement
  • First parameter determines where to read and write.
    • When reading from standard input or writing to standard output an asterisk (*) kan be used as the first parameter
  • Fortran automatically converts text in a file to typed variable values
  • Several variables can be read in the same statement using a comman separated list of variables
  • To handle a large number of values on a row an implicit loop can be used

Reading and writing

! Read 8 floating point values from unit ir

read(*,*) a(1), a(2), a(3), a(4), a(5), a(6), a(7), a(8)

! or perhaps even better...

read(*,*) (a(i), i=1,8)

! Same procedure for writing

write(*,*) (a(i), i=1,8)

For only writing to standard output, the print-statement can be used.

print*, 'Hello, world!'

ex27

Formatted I/O

  • Second parameter in the WRITE statement defines the output format
  • * represents the default format which often displays at full precision
  • A format parameter is a string describing the output format
  • The format is defined using format codes

Formatted I/O

  • Common codes
    • A[n] – character string with the width n
    • I[n] – integer with the width n
    • G[m.n] – floating point value with width m and n decimals
  • Format codes can be repeated by giving a integer in front of the code
    • “(6I5)” = 6 integers with the width 5

Formatted I/O

program format_print

    real(8), dimension(3) :: a
    integer, dimension(4) :: b
    integer :: i, j
	  
    ! Write table header
	
    a = (/ 1.0, 2.0, 3.0 /)
    b = (/ 4, 5, 6, 7 /)
	
    write(*, '(3A8,4A5)') 'aaaaaaaa', &
        'bbbbbbbb', 'cccccccc', 'ddddd', 'eeeee', 'fffff', &
        'ggggg‘

    ! Write table

    do j=1,10
        write(*, '(3G8.3,4I5)') (a(i),i=1,3), (b(i), i=1,4)
    end do
	
end program format_print

ex28

Formatted I/O

aaaaaaaabbbbbbbbccccccccdddddeeeeefffffggggg
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7
1.00    2.00    3.00        4    5    6    7

"Dynamic" format codes

  • Format strings are static strings
  • How to create a format code that can handle dynamic number of values
  • WRITE can use a character string as a “file”

"Dynamic" format codes

subroutine writeArray(A)
		
    real(8), dimension(:,:) :: A
    integer :: rows, cols, i, j
    character(255) :: fmt
		
    rows = size(A,1)
    cols = size(A,2)
		
    write(fmt, '(A,I1,A)') '(',cols, 'G8.3)'  

    do i=1,rows
        print fmt, (A(i,j), j=1,cols)
    end do
		
    return
	
end subroutine writeArray

String that will contain the final format code.

Write to fmt string using a write statement

Use the format string when printing array

ex29

Reading and writing to file

  • To read and write data to file a file must be associated with a file unit (integer number)
    • The open()-statement is used to accomplish this
    • Also determines if the file should be read, written or appended.
  • There are 3 modes of file operations in Fortran
    • Text I/O - Textfiles
    • Namelist I/O - Textfiles with named variables
    • Unformatted I/O - Binary files
  • When file I/O is completed files needs to be closed
    • Tells the operating environment that a file is not used
    • If files are not closed there is a risk that they won't be written correctly to disk (due to buffering)

Opening files

integer, parameter :: infile = 15
integer, parameter :: outfile = 16

! ---- Open a file for reading

open(unit=infile,file='indata.dat', access='sequential', action='read')

! ---- Read from file

read(infile, *) ...

! ---- Close file

close(infile)

! ---- Open a file for writing

open(unit=outfile,file='utdata.dat', access='sequential', action='write')

! ---- Write to file

write(outfile, *) ...

! ---- Close file

close(outfile)

Texfile mode

File operation

Filename

Reading from file and keyboard

read(*,*) A
read(5,*) A

integer, parameter :: ir=15

open(unit=ir, file=’example.dat’)
read(ir,*) A
close(unit=ir)

Keyboard

File

Writing to file and screen

write(*,*) A
write(6,*) A
write(*,’ (TR1,A,G15.6)’) ’A = ’, A

integer, parameter :: iw=16

open(unit=iw, file=’example.dat’)
write(iw, ’(A,G15.6)’) A
close(unit=iw)

Keyboard

File

Code structuring

Structuring of projects

  • For smaller project modularisation not necessary
  • For larger project it is a must to be able to maintain the code
  • In Fortran 95 the key to modularisation is the module
  • Modules should be used to group related subroutines and data

Code structuring

Example code

program stress

	use inputdata
	use outputdata
	use fem
	use solve

	.
	.

end program stress
module fem 

	use utils

	.
	.

end module fem
module utils

	

	.
	.

end module utils

More on subroutines

Procedures as arguments

  • procedures in Fortran 95 can take procedures as arguments
  • Properties of “dummy” procedure arguments must agree
  • The procedure type is declared in the specification part
  • Internal procedures can’t be used

Procedures as arguments

module utils

	implicit none

        integer, parameter :: dp = selected_real_kind(15,300)

contains

real(dp) function myfunc(x)

	real(dp), intent(in) :: x
	
	myfunc = sin(x)
	
end function myfunc

subroutine tabulate(startInterval, endInterval, step, func)

	real(dp), intent(in) :: startInterval, endInterval, step
	real(dp) :: x
	
	interface 
		real(8) function func(x)
			real(8), intent(in) :: x
		end function func
	end interface
	
	x = startInterval
	do while (x<endInterval) 
		print *, x, func(x)
		x = x + step
	end do
	
	return
end subroutine tabulate

end module utils

Declaration of  function interface. Function used as argument must have the same interface

Procedures as arguments

program procedures_as_arguments

	use utils

	implicit none
	
	call tabulate(0.0_dp, 3.14_dp, 0.1_dp, myfunc)
	
end program procedures_as_arguments

Procedures as arguments

  0.0000000000000000        0.0000000000000000     
  0.10000000000000001       9.98334166468281548E-002
  0.20000000000000001       0.19866933079506122     
  0.30000000000000004       0.29552020666133960     
  0.40000000000000002       0.38941834230865052     
  0.50000000000000000       0.47942553860420301     
  0.59999999999999998       0.56464247339503537     
  0.69999999999999996       0.64421768723769102     
  0.79999999999999993       0.71735609089952268     
  0.89999999999999991       0.78332690962748330     
  0.99999999999999989       0.84147098480789639     
   1.0999999999999999       0.89120736006143531     
   1.2000000000000000       0.93203908596722629     
   1.3000000000000000       0.96355818541719296     
   1.4000000000000001       0.98544972998846025     
   1.5000000000000002       0.99749498660405445     
   1.6000000000000003       0.99957360304150511     
   1.7000000000000004       0.99166481045246857     
   1.8000000000000005       0.97384763087819504     
   1.9000000000000006       0.94630008768741425     
   2.0000000000000004       0.90929742682568149     
   2.1000000000000005       0.86320936664887349     
   2.2000000000000006       0.80849640381958987     
   2.3000000000000007       0.74570521217671970     
   2.4000000000000008       0.67546318055115029     
   2.5000000000000009       0.59847214410395577     
   2.6000000000000010       0.51550137182146338     
   2.7000000000000011       0.42737988023382895     
   ...

Keyword and optional arguments

  • Many procedures have long argument lists
    • All arguments not needed
  • Procedural arguments in Fortran can be given the attribute optional
    • real, optional :: a
  • Optional arguments can be omitted in the procedure call
    • Presence of an argument can be tested with the present function.
  • Arguments can be specified with keywords

Keyword and optional arguments

call order_icecream(2)
call order_icecream(2, 1)
call order_icecream(4, 4, 2)
call order_icecream(4, topping=3)
subroutine order_icecream(number, flavor, topping)
	
	integer :: number
	integer, optional :: flavor
	integer, optional :: topping
	
	print *, number, 'icecreams ordered.'
	
	if (present(flavor)) then
		print *, 'Flavor is ', flavor
	else
		print *, 'No flavor was given.'
	end if
	
	if (present(topping)) then
		print *, 'Topping is ', topping
	else
		print *, 'No topping was given.'
	end if
	
end subroutine order_icecream

Overloading

  • Procedure call to different procedures depending on datatype
  • Declare an interface with procedural interfaces for all used datatypes
  • Explicit interfaces (modules) can use a simpler form
    • module procedure procedure-name-list

Overloading

module special

interface func
	module procedure ifunc, rfunc
end interface

contains

integer function ifunc(x)
	
	integer, intent(in) :: x
	ifunc = x * 42
	
end function ifunc

real(dp) function rfunc(x)
	
	real(dp), intent(in) :: x
	rfunc = x / 42.0_dp
	
end function rfunc

end module special 
program overloading

	use special
	
	implicit none
	
	integer :: a = 42
	real(dp) :: b = 42.0_dp
	
	a = func(a)
	b = func(b)
	
	print *, a
	print *, b 
	
end program overloading
 1764
  1.00000000000000000 

Operator overloading

  • Define operations on derived types
  • Vector / Matrix operations
  • interface operator(operator) used to define a function implementing the operations for this operator
  • Function has the form
    • type(derived-type-name) function-name(operand1, operand2)

Operator overloading

module vector_operations

        integer, parameter :: dp = selected_real_kind(15,300)

	type vector
		real(dp) :: components(3)
	end type vector
	
	interface operator(+)
		module procedure vector_plus_vector
	end interface

contains

type(vector) function vector_plus_vector(v1, v2)

	type(vector), intent(in) :: v1, v2
	vector_plus_vector%components = v1 % components + v2 % components
	
end function vector_plus_vector

end module vector_operations

Operator overloading

program operator_overloading

    use vector_operations

    implicit none

    type(vector) :: v1
    type(vector) :: v2
    type(vector) :: v
	
    v1 % components = (/1.0, 0.0, 0.0/)
    v2 %components = (/0.0, 1.0, 0.0/)
	
    v = v1 + v2
	
    print *, v

end program operator_overloading
1.00000000000000000  1.00000000000000000   0.0000000000000000

Public and private attributes

  • Control visibility of variables and functions
  • Hide module implementation details
  • Prevent side effects in modules
  • Visibility of variables controlled by private and public attributes
  • procedure visibility controlled by
    • private access-list
  • public is the default for all module variables and procedures

Public and private attributes

module mymodule

    implicit none

    integer, public :: visible
    integer, private :: invisible
	
    private privatefunc
    public publicfunc
	
contains

subroutine privatefunc

    print *, 'This function can only be called from within a module.'
		
end subroutine privatefunc

subroutine publicfunc

    call privatefunc
	
end subroutine publicfunc

end module mymodule 

Public and private attributes

program private_entities

    use mymodule
	
    implicit none
	
    call publicfunc
	
end program private_entities
program private_entities

    use mymodule
	
    implicit none
	
    call privatefunc
	
end program private_entities

Works as publicfunc is public

make all 
gfortran -g -fbounds-check -Wall -Wtabs -c main.f90
gfortran  -o private_entities mymodule.o main.o 
main.o: In function `private_entities':
D:\edu\.../main.f90:7: undefined reference to `privatefunc_'
collect2: ld returned 1 exit status
make: *** [private_entities] Error 1

Doesn't work as privatefunc is private

Pointers

Pointers

  • Pointer = reference to a variable
  • Strictly typed
  • Pointer targets must have target attribute
    • For compiler optimisation
  • Enables the return of allocatable arrays from procedures
  • nullify(pointer) disassociates pointer from target
  • => operator is used to associated a pointer with a target

Pointers

program pointers

    implicit none
	
    integer, allocatable, dimension(:,:), target :: A
    integer, dimension(:,:), pointer :: B, C
    	
    allocate(A(20,20))
	
    B => A
	
    print *, size(B,1), size(B,2)
	
    call createArray(C)
	
    print *, size(C,1), size(C,2)
	
    deallocate(C)
	
    B => null()
	
    B(1,1) = 0 ! Dangerous!
	
contains

subroutine createArray(D)

    integer, dimension(:,:), pointer :: D
	
    allocate(D(10,10))
	
end subroutine createArray
	
end program pointers

B points to A

B can be queried just like a normal array

An unassociated pointer can be passed to a subroutine and be returned as an allocated array

Advanced I/O

List directed I/O

  • Fortran uses a specific format for reading and writing variables to files
  • Variables output from write(*,*) are separated with space/blanks
    • Complex numbers surrounded by ( )
    • Strings must be enclosed in ‘ ‘ or “ “ when output
  • Reading variables require the same format

List directed I/O

program list_io

	implicit none
	
	integer, parameter :: ir = 15
	integer, parameter :: iw = 16
	
	integer :: a = 42
	real :: b = 42.0 * 42.0
	character(len=20) :: string = 'Hello, string!'
	logical :: topping = .true.
	complex :: c = (1.0,2.0)
	
	open(unit=iw, file='list.txt', status='replace')
	write(iw,*) a, b, '"',string,'"', topping, c
	close(unit=iw)
	
	open(unit=ir, file='list.txt', status='old')
	read(ir,*) a, b, string, topping, c
	close(unit=ir)
	
	print *, a
	print *, b
	print *, topping
	print *, c
	
end program list_io
 42   1764.0000     "Hello, string!      " F ( 1.00000000    ,  2.0000000    )

.true. and .false. can also be used as input for logical variables

Namelist I/O

  • Reading list of named values
  • Value names defined in a namelist
  • Input must consist of lists of named value pairs separated with comma
    • & name = value, name = value /
  • Can contain arrays
    • name = 1,2,3,4,5
  • Fortran can also write named values

Namelist I/O

program namelist_io

	implicit none
	
	integer, parameter :: ir = 15
	integer, parameter :: iw = 16
	integer :: no_of_eggs, litres_of_milk, kilos_of_butter, list(5)
	namelist /food/ no_of_eggs, litres_of_milk, kilos_of_butter, list
	
	list = 0
	
	open(unit=ir, file='food.txt', status='old')
	read(ir, nml=food)
	close(unit=ir)
	
	print *, no_of_eggs, litres_of_milk, kilos_of_butter
	
	open(unit=iw, file='food2.txt', status='new')
	write(iw, nml=food)
	close(unit=iw)

end program namelist_io

namelist defined variables to be read from file

namelist defined variables to be written to file.

Unformatted I/O

  • Writing data using a textual representation not efficient in all cases
  • No format needed, binary output
    • read(unit=xx) a
    • write(unit=xx) b
  • Output from unformatted I/O often system dependent
  • Scratchfiles and temporary files

Direct access files

  • Used when a random access pattern is needed
  • File consists of a set of equal sized records
  • Any record in the file can be accessed randomly
  • If the record is changed data cannot be read back from file
  • Size of a record can be found using inquire(iolength=variable-name) recordLength

Direct access files / unformatted I/O

program unformatted_io

implicit none
	
    type account
        character(len=40) :: account_holder
        real :: balance
    end type account
	
    integer, parameter :: iw = 15
    type(account) :: accountA
    type(account) :: accountB
    integer :: recordSize
	
    inquire(iolength=recordSize) accountA
	
    print *, 'Record size =',recordSize
	
    accountA % account_holder = 'Olle'
    accountA % balance = 400
	
    accountB % account_holder = 'Janne'
    accountB % balance = 800
	
    open(unit=iw, file='bank.dat', access='direct', recl=recordSize, status='replace')
    write(iw, rec=1) accountA
    write(iw, rec=2) accountB
    close(unit=iw)

end program unformatted_io

Derived datatypes good candidates for storing as records in a random access file

Inquire for the required record length

access=’direct’ creates a direct access file.

The rec attribute determines the location to store/read the record

File positioning

  • File pointer – Invisible cursor in the file
  • backspace unit
    • Move file pointer back 1 record
  • rewind unit
    • Move file pointer to beginning of file
  • endfile unit
    • Move file pointer to the end of the file.

Error handling in file operations

  • open, close, read and write have an err attribute that can be set to a specific label to jump to when an error occurs
  • Well behaved programs should implement error handling when reading and writing files

Error handling in file operations

program error_handling

    implicit none
	
    integer, parameter :: ir = 15
    integer :: a 
	
    open(unit=ir, file='test.txt', status='old', err=99)
    read(ir,*,err=99) a
    close(unit=ir,err=99)
	
    stop
	
99  print *, 'An error occured reading the file.'
	
end program error_handling

New features

Allocatable array extensions

  • Remove the need for pointers to use allocatable arrays as dummy arguments
  • Inefficient

Allocatable array extensions

program allocatable_dummy

    implicit none
	
    real, allocatable :: A(:,:)
	
    call createArray(A)
	
    print *, size(A,1), size(A,2)
	
    deallocate(A)
	
contains

subroutine createArray(A)
	
    real, allocatable, intent(out) :: A(:,:)
	
    allocate(A(20,20))
	
end subroutine createArray
	
end program allocatable_dummy

A pointer dummy argument was needed to implement this i Fortran 95

Allocatable functions

program allocatable_function

    implicit none
	
    real :: A(20)
	
    A = createVector(20)
    print *, size(A,1)
	
contains

function createVector(n)

    real, allocatable, dimension(:) :: createVector 
    integer, intent(in) :: n
	
    allocate(createVector(n))
	
end function createVector
	
end program allocatable_function

Vector allocated in function will be automatically deallocated when it has been used.

Allocatable components

type stack
    integer :: index
    integer, allocatable :: content(:)
end type stack

Pointers used in Fortran 95, but no automatic deallocation available

Submodules

  • The Fortran 95 module concept adequate for moderate size projects
  • Difficult to modularize modules themselves
  • Dividing modules into several modules
    • exposes internal structure
    • nameclashes
  • Changes to module features require recompilation of all files using this module
  • Separation of module into

    • Interface – defined in the module

    • Body – defined in submodule

Submodules

module points

type point
    real :: x, y
end type point
	
interface 
    real module function point_dist(a, b)
        type(point), intent(in) :: a, b
    end function point_dist
end interface

end module points
submodule (points) points_a

contains

real module function point_dist(a,b)
    type(point), intent(in) :: a, b
    point_dist = sqrt((a%x-b%x)**2+(a%y-b%y)**2)
end function point_dist

end submodule points_a

C Interoperability

  • No clear definitions existed between Fortran 95 and the C language
  • C/Fortran programming is important
    • Interfacing with C libraries
    • C user interface application interfacing with Fortran code
  • New intrinsic module, iso_c_binding, included
    • Contains definitions of all the C datatypes mapped to Fortran types

C Interoperability

program c_interop

    use iso_c_binding

    implicit none
	
    integer(c_int) :: a
    real(c_float) :: b
    real(c_double) :: c
	
    a = 42
    b = 42.0_c_float
    c = 84.0_c_double
	
    print *, a, b, c

end program c_interop

C Interoperability

  • Fortran always passes all variables by reference
  • In C scalar arguments are often passed by value
    • Argument value is copied to procedure
  • The variable attribute value is added for compatibility with C code
  • Enables direct linking with C

Object-oriented programming

module mytype_module

    type mytype
        real :: myvalue(4) = 0.0
    contains
        procedure :: write => write_mytype
        procedure :: reset 
    end type mytype
	
    private :: write_mytype, reset
	
contains

subroutine write_mytype(this, unit)
    class(mytype) :: this
    integer, optional :: unit
    if (present(unit)) then
        write(unit,*) this % myvalue
    else
        print *, this % myvalue
    end if
end subroutine write_mytype
	
subroutine reset(variable)
    class(mytype) :: variable
    variable % myvalue = 0.0
end subroutine reset
	
end module mytype_module
class(mtype) :: x

call x % write(6)
call x % reset

Access to computing environment

  • get_environment_variable(…)
    • Query environment variables
  • command_argument_count()
    • Retrieve the number of command line arguments
  • get_command_argument(…)
    • Retrieve specific command line argument

Introduction to Modern Fortran

By Jonas Lindemann

Introduction to Modern Fortran

  • 1,041