Professional Documents
Culture Documents
Fortran 3days
Fortran 3days
to the Fortran
programming language
Reinhold Bader
Nisarg Patel, Gilbert Brietzke, Ivan Pribec
Leibniz Supercomputing Centre
History of Fortran
Fortran – the oldest portable Generations of standards
programming language
Fortran 66 ancient
first compiler developed by John
Fortran 77 (1980) traditional
Backus at IBM (1957-59)
Fortran 90 (1991) large revision
design target: generate code
with speed comparable to Fortran 95 (1997) small revision
assembly programming, i.e. Fortran 2003 (2004) large revision
for efficiency of compiled
Fortran 2008 (2010) mid-size revision
executables
TS 29113 (2012) extends C interop
targeted at scientific /
TS 18508 (2015) extends parallelism
engineering (high
performance) computing Fortran 2018 (2018) current standard
© LRZ 2009-23 8
Formula translation
First programming task:
calculate and print the real-valued solutions of the quadratic equation
2𝑥 2 − 2𝑥 − 1.5 = 0
mathematical solution for the general case 𝑎𝑥 2 + 𝑏𝑥 + 𝑐 = 0 is
−𝑏 ± 𝑏 2 − 4𝑎𝑐
𝑥± =
2𝑎
real, = -1.5
by convention
:
executable statements: see next slide
end program
program solve_my_quadratic
implicit none
real, parameter :: a = 2.0, b = -2.0, c = -1.5
real :: x1, x2 declarations must always
intrinsic :: sqrt intrinsic function call
precede executable statements
assignment
Executed in order
sqrt(b**2 – 4. * a * c) ) / ( 2. * a )
of appearance
x1 = ( -b +
x2 = ( -b - sqrt(b**2 – 4. * a * c) ) / ( 2. * a )
expression
write(*, fmt=*) ‘Solutions are: ‘, x1, x2
I/O statement string literal
end program (output)
Note:
Operating environment usually Linux/UNIX
On x86 or x86_64, some compilers also support the Windows or MAC
operating systems
compilers marked green are available in the hands-on sessions, possibly
using a slightly older version
Quiz: how does one obtain data inside the program from standard input?
Programmer control over layout:
specify an explicit format string:
write(*,fmt='(A,F12.5,1X,E12.5)') 'Solutions are ', x1, x2
single blank
14 chars 12 chars, 12 chars,
format string for integer 5 decimals 5 decimals
output look like? (string) (scientific
(fixed point) notation)
a = 0.0
write(*,*) ′Hello:′‚ a
Three numeric intrinsic types numeric storage unit: typical value nowadays 4 bytes
1. integer integer :: years one storage
2. real real :: mass unit each
complex :: psi
3. complex two storage units
Two non-numeric intrinsic types
single character
4. character character :: c = ‘s‘
5. logical logical :: flag = .true.
or .false.
initialization of variable
Non-intrinsic types
derived types will be discussed later
integer kind max. exponent real kind dec. digits exponent range
default 109 (231-1) default 6 10-37 – 10+38
extended 1019 (263-1) extended 15 10-307 – 10+308
Declaration:
c = ( 1.2, 4.5e1 )
integer, parameter :: dm = 6
real, dimension(dm) :: a trivial mapping between storage
sequence index and array index
applies only for simple arrays
Alternative declaration
variants:
(1) real :: a(dm) attribute is implicit
Recommendation:
avoid statement form (can be confusing if non-local)
1. 3. 5. 7. 9. 11. 1. 6. 2. 7. 9. 11.
a(1) a(2) a(3) a(4) a(5) a(6)
a(1) a(2) a(3) a(4) a(5) a(6)
disc = b**2 – 4. * a * c
scalar logical expression
a block with
if (disc >= 0.0) then three statements
x1 = ( -b + sqrt(disc) ) / ( 2. * a )
x2 = ( -b – sqrt(disc) ) / ( 2. * a )
write(*,*) ‘Solutions are: ‘, x1, x2
else
write(*,*) ‘No real-valued solution exists‘
end if
if (scalar-logical-expr) then
if (scalar-logical-expr) &
block
action-stmt
else if (scalar-logical-expr) then
block action statement: essentially
else if … ! further blocks any single statement
: examples:
else ! optional
if (x < 2.) y = y + x
block
end if if (z /= 0.) a = b/z
„not equal“
the first block for which its
legacy form of IF:
condition is true is executed
• arithmetic if
if none, the else block is • not discussed here
executed, if it exists
Shown here:
two conditions and an else block Recommendation:
yes yes
else
block 1 block 2
block
regular
(unconditional)
execution
General concept:
Labeled GOTO statements
construct by default has one entry and one exit point should not be used any more
modifies statement execution order
Overview of constructs defined in the standard:
Name Purpose treated in this course?
legacy DO:
• with labeled ending statement continue
regular
• is not discussed here
execution
Endless DO construct
[ name: ] do
:
if (scalar-logical-expr) exit
:
end do [ name ]
requires a conditioned exit statement to eventually complete
Semantics: Example:
delineated block of executable
statements, which are executed in the real :: result
usual order, beginning with the first : ! executable statements
one appearing inside the construct construct name
optionally, prepended by block-local do_some_task : block
variable declarations – these variables integer :: i local declarations
only exist while the block executes real :: b(n)
This is not permitted for the other block constructs
:
b(i) = … executable
optionally, the block construct may
be given a name : statements
result = …
an exit statement can appear as one local b,i cease to exist
of the executable statements. If it
end block do_some_task
references the given construct (e.g.,
by name), execution continues after result remains available
: ! executable statements
the block
… = result + …
Syntax alternatives:
Semantics:
stops execution of the complete program
provided access code is usually printed to error output
an integer constant may also be propagated as process exit value
for serial programs, no substantive difference between the two
(for parallel programs that e.g. use coarrays, there is a difference)
39
Data representations
𝑖 = 𝑠 × 𝑤𝑘 × 𝑟 𝑘−1 𝑥 = 𝑏𝑒 × 𝑠 × 𝑓𝑘 × 𝑏−𝑘 or x = 0
𝑘=1 𝑘=1
epsilon(x)
tiny(x) spacing(0.35)
0 1 1 0.35 1 1
u=
8 4 2
nearest(0.35, -1.0)
Negative zero:
hardware may distinguish from positive zero
e.g., rounding of negative result toward zero retains sign,
e.g., I/O operations (sign stored in file)
Variant 1: Variant 2:
LHS an array, RHS a LHS and RHS an array
scalar
real :: a(dm), b(dm), c(dm+4)
real :: a(dm)
real :: y a = c ! non-conformable
: a = b ! OK
y = 4.0 a = c(1:dm) ! OK
a = y * 2.1 subobject of c
in this example: of same
→ RHS is broadcast to all size
array elements of LHS
→ causes element-wise copy
Assume declarations
real(rk) :: r 4 bytes integer(ik) :: i
real(dk) :: d 4 bytes
8 integer(lk) :: k
Examples:
1. Not exactly representable values Produced output
r = 1.1
loss of precision
d = r
write(*,*) abs(d – 1.1_dk) 2.3841857821338408E-008
3. Avoid overflow
if (abs(k) <= huge(i)) then
i = k this also works for integers!
else
: ! handle error
end if
List or
cmplx(x [,point
y] [,atkind])
Internet conversion to complex, or between complex KINDs
source?
int(x [, kind]) conversion to integers, or between integer KINDs
real(x [, kind]) conversion to reals, or between real KINDs
ceiling(a [, kind]) produces nearest higher (or lower) integer from real
floor(a [, kind])
nint(a, [, kind]) produces nearest integer from real
anint(a, [, kind]) produces nearest whole real from real
operator <operand> -c
Validity of expressions
operands must have a well-defined value
mathematical rules – e.g., no non-integer exponents of negative
numbers
limitations of model numbers may cause trouble sometimes
Initially, only operands of intrinsic types will be discussed
note however that even intrinsic operators can be overloaded for derived
type operands (treated later)
Legend:
I → integer
R → real
C → complex
Operands:
variables as well as evaluated result are of type logical
Precedence increases (.neqv. and .eqv. have same level)
Examples: logical :: a, b, c, d
: ! define a, b, c
d = ( a .or. b ) .and. c
write(*,*) d
d = a .or. .not. c
write(*,*) d
iachar(i)
{0, 1, 2, … , 𝑛 − 1} {′ 𝑎′ , ′𝑏′, … }
ichar(i)
replication of code
(maybe even multiple times in the dummy arguments
same program) (declarations below):
only visible inside procedure
difficult to navigate, edit,
compile, test implicit none
(maintainability issues) real :: a, b, c, x1, x2
integer :: n
Solution: : ! local variable declarations
: ! calculate solutions
functional encapsulation into end subroutine
subprograms
(sometimes also called
procedures) implementation calculates n,
x1, x2 from a, b, c
u
outside the module –
solve_quadratic()
here a main program
invocation
program my_main
use mod_solvers access solve_quadratic
from module mod_solvers
implicit none („use association“)
: ! declarations
a1 = 2.0; a2 = 7.4; a3 = 0.2
call solve_quadratic( a1, a2, a3, nsol, x, y )
write(*, *) nsol, x, y
actual argument list
end program
the actual arguments nsol and possibly x,y are overwritten on each
invocation
A() mod_solvers
a statement in
B() invokes A() solve_quadratic()
B() solve_something_else()
mod_solvers mod_other
solve_quadratic() other_procedure()
Separate compilation
different program units are usually stored in separate source files
Argument association
each dummy argument becomes associated with its corresponding actual
argument
two variants:
1. Positional correspondence
call solve_quadratic( a1, a2, a3, nsol, x, y )
the Fortran standard does not specify the means of establishing the
association
Establish (unsaved) local variables
usually on the stack
Note: dummy arguments are visible only within the scope of their defining procedure,
and possibly within an enclosed scoping unit
in procedure must not modify the argument (or any part of it)
real, intent(in) :: a
:
a = … ! rejected by compiler
Scenario: Invocations:
not all arguments needed at any
given invocation y = wsqrt(x1,pg) uses path 1
Example:
in the second invocation, refe-
real function wsqrt(x, p) rencing dummy p (except via
real, intent(in) :: x present) is non-conforming
real, intent(in), optional :: p
real :: p_loc
if ( present(p) ) then Notes:
p_loc = p path 1 optional arguments are permitted
else
for functions and subroutines,
p_loc = 1.0 path 2
end if an explicit interface is required,
: keyword calls are typically nee-
end function wsqrt ded if a non-last argument is
use of intrinsic logical function optional.
present is obligatory
subroutine process_expressions(…)
real :: x, y
slin(x, y) = a*x + b*y
…
z = slin(x1, y1) / slin(x2, y2) + slin(x3, y3) / slin(x4, y4)
end subroutine process_expressions
Possible consequences:
program crashes immediately, or somewhat later, or
element of another array is overwritten → incorrect result, or
you‘re lucky, and nothing bad happens (until you start using a different
compiler, or other compiler options)
An improved way of passing arrays will be shown tomorrow
Second call:
aliases its dummy arguments
how can two results be
written to a single variable?
(same memory location!)
subroutine modify_a(a, b)
real :: x, y
real, intent(inout) :: a
…
real, intent(in) :: b
x = …
y = …
A a = 2 * b
call modify_a(x, y)
B … = b
call modify_a(x, x)
C a = a + b
call modify_a(x, (x))
end subroutine
Consequence: Intent:
restriction in language which enable performance
makes the problematic calls optimizations by statement
illegal reordering and/or register use
but aliasing is not generally avoid ambiguities in assignments
disallowed to dummy arguments
Notes:
Restriction: further rules exist that apply to
dynamic features of the language
if (a subobject of) the argument
→ see advanced course
is defined in the subprogram, it
may not be referenced or defined exceptions to restrictions exist for
by any entity aliased to that special situations
argument → see advanced course
restriction effectively also applies
to parallel procedure invocations
in a shared memory environment
(e.g., OpenMP)
…
→ compile-time rejection of procedures that violate the rules
Notes:
in contexts where PURE is not needed, an interface not declaring the function as PURE
might be used
in the implementation, obeying the rules becomes programmer's responsibility if PURE
is not specified
program uses_sscal
implicit none statement often omitted
external :: sscal → sscal external by default
real :: x(7)
call sscal(4, 2.0, x, 2)
call sscal(3, -2, x(2), 2)
write(*,*) x
no complaint from compiler
end program about wrong type of actual argument
Fortran interface:
module libm_interfaces
implicit none enforce C name mangling
interface
real(c_float) function lgammaf_r(x, is) BIND(C)
use, intrinsic :: iso_c_binding
KIND numbers:
c_float and c_int are usually the default Fortran KINDs anyway
further types supported: c_char (length 1 only), c_double, …
unsigned types are not supported
Mixed-case C functions example C prototype:
void Gsub(float x[], int n);
an additional label is needed
invocation from Fortran via Fortran name
interface
subroutine ftn_gsub(x, n) BIND(C, name=′Gsub′)
use, intrinsic :: iso_c_binding
real(c_float), dimension(*) :: x
integer(c_int), value :: n
end function
end interface
C-style arrays
require assumed size declaration in Fortran interface
Much more information is provided in the advanced course
© LRZ 2009-23 Introduction to the Fortran programming language 99
Procedures as arguments (1)
Implementation of „methods“
use mod_body
type(body) :: ball invocation requires an
type(body) :: asteroids(ndim) actual argument of exactly
… ! define objects
that type (→ explicit interface
call kick(ball, …)
required)
call kick(asteroids(j), …)
do i = 1, 3
this % vel(i) = this % vel(i) + dp(i) / this % mass
end do
end subroutine
… = my_data(:)
disadvantage: need to declare
in exactly one calling program
unit; access not needed from my_data
any other program unit
(including the calling one) better separation of concerns
module mod_globaldata
implicit none
integer, parameter :: dm = 10000
real :: my_data(dm)
contains
subroutine set(…)
…
my_data(:) = … my_data is not among arguments.
my_data is not among arguments
It is accessed by host association
end subroutine set
subroutine op1(…)
…
end subroutine op1
end module mod_globaldata
my_data(5) = …
module mod_globaldata
implicit none blanket private statement
private
public :: set, op1, …
integer, parameter :: dm = 10000
real :: my_data(dm)
contains
…
end module mod_globaldata
Usage example:
use mod_person
type(person) :: a_person
a_person%name = 'Matthew' name is private – rejected by compiler
mod_date mod_person
date public
prog
u
set_date()
u
Public entities of mod_date
can be accessed inside host of mod_person
can also be accessed inside host of prog due to the blanket public
statement
Note: access can be changed from public to private for individual entities from
mod_date inside mod_person. But this will have no effect if the associating unit
directly uses mod_date (dotted line)
mod_date mod_person
date private
prog
u
set_date()
u
This does not mean
Public entities of mod_date
that date and set_date()
can be accessed inside host of mod_person
are private per se,
cannot be accessed inside host of prog due to
since prog may still
the blanket private statement
access them by using
Note: access can be set to public for individual mod_date directly (dotted
entities from mod_date inside mod_person line)
Exception:
generic procedure names
discussed tomorrow
Scheme 1 Scheme 2
Module name Module name
mod_<purpose> <name>
Data type in module Data type in module
<purpose> <name>_<purpose>
<purpose>_<detail> if multiple Public variables / constants
types are needed <name>_<purpose>
Public variables / constants Public procedures
var_<purpose>_<detail> <name>_<verb> or
const_<purpose>_<detail> <name>_<verb>_<purpose>
Public procedures
<verb>_<purpose> or
<verb>_<purpose>_<detail>
disallowed
u example: m1 may not use m3, since
m3 (indirectly) uses m1
m4
Recompilation cascade:
If a program unit use if a module is changed, all program
associates a module units using it must be recompiled
the latter must be compiled first
usually even if only the implemen-
directed acyclical dependency
tation (contains part) is modified
graph („DAG“)
solution to this in advanced course
At compilation
the usual object file is generated
per module contained in the file, one additional file with information describing at
least the specification part of the module, including the signatures of all explicit
interfaces, is created
this module information file usually is named module_name.mod; it is essentially a
kind of pre-compiled header
it is needed whenenever the compiler encounters a
use <module_name>
statement in another program unit → potentially forces compilation order
Note: large modules and multitudes of dependent modules can cause problems
→ use submodules to deal with this (cf. advanced topics course)
128
More on array declarations
layout in memory: 1 1 3 5
bb(2,0) is fourth element
„column major“ array 1st dimension 2 2 4 6 in the sequence
element sequence
© LRZ 2009-23 Introduction to the Fortran programming language 129
Array inquiry intrinsic functions
Note:
extent = ubound – lbound + 1
1
4 11 d(4:7,4:11)
4 non-contiguous
7 10
10
d(:,16:19)
contiguous d(:,6:20:5)
16 19 fully specified
subscript triplet
a colon without bounds specifi- it is allowed to omit index
cations means the complete set specifications:
of indices in the dimension it is
specified in d(:,::2)
also possible: only lower or only
upper bounds are specified in the every second column of
subscript d, starting in the first one
d(:,11:) = d(:,5:14)
used for defining complete overlap of LHS and RHS
arrays (all array elements) → array temporary may
be created
intrinsic reshape creates a
higher rank array from a rank scalars are broadcast
1 array element-wise assignment by
array element order
… = v( [ 2, 3, 9, 5 ] )
1 2 4 3 element order
many-to-one:
… = v( [ 2, 3, 9, 2 ] )
1,4 2 3 element order
you can also use an integer array variable as vector subscript:
iv = [ 2, 3, 9, 5 ]
… = v( iv )
Zero-size arrays
may result from suitable (algorithm-induced) indexing of a defined
array, or by dynamic allocation (discussed later)
always defined, but no valid reference of an array element is possible
lower bound is 1, upper bound 0
Example:
do i = 1, n
:
last loop iteration
… = d(:,i:n-1) produces a zero-sized array
end do
Earlier declarations …
type :: body
…
real :: pos(3) However, there may not be
end type two (or more) designators
type(body) :: asteroids(ndim) which are arrays:
asteroids(:)%pos disallowed
Subobject designators:
real :: x(6,4)
example
real :: xs2(6) shows
: ! define x 4.1 0.9 1.2 0.3 6.5 3rd of 6
xs2 = sum(x, dim = 2) array
elements
real :: a(4), s
a = …
s = sum(a, mask = a>0.)
Further intrinsics that
support dim and mask exist
Illustration of masked
reduction see compiler documentation
dot_product (vector_a, vector_b) dot (scalar) product of numerical or logical rank 1 arrays.
Transformational functions:
unpack (vector, mask, field)
convert from multi-rank arrays
(of any type) to rank 1 arrays Unpack result:
(of same type) and back type is that of vector
a logical mask is used to shape is that of logical array mask
select a subset of array
elements (may be a scalar with size of vector: at least number of
value .true.) true elements of mask
field of same type as vector,
pack (array, mask [, vector]) and a scalar, or same shape as
mask
1 2 3 6 9
1 4 result field 10 1 9 6 8
2 5 pack (mask color coded) unpack 2 3 7 10
array 3 6
(mask color coded)
1 2 3 6 7 8
result (→ vector for unpack)
vector 7 8
(optional, size at least number of true elements of mask)
P P P P P P P P
T1 T1 T1 T1 T1 T1 T1 T1
socket
L1D L1D L1D L1D L1D L1D L1D L1D
L2 L2 L2 L2 L2 L2 L2 L2
L3 L3
HT or QPI
Memory Memory
limitations
x
Time interval T between
request of worker for single
datum
and
availability of data item for
being worked on
T
depends on speed and
length of assembly line
x
time
x1 x2 x3 x4 x5
All items delivered
time
run repeated iterations for varying vector lengths (working set sizes)
L3 – 20 MB
~ 33 GB/s
L1D – 32kB
< 112 GB/s
Memory
Vectorization (256 Bit registers)
provides performance boost
~ 14.7 GB/s
mostly in L1/L2 cache
2x
2x
~ 60 MFlop/s
Note:
vector processors
have a qualitatively
different characteristic
PF cache line 2
PF cache line 3
execution time
vectorizability
is lost
Notes:
▪ stride known at
compile time
▪ serial compiler
optimizations may
compensate perfor-
mance losses in
real-life code
ca. 40 MFlop/s
(remains constant
for strides > ~25)
do i=1, n do j=1, m
do j=1, m do i=1, n
a(i, j) = … a(i, j) = …
end do end do
jumps through in innermost loop
end do end do corresponds to
strides of ndim
leftmost array index
Accessing type components
type(body) :: a(ndim)
type(body) :: a(ndim)
do i=1, n effectively
stride 8 do i=1, n uses 7/8 of
… = a(i)%vel(3) cache line
… = a(i)%mass
end do
… = a(i)%pos(:)
do i=1, n
… = a(i)%vel(:)
… = a(i)%pos(3)
end do
end do
Fortran Environment
160
Intrinsics
https://gcc.gnu.org/onlinedocs/gfortran/Intrinsic-Modules.html
real(kind=wp) :: x
integer(kind=ik) :: i4
module mod_scoping_1
implicit none
type :: p type components
are „class 2“ identifiers;
integer :: i must be unique per-type
real :: x scope 2
Typical situation:
(memory for an) entity exists from start of execution of its scoping
unit
until execution of its scoping unit has completed
Definition status:
may become defined after start of execution, or not at all
will become undefined once execution of its scoping unit completes
Exceptional situation:
module variables („globals“) are persistent
(static module variables exist for the duration of the program execution)
Example Properties:
(legacy) standalone procedure at the subsequent invocation of
the procedure, SAVEd local
subroutine process(a, n) variables have the same defini-
implicit none tion status and value as at the
real :: a(*) end of the previous execution
integer :: n attribute form → „lifetime extension“
integer, save :: first = 0 for recursive subroutines, only
real :: work(1000) one instance of SAVEd local
save :: work variable exists for all active
statement form invocations
if (first .EQ. 0) then
a blanket SAVE statement
…
work(…) = … expensive calculation of applies to all local variables in
reusable array work
first = 1 the scoping unit, or all module
is done once only
end if variables if in the specification
: update array a section of a module
end subroutine → avoid the above two items
171
What are initializations?
Intent: Variant 2:
the DATA statement
provide a value with which a
(local or global) variable is integer :: i
defined at the beginning of the character(len=4) :: cn
type(date) :: o
first execution of its scoping
real :: xx(3)
unit data i, o / 5, date(…) /, &
cn, xx / 'f ', 3*0.0 /
Variant 1:
integer :: i = 5
character(len=4) :: cn='f' sequence of values
type(date) :: o = date(…) matching the type of each
real :: xx(3) = [ 0.,0.,0. ] element of the object list
note the repeat factor for
follow the declaration with a the array initial values
constant expression Recommendation:
rules as for intrinsic assign- variant 1 for readability
ment
statement ends
(not a constant expression)
are integers of
any kind
for bb, a rank-1 array is constructed via two nested implied-do loops, then
reshape() is used to convert to a rank-2 array
if the complete implied-DO loop is intended to be a constant expression,
the argument expression must be a constant expression
need not do so for all components (in fact it may not be possible for
components of opaque type)
derived type components: any pre-existing initialization is overridden if a
default initialization is specified
Now we proceed to an
exercise session …
Invocation is straightforward
program use_solver
access explicit interface
use mod_solver for process_array
implicit none
real :: aa(0:1, 3), ab(0:2, 9)
: ! define aa, ab
consistency of argument‘s
call process_array( aa )
type, kind and rank
call process_array( ab(0::2,1::3) ) with interface specification
: is required
end program
Actual argument
must have a shape normally, a descriptor will be
can be an array section created and passed → no
copying of data happens
Notes:
leading dimension(s) of array as well as problem dimensions must be
explicitly passed
this permits (but does not force) the programmer to assure that
ad(i,j) corresponds to element (i,j) of the actual argument
actual memory requirement implied by addressing: LDA*(M-1) + N array
elements
Example: Level 2 and 3 BLAS interfaces (e.g., DGEMV)
Actual argument is
a complete array
of same type, kind and rank as
dummy argument
aa(1,2)
integer, parameter :: lda = …
aa(1,1)
real :: aa(lda, lda)
: N
: ! calculate n, m aa(n,m)
call slvr( aa, lda, n, m ) LDA
M part of array
actually defined
in procedure call
behaves as if address of first array AD == AA
element is passed to procedure
Actual argument is
a non-contiguous array subobject AD is a compactified
for INTENT(IN) only
Actual argument is
a complete array
of same type and kind as dummy
argument
but of different rank
aa(lda+1)
Example:
blocked processing of subarrays distance: LDA
array elements
real :: aa(lda, lda)
: aa(i,j)
aa(i,j+1)
: ! calculate i, j, nb, mb
call slvr(aa(i, j), lda, nb, mb) LDA
NB part of array
pass scalar to procedure actually defined
MB in procedure call
Example: Usage
conforming LHS required in an
function add_real_int(r, i) &
result(ri) assignment
real :: r(:)
integer :: i(:) use … whatever module
real :: ri(size(r)) implicit none the function is part of
integer :: k integer, parameter :: ndim=…
do k = 1, size(r) real :: r(ndim)
ri(k) = r(k) + real(i(k)) integer :: ix(ndim)
end do : ! initialize r, ix
end function
r = add_real_int(r, ix)
Interface must be explicit
shape of result evaluated at
run time through use of a
specification expression (at
entry to function)
Declaration:
module elem_stuff
elemental prefix: contains
elemental subroutine swap(x, y)
real, intent(inout) :: x, y
real :: wk
wk = x; x = y; y = wk
end subroutine swap
end module
Assignment may be
a defined assignment (introduced later) if it is elemental
Right hand side
may contain an elemental function reference. Then, masking extends
to that reference
may contain a non-elemental function reference. Masking does not
extend to the argument of that reference
Parallel semantics
of array element assignment
Text
every process uses the same
(formal) memory layout initialized global variables
Data
physical memory is mapped static memory
to the virtual address space uninitialized global variables
BSS
by the OS („block started by symbol“)
protection mechanisms
Stack
prevent processes from Stack: dynamic data needed
interfering with each other's due to generation of new
memory scope (grows/shrinks automa-
tically as subprograms are invoked
32 vs. 64 bit address space or completed; size limitations apply)
Allocatable scalars
deferred-length string
auto-allocation
ai = [ 1, 2 ]
str = “Hello World“
re-allocation to new
ai = [ 3, 4, 7 ] size / string length
str = str(1:5)
no re-allocation because
ai = [ 4, 4, 7 ] RHS is conformant
Note: this only works for assignment, not for an I/O transfer statement
Declaration:
deferred shape
pt X
real(dk), pointer :: pt(:) state after
real(dk), target :: tg(3) declaration: tg
undefined
pt => tg
pt
pt(2) = 7.2 after pointer
:
tg(2) is defined assignment: tg
pt => null() associated
:
now pt is disassociated pt
1.2
pt associated with non-
is_contiguous(pt) returns .false.
contiguous subobject
pt
after allocation
pt
tg
pt_2
pt
after deallocation pt is nullified
after reassignment tg pt
of pointer … but pt_2 is undefined
unreachable memory area created pt_2 X
(cannot use associated on it)
type :: cont_t
type :: polynomial default value is
private disassociated
private
real, pointer ::
real, allocatable :: f(:)
item(:) => null()
end type
end type
default (initial) value is
not allocated
polynomial cont_t
f(:) item(:)
: define p1 : define s1
(see e.g. next slide)
p2 = p1 s2 = s1
type(cont_t) :: s1
type(polynomial) :: p1 real, target :: t1(ndim)
real, parameter :: t2(ndim) = …
p1 = polynomial( [1.0, 2.0] )
could be omitted (default initialized component)
s1 = cont_t( null() )
dynamically allocates p1%f to
become a size 2 array with
explicit target:
elements 1.0 and 2.0
s1 = cont_t( t1 )
Irregularity:
each array element might have a component of different length
or an array element might be unallocated (or disassociated)
type(polynomial) :: p_arr(4)
n = … ;lb = …; ub = lb + n - 1
allocate(storage(n*n))
Array syntax
D(:) = A(:) + B(:)*C(:)
fully optimized by
compiler
Idea:
fold array properties into type component → structure of arrays (SoA)
this is achieved via integer-typed parameters, which become part of the type
Two variants: KIND and LEN (length) parametrization
semantic difference: compile-time vs. run-time resolution of parameter values
Static declarations
unspecified type parameters take default values;
specification is obligatory if no defaults exist
type( body_p (ntraj=ndim) ) :: traj_ndim
type( body_p ) :: traj_1
type( body_p (k=kind(1.0d0), ntraj=ndim) ) :: dptraj_ndim
constant expression required
Dynamic objects
length type parameters are usually deferred:
a PDT scalar
type( body_p (ntraj=:) ), allocatable :: dyn_traj
actual
velocity for array element 2
layout
SoA with LEN parameter value 3 may differ
all masses all velocities in details
Invocation Distinguishability:
(only the most relevant rules listed here)
use mod_functions
implicit none at least one non-optional
real :: x,p argument must be different with
real(dk) :: xd, pd
respect to either type, kind or
initialize variables
: rank (TKR),
write(*,*) wsqrt(x,p) invokes specific wsqrt
or differ by being a dummy
write(*,*) wsqrt(xd,pd) invokes specific wsqrt_dk procedure argument as opposed
to a dummy data argument
write(*,*) wsqrt(x,pd)
Specific functions
must have sufficiently different
interface
invocations always determined at
compile time
gfun()
fun_spec4()
prog
u
u
call gfun(...)
Exception to naming rules
with generics, same name can be re-used in different modules
Unambiguous resolution:
also depends on which specifics are accessed
gfun(): interface of fun_spec2() might be ambiguous with respect to other
specifics (not recommended!), since not use associated by „prog“
Scenario:
An algorithm is considered that can handle problems of different
dimensionality
The functionality cannot be handled by an ELEMENTAL procedure
Consequences:
the computational interface should supply a single specific that can handle
calls with arrays of arbitrary rank
it should be also possible to use this as a specific in a generic interface (e.g.,
because types might also be varied)
use mod_io
:
real :: a, b(nbdim), c(ncdim1,ncdim2), d(nddim1,nddim2,nddim3)
: ! supply all values
module mod_io
select rank (buf) actual argument
:
rank (0) is scalar
contains
write(iu) buf
subroutine write_iobuf(buf,file)
rank (1)
real, intent(in) :: buf(..) write(iu) buf(:)
character(len=*), & rank (2)
intent(in) :: file write(iu) buf(:,:)
: ! open file rank (3)
: write(iu) buf(:,:,:) actual argument
end subroutine write_iobuf rank (*) is assumed-size
end module mod_io stop 'assumed size unsupported'
rank default
also permitted: explicit stop 'rank > 3 unsupported'
DIMENSION(..) attribute end select
module mod_date
: ! previous type definition for date
interface date any number of
module procedure create_date specific functions
module procedure create_date_safe
end interface
contains must be a function with scalar result
type(date) function create_date(day, mon, year)
integer, intent(in) :: day, mon provide additional
integer, intent(in) :: year semantics
: ! check constraints on input
create_date%day = day; … ! define object obliged to use
end function component notation
type(date) function create_date_safe(day, mon, year)
integer, intent(in) :: day
character(len=3), intent(in) :: mon improve safety of use
integer, intent(in) :: year
via a suitably chosen
: ! implementation omitted
interface signature
end function
end module mod_date
invokes create_date_safe
x = op .convolve. x
module user_ops
interface operator (.convolve.) invokes
module procedure conv x = conv((op),(x))
end interface
contains Further rules:
function conv(op, vc) result(r) generic name can have up to 31
real, intent(in) :: op(:),vc(:)
real :: r( size(vc) )
characters between dots
: otherwise same rules as for intrinsic
end function operations
end module
implementation not
shown here
X = (A .convolve. B) + C
Abstraction:
allows the program to refer to a file
via a default integer,
which is part of the global state of the program
Pre-connected units:
units associated with a (special) file without executing an OPEN statement
special notation: star instead of integer
standard output write(*, …) …
standard input read(*, …) …
error unit: this is where error messages from the run time library typically are
written to. May be the same as standard output
Alternative:
replace star by constants defined in ISO_FORTRAN_ENV:
use, intrinsic :: iso_fortran_env
write(output_unit, …) … or to error_unit
read(input_unit, …) …
Note:
Shell/OS limit on number of
filehandles – not a Fortran issue
A statement of the form keyword “fmt=“ is optional. It stands for the word “format”.
write(iunit, fmt=*) a, b, c
bbb22bcomesbbeforebbbb23
Note: repeated single quote masks a single one inside format string
complex types
E real indicate exponent
b0.233E+02-0.715E+00b7b9-3de
field width is 10 (includes sign) width 2 – blank padding if not all characters needed
integer :: i
real(dk) :: x
Assumption: Input:
format string without format exhaustion → remain-
components in parentheses der of record is skipped
more items in I/O list than edit otherwise similar to output
descriptors are available example: file with contents
Output:
1 2 3 4 5
integer :: i(24) 11 12 13 14 15
i = … 21 22 23
write(iu, '(10i4)') i
which is processed using
will produce three records
read(…, fmt='(3i3)') is(:3,:3)
(the last one incomplete)
format specification is will only read the values
repeated marked red (in which order?)
argument values
mode keyword semantics
(defaults in bold)
'direct', 'sequential' or
access= determines access method
'stream'
'read', 'write' or determines I/O direction;
action=
'readwrite' default is processor-dependent.
asynchronous= 'yes' or 'no' necessary (but not sufficient) for AIO
encoding= 'default' or 'utf-8' UNICODE might work ...
'formatted' or conversion method;
form=
'unformatted' default depends on access method.
specifies the initial position of the file (sequential
position= 'asis', 'rewind' or 'append' or stream access)
record length (in file storage units – often 1 byte)
recl= positive integer value
for direct or sequential access files
'old', 'new', 'unknown', enforce condition on existence state of file
status=
'replace' or 'scratch' before the OPEN statement is executed.
General properties
set additional properties in the OPEN statement which apply for all
subsequent I/O statements
set additional properties within subsequent READ or WRITE statements
which apply for that particular statement
use INQUIRE on unit to obtain presently set properties (see later; RHS
expressions are then replaced by character string variables)
these modes apply for formatted I/O only
Example:
real :: r = 2.33444e+2
open(iu, …) assumption: open expected output
write(iu, '(e11.4)') r does not specify sign= b0.2334E+03
write(iu, sign='plus', '(e11.4)') r +0.2334E+03
write(iu, sign='suppress' , '(e11.4)') r b0.2334E+03
(first line is
processor dependent)
argument values
mode keyword semantics
(defaults in bold)
determine how blanks in input field are
blank= 'null' or 'zero' interpreted
set character used as decimal point during
decimal= 'comma' or 'point' numeric conversion
'apostrophe', 'quote' or sets delimiter for character values in list-directed
delim=
'none' and namelist output
padding with blanks during input if more charac-
pad= 'yes' or 'no' ters must be processed than contained in record
'up', 'down', 'zero', 'nearest',
round= 'compatible', set rounding mode for formatted I/O processing
'processor_defined'
'plus', 'suppress' controls whether an optional plus sign will appear
sign= 'processor_defined' in formatted output
General rules
may specify a file or a unit, but not both
uses inquiry specifiers of the form keyword=variable
for some of the keywords (also those that are also permitted in an OPEN
statement), an additional status of 'UNKNOWN' or 'UNDEFINED' may
be returned
Note:
Stream I/O
Non-advancing I/O
are not dealt with in this course
record
rec = 1 2 3
record size specified in file storage units (whose size is processor dependent)
any record can be written, read and rewritten without interfering with
another one
(contrast to sequential file: overwriting a record invalidates all following
records)
A direct access file may be formatted or unformatted
default is unformatted
© LRZ 2009-23 Introduction to the Fortran programming language 296
Direct access files (2)
do nr=…
Step 3: : ! set up x, y
usually, a single record
Write a record write(unit=iu, rec=nr) size(x), size(y), x, y
end do
record not filled → remainder is undefined
... Step 4: close file
Limitations Remark on
processor-dependent upper limit formatted direct access
for record sizes (may differ for slash edit descriptor causes
formatted and unformatted files) record number to increase by
large number/size of records may one, and further I/O
lead to performance issues processing starts at the
(access times) beginning of the next record
parallel access (MPI or coarray
programs) to disjoint records may
or may not work as expected
(depends on access pattern and file
system semantics)
are provided that check whether the iostat value of an I/O operation
corresponds to an EOF (end of file) or EOR (end of record) condition
Rules (cont'd):
file positioning and file inquiry are not available
single record: corresponds to a character scalar
multiple records: correspond to a character array
length of string is the (maximum) record length
Example 2:
generate format dynamically character(len=...) :: my_fmt
integer, allocatable :: iarr(:)
integer :: iw
: iarr is allocated to size 7 and defined
iw = … ! prospective width e.g., 4
also illustrates character write(my_fmt, fmt= &
string descriptor '(''('',i0,''i'',i0,'')'')' &
) size(iarr), iw
:
value of my_fmt is '(7i4)' write(unit=…, fmt=my_fmt) iarr