Scientific Computing

Grub force text console boot

When using a virtual machine, and especially if the virtual machine is in emulation mode, which is generally many times slower than virtualization mode, it can greatly help interaction speed to use a console boot rather than graphical desktop. In operating systems like Ubuntu, this can be done by editing /etc/default/grub and setting:

GRUB_CMDLINE_LINUX_DEFAULT="text"
GRUB_TERMINAL=console

Then from Terminal:

update-grub

systemctl set-default multi-user.target

Then the system will boot into a text console on all future boots.


Reference

Install Windows Subsystem for Linux

Windows Subsystem for Linux WSL has Ubuntu LTS releases among other Linux distros on the Microsoft Store. The Microsoft Store is the recommended method to install WSL.

If the Microsoft Store isn’t available on the computer, manual WSL install is also available. The WSL changelog shows the continually expanding WSL feature set.

WSL can use GUI and sound with programs like Spyder via WSLg.

List WSL distros already installed on the computer from PowerShell / Command Prompt:

wsl --list --verbose

Install, list, and switch between Linux distros on Windows default for bash from Command Prompt:

wslconfig

WSL configuration

Limit the amount of RAM WSL2 can use for all installed WSL instances by editing Windows file $HOME/.wslconfig to include:

[wsl2]
swap=0
memory=4GB

Set memory= to less than the total computer physical RAM to help avoid using Windows swap.

A per-WSL instance default that is confusing and slows down WSL program-finding is stuffing Windows PATH into WSL PATH. We normally disable Windows PATH injection into WSL, because it also breaks library finding in build systems like CMake. Additionally, we enable filesystem metadata, as weird permission errors can occur, even causing CMake to fail to configure simple projects.

Add to each distro’s /etc/wsl.conf file:

[interop]
enabled=false
appendWindowsPath=false

Run Ubuntu apps from Windows Command Prompt or PowerShell:

wsl ls -l

Run Windows program from Ubuntu terminal:

/mnt/c/Windows/System32/notepad.exe

Note that capitalization matters and .exe must be at the end.

Select CMake cURL version to build with

CMake uses the bundled cURL library by default. When debugging connectivity issues with CMake, a developer may wish to build CMake with a specific cURL version. To do so, from the cmake/ project source directory, use options like:

cmake -Bbuild -DCURL_ROOT=/path/to/curl -DCMAKE_USE_SYSTEM_LIBRARY_CURL=yes

Then build CMake as usual.

Verify the Curl version used with this script.

macOS software update from Terminal

macOS can update system software from Terminal, even macOS itself. However, to complete the upgrade requires using the graphical desktop, perhaps over VNC.

softwareupdate -l

shows the available updates.

softwareupdate -i -a

installs all available updates.

Fortran module file format

The Fortran standard does not define a .mod file format, and every compiler has a unique incompatible format. This means that .mod files are not portable between different compilers or even different versions of the same compiler.

The per-compiler examples below assume Fortran source file “example.f90”:

module dummy

real, parameter :: pi = 4*atan(1.0)

contains

real function tau()
  tau = 2*pi
end function tau

end module dummy

Flang-new (LLVM)

The flang-f18 or flang-new LLVM Fortran .mod files generated are legal Fortran syntax – they are text files. The .mod format gives the version number.

Build:

gfortran -c example.f90

This generates Fortran module file “dummy.mod” and object file “example.o”.

Get the .mod file header:

head -n1 dummy.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
up to 4.3.2 unversioned
4.4 0
4.5.1 4
4.6.3 6
4.7.0pre 8
4.7.1 9
4.8.1 10
4.9.2 12
5.1.0 14
8.x - 14.x 15

Build:

gfortran -c example.f90

This generates Fortran module file “dummy.mod” and object file “example.o”.

Extract the .mod file header like:

gunzip -c dummy.mod | head -n1

The output starts like:

GFORTRAN module version ‘15’ created from …

If you get:

gzip: not in gzip format

the .mod file is probably from another compiler vendor e.g. Intel oneAPI.

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 -N4 -d dummy.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”.

NVHPC and other old Flang compilers

NVIDIA HPC SDK (NVHPC), AOCC, and other old Flang compilers generate .mod files that are text files. The format is different than flang-new.

Create the .mod file like:

nvfortran -c example.f90

# or

flang -c example.f90

generates a text file, beginning with the version number.

head -n1 dummy.mod

the output is like:

V34 :0x24 dummy

Cray Fortran

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

ftn -c example.f90

head -n2 DUMMY.mod

Related: Fortran submodule file naming

Fortran character allocatable

Variable length strings are implemented in Fortran 2003 standard like:

character(:), allocatable :: str

Passing such variables to procedures is declared the same as fixed length strings. In fact, we always declare actual arguments with “*” to avoid needing every string an exact length.

subroutine example(str)
character(*), intent(in) :: str

Before Fortran 202X standard, intrinsic Fortran functions need traditional fixed length character variables. For example:

integer :: i, L
character(:), allocatable :: buf

call get_command_argument(1, length=L, status=i)
if (i /= 0) error stop "first command argument not available"
allocate(character(L) :: buf)
call get_command_argument(1, buf)

Fortran function can return allocatable characters. If you run into bugs with this on old compilers, try manually allocating the character variable. Fortran standard compliant compilers auto-allocate character functions like numeric arrays.

It is proper to manually allocate character variable actual arguments when bind(C) interfaces are used.

function greet(b)

logical, intent(in) :: b

character(:), allocatable :: greet

!! Manual allocation of character variable. This could be necessary on old or buggy compilers.

if(b) then
  allocate(character(5) :: greet)
  greet = 'hello'
else
  allocate(character(3) :: greet)
  greet = 'bye'
endif

end function greet

GitHub Actions per-job compiler

GitHub Actions workflows can use different compilers per job by writing the compiler name or path to environment files in each job. This is useful for programs and libraries that need distinct compiler versions. An example of this is Matlab, where each Matlab release has a range of compatible compilers.

  • Matlab R2021a, R2021b: GCC-8
  • Matlab R2022a and newer: GCC-10

Implement in GitHub Actions:

jobs:

  linux:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        release: [R2021b, R2024a, latest-including-prerelease]

    steps:

    - name: GCC-8 - Matlab < R2022a
      if: ${{ matrix.release < 'R2022a' && matrix.release != 'latest-including-prerelease' }}
      run: |
        echo "CC=gcc-8" >> $GITHUB_ENV
        echo "CXX=g++-8" >> $GITHUB_ENV
        echo "FC=gfortran-8" >> $GITHUB_ENV        

    - name: GCC-10 - Matlab >= R2022a
      if: ${{ matrix.release >= 'R2022a' || matrix.release == 'latest-including-prerelease' }}
      run: |
        echo "CC=gcc-10" >> $GITHUB_ENV
        echo "CXX=g++-10" >> $GITHUB_ENV
        echo "FC=gfortran-10" >> $GITHUB_ENV        

    - name: Install MATLAB
      uses: matlab-actions/setup-matlab
      with:
        release: ${{ matrix.release }}
        cache: true

Enable Windows battery time on hover

Battery time remaining estimates for computing devices can vary widely in accuracy. The estimates are based on assumptions about future behavior based on prior usage trends, from a mix of current and prior charge usage. Windows updates can disable battery time remaining, and devices may come from the factory with battery time estimates disabled.

Hovering over the battery icon on the Windows taskbar can show the estimated battery time remaining along with the percent battery charge. The estimated battery time remaining is the same shown under System / Power & Battery.

The usual precautions on modifying the Windows Registry apply–do a Windows System Recovery milestone first. These keys are under:

HKLM\SYSTEM\CurrentControlSet\Control\Power

Reboot after making these changes. It may take a minute or two after first reboot for the estaimted battery life to show up.

If these registry keys exist, set their value to 0. If they don’t exist, that’s fine too.

EnergyEstimationDisabled=0
UserBatteryDischargeEstimator=0

Create this DWORD32 value (if not existing) and set to 1

EnergyEstimationEnabled=1

Reference: forum

CMake JSON array iteration

JSON can hold several data types including arrays. This example iterates over a JSON array in CMake.

Suppose file “json.cmake” contains:

cmake_minimum_required(VERSION 3.19)

set(jarr "{
\"c\":[3,4,5]
}")

string(JSON L LENGTH ${jarr} "c")
math(EXPR L "${L}-1")

string(JSON t TYPE ${jarr} "c")
message(STATUS "type: ${t}")

foreach(i RANGE ${L})
  string(JSON v GET ${jarr} "c" ${i})
  string(JSON t TYPE ${jarr} "c" ${i})
  message(STATUS "c[${i}] = ${v}  ${t}")
endforeach()

This results in:

$ cmake -P json.cmake

-- type: ARRAY
-- c[0] = 3  NUMBER
-- c[1] = 4  NUMBER
-- c[2] = 5  NUMBER