This started from a short syntax refresher on Fortran 90 that I found on the web from the University of Munich: F90 For Beginners. Thanks to the original authors! I’ve reformatted the text into org mode for my own reference, annotation. As I learn more Fortran, notes and ideas on usage will end up in here until I’ve internalized enough that I don’t need to refer to this any more.
As it’s now 2024 and the Fortran 2023 standard is released, I expect to have to update this to use F95, maybe 2003? My last serious Fortran was on an IBM 360 in 1978, but I know it wasn’t FORTRAN 77. I’ve got a lot of catching up to do.
After a few weeks of using Fortran daily with Emacs on the Mac, it’s grown on me. I need to update the text below somewhat, but the spirit of the changes described below from the Fortran I learned in college holds true. More features are added every few years, but for my use Fortran 95/2003 provide most everything I want.
If you can do it in Pascal or C, you can do it in Fortran. It’s just a matter of preference.
I’m making a pass to remove anachronisms from my original attempt and absorbing the F90 For Beginners material and hope this will serve as a quick set of pointers and reminders. I don’t expect anyone else would have much use for this level of reference, but if you find it helpful all the better.
There are several decent overviews of the basic language syntax suitable for undergraduate programming classes. The one listed in the introduction was one of the better ones I reviewed.
A recent version of Modern Fortran Explained by Metcalf, Reid, & Cohen appears to be the best language reference. I have the 2018 version (red cover). If you don’t live on the bleeding edge of HPC, I get the impression that the Fortran 2008 language is more than adequate for most hobbyist programming.
I have found Modern Fortran: Style and Usage 1st Edition by Clerman & Spector an enjoyable read. Rules of thumb explained clearly. Strunk & White for the programmer.
I have, but have yet to read in depth, Modern Fortran: Building efficient parallel applications 1st Edition, by Curcic. It was a COVID lock down deal from the publisher and I saw enough to make me want to explore Fortran. This book is why I started using Fortran for my Advent of Code work.
I plan to purchase Modern Fortran in Practice 1st Edition by Markus once I’ve completed the 2022 and 2023 Advent of Code exercises and made some progress on Curcic’s book.
- Fortran Language Discourse is a helpful, low volume but also low latency, online community of knowledgeable people, including some of the authors listed above.
- Fortran Programming Language Organization website.
- r/fortran is out there, but I haven’t found much of use on it.
To do anything programming related on the Mac you’ll need to install the XCode Command Line Tools. Once that’s done, pick a package manager and use it. Several are available. Many in the Fortran community are in the Big Data business and a lot of their tooling comes from Conda. Brew and MacPorts are the other options. I dislike the Conda environment switching, so I only use Conda for lfortran related work. I get everything else from Brew.
Here’s the bits you’ll want to lay down on your system:
- XCode command line tools from Apple
xcode-select --install
- Homebrew via their macOS .pkg installer.
- CMake, fprettify, fortls, and emacs-mac with
brew install cmake fprettify fortls emacs-mac
. There are a lot of dependencies that come down with these, but they don’t get in the way or take much space.
Reference sites:
- MacPorts at https://www.macports.org
- Homebrew at https://brew.sh
- CONDA at https://conda.io and more specifically for the hobbyist miniconda https://docs.anaconda.com/free/miniconda/index.html
The LLVM Flang doesn’t seem to get much use. The two compilers referenced most are gfortran and ifort (Intel). An alpha stage lfortran is in active development.
I’m doing this on an Apple silicon Mac Book Air. Apple’s infrastructure is built on LLVM so there are a few hoops to jump through if you want to use gfortran and gdb. Any of the options for just the compiler work fine, but the only gdb setup I’ve seen working is from MacPorts
Do the following to get a working debug capable gfortran compiler, most of which require ~sudo~.
- install macports from https://www.macports.org/install.php MacPorts
- install gcc and friends ~port install gcc13 gdb cgdb~
- select the version to use ~port select –set gcc=mp-gcc13~
- code sign the gdb executable (ggdb). You can find various sources for this but I used https://sourceware.org/gdb/wiki/PermissionsDarwin these instructions, but the interactive approach instead of a shell script.
After the above, gfortran and ggdb (note the extra “g”) are available and work well.
Sadly, the above doesn’t work consistently. I see a lot of frustration when searching the web about this. After experimentation, I decided to switch rather than fight. A dev container
for Docker
is a workable solution. I created an Ubuntu image for AARCH64 with tooling for gfortran and gdb. Starting that container with Fortran source directories shared lets me build and use gdb when I need to. Most of the time I work in MacOS and it’s mostly no issue to switch. I want to make this a bit more transparent, but it works.
I tried using straight Emacs, but my hands know vim key binds too well. I’d rather switch than fight. Follow the installation steps from https://github.com/doomemacs/doomemacs to get the basic setup. For Fortran support, enable it in the languages section of init.el
along with a few other things. Here are the pertinent sections from my init.el
with minimal context:
:editor
(format +onsave) ; automated prettiness
:tools
lookup ; navigate your code and its documentation
lsp ; M-x vscode
make ; run make tasks from Emacs
tree-sitter ; syntax and parsing, sitting in a tree...
:lang
(fortran +lsp) ; in FORTRAN, GOD is REAL (unless declared INTEGER)
If you have installed fortls
and fprettify
, things will just work. They are pretty much zero configuration. I have the following in my config.el
to tweak some of the formatting rules:
For editing, Doom Emacs has decent Fortran support for the level of work I’m doing. Specifically, make
, lsp
for basic code navigation, and fprettify
for formatting code. While I don’t like all the rules, formatting code on save is the right way to go.
I haven’t had to get too deep into this aspect yet. The Fortran Package Manager (fpm) is available and I built it from GitHub, but most will probably want to install it from brew. CMake seems to be common but so far I’m still at the simple makefile stage for my work.
This is unexplored territory. There are several tools available but I haven’t settled on one. I prefer to find a Fortran-only solution which will limit my choices.
- Always use
implicit none
. It should be specified in each scope. - Use underscore instead of camel or Pascal casing for names.
- Factor out code into modules when possible.
- Use FPM.
- Use CMake.
- Use a testing framework. Two I have found that look good for my needs and style are:
use
directives must be the first thing in a scope, even before implicit none
.
There are two standard modules that should be available on all compilers that I expect to use.
iso_fortran_env
provides several named integer constants that can be used refine variable types by kind, common unit numbers for standard input/output, and some error status codes. Fortran 2003 and later.iso_c_binding
provides intrinsic functions for low level access and named constants to help define interfaces to C based APIs.
It seems to be the common practice to specify only what you expect to use from a module. The syntax for this is use module, only: name
. It is also possible to provide an alias via use module, only: alias => name
.
Some examples:
use iso_fortran_env, only: sp => real32, dp => real64, qp => real128
! If we decide later to use iso_c_binding instead of iso_fortran_env:
! use iso_c_binding, only: sp => c_float, dp => c_double, qp => c_float128
! resolve name collision
use module1, only: mod1_init => initialize
use module2, only: mod2_init => initialize
call mod1_init
call mod2_init
Modern Fortran is a line oriented language but the old card column restrictions no longer apply. Comments begin with an exclamation point and extend to the end of the line. Statements are separated by either the end of the line or a semicolon. A physical line is limited to 132 characters. Lines can be continued with an ampersand.
! code all on one line
if (a > 5) then; b = 7; else; b = 8; end if
! is the same as this more readable code
if (a > 5) then
b = 7
else
b = 8
end if
! and lines can be split as
a = 3 * b + &
7 * c
I’m sure it’s more diverse than this, but everything I see is Western Latin alphabetic and numeric characters. The special characters used for non-identifier tokens and grouping are a small set. You could still easily type in source on a card punch machine.
- a-z (or A-Z), case insensitive
- 0-9
- _ an underscore
- + - / * ! . , ( ) < > ; : ’ ” = % [ ]
Identifier names can be up to 31 characters long, must start with an alphabetic character, followed by any of alphabetic, numeric, or underscore characters.
Declare variables explicitly
Always begin a program with implicit none
. Otherwise the old Fortran 77 convention of names beginning with I-N imply the variable is an integer, while all others are real.
Programs should be named, as in program test
, and conclude with end program test
’.
The elementary or basic data types are generic. They are:
- integer
- real
- complex (real and imaginary parts)
- character (fixed length)
- logical (true, false)
kind
can be used to specify expected precision or other implementation dependent properties of numbers. See also the iso_fortran_env
and iso_c_binding
modules. kind=n
where n
is an integer.
In earlier Fortran implementations, star notation was used to indicate size which carried with it an implied precision. Then kind=n
was introduced with the following meanings in GNU compilers.
- 1 means the default size for the generic type in the implementation. GNU says these are typically REAL*4, INTEGER*4, LOGICAL*4, and COMPLEX*8. Full words from my assembly days.
- 2 means twice the default. Double words.
- 3 means the type occupies the storage required for a default character in the implementation. Typically one byte, as in INTEGER*1 and LOGICAL*1. Further increments by 3 double the storage of the prior 3-kind. This is not available in all Fortran implementations.
- 5 means half the default. INTEGER*2, LOGICAL*2. Half words.
- 7 integer only, and refers to the smallest sized pointer in the implementation that can address a byte or array element or sub-string.
Fortran 2003 and 2008 cleaned this up. In addition to providing standard names for kinds (see later) there are intrinsic functions to determine if the required precision or range are available.
Fortran 2003 introduced the iso_fortran_env
module, and Fortran 2008 introduced the following standard names that mean what most of us would expect.
- INT8
- INT16
- INT32
- INT64
- REAL32
- REAL64
- REAL128
Literal constants also have type and kind. A common idiom is to append _<kind> to a constant. The <kind> name could be a one of the above, or a named constant created with the selected_real_kind(mantissa_digits, exponent_range)
or selected_int_kind(digits)
functions.
! example, use one of sp and dp below and then the declarations follow
! mnemonically sp for single precision, dp for double precision, etc
integer, parameter :: sp = selected_real_kind(6, 37)
integer, parameter :: sp = kind(1.)
integer, parameter :: dp = selected_real_kind(15,307)
integer, parameter :: dp = kind(1.d0)
integer, parameter :: qp = selected_real_kind(33,4931)
integer, parameter :: i4 = selected_int_kind(9)
integer, parameter :: i8 = selected_int_kind(16)
real (kind=sp) :: x,y ! or: real (sp) :: x,y
real (kind=dp) :: a,b ! ("double precision")
! other code
a = 3.14159_dp
dword = 8_i4
There are some heated debates on how to properly use the selected_xxxxx_kind()
intrinsic functions, but following creates sets r8
to the kind
that will support reals with at least 15 decimal digits of precision and an exponent range of at least 9. This might map to double precision floating point (REAL64) but that is not guaranteed. All that is guaranteed is if selected_real_kind returns a positive value, that kind will support the requested precision.
integer, parameter :: r8 = selected_real_kind(15,9)
real(kind=r8) :: a
Derived or programmer defined types are created by the type…end type
construct. For example:
type person
character (len=20) :: name
integer :: age
end type person
type(person) :: myself
myself % name = 60
The percent sign %
is used to access the named member of the type, much like .
is used in other languages.
Variables can be further refined by attributes describing usage, source, scope, and so forth.
dimension
allocatable
(dynamically acquired and sized)parameter
(within this scoping unit, this is a constant)intent
(in, out, “inout” for arguments)len
save
(static)pointer
public
private
optional
For example:
integer, parameter :: np = 3
real, dimension(np) :: b ! vector of length 3
integer :: i
do i = 1, np
b(i) = sqrt(i)
end do
You might be tempted to add an initialization to the variable definition. DO NOT DO THIS While integer, parameter :: np = 3
creates a constant named np
, integer :: counter = 1
implies save
and creates a static variable. counter
will keep its value across function/subroutine invocations.
Fortran has all the traditional operators and intrinsic functions:
- + - * /
- ** (power)
- sin cos tan atan, hyperbolic varieties of these
- rand, int, real
- min, max
- exp log log10
- sqrt
- and so many more
Operations use the precision of the most precise operand. It’s not clear to me yet if or when lower precision operands are promoted, but I don’t expect many surprises in my code.
- 1/2 ==> 0
- 1./2 ==> 0.5000000
- 1/2. ==> 0.5000000
- 1/2._dp ==> 0.50000000000000
- 1+(1.,3) ==> (2.000000,3.000000)
Logical and comparison operators use the original .xx. style and have some symbolic representations as well.
- .and.
- .or.
- .not.
- .eq. or ==
- .ne. or /=
- .gt. or >
- .ge. or >=
- .lt. or <
- .le. or <=
- .eqv. and .neqv. to compare logical variables
- .llt., .lle., .lgt., .lge. for lexical comparison of characters
- // (string concatenation)
For counting or fixed length loops, use do/end do
. do var=begin, end, increment
where increment is optional and assumed to be one if omitted. enddo
is legal but end do
is preferred. If begin > end and increment is not provided, the loop does not execute.
! executes with i of 1, 3, 5, 7, and 9
do i = 1, 10, 2
print *, i, i**2
end do
! does not execute
do i = 10, 1
print *, i, i**2
end do
! executes for i = 10, 8, 6, 4, 2
do i = 10, 1, -2
print *, i, i**2
end do
! executes for i = 10
do i = 10, 10
print *, i, i**2
end do
Use do while
for non counting conditional loops. For example:
do while(x .lt. .95)
x = 3.8 * x * (1. - x)
end do
An infinite loop can be coded with just do
:
do
print *, 'enter a number, negative to exit'
read *, x
if (x .lt. 0.) exit
print *, 'the square root of ', x, ' is ', sqrt(x)
end do
In some situations, an implied do
can be used. For example:
print *, (i, i**2, i=1, 100)
exit
may be used to end a loop. This is the same as break
in other languages. Control passes to the statement after the end do
. The name of the loop to cycle back to can be specified and works correctly when dealing with nested loops.
real, dimension(327) :: a
integer :: i
! … do something here to populate a with increasing numbers
do i = 1, 327
if (a(i) .gt. 1.2345) exit
end do
! loop control variable is reliable at exit
if (i .eq. 327 + 1) then
print *, 'index not found'
stop
else
print *, 'index', i, ': value =', a(i)
end if
cycle
starts a new cycle of a loop. (continue
in other languages). The name of the loop to cycle back to can be specified and works correctly when dealing with nested loops. For example:
real, dimension(5,5) :: a
integer :: i, j
call random_number(a)
do i = 1, 5
print *, (a(i, j), j = 1, 5)
enddo
outer: do i = 1, 5 ! all matrix rows
inner: do j = 1, 5 ! matrix columns, search loop:
! searches for first number > 0.8 in row i
if (a(i, j) .gt. 0.8) then
print *, 'row', i, ': column', j, ':', a(i, j)
cycle outer
end if
end do inner ! named do requires named end do
print *, 'row ', i, ': nothing found'
end do outer
Block structuring seems to have come along with the Fortran 77 standard. Some things can be done in one statement or more completely in a block. The logical and arithmetic if
statements from Fortran IV are still available but I believe only the logical form should be used.
- logical
if
:IF (logical expression) <any statement other than DO or IF>
would be useful forexit
orcycle
in loops. - arithmetic
if
:IF (numeric expression) <label if negative>,<label if zero>,<label if positive>
should not be used.
if then else end if
and variations are available.
! a single statement
if (x > 0.) x = sqrt(x)
! a block style
if (x > 0.) then
x = sqrt(x)
y = y - x
end if
! if-then-else
if (x < 0.) then
print *, 'x is negative'
else
if (x > 0.) then
print *, 'x is positive'
else
print *, 'x must be zero'
end if
end if
! or even better for the above, if-then-else if-…
if (x < 0.) then
print *, 'x is negative'
else if (x > 0.) then
print *, 'x is positive'
else
print *, 'x must be zero'
end if
The select case
can be used for picking among ordinal values (integer, boolean, and character).
read *, i
select case(i)
case(1)
print *, 'excellent'
case(2, 3)
print *, 'meh'
case(4:6)
print *, 'for shame!'
case default
print *, 'unpossible'
end select
Terminal oriented with minimal formatting. Older code may use write(*,*)` or ~read(*,*)
but for the terminal or standard input and output, print *,
and read *,
are preferred.
real :: a
print *, 'enter a real number'
read *, a
print *, 'input was ', a
The (*,*)
is a shorthand for (unit=*, fmt=*)
. Formatting will come along soon.
To open a file for writing:
open (1, file='output')
write (1,*) 'hello world'
close (1)
Error or event handling on files are specified as keyword operands in the (unit,…) portion of the statement.
Two options are end=
for end of file, and err=
for an error. This example uses line numbers but I hope that isn’t the only option.
program read
implicit none
integer, parameter :: m = 10
integer :: i
real, dimension (m) :: a
real :: t
open (77, file='numbers')
i = 0
do
read (77, *, end=200, err=100) t
i = i + 1
if (i > m) then
print *, 'array too small! increase m and recompile!'
close (77)
stop
end if
a(i) = t
end do
100 continue
print *, 'read error in line ', i + 1
close (77)
stop
200 continue
print *, i, ' numbers read'
close (77)
print *, a(1:i)
end program read ! program
After some research, the iostat=
parameter should be used instead. The status can be checked in a visible and readable way in code. Negative values are end of file, 0 is normal completion, while positive values are an error.
Reading and writing to character variables can use a concept of an “internal file”. Character index addressing, slicing, and concatenation are also available. I need to write up a complete section on character variables.
character (len=20) :: a
write(a, *) "Hello, world!"
Classic formatted input/output is still available, but seems discouraged in favor of list-directed input/output. This is fmt=*
.
write (*, 700) 1, 1.23, (7., 8.), 'Hello', .true.
write (*, 701)
write (*, 702)
700 format (i5, e12.4e3, 2f8.2, 1x, a3, l7)
701 format ('12345678901234567890123456789012345678901234567890')
702 format (' 1 2 3 4 5')
write(*,'(i5, e12.4e3, 2f8.2, 1x, a3, l7)') 1, 1.23, (7.,8.), 'Hello', .true.
Produces:
1 0.1230E+001 7.00 8.00 Hel T 12345678901234567890123456789012345678901234567890 1 2 3 4 5 1 0.1230E+001 7.00 8.00 Hel T
Format definitions can be a separate labeled statement, a character constant, or a character variable. Parenthesis are part of the format specification in this form. These are all equivalent:
real :: x
character (len=8) :: a
write (*, 123) x
123 format (es10.2)
write(*, '(es10.2)') x
a = '(es10.2)'
write (*, a) x
Format descriptors can be used to format output allowing for leading blanks (or right alignment if you prefer), different number base, precision, and to select between exponential, scientific, and engineering floating point conventions.
- integers
- i decimal
- b binary (BOZ literals are a thing)
- o octal
- z hexadecimal
- real
- d
- e exponential (0.nnnnnnexx)
- f
- g
- es exponential using scientific convention (n.nnnnnexx)
- en exponential using engineering convention (powers of 10 by orders of magnitude, 12.378e03)
- logical
- l (ell) produces T or F for .true. or .false.
- character
- a
- other
- n (number) repeat following n times, as in 3f8.2
- x space
- / new line
- ‘…’ literal text
- (…) for grouping
- p scale
Arrays have dimensions. An array can be a vector (one dimension) or matrix (multiple dimensions). Fortran allows up to seven fifteen dimensions. In Fortran the default starting subscript is 1. Bravo. Start and end bounds can be specified to override the default.
real, dimension(2, 2) :: a ! 2x2, (1,1) -> (2,2)
real, dimension(3:4, -2:-1) :: q ! also a 2x2, (3,-2) -> (4,-1)
integer, parameter :: m=27, n=123
real, dimension(n, m) :: b, c
real, dimension(m) :: x, y
Intrinsic functions can describe the array (reflection). Referring to the prior definitions:
shape(b) !-> 123, 27 (= n,m)
size(b) !-> 3321 (= 123*27)
size(b, 1) !-> 123
size(b, 2) !-> 27
lbound(q, 2) !-> -2
ubound(q, 1) !-> 4
Array constructors provide a constant or initialization of an array:
x = (/ 1., 2., 3., 4., 5. /)
y = (/ (0.1*i, i=1, m) /) ! -> 0.1 0.2 0.3 0.4 0.5 …
This technique only works for single dimensional arrays. It is possible to reshape
an array, but be aware that in Fortran the first index cycles first. A Fortran two dimensional array is not laid out as it would be in C, where each row (or first index) can be viewed as holding another array.
This is column major order. Fortran and Julia store arrays in column major order, while C and Pascal store them in row major order.
a = reshape( (/ 1., 2., 3., 4. /), (/ 2, 2 /) )
Before the reshape
the elements are a(1) = 1., a(2) = 2., a(3) = 3., a(4) = 4., while afterwards they are a(1,1) = 1., a(2,1) = 2., a(1,2) = 3., a(2,2) = 4.!
Fortran provides operations for complete arrays, removing the need to write code to iterate over elements in many situations.
real, dimension(n,m) :: b, c
b = sin(c)
! is much better than
real, dimension(n, m) :: b, c
integer :: i, j
do i = 1, n
do j = 1, m
b(i, j) = sin(c(i, j))
end do
end do
Similarly, you can operate on slices or sections of arrays if they are the same shape.
real, dimension(10) :: u, v
real, dimension(5, 4) :: w
u(2:10, 2) = sin(w(:,1))
v(1:3) = 5 ! or v(:3) = 5
So u(i:j:k) means those elements of u starting from index i until index j, but only every k-th element. k is optional and defaults to 1. Omitting i or j implies the lower or upper bound.
Where blocks allow selection or filtering by cell contents (e.g., avoid division by 0):
where (x == 0)
y = 1.
else where
y = sin(x) / x
end where
Array level operations and do loop variations have different semantics. Array level operations evaluate the entire right side of the expression. The following are not equivalent:
do i = 2, m
x(i) = x(i) + x(i - 1)
end do
! versus
x(2:m) = x(2:m) + x(1:m-1)
Character variables are fixed length, which I should have no problem adjusting to given my career as an assembly language programmer. In my work so far I see them all padded on the right. Slicing by byte index works but remember those blanks!
character(len=255) :: str
str = ""
str = str//"asdf" ! wrong, str will be "" after this statement
str = trim(str)//"asdf" ! right, str will be "asdf" after this statement
The specific syntax for passing arrays and allowing for non-compile-time constant dimensions isn’t completely clear to me yet. The snippets and recommendations from the original document are helpful, but this needs to be clarified.
Here is a simple example.
program main
implicit none
integer i
real :: x, y, sinc
do i=0, 80, 2
x = i / 10.
y = sinc(x) ! ??? implicit function ???
print *, x, y
end do
call output(0, 80, 2) ! ??? explicit subroutine ???
end program main
function sinc(x)
implicit none
real :: x, sinc
if (x .eq. 0.) then
! be careful with comparison to real numbers because of rounding errors
! better: if (abs(x).lt.1.e-16) then
sinc = 1.
else
sinc = sin(x) / x
endif
end function sinc
subroutine output(a, e, s)
integer, intent(in) :: a, e, s
real :: x, y, sinc
integer :: i
open(1, file='sinc.data')
do i = a, e, s
x = i / 10.
y = sinc(x)
write (1,10) x, y
end do
close(1)
10 format(2e14.6)
end subroutine output
Function sinc
above cannot be called with array arguments as it is defined above. Who reserves the storage for arrays? Must the size be fixed at compile time or can it change at run time?
program main
implicit none
! …
integer, parameter :: n=100
real, dimension(n) :: a, b, c, d
call sub(a, b, c, d, n)
end program main
subroutine sub(u, v, w, x, m)
real, dimension(100) :: u ! constant size
real, dimension(m) :: v ! adjustable size
real, dimension(*) :: w ! assumed size
real, dimension(:) :: x ! assumed shape (needs interface block in caller)
real, dimension(100) :: y ! constant size (local)
real, dimension(m) :: z ! automatic (local)
real, dimension(:), allocatable :: t ! deferred-shape (local)
! …
allocate(t(m))
! …
print *, u, v, x, y, z, t ! assumed size needs explicit indexing
print *, w(1:m) ! because upper bound is unknown
! …
deallocate(t)
end subroutine sub
The original of this recommends using either adjustable size (passed as a parameter) or assumed shape (requires an interface
block in the caller, see later). There may be limits on the maximum size of automatic arrays.
Array slices or sections are a special case of ‘assumed shape’ and also require an interface
block. Upcoming.
program main
implicit none
interface
subroutine sub(x)
real, dimension(:) :: x
end subroutine sub
end interface
integer, parameter :: n=100
real, dimension(n) :: a
call sub(a(1:50:3))
end program main
subroutine sub(x)
real, dimension(:) :: x
print *, shape(x)
end subroutine sub
Interface blocks should be collected in a specific module
. Modules are described next.
While not exactly the same, a module is similar to a Pascal unit. They are included by the use
directive and are best kept in separate source files.
- Declare subroutines, functions, and interface blocks.
- Global variables can be defined in a module and explicitly exposed on the use directive.
- Supporting variables and implementation details can be hidden (private) to the module.
Modules can also be used to control precision by the definition of kind-numbers.
module my_type
! Useful trick: precision of following routines can be easily changed
! from single to double precision by alternatively
! commenting/uncommenting the statements defining sp
integer, parameter :: ib = selected_int_kind(9) !integer*4
integer, parameter :: sp = selected_real_kind(6,37) !real*4 or sp = kind(1.)
! integer, parameter :: sp = selected_real_kind(15,307) !real*8 or dp = kind(1.d0)
end module my_type
program random
use my_type ! use statement(s) must be given before further declarations
implicit none
integer(ib) :: i
real(sp) :: x
do i = 1,5
call random_number(x)
print *,x
end do
end program random
An example of global variables.
module common
implicit none
real :: x, y=5.
end module common
program test
implicit none
call sub1
call sub2
call sub3
end program test
subroutine sub1
use common, only: x ! note that common.y is not visible
implicit none
real :: y
x = 3.
y = 1.
print *, x, y
end subroutine sub1
subroutine sub2
use common, only: x
implicit none
print *, x
x = 7.
end subroutine sub2
subroutine sub3
use common ! both x and y are visible
implicit none
print *, x, y
end subroutine sub3
The following is rather raw and I haven’t grokked it yet, but this shows how to better handle assumed shape parameters, among other things. I have done minimal reformatting but a real pass through this to fully understand it and link back to the subroutine section is still needed.
Declaration of subroutine(s) or corresponding interfaces in a module:
No explicit interface block if the subroutine is ‘contained’ in the module.
module mymod
! no explicit interface block if routine is "contained"
contains
subroutine mysub(x)
implicit none
real, dimension(:) :: x
write(*,*) shape(x)
end subroutine mysub
end module mymod
program main
use mymod
implicit none
integer, parameter :: n=100
real, dimension(n) :: a
call mysub(a(1:50:3))
end program main
An interface block is needed if the routine is defined elsewhere.
module mymod
interface
subroutine mysub(x)
implicit none
real, dimension(:) :: x
end subroutine mysub
end interface
end module mymod
program main
use mymod
implicit none
integer, parameter :: n=100
real, dimension(n) :: a
call mysub(a(1:50:3))
end program main
subroutine mysub(x)
implicit none
real, dimension(:) :: x
print *, shape(x)
end subroutine mysub
And finally an example of using an interface block to overload a function definition to allow for passing scalars or arrays.
module sincm
interface sinc
module procedure sinca, sincs
end interface sinc
contains
function sinca(x) result(z) ! array
implicit none
real, dimension(:) :: x
real, dimension(size(x)) :: z
where(x == 0.)
z = 1.
elsewhere
z = sin(x) / x
endwhere
end function sinca
function sincs(x) result(z) ! scalar
implicit none
real :: x,z
if(x == 0.) then
z = 1.
else
z = sin(x) / x
endif
end function sincs
end module sincm
program main
use sincm
implicit none
integer, parameter :: m=100
real, dimension(m) :: x,y
integer :: i
x=(/ (0.2*i,i=1,m) /)
y=sinc(x) ! array sinc
write(*,777) (i,x(i),y(i),i=1,m)
777 format(i5,2e12.4)
print *, sinc(1.23) ! scalar sinc
end program main