Fortran — Reference
Source: https://fortran-lang.org/learn/
Fortran
- Created: 1957 by John Backus at IBM (first compiler delivered April 1957 for the IBM 704). Originally “FORmula TRANslator.” Standardized by ISO/IEC JTC1/SC22/WG5 with the US technical body J3 (INCITS/Fortran).
- Latest stable: Fortran 2023 = ISO/IEC 1539:2023, published November 2023. Predecessors: Fortran 2018 (ISO/IEC 1539:2018), 2008, 2003, 95, 90, 77, 66.
- Paradigms: Imperative, procedural, array-oriented, object-oriented (since 2003), parallel (since 2008 via coarrays + 2018 teams).
- Typing: Strong, static, with explicit numeric kinds (
integer(kind=8),real(real64)).implicit noneis the modern default — turn it on at the top of every scope. - Memory: No GC. Stack + heap with
allocatable(preferred, RAII-like — auto-deallocated at scope exit) andpointer(use sparingly). No null-deref UB if you stick toallocatable. - Compilation: Ahead-of-time native code. Major compilers: gfortran (GCC), Intel ifx/ifort (LLVM-based), NVIDIA nvfortran (HPC SDK, GPU offload), LFortran (modern, interactive, MLIR-based, alpha), LLVM Flang (production-ready as of LLVM 19+), Cray, AOCC.
- Primary domains: HPC, climate/weather, CFD, computational chemistry, astrophysics, finite-element, numerical libraries (BLAS, LAPACK), legacy scientific code.
- Official docs: https://fortran-lang.org/learn/ (community), https://j3-fortran.org/ (standards body), https://wg5-fortran.org/ (ISO WG5).
At a glance
Fortran has been continuously redesigned since 1957 and is now a modern array+OO+parallel language, not the 1970s caricature. Free-form source is standard since Fortran 90; .f/.for is fixed-form (column 7 onward), .f90/.f95/.f03/.f08 is free-form. Use .f90 regardless of standard year unless you need fixed-form. Where it dominates: anything where dense-array performance and the BLAS/LAPACK ABI matter — Fortran’s array slicing, intrinsic reductions, and do concurrent map directly to vectorized + parallel codegen, and most of the numerical-library world (BLAS, LAPACK, ScaLAPACK, FFTW interfaces, MPI Fortran bindings) speaks Fortran natively.
Getting started
Install:
- Linux:
sudo apt install gfortran(Debian/Ubuntu),sudo dnf install gcc-gfortran(Fedora),sudo pacman -S gcc-fortran(Arch). - macOS:
brew install gcc(gfortran ships with it), orbrew install lfortran. - Windows: MSYS2 (
pacman -S mingw-w64-x86_64-gcc-fortran), Intel oneAPI HPC Toolkit (free), or WSL. - HPC clusters:
module load intel-oneapi-compilersormodule load nvhpc.
Hello world (hello.f90):
program hello
implicit none
print *, "Hello, world!"
end program helloCompile + run: gfortran -O2 -Wall -std=f2018 hello.f90 -o hello && ./hello.
Project layout with fpm (Fortran Package Manager — the modern, Cargo-like tool):
myproj/
fpm.toml
src/myproj.f90
app/main.f90
test/check.f90
fpm new myproj && cd myproj && fpm run and fpm test. Dependencies declared in fpm.toml like [dependencies] stdlib = "*".
Version manager: No first-class one. On HPC, use Lmod/Environment Modules. Locally, pick a package manager (apt, brew, conda install -c conda-forge gfortran) or use Spack for reproducible HPC stacks.
REPL: Fortran has historically had no REPL. LFortran changes that: lfortran opens an interactive prompt with Jupyter kernel support. Useful for exploration; not yet a full standards-conforming compiler.
Basics
Types & literals. integer (default kind 4 bytes), real (default kind 4), double precision (legacy alias for real(kind=real64)), complex, logical, character(len=N). Use the iso_fortran_env module’s named kinds: integer(int32), real(real64), real(real128). Literal suffixes: 1.0_real64, 1_int64. Arrays declared with shape: real(real64) :: a(100, 100). Strings are fixed-length unless declared character(len=:), allocatable.
Variables/scoping. Lexical scope per program/module/subprogram. Always start every scope with implicit none — the default i-n integer rule from 1957 is the source of countless bugs. Module variables visible via use mymod (with optional only: allow-list).
Control flow. if/else if/else/end if, select case, do i = 1, n (default step 1) / do while / do (infinite, exit with exit/cycle). Modern: do concurrent (i = 1:n) declares the iterations are independent — compiler is free to vectorize/parallelize/offload. associate (x => long%nested%expr) ... end associate for local aliases. Conditional expressions (Fortran 2023): result = (x > 0 ? x : -x) via the new merge-style if-expression syntax.
Procedures. subroutine (no return value, called with call) and function (returns a value). Argument intent must be declared: intent(in), intent(out), intent(inout). pure and elemental markers enable optimization and parallelism (elemental lifts a scalar function over array args automatically). Recursion requires recursive keyword.
Strings. character(len=20) :: name = "Ada". Concatenate with //. Slicing: name(1:3). Allocatable strings (character(len=:), allocatable) auto-resize on assignment. No native Unicode — selected_char_kind('ISO_10646') exists but support is uneven.
Collections. Arrays are first-class. Whole-array ops: c = a + b, dot_product(a, b), matmul(A, B), sum(a, dim=1), maxval, minloc, pack, reshape. Array sections: a(1:10:2, :) (stride 2, all columns). Allocatable arrays: real, allocatable :: a(:) then allocate(a(n)). Derived types replace structs: type :: point ; real :: x, y ; end type.
Intermediate
Type system depth. Derived types with type-bound procedures (since 2003), inheritance via extends, abstract types, polymorphic pointers (class(parent_t), pointer :: p). Parameterized derived types (PDTs) since 2003: type :: matrix(rows, cols, kind) ; integer, len :: rows, cols ; integer, kind :: kind ; real(kind) :: data(rows, cols) ; end type — kind parameters at compile time, len parameters at runtime. (Compiler support varies; gfortran has historically been weak here, ifx is solid.)
Modules. module foo ... contains ... end module is the unit of encapsulation. use foo, only: bar controls imports. Submodules (since 2008) split interface from implementation, dramatically reducing recompilation cascades for large projects — declare a procedure interface in a parent module, implement in a submodule, and changing the implementation doesn’t recompile users of the parent.
Error handling. No exceptions. Conventions: an integer, intent(out) :: stat argument (mirrors allocate(..., stat=ierr)) plus optional errmsg. error stop "msg" aborts with optional message and stop-code. Modern stdlib (stdlib_error) provides check/error_stop helpers.
Concurrency primitives. Three layers: (1) do concurrent — single-image data parallelism, compiler-driven. (2) OpenMP/OpenACC pragmas (!$omp parallel do) — threads + GPU offload. (3) Coarrays (Fortran 2008+) — Partitioned Global Address Space (PGAS) model, multiple “images” with one-sided communication: a[image] = b. Coarray teams (Fortran 2018) split images into subgroups.
I/O. print *, x (list-directed to stdout), write(unit, fmt) x. Format strings: '(I5, 2X, F10.4)'. Open files with open(unit=10, file="data.txt", action="read", iostat=ios). NAMELIST I/O for self-describing config blocks. Stream access (Fortran 2003): access="stream" for binary byte streams without record markers.
Stdlib highlights. Intrinsics cover most numerical needs: sin/cos/exp/log (now elemental), bessel_j0, gamma, erf, dot_product, matmul, transpose, cshift, random numbers via random_number(x) + random_seed. The community Fortran stdlib (https://stdlib.fortran-lang.org/) adds string handling, sorting, statistics, linear algebra wrappers, hash maps, optional types — install via fpm.
Advanced
Memory model. No GC. allocatable is the right default — automatic deallocation at end of scope, no aliasing surprises (assignment makes a copy by default; use move_alloc to transfer ownership cheaply). pointer enables aliasing and linked structures but disables many optimizations and re-introduces dangling-reference bugs. target attribute is required on anything pointable. Compilers assume non-aliasing on dummy arguments by default (Fortran’s killer perf advantage over C); violating that with target + pointer aliasing breaks vectorization.
Concurrency deep dive. Coarrays: declare with real :: a(100)[*], the [*] codimension makes it visible to all images. num_images() and this_image() query the team. Sync with sync all, sync images([2,3]), atomic ops via atomic_define/atomic_ref, locks, events. Implementations: gfortran uses OpenCoarrays + MPI under the hood; Intel ifx has native shared-memory coarrays; Cray’s compiler is the original PGAS reference. OpenMP support is mature in gfortran/ifx (5.0+). OpenACC for GPUs is best in nvfortran. do concurrent is increasingly compiler-parallelized (nvfortran offloads it to GPU under -stdpar=gpu).
FFI. iso_c_binding module is the standardized C interop (Fortran 2003). Declare bind(C):
interface
function c_strlen(s) bind(C, name="strlen") result(n)
use iso_c_binding
character(kind=c_char), intent(in) :: s(*)
integer(c_size_t) :: n
end function
end interfacec_ptr, c_loc, c_f_pointer, c_funloc round-trip pointers. For BLAS/LAPACK, use the de-facto F77 ABI: column-major arrays, all args by reference, character strings with hidden trailing length argument (compiler-specific — gfortran/ifort/ifx all agree on the standard convention).
Reflection. Essentially none. storage_size(x) returns bits, kind(x) returns the type kind. No runtime type introspection except via polymorphic class(*) + select type. Generic dispatch is compile-time only.
Performance tools. Compiler flags: -O3 -march=native -funroll-loops (gfortran), -O3 -xHost -ipo (Intel), -fast (nvfortran). Diagnostics: -fopt-info-vec/-fopt-info-missed (gfortran), -qopt-report (Intel). Profilers: gprof, Linaro MAP, Intel VTune, NVIDIA Nsight Systems/Compute (for GPU offload), TAU, Score-P + Vampir (HPC tracing). Sanitizers: gfortran supports -fsanitize=address,undefined; bounds checking with -fcheck=all is essential during development (slow but catches whole classes of bugs).
God mode
Coarrays + teams (Fortran 2018). PGAS distributed-memory programming without dropping to MPI. form team(2, my_team) splits images; change team (my_team) ... end team scopes coarray access. Atomic intrinsics (atomic_add, atomic_cas) implement lock-free algorithms across images. The OpenCoarrays runtime layers this on MPI for clusters; on a single node, Intel implements it via shared memory.
do concurrent for offload. Modern code increasingly uses do concurrent with locality clauses (Fortran 2018: local, local_init, shared, default(none)) instead of OpenMP. nvfortran’s -stdpar=gpu lifts these loops to the GPU automatically; this is the path Fortran is staking on for portable parallelism.
Submodules + interface stability. Large projects (climate models, CFD codes) put public interfaces in a thin parent module and implementations in submodules. Changing a submodule body recompiles only that submodule; the parent’s .mod file stays binary-compatible. Without submodules, changing any line in a module recompiles every dependent.
Parameterized derived types (PDTs). type :: tensor(k, rank) ; integer, kind :: k ; integer, len :: rank ; real(k) :: data(rank) ; end type — encode shape and precision in the type. Compiler support: ifx/Cray solid, gfortran improving (still some bugs as of GCC 14). When they work, they’re the cleanest way to write generic numerical containers.
iso_c_binding + c_ptr round-tripping. Pass a Fortran array to C, store the c_loc(arr), and recover it later with c_f_pointer(cptr, fptr, [n]). This is how you bind to GSL, FFTW, HDF5, MPI’s MPI_Aint, CUDA host pointers, etc. Watch out: c_loc requires target or pointer attribute; passing a plain array variable is non-conforming.
Assumed-rank arrays (Fortran 2018). real, intent(in) :: a(..) — a procedure can accept any-rank arrays. Use select rank inside to dispatch. Replaces the old hack of overloading on rank with a generic interface.
Preprocessing. Most compilers run fpp (Intel) or cpp (gfortran with -cpp or files named .F90/.fpp). Standard #ifdef/#define work. Use sparingly — modern Fortran prefers compile-time selection via kind parameters, generics (in standard 202y), and submodules.
Compiler internals worth knowing. gfortran emits GIMPLE → RTL like other GCC frontends; LFortran uses Abstract Semantic Representation (ASR) then lowers to LLVM, with the deliberate goal of being usable as a library (for IDEs, JIT, kernel JIT). LLVM Flang uses MLIR’s FIR dialect for high-level optimization (loop transforms, OpenMP lowering) before LLVM IR.
Idioms & style
implicit noneeverywhere. Always. Configure your editor and CI to fail without it. Useimplicit none (type, external)(Fortran 2018) to also force explicitexternaldeclarations.- Naming:
lower_snake_casefor everything in modern code. Old code is oftenSCREAMING_CASEfrom fixed-form days. Fortran is case-insensitive, but pick one and be consistent. only:onusestatements. Always:use iso_fortran_env, only: real64, int32. Otherwise you import the module’s entire surface and pollute the namespace; refactoring a module breaks distant callers.intent(in)/intent(out)/intent(inout)on every dummy. No exceptions. Compilers can elide copies and detect bugs.allocatableoverpointerby default. Pointers exist for linked structures and aliasing; they disable optimizations and add bug surface.- Formatter/linter: fprettify (auto-formatter — opinionated about indentation, operator spacing). fortls (LSP for editors). stdlib + fpm fmt is emerging. No single dominant linter — gfortran’s
-Wall -Wextra -Wno-unused -pedantic+ ifx’s-warn allcover most lint. - Expert review focus: (1)
implicit nonepresent? (2)intenton every dummy? (3)only:on everyuse? (4) Mixed-precision arithmetic — accidental promotion to wrong kind? (5) Array temporaries — contiguous descriptor mismatches force compiler-generated copies; check with-Warray-temporaries. (6)do concurrentloops with hidden dependencies (assignments to shared scalars). (7) Pointer aliasing oftarget-attributed arrays.
Ecosystem
- Numerical libraries: BLAS + LAPACK (the foundation), ScaLAPACK (distributed), FFTW, PETSc (PDEs), SUNDIALS (ODEs), Trilinos, HDF5, NetCDF (climate/geo data formats). All have first-class Fortran bindings.
- Parallel runtimes: OpenMPI / MPICH (MPI Fortran 2008 bindings via
use mpi_f08), OpenCoarrays (coarray runtime), OpenMP (built into compilers), OpenACC (best in nvfortran). - HPC/GPU: NVIDIA HPC SDK (nvfortran + cuFortran + cuTensor), AMD ROCm (flang-aocc + ROCmFortran), Intel oneAPI (ifx with SYCL interop).
- Climate/CFD: WRF, CESM, MPAS, ECMWF IFS, OpenFOAM (mostly C++ but Fortran-adjacent), MOM6, NEMO — all million-line Fortran codes.
- Modern tooling: fpm (package manager), stdlib (https://stdlib.fortran-lang.org/), fortran-lang.org community, Fortran Discourse, fortls (LSP), fprettify (formatter), test-drive (xUnit-style testing).
- Notable users: NOAA, NASA, ECMWF, CERN (lattice QCD), most national weather services, every major aerospace + automotive sim shop, every academic computational physics/chemistry/astronomy group on Earth.
Gotchas
- Default
implicit i-n. Withoutimplicit none, undeclared variables starting withi-nare integer, others are real. A typo silently creates a new variable with default kind. Alwaysimplicit none. - Column rules in fixed-form.
.f/.forfiles: code starts at column 7, columns 1-5 are labels, column 6 is continuation, columns 73-80 ignored. If you see weird “syntax error” on what looks like valid code, you’re probably in fixed form. Use.f90/ free-form unless maintaining legacy. - Array slicing copies. Passing
a(1:n:2)to a procedure expecting a contiguous array forces a copy in/out. Declare dummies asassumed-shape(real :: a(:)) or usecontiguousattribute (Fortran 2008):real, intent(in), contiguous :: a(:). - Character length in C interop. Hidden trailing length argument when passing strings to old-style F77 routines.
iso_c_bindingandbind(C)avoid this — use them for any new interop. - Allocatable LHS in F2003.
s = "longer string"re-allocatessto fit if it’scharacter(len=:), allocatable. Compilers default-on this since gfortran 5+ / ifort 17+, but old code written for F95 expects truncation. Compile with-fallocatable-lhs(it’s the default now). - Coarray performance. Single-image performance is unaffected, but inter-image communication latency on OpenCoarrays-over-MPI is ~MPI-message tier; if you’re chasing nanoseconds, native shared-memory implementations (Intel) or hand-written MPI-3 RMA win.
- Mixed compiler
.modfiles..modfiles are not portable across compilers (gfortran’s differ from ifx’s). Build everything with one compiler in a project, or usebind(C)interfaces for cross-compiler boundaries. integerdefault kind. Defaultintegeris 32-bit in nearly all compilers; do not assume — querykind(0)or useinteger(int64)fromiso_fortran_env. Some compilers expose-fdefault-integer-8which silently breaks every library binding.realdefault kind matchesintegerby default but-fdefault-real-8exists too — never use it; pollutes the ABI. Specifyreal(real64)explicitly when you mean double precision.
Citations
- Fortran-lang community: https://fortran-lang.org/learn/ (tutorials, fpm, stdlib).
- J3 standards committee: https://j3-fortran.org/ (US national body for ISO/IEC JTC1/SC22/WG5).
- ISO WG5: https://wg5-fortran.org/.
- Wikipedia, Fortran (history, current standard, compilers): https://en.wikipedia.org/wiki/Fortran (accessed 2026-05-06; cites Fortran 2023 = ISO/IEC 1539:2023, published Nov 2023).
- Fortran stdlib: https://stdlib.fortran-lang.org/.
- fpm (Fortran Package Manager): https://fpm.fortran-lang.org/.
- LFortran: https://lfortran.org/.
- LLVM Flang: https://flang.llvm.org/docs/.
- OpenCoarrays: http://www.opencoarrays.org/.
- NVIDIA HPC SDK (nvfortran,
-stdpar=gpu): https://docs.nvidia.com/hpc-sdk/. - Intel Fortran Compiler (ifx): https://www.intel.com/content/www/us/en/developer/tools/oneapi/fortran-compiler.html.