A Gentle Introduction to Fortran
Originally known as FORTRAN, but written in lower case since the 1990s with Fortran 90, this language was developed initially by John Backus as a way to make writing programs for the IBM 704 mainframe easier. The 704 was a 1954 mainframe with the honor of being the first mass-produced computer that supported hardware-based floating point calculations. This functionality opened it up to a whole new dimension of scientific computing, with use by Bell Labs, US national laboratories, NACA (later NASA), and many universities.
Much of this work involved turning equations for fluid dynamics and similar into programs that could be run on mainframes like the 704. This translating of formulas used to be done tediously in assembly languages before Backus’ Formula Translator (FORTRAN) was introduced to remove most of this tedium. With it, engineers and physicists could focus on doing their work and generating results rather than deal with the minutiae of assembly code. Decades later, this is still what Fortran is used for today, as a domain-specific language (DSL) for scientific computing and related fields.
In this introduction to Fortran 90 and its later updates we will be looking at what exactly it is that makes Fortran still such a good choice today, as well as how to get started with it.
Modern Fortran
Punch card from a typical FORTRAN program by the early 1970s. (Credit: Arnold Reinhold, Wikimedia)
The release of the Fortran 90 (F90) specification in 1991 was the first major update to the language since Fortran 77, and introduced many usability improvements, as well as the dropping of punch card era legacy requirements and limitations to variable lengths and more. This is the reason for our focus on F90 here, as there is no real reason to use F77 or earlier, unless you’re maintaining a legacy codebase, or you have a stack of new cards that need punching. In case you are dying to know what changed, the Wikibooks Fortran Examples article has examples of early FORTRAN all the way to modern Fortran.
Of note here is that a modern Fortran compiler like GCC’s GFortran
(forked from g95
) still supports F77, but users are highly encouraged to move on to Fortran 95, a minor update to F90, with GFortran supporting up to F2008, with coarray support, as covered later. F2018 support is still a work in progress as of writing, but many features are already available.
Support for the latest standard (F2023) is not widely available yet outside of commercial compilers, but as a minor extension of F2018 it should eventually get rolled into those features as the implementation progresses. This means that for now F2008 is the latest standard we can reliably target across toolchains with new Fortran code.
Beyond GFortran there are a few more options, including Flang
in LLVM and LFortran in addition to a gaggle of commercial offerings. Unless you intend to run high-performance computing code on massive parallel clusters like supercomputers, the GNU and LLVM offerings will probably suffice. Simply fetch either GFortran or Flang from your local package manager or equivalent and you should be ready to start with programming in Fortran.
Hello World
As with most DSLs, there is very little preamble to start writing the business logic. The ‘Hello World’ example is most succinct:
program helloworld
print *, "Hello, World!"
end program helloworld
The program name is specified right after the opening program
keyword, which is repeated after the closing end program
. This is similar to languages like Ada and Pascal. The program name does not have to match the name of the file. Although there’s no explicit specification for what the file extension has to be for a Fortran source file, convention dictates that for F77 and older you use .f
or .for
, while F90 and newer uses generally .f90
as extension. Although some opt to use extensions like .f95
and .f03
, this is rather confusing, isn’t recognized by all compilers and all of those are similar free-form Fortran source files anyway.
Tl;dr: Use .f90
for modern Fortran source files. Our Hello World example goes into a file called hello_world.f90
.
The other point of note in this basic example is the print
command, which looks somewhat cryptic but is quite easy. The first argument is the format, reminiscent of C’s printf
. The asterisk here simply means that we use the default format for the provided value, but we could for example print the first string as an 11 character wide field and a variable string as 8 wide:
character(len=8) :: name = 'Karl'
print '(a11,a8)', 'My name is ', name
This also shows how to declare and define a variable in Fortran. Note that if you do not start the code with implicit none
, variable names that start with I through N are considered to be integer
type and real
otherwise. With gfortran you can also globally do this by compiling with the -fimplicit-none
flag.
A total of five basic types are supported:
- real
- integer
- logical (boolean)
- complex
- character
Finally, comments in Fortran are preceded by an exclamation mark !
. It’s also relevant to note that Fortran – like all good programming languages – is case insensitive, so you can still write your Fortran code like it’s F77 or Fortran II, yelling in all caps without anyone but the people reading your code batting an eye.
Hello Science
Now that we have got a handle on the basics of Fortran, we can look at some fun stuff that Fortran makes really easy. Perhaps unsurprisingly, as a DSL that targets scientific computing, much of this fun stuff focuses around making such types of computing as easy as possible. Much of this can be found in the intrinsic procedures of Fortran, which make working with real, integer and complex values quite straightforward.
For example, conjugating a complex number with conjg
:
(Credit: Fortran Wiki)
Basically, whatever mathematical operation you wish to perform, Fortran should have you covered, allowing you to translate your formulas into a functional program without having to bother with any dependencies or the like. This includes working with matrices and getting into the weeds with numerical precision.
Even better is that you’re not stuck running your code on a single CPU core either. Since Fortran 2008, Coarray Fortran (CAF) is now part of the specification. This feature enables parallel processing, which is generally implementing on top of the Message Passing Interface (MPI) protocol, with gfortran implementing CAF support. Depending on the selected option with the -fcoarray=
flag, gfortran can use the ‘single’ image (thread) option, or with a library like OpenCoarrays it can use MPI, GASNet, and others.
When using CAF with MPI, the program (‘image’) is distributed across all nodes in the MPI cluster per its configuration, with synchronization occurring as defined by the program. With OpenCoarrays available from many local OS repositories, this means that any budding molecular scientist and astrophysicists can set up their own MPI cluster and start running simulations with relatively very little effort.
The DSL Life
Much like when we looked at COBOL, a DSL like Fortran is often misunderstood as ‘yet another programming language’, much like how in the 1980s some thought that the scientific and engineering communities were going to switch over to Pascal or Modula-2. One simple reason that didn’t happen lies in the very nature of DSLs, with them being developed explicitly to deal with that specific domain. It will always be possible to do everything a DSL does in any generic programming language, it’s just that a DSL can be optimized more exactly, and is often easier to maintain, since it is not generic.
There are many things that Fortran does not have to concern itself with, yet which haunt languages like C and its kin. Meanwhile, reimplementing Fortran in C would come at considerable cost, run into certain limitations of the language and potentially require compromises in order to get close to the original functionality of the DSL.
Running a cluster with Coarray-based Fortran source, churning through complex simulations that require utmost control over precision and where one small flaw can waste days of very expensive calculations, those are the kind of scenarios where you’re not looking for some generic language to poorly reinvent the wheel, but where you keep using the same – yet much more refined – wheel that has gotten us this far.