Scientific Computing

Matlab MEX compiler setup

Matlab requires compilers for mex -setup langage used (C / C++ / Fortran) and Matlab Engine for the respective code language. Windows Matlab supported compiler locations are communicated to Matlab via environment variables.

One-time setup MEX:

mex -setup C
mex -setup C++
mex -setup Fortran

Inspect Matlab MEX parameters:

mex.getCompilerConfigurations('c')
mex.getCompilerConfigurations('c++')
mex.getCompilerConfigurations('fortran')

It’s possible to switch between compilers that are setup with MEX. Choosing compilers is generally not possible on Linux or macOS from within Matlab. If a oneAPI version compatible with Matlab is installed on Windows, Matlab may detect it and allow switching compilers. If a different compiler is detected and allowed by Matlab, commands to choose the compiler will be at the bottom of the output when using the “mex -setup” commands below.

If having trouble with mex -setup for example if setup fails on macOS like:

“sh: /var/folders/…/mex_…: No such file or directory”

Try running the mex -setup command from Terminal using Matlab batch mode to see if Matlab’s shell was breaking setup. This usually fixes the setup issue. In our cases, we found that environment variables MATLAB_SHELL and SHELL were already set appropriately (not the generic /bin/sh), but we still had to run the mex -setup command from Terminal.

matlab -batch "mex -setup C -v"
matlab -batch "mex -setup C++ -v"
matlab -batch "mex -setup Fortran -v"

Once MEX is working, consider using Matlab buildtool build system for simple, terse syntax to build and test MEX and Matlab Engine code.

Windows MinGW MEX

Using MinGW on Windows with Matlab requires having an exact version of MinGW supported by Matlab. For example, the version of MinGW with MSYS2 is generally not supported by Matlab.

Tell Matlab the supported MinGW compiler path via Windows environment variable MW_MINGW64_LOC.

Find the MinGW compiler location from PowerShell:

(Get-Item((Get-Command gcc.exe).Path)).Directory.FullName

Put that path in Matlab:

setenv('MW_MINGW64_LOC', '<path of gcc.exe>')

Do “mex -setup” as above.

C MEX Example

Compile built-in C example

mex(fullfile(matlabroot,'extern/examples/mex/yprime.c'))

Run

yprime(3, [4,2,7,1])

ans = 2.0000 5.9924 1.0000 2.986

Fortran MEX example

Compile built-in Fortran example:

mex(fullfile(matlabroot,'extern/examples/refbook/timestwo.F'))

Run:

timestwo(3)

ans = 6.0

Matlab refresh function cache

Matlab might not use newly-compiled MEX functions if the function cache is not cleared. This can happen when the MEX function was previously called before building the MEX code. Detect if the MEX implementation of a function is being used in memory:

function y = is_mex_fun(name)
  y = endsWith(which(name), mexext());
end

Example: Matlab function timestwo.m and optionally MEX compiled function also called timestwo.

function y = timestwo(x)
  disp("this is plain Matlab script")
  y = 2 * x;
end

If one builds the MEX function with the same name and then calls the function, Matlab may not use the MEX compiled version until the function cache is cleared. Clear the Matlab function cache, thereby enabling newly-compiled MEX functions to be used by command

clear functions

% or

clear all

% then

assert(is_mex_fun("timestwo"))

These commands do NOT clear the function cache:

% these don't help
rehash
rehash path
clear mex

Remove CMake internal definition like -DNDEBUG

For C or C++ projects with incorrect #define logic or due to compiler bugs, it may be necessary to avoid CMake internally set definitions like -DNDEBUG. CMake internally sets -DNDEBUG when the CMAKE_BUILD_TYPE is set to Release, RelWithDebInfo, or MinSizeRel.

This can be done in scope like:

string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")

remove_definitions(-DNDEBUG) does not work here because -DNDEBUG is set internal to CMake.

The same may be accomplished per target by manipulating target COMPILE_DEFINITIONS:

get_target_property(_cf my_target COMPILE_DEFINITIONS)

string(REPLACE "-DNDEBUG" "" _cf "${_cf}")

set_target_properties(my_target PROPERTIES COMPILE_DEFINITIONS "${_cf}")

Matplotlib constrained_layout vs. tight_layout

In general, Matplotlib figures look better with constrained_layout. The older tight_layout is not as flexible and can lead to overlapping text, particularly with “suptitle”.

To make figures with subplots and suptitle work better, use:

matplotlib.pyplot.figure(layout='constrained')

# or

matplotlib.figure.Figure(constrained_layout=True)

Example:

import matplotlib.pyplot as plt

fg = plt.figure(layout='constrained')
ax = fg.subplots(3, 1)

for i in range(3):
    ax[i].plot(range(5+5*i))

fg.suptitle('lots of lines')

plt.show()

Upcoming CMake improvements

These are CMake MRs (Merge Requests) that have been or may be merged. They are not yet in a CMake release, but they may be included in future releases.

  • ExternalProject set environment variables for each of the configure, build, test, and install steps. Previously this was a cumbersome syntax invoking cmake -E env or similar.
  • Fix Windows Console handling: CMake 4.1 aims to enable CMake Windows color console output and fix long-standing race conditions in Windows Console handling.

Xarray, NumPy, and NetCDF ABI Compatibility

Xarray can write and load netCDF4 files into datasets. Warning messages may appear when using older netCDF4 files with newer versions of Xarray or NumPy like:

RuntimeWarning: numpy.ndarray size changed, may indicate binary incompatibility. Expected 16 from C header, got 96 from PyObject

This may indicate underlying incompatibilities between the versions of Xarray, Pandas, NumPy, and the netCDF4 library.

Conda per-environment channels

Advantages of conda over pip include:

  • distributing per-platform optimized libraries
  • prioritized channels resolve version conflicts, helping mitigate Python package dependency hell

Conda channel priority order is ordered by which channel appears first (highest) in .condarc. We generally recommend adding per-environment channels rather than modifying the global configuration to avoid corrupting multiple environments with incompatible packages`.

In general “strict” channel priority is recommended to mitigate compatibility problems.

conda config --set channel_priority strict

conda config --get channel_priority

Add a conda per-environment channel:

conda activate <env-name>

conda config --env --add channels <channel-name>

Get the current channel list for an environment:

conda activate <env-name>

conda config --env --show channels

Floating point comparisons in Python xarray

Like the Numpy helper function numpy.testing.assert_allclose(), the Xarray helper function xarray.testing module compares floating point arrays within a specified tolerance. Example:

import xarray
import xarray.testing

# code under test
dat = myfunc(...)

# load the reference Dataset to compare against
ref = xarray.open_dataset("ref.nc")

xarray.testing.assert_allclose(ref, dat)

dat.equals(ref) is generally inappropriate to directly compare floating point numbers. Clive Moler’s article address the topic of floating point comparisons succinctly.

Floating point comparison algorithm: across computing languages, an algorithm suitable for comparing floating point numbers “actual, desired” to absolute tolerance “atol” and relative tolerance “rtol” is:

isclose = abs(actual - desired) <= max(rtol * max(abs(actual), abs(desired)), atol)

isclose is boolean True or False – an array if actual or desired are arrays.

Determine GCC -Wall flags enabled

Projects using GCC, G++, and Gfortran compilers often set the -Wall option as default. The intent of -Wall is to enable a common subset of warnings that are generally useful for catching potential issues in your code. However, new warnings may be buggy to the point of breaking builds on valid code, or cause false warnings. It may be beneficial to not enable -Wall by default for end-user builds, but only for development builds.

GCC does not appear to have a way to tell what warnings are enabled by -Wall programmatically. Instead, one must refer to the GCC documentation or even the GCC source code to see which warning flags are included in -Wall. For example, in Gfortran 15.1, the -Wall option includes the -Wexternal-argument-mismatch warning, which was broke builds on valid code, and was fixed in Gfortran 15.2.

Gfortran 15.1 Wexternal-argument-mismatch bug

Gfortran 15.1 has a bug with the -Wexternal-argument-mismatch flag. This flag is implicitly included in the -Wall option. In Gfortran 15.1–fixed in Gfortran 15.2–the -Wexternal-argument-mismatch warning causes compilation to fail on correct code. To work around this bug, suggest in the build system such as CMake to detect GCC 15.1 and disable the -Wexternal-argument-mismatch warning. It’s also relevant to consider if -Wall is a flag one wants to automatically set for the default end-user build, as such a bug could happen for future versions of GCC as well.

In CMake, detect GCC 15.1 and disable the problem flag:

if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND
   CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "15.0" AND
   CMAKE_Fortran_COMPILER_VERSION VERSION_LESS "15.2"
   )
  add_compile_options($<$<COMPILE_LANGUAGE:Fortran>:-Wno-external-argument-mismatch>)
endif()