CTest parallel run by default

CMake environment variable CTEST_PARALLEL_LEVEL can be manually set to control default test parallel runs. It’s often desired to run tests in parallel to save time, and help ensure there aren’t hidden dependencies between tests. That is because CTest when run in parallel will effectively randomize the order of the tests, while maintaining dependencies setup by CTest fixtures. We usually set CTEST_PARALLEL_LEVEL environment variable to be equal to the number of physical CPU cores as a starting point.

If the computer runs out of RAM or has conflicts with parallel tests, then use Resource Locks and/or Fixtures to control the parallelism on a per-test basis.

iftop config file

iftop is a handy utility on MacOS, Linux and other Unices for a live Terminal graph of network data flow to particular addresses. On computers with many network interfaces, including virtual interfaces such as on MacOS, it is handy to set a default interface in a config file. iftop uses the file ~/.iftoprc.

For example, on MacOS you may be interested in interface “en1”. To help determine the desired interface, use ifconfig or ip a to find the interface with the public IP address. Then create ~/.iftoprc containing like:

interface: en1

where “en1” is your desired interface determined as per above.

ctest_empty_binary_directory usage

CTest CDash scripts can use the function ctest_empty_binary_directory to recursively remove a CMake build directory to avoid hidden state between test runs in an overall build-test cycle. However, this function will cause the overall CTest run to return a non-zero error code if CMakeCache.txt isn’t present in the build directory. This is confusing in a CI system particularly. While we do appreciate this safety feature over simply using file(REMOVE_RECURSE), it’s necessary to enclose in if() statements like below to avoid false errors. Here we assume you have already set CTEST_MODEL.

if(CTEST_MODEL STREQUAL Nightly OR CTEST_MODEL STREQUAL Continuous)
  if(EXISTS ${CTEST_BINARY_DIRECTORY}/CMakeCache.txt)
    ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY})
  endif()
endif()

...

ctest_start(${CTEST_MODEL})

...

Red Hat firewalld port add

Red Hat / CentOS uses firewalld to provide network firewall. firewalld has the concept of runtime vs. permanent rules, which help avoid getting the firewall into an unusable state. Permanent rules become live at next restart/reboot, while runtime rules disappear at restart/reboot.

Suppose one wishes to put the SSH server on a non-default port 12345 to mitigate auth log clutter. First configure the SSH server in /etc/ssh/sshd_config, then restart SSH and verify the SSH configuration is working by adding your port to firewalld (here, 12345):

firewall-cmd --add-port=12345/tcp

If this works, make the firewalld rule permanent:

firewall-cmd --permanent --add-port=12345/tcp

SELinux will also need an updated policy to allow the SSH port change, like:

sudo semanage port -a -t ssh_port_t -p tcp 12345

Use modern compilers in CentOS

Often we need a more modern compiler than is included in Red Hat / CentOS for HPC systems. Once an admin sets up GCC Toolset they can be loaded by any user thereafter. Prior to CentOS / RHEL 8, this was known as Developer Toolset devtoolset. CentOS / RHEL 8 introduced GCC Toolsets.

For example, to install and use GFortran 10, have the system admin do:

yum install gcc-toolset-10-gcc-gfortran

Thereafter, users without sudo use the new Gfortran version by:

scl enable gcc-toolset-10 bash

which is for that Terminal session only.

Fortran contiguous variables

Fortran 2018 contiguous arrays build upon Fortran 2008. In general, operating on contiguous arrays is faster than non-contiguous arrays. A non-contiguous array actual argument into a contiguous subroutine dummy argument is incorrect syntax. GCC < 9 may fail with SIGSEGV or SIGABRT in this case, dependent on array size. Other compiler correct this incorrect syntax by temporary array copy-in, copy-out which is slower.

Contiguous variables happen by default, so unless you do something with pointers or array striding, the variable is likely to be contiguous for simple Fortran code. To be sure, use is_contiguous intrinsic function. GCC ≥ 9 has is_contiguous and so do other modern Fortran compilers.


References: Fortran 2008 Contiguity

CMake expanduser tilde ~

To have the most reliable path operations in CMake, it’s typically best to resolve paths to the full expanded path. Note: there are a few CMake functions that desire relative paths, but those are clearly spelled out in the docs.

We have created expanduser.cmake:


Related: CMake file separator

List all CMake tests with CTest

As a CMake project grows, increasing complexity can make it hard to discern what tests are to be run and their properties. Perhaps the project logic is unexpectedly omitting necessary tests. The CI system or human can verify the list of tests by:

ctest -N

For machine parsing and human-readable verbose details including fixtures and labels, output JSON:

ctest --show-only=json-v1

To ensure an accurate test list, the project must first be configured and built as usual:

cmake -B build

cmake --build build

ctest --test-dir build -N

CMake file separator

In many cases, using the Unix-type slash file separator / will work even on Windows. Trying to manually specify backslash Windows file separator \ can cause problems in CMake and many other languages. Thus in CMake and other languages like Python, we always use / as path separator, and only transform to backslash for the rare cases it’s needed.

Transform to Unix file separator:

cmake_path(CONVERT "path\in\file.txt" TO_CMAKE_PATH_LIST out)

That switches backslash \ file separators to Unix slash / file separators. This becomes relevant if manually adjusting Include paths by appending to _INCLUDE_DIRS or similar. If backslashes sneak through, all kinds of weird build-time errors can result, and even configure-time errors if you use “check_c_source_compiles()” and similar. As the docs note, put quotes "${mypath}" around the variable expansion to ensure CMake doesn’t mangle the path.

Transform to native file separator is generally more rarely used. CMake can transform paths to native file separator, with the caveat that this can cause unpredictable Windows-specific backslash problems, as with any program.


Related: CMake expanduser ~

Fortran GDB Debugging

Debugging Fortran code with GNU Debugger gdb is roughly akin to debugging from the command line with Python pdb. Intel forked GDB into gdb-oneapi, formerly “gdb-ia”, that replaced “idb”. gdb is capable of far more than what’s listed here, down to stack registers and assembly instructions.

GDB also works with Visual Studio Code, which we generally recommend across coding languages.

Start GDB Fortran debugger: assuming executable myprog with arguments hello and 3:

gdb --args ./myprog hello 3

Run program in gdb (perhaps after setting breakpoints) with

r

A typical debugging task uses breakpoints. Breakpoints are where the debugger stops running until you type

c

Set breakpoints by functionName:lineNumber. Example: Set a breakpoint in function myfun on line 32

b myfun:32

For breakpoints in Fortran modules in this example line 32 of a module named mymod in function myfun:

b mymod::myfun:32

List all scope variables as two separate steps.

  1. local variables
  2. arguments to the function.

Variable type, size (bytes/element), and shape (elements/dim) are available for each variable “var” by

whatis var

Example: a Fortran iso_fortran_env real64 3-D array of size 220 x 23 x 83:

var = REAL(8) (220,23,83)

If “var” is a “derived type” (similar to “struct” in other languages), get the same information about each record (akin to “property”) of the derived type by

whatis var%prop

Local variables are variables used only within the scope of the function–excluding arguments to the function.

info locals

List the names and values of all arguments to the current function:

info args

Example: in integer function myfun(a,b) or subroutine mysub(a,b), upon info args you’d see perhaps

a = 1.5
b = 0.2

If a or b are arrays or structs, array values are printed as well.