Compile the SWAT+ fork
Clone rafiei-vahid/swatplus, build with Intel ifx and ifx-built NetCDF-Fortran, set the CMake NetCDF and OpenMP switches, install into TxtInOut.
SWATGenX packages are ordinary SWAT+ TxtInOut trees plus documented extensions. The engine that reads NetCDF cdfout, OpenMP run modes, PFAS inputs, and research mf6.con coupling is the public fork of upstream SWAT+ at github.com/rafiei-vahid/swatplus. This chapter is the operator build recipe for that fork. Stock SWAT+ build docs remain upstream; do not treat this page as a substitute for swat-model/swatplus.
pfas.dat / related files) or mf6.con (plus libmf6 on the library path). NetCDF and OpenMP are the two build-time toggles.Prerequisites
| Component | Requirement | Notes |
|---|---|---|
| Fortran compiler | Intel ifx (oneAPI HPC Toolkit) for production-scale builds | gfortran can configure the same CMake options but is not the production toolchain; large models have failed under gfortran while building cleanly under ifx. |
| CMake | ≥ 3.16 | Unix Makefiles generator is the documented path. |
| NetCDF-C + HDF5 | System packages OK | Debian/Ubuntu: libnetcdf-dev (and runtime libs). Only the Fortran bindings must match the Fortran compiler. |
| netcdf-fortran | Built with the same ifx as the engine | Distro libnetcdff-dev is gfortran-built; ifx cannot read those .mod files. |
| OpenMP | Provided by ifx when SWATPLUS_OPENMP=ON | CMake uses find_package(OpenMP) → OpenMP::OpenMP_Fortran (−fiopenmp / equivalent). |
| git | Required | CMake names the executable from git describe --tags. |
# Load Intel oneAPI (do this before set -u — setvars.sh dereferences unset vars) source /opt/intel/oneapi/setvars.sh command -v ifx # must resolve (e.g. …/compiler/2026.0/bin/ifx) # gfortran-only path (simpler, not production toolchain) # sudo apt-get install -y gfortran cmake libnetcdff-dev libnetcdf-dev
Clone the fork
Pin a release tag for reproducible builds. A stable public tag on the fork is ship/v1.0-49. Branch main is the consolidated line (OpenMP + NetCDF + PFAS + MF6) and moves; prefer a ship/* tag for a known binary.
git clone https://github.com/rafiei-vahid/swatplus.git cd swatplus git fetch --tags git checkout ship/v1.0-49 # optional: confirm tip git describe --tags # expect ship/v1.0-49 (or equivalent)
rafiei-vahid/swatplus (fork) · swat-model/swatplus (upstream)
What to compile
- One executable — CMake target name swatplus-<git-describe>-ifx-lin_<arch>-Rel (slashes in tags become hyphens). There is no separate “PFAS binary” vs “base binary” on this fork line.
- SWATPLUS_NETCDF=ON — compiles the NetCDF-4 output backend (still gated at run time by cdfout in
print.prt). - SWATPLUS_OPENMP=ON — links OpenMP; parallelism is selected at run time via OMP_NUM_THREADS and SWATPLUS_ROUTING_SERIAL (see Engine I/O).
- PFAS + MF6 coupler sources — always present on main / ship tags; inert without model inputs / libmf6.
| CMake option | Default in fork CMakeLists | Recommended |
|---|---|---|
| SWATPLUS_NETCDF | ON | ON |
| SWATPLUS_OPENMP | OFF | ON |
| CMAKE_BUILD_TYPE | (unset) | Release |
| CMAKE_Fortran_COMPILER | (auto) | ifx (pass explicitly) |
Build netcdf-fortran with ifx (required trap)
Fortran .mod files are compiler-specific. If you build the engine with ifx, rebuild netcdf-fortran with ifx against the system NetCDF-C. Point PKG_CONFIG_PATH at that prefix before cmake so pkg_check_modules does not pick the gfortran system package.
# Example prefix; any writable path is fine
PREFIX="$HOME/deps/netcdf-ifx"
# Source tarball: netcdf-fortran 4.5.4 (Unidata release)
export FC=ifx F77=ifx CC=gcc CXX=g++
./configure --prefix="$PREFIX" \
--enable-shared --disable-fortran-type-check \
CPPFLAGS="$(pkg-config --cflags netcdf)" \
LDFLAGS="$(pkg-config --libs netcdf)"
make -j"$(nproc)" && make install
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"
# Runtime later: LD_LIBRARY_PATH="$PREFIX/lib:<oneAPI ifx lib dir>:$LD_LIBRARY_PATH"Configure and build
These are the CMake switches for a NetCDF + OpenMP ifx build matching the fork’s documented recipe. Always pass CMAKE_Fortran_COMPILER=ifx on a clean build directory.
cmake -S . -B build/ifx \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_Fortran_COMPILER=ifx \
-DCMAKE_Fortran_FLAGS="-O3 -ipo -recursive -init=zero -init=arrays -free -fpe0" \
-DSWATPLUS_NETCDF=ON \
-DSWATPLUS_OPENMP=ON
cmake --build build/ifx -j"$(nproc)"
# Locate the executable (name embeds git describe)
ls -1 build/ifx/swatplus-*-ifx-*-Rel- −init=zero −init=arrays — zeroes uninitialized locals; used so thread-count comparisons are meaningful.
- −ipo — interprocedural optimization (ifx).
- Do not pass −static together with a dynamic NetCDF build (CMakeLists refuses that combination when SWATPLUS_NETCDF=ON).
- CMAKE_Fortran_FLAGS_RELEASE in the fork CMakeLists is overwritten to a short −O; put production flags in CMAKE_Fortran_FLAGS (as above), not only in FLAGS_RELEASE.
Install the binary into a model
SWAT+ must be launched from inside the scenario TxtInOut directory (where object.cnt lives). Copy or symlink the built executable as ./swatplus in that directory, or invoke it by absolute path from that cwd.
TXTINOUT=/path/to/Scenarios/Default/TxtInOut
BIN=$(ls -1 build/ifx/swatplus-*-ifx-*-Rel | head -1)
cp -f "$BIN" "$TXTINOUT/swatplus"
chmod +x "$TXTINOUT/swatplus"
# Runtime libs (ifx OpenMP + ifx netcdf-fortran)
export LD_LIBRARY_PATH="$PREFIX/lib:${ONEAPI_ROOT:-/opt/intel/oneapi}/compiler/latest/lib:${LD_LIBRARY_PATH:-}"
cd "$TXTINOUT"
OMP_NUM_THREADS=1 ./swatplus- NetCDF output: set cdfout = y on the value line under csvout dbout cdfout in
print.prt(shipped SWATGenX packages already do). Optional: SWATPLUS_NC_DEFLATE=0|4. - Parallel modes: see Engine I/O — OMP_NUM_THREADS and SWATPLUS_ROUTING_SERIAL. The engine binary ignores unknown CLI flags and will start a simulation anyway; do not use --version as a no-op.
- Research MF6 coupling: place
mf6.conper the MODFLOW coupling chapter and ensure libmf6 is discoverable.
Optional checks
- Smoke: serial run (OMP_NUM_THREADS=1) of a small TxtInOut completes and writes expected channel_sd / NetCDF streams.
- Same-binary thread check: re-run at N threads and compare the streams you care about. Prefer NetCDF (cdfout=y). When management text output is enabled, mgt_out.txt line order can differ across parallel runs even when sorted content matches — exclude or sort before comparing.
- Do not treat a fresh rebuild as byte-identical to an older binary of the same source: compiler floating-point noise between builds is expected. Same-binary mode comparisons are the meaningful check.
- Carbon (cswat = 2): not supported in parallel on this fork — use OMP_NUM_THREADS=1 if carbon is on.
Common failure modes
| Symptom | Likely cause | Fix |
|---|---|---|
| f95: unrecognized option -recursive / -init=zero | CMake picked gfortran because ifx was not on PATH | Source setvars.sh without set -u; pass -DCMAKE_Fortran_COMPILER=ifx; delete the build dir and reconfigure |
| error #7013 … module file … [NETCDF] | pkg-config found gfortran netcdf-fortran | Rebuild netcdf-fortran with ifx; export PKG_CONFIG_PATH before cmake; fresh build dir |
| Cannot find object.cnt | Binary launched outside TxtInOut | cd into TxtInOut first |
| Missing libiomp5 / libnetcdff at runtime | LD_LIBRARY_PATH incomplete | Add ifx lib dir and the ifx netcdf-fortran lib dir |
| Large-model crash under gfortran build | Known gfortran limitation at regional HRU counts | Rebuild with ifx before treating as an engine bug |
