Appendix H: Libraries and Tools Reference
A working scientific programmer spends far more time composing libraries than writing loops. This
appendix is the bench reference for that work: for each library and tool the book relies on, it gives
what it is, when to reach for it, and the exact flag or command to use it. It is the
practical companion to two neighbours — Appendix C installs
the toolchain and catalogues the compiler flags, and
Appendix J is the annotated reading list and history. This one is
the linking reference: the module names, the -l flags, the config helpers, and the manifest keys, all
in one place. Chapter 16
introduces this landscape narratively; here it is tabulated for fast lookup.
Everything named here is a real, current tool of computational science — Tier 1 in the book's citation policy. Two kinds of detail are softer and marked inline as Tier 2: any specific version number or tag (packages move — take the current one from the library's own README), and any speed comparison (an illustrative order of magnitude, never a promise). The link flags below assume the library is already installed (Appendix C covers installation); each has been kept consistent with the chapter that puts it to work.
H.1 Numerical libraries
BLAS and LAPACK — the floor everything stands on
BLAS (Basic Linear Algebra Subprograms) and LAPACK (Linear Algebra PACKage) are the load-bearing numerical libraries of scientific computing, and — the fact worth repeating — they are written in Fortran. BLAS provides the low-level kernels (vector, matrix-vector, and matrix-matrix operations, its Levels 1/2/3); LAPACK sits on top of BLAS to solve real problems: linear systems, eigenvalue problems, and singular value decompositions. When NumPy, MATLAB, or R does linear algebra, this compiled Fortran is what actually runs. You call them for real in Chapter 21.
Reach for LAPACK the moment you need to solve $A\mathbf{x} = \mathbf{b}$, factor a matrix, or find
eigenvalues or singular values — anything beyond the whole-array matmul/dot_product intrinsics. Never
hand-roll these; a career's worth of tuning is already in the library.
The naming scheme. Classic routine names pack three fields into a few characters:
[ s | d | c | z ] [ matrix-type ] [ operation ]
precision e.g. ge e.g. sv
- Precision:
ssingle real,ddouble real,csingle complex,zdouble complex. - Matrix type:
gegeneral,sysymmetric,posymmetric positive-definite,trtriangular,gbgeneral band, and more. - Operation:
svsolve,trffactor,trssolve-from-factor,eveigenvalues,svdsingular values; the BLAS-3mmmatrix-multiply, BLAS-2mvmatrix-vector.
So dgesv reads double · general · solve, and once you see the pattern a wall of names becomes
legible. The routines you will meet most:
| Routine | Decodes as | What it does | Level |
|---|---|---|---|
dgemm |
double · general · matrix-multiply | $C \leftarrow \alpha AB + \beta C$ (the tuned kernel everything leans on) | BLAS-3 |
dgesv |
double · general · solve | solve dense $A\mathbf{x} = \mathbf{b}$ via LU with partial pivoting | LAPACK |
dsyev |
double · symmetric · eigenvalues | eigenvalues (and optionally eigenvectors) of a real symmetric matrix | LAPACK |
dgesvd |
double · general · SVD | singular value decomposition of a general matrix | LAPACK |
Swap the leading d for s/c/z to change precision. Two argument conventions trip up newcomers and
are covered in Chapter 21: the leading dimension (lda — the declared first dimension, i.e. the
stride between columns) and the info status flag (0 success, < 0 a bad argument in your call,
> 0 a numerical failure). Always check info.
Linking. The reference implementation from Netlib is correct but deliberately simple; a cache-tuned implementation is many times faster on a large matrix (Tier 2 — an illustrative order of magnitude, not a fixed figure), so ship a tuned BLAS in production.
| You want | Link with | Notes |
|---|---|---|
| Reference BLAS + LAPACK | -llapack -lblas |
correct and portable; fine for learning and small problems |
| OpenBLAS (tuned, open source) | -lopenblas |
bundles an optimized LAPACK, so this one flag usually replaces -llapack -lblas |
| Intel MKL (tuned, Intel CPUs) | use Intel's MKL Link Line Advisor | the link line is genuinely intricate and hardware/threading-dependent — generate it, don't memorize it |
$ gfortran -std=f2018 -Wall solve.f90 -o solve -llapack -lblas # reference
$ gfortran -std=f2018 -Wall solve.f90 -o solve -lopenblas # tuned (OpenBLAS)
Tip — route
matmulto a real BLAS. gfortran's-fexternal-blasflag sends largematmulcalls to an externaldgemminstead of the compiler's built-in version, so you inherit the tuned kernel without rewriting a line. It only helps above a size threshold; measure before relying on it (Chapter 21).
FFTW — fast Fourier transforms
FFTW ("Fastest Fourier Transform in the West") is the de-facto standard fast Fourier transform,
turning an $O(n^2)$ transform into $O(n \log n)$. Unlike BLAS/LAPACK it is a C library, but it ships a
first-class Fortran interface built on iso_c_binding and is called from Fortran constantly — a textbook
example of the C interoperability of
Chapter 16's ecosystem.
Reach for FFTW whenever your science moves between real space and frequency space: spectral PDE solvers, signal and image processing, digital filtering, correlation. Its Fortran interface is plan-based — you build a plan once, execute it many times, then destroy it:
use, intrinsic :: iso_c_binding
include 'fftw3.f03' ! the Fortran 2003 interface FFTW installs
! ... plan = fftw_plan_dft_1d(n, in, out, FFTW_FORWARD, FFTW_ESTIMATE)
! call fftw_execute_dft(plan, in, out)
! call fftw_destroy_plan(plan)
| You want | Link with |
|---|---|
| Double-precision FFTW | -lfftw3 |
| Single-precision FFTW | -lfftw3f |
The exact plan/execute API (real-to-complex transforms, multidimensional plans, threading with
-lfftw3_threads or -lfftw3_omp) is in FFTW's own manual; the shape above is the piece to remember.
Large and sparse: ScaLAPACK and PETSc (brief)
When a problem outgrows a single node, or is sparse, dense LAPACK is the wrong tool. Two libraries dominate — both are big enough to be their own courses, so this is only a signpost. Chapter 21 touches sparse formats where these belong.
| Library | What it is | Reach for it when |
|---|---|---|
| ScaLAPACK | distributed-memory LAPACK for dense problems (built on MPI + the BLACS/PBLAS layers) | a dense factorization or eigenproblem is too large for one machine's memory |
| PETSc | a large MPI-based toolkit for sparse linear/nonlinear systems and PDEs, with scalable solvers and preconditioners | you assemble large sparse systems and need parallel Krylov solvers and preconditioners |
Both are typically linked through their own build systems or pkg-config files rather than a single -l
flag; consult each project's documentation
(Appendix J lists where they live).
H.2 Scientific data libraries
Once a run produces more than a few megabytes, plain text stops scaling and you move to a portable, self-describing binary format. The two standards, their APIs, and when to choose which, are the subject of Chapter 25; here are the practical link details. Both are C libraries with an official, separately packaged Fortran interface — and forgetting to install that separate Fortran package is the classic "undefined reference" trap.
| Library | Fortran module | API prefix | By-hand link | Config helper (preferred) |
|---|---|---|---|---|
| NetCDF-Fortran | use netcdf |
nf90_* |
-lnetcdff -lnetcdf |
`nf-config --fflags --flibs` |
| HDF5 (Fortran) | use hdf5 |
h5*_f |
-lhdf5_fortran -lhdf5 |
the h5fc compiler wrapper |
Note the doubled f in -lnetcdff — that is the Fortran layer, distinct from the C core -lnetcdf.
Prefer the config helpers, which emit the exact flags for your install so you never hard-code a path:
$ gfortran -std=f2018 -Wall write_nc.f90 `nf-config --fflags --flibs` -o write_nc # NetCDF
$ h5fc -std=f2018 -Wall write_h5.f90 -o write_h5 # HDF5
When to choose which (from Chapter 25):
- NetCDF — the standard for gridded climate, weather, and ocean data. Simpler API (
nf90_create→nf90_def_dim→nf90_def_var→nf90_put_att→nf90_enddef→nf90_put_var→nf90_close), and the CF metadata conventions that make one lab's file readable by another decades later. Everynf90_*call returns an integer status — check it againstnf90_noerrevery time. - HDF5 — the standard for large, hierarchical simulation output: groups and datasets like a
filesystem inside one file, with built-in chunking and deflate compression. Every
h5*_fcall takes a trailinghdferr;h5open_f/h5close_fmust bracket all HDF5 work. NetCDF-4, incidentally, is built on top of HDF5.
Pitfall — the Fortran interface is a separate package. "NetCDF is installed" usually means the C core is installed; the
netcdf-fortranpackage (and, for HDF5, the Fortran build) is a distinct install. A link that fails on a library you are sure you have is almost always the missing Fortran layer.
H.3 Parallel libraries
Two models scale a Fortran code beyond one core. OpenMP (shared-memory threads) is a compiler
feature — the -fopenmp flag in Appendix C, not a library —
so it is not listed here. The two that are libraries or runtimes:
| Library / runtime | What it is | How you build & run | Chapter |
|---|---|---|---|
| Open MPI | a widely used MPI implementation | wrappers mpifort (or mpif90) to compile; mpirun -np N ./exe to run |
34 |
| MPICH | another major MPI implementation (same standard) | identical wrappers and launcher; MPI code is portable across the two | 34 |
| OpenCoarrays | the runtime that makes gfortran coarrays run across images (over MPI) | compile -fcoarray=lib via the caf wrapper; run cafrun -n N ./exe |
32 |
MPI (the Message Passing Interface) is a specification, not one library — Open MPI and MPICH are two
implementations of it, and modern Fortran talks to either through the mpi_f08 module. It is the backbone
of distributed-memory HPC and the whole subject of
Chapter 34, where the heat solver is decomposed
across processes.
Coarrays are Fortran's own parallel model, built into the language rather than bolted on as a library
(Chapter 32). To run them across multiple
images with gfortran you need the OpenCoarrays runtime and -fcoarray=lib; -fcoarray=single
(Appendix C) compiles coarray code to a single image for serial testing with no runtime at all.
$ mpifort -O2 heat_mpi.f90 -o heat_mpi && mpirun -np 4 ./heat_mpi # MPI, 4 processes
$ caf -O2 heat_ca.f90 -o heat_ca && cafrun -n 4 ./heat_ca # coarrays, 4 images
H.4 Build and packaging tools
How a project is built is its own decision, and the right answer depends on the project. The four common choices, in rough order of least-to-most ceremony:
| Tool | Reach for it when | Invoke it with | Handles module order? |
|---|---|---|---|
| fpm | new projects, libraries, anything you can shape to its conventions | fpm build / fpm run / fpm test |
yes, automatically |
| CMake | large or mixed-language projects, especially with C/C++; ubiquitous in HPC | cmake -B build && cmake --build build |
yes (you configure it) |
| Meson | modern projects wanting CMake's power with less boilerplate | meson setup build && meson compile -C build |
yes |
| Make | small projects, or legacy codes that already use it | a hand-written Makefile; make |
no — you track dependencies |
For your own new work, start with fpm, the Fortran Package Manager: it discovers your sources
(app/ programs, src/ library, test/ tests), works out module compilation order for you, and fetches
git dependencies — all from one fpm.toml manifest:
name = "heat-solver"
version = "0.1.0"
license = "MIT"
[dependencies]
stdlib = { git = "https://github.com/fortran-lang/stdlib" }
Tip — pin what you publish. A bare
gitdependency tracks the latest commit, which can change under you. For anything reproducible, pin atag,branch, orrev, and take the exact spec from the library's current README rather than from memory — the repository URL is stable, but a specific tag string is a Tier 2 detail to confirm at the source (Chapter 37 makes the case for reproducible builds).
You meet CMake, Meson, and Make again in the wild in Chapter 16's survey and whenever you contribute to an existing code — because then you build it their way.
H.5 Testing and documentation
Numerical code that is not tested is numerical code you do not actually trust. These are the tools that pin behavior and explain it; all three come together in Chapter 37.
| Tool | Purpose | How you use it | When to prefer it |
|---|---|---|---|
| pFUnit | unit testing, developed at NASA Goddard; can run tests in parallel under MPI | annotated test files (processed to Fortran), integrated via CMake | testing large or parallel scientific code |
| test-drive | a lightweight, fpm-native unit-test framework | add as an fpm dev-dependency; write simple test procedures | small-to-medium projects already using fpm |
| FORD | generate browsable HTML docs from source and special comments | pip install ford, then run ford on a project file |
documenting any Fortran project |
FORD (FORtran Documenter) reads documentation comments that use doubled markers — !> before an
entity or !! after a declaration — and produces a linked, searchable website with call graphs.
Because those markers are ordinary Fortran comments, adding them never affects the build:
!> Compute the arithmetic mean of a 1-D array.
pure function mean_value(x) result(m)
real(dp), intent(in) :: x(:) !! the input samples
real(dp) :: m !! the returned mean
m = sum(x) / real(size(x), dp)
end function mean_value
The common idea across the test frameworks is the one Chapter 37 leans on: pin the code's behavior with
automated checks so that when you later optimize (Part VII) or parallelize (Part VIII) the solver, a
single fpm test tells you instantly whether you changed a number you were not supposed to change.
H.6 The standard library and community tools
The modern connective tissue of Fortran — most of it built in the open by the fortran-lang community —
is what turns the language from "a text editor and an inherited Makefile" into a comfortable place to
work.
| Tool | What it is | How you use it |
|---|---|---|
| stdlib | the community standard library: routines the intrinsics don't cover | add as an fpm dependency; use modules like stdlib_stats, stdlib_linalg, stdlib_sorting, stdlib_strings, stdlib_io, stdlib_math |
| fortls | the Fortran language server (LSP) — completion, hover docs, go-to-definition, live errors | pip install fortls, then pair with an editor extension (the "Modern Fortran" extension for VS Code; Vim/Neovim/Emacs also speak LSP) |
| Compiler Explorer | godbolt.org — see the assembly your code compiles to | paste a snippet, select gfortran, read the optimizer's output; no install |
| LFortran | lfortran.org — a modern LLVM-based, interactive Fortran compiler | run Fortran statement-by-statement like a REPL; it powers the in-browser play.fortran-lang.org. Still maturing — a superb teaching and exploration tool, not yet a drop-in production compiler |
Of these, install fortls first — a language server is the single highest-value day-to-day upgrade to the Fortran editing experience. Reach for stdlib in any real project instead of re-writing means, sorts, and text-table readers by hand (it is a dependency you declare via fpm, exactly as in §H.4). Keep Compiler Explorer bookmarked for the performance chapters, and treat LFortran as the place to try a new idea or a Fortran 2023 feature interactively.
H.7 A typical scientific project stack
Put together, a modern Fortran project of the kind this book builds toward assembles from one component per layer. There is nothing mandatory about these particular choices — but this is a sensible, entirely free default that composes cleanly, and it is roughly the stack behind the capstone solver.
| Layer | Typical choice | Why / how |
|---|---|---|
| Compiler | gfortran (or Intel ifx in CI for a second opinion) |
free, portable, everywhere — Appendix C |
| Build & packaging | fpm | zero-config, resolves module order, fetches dependencies |
| Numerics | LAPACK/BLAS via OpenBLAS (-lopenblas); FFTW (-lfftw3) if you transform |
inherit tuned kernels instead of hand-optimizing |
| Data I/O | NetCDF (gridded/geoscience) or HDF5 (large hierarchical output) | portable, self-describing, compressed — Chapter 25 |
| Parallelism | OpenMP within a node (-fopenmp); MPI (Open MPI/MPICH) or coarrays (OpenCoarrays) across nodes |
node-level threads plus cluster-level messaging — Chapters 32/34 |
| Testing | test-drive (light) or pFUnit (parallel-capable) | pin behavior; fpm test in CI — Chapter 37 |
| Documentation | FORD | HTML manual generated from !>/!! comments |
| Editor | fortls + the Modern Fortran extension | completion, diagnostics, navigation |
H.8 Quick linking cheat-sheet
Every library flag in one place. Compiler and parallel flags themselves live in Appendix C; this table is the libraries half.
| Library | Add to the compile line | Introduced / used in |
|---|---|---|
| Reference LAPACK + BLAS | -llapack -lblas |
Ch. 21 |
| OpenBLAS (tuned; includes LAPACK) | -lopenblas |
Ch. 21 |
| Intel MKL | generate with the MKL Link Line Advisor | Ch. 21 |
Route matmul to external BLAS |
-fexternal-blas (a gfortran flag) |
Ch. 21 |
| FFTW (double / single) | -lfftw3 / -lfftw3f |
spectral methods |
| NetCDF-Fortran | `nf-config --fflags --flibs` (or -lnetcdff -lnetcdf) |
Ch. 25 |
| HDF5 Fortran | build with h5fc (or -lhdf5_fortran -lhdf5) |
Ch. 25 |
| MPI | build with mpifort; run with mpirun -np N |
Ch. 34 |
| Coarrays (multi-image) | -fcoarray=lib via caf; run with cafrun -n N |
Ch. 32 |
| stdlib, and any fpm dependency | declare in fpm.toml; fpm build links it |
Ch. 16, 37 |
The through-line is Chapter 16's lesson: Fortran's real power is the mature, composable set of libraries and tools around the language. You solve the algebra with LAPACK, transform with FFTW, store with NetCDF or HDF5, scale with MPI or coarrays, build and share with fpm, and trust the result because it is tested and documented — each tool doing one thing well and expecting to be composed with the rest.