SIESTA Compilation: GNU Compilers with Fujitsu MPI

This page describes how to compile SIESTA on Fugaku using GCC/GFortran compilers combined with Fujitsu’s MPI implementation. Before proceeding, make sure you have configured your Spack environment as described in Spack 1.0.1 Configuration Guide for Fugaku.

Note

The scripts on this page rely on the environment variables MY_USER, MY_PROJECT, and MY_VOLUME to construct all file paths. These variables are defined during the Spack account setup described in Spack 1.0.1 Configuration Guide for Fugaku. Make sure they are exported in your session before running any script:

export MY_USER=your_username
export MY_PROJECT=your_project
export MY_VOLUME=your_volume

If you added these variables to your ~/.bashrc as described in Step 12 of the Spack setup guide, they will be available automatically in every new session. Alternatively, the variables can be defined directly inside each script at the top of the file.

Prerequisites

Interactive Session

All compilation steps must be run inside a compute node session on Fugaku. Do not run the scripts on a login node.

Request an interactive session before proceeding, then work entirely within that session.

Spack Environment

Once inside the interactive session, load the local Spack environment:

. /${MY_VOLUME}/mdt1/data/${MY_PROJECT}/${MY_USER}/tmp/spack/share/spack/setup-env.sh

This is the same source call that appears at the top of the compilation script. If you added the Spack setup to your ~/.bashrc as described in the account setup guide, this step is handled automatically.

py-ruamel-yaml

The SIESTA test suite requires the ruamel.yaml Python package. Install it via Spack inside the interactive session. First load the Python module that will own the package:

spack load /3mtxbkd          # python@3.13.5 %gcc@12.2.0

Then install py-ruamel-yaml pinned to that Python:

spack install py-ruamel-yaml ^/3mtxbkd

After the installation finishes, verify the package is importable:

python3.13 -c "import ruamel.yaml; print('Ok')"

If the command prints Ok, the installation is correct and the test suite will be able to locate the package. If it raises an ImportError, check that the correct Python module is active in your session.

Note

The hash /3mtxbkd refers to python@3.13.5 built with gcc@12.2.0. The same hash is used in get_siesta.sh via spack load /3mtxbkd so that the build environment is consistent with the one used to install py-ruamel-yaml.

NetCDF Installation

Before compiling SIESTA, you need to install NetCDF using the install_netcdf.sh script. NetCDF is not available as a pre-installed module on Fugaku and must be compiled from source.

Note

The install_netcdf.sh script builds NetCDF from source using the following Spack-installed modules:

  • gcc@12.2.0 — C, C++ and Fortran compilers

  • fjmpi-gcc12 — Fujitsu MPI with GCC bindings

Run the script before proceeding with the SIESTA compilation:

./install_netcdf.sh

You can download install_netcdf.sh directly.

Show full script: install_netcdf.sh
Listing 1 install_netcdf.sh
#!/bin/bash
#
# install_netcdf.sh – build NetCDF from source using GCC + Fujitsu MPI
#
# Requires the following environment variables (set in ~/.bashrc or exported
# before calling this script):
#   MY_USER     – your Fugaku username
#   MY_PROJECT  – your project code
#   MY_VOLUME   – your storage volume
#
# The script builds zlib, HDF5, NetCDF-C, and NetCDF-Fortran in sequence,
# installs everything under ${WORK_DIR}/install, and writes a small
# environment file (netcdf_env.sh) that get_siesta.sh sources later.
#

set -e

###############################################################################
# Check required environment variables
###############################################################################
for var in MY_USER MY_PROJECT MY_VOLUME; do
  if [[ -z "${!var:-}" ]]; then
    echo "Error: environment variable ${var} is not set." >&2
    echo "Export it or add it to your ~/.bashrc before running this script." >&2
    exit 1
  fi
done

echo "=========================================="
echo "NetCDF installation – GCC + Fujitsu MPI"
echo "=========================================="

###############################################################################
# Paths
###############################################################################
BASE_DIR="/${MY_VOLUME}/mdt1/data/${MY_PROJECT}/${MY_USER}"
WORK_DIR="${BASE_DIR}/netcdf_gcc"
SRC_DIR="${WORK_DIR}/src"
INSTALL_DIR="${WORK_DIR}/install"

MPI_GCC_DIR="/vol0004/apps/oss/mpigcc/fjmpi-gcc12"
FUJITSU_MPI_DIR="/opt/FJSVxtclanga/tcsds-1.2.42"

###############################################################################
# Library versions
###############################################################################
ZLIB_VERSION="1.3.1"
HDF5_VERSION="1.14.3"
NETCDF_C_VERSION="4.9.2"
NETCDF_FORTRAN_VERSION="4.6.1"

###############################################################################
# Download URLs
###############################################################################
ZLIB_URL="https://zlib.net/fossils/zlib-${ZLIB_VERSION}.tar.gz"
HDF5_URL="https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.14/hdf5-${HDF5_VERSION}/src/hdf5-${HDF5_VERSION}.tar.gz"
NETCDF_C_URL="https://downloads.unidata.ucar.edu/netcdf-c/${NETCDF_C_VERSION}/netcdf-c-${NETCDF_C_VERSION}.tar.gz"
NETCDF_FORTRAN_URL="https://downloads.unidata.ucar.edu/netcdf-fortran/${NETCDF_FORTRAN_VERSION}/netcdf-fortran-${NETCDF_FORTRAN_VERSION}.tar.gz"

###############################################################################
# Environment
###############################################################################
export PATH="${MPI_GCC_DIR}/bin:${INSTALL_DIR}/bin:${PATH}"
export LD_LIBRARY_PATH="${INSTALL_DIR}/lib:${FUJITSU_MPI_DIR}/lib64:${LD_LIBRARY_PATH:-}"

mkdir -p "${SRC_DIR}" "${INSTALL_DIR}"

###############################################################################
# Helper: resolve Fortran MPI wrapper (mpif90 or mpifort)
###############################################################################
get_fc_wrapper() {
    if [ -f "${MPI_GCC_DIR}/bin/mpif90" ]; then
        echo "${MPI_GCC_DIR}/bin/mpif90"
    elif [ -f "${MPI_GCC_DIR}/bin/mpifort" ]; then
        echo "${MPI_GCC_DIR}/bin/mpifort"
    else
        echo "Error: no Fortran MPI wrapper found in ${MPI_GCC_DIR}/bin." >&2
        exit 1
    fi
}

###############################################################################
# Helper: download and extract a tarball
###############################################################################
download_and_extract() {
    local url=$1
    local filename
    filename=$(basename "${url}")
    cd "${SRC_DIR}"
    if [ ! -f "${filename}" ]; then
        echo "Downloading ${filename} ..."
        wget "${url}" || curl -L -O "${url}"
    else
        echo "${filename} already downloaded."
    fi
    echo "Extracting ${filename} ..."
    tar -xzf "${filename}"
}

###############################################################################
# Verify MPI wrappers before starting
###############################################################################
verify_mpi_wrappers() {
    echo ""
    echo "=========================================="
    echo "Verifying MPI wrappers"
    echo "=========================================="

    if [ ! -d "${MPI_GCC_DIR}" ]; then
        echo "Error: MPI directory not found: ${MPI_GCC_DIR}" >&2
        exit 1
    fi

    if [ -f "${MPI_GCC_DIR}/bin/mpicc" ]; then
        echo "  ✓ mpicc found"
    else
        echo "  ✗ mpicc NOT found in ${MPI_GCC_DIR}/bin" >&2
        exit 1
    fi

    local fc_wrapper
    fc_wrapper=$(get_fc_wrapper)
    echo "  ✓ Fortran wrapper found: $(basename "${fc_wrapper}")"

    # Compile a minimal MPI test to confirm the wrappers work
    local test_dir="${WORK_DIR}/mpi_test"
    mkdir -p "${test_dir}"
    cd "${test_dir}"

    cat > test_mpi.f90 << 'TESTMPI'
program test_mpi
    include 'mpif.h'
    integer :: ierr, rank
    call MPI_Init(ierr)
    call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)
    print *, 'MPI OK'
    call MPI_Finalize(ierr)
end program test_mpi
TESTMPI

    echo "Compiling MPI test ..."
    "${fc_wrapper}" -o test_mpi test_mpi.f90

    if [ $? -eq 0 ]; then
        echo "  ✓ MPI test compiled successfully (not executed – requires batch system)"
    else
        echo "  ✗ MPI test compilation failed" >&2
        exit 1
    fi
}

###############################################################################
# zlib
###############################################################################
install_zlib() {
    echo ""
    echo "=========================================="
    echo "Installing zlib ${ZLIB_VERSION}"
    echo "=========================================="

    download_and_extract "${ZLIB_URL}"
    cd "${SRC_DIR}/zlib-${ZLIB_VERSION}"

    ./configure --prefix="${INSTALL_DIR}"
    make -j 4
    make install
    echo "  ✓ zlib installed"
}

###############################################################################
# HDF5
###############################################################################
install_hdf5() {
    echo ""
    echo "=========================================="
    echo "Installing HDF5 ${HDF5_VERSION}"
    echo "=========================================="

    download_and_extract "${HDF5_URL}"
    cd "${SRC_DIR}/hdf5-${HDF5_VERSION}"

    local fc_wrapper
    fc_wrapper=$(get_fc_wrapper)

    echo "Configuring HDF5 ..."
    echo "  CC: ${MPI_GCC_DIR}/bin/mpicc"
    echo "  FC: ${fc_wrapper}"

    ./configure --prefix="${INSTALL_DIR}" \
                --enable-fortran \
                --enable-parallel \
                --enable-shared \
                --enable-static \
                --with-zlib="${INSTALL_DIR}" \
                CC="${MPI_GCC_DIR}/bin/mpicc" \
                FC="${fc_wrapper}" \
                CFLAGS="-O2 -fPIC" \
                FCFLAGS="-O2 -fPIC" \
                LDFLAGS="-L${INSTALL_DIR}/lib -Wl,-rpath,${INSTALL_DIR}/lib"

    if [ $? -ne 0 ]; then
        echo "  ✗ HDF5 configure failed. Last 100 lines of config.log:"
        tail -100 config.log
        exit 1
    fi

    echo "Building HDF5 ..."
    make -j 4

    echo "Installing HDF5 ..."
    make install
    echo "  ✓ HDF5 installed"
}

###############################################################################
# NetCDF-C
###############################################################################
install_netcdf_c() {
    echo ""
    echo "=========================================="
    echo "Installing NetCDF-C ${NETCDF_C_VERSION}"
    echo "=========================================="

    download_and_extract "${NETCDF_C_URL}"
    cd "${SRC_DIR}/netcdf-c-${NETCDF_C_VERSION}"

    echo "Configuring NetCDF-C ..."

    ./configure --prefix="${INSTALL_DIR}" \
                --enable-netcdf-4 \
                --enable-shared \
                --enable-static \
                --disable-dap \
                CC="${MPI_GCC_DIR}/bin/mpicc" \
                CFLAGS="-O2 -fPIC" \
                CPPFLAGS="-I${INSTALL_DIR}/include" \
                LDFLAGS="-L${INSTALL_DIR}/lib -Wl,-rpath,${INSTALL_DIR}/lib"

    if [ $? -ne 0 ]; then
        echo "  ✗ NetCDF-C configure failed. Last 100 lines of config.log:"
        tail -100 config.log
        exit 1
    fi

    echo "Building NetCDF-C ..."
    make -j 4

    echo "Installing NetCDF-C ..."
    make install
    echo "  ✓ NetCDF-C installed"
}

###############################################################################
# NetCDF-Fortran
###############################################################################
install_netcdf_fortran() {
    echo ""
    echo "=========================================="
    echo "Installing NetCDF-Fortran ${NETCDF_FORTRAN_VERSION}"
    echo "=========================================="

    download_and_extract "${NETCDF_FORTRAN_URL}"
    cd "${SRC_DIR}/netcdf-fortran-${NETCDF_FORTRAN_VERSION}"

    local fc_wrapper
    fc_wrapper=$(get_fc_wrapper)

    echo "Configuring NetCDF-Fortran ..."

    ./configure --prefix="${INSTALL_DIR}" \
                --enable-shared \
                --enable-static \
                CC="${MPI_GCC_DIR}/bin/mpicc" \
                FC="${fc_wrapper}" \
                F77="${fc_wrapper}" \
                CFLAGS="-O2 -fPIC" \
                FCFLAGS="-O2 -fPIC" \
                FFLAGS="-O2 -fPIC" \
                CPPFLAGS="-I${INSTALL_DIR}/include" \
                LDFLAGS="-L${INSTALL_DIR}/lib -Wl,-rpath,${INSTALL_DIR}/lib"

    if [ $? -ne 0 ]; then
        echo "  ✗ NetCDF-Fortran configure failed. Last 100 lines of config.log:"
        tail -100 config.log
        exit 1
    fi

    echo "Building NetCDF-Fortran ..."
    make -j 4

    echo "Installing NetCDF-Fortran ..."
    make install
    echo "  ✓ NetCDF-Fortran installed"
}

###############################################################################
# Write environment file sourced by get_siesta.sh
#
# INSTALL_DIR is expanded now (at write time) so the generated file contains
# the resolved absolute path and works correctly when sourced in a fresh
# session where MY_* variables may not be set.
###############################################################################
create_env_file() {
    echo ""
    echo "Writing environment file ..."
    ENV_FILE="${WORK_DIR}/netcdf_env.sh"

    cat > "${ENV_FILE}" << ENVEOF
#!/bin/bash
# netcdf_env.sh – sourced by get_siesta.sh to expose NetCDF paths.
# Generated by install_netcdf.sh on $(date).

export NETCDF_DIR="${INSTALL_DIR}"
export MPI_GCC_DIR="${MPI_GCC_DIR}"
export FUJITSU_MPI_DIR="${FUJITSU_MPI_DIR}"

export PATH="\${MPI_GCC_DIR}/bin:\${NETCDF_DIR}/bin:\${PATH}"
export LD_LIBRARY_PATH="\${NETCDF_DIR}/lib:\${FUJITSU_MPI_DIR}/lib64:\${LD_LIBRARY_PATH:-}"

export NETCDF_INC="-I\${NETCDF_DIR}/include"
export NETCDF_LIB="-L\${NETCDF_DIR}/lib -lnetcdff -lnetcdf -lhdf5_hl -lhdf5 -lz"

echo "NetCDF environment configured."
echo "  NETCDF_DIR  : \${NETCDF_DIR}"
echo "  MPI wrappers: \${MPI_GCC_DIR}/bin"
ENVEOF

    chmod +x "${ENV_FILE}"
    echo "  ✓ Environment file written: ${ENV_FILE}"
}

###############################################################################
# Verify critical libraries
###############################################################################
verify_installation() {
    echo ""
    echo "=========================================="
    echo "Verifying installation"
    echo "=========================================="

    source "${WORK_DIR}/netcdf_env.sh"

    if [ -f "${INSTALL_DIR}/bin/nc-config" ]; then
        echo "nc-config --version: $(nc-config --version)"
        echo "nc-config --libs:    $(nc-config --libs)"
    fi

    if [ -f "${INSTALL_DIR}/bin/nf-config" ]; then
        echo "nf-config --version: $(nf-config --version)"
        echo "nf-config --flibs:   $(nf-config --flibs)"
    fi

    echo ""
    echo "Installed libraries:"
    ls -lh "${INSTALL_DIR}/lib/"*.{a,so} 2>/dev/null | grep -E "libz|libhdf5|libnetcdf" || true

    echo ""
    CRITICAL_FILES=(
        "${INSTALL_DIR}/lib/libz.a"
        "${INSTALL_DIR}/lib/libhdf5.a"
        "${INSTALL_DIR}/lib/libhdf5_hl.a"
        "${INSTALL_DIR}/lib/libhdf5_fortran.a"
        "${INSTALL_DIR}/lib/libnetcdf.a"
        "${INSTALL_DIR}/lib/libnetcdff.a"
    )

    local all_ok=true
    for f in "${CRITICAL_FILES[@]}"; do
        if [ -f "$f" ]; then
            echo "  ✓ $(basename "$f")"
        else
            echo "  ✗ Missing: $(basename "$f")"
            all_ok=false
        fi
    done

    if [ "${all_ok}" = true ]; then
        echo ""
        echo "  ✓ All critical libraries present."
    fi
}

###############################################################################
# Compile a NetCDF+MPI test program
###############################################################################
create_test_program() {
    echo ""
    echo "=========================================="
    echo "Compiling NetCDF test program"
    echo "=========================================="

    local test_dir="${WORK_DIR}/test"
    mkdir -p "${test_dir}"
    cd "${test_dir}"

    cat > test_netcdf.f90 << 'EOF'
program test_netcdf
  use netcdf
  implicit none
  include 'mpif.h'
  integer :: ncid, status, ierr, rank
  character(len=256) :: filename

  call MPI_Init(ierr)
  call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)

  if (rank == 0) then
    print *, '=========================================='
    print *, 'NetCDF-Fortran + MPI test'
    print *, 'NetCDF version: ', trim(nf90_inq_libvers())
    print *, '=========================================='

    filename = 'test.nc'
    status = nf90_create(filename, NF90_CLOBBER, ncid)

    if (status == NF90_NOERR) then
      status = nf90_close(ncid)
      print *, 'TEST SUCCESSFUL'
    else
      print *, 'TEST FAILED'
    end if
  end if

  call MPI_Finalize(ierr)
end program test_netcdf
EOF

    local fc_wrapper
    fc_wrapper=$(get_fc_wrapper)

    echo "Compiling test ..."
    "${fc_wrapper}" -o test_netcdf test_netcdf.f90 \
        ${NETCDF_INC} ${NETCDF_LIB}

    if [ $? -eq 0 ]; then
        echo "  ✓ Test compiled successfully."
        echo ""
        echo "  To run:  mpiexec -n 1 ./test_netcdf"
        echo "  Or submit via your batch system."
    else
        echo "  ✗ Test compilation failed."
    fi
}

###############################################################################
# Main
###############################################################################
main() {
    echo "Start: $(date)"
    echo ""

    verify_mpi_wrappers
    install_zlib
    install_hdf5
    install_netcdf_c
    install_netcdf_fortran
    create_env_file
    verify_installation
    create_test_program

    echo ""
    echo "=========================================="
    echo "✓ Installation complete"
    echo "=========================================="
    echo ""
    echo "Elapsed: ${SECONDS} seconds"
    echo ""
    echo "To use:  source ${WORK_DIR}/netcdf_env.sh"
    echo ""
    echo "Example compilation:"
    echo "  mpif90 program.f90 \${NETCDF_INC} \${NETCDF_LIB} -o program"
}

LOG_FILE="${WORK_DIR}/install_log_$(date +%Y%m%d_%H%M%S).txt"
mkdir -p "${WORK_DIR}"
main 2>&1 | tee "${LOG_FILE}"

Compilation Script

The get_siesta.sh script automates the full process of downloading, extracting, configuring, and compiling SIESTA on Fugaku.

Basic usage:

./get_siesta.sh                              # default versions (SIESTA 5.4.2, TCSDS 1.2.42)
./get_siesta.sh 5.4.1                        # different SIESTA version
./get_siesta.sh --tcsds-version 1.2.42       # different TCSDS version
./get_siesta.sh 5.4.1 --tcsds-version 1.2.42 # specify both versions
./get_siesta.sh -c                           # clean extracted directory before starting
./get_siesta.sh -r                           # re-download tarball if missing and re-extract

What the script does:

  1. Loads the Spack and NetCDF environments by sourcing the Spack setup script and the NetCDF environment file produced by install_netcdf.sh.

  2. Downloads the SIESTA source tarball from the GitLab release page if not already present. It falls back from curl to wget if needed.

  3. Extracts the tarball and prepares two build directories inside the source tree: _build_gnu_fjmpi for intermediate build artifacts and _siesta_bin_gnu_fjmpi for the final installed binaries.

  4. Loads the software environment by purging all modules and reloading the TCSDS toolchain (lang/tcsds-<version>), then loads the required Spack packages: GCC 12.2.0, Fujitsu MPI, OpenBLAS, netlib-ScaLAPACK, Python, CMake, Ninja, pip, Flex, FFTW, Bison, and py-ruamel-yaml.

  5. Configures SIESTA with CMake, setting explicit paths for the Fujitsu MPI headers and libraries under /opt/FJSVxtclanga/<tcsds-version>/, and linking against OpenBLAS (for BLAS/LAPACK), netlib-ScaLAPACK, and the previously installed NetCDF libraries. Notable build options include:

    • OpenMP enabled (-DSIESTA_WITH_OPENMP=ON)

    • MPI enabled via Fujitsu MPI libraries

    • NetCDF enabled (-DSIESTA_WITH_NETCDF=ON)

    • ELSI enabled (-DSIESTA_WITH_ELSI=ON)

    • ELPA, DFTD3, and Flook disabled

    Note

    SIESTA 5.4.2 does not compile with the legacy MPI interface. The script automatically sets -DSIESTA_WITH_MPI_INTERFACES=none for version 5.4.2 and legacy for all other versions.

  6. Builds and installs using cmake --build (with 8 parallel jobs) and cmake --install, placing the final binaries in _siesta_bin_gnu_fjmpi.

You can download get_siesta.sh directly.

Show full script: get_siesta.sh
Listing 2 get_siesta.sh
#!/usr/bin/env bash
#
# get_siesta.sh – download, configure, and compile SIESTA with GNU + Fujitsu MPI
#
# Requires the following environment variables (set in ~/.bashrc or exported
# before calling this script):
#   MY_USER     – your Fugaku username
#   MY_PROJECT  – your project code
#   MY_VOLUME   – your storage volume 
#
# Usage examples
#   ./get_siesta.sh                              # default versions (SIESTA 5.4.2, TCSDS 1.2.42)
#   ./get_siesta.sh 5.4.1                        # different SIESTA version
#   ./get_siesta.sh --tcsds-version 1.2.42       # different TCSDS version
#   ./get_siesta.sh 5.4.1 --tcsds-version 1.2.42 # specify both versions
#   ./get_siesta.sh -c                           # clean extracted directory before starting
#   ./get_siesta.sh -r                           # re-download tarball if missing and re-extract
#

set -euo pipefail

###############################################################################
# Helper: display usage
###############################################################################
usage() {
  cat <<EOF
Usage: $0 [version] [-c|--clean] [-r|--refresh] [--tcsds-version VERSION]

  version              SIESTA version to download (default: 5.4.2)
  --tcsds-version VER  TCSDS version to use (default: 1.2.42)
  -c, --clean          Delete the extracted directory if it already exists.
  -r, --refresh        If the directory exists *and* the tarball does not,
                       download the tarball, delete the directory, and extract again.

Only one of -c or -r can be supplied at the same time.
EOF
  exit 1
}

###############################################################################
# Parse command-line arguments
###############################################################################
VERSION="5.4.2"
CLEAN=false
REFRESH=false
versiontcsds="tcsds-1.2.42"

while [[ $# -gt 0 ]]; do
  case "$1" in
    -c|--clean)
      CLEAN=true
      shift
      ;;
    -r|--refresh)
      REFRESH=true
      shift
      ;;
    --tcsds-version)
      if [[ $# -lt 2 ]]; then
        echo "Error: --tcsds-version requires a version number." >&2
        usage
      fi
      versiontcsds="tcsds-$2"
      shift 2
      ;;
    -h|--help)
      usage
      ;;
    -*)
      echo "Unknown option: $1" >&2
      usage
      ;;
    *)
      VERSION="$1"
      shift
      ;;
  esac
done

# Reject simultaneous -c and -r
if $CLEAN && $REFRESH; then
  echo "Error: -c/--clean and -r/--refresh are mutually exclusive." >&2
  exit 1
fi

###############################################################################
# Check required environment variables
###############################################################################
for var in MY_USER MY_PROJECT MY_VOLUME; do
  if [[ -z "${!var:-}" ]]; then
    echo "Error: environment variable ${var} is not set." >&2
    echo "Export it or add it to your ~/.bashrc before running this script." >&2
    exit 1
  fi
done

###############################################################################
# Variables derived from version and environment
###############################################################################
PKG="siesta-${VERSION}"
TARBALL="${PKG}.tar.gz"
URL="https://gitlab.com/siesta-project/siesta/-/releases/${VERSION}/downloads/${TARBALL}"

SPACK_SETUP="/${MY_VOLUME}/mdt1/data/${MY_PROJECT}/${MY_USER}/tmp/spack/share/spack/setup-env.sh"
NETCDF_ENV="/${MY_VOLUME}/mdt1/data/${MY_PROJECT}/${MY_USER}/tmp/netcdf_gcc/netcdf_env.sh"

###############################################################################
# Helper: download tarball
###############################################################################
download_tarball() {
  echo "Downloading ${TARBALL} ..."
  if command -v curl &>/dev/null; then
    curl -L -o "${TARBALL}" "${URL}"
  elif command -v wget &>/dev/null; then
    wget -O "${TARBALL}" "${URL}"
  else
    echo "Error: neither curl nor wget is available." >&2
    exit 1
  fi
}

###############################################################################
# 1. Handle clean / refresh logic
###############################################################################
if $CLEAN && [[ -d "${PKG}" ]]; then
  echo "Removing existing directory ${PKG} (requested with --clean) ..."
  rm -rf "${PKG}"
fi

if $REFRESH && [[ -d "${PKG}" ]] && [[ ! -f "${TARBALL}" ]]; then
  echo "Refresh requested and tarball missing."
  download_tarball
  echo "Removing existing directory ${PKG} ..."
  rm -rf "${PKG}"
fi

###############################################################################
# 2. Ensure tarball is present
###############################################################################
if [[ ! -f "${TARBALL}" ]]; then
  download_tarball
else
  echo "Tarball ${TARBALL} already exists. Skipping download."
fi

###############################################################################
# 3. Extract tarball
###############################################################################
if [[ ! -d "${PKG}" ]]; then
  echo "Extracting ${TARBALL} ..."
  tar -xzf "${TARBALL}"
else
  echo "Directory ${PKG} already exists. Re-extracting ..."
  rm -rf "${PKG}"
  tar -xzf "${TARBALL}"
fi

###############################################################################
# 4. Create build directories
###############################################################################
cd "${PKG}"
build_="_build_gnu_fjmpi"
bin_="_siesta_bin_gnu_fjmpi"
mkdir -p "${build_}" "${bin_}"
echo "Directories ${build_} and ${bin_} are ready in $(pwd)."

###############################################################################
# 5. Load Spack environment
###############################################################################
echo "Loading Spack environment ..."
source "${SPACK_SETUP}"
echo "Spack environment loaded."

###############################################################################
# 6. Load NetCDF environment
###############################################################################
echo "Loading NetCDF environment ..."
source "${NETCDF_ENV}"
echo "NetCDF environment loaded."

###############################################################################
# 7. Load modules
###############################################################################
ml purge
ml lang/${versiontcsds}
echo "Modules purged and TCSDS ${versiontcsds} loaded."
ml list

echo "Loading Spack packages ..."
spack load /f57uyl2  # gcc@12.2.0
spack load /tcvpuhv  # fujitsu-mpi@head %gcc@12.2.0
spack load /tb7qm25  # netlib-scalapack@2.2.2 %gcc@12.2.0
spack load /3mtxbkd  # python@3.13.5 %gcc@12.2.0
spack load /fvktbik  # cmake@3.31.8 %gcc@12.2.0
spack load /ixbcgpp  # ninja@1.12.1 %gcc@12.2.0
spack load /ptmidr7  # py-pip@25.1.1
spack load /klrzwek  # openblas@0.3.21 %gcc@12.2.0
spack load /mvuivif  # flex@2.6.4 %gcc@12.2.0
spack load /unx5qet  # fftw@3.3.10 %gcc@12.2.0
spack load /3btulwm  # bison@3.8.2 %gcc@12.2.0

#spack load /XXXXXXX  # py-ruamel-yaml@0.17.32 ^python /3mtxbkd %gcc@12.2.0
echo "All Spack packages loaded."

###############################################################################
# 8. Resolve library paths
###############################################################################
export OPENBLAS_PATH=$(spack location -i openblas%gcc@12.2.0)
export SCALAPACK_PATH=$(spack location -i netlib-scalapack%gcc@12.2.0)
echo "Library paths resolved."

###############################################################################
# 9. Set MPI interface flag (5.4.2 does not support legacy interface)
###############################################################################
if [ "${VERSION}" == "5.4.2" ]; then
    interface="none"
else
    interface="legacy"
fi

###############################################################################
# 10. Configure SIESTA with CMake
###############################################################################
echo "Starting CMake configuration ..."
FC=gfortran CC=gcc CXX=g++ cmake -S . -B "${build_}" \
        -DCMAKE_Fortran_COMPILER=gfortran \
        -DCMAKE_C_COMPILER=gcc \
        -DCMAKE_CXX_COMPILER=g++ \
        -DCMAKE_INSTALL_PREFIX="${bin_}" \
        -DCMAKE_Fortran_FLAGS="-I/opt/FJSVxtclanga/${versiontcsds}/include/mpi/fujitsu -fopenmp -fallow-argument-mismatch" \
        -DCMAKE_C_FLAGS="-I/opt/FJSVxtclanga/${versiontcsds}/include/mpi/fujitsu -fopenmp" \
        -DCMAKE_CXX_FLAGS="-I/opt/FJSVxtclanga/${versiontcsds}/include/mpi/fujitsu -fopenmp -Wno-stringop-overflow -DMPICH_SKIP_MPICXX -DOMPI_SKIP_MPICXX -DMPI_NO_CPPBIND" \
        -DMPI_Fortran_LIBRARIES="/opt/FJSVxtclanga/${versiontcsds}/lib64/libmpi_usempif08.so;/opt/FJSVxtclanga/${versiontcsds}/lib64/libmpi_mpifh.so;/opt/FJSVxtclanga/${versiontcsds}/lib64/libmpi.so" \
        -DMPI_C_LIBRARIES="/opt/FJSVxtclanga/${versiontcsds}/lib64/libmpi.so" \
        -DMPI_CXX_LIBRARIES="/opt/FJSVxtclanga/${versiontcsds}/lib64/libmpi.so" \
        -DCMAKE_EXE_LINKER_FLAGS="-L/opt/FJSVxtclanga/${versiontcsds}/lib64 -Wl,-rpath,/opt/FJSVxtclanga/${versiontcsds}/lib64" \
        -DMPIEXEC_EXECUTABLE="/vol0004/apps/oss/mpigcc/fjmpi-gcc12/bin/mpiexec" \
        -DMPIEXEC_MAX_NUMPROCS=4 \
        -DBLAS_LIBRARY="${OPENBLAS_PATH}/lib/libopenblas.so" \
        -DLAPACK_LIBRARY="${OPENBLAS_PATH}/lib/libopenblas.so" \
        -DSCALAPACK_LIBRARY="${SCALAPACK_PATH}/lib/libscalapack.so" \
        -DSIESTA_WITH_NETCDF=ON \
        -DSIESTA_WITH_OPENMP=ON \
        -DSIESTA_WITH_MPI_INTERFACES="${interface}" \
        -DCMAKE_BUILD_TYPE=Release \
        -DSIESTA_WITH_ELPA=OFF \
        -DSIESTA_WITH_ELSI=ON \
        -DSIESTA_WITH_DFTD3=OFF \
        -DSIESTA_WITH_FLOOK=OFF

###############################################################################
# 11. Build and install
###############################################################################
if [ $? -eq 0 ]; then
    echo ""
    echo "CMake configuration successful. Building ..."
    cmake --build "${build_}" -j 8

    if [ $? -eq 0 ]; then
        echo ""
        echo "Build successful. Installing ..."
        cmake --install "${build_}"
    else
        echo ""
        echo "ERROR: Build failed."
        exit 1
    fi
else
    echo ""
    echo "ERROR: CMake configuration failed."
    exit 1
fi