Modern Fortran
Modern Fortran for FORTRAN77 users
Jonathan Dursi
This is the Wiki-fied version of the slides used for the Modern Fortran course at SciNet. To follow along, with both the examples and the hands on, you will want to download the source code and make sure you have a working Fortran 2003 compiler.
Course Overview
|
A Brief History of Fortran
- Only major compiled programming language designed specifically for scientific programming.
- Powerful array operations; many mathematical functions (Bessel functions!) built in; designed to enable compiler optimizations for fast code
- Oldest (54-57 yrs) still-used programming language.
- Most people come to Fortran via being given old code by someone.
- Can’t understand the old code, or quirks of modern language, without understanding it’s history
- 1957 - J.W. Backus et al. In Proceedings Western Joint Computer Conference, Los Angeles, California, February 1957.
- IBM 704
- (Arguably) first modern compiled programming language.
- Idea of compilers at all was controversial at time.
FORTRAN (1957)
|
Incremental changes
|
FORTRAN66
|
FORTRAN77
- The most common to see “in the wild” of old code today
- if/else/endif, better do loops, control of implicit typing
- Character strings, saved variables, IO improvements
- Approved in 1978, beginning long tradition of “optimistic” naming of standards by year.
The interregnum
- Programming languages and techniques were moving quite quickly
- Several attempts were made to make new version, but standardization process very slow, failed repeatedly.
- Absent new real standard, implementations began to grow in many different directions
- Some extensions became quasi-standard, many were peculiar to individual compilers.
Fortran90
- Enormous changes; the basis of modern Fortran (not FORTRAN!)
- Free form, array slices, modules, dynamic memory allocation, derived types...
- Changes so major that took several years for compilers to catch up.
- Modern fortran
And since...
- Fortran95 - modest changes to Fortran90, killed off some deprecated F77 constructs.
- Fortran 2003 - bigger change; object-oriented, C interoperability. Most compilers have pretty good F2003 support.
- Fortran 2008 - mostly minor changes, with one big addition (coarray), other parallel stuff. Compiler-writers getting there.
F90, F95, F2003, F2008..
- We won’t distinguish between versions; we’ll just show you a lot of useful features of modern fortran.
- Will only show widely-implemented features from 2003 and 8, with exception of coarrays; these are being implemented and are very important.
New Format, New Syntax
Free Format: some highlights
|
<source lang="fortran"> program example implicit none integer, parameter :: npts = 10000 real, parameter :: startx=0., endx=1. real, parameter :: dx = (endx-startx)/npts real :: integral, xleft, xright, xmid integer :: i if (npts < 2) then print *,'Too few points!' else integral = 0. ! Simpson's Rule xleft = 0. int: do i=0,npts-1 xright = (i+1)*dx xmid = (xleft+xright)/2. integral = integral + (dx/6.)*(f(xleft) + 4.*f(xmid) + & f(xright)) xleft = xright end do int print *,'Numerical integral is ', integral print *,'Exact soln is ', (endx-startx)/2. - & (sin(2*endx)-sin(2*startx))/4. endif
function f(x) implicit none real :: f real, intent(in) :: x f = sin(x)**2 end function f end program example </source> |
Variable Declarations
<source lang="fortran"> integer i parameter (i=5) </source>
|
<source lang="fortran"> implicit none integer, parameter :: npts = 10000 real, parameter :: startx=0., endx=1. real, parameter :: dx = (endx-startx)/npts real :: integral, xleft, xright, xmid integer :: i </source> |
Variable Initialization
|
<source lang="fortran"> |
...
subroutine testvarinit implicit none integer :: i = 5 print '(A,I3)', 'On entry; i = ', i i = 7 print '(A,I3)', 'Now set; i = ', i end subroutine testvarinit |
...
use inittest |
...
call testvarinit call testvarinit |
...
end program initialization </source> ( From samples/variables/initialization/initialization.f90) <source lang="bash"> $ gfortran initialization.f90 -o initialization $ ./initialization On entry; i = 5 Now set; i = 7 On entry; i = 7 Now set; i = 7 </source> |
---|
Real Kinds
|
<source lang="fortran"> program realkinds use iso_fortran_env implicit none real :: x real(kind=real32) :: x32 real(kind=real64) :: x64 real(kind=real128):: x128 real(kind=selected_real_kind(6)) :: y6 real(kind=selected_real_kind(15)):: y15 print *,'Default:' print *, precision(x), range(x) print *,'Real32:' print *, precision(x32), range(x32) print *,'Real64:' print *, precision(x64), range(x64) print *,'Real128:' print *, precision(x128), range(x128) print *, print *,'Selected Real Kind 6:' print *, precision(y6), range(y6) print *,'Selected Real Kind 15:' print *, precision(y15), range(y15) end program realkinds </source> (from samples/variables/kinds/realkinds.f90) <source lang="bash"> $ ./realkinds Default: 6 37 Real32: 6 37 Real64: 15 307 Real128: 18 4931 Selected Real Kind 6: 6 37 Selected Real Kind 15: 15 307 </source> |
Integer Kinds
|
<source lang="fortran"> program integerkinds use iso_fortran_env implicit none integer :: i integer(kind=int8) :: i8 integer(kind=int16) :: i16 integer(kind=int32) :: i32 integer(kind=int64) :: i64 integer(kind=selected_int_kind(6)) :: j6 integer(kind=selected_int_kind(15)):: j15 print *,'Default:' print *, huge(i) print *,'Int8:' print *, huge(i8) print *,'Int16:' print *, huge(i16) print *,'Int32:' print *, huge(i32) print *,'Int64:' print *, huge(i64) print *, print *,'Selected Integer Kind 6:' print *, huge(j6) print *,'Selected Integer Kind 15:' print *, huge(j15) end program integerkinds </source> (from samples/variables/kinds/intkinds.f90) <source lang="bash"> $ ./intkinds Default: 2147483647 Int8: 127 Int16: 32767 Int32: 2147483647 Int64: 9223372036854775807 Selected Integer Kind 6: 2147483647 Selected Integer Kind 15: 9223372036854775807 </source> |
Strings
|
<source lang="fortran"> program strings implicit none character(len=20) :: hello character(len=20) :: world character(len=30) :: helloworld hello = "Hello" world = "World!" helloworld = trim(hello) // " " // trim(world) print *, helloworld if (hello < world) then print *, '<', hello, '> is smaller.' else print *, '<', world, '> is larger.' endif end program strings </source> (from samples/variables/strings/strings.f90) <source lang="bash"> $ ./strings Hello World! <Hello > is smaller. </source> |
Array declarations
|
<source lang="fortran"> program arrays implicit none real, dimension(3) :: x, y x = [1,2,3] y = 2*x print *, x print *, y end program arrays </source> ( from samples/variables/arrays/arrays.f90) <source lang="bash"> $ ./arrays 1.0000000 2.0000000 3.0000000 2.0000000 4.0000000 6.0000000 </source> |
Do loops
|
<source lang="fortran"> program doi implicit none integer :: i do i=1,10 print *, i, i**2, i**3 enddo end program doi </source> ( from samples/variables/doloops/doi.f90 ) <source lang="bash"> $ ./doi 1 1 1 2 4 8 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 </source> |
Named loops
|
<source lang="fortran"> program nameddo implicit none integer :: i, j outer: do i=1,3 inner: do j=1,3 print *, i, j, i*i+j*j enddo inner end do outer end program nameddo </source> ( from samples/variables/doloops/nameddo.f90 ) <source lang="bash"> $ ./nameddo 1 1 2 1 2 5 1 3 10 2 1 5 2 2 8 2 3 13 3 1 10 3 2 13 3 3 18 </source> |
Cycle/exit
|
<source lang="fortran"> program cycleexit implicit none integer :: i do print *, 'Enter a number between 1-13' read *, i if (i>=1 .and. i<=13) exit print *, 'Wrong; try again.' enddo print *, 'Good; you entered ', i end program cycleexit </source> ( from samples/variables/doloops/cycleexit.f90 ) <source lang="bash"> $ more cycleexit-out.txt $ ./cycleexit Enter a number between 1-13 23 Wrong; try again. Enter a number between 1-13 -1 Wrong; try again. Enter a number between 1-13 12 Good; you entered 12 </source> |
Do while
|
<source lang="fortran"> program dowhile implicit none integer :: i i = -1 do while (i < 1 .or. i > 13) print *, 'Enter a number between 1-13' read *, i if (i<1 .or. i>13) print *, 'Wrong; try again.' enddo print *, 'Good; you entered ', i end program dowhile </source> ( from samples/variables/doloops/dowhile.f90 ) |
Hands On 1
- In workedexample/f77 is a simplified, F77ized version of a fluid-dynamics code from Ue-Li Pen, CITA, U of Toronto (http://www.cita.utoronto.ca/~pen/MHD/)
- For the purposes of this class, we've turned it from a perfectly good f90 code to something that looks more like something your supervisor would dust off and give to you.
- Today we’ll be translating this version into a very modern Fortran
- Compile (using make) and run (./hydro)
- Outputs a .pbm file; use “display dens.pbm” to see the result of dense blob of fluid moving through a light medium.
- In workedexamples/freeform, have partly converted the program to new freeform format, with enddos, ending procedures, implicit nones, and new variable declaration syntax.
- Finish doing so - just need to do program hydro, subroutine color, subroutine outputpbm, function cfl. Fix indenting (Don't need to start at col 7 anymore).
- ~1 hr (for getting logged in and everything working)
Procedures and Modules
|
Modules
|
<source lang="fortran"> module gravity implicit none real, parameter :: G = 6.67e-11 ! MKS units contains real function gravforce(x1,x2,m1,m2) implicit none real, dimension(3), intent(in) :: x1,x2 real, intent(in) :: m1, m2 real :: dist dist = sqrt(sum((x1-x2)**2)) gravforce = G * m1 * m2 / dist**2 end function gravforce end module gravity program simplemod use gravity implicit none print *, 'Gravitational constant = ', G print *, 'Force between 2 1kg masses at [1,0,0] & &and [0,0,1] is' print *, gravforce([1.,0.,0.],[0.,0.,1.],1.,1.) end program simplemod </source> (from samples/procedures/simplemod/simplemod.f90) |
Compiling & Running
|
<source lang="bash"> $ ls simplemod.f90 $ gfortran -o simplemod simplemod.f90 -Wall $ ls gravity.mod simplemod simplemod.f90 $ ./simplemod Gravitational constant = 6.6700000E-11 Force between 2 1kg masses at [1,0,0] and [0,0,1] is 3.3350003E-11 </source> |
Modules
|
<source lang="fortran"> <source lang="fortran"> module gravity implicit none real, parameter :: G = 6.67e-11 ! MKS units contains real function gravforce(x1,x2,m1,m2) implicit none real, dimension(3), intent(in) :: x1,x2 real, intent(in) :: m1, m2 real :: dist dist = sqrt(sum((x1-x2)**2)) gravforce = G * m1 * m2 / dist**2 end function gravforce end module gravity program simplemod use gravity implicit none print *, 'Gravitational constant = ', G print *, 'Force between 2 1kg masses at [1,0,0] & &and [0,0,1] is' print *, gravforce([1.,0.,0.],[0.,0.,1.],1.,1.) end program simplemod </source> (from samples/procedures/simplemod/simplemod.f90) |
use module, only :
|
<source lang="fortran"> module gravity implicit none real, parameter :: G = 6.67e-11 ! MKS units contains real function gravforce(x1,x2,m1,m2) implicit none real, dimension(3), intent(in) :: x1,x2 real, intent(in) :: m1, m2 real :: dist dist = sqrt(sum((x1-x2)**2)) gravforce = G * m1 * m2 / dist**2 end function gravforce end module gravity program simplemod2 use gravity, only : G, gravforce implicit none print *, 'Gravitational constant = ', G print *, 'Force between 2 1kg masses at [1,0,0] & &and [0,0,1] is' print *, gravforce([1.,0.,0.],[0.,0.,1.],1.,1.) end program simplemod2 </source> samples/procedures/simplemod/simplemod2.f90 |
Modules usually get their own files
|
<source lang="fortran"> module gravity implicit none private character (len=8), parameter, public :: massunit="kilogram" character (len=8), parameter, public :: forceunit="Newton" public :: gravforce real, parameter :: G = 6.67e-11 ! MKS units contains real function distance(x1,x2) implicit none real, dimension(3), intent(in) :: x1, x2 distance = sqrt(sum((x1-x2)**2)) end function distance real function gravforce(x1,x2,m1,m2) implicit none real, dimension(3), intent(in) :: x1,x2 real, intent(in) :: m1, m2 real :: dist dist = distance(x1,x2) gravforce = G * m1 * m2 / dist**2 end function gravforce end module gravity </source> (from samples/procedures/multifilemod/gravity.f90 ) |
Modules usually get their own files
|
<source lang="make"> FC=gfortran FFLAGS=-O3 -Wall multifilemod: multifilemod.o gravity.o $(FC) -o $@ multifilemod.o gravity.o %.mod: %.f90 $(FC) $(FFLAGS) -c $< multifilemod.o: multifilemod.f90 gravity.mod $(FC) $(FFLAGS) -c $< clean: rm -f *.o *~ *.mod multifilemod </source> (from samples/procedures/multifilemod/Makefile) |
.mod needed for compilation
|
<source lang="fortran"> program simplemod2 use gravity, only : gravforce, massunit, forceunit implicit none print *, 'Force between 2 1 ', massunit ,' masses ', & ' at [1,0,0] and [0,0,1] is' print *, gravforce([1.,0.,0.],[0.,0.,1.],1.,1.), forceunit end program simplemod2 </source> (from samples/procedures/multifilemod/multifilemod.f90) |
.o needed for linking
|
<source lang="make"> FC=gfortran FFLAGS=-O3 -Wall multifilemod: multifilemod.o gravity.o $(FC) -o $@ multifilemod.o gravity.o %.mod: %.f90 $(FC) $(FFLAGS) -c $< multifilemod.o: multifilemod.f90 gravity.mod $(FC) $(FFLAGS) -c $< clean: rm -f *.o *~ *.mod multifilemod </source> (from samples/procedures/multifilemod/Makefile) |
Compiling and running
|
<source lang="bash"> $ make gfortran -O3 -Wall -c gravity.f90 gfortran -O3 -Wall -c multifilemod.f90 gfortran -o multifilemod multifilemod.o gravity.o reposado-$ ./multifilemod Force between 2 1 kilogram masses at [1,0,0] and [0,0,1] is 3.33500033E-11 Newton </source> |
Private and public
|
<source lang="fortran"> module gravity implicit none private character (len=8), parameter, public :: massunit="kilogram" character (len=8), parameter, public :: forceunit="Newton" public :: gravforce real, parameter :: G = 6.67e-11 ! MKS units contains real function distance(x1,x2) implicit none real, dimension(3), intent(in) :: x1, x2 distance = sqrt(sum((x1-x2)**2)) end function distance real function gravforce(x1,x2,m1,m2) implicit none real, dimension(3), intent(in) :: x1,x2 real, intent(in) :: m1, m2 real :: dist dist = distance(x1,x2) gravforce = G * m1 * m2 / dist**2 end function gravforce end module gravity </source> ( from samples/procedures/multifilemod/gravity.f90 ) |
Procedures
|
<source lang="fortran"> module procedures contains function square(x) result(xsquared) implicit none real :: xsquared real, intent(IN) :: x xsquared = x*x end function square function cube(x) implicit none real :: cube real, intent(IN) :: x cube = x*x*x end function cube subroutine squareAndCube(x, squarex, cubex) implicit none real, intent(in) :: x real, intent(out) :: squarex real, intent(out) :: cubex squarex = square(x) cubex = cube(x) end subroutine squareAndCube end module procedures </source> ( from samples/procedures/funcsub/procedures.f90 ) |
Functions
|
<source lang="fortran"> function square(x) result(xsquared) implicit none real :: xsquared real, intent(IN) :: x xsquared = x*x end function square function cube(x) implicit none real :: cube real, intent(IN) :: x cube = x*x*x end function cube subroutine squareAndCube(x, squarex, cubex) implicit none real, intent(in) :: x real, intent(out) :: squarex real, intent(out) :: cubex squarex = square(x) cubex = cube(x) end subroutine squareAndCube </source> ( from samples/procedures/funcsub/procedures.f90 ) |
Procedure interfaces
|
<source lang="fortran"> function square(x) result(xsquared) implicit none real :: xsquared real, intent(IN) :: x xsquared = x*x end function square function cube(x) implicit none real :: cube real, intent(IN) :: x cube = x*x*x end function cube subroutine squareAndCube(x, squarex, cubex) implicit none real, intent(in) :: x real, intent(out) :: squarex real, intent(out) :: cubex squarex = square(x) cubex = cube(x) end subroutine squareAndCube </source> ( from samples/procedures/funcsub/procedures.f90 ) |
Procedure interfaces
|
<source lang="fortran"> function integratefx(xlo, xhi, f, n) |
integrate with trapezoid rule | ....
integer :: i real :: dx, xleft, xright integratefx = 0. dx = (xhi-xlo)/n xleft = xlo do i=0, n-1 xright = xleft + dx integratefx = integratefx + dx*(f(xright)+f(xleft))/2. xleft = xright enddo end function integratefx </source> (from samples/procedures/interface/integrate.f90 ) (from http://en.wikipedia.org/wiki/File:Trapezoidal_rule_illustration_small.svg ) |
---|
Procedure interfaces
|
<source lang="fortran"> function integratefx(xlo, xhi, f, n) |
integrate with trapezoid rule
implicit none real, intent(in) :: xlo, xhi interface function f(x) implicit none real :: f real, intent(in) :: x end function f end interface integer, intent(in) :: n real :: integratefx integer :: i real :: dx, xleft, xright integratefx = 0. dx = (xhi-xlo)/n xleft = xlo do i=0, n-1 xright = xleft + dx integratefx = integratefx + dx*(f(xright)+f(xleft))/2. xleft = xright enddo end function integratefx </source> (from samples/procedures/interface/integrate.f90 ) |
---|
Recursive procedures
|
<source lang="fortran"> recursive function integratefx(xlo, xhi, f, tol) result(integral) |
integrate with trapezoid rule, simpsons rule; | if difference between two is larger than | relevant tolerance, subdivide region. | ...variable declarations as before...
dx = xhi-xlo xmid = (xlo+xhi)/2. trapezoid = dx*(f(xlo)+f(xhi))/2. simpsons = dx/6.*(f(xlo)+4.*f(xmid)+f(xhi)) error = abs(trapezoid-simpsons)/(0.5*(trapezoid+simpsons)) if (error > tol) then |
too coarse; subdivide
integral = integratefx(xlo,xmid,f,tol) + & integratefx(xmid,xhi,f,tol) else integral = trapezoid endif end function integratefx </source> ( from samples/procedures/recursive/integrate.f90) |
---|
Pure procedures
|
<source lang="fortran"> pure subroutine axpy(a, x, y) |
y = y + a*x
implicit none real, intent(IN) :: a, x real, intent(INOUT) :: y y = y + a*x end subroutine axpy subroutine printaxpy(a, x, y) |
y = y + a*x
implicit none real, intent(IN) :: a, x real, intent(INOUT) :: y print *, a, '*', x, ' + ', y, & ' = ', a*x+y y = a*x + y end subroutine printaxpy </source> (from samples/procedures/purity/purity.f90) |
---|
Optional Arguments
|
<source lang="fortran"> recursive function integratefx(xlo, xhi, f, tol) result(integral) |
....
real, intent(in), optional :: tol |
....
real :: errtol |
use parameter if passed, | else use default
if (present(tol)) then errtol = tol else errtol = 1.e-6 endif |
....
if (error > errtol) then |
too coarse; subdivide
integral = integratefx(xlo,xmid,f,errtol) + & integratefx(xmid,xhi,f,errtol) else integral = trapezoid endif end function integratefx </source> (from samples/procedures/optional/integrate.f90) |
---|
Optional Arguments
|
<source lang="fortran"> print *, 'Integrating using default tol' approx = integratefx(0., 2*pi, sinesquared) print *, 'Approximate integral = ', approx print *, 'Exact integral = ', exact print *, print *, 'Integrating using coarser tol' approx = integratefx(0., 2*pi, sinesquared, 0.01) print *, 'Approximate integral = ', approx |
....
</source> (from samples/procedures/optional/optional.f90) |
---|
Keyword Arguments
|
<source lang="fortran"> |
....
print *, 'Integrating using still coarser tol' approx = integratefx(xhi=2*pi, xlo=0., tol=0.5, & f=sinesquared) print *, 'Approximate integral = ', approx </source> (from samples/procedures/optional/optional.f90) |
---|
Procedures and Modules Summary
|
Hands On 2
|
Fortran arrays
|
<source lang="fortran"> program basicarrays implicit none integer, dimension(5) :: a, b, c integer :: i a = [1,2,3,4,5] b = [(2*i+1, i=1,5)]
print *, 'a = ', a print *, 'b = ', b c = a+b print *, 'c = ', c c = a*b + 1 print *, 'a*b+1=', c end program basicarrays </source> (from samples/arrays/basic.f90 ) |
Array constructors
|
<source lang="fortran"> x = [1,2,3,4,5] x = (/1,2,3,4,5/) x = [ (i,i=1,5)] a = [ ((i*j,j=1,3),i=1,5)] </source> |
Elementwise operations
|
<source lang="fortran"> program elementwise implicit none real, dimension(10) :: x,y,z integer :: i real, parameter:: pi = 4.*atan(1.) x = [(2*pi*(i-1)/9.,i=1,10)] y = sin(x) z = x*x print *, x print *, y print *, z end program elementwise </source> (from samples/arrays/elementwise.f90 ) |
Elemental Functions
|
<source lang="fortran"> program elementalfn implicit none real, dimension(10) :: x,y,z integer :: i real, parameter:: pi = 4.*atan(1.) x = [(2*pi*(i-1)/9.,i=1,10)] y = sinesquared(x) z = sin(x)*sin(x) print *, x print *, y print *, z print *,z(::3) contains elemental function sinesquared(x) implicit none real :: sinesquared real, intent(in) :: x sinesquared = sin(x)**2 end function sinesquared end program elementalfn </source> (from samples/arrays/elemental.f90 ) |
Array comparisons
|
<source lang="fortran"> program comparearrays implicit none integer, dimension(5) :: a, b integer :: i a = [1,2,3,4,5] b = [(2*i-3, i=1,5)] print *, 'A = ', a print *, 'B = ', b if (any(a > b)) then print *, 'An A is larger than a B' endif if (all(a > b)) then print *, 'All As ares larger than Bs' else if (all(b > a)) then print *, 'All Bs are larger than As' else print *, 'A, B values overlap' endif end program comparearrays </source> (from samples/arrays/compare.f90) |
Array masks
|
<source lang="fortran"> program mask implicit none integer, dimension(10) :: a logical, dimension(10) :: pos integer :: i a = [(2*i-7, i=1,10)] pos = (a > 0) print '(A,10(I4,1X))','A = ', a print *,'# of positive values: ', count(pos) print *,'Sum of positive values: ', sum(a,pos) print *,'Minimum positive value: ', minval(a,pos) end program mask </source> ( from samples/arrays/mask.f90 ) <source lang="bash"> $ ./mask A = -5 -3 -1 1 3 5 7 9 11 13 # of positive values: 7 Sum of positive values: 49 Minimum positive value: 1 </source> |
Where construct
|
<source lang="fortran"> program wherearray implicit none real, dimension(6) :: a, diva integer :: i a = [(2*i-6, i=1,6)] where (a /= 0) diva = 1/a elsewhere diva = -999 endwhere print *,'a = ' print '(6(F8.3,1X))',a print *,'1/a = ' print '(6(F8.3,1X))',diva end program wherearray </source> (from samples/arrays/where.f90) <source lang="bash"> $ ./where a = -4.000 -2.000 0.000 2.000 4.000 6.000 1/a = -0.250 -0.500 -999.000 0.500 0.250 0.167 </source> |
Forall construct
|
<source lang="fortran"> program forallarray implicit none integer, dimension(6,6) :: a integer :: i,j a = -999 forall (i=1:6, j=1:6, i/=j) a(i,j) = i-j endforall do i=1,6 print '(6(I5,1X))',(a(i,j),j=1,6) enddo end program forallarray </source> (from samples/arrays/forall.f90) <source lang="bash"> $ ./forall -999 -1 -2 -3 -4 -5 1 -999 -1 -2 -3 -4 2 1 -999 -1 -2 -3 3 2 1 -999 -1 -2 4 3 2 1 -999 -1 5 4 3 2 1 -999 </source> |
Array Sections
|
<source lang="fortran"> a([start]:[end][:step]) a = [1,2,3,4,5,6,7,8,9,10] a(7:) == [7,8,9,10] a(:3) == [1,2,3] a(2:4) == [2,3,4] a(::3) == [1,4,7,10] a(2:4:2) == [2,4] a(2) == 2 a(:) == [1,2,3,4,5,6,7,8,9,10] </source> |
Array Sections
|
<source lang="fortran"> program derivative implicit none real, dimension(10) :: x real, dimension(9) :: derivx integer :: i real, parameter:: pi = 4.*atan(1.), h=1. x = [(2*pi*(i-1)/9.,i=1,10)] derivx = ((x(2:10)-x(1:9))/h) print *, derivx do i=1,9 derivx(i) = (x(i+1)-x(i))/h enddo print *, derivx end program derivative </source> (from samples/arrays/derivative.f90) |
Array Sections
|
<source lang="fortran">
cshift(a,1) == [2,3,4,5,1] cshift(a,-1) == [5,1,2,3,4] eoshift(a,1) ==[2,3,4,5,0] eoshift(a,-1)==[0,1,2,3,4] </source> |
Other important array intrinsics
<source lang="fortran">
1,4 reshape([1,2,3,4,5,6],[3,2]) == 2,5 3,6 </source> |
Linear algebra in Fortran
|
Matrix Multiplication Times
<source lang="fortran"> |
...
print *, 'Experiment with matrix size ', n print *, 'Times in seconds.' allocate(a(n,n)) allocate(b(n,n)) allocate(c(n,n)) call random_number(a) call random_number(b) call tick(starttime) do j=1,n do i=1,n c(i,j) = 0. do k=1,n c(i,j) = c(i,j)+a(i,k)*b(k,j) enddo enddo enddo looptime = tock(starttime) call tick(starttime) c = matmul(a,b) matmultime = tock(starttime) call tick(starttime) call sgemm('N','N',n,n,n,1.,a,n,b,n,0.,c,n) sgemmtime = tock(starttime)
print *, 'Triple-loop time: ', looptime print *, 'matmul intrinsic time: ', matmultime print *, 'SGEMM lapack call time:', sgemmtime |
...
</source> |
<source lang="bash"> $ ./matmul 2500 Experiment with matrix size 2500 Triple-loop time: 149.63400 matmul intrinsic time: 10.370000 SGEMM lapack call time: 1.4809999 </source> (gfortran 4.6, compiled -O3 -march=native using Intel MKL 10.3 for sgemm) (program from samples/arrays/matmul.f90) |
---|
Linear algebra in Fortran
<source lang="fortran"> program matvec implicit none integer, dimension(4,5) :: a integer, dimension(5,4) :: at integer, dimension(4,4) :: aat integer :: i a = reshape([(i,i=1,4*5)],[4,5]) at = transpose(a) print *,'A = ' call printmat(a) print *,'A^T = ' call printmat(at) aat = matmul(a,at) print *,'A . A^T = ' call printmat(aat) |
...
</source> |
<source lang="bash"> $ ./matrix A = 1 5 9 13 17 2 6 10 14 18 3 7 11 15 19 4 8 12 16 20 A^T = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 A . A^T = 565 610 655 700 610 660 710 760 655 710 765 820 700 760 820 880 A . A^T subarray = 610 660 710 655 710 765 700 760 820 </source> |
---|
Array sizes and Assumed Shape
integer nx, ny integer a(nx,ny) or worse, integer a(*,ny)
|
<source lang="fortran"> subroutine printmat(a) implicit none integer, dimension(:,:) :: a integer :: nr, nc, i, j nr = size(a,1) nc = size(a,2) do i=1,nr print '(99(I4,1X))', (a(i,j), j=1,nc) enddo end subroutine printmat </source> (from samples/arrays/matrix.f90) |
Allocatable Arrays
|
Allocate(), Deallocate()
|
<source lang="fortran"> program allocarray implicit none integer :: i, n integer, dimension(:), allocatable :: a n = 10 allocate(a(n)) a = [(i, i=2,20,2)] print *,'A = ' print *,a deallocate(a) end program allocarray </source> (from samples/arrays/allocatable.f90 ) |
get_command_argument()
|
<source lang="fortran"> program allocarray2 implicit none integer :: i, n integer, dimension(:), allocatable :: a character(len=30) :: arg if (command_argument_count() < 1) then print *,'Use: allocatable N, '//& ' where N is array size.' stop endif call get_command_argument(1, arg) read( arg,'(I30)'), n print *,'Allocating array of size ', n allocate(a(n)) a = [(i,i=1,n)] print *, a deallocate(a) end program allocarray2 </source> (from samples/arrays/allocatable2.f90) <source lang="bash"> $ ./allocatable2 Use: allocatable N, where N is array size. $ ./allocatable2 3 Allocating array of size 3 1 2 3 $ ./allocatable2 5 Allocating array of size 5 1 2 3 4 5 </source> |
Hands on #3
|
Fortran Pointers
|
<source lang="fortran"> real, target :: x = 3.2 real, pointer:: p p => x p x 3.2 </source> samples/pointers/ptr1.f90 |
Fortran Pointers
|
<source lang="fortran"> real, target :: x = 3.2 real, pointer:: p p => null() p x </source> |
Fortran Pointers
|
<source lang="fortran"> real, target :: x = 3.2 real, pointer:: p1, p2 p1 => x p2 => p1 p1 p2 x 3.2 </source> |
Allocating a pointer
|
<source lang="fortran"> real, pointer:: p allocate(p) p = 7.9 p 7.9 |
</source> samples/pointers/ptr2.f90 |
What are they good for? (1)
|
http://en.wikipedia.org/wiki/File:Singly-linked-list.svg http://en.wikipedia.org/wiki/File:Max-Heap.svg |
What are they good for? (2)
|
<source lang="fortran"> real, target, dimension(7) :: x real, pointer:: p(:) p => x(2:6) p x 1 2 3 4 5 6 7 </source> samples/pointers/views.f90 |
Hands on #4
|
Derived Types and Objects
<source lang="fortran"> type griddomain real :: xmin, xmax real :: ymin, ymax real :: nx, ny real, dimension(:,:) :: u endtype griddomain type(griddomain) :: g g % xmin = -1 g % xmax = +1 </source> |
Derived Types and Objects
|
(from samples/derivedtypes/simple/intervalmath.f90 ) |
Procedures using types
|
(from samples/derivedtypes/intervalfunctions/intervalmath.f90 ) |
Procedures using types
|
samples/derivedtypes/genericintervals/interval2.f90 |
Generic Interfaces
Generic Interfaces
Generic interfaces
Operator overloading
Operator overloading
Generic interfaces
Type bound procedures
Type bound procedures
Type bound procedures
Object oriented programming
Interoperability with other languages
C-interoperability
Calling a C routine from Fortran
Calling a C routine from Fortran
Calling a C routine from Fortran
C strings
Calling Fortran from C
Calling Fortran from C
More advanced
Fortran calling C, which calls Fortran
samples/C-interop/valueref/croutine.csamples/C-interop/valueref/froutine.f90$ ./main
samples/C-interop/valueref/MakefileF2py
* Will only use solver module.
* generates the following header file (hydro_solver.pyf)
$ ipython
Coarrays
Coarrays
Coarrays
Sychronization
Sychronization
1d Diffusion Eqn
1d Diffusion Eqn
1d Diffusion Eqn
1d Diffusion Eqn
$ ./diffusion
Coarray Diffusion
Coarray Diffusion
Coarray Diffusion
Coarray Diffusion
Coarray Diffusion
$ export FOR_COARRAY_NUM_IMAGES=3
gnuplot> plot '1-output.txt' using 1:2, '2-output.txt' using 1:2, '3output.txt' using 1:2, '1-output.txt' using 1:3 with lines title 'Theory',
Parallel Matrix Multiplication
Parallel Matrix Multiplication
Parallel Matrix Multiplication
Parallel Matrix Multiplication
Parallel Matrix Multiplication
Coarray Summary
Closing Hints
Useful Resources
|