NetCDF-Parallel on an Apple Silicon Mac

From Planets
Revision as of 18:45, 15 October 2026 by Mwolff (talk | contribs) (Created page with "= Building a parallel NetCDF-4 stack on macOS (Apple Silicon) = This page builds '''Parallel HDF5 → NetCDF-C → NetCDF-Fortran → NetCDF-C++4''' from source with Open MPI...")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Building a parallel NetCDF-4 stack on macOS (Apple Silicon)

This page builds Parallel HDF5 → NetCDF-C → NetCDF-Fortran → NetCDF-C++4 from source with Open MPI and installs everything into /opt/parallel-netcdf. Every step is a block of commands to copy and paste into a terminal, followed by what you should see.

It reproduces the build scripts 0_env_setup.sh … 4_build_netcdf_cxx.sh in the NetCDFlib directory. If the two ever disagree, the scripts are the reference.

Package Version Source
HDF5 2.2.0 GitHub release (HDFGroup/hdf5)
NetCDF-C 4.10.1 downloads.unidata.ucar.edu
NetCDF-Fortran 4.6.4 downloads.unidata.ucar.edu
NetCDF-C++4 4.3.1 downloads.unidata.ucar.edu

Last verified: 2026-10-15, macOS 26 (Darwin 25.6), Apple Silicon, 18 cores, Homebrew Open MPI 5.0.11. Total time is about 10–12 minutes, plus under a minute of downloads.

Before you start

  • Use one terminal window for the whole page. Step 0 sets variables that every later step needs. If you close the terminal or open a new one, paste the Step 0 block again before continuing.
  • The commands are written for bash. macOS terminals start zsh by default, so type bash first and press Enter.
  • Paste one block at a time and compare the result with the Expected box before moving on. If something differs, stop and see Troubleshooting.
  • Don't paste the contents of the Expected boxes; they are output, not commands.

Prerequisites

Install the compilers, Open MPI and build tools with Homebrew:

brew install gcc openmpi zlib ccache wget cmake

Expected: Homebrew finishes without errors. Check that the MPI compiler wrappers are found:

which mpicc mpicxx mpif90

Expected: three paths under /opt/homebrew/bin/.

Step 0 – Set up the environment

This defines the versions, directories and compilers used by every later step. It creates /opt/parallel-netcdf the first time, which asks for your password (sudo).

ROOT is the base directory for the downloaded source archives ($ROOT/netcdf_archives) and the temporary build tree ($ROOT/netcdf_parallel_scratch). It is set to your home directory; to use another location (for example a larger disk), change the first line before pasting. The installation itself always goes to /opt/parallel-netcdf.

export ROOT=$HOME

export HDF5_VER="2.2.0"
export NETCDF_C_VER="4.10.1"
export NETCDF_F_VER="4.6.4"
export NETCDF_CXX_VER="4.3.1"

export ARCHIVE_DIR="$ROOT/netcdf_archives"
export PREFIX="/opt/parallel-netcdf"
export TMP_BUILD_DIR="$ROOT/netcdf_parallel_scratch"
export HOMEBREW_PREFIX="/opt/homebrew"
export NUM_CORES=$(sysctl -n hw.ncpu)

export CC=mpicc
export CXX=mpicxx
export FC=mpif90
export F77=mpif77

export OMPI_MCA_io=romio341

export CPPFLAGS="-I$PREFIX/include -I$HOMEBREW_PREFIX/opt/zlib/include"
export LDFLAGS="-L$PREFIX/lib -L$HOMEBREW_PREFIX/opt/zlib/lib"
export PATH="$PREFIX/bin:$PATH"

if [ ! -d "$PREFIX" ]; then
    sudo mkdir -p "$PREFIX"
    sudo chown -R $(whoami):admin "$PREFIX"
fi
mkdir -p "$ARCHIVE_DIR" "$TMP_BUILD_DIR"

echo "ROOT=$ROOT  PREFIX=$PREFIX  cores=$NUM_CORES  HDF5=$HDF5_VER  C=$NETCDF_C_VER  F=$NETCDF_F_VER  CXX=$NETCDF_CXX_VER"

Expected: one line such as ROOT=/Users/yourname PREFIX=/opt/parallel-netcdf cores=18 HDF5=2.2.0 C=4.10.1 F=4.6.4 CXX=4.3.1

OMPI_MCA_io=romio341 is not optional: without it, Open MPI's default I/O layer can hang during large parallel writes on macOS (see Troubleshooting).

To check later that the environment is still set (for example after a break), run:

echo "PREFIX=$PREFIX  cores=$NUM_CORES  HDF5=$HDF5_VER"

If PREFIX= is empty, paste the Step 0 block again.

Step 1 – Download the sources

These must be the official release tarballs. The "Source code" archives that GitHub generates automatically lack the configure script and will not build. The NetCDF libraries are downloaded from Unidata because their GitHub releases have no downloadable files.

cd "$ARCHIVE_DIR"
curl -fL --retry 3 -o "hdf5-${HDF5_VER}.tar.gz" "https://github.com/HDFGroup/hdf5/releases/download/${HDF5_VER}/hdf5-${HDF5_VER}.tar.gz"
curl -fL --retry 3 -o "netcdf-c-${NETCDF_C_VER}.tar.gz" "https://downloads.unidata.ucar.edu/netcdf-c/${NETCDF_C_VER}/netcdf-c-${NETCDF_C_VER}.tar.gz"
curl -fL --retry 3 -o "netcdf-fortran-${NETCDF_F_VER}.tar.gz" "https://downloads.unidata.ucar.edu/netcdf-fortran/${NETCDF_F_VER}/netcdf-fortran-${NETCDF_F_VER}.tar.gz"
curl -fL --retry 3 -o "netcdf-cxx4-${NETCDF_CXX_VER}.tar.gz" "https://downloads.unidata.ucar.edu/netcdf-cxx/${NETCDF_CXX_VER}/netcdf-cxx4-${NETCDF_CXX_VER}.tar.gz"

Check that all four files are intact:

cd "$ARCHIVE_DIR"
gzip -t "hdf5-${HDF5_VER}.tar.gz" && echo "hdf5 OK"
gzip -t "netcdf-c-${NETCDF_C_VER}.tar.gz" && echo "netcdf-c OK"
gzip -t "netcdf-fortran-${NETCDF_F_VER}.tar.gz" && echo "netcdf-fortran OK"
gzip -t "netcdf-cxx4-${NETCDF_CXX_VER}.tar.gz" && echo "netcdf-cxx4 OK"

Expected: four OK lines. A curl: (22) ... 404 error means that version is not available at that URL; check the version numbers in Step 0.

Step 2 – Parallel HDF5 (about 3 minutes)

HDF5 2.x is built with CMake.

Extract

rm -rf "$TMP_BUILD_DIR/hdf5-${HDF5_VER}"
tar -xzf "$ARCHIVE_DIR/hdf5-${HDF5_VER}.tar.gz" -C "$TMP_BUILD_DIR"
mkdir -p "$TMP_BUILD_DIR/hdf5-${HDF5_VER}/build"
cd "$TMP_BUILD_DIR/hdf5-${HDF5_VER}/build"

Configure

cmake .. \
  -DCMAKE_INSTALL_PREFIX="$PREFIX" \
  -DCMAKE_INSTALL_NAME_DIR="$PREFIX/lib" \
  -DHDF5_ENABLE_PARALLEL=ON \
  -DHDF5_BUILD_FORTRAN=ON \
  -DHDF5_ENABLE_ZLIB_SUPPORT=ON \
  -DZLIB_INCLUDE_DIR="$HOMEBREW_PREFIX/opt/zlib/include" \
  -DZLIB_LIBRARY="$HOMEBREW_PREFIX/opt/zlib/lib/libz.dylib" \
  -DBUILD_SHARED_LIBS=ON \
  -DBUILD_TESTING=ON \
  -DHDF5_TEST_TOOLS=OFF \
  -DHDF5_TEST_SWMR=OFF \
  -DHDF5_TEST_EXAMPLES=OFF \
  -DMPIEXEC_MAX_NUMPROCS=4

Expected: ends with -- Build files have been written to: …/build

Build

cd "$TMP_BUILD_DIR/hdf5-${HDF5_VER}/build"
make -j"$NUM_CORES"

Expected: ends at [100%] with no Error lines.

Test (about 1.5 minutes)

Paste this block on its own and wait for it to finish. The MPI tests read from the terminal, so commands pasted ahead of time would be swallowed and never run.

cd "$TMP_BUILD_DIR/hdf5-${HDF5_VER}/build"
ctest -j"$NUM_CORES" --output-on-failure 2>&1 | tee ctest.log | tail -5

Expected: 100% tests passed out of 826

Install

cd "$TMP_BUILD_DIR/hdf5-${HDF5_VER}/build"
make install
otool -D "$PREFIX/lib/libhdf5_hl.dylib"

Expected: the last line is /opt/parallel-netcdf/lib/libhdf5_hl.320.dylib. If it starts with @rpath/ instead, the CMAKE_INSTALL_NAME_DIR setting was lost; redo this step.

Step 3 – NetCDF-C (about 5–6 minutes)

Extract and configure

rm -rf "$TMP_BUILD_DIR/netcdf-c-${NETCDF_C_VER}"
tar -xzf "$ARCHIVE_DIR/netcdf-c-${NETCDF_C_VER}.tar.gz" -C "$TMP_BUILD_DIR"
cd "$TMP_BUILD_DIR/netcdf-c-${NETCDF_C_VER}"
export LIBS="-lhdf5_hl -lhdf5 -lz"
./configure --prefix="$PREFIX" --host=aarch64-apple-darwin --enable-netcdf-4 --enable-parallel4 --disable-dap --disable-parallel-tests

Expected: ends with a "NetCDF C Configuration Summary" that includes HDF5 Support: yes and NC-4 Parallel Support: yes.

Build

cd "$TMP_BUILD_DIR/netcdf-c-${NETCDF_C_VER}"
make -j"$NUM_CORES"

Test (about 5 minutes)

cd "$TMP_BUILD_DIR/netcdf-c-${NETCDF_C_VER}"
make check > check.log 2>&1
echo "PASS: $(grep -c '^PASS:' check.log)   FAIL: $(grep -c -E '^(FAIL|ERROR):' check.log)"

Expected: PASS: 240 FAIL: 0. A much smaller PASS count means testing stopped early at a failure; run grep -E '^(FAIL|ERROR):' check.log to see which one.

Install and verify

cd "$TMP_BUILD_DIR/netcdf-c-${NETCDF_C_VER}"
make install
unset LIBS
nc-config --has-parallel4

Expected: yes. If it prints no, NetCDF-C was built without parallel support: stop here.

Step 4 – NetCDF-Fortran (about 2 minutes)

Extract and configure

rm -rf "$TMP_BUILD_DIR/netcdf-fortran-${NETCDF_F_VER}"
tar -xzf "$ARCHIVE_DIR/netcdf-fortran-${NETCDF_F_VER}.tar.gz" -C "$TMP_BUILD_DIR"
cd "$TMP_BUILD_DIR/netcdf-fortran-${NETCDF_F_VER}"
export LIBS="-lnetcdf -lhdf5_hl -lhdf5 -lz"
./configure --prefix="$PREFIX" --host=aarch64-apple-darwin --disable-parallel-tests

Build

cd "$TMP_BUILD_DIR/netcdf-fortran-${NETCDF_F_VER}"
make -j"$NUM_CORES"

Expected: many Warning: Type mismatch … / Rank mismatch … messages from gfortran. These are normal for NetCDF-Fortran; only Error lines matter.

Test

cd "$TMP_BUILD_DIR/netcdf-fortran-${NETCDF_F_VER}"
make check > check.log 2>&1
echo "PASS: $(grep -c '^PASS:' check.log)   FAIL: $(grep -c -E '^(FAIL|ERROR):' check.log)"

Expected: PASS: 59 FAIL: 0

Install and verify

cd "$TMP_BUILD_DIR/netcdf-fortran-${NETCDF_F_VER}"
make install
unset LIBS
nf-config --has-nc4

Expected: yes

Step 5 – NetCDF-C++4 (under 1 minute)

NetCDF-C++ needs two extra commands that remove files named VERSION; the reason is explained in Troubleshooting. Don't skip them.

Extract and configure

rm -rf "$TMP_BUILD_DIR/netcdf-cxx4-${NETCDF_CXX_VER}"
tar -xzf "$ARCHIVE_DIR/netcdf-cxx4-${NETCDF_CXX_VER}.tar.gz" -C "$TMP_BUILD_DIR"
cd "$TMP_BUILD_DIR/netcdf-cxx4-${NETCDF_CXX_VER}"
find . -type f \( -name "version" -o -name "VERSION" \) -print -delete
export LIBS="-lnetcdf -lhdf5_hl -lhdf5 -lz"
CXXFLAGS="$CPPFLAGS" ./configure --prefix="$PREFIX" --host=aarch64-apple-darwin --disable-filter-testing
rm -f VERSION

Build

cd "$TMP_BUILD_DIR/netcdf-cxx4-${NETCDF_CXX_VER}"
make -j"$NUM_CORES"

Expected: no error: lines. If you see ./VERSION:1:1: error: expected unqualified-id, the rm -f VERSION line was skipped: redo this step from "Extract and configure".

Test

cd "$TMP_BUILD_DIR/netcdf-cxx4-${NETCDF_CXX_VER}"
make check > check.log 2>&1
echo "PASS: $(grep -c '^PASS:' check.log)   FAIL: $(grep -c -E '^(FAIL|ERROR):' check.log)"

Expected: PASS: 16 FAIL: 0

Install

cd "$TMP_BUILD_DIR/netcdf-cxx4-${NETCDF_CXX_VER}"
make install
unset LIBS
ls "$PREFIX/lib/libnetcdf_c++4.dylib"

Expected: /opt/parallel-netcdf/lib/libnetcdf_c++4.dylib

Step 6 – Final check

This confirms that the installed tools run without any special library path settings, and that no installed file depends on @rpath.

env -u DYLD_LIBRARY_PATH "$PREFIX/bin/h5dump" --version
env -u DYLD_LIBRARY_PATH "$PREFIX/bin/nc-config" --version
env -u DYLD_LIBRARY_PATH "$PREFIX/bin/nf-config" --has-nc4
env -u DYLD_LIBRARY_PATH "$PREFIX/bin/ncdump" 2>&1 | head -1
otool -L "$PREFIX"/lib/*.dylib "$PREFIX"/bin/* 2>/dev/null | grep -c '@rpath'

Expected: h5dump: Version 2.2.0, netCDF 4.10.1, yes, a line starting with /opt/parallel-netcdf/bin/ncdump [-c|-h], and finally 0.

The scratch build directory is no longer needed. To free the disk space:

rm -rf "$TMP_BUILD_DIR"

Shell profile

Add these lines to your shell profile (~/.bash_profile for bash, ~/.zshrc for zsh) so programs can find the tools:

export PATH="/opt/parallel-netcdf/bin:$PATH"
export OMPI_MCA_io=romio341
  • PATH is required.
  • OMPI_MCA_io is possibly required, but untested with model runs: it avoids the Open MPI hang described below. It was needed for the HDF5 parallel tests to pass.
  • DYLD_LIBRARY_PATH is not needed: the libraries record their full install paths.

Troubleshooting

curl: (22) The requested URL returned error: 404
That version isn't hosted at the URL. Unidata only keeps recent versions (for example, NetCDF-C++ 4.2.1 is gone). Check the latest versions with check_versions.sh in the NetCDFlib directory, or on the GitHub release pages.
configure: error: cannot run C compiled programs
The --host=aarch64-apple-darwin option is missing from the ./configure line. It makes configure skip a test-run of a compiled program that fails on macOS with the MPI compiler wrappers; the libraries are still built natively.
dyld: Library not loaded: @rpath/libhdf5_hl.320.dylib
HDF5 was installed without -DCMAKE_INSTALL_NAME_DIR="$PREFIX/lib". Redo Step 2, then Steps 3–5 so they link against the corrected libraries. Setting DYLD_LIBRARY_PATH is not a reliable fix: macOS removes it whenever a program is started via /bin/sh or /bin/bash (as make, job scripts and many mpirun setups do).
A parallel test or model run hangs after mca_fbtl_posix_ipwritev: error in aio_write(): Resource temporarily unavailable
OMPI_MCA_io=romio341 is not set. Open MPI's default I/O layer (OMPIO) issues more asynchronous writes than macOS allows per process, then waits forever. ROMIO avoids this. Set the variable (Step 0 and your shell profile), or run mpirun --mca io romio341 ….
NetCDF-C++
./VERSION:1:1: error: expected unqualified-id (or expected external declaration)
The standard C++ headers include a header called <version>. On macOS the file system ignores case and the build searches the source directory for headers, so the compiler picks up the package's own VERSION file instead. NetCDF-C++'s configure creates that file, which is why Step 5 deletes it after configure. Never create version/VERSION files in that tree.
NetCDF-C++
Undefined symbols … _nc_set_log_level
--disable-filter-testing is missing from the configure line. Without it, an example program is built that needs a NetCDF-C function which only exists when NetCDF-C was built with logging enabled.
make check shows far fewer passes than expected
make check stops at the first directory with a failure, so the remaining tests never run. Look at the first FAIL: line in check.log and the matching .log file next to that test.