Scientific Computing

Fortran compiler standard enforce

Fortran compilers typically have options for enforcing Fortran standards. The compilers raise additional warnings or errors for code that is not deemed compliant. Fortran standard options can make false warnings, so we generally do not enable standards checking for user defaults. However, we do enforce implicit none as a quality measure.

It’s also important to use implicit none so that each variable must be assigned beforehand. We recommend the Fortran 2018 statement:

implicit none (type, external)

which requires explicitly defined procedure interfaces as well.

type
the traditional implicit none default
external
new for Fortran 2018, requires explicit interface for external procedures.

GCC Gfortran -std=f2018 enforces Fortran 2018 standard. Consider these Gfortran options:

gfortran -fimplicit-none

LLVM Flang similarly uses -std=f2018 and similar, as well as:

flang -fimplicit-none

Intel oneAPI -stand:f18 enforces Fortran 2018 standard. Consider these options that also enforce implicit none:

ifx -warn:declarations

Cray Fortran compiler enforces implicit none via option:

ftn -eI

note that’s a capital “I” not a lowercase “ell”.

Nvidia HPC Fortran compiler enforces implicit none via:

nvfortran -Mdclchk

NAG Fortran has -f2018 Fortran 2018 flag. Enforce implicit none by:

nagfor -u

CMake logic to enforce these standards:

if(CMAKE_Fortran_COMPILER_ID STREQUAL "Cray")
  add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:-eI>")
elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU|LLVMFlang")
  add_compile_options(-Wall "$<$<COMPILE_LANGUAGE:Fortran>:-fimplicit-none>")
elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel")
  add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:-warn:declarations>")
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "NVHPC")
  add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:-Mdclchk>")
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "NAG")
  add_compile_options("$<$<COMPILE_LANGUAGE:Fortran>:-u>")
endif()

Fortran undefined and unused variable warnings

Like other coding languages, Fortran code might have variables that are used before they are defined, or variables that are defined but never used. Uninitialized variables cause unexpected behavior that can be difficult to debug. Variables that are defined but never used are a waste of memory and possibly computation time and make the code less readable. Here is a trivial example of a Fortran program that has an undefined variable, that Fortran compilers generally don’t warn about by default, and may not even be able to warn at compile time.

program test
implicit none
integer :: i, j

i = j + 1
print '(i0)', i

end program test

We provide over 20 such tests in a CMake project that outputs JSON for the compiler tested.

GCC Gfortran 17 new compile-time warnings

GCC / GFortran 17 adds two Fortran compiler options to provide more robust (less false positive and false negative) warnings about undefined and unused variables. This is a potentially significant improvement over the -Wuninitialized and -Wmaybe-uninitialized flags, which operate on the Static Single Assignment (SSA) form and are known to be more likely to produce false positives and false negatives.

These new options are

The new -Wundefined-vars option by Thomas Koenig is in effect a front-end static analysis tool using tables of variable definitions and uses to determine if a variable is used before it is defined.

LLVM Flang Fortran compiler

The LLVM Flang compiler has an option -Wused-undefined-variable that at least in Flang 22.1 didn’t catch the undefined variable in the toy example above, but does catch the unused variable with the option -pedantic. It is mentioned in the Flang forums that the Flang team considers some of the cases Gfortran 17 catches to be for possibly future implemented runtime checks rather than compile-time checks as in GCC 17.

warning: Value of uninitialized local variable ‘i’ is used but never defined [-Wused-undefined-variable]

Intel oneAPI Fortran compiler

Uninitialized variable warnings are a runtime -check:uninit with “ifx” Fortran compiler, not currently available in compile-time -warn.

NVIDIA HPC SDK Fortran compiler

The “nvfortran” compiler nvfortran -help didn’t reveal any options to detect undefined variables beyond the usual -Mdclchk that enforces implicit none - but that’s a declaration check, not a defined variable check.

Xcode 15.x ld_classic linker workaround

Whether using Clang / LLVM or Homebrew GNU GCC compiler, GNU ld is not supported on macOS. Only the Apple macOS Xcode ld is supported. The new ld linker in Xcode 15 broke numerous projects, including OpenMPI < 4.1.6.

A special workaround for Xcode 15.x only is the linker flag -ld_classic, which is deprecated in Xcode 16 and removed in Xcode 27.

Set in file “~/.zshrc”

export LDFLAGS="$LDFLAGS -Wl,-ld_classic"

Note that for CMake, LDFLAGS environment variable is read only on the first CMake configure and cached.

macOS/Windows/Linux get/set sudo/admin users from Terminal

Each of macOS, Windows, and Linux has a way to get and set which users have administrative privileges (i.e. can use sudo or are in the Administrators group) from Terminal.

macOS get / set sudo users from Terminal

On macOS, the users who are sudo -capable (i.e. administrators) can be discovered from Terminal using the Directory Service command line utility dscl:

dscl . -read /Groups/admin GroupMembership

To add a user to the sudoers list, use the following command:

dscl . -append /Groups/admin GroupMembership <username>

Windows get Administrator users from Terminal

On Windows, the users who are Administrator capable can be discovered from PowerShell using:

Get-LocalGroupMember -Group "Administrators"

Query a specific user to see if they are an Administrator:

$user = "myUsername"
# put the desired username in the $user variable, then run the following command:

Get-LocalGroupMember -Group "Administrators" | Where-Object { $_.Name -like "*\$user" }

Make a user an Administrator:

Add-LocalGroupMember -Group "Administrators" -Member "myUsername"

For Windows Subsystem for Linux (WSL), use the Linux commands below.

Linux get / set sudo users from Terminal

On Linux, the users who are sudo -capable (i.e. administrators) can be discovered from Terminal using:

getent group sudo

Add a user to the sudo group (allowing them to use sudo):

sudo usermod -aG sudo <username>

List connected hard drives on macOS

List connected hard drives on macOS using the Terminal:

diskutil list

The “NAME” column shows the names of the hard drives under “/Volumes”. The following command gives a more detailed view of the connected hard drives:

system_profiler SPStorageDataType

Keep computer running disable suspend

Long-running Terminal programs might not be detected by the operating system on a laptop or other device that has power-saving sleep or suspend modes. This can lead to disappointment as the long-running program hasn’t completed when the user checks back. This caffeinate Python script works with:

to keep the computer awake while a command line program is running, using features built into the operating system utilities.

To avoid a computer going to sleep / suspend in general persistently is OS-dependent.

macOS power management from Terminal

For macOS, check and save to a text file the current power management settings with pmset:

pmset -g

To disable sleep / standby while on AC power (charger), which is also suitable for fixed devices like the Mac Mini:

pmset -a sleep 0 standby 0

Linux power management from Terminal

For Linux using systemd, check the current power management settings with:

systemctl list-unit-files | grep -E 'sleep|suspend|hibernate|hybrid'

look for any that show “enabled”.

To disable sleep while on AC power (charger), which is also suitable for fixed devices like a desktop, use systemctl mask to disable the sleep, suspend, hibernate, and hybrid-sleep targets:

systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

Windows power management from Command Prompt

For Windows, check and save to a text file the current power management settings with powercfg:

powercfg /query

Look for “Current AC Power Setting Index” in the output for “Sleep” and “Hibernate” to see the current settings.

To disable sleep while on AC power (charger), which is also suitable for fixed devices like a desktop:

powercfg /change standby-timeout-ac 0

To set Hibernate to never while on AC power (charger), while allowing hibernate to be enabled for battery power (important for laptop tucked away for the weekend), run the following command:

powercfg /change hibernate-timeout-ac 0

Note that powercfg /hibernate off completely disables hibernate, which is not necessarily recommended for laptops that may be stored without power when not manually shut down each time.

Build CMake from dev sources

While CMake binaries can be downloaded for most platforms, there are certain cases where one wishes to build CMake from source. For example, when preparing a merge request to fix or enhance CMake. Usually the computer will have at least an older version of CMake that can be used. If so, we recommend using the existing CMake to build the newer CMake from the CMake source directory. We recommend using Ninja in general for faster build and rebuild for any CMake project.

cmake -B build -DBUILD_TESTING:BOOL=OFF -DCMAKE_BUILD_TYPE=Release --install-prefix=$HOME/cmake-dev -G Ninja

cmake --build build

cmake --install build

A one-step download-build script is the scripts/build_cmake.cmake.


If an old CMake isn’t available on the computer, then use CMake bootstrap:

./bootstrap --prefix=$HOME/cmake-dev --generator=Ninja -- -DBUILD_TESTING:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=Release

ninja -C Bootstrap.cmk/ install

This puts the compiled CMake under ~/cmake-dev, without disturbing the primary CMake install. Upon making any CMake code changes, simply recompile the minimum needed bits by:

ninja -C Bootstrap.cmk/ install

Find Windows App install location with winget

Regardless of how an App is installed, whether through WinGet, the Microsoft Store, or a direct download, WinGet can be used to find the program install location on disk. For example, to find the install location of the Microsoft Edge browser:

winget list msedge --details

(one can also use the names Microsoft.Edge or Edge)

The results include “Installed Location” which for this example may be examined like:

ls ${Env:ProgramFiles(x86)}/microsoft/edge/application

with results including the Edge executable msedge.exe.

Matlab .NET on Linux, macOS, and Windows

Matlab external language interfaces includes .NET on Windows, Linux, and macOS. This allows efficiently calling .NET assemblies and using .NET libraries directly from Matlab.

The Matlab function dotnetenv is used to set up and check the active .NET environment in Matlab. Environment variable DOTNET_ROOT is vital for Matlab to detect the .NET installation, particularly on Linux and macOS. If Matlab is having issues detecting the .NET installation NET.isNETSupported is false, determine the value for DOTNET_ROOT from system Terminal:

dotnet --info

If “dotnet” command is not found, install .NET SDK:

  • macOS: brew install dotnet
  • Windows: winget search Microsoft.DotNet.SDK
  • Linux: from Microsoft
  • Matlab Online (Linux): if .NET isn’t detected, use .NET install script to install .NET SDK.

On macOS, determine environment variable DOTNET_ROOT by $(brew --prefix dotnet)/libexec.

If this path is not defined, look for the Base Path, and pick the directory that contains the “dotnet” executable.

Set the DOTNET_ROOT environment variable in Matlab by adding to Matlab startup.m so that future Matlab sessions have the correct DOTNET_ROOT set.

edit(fullfile(userpath,'startup.m'))

Then add the following line to startup.m:

setenv('DOTNET_ROOT', '/opt/homebrew/opt/dotnet/libexec')

Where the path was obtained from $(brew --prefix dotnet)/libexec on macOS, or from the .NET install location on Linux.

Restart Matlab. Do the following one-time command to finish setting up .NET in Matlab:

dotnetenv("core", Version="10")

Where version number “10” must match the major version of .NET in DOTNET_ROOT as shown by dotnet --list-sdks in Terminal.

In future Matlab sessions, dotnetenv() will be empty until the first .NET command is run, for example

NET.isNETSupported

then (optionally) running dotnetenv() shows the current .NET environment.

Fortran stop return codes

Summary:

  • stop (1956 Fortran I): return integer on stderr (recommendation)
  • stop (Fortran 77): return integer or constant character, if character it may or may not be printed, but return code is 0 (no error)
  • error stop (Fortran 2008): constant character with error code
  • error stop (Fortran 2018): variable character with error code, also allowed inside pure procedure.

Fortran 2018 finally brought the needed behavior for convenient error messages and continuous integration.

CMake and Meson handle automatic detection of compiler supported features like error stop.

Fortran 2008 error stop with constant code:

  • Gfortran ≥ 5
  • NAG ≥ 6.0
  • Intel oneAPI
  • Nvidia HPC SDK

Fortran 2018 error stop with variable code

  • Gfortran ≥ 7
  • NAG ≥ 6.2
  • Intel oneAPI
  • Nvidia HPC SDK

Fortran 2018 error stop,QUIET=.true./.false.

  • NAG ≥ 6.2

This feature was promoted by Steve Lionel, but has not yet been widely adopted. From the Fortran 2018 standard, the quiet= parameter not only suppresses any console output but also may suppress the error code? I would prefer to have the error return code, without the console text.

Since Fortran I in 1956, the stop statement has generally displayed a return code to indicate an error if an integer value was provided. Over time, stop statement behavior has changed to allow more refined signaling on program stop.

Since Fortran I in 1956, stop without return code to stop execution normally has been supported, along with stop with integer return code to indicate abnormal termination.

stop 1

The Fortran 2008 and 2018 standards recommend that the error code be returned on iso_fortran_env: error_unit, which was first defined in Fortran 2003 standard. The Fortran 77 standard defines the character string as “accessible” but doesn’t define where it goes. A best practice if desired to print a message when stopping a program is with explicit “print” or “write” statement. stop with integer code is still normal program termination in modern Fortran.

Since Fortran 77, stop may instead return a constant scalar character like “goodbye”. This generally sets return code to 0, that is, no error is indicated.

For continuous integration, having a reliable way to indicate error behavior is crucial. For HPC, indicating abnormal conditions to the shell is also vital to avoid taking resources on runs that suffered a computational error.

Fortran 2008 brought the long overdue error stop statement. stop still indicates normal program termination, and can for example stop individual images in a parallel executing program. Say an individual cell in a 3-D simulation did not find a stable solution. Depending on the simulation, that can be OK, perhaps set that value to NaN and stop with an error code on stderr, while letting the other images continue running.

However, in other types of simulations, an early failure to converge to a solution in a cell may invalidate the entire simulation taking a month of CPU time. Instead of writing cumbersome external checking code, the programmer can instead use error stop to reliably terminate all images when a critical failure is detected. Fortran 2008 error stop with constant string or integer code: both return non-zero exit status on stderr.

use, intrinsic:: iso_fortran_env, only: stderr=>error_unit

write(stderr,*) 'the failure was in ' // failedmod

error stop

Fortran 2018 added error stop with variable scalar string or variable integer code. A vital addition of Fortran 2018 is that error stop can be used within pure procedures, a very commonly needed use case. Fortran 2018 error stop variable character string allows for cleaner syntax, for example:

error stop 'the failure was in ' // failedmod