Scientific Computing

CMake build type default

The default CMAKE_BUILD_TYPE is often desired to be set to Release. CMake environment variable CMAKE_BUILD_TYPE can be a convenient user default.

  • Linux add to ~/.profile:
  • macOS add to ~/.zshrc:
export CMAKE_BUILD_TYPE=Release
  • Windows, set CMAKE_BUILD_TYPE=Release via the user environment variable GUI.

We also generally recommend setting the default CMake generator.


For projects used by a range of users who may or may not know to set a default build type, and where the developers want to ensure users are normally getting the full performance of a Release build, use GENERATOR_IS_MULTI_CONFIG:

get_property(gen_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT gen_multi AND NOT (CMAKE_BUILD_TYPE OR DEFINED ENV{CMAKE_BUILD_TYPE}))
  set(CMAKE_BUILD_TYPE Release CACHE STRING "Release can have faster run time than Debug")
endif()

MUMPS CMake FetchContent

It’s helpful to see how CMake FetchContent works by example, which we have provided for MUMPS. This example uses FetchContent to retrieve and build MUMPS via CMake scripts, which itself retrieves MUMPS source code from the authors’ website via FetchContent.

Set OpenMPI MCA parameters

OpenMPI MCA parameters can be set in several ways. Some MCA parameters configure hardware, while other configure warnings. Depending on whether the parameters need to be per-user, per-hardware, etc. it may be useful to set some in a file, and others in the environment variables, and still others on the command line.

OpenMPI looks for a file at ~/.openmpi/mca-params.conf to set MCA parameters. The environment variables have a higher priority, and the command line has the highest priority as is intuitive.

The mca-params.conf syntax is like:

btl_openib_warn_no_device_params_found = 0

The environment variables would be set in ~/.profile like:

export UCX_TLS=ud,sm,self

Bluetooth trackballs with Linux

Trackballs declutter busy desks and can be more comfortable than mice for precision pointing. Logitech trackballs also support Linux, paired and configured via Solaar. Every few months pop out the trackball and clean the rollers or just blow on them to get the dust out.

Whatever mouse or trackball one buys, be sure to get one that work via Bluetooth Low Energy (BLE) or the Unifying receiver. The main observed differences of using BLE is slightly shorter battery life and a slight initial lag if the buttons/roller haven’t been used for several seconds. The advantage of BLE is avoiding damage to the laptop USB port with the dongle while handling the laptop.

The Logitech trackballs below have a user-maintainable trackball with back/forward buttons and clickable roller for programming and web-browsing convenience. These trackballs support BLE and Unifying receiver.

  • Logitech MX Ergo: a higher-end trackball with two-position tilt for comfort. The Ergo has a button to switch between two Bluetooth devices e.g. laptop, phone, etc.

  • Logitech M575: fixed tilt, no device switching button

Fortran random seed initialization

Fortran 2018 specifies a new subroutine random_init(). Gfortran 9 and Intel oneAPI support random_init.

Build systems such as Meson and CMake can detect compiler capabilities. random/ folder full example

Meson standard random_init detection:

project('foo', ['fortran'])

fc = meson.get_compiler('fortran')

f18random = fc.compiles('''program test
implicit none
intrinsic :: random_init
call random_init(.false., .false.)
end''',
name: 'F2018 random_init')

CMake standard random_init detection:

include(CheckSourceCompiles)

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# save link time, only compile is needed

check_source_compiles(Fortran "program test
implicit none
intrinsic :: random_init
call random_init(.false., .false.)
end"
f18random)

Older versions of gfortran call random_seed() gives a unique seed. Not every compiler vendor gives a unique seed for random_seed() however.

Play, Record, Process live audio with Numpy

PyGame, PyAudio and PySoundDevice are three of the best currently maintained packages for playing audio from Python, including from Numpy arrays or streaming sources. For these examples, we will use this common sinewave-generating code in a Numpy array.

import numpy as np
fs = 8000 # Hz
T = 1. # second, arbitrary length of tone

# 1 kHz sine wave, 1 second long, sampled at 8 kHz
t = np.arange(0, T, 1/fs)
x = 0.5 * np.sin(2*np.pi*1000*t)   # 0.5 is arbitrary to avoid clipping sound card DAC
x  = (x*32768).astype(np.int16)  # scale to int16 for sound card

PySoundDevice has the simplest syntax for Python audio play/record packages.

import sounddevice
import time

sounddevice.play(x, fs)  # releases GIL
time.sleep(1)  # NOTE: Since sound playback is async, allow sound playback to finish before Python exits

PyGame is for building games and does much more than audio. I find PyGame to be very robust across a wide variety of systems (including embedded systems) to play audio from Python.

pip install pygame

uses pre-compiled .whl binary wheel.

import pygame
from time import sleep

pygame.mixer.pre_init(fs, size=-16, channels=1)
pygame.mixer.init()
sound = pygame.sndarray.make_sound(x)

sound.play()

sleep(0.01) # NOTE: Since sound playback is async, allow sound playback to start before Python exits

The PyAudio and PySoundDevice packages are PortAudio based.

PyAudio uses PortAudio to allow easy playback of Numpy arrays. If you find that you’re not able to install PortAudio, consider using PyGame instead of PyAudio.

import pyaudio

# PyAudio doesn't seem to have context manager
P = pyaudio.PyAudio()

stream = P.open(rate=fs, format=pyaudio.paInt16, channels=1, output=True)
stream.write(x.tobytes())

stream.close() # this blocks until sound finishes playing

P.terminate()

Oct2Py doesn’t work as temporary files are needed. Example PyGame program: audio_pygame.py. Advanced PyGame setup.

Anaconda Python + Spyder on WSL

Python can be used within Windows Subsystem for Linux. Using Python on WSL can be advantageous because of easier compiler access. Miniconda Python works well on WSL.

conda install matplotlib spyder

If Spyder won’t start, look in the error message for missing libraries such as libxcomposite libxss1 X11 prereqs are specified in the error message on starting GUI programs like Spyder.

ModuleNotFoundError: No module named ‘PyQt5.QtWebKitWidgets’

See if libxcomposite or libxss etc. need to be installed via apt install.


Notes on setting up Matplotlib for WSL

Disk free space and Snap drives

Disk free space “df” includes Snap virtual drives. Listing actual hard drive partitions is accomplished with “grep”.

Find only Ext4 and FUSE drives:

df -hT | grep -E "ext4|fuseblk"

Exclude “tmpfs” and “snap” virtual drives:

df -hT | grep -vE "squashfs|tmpfs"

As Snap packages update, old versions are left behind. Find Snap packages with multiple versions by examining output of:

df -t squashfs | sort -k6

Suppose /snap/slack/6 and /snap/slack/7 are installed. Remove the old Slack Snap version:

snap remove slack --revision=6

Find drive types:

df -T

CMake download and extract compressed files

Using CMake to download, verify the checksum of files and extract compressed files is easy and seamless. FetchContent downloads the file at configure time.

include(FetchContent)

function(download_file url hash)

FetchContent_Declare(download_${hash}
URL ${url}
URL_HASH SHA256=${hash}
DOWNLOAD_NO_EXTRACT true
)

if(NOT download_${hash}_POPULATED)
  FetchContent_Populate(download_${hash})
endif()

endfunction(download_file)

# === example
download_file(
  https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg
  12794390cce7d0682ffc783c785e4282305684431b30b29ed75c224da24035b4
)

This downloads the file and:

  • checks hash signature
  • verifies the SSL certificate

CMake uses vendored curl internally.


CMake also can extract compressed files like .zip, .tar.bz2, etc. This command can also specify output directory, extract a subset of files matching a pattern and more.

file(ARCHIVE_EXTRACT INPUT in_file.zip)