Scientific Computing

Fortran terminal ISO standard units

iso_fortran_env is respected by modern Fortran compilers, as implied by its name. iso_fortran_env is part of the Fortran 2003 specification and popular Fortran compilers added it by calendar year 2010 generally.

Why use iso_fortran_env terminal I/O: legacy programs written before Fortran 2003 often write to terminal with:

write(*,*) 'The value of X is ',x

or

write(6,*) 'The value of X is ',x

This is a problem when trying to debug with text output to terminal, especially where someone has used “6” for file I/O unit by mistake. Or, if trying to write to a file with an uninitialized unit number, stderr gets redirected to file fort.0.

For operating systems including macOS, Windows, and Linux the standard terminal units correspond like:

File descriptor Fortran unit number Standard unit Gfortran env var
0 5 input_unit (stdin) GFORTRAN_STDIN_UNIT
1 6 output_unit (stdout) GFORTRAN_STDOUT_UNIT
2 0 error_unit (stderr) GFORTRAN_STDERR_UNIT

Legacy code making use of open(unit=) number in this range can cause unexpected behavior. Please use open(newunit=...) to avoid conflicts with the standard terminal units. Of note, GFortran can use environment variables to change the default unit numbers for standard input, output, and error as in the table above.

Example

Print repeatedly to the same line, and combine prompt text on the same line with input.

iso_fortran_env terminal I/O: example prints to stdout, then stderr and finally asks for user input with a prompt on the same line.

program myterm
use iso_fortran_env
implicit none (type, external)
character(1000) :: usertxt  ! 1000 is an arbitrarily large number
integer :: ios

! could also just use print *,'printed to stdout'
write(output_unit,*) 'Printed to stdout'

write(error_unit,*) 'printed to stderr'

! prompt with caret on same line as input, here using a greater than sign >
write(output_unit,'(A)',advance='no') ' >'
flush(output_unit)

read(input_unit,"(A)", iostat=ios) usertxt
! trap Ctrl-D EOF on Unix-like systems to avoid crashing program
if (ios/=0) backspace(input_unit)  ! ctrl D gobble

write(output_unit,*) usertxt

end program

If stderr goes to file fort.0

If stderr from error_unit gets written to a file fort.0 instead of being printed to screen, this is an indication of open(u)ing a file without first setting a value for u, which might default to 0.

Unless needing to persist a file opening between calls of a function/subroutine, normally open a file with newunit.

program myfile
use iso_fortran_env
implicit none (type, external)

integer :: u, ios
character(1000) :: fn ! 1000 is an arbitrarily large number
character(1000) :: dat

print *, "please input file to open"

read(input_unit, '(a)', iostat=ios) fn
if(is_iostat_end(ios)) stop

! open file, using better to ask forgiveness than permission principle
! status='old' means generate error if file doesn't exist
open(newunit=u,file=fn, status='old',action='read',iostat=ios)
if (ios /= 0) then
    write(error_unit,*) 'could not open file ',trim(fn)
    error stop 'file IO error'
endif

read(u,'(A)') dat
print *,'first two lines of ',trim(fn),' are:'
print *,trim(dat)
read(u,'(A)') dat
print *,trim(dat)

close(u)  ! implicitly closed at end of program, but as good practice...
end program

Related:

UTM use serial remote

UTM virtual machine host has a built-in display, but its terminal is rather limited and currently doesn’t allow scroll back. It’s convenient to use the serial device with a terminal program like screen from the host OS. To configure the serial device, power off the VM. Add a Serial Device under the UTM VM settings. Before starting the VM, note on the VM settings main page (starts with “Status Stopped”) the name of the “Serial (TTY)” device like /dev/ttys005 or similar.

UTM serial display

The GNU screen program can be used from the host OS to connect efficiently to the guest OS.

brew install screen

From the host OS Terminal, connect like

screen /dev/ttys005

The serial device with screen enables scroll back through the terminal. On the host OS, edit the file “~/.screenrc” to include the following lines:

defscrollback 10000
termcapinfo xterm* ti@:te@
defscrollback 10000
Sets the default scrollback buffer to 10000 lines in screen.
termcapinfo xterm* ti@:te@
allow scrollback to work as expected.

Homebrew GCC version select

Homebrew can select GCC versions (and versions of other Homebrew formulae) like:

brew install gcc@15

Then create a file like “~/gcc-15.sh” with the following content:

v=15
prefix=$(brew --prefix gcc@$v)/bin

export CC=$prefix/gcc-$v CXX=$prefix/g++-$v FC=$prefix/gfortran-$v

Activate the GCC version in this shell session by running:

source ~/gcc-15.sh

If there is still trouble, try modifying SDKROOT to find a compatible SDK for the selected Homebrew GCC version.

Using an older version of Homebrew-distributed library or program could be useful to workaround bugs, or if one wants to still use their Intel CPU Mac hardware with Homebrew as GCC 16 isn’t supported by Homebrew on Intel CPUs on macOS for example. The Homebrew bottle binaries or source compile scripts must be available for the CPU architecture and OS version of the computer.

Shell scripts should avoid use of aliases

Do not use shell aliases in non-interactive shell scripts to stay shell-agnostic.

In Bash, aliases are not expanded in non-interactive shells unless the script first invokes

shopt -s expand_aliases

In Zsh, aliases are expanded in non-interactive scripts as well. However, while on macOS the default interactive shell is Zsh, non-interactive scripts with the usual #!/bin/sh shebang run Apple’s Bash in POSIX mode instead of Zsh.

List the aliases defined in the current shell (Linux, macOS, BSD) with:

alias

Our practice is to avoid aliases in non-interactive scripts and use explicit paths or functions instead. For example, to pin a user-specified GCC for CMake, pass the compiler paths directly:

#!/bin/sh

prefix="$HOME/.local/bin"
gcc_version=17
CC="$prefix/gcc-$gcc_version"
CXX="$prefix/g++-$gcc_version"
FC="$prefix/gfortran-$gcc_version"

cmake -B build -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_Fortran_COMPILER=$FC

cmake --build build

Windows PowerShell

Although the preceding discussion is for Unix-like systems, Windows PowerShell can also use aliases and has similar considerations for non-interactive scripts.

To list the aliases defined in the current PowerShell session:

Get-Alias

An analogous script for non-interactive PowerShell scripts would be like:

$prefix = "$HOME/.local/bin"
$gcc_version = 17
$CC = "$prefix/gcc-$gcc_version"
$CXX = "$prefix/g++-$gcc_version"
$FC = "$prefix/gfortran-$gcc_version"

cmake -B build -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_Fortran_COMPILER=$FC

cmake --build build

List comments/annotations in PDF

Adobe Reader can export and view a list of comments through the Comment tab on the right side. To do this from a command line program (for example to feed AI analysis / training) consider a Python script using the high performance PyMuPDF (fitz) library:

import fitz
import sys

file = sys.argv[1]

doc = fitz.open(file)

for i, page in enumerate(doc, start=1):
    for a in page.annots() or []:
        info: dict = a.info

        print(
            f"page {i}  {a.type[1]}  "
            f"author={info.get('title')!r}  "
            f"text={info.get('content')!r}"
        )

        # for highlights, get the actual selected words:
        if a.type[1] in ("Highlight", "Underline", "StrikeOut", "Squiggly"):
            print("  quoted:", page.get_textbox(a.rect))

Print PDF from any Linux program

On Linux, the CUPS PDF program saves printed PDFs to the ~/PDF directory from any program.

apt install printer-driver-cups-pdf
# or
dnf install cups-pdf

# then
systemctl restart cups

Verify a PDF printer is visible:

lpstat -p -d

The PDF output directory can be configured by editing the “Out” directory in /etc/cups/cups-pdf.conf

Higher quality PDF might be obtained by using the PDF print options in the specific program, if available.

LibreOffice PDF Conversion

Alternatively, LibreOffice can print compatible documents to PDF directly from the command line in headless mode like:

soffice --headless --convert-to pdf file.docx

Libreoffice can be installed like

apt install libreoffice-common

Related: convert image stack to PDF

Fortran module file format

The Fortran standard does not define a specific Fortran module file format. Each compiler vendor has a unique incompatible Fortran module file format. Fortran module files are not portable between different compilers or even different versions of the same compiler.

LLVM Flang

The LLVM Flang .mod files generated are legal Fortran syntax – they are text files. The .mod format gives the version number, which may be seen like:

head -n1 <moduleName>.mod

The output starts like:

!mod$ v1

GNU Fortran (GFortran)

The GFortran header version is defined in module.cc as variable “MOD_VERSION”. GNU Fortran “gfortran” .mod files are GZIP and is not documented

GCC version module file version
15.x 16
8.x - 14.x 15
5.1.0 14
4.9.2 12
4.8.1 10
4.7.1 9

Examine Gfortran .mod file header like:

gunzip -c <moduleName>.mod | head -n1

The output starts like:

GFORTRAN module version ‘15’ created from …

Intel oneAPI (ifx)

Intel oneAPI .mod files are a proprietary binary format. It is possible to determine the version of the .mod file by using od to look at the first 2 bytes of the .mod file.

od -An -N4 -d <moduleName>.mod

The first number is like “13” and is the module format version. This version may change over time as oneAPI internals change. The second number is the update version, which is fixed at “1”.

There is an undocumented “ifx” compiler option -switch:fe_module_dump that outputs the module and submodule data in text format.

NVHPC and AOCC

NVIDIA HPC SDK (NVHPC) and AOCC compilers generate .mod files that are text files. The format for legacy Flang module files is distinct from LLVMFlang Fortran module files.

The .mod file is a text file, beginning with the version number.

head -n1 <moduleName>.mod

The output is like:

V34 :0x24 dummy

Cray Fortran

By default, Cray Fortran stores uppercase DUMMY.mod filenames. This can be made lowercase with the ftn -ef flag. The Cray Fortran .mod format is proprietary, but the version number might be seen like:

head -n2 <moduleName>.mod

Related: Fortran submodule file naming

Homebrew EOL Intel CPU support

At WWDC25 on June 9 2025, Apple announced that macOS 27 would no longer support Intel CPUs.

In August 2025, Homebrew announced under Future macOS Support that by the end of summer 2026, Intel CPU Macs would no longer receive binary “bottles” and would be deprioritized to Tier 3 in general. Homebrew Tier 3 status implications includes:

  • Homebrew may work, but with a poor and unstable experience
  • Migration to a Tier 1 or 2 configuration or to a non-Homebrew tool is strongly recommended

Homebrew is not distributing binary “bottles” for Intel CPU Mac hardware. This means that commands “brew upgrade” or “brew install” may take a long time (hours) as packages are compiled from source - or may even fail.

To temporarily keep an Intel CPU Mac functional with Homebrew, one can try to use older versions of Homebrew formulae that still support Intel CPUs, or compile packages from source manually. For example, GCC 15 can be used to maintain compatibility with Intel CPU Macs.

Fortran compiler max line length options

The Fortran 202x standard raises the individual Fortran source code line limit to 10000 characters. The new limits also include unlimited continuation lines and a maximum statement length of one million characters. These are stated as “hard limits”. As has historically been the case, many compilers continue to allow going beyond the line limit in a line or beyond even ten million characters in a statement. LLVMFlang is explicitly designed without a particular character limit per line.

Fortran standard source code line length limit
202x 10000
90 132
77 72

Compiler differences in default maximum source code line length can lead to portability issues. For example, Linux workstations or Linux HPC may default to a GCC Gfortran older than version 14 that has a default Fortran source code line length maximum of 132 characters, which is too short for say generated source code containing a data path on a networked HPC drive.

It’s important to be cognizant of the maximum Fortran line length across compilers as developer laptops often default to a recent Flang or Gfortran that both have effectively unlimited line length.

The maximum line length we experienced with no options follows, as determined experimentally with our Fortran maximum line length test program.

Git file mode permissions

In certain situations, there can be nuisance Git dirty repo messages like:

old mode 100755
new mode 100644

The root cause of this false dirty Git status is loss of fidelity of the executable file mode bit. This can occur in situations including:

  • Using WSL Git on a native Windows filesystem or vice versa
  • Linux HPC where files are shared between users

Workaround this ambiguous executable filemode bit for each Git client by executing within the Git repository:

git config core.filemode false