mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 17:41:41 +00:00
503 lines
17 KiB
Markdown
503 lines
17 KiB
Markdown
---
|
|
language: Fortran
|
|
contributors:
|
|
- ["Robert Steed", "https://github.com/robochat"]
|
|
filename: learnfortran.f90
|
|
---
|
|
|
|
Fortran is one of the oldest computer languages. It was developed in the 1950s
|
|
by IBM for numeric calculations (Fortran is an abbreviation of "Formula
|
|
Translation"). Despite its age, it is still used for high-performance computing
|
|
such as weather prediction. However, the language has changed considerably over
|
|
the years, although mostly maintaining backwards compatibility; well known
|
|
versions are FORTRAN 77, Fortran 90, Fortran 95, Fortran 2003, Fortran 2008,
|
|
Fortran 2018 and Fortran 2023.
|
|
|
|
This overview will discuss the features of Fortran 2008 since it is the most
|
|
widely implemented of the more recent specifications and the later versions are
|
|
largely similar (by comparison FORTRAN 77 is a very different language).
|
|
|
|
```fortran
|
|
! This is a comment.
|
|
|
|
program example ! declare a program called example.
|
|
|
|
! Code can only exist inside programs, functions, subroutines or modules.
|
|
! Using indentation is not required but it is recommended.
|
|
|
|
! Declaring Variables
|
|
! ===================
|
|
|
|
! All declarations must come before statements and expressions.
|
|
|
|
implicit none ! prevents dynamic declaration of variables
|
|
! Recommended!
|
|
! Implicit none must be redeclared in every function/program/module...
|
|
|
|
! IMPORTANT - Fortran is case insensitive.
|
|
real z
|
|
REAL Z2
|
|
|
|
real :: v, x ! WARNING: default initial values are compiler dependent!
|
|
real :: a = 3, b = 2E12, c = 0.01
|
|
integer :: i, j, k = 1, m
|
|
real, parameter :: PI = 3.14159265 ! declare a constant.
|
|
logical :: y = .TRUE., n = .FALSE. ! boolean type.
|
|
complex :: w = (0, 1) ! sqrt(-1)
|
|
character(len=3) :: month ! string of 3 characters.
|
|
|
|
! declare an array of 6 reals.
|
|
real :: array(6)
|
|
! another way to declare an array.
|
|
real, dimension(4) :: arrayb
|
|
! an array with a custom index -10 to 10 (inclusive)
|
|
integer :: arrayc(-10:10)
|
|
! A multidimensional array.
|
|
real :: array2d(3, 2)
|
|
|
|
! The '::' separators are not always necessary but are recommended.
|
|
|
|
! many other variable attributes also exist:
|
|
real, pointer :: p ! declare a pointer.
|
|
|
|
integer, parameter :: LP = selected_real_kind(20)
|
|
real(kind=LP) :: d ! long precision variable.
|
|
|
|
! WARNING: initialising variables during declaration causes problems
|
|
! in functions since this automatically implies the 'save' attribute
|
|
! whereby values are saved between function calls. In general, separate
|
|
! declaration and initialisation code except for constants!
|
|
|
|
! Strings
|
|
! =======
|
|
|
|
character :: a_char = 'i'
|
|
character(len=6) :: a_str = "qwerty"
|
|
character(len=30) :: str_b
|
|
character(len=*), parameter :: a_long_str = "This is a long string."
|
|
!can have automatic counting of length using (len=*) but only for constants.
|
|
|
|
str_b = a_str//" keyboard" ! concatenate strings using // operator.
|
|
|
|
! Assignment & Arithmetic
|
|
! =======================
|
|
|
|
Z = 1 ! assign to variable z declared above
|
|
j = 10 + 2 - 3
|
|
a = 11.54/(2.3*3.1)
|
|
b = 2**3 ! exponentiation
|
|
|
|
! Control Flow Statements & Operators
|
|
! ===================================
|
|
|
|
! Single-line if statement
|
|
if (z == a) b = 4 ! conditions always need parentheses.
|
|
|
|
if (z /= a) then ! z not equal to a
|
|
! Other symbolic comparisons are < > <= >= == /=
|
|
b = 4
|
|
else if (z .GT. a) then ! z greater than a
|
|
! Text equivalents to symbol operators are .LT. .GT. .LE. .GE. .EQ. .NE.
|
|
b = 6
|
|
else if (z < a) then ! 'then' must be on this line.
|
|
b = 5 ! execution block must be on a new line.
|
|
else
|
|
b = 10
|
|
end if ! end statement needs the 'if'
|
|
|
|
if (.NOT. (x < c .AND. v >= a .OR. z == z)) then ! boolean operators.
|
|
inner: if (.TRUE.) then ! can name if-construct.
|
|
b = 1
|
|
end if inner ! then must name endif statement.
|
|
endif ! 'endif' is equivalent to 'end if'
|
|
|
|
i = 20
|
|
select case (i)
|
|
case (0, 1) ! cases i == 0 or i == 1
|
|
j = 0
|
|
case (2:10) ! cases i is 2 to 10 inclusive.
|
|
j = 1
|
|
case (11:) ! all cases where i>=11
|
|
j = 2
|
|
case default
|
|
j = 3
|
|
end select
|
|
|
|
month = 'jan'
|
|
! Condition can be integer, logical or character type.
|
|
! Select constructions can also be named.
|
|
monthly:select case(month)
|
|
case ("jan")
|
|
j = 0
|
|
case default
|
|
j = -1
|
|
end select monthly
|
|
|
|
do i = 2, 10, 2 ! loops from 2 to 10 (inclusive) in steps of 2.
|
|
innerloop: do j = 1, 3 ! loops can be named too.
|
|
exit ! quits the loop.
|
|
end do innerloop
|
|
cycle ! jump to next loop iteration.
|
|
end do
|
|
|
|
! Goto statement exists but it is heavily discouraged.
|
|
goto 10
|
|
stop 1 ! stops the program, returns condition code 1.
|
|
10 j = 201 ! this line is labeled as line 10
|
|
|
|
! Arrays
|
|
! ======
|
|
array = (/1, 2, 3, 4, 5, 6/)
|
|
array = [1, 2, 3, 4, 5, 6] ! using Fortran 2003 notation.
|
|
arrayb = [10.2, 3e3, 0.41, 4e-5]
|
|
array2d = reshape([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [3, 2])
|
|
|
|
! Fortran array indexing starts from 1.
|
|
! (by default but can be defined differently for specific arrays).
|
|
v = array(1) ! take first element of array.
|
|
v = array2d(2, 2)
|
|
|
|
print *, array(3:5) ! print all elements from 3rd to 5th (inclusive).
|
|
print *, array2d(1, :) ! print first column of 2d array.
|
|
|
|
array = array*3 + 2 ! can apply mathematical expressions to arrays.
|
|
array = array*array ! array operations occur element-wise.
|
|
! array = array*array2d ! these arrays would not be compatible.
|
|
|
|
! There are many built-in functions that operate on arrays.
|
|
c = dot_product(array, array) ! this is the dot product.
|
|
! Use matmul() for matrix maths.
|
|
c = sum(array)
|
|
c = maxval(array)
|
|
print *, minloc(array)
|
|
c = size(array)
|
|
print *, shape(array)
|
|
m = count(array > 0)
|
|
|
|
! Loop over an array (could have used Product() function normally).
|
|
v = 1
|
|
do i = 1, size(array)
|
|
v = v*array(i)
|
|
end do
|
|
|
|
! Conditionally execute element-wise assignments.
|
|
array = [1, 2, 3, 4, 5, 6]
|
|
where (array > 3)
|
|
array = array + 1
|
|
elsewhere(array == 2)
|
|
array = 1
|
|
elsewhere
|
|
array = 0
|
|
end where
|
|
|
|
! Implied-DO loops are a compact way to create arrays.
|
|
array = [(i, i=1, 6)] ! creates an array of [1,2,3,4,5,6]
|
|
array = [(i, i=1, 12, 2)] ! creates an array of [1,3,5,7,9,11]
|
|
array = [(i**2, i=1, 6)] ! creates an array of [1,4,9,16,25,36]
|
|
array = [(4, 5, i=1, 3)] ! creates an array of [4,5,4,5,4,5]
|
|
|
|
! Input/Output
|
|
! ============
|
|
|
|
print *, b ! print the variable 'b' to the command line
|
|
|
|
! We can format our printed output.
|
|
print "(I6)", 320 ! prints ' 320'
|
|
print "(I6.4)", 3 ! prints ' 0003'
|
|
print "(F6.3)", 4.32 ! prints ' 4.320'
|
|
|
|
! The letter indicates the expected type and the number afterwards gives
|
|
! the number of characters to use for printing the value.
|
|
! Letters can be I (integer), F (real), E (engineering format),
|
|
! L (logical), A (characters) ...
|
|
print "(I3)", 3200 ! print '***' since the number doesn't fit.
|
|
|
|
! we can have multiple format specifications.
|
|
print "(I5,F6.2,E6.2)", 120, 43.41, 43.41
|
|
|
|
! 3 repeats of integers (field width = 5).
|
|
print "(3I5)", 10, 20, 30
|
|
|
|
! repeated grouping of formats.
|
|
print "(2(I5,F6.2))", 120, 43.42, 340, 65.3
|
|
|
|
! We can also read input from the terminal.
|
|
read (*, *) v
|
|
read (*, "(2F6.2)") v, x ! read two numbers
|
|
|
|
! To write a file.
|
|
open (unit=12, file="records.txt", status="replace")
|
|
! The file is referred to by a 'unit number', an integer that you pick in
|
|
! the range 9:99. Status can be one of {'old','replace','new'}.
|
|
write (12, "(F10.2,F10.2,F10.2)") c, b, a
|
|
close (12)
|
|
|
|
! To read a file.
|
|
open (newunit=m, file="records.txt", status="old")
|
|
! The file is referred to by a 'new unit number',
|
|
! an integer that the compiler picks for you.
|
|
|
|
read (unit=m, fmt="(3F10.2)") a, b, c
|
|
close (m)
|
|
|
|
! There are more features available than discussed here and alternative
|
|
! variants due to backwards compatibility with older Fortran versions.
|
|
|
|
! Built-in Functions
|
|
! ==================
|
|
|
|
! Fortran has around 200 functions/subroutines intrinsic to the language.
|
|
! Examples -
|
|
call cpu_time(v) ! sets 'v' to a time in seconds.
|
|
k = ior(i, j) ! bitwise OR of 2 integers.
|
|
v = log10(x) ! log base 10.
|
|
i = floor(b) ! converts b to integer by rounding down.
|
|
v = aimag(w) ! imaginary part of a complex number.
|
|
|
|
! Functions & Subroutines
|
|
! =======================
|
|
|
|
! A subroutine runs some code on some input values and can cause
|
|
! side-effects or modify the input values.
|
|
|
|
call routine(a, c, v) ! subroutine call.
|
|
|
|
! A function takes several input parameters and returns a single value.
|
|
! However the input parameters may still be modified and side effects
|
|
! executed.
|
|
|
|
m = func(3, 2, k) ! function call.
|
|
|
|
! Function calls can also be evoked within expressions.
|
|
print *, func2(3, 2, k)
|
|
|
|
! A pure function is a function that doesn't modify its input
|
|
! parameters or cause any side-effects.
|
|
m = func3(3, 2, k)
|
|
|
|
contains ! Start defining the program's internal procedures:
|
|
|
|
! Fortran has a couple of slightly different ways to define functions.
|
|
|
|
integer function func(a, b, c) ! a function returning an integer value.
|
|
! implicit none ! - no longer used in subvariable fields
|
|
integer, intent(in) :: a, b, c ! type of input parameters
|
|
! the return variable defaults to the function name.
|
|
|
|
if (a >= 2) then
|
|
func = a + b + c
|
|
return ! returns the current value at 'func'
|
|
end if
|
|
func = a + c
|
|
|
|
! Don't need a return statement at the end of a function.
|
|
end function func
|
|
|
|
function func2(a, b, c) result(f) ! return variable declared to be 'f'.
|
|
integer, intent(in) :: a, b ! can declare and enforce that variables
|
|
!are not modified by the function.
|
|
integer, intent(inout) :: c
|
|
integer :: f
|
|
! function return type declared inside the function.
|
|
integer :: cnt = 0 ! GOTCHA -
|
|
! assigning a value at initalization
|
|
! implies that the variable is
|
|
! saved between function calls.
|
|
|
|
f = a + b - c
|
|
c = 4 ! changing value of input variable c.
|
|
cnt = cnt + 1 ! count number of function calls.
|
|
|
|
end function func2
|
|
|
|
pure function func3(a, b, c) ! a pure function has no side-effects.
|
|
integer, intent(in) :: a, b, c
|
|
integer :: func3
|
|
|
|
func3 = a*b*c
|
|
|
|
end function func3
|
|
|
|
! a subroutine does not return anything,
|
|
! but can change the value of arguments.
|
|
subroutine routine(d, e, f)
|
|
real, intent(inout) :: f
|
|
real, intent(in) :: d, e
|
|
|
|
f = 2*d + 3*e + f
|
|
|
|
end subroutine routine
|
|
|
|
end program example
|
|
! End of Program Definition -----------------------
|
|
|
|
! Functions and Subroutines declared externally to the program listing need
|
|
! to be declared to the program using an Interface declaration (even if they
|
|
! are in the same source file!) (see below). It is easier to define them within
|
|
! the 'contains' section of a module or program.
|
|
|
|
elemental real function func4(a) result(res)
|
|
! An elemental function is a Pure function that takes a scalar input variable
|
|
! but can also be used on an array where it will be separately applied to all
|
|
! of the elements of an array and return a new array.
|
|
real, intent(in) :: a
|
|
|
|
res = a**2 + 1.0
|
|
|
|
end function func4
|
|
|
|
! Modules
|
|
! =======
|
|
|
|
! A module is a useful way to collect related declarations, functions and
|
|
! subroutines together for reusability.
|
|
|
|
module fruit
|
|
|
|
real :: apple
|
|
real :: pear
|
|
real :: orange
|
|
|
|
end module fruit
|
|
|
|
module fruity
|
|
! Declarations must be in the order: modules, interfaces, variables.
|
|
! (can declare modules and interfaces in programs too).
|
|
|
|
use fruit, only: apple, pear ! use apple and pear from fruit module.
|
|
implicit none ! comes after module imports.
|
|
|
|
! By default all module data and functions will be public
|
|
private ! Instead set default to private
|
|
! Declare some variables/functions explicitly public.
|
|
public :: apple, mycar, create_mycar
|
|
! Declare some variables/functions private to the module (redundant here).
|
|
private :: func4
|
|
|
|
! Interfaces
|
|
! ==========
|
|
! Explicitly declare an external function/procedure within the module
|
|
! (better in general to put functions/procedures in the 'contains' section).
|
|
interface
|
|
elemental real function func4(a) result(res)
|
|
real, intent(in) :: a
|
|
end function func4
|
|
end interface
|
|
|
|
! Overloaded functions can be defined using named interfaces.
|
|
interface myabs
|
|
! Can use 'module procedure' keyword to include functions already
|
|
! defined within the module.
|
|
module procedure real_abs, complex_abs
|
|
end interface
|
|
|
|
! Derived Data Types
|
|
! ==================
|
|
! Can create custom structured data collections.
|
|
type car
|
|
character(len=100) :: model
|
|
real :: weight ! (kg)
|
|
real :: dimensions(3) ! i.e. length-width-height (metres).
|
|
character :: colour
|
|
contains
|
|
procedure :: info ! bind a procedure to a type.
|
|
end type car
|
|
|
|
type(car) :: mycar ! declare a variable of your custom type.
|
|
! See create_mycar() routine for usage.
|
|
|
|
! Note: There are no executable statements in modules.
|
|
|
|
contains
|
|
|
|
subroutine create_mycar(mycar)
|
|
! Demonstrates usage of a derived data type.
|
|
type(car), intent(out) :: mycar
|
|
|
|
! Access type elements using '%' operator.
|
|
mycar%model = "Ford Prefect"
|
|
mycar%colour = 'r'
|
|
mycar%weight = 1400
|
|
mycar%dimensions(1) = 5.0 ! default indexing starts from 1!
|
|
mycar%dimensions(2) = 3.0
|
|
mycar%dimensions(3) = 1.5
|
|
|
|
end subroutine create_mycar
|
|
|
|
subroutine info(self)
|
|
class(car), intent(in) :: self
|
|
! 'class' keyword used to bind a procedure to a type here.
|
|
|
|
print *, "Model : ", self%model
|
|
print *, "Colour : ", self%colour
|
|
print *, "Weight : ", self%weight
|
|
print *, "Dimensions: ", self%dimensions
|
|
|
|
end subroutine info
|
|
|
|
real pure function real_abs(x)
|
|
real, intent(in) :: x
|
|
|
|
if (x < 0) then
|
|
real_abs = -x
|
|
else
|
|
real_abs = x
|
|
end if
|
|
|
|
end function real_abs
|
|
|
|
real pure function complex_abs(z)
|
|
complex, intent(in) :: z
|
|
! long lines can be continued using the continuation character '&'
|
|
|
|
complex_abs = sqrt(real(z)**2 + &
|
|
aimag(z)**2)
|
|
|
|
end function complex_abs
|
|
|
|
end module fruity
|
|
|
|
! ISO Standard Fortran 2008 introduced the DO CONCURRENT construct to allow you
|
|
! to express loop-level parallelism
|
|
|
|
integer :: i
|
|
real :: array(10)
|
|
|
|
DO CONCURRENT (i = 1:size(array))
|
|
array(i) = sqrt(real(i)**i)
|
|
END DO
|
|
|
|
|
|
! Only calls to pure functions are allowed inside the loop and we can declare
|
|
! multiple indices:
|
|
|
|
integer :: x, y
|
|
real :: array(8, 16)
|
|
|
|
do concurrent (x = 1:size(array, 1), y = 1:size(array, 2))
|
|
array(x, y) = real(x)
|
|
end do
|
|
|
|
! loop indices can also declared inside the contruct:
|
|
|
|
real :: array(8, 16)
|
|
|
|
do concurrent (integer :: x = 1:size(array, 1), y = 1:size(array, 2))
|
|
array(x, y) = real(x)
|
|
end do
|
|
```
|
|
|
|
### More Resources
|
|
|
|
For more information on Fortran:
|
|
|
|
+ [wikipedia](https://en.wikipedia.org/wiki/Fortran)
|
|
+ [Fortran-lang Organization](https://fortran-lang.org/)
|
|
+ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features)
|
|
+ [fortranwiki.org](http://fortranwiki.org)
|
|
+ [www.fortran90.org/](http://www.fortran90.org)
|
|
+ [list of Fortran 95 tutorials](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/)
|
|
+ [Fortran wikibook](https://en.wikibooks.org/wiki/Fortran)
|
|
+ [Fortran resources](http://www.fortranplus.co.uk/resources/fortran_resources.pdf)
|
|
+ [Mistakes in Fortran 90 Programs That Might Surprise You](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html)
|