NetCDF-Parallel on an Apple Silicon Mac
Contents
- 1 Building a parallel NetCDF-4 stack on macOS (Apple Silicon)
- 1.1 Before you start
- 1.2 Prerequisites
- 1.3 Step 0 – Set up the environment
- 1.4 Step 1 – Download the sources
- 1.5 Step 2 – Parallel HDF5 (about 3 minutes)
- 1.6 Step 3 – NetCDF-C (about 5–6 minutes)
- 1.7 Step 4 – NetCDF-Fortran (about 2 minutes)
- 1.8 Step 5 – NetCDF-C++4 (under 1 minute)
- 1.9 Step 6 – Final check
- 1.10 Shell profile
- 1.11 Troubleshooting
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
zshby default, so typebashfirst 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
-
PATHis required. -
OMPI_MCA_iois 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_PATHis 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.shin theNetCDFlibdirectory, or on the GitHub release pages.
-
configure: error: cannot run C compiled programs - The
--host=aarch64-apple-darwinoption is missing from the./configureline. 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. SettingDYLD_LIBRARY_PATHis not a reliable fix: macOS removes it whenever a program is started via/bin/shor/bin/bash(asmake, job scripts and manympirunsetups do).
- A parallel test or model run hangs after
mca_fbtl_posix_ipwritev: error in aio_write(): Resource temporarily unavailable -
OMPI_MCA_io=romio341is 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 runmpirun --mca io romio341 ….
- NetCDF-C++
-
./VERSION:1:1: error: expected unqualified-id(orexpected 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 ownVERSIONfile instead. NetCDF-C++'sconfigurecreates that file, which is why Step 5 deletes it after configure. Never createversion/VERSIONfiles in that tree.
- NetCDF-C++
-
Undefined symbols … _nc_set_log_level -
--disable-filter-testingis 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 checkshows far fewer passes than expected -
make checkstops at the first directory with a failure, so the remaining tests never run. Look at the firstFAIL:line incheck.logand the matching.logfile next to that test.