This notes were possible by reading from several sources, but the book Introduction to Programming with Fortran 1 was specifically useful because its large set of examples and progressive definitions. My first approach to Fortran 90 relies on the manual Physique Numérique2 that I recommend to those people interested in an introduction to Computational Physics based in Fortran.
General information
Fortran is one of the oldest computer programming languages that is still alive (together with Lisp). It is not a main language, but it has its niche in scientific computing applications. There are several blog entries that a I think useful to understand recent updates in the language itself. First in github j3-fortran there is a list of proposals that are related, as far as I understood, with LFortran new compiler.
Other important source of updates of fortran comunity is centralized in the fortran-lang github group. There you have a webpage fortran-lag.org where you can find news and information of the related projects. From them I highlight two: the fortran-package-manager fpm and the standard-lib. The last one relies on few python libraries to create an interesting tool.
For further information you can read two summaries at resurrecting-fortran and 1st-year of fortran-lang.
Useful Tools
Name | Goal | Licence | Docs | Link |
---|---|---|---|---|
findent | Beautify f90, f2003 code | Open-Source | info | sourceforge |
tags (ctags) | Syntax higlight | Open-Source | info | sourceforge |
f2c | Fortran77 to C translator | Open-Source | info | code |
fortran-language-server | support for editors | MIT | info | github |
fntcheck | code checker F77 | |||
gdb | gnu debugger | GPL | ||
fpm | package manager | MIT | info | github |
IDE (Integrated Develpment Environments)
Here there is a list of text editors that I have tested myself. My main editor has been vim or neovim, now I am learning emacs (doom-emacs in particular). From time to time I use Geany which works well for many projects. For very large projects it might be worth to check Eclipse and Code-Blocks, as they have specific features of fortran. My recommendation if you are beginner is to try emacs or neovim.
Name | Kind | Licence | Docs | Link |
---|---|---|---|---|
Geany | General IDE | Open-Source | ||
Code-Blocks | Support Fortran and C | Open-Source | ||
Eclipse+Photran | Fortran in Eclipse | Open-Source | ||
Kate / Gedit | general IDE | Open-Source | ||
Atom | general IDE (plugins) | Open-Source | ||
Emacs | Fortran modes (f77 & f90) | Open-Source | ||
Visual Studio | general IDE (plugins) | Open-Source | ||
Vim/Neovim | with vim-fortran plugin | Open-Source | ||
kakoune | Open-Source |
Compilers (here only open-source)
There are several compilers, here I list only those that are open-source. I use mostly gfortran although it is generaly consider that the intel fortran is a bit faster.
Name | Kind | Licence | Docs | Link |
---|---|---|---|---|
gfortran | GCC | Open-Source | info | web |
LFortran | LLVM | Open-Source | web | |
Flang | LLVM | Open-Source | web |
Terminology
Fortran Arrays
- explicit-shape array: declared with explicit values for the bounds in each dimension of the array. For this we can have automatic arrays, when the array is a local variable, and adjustable array when it is a dummy argument to a subprogram or procedure.
- assumed-shape array: it is a non-pointer(!) dummy argument array that takes its shape from the associated actual argument array (by actual I mean the array that is introduced or demanded by the main program when it is called the subprogram or procedure). It is interesting to combine this approach with the size() instrinc procedure that gives the actual size of an array. We then can obtain within the procedure the size of the array and use this integer value inside the subprogram. For example to allocate an intent(out) array with the correct dimensions.
- deferred-shape array: is an allocatable array (it has the ALLOCATABLE attribute and a specified rank but their bounds are set by allocation or argument association) or array pointer.
- automatic array: it is explicit-shape and local. It is usually in subprograms and the bounds are set when the subprogram is called
Note here that how the arrays are implemented and defined in a specific programming language is crucial, as it is the main basic piece used to build all the scientific computations. It is illustrative to compare the evolution of the several arrays on the different languages: Fortran, Python, C. In particular, the several libraries on Haskell due to the central role that the type system has in this functional programming language.
Few Examples
An example with a subprogram (a subroutine) with arguments x and n will be:
subroutine example(x , n)
implicit none
integer , intent(in) :: n
real , intent(in) , dimension(1:n) :: x
Here x is a dummy argument declared with specific bounds therefore it is a explicit-shape dummy array. However we could use an assumed-shape dummy array because in Fortran 90 and later versions the actual array size and the associated dummy arguments have the same rank and the same size in each dimension. In this situation a different approach will be:
program example_main
implicit none
integer :: n
real, allocatable, dimension(:) :: a
! The interface part is optional and not mandatory
! it is useful when we have external subprograms
! and we can help the compiler to a full consistence
! checking. It is recomended its use.
interface
subroutine example(x)
implicit none
integer :: m
real , intent(in), dimension(:) :: x
end subroutine example
end interface
! n can be read from a file for example
read *,n
allocate(x(1:n))
CALL example(x)
end example_main
subroutine example(x)
implicit none
integer :: n
real , intent(in) , dimension(:) :: x
n = size(x)
Procedures in Fortran
They are very important for break the code in smaller problems, to don't repeat code within a program, to allow a better testing strategy.
In general the procedures communicate with the main program or other procedure by the arguments, inside the procedure they are named dummy arguments.Because Fortran is an strongly type code, we have to define the type of each argument both in the main program and in the procedures and they have to be consistent (this is for instance ensured by the definition of an interface).
Inside the procedures, specifically in the subroutines, we have the possibility of define if the dummy argument is an input, an output or both, by the intent sentence in the type definition block at the beginning of the sentence. The internal local variables are simple not included in the list of arguments of the procedure and they are destroyed once the procedure end.
The interface block is mandatory in several cases:
- A procedure with optional or keywords arguments
- Function returning an array or a pointer
- Procedure with assumed-shape dummy arguments
- Procedure with dummy arguments with pointer or target attribute
- Procedure generic (this allow overloading)
- Procedure defining a user operator
- Procedure with a user defined assignment
Functions
Subroutines
A very interesting case is when the subroutine is including a procedure as argument this means that we have to define an interface for the function that is consistent with the actual procedure argument
subroutine my_own(a, ext_sub, b, c, d)
implicit none
real, intent(in), dimension(:) :: a
real, intent(out), dimension(:) :: b
integer :: c,d
interface
subroutine ext_sub(a,b,c)
implicit none
real, intent(in), dimension(:) :: a
real, intent(out), dimension(:) :: b
integer, intent(inout) :: c
real :: parm1, parm2
end subroutine ext_sub
end interface
! now we have operations including call ext_sub(a,b,c) and using the arguments b and c
! ..
end subroutine my_own
In this situation we have to include information on a main program using this subroutine as
program main
implicit none
real :: parmx, parmy
integer :: i,j,k
real, allocatable, dimension(:) :: x,y,z
interface
subroutine my_own()
interface
subroutine ext_sub()
end subroutine ext_sub
end interface
end subroutine my_own
end interface
interface
subroutine ext_sub1()
end subroutine ext_sub1
end interface
interface
subroutine ext_sub2()
end subroutine ext_sub2()
end interface
Pointers
In general all the most common multi-dimensional data types are static, which means that the size has to be clearly defined before any possible operation with the them. In Fortran as commented in my previous Fortran Notes there are several "kind" of arrays concerning how it is determined the shape, but even with allocated arrays its shape has to be defined on execution time. Pointers aims to be a flexible tool when the problems to be solved are more complex or not totally defined a-priori. The general idea is that a pointer can be associated to the values of another variable, this association can be changed during runtime, but also the pointer can "point" to a "target" that is a sub-array for another array-variable. Alse these kind of associations give to pointers a lot of flexibility to the programer. The pointers are not present in Fortran 77.
Derived Types
Other tool not present in Fortran 77 are the derived types. The provide a similar functionality to the "objects" that we can find in other languages, but it is possible just to consider them a kind of encapsulation of several data types on a single entity.
Examples
subroutine date_type(dtime)
implicit none
type date_time
integer :: year
integer :: month
integer :: day
integer :: hour
integer :: minute
real(8) :: second
end type date
type (date_time), intent(out) :: dtime
dtime%year = 2016
dtime%month = 1
dtime%day = 21
dtime%hour = 11
dtime%minute = 15
dtime%second = 20.345
end subroutine
In this subroutine is defined a typical date and time derived type. We can see that in the object dtime is included all the information. We can access to the different date-time parts simply as dtime%day, dtime%time etc... but also we can potentially define an operation like: dsum(dtime1, dtime2) that will give a new date_time derived type, or even override the intrinsic sum '+' to deal with date_time types.
Combining C and Fortran
Common terminology
The global framework to call methods/functions from one language in other is named: foreign function interface (FFI), and the process named binding. Nowadays, this is a common procedure to use libraries that we need but are not yet developed for our preferred language.
The interoperability is possible in Fortran 2003 as it incorporates an specific module called ISO_C_BINDING that ensures a correct use of the C intrinsic types. Even more, we can have a interoperability of derived types,
use iso_c_binding
...
type, bind(c) :: my_own_type
integer (c_init) :: a,b,c
real (c_float) :: o,p
end type my_own_type
...
by using the above code we can inter-operate with a typical C code,
typedef struct {
int i,j,k;
float r,s;
} my_c_type;
However, we can also have a procedure inter-operations. For example in Fortran we can make,
use iso_c_binding
...
function func(a,b,c,d) bind(c, name='c_func')
...
This feature of Fortran 2003 is useful not only to like between C and Fortran. Other quite common case is a language that can talk with C (Haskell, Go, Rust, Julia ...), so they can talk with Fortran if we are able to provide a wrapper based on ISO_C_BINDING in such way that the other language thinks that is talking to a C procedure/code.