diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 00000000000..c200a1adb78 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,280 @@ +name: cmake +env: + # update urls for oneapi packages according to + # https://github.com/oneapi-src/oneapi-ci/blob/master/.github/workflows/build_all.yml + WINDOWS_BASEKIT_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/b380d914-366b-4b77-a74a-05e3c38b3514/intel-oneapi-base-toolkit-2025.0.0.882_offline.exe + WINDOWS_BASEKIT_COMPONENTS: intel.oneapi.win.mkl.devel + WINDOWS_HPCKIT_URL: https://registrationcenter-download.intel.com/akdlm/IRC_NAS/f07e32fa-b505-4b90-8a79-e328ce9ad9d6/intel-oneapi-hpc-toolkit-2025.0.0.822_offline.exe + WINDOWS_HPCKIT_COMPONENTS: intel.oneapi.win.ifort-compiler:intel.oneapi.win.mpi.devel + +on: + push: + paths: + - .github/** + - Build/** + - Source/** + - CMakeLists.txt + pull_request: + paths: + - .github/** + - Build/** + - Source/** + - CMakeLists.txt + + +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + + +permissions: + contents: read + +jobs: + cmake-linux: + name: ${{ matrix.container }} ${{ matrix.compiler_mpi }} openmp=${{ matrix.openmp }} ${{ matrix.build_type }} + runs-on: [ubuntu-latest] + container: ${{ matrix.container }} + strategy: + matrix: + container: + # - "fedora:40" + - "rockylinux:9" + - "ubuntu:24.04" + compiler_mpi: + - "intel_intelmpi" + # - "intel_openmpi" + - "gnu_openmpi" + openmp: + - "ON" + - "OFF" + build_type: + - "Debug" + - "Release" + defaults: + run: + shell: bash + steps: + - name: Install prerequisites (dnf) + if: matrix.container != 'ubuntu:24.04' + run: dnf install -y git gcc make cmake sudo environment-modules + - name: Install prerequisites (apt) + if: matrix.container == 'ubuntu:24.04' + run: | + apt-get -y update + apt-get -y install -y git gcc make cmake sudo environment-modules + + # # Setup OneAPI, icx is necessary for third-party libs + # - uses: rscohn2/setup-oneapi@v0 + # if: endsWith(matrix.compiler_mpi, '_intelmpi') != true + # with: + # components: | + # ifx + # icx + # mkl + + # Setup OneAPI, icx is necessary for third-party libs + - uses: rscohn2/setup-oneapi@v0 + # if: endsWith(matrix.compiler_mpi, '_intelmpi') + with: + components: | + ifx + icx + impi + mkl + + - name: install openmpi + if: endsWith(matrix.compiler_mpi, '_openmpi') && (startsWith(matrix.container, 'rockylinux') || startsWith(matrix.container, 'fedora')) + run: dnf install -y openmpi-devel + + - name: install openmpi + if: endsWith(matrix.compiler_mpi, '_openmpi') && startsWith(matrix.container, 'ubuntu') + run: | + sudo apt-get -y update + sudo apt-get -y install libopenmpi-dev openmpi-bin + + - uses: actions/checkout@v4 + - run: git config --global --add safe.directory /__w/fds/fds + + - name: set linux-gnu compiler + if: startsWith(matrix.compiler_mpi, 'gnu_') + shell: bash + run: | + echo "CC=gcc" >> $GITHUB_ENV + echo "CXX=g++" >> $GITHUB_ENV + echo "FC=gfortran" >> $GITHUB_ENV + + - name: set linux-gnu compiler + if: startsWith(matrix.compiler_mpi, 'intel_') + shell: bash + run: | + echo "CC=mpiicx" >> $GITHUB_ENV + echo "CXX=mpiicx" >> $GITHUB_ENV + echo "FC=mpiifx" >> $GITHUB_ENV + + - name: build fds + if: endsWith(matrix.compiler_mpi, '_intelmpi') + run: | + source /opt/intel/oneapi/setvars.sh + cmake -B builddir -S . -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DMKL_LINK=dynamic -DUSE_OPENMP=${{matrix.openmp}} + cmake --build builddir -j --target fds + + - name: build fds + if: endsWith(matrix.compiler_mpi, '_openmpi') + run: | + # source /opt/intel/oneapi/setvars.sh + if [ "${{matrix.container}}" = "rockylinux:9" ]; then + source /etc/profile.d/modules.sh + module load mpi/openmpi-x86_64 + fi + cmake -B builddir -S . -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DMKL_LINK=dynamic -DUSE_OPENMP=${{matrix.openmp}} + cmake --build builddir -j --target fds + + - name: Test + if: endsWith(matrix.compiler_mpi, '_intelmpi') + run: | + source /opt/intel/oneapi/setvars.sh + ctest --test-dir builddir -j --output-on-failure -V + + - name: Test + if: endsWith(matrix.compiler_mpi, '_openmpi') + run: | + source /opt/intel/oneapi/setvars.sh + if [ "${{matrix.container}}" = "rockylinux:9" ]; then + source /etc/profile.d/modules.sh + module load mpi/openmpi-x86_64 + fi + ctest --test-dir builddir -j --output-on-failure -V + + cmake-osx: + # Set the name of this build, variable depending on the OS + name: ${{ matrix.os }} ${{ matrix.compiler_mpi }} openmp=${{ matrix.openmp }} ${{ matrix.build_type }} + strategy: + fail-fast: false + # The matrix sets all the different combinations of builds, e.g. platforms + # and build configurations + matrix: + os: + - macos-latest + compiler_mpi: + - "gnu_openmpi" + openmp: + - "ON" + - "OFF" + build_type: + - "Debug" + - "Release" + runs-on: ${{ matrix.os }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: install openmpi + run: brew install open-mpi + + - name: set macos gcc + if: startsWith(matrix.compiler_mpi, 'gnu_') + shell: bash + run: | + echo "CC=gcc-14" >> $GITHUB_ENV + echo "CXX=g++-14" >> $GITHUB_ENV + echo "FC=gfortran-14" >> $GITHUB_ENV + echo "OMPI_FC=gfortran-14" >> $GITHUB_ENV + brew install glew gd zlib json-c + + - name: set macos intel + if: startsWith(matrix.compiler_mpi, 'intel_') + shell: bash + run: | + echo "CC=icx" >> $GITHUB_ENV + echo "CXX=icx" >> $GITHUB_ENV + echo "FC=mpiifx" >> $GITHUB_ENV + echo "OMPI_FC=mpiifx" >> $GITHUB_ENV + + - name: Build + if: startsWith(matrix.compiler_mpi, 'gnu_') + shell: bash + run: | + cmake -B builddir -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DUSE_OPENMP=${{matrix.openmp}} + cmake --build builddir -j1 --target fds + + - name: Build + if: startsWith(matrix.compiler_mpi, 'intel_') + shell: bash + run: | + source /opt/intel/oneapi/setvars.sh + cmake -B builddir -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DUSE_OPENMP=${{matrix.openmp}} + cmake --build builddir -j1 --target fds + + - name: Test + if: startsWith(matrix.compiler_mpi, 'gnu_') + run: ctest --test-dir builddir -j --output-on-failure -V + + - name: Test + if: startsWith(matrix.compiler_mpi, 'intel_') + run: | + source /opt/intel/oneapi/setvars.sh + ctest --test-dir builddir -j --output-on-failure -V + + cmake-windows: + # build on windows using ifort with intelmpi and mkl based on + # https://github.com/oneapi-src/oneapi-ci + + name: windows ${{matrix.compiler}} intelmpi openmp=${{ matrix.openmp }} ${{ matrix.build_type }} + runs-on: [windows-latest] + strategy: + fail-fast: false + # The matrix sets all the different combinations of builds, e.g. platforms + # and build configurations + matrix: + build_type: + - "Debug" + - "Release" + openmp: + - "ON" + - "OFF" + compiler: + - "mpiifort" + - "mpiifx" + defaults: + run: + shell: cmd + + steps: + - uses: actions/checkout@v4 + + # install oneapi components from web installer based on + # oneapi-ci/scripts/install_windows.bat + - name: cache install oneapi + id: cache-install + uses: actions/cache@v4 + with: + path: C:\Program Files (x86)\Intel\oneAPI\ + key: install-${{ env.WINDOWS_BASEKIT_URL }}-${{ env.WINDOWS_BASEKIT_COMPONENTS }}-${{ env.WINDOWS_HPCKIT_URL }}-${{ env.WINDOWS_HPCKIT_COMPONENTS }} + - name: install oneapi mkl + if: steps.cache-install.outputs.cache-hit != 'true' + run: | + curl.exe --output %TEMP%\webimage_base.exe --url %WINDOWS_BASEKIT_URL% --retry 5 --retry-delay 5 + start /b /wait %TEMP%\webimage_base.exe -s -x -f webimage_base_extracted --log extract_base.log + del %TEMP%\webimage_base.exe + webimage_base_extracted\bootstrapper.exe -s --action install --components=%WINDOWS_BASEKIT_COMPONENTS% --eula=accept -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 --log-dir=. + rd /s/q "webimage_base_extracted" + - name: install oneapi compiler, mpi + if: steps.cache-install.outputs.cache-hit != 'true' + run: | + curl.exe --output %TEMP%\webimage_hpc.exe --url %WINDOWS_HPCKIT_URL% --retry 5 --retry-delay 5 + start /b /wait %TEMP%\webimage_hpc.exe -s -x -f webimage_hpc_extracted --log extract_hpc.log + del %TEMP%\webimage_hpc.exe + webimage_hpc_extracted\bootstrapper.exe -s --action install --components=%WINDOWS_HPCKIT_COMPONENTS% --eula=accept -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 --log-dir=. + rd /s/q "webimage_hpc_extracted" + + - name: build fds + run: | + call Build\Scripts\setup_intel_compilers.bat + cmake -B builddir -S . -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DUSE_SUNDIALS=OFF -DUSE_HYPRE=ON -DHYPRE_FMANGLE=4 -DUSE_OPENMP=${{matrix.openmp}} + cmake --build builddir -j --target fds + + - name: Test + run: | + call Build\Scripts\setup_intel_compilers.bat + ctest --test-dir builddir -j --output-on-failure -V diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000000..864753c30d5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,232 @@ +cmake_minimum_required(VERSION 3.24 FATAL_ERROR) + +project( + fds + VERSION 6.9.1 + LANGUAGES Fortran +) +enable_language(Fortran) + +option(USE_HYPRE "Use the hypre library" ON) +option(USE_SYSTEM_HYPRE "Use the hypre library from the system" OFF) + +option(USE_SUNDIALS "Use the sundials library" ON) +option(USE_SYSTEM_SUNDIALS "Use the sundials library from the system" OFF) + +option(USE_OPENMP "Use OpenMP" ON) + +# The existing FDS makefile links MKL statically, but this is optional and can +# be changed to dynamic +set(MKL_LINK static CACHE STRING "Linking method for MKL (static or dynamic)") +set_property(CACHE MKL_LINK PROPERTY STRINGS static dynamic) + +add_executable(fds + Source/main.f90 + Source/prec.f90 + Source/cons.f90 + Source/chem.f90 + Source/prop.f90 + Source/devc.f90 + Source/type.f90 + Source/data.f90 + Source/mesh.f90 + Source/func.f90 + Source/gsmv.f90 + Source/smvv.f90 + Source/rcal.f90 + Source/turb.f90 + Source/soot.f90 + Source/pois.f90 + Source/geom.f90 + Source/ccib.f90 + Source/radi.f90 + Source/part.f90 + Source/vege.f90 + Source/ctrl.f90 + Source/hvac.f90 + Source/mass.f90 + Source/imkl.f90 + Source/wall.f90 + Source/fire.f90 + Source/velo.f90 + Source/pres.f90 + Source/init.f90 + Source/dump.f90 + Source/read.f90 + Source/divg.f90 +) +target_include_directories(fds PRIVATE .) + +# Get various properties about the time and git revision. These can be +# overridden which is important for building in situations where we don't have +# the git repo. +if (NOT(WIN32) AND NOT(BUILD_DATE_XLF)) + string(TIMESTAMP BUILD_DATE_XLF "%b %d\\, %Y %H:%M:%S") +endif() +if (NOT(BUILD_DATE)) + string(TIMESTAMP BUILD_DATE "%b %d, %Y %H:%M:%S ") +endif() +if (NOT(GIT_DATE)) + execute_process(COMMAND git log -1 --format=%cd OUTPUT_VARIABLE GIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +if (NOT(GIT_BRANCH)) + execute_process(COMMAND git rev-parse --abbrev-ref HEAD OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +if (NOT(GIT_HASH)) + execute_process(COMMAND git describe --long --abbrev=7 OUTPUT_VARIABLE GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +if (NOT(GIT_DIRTY)) + execute_process(COMMAND git diff --shortstat Source/*.f90 OUTPUT_VARIABLE GIT_DIRTY) +endif() +if (GIT_DIRTY STREQUAL "") + set(GIT_STAT "") +else() + set(GIT_STAT "-dirty") +endif() + +target_compile_definitions(fds PRIVATE BUILDDATE_PP="${BUILD_DATE}") +target_compile_definitions(fds PRIVATE GITHASH_PP="${GIT_HASH}${GIT_STAT}-${GIT_BRANCH}") +target_compile_definitions(fds PRIVATE GITDATE_PP="${GIT_DATE}") + +# Use MPI (mandatory) +find_package(MPI REQUIRED) +target_link_libraries(fds PRIVATE MPI::MPI_Fortran) + +# FDS use lp64 (4-byte integer), MKL often defaults to ilp64 (8-byte integer) +set(MKL_INTERFACE lp64) +# Turn on BLACS +set(ENABLE_BLACS ON) +# We don't need ScaLAPACK +set(ENABLE_SCALAPACK OFF) +# Use MKL if found +find_package(MKL CONFIG) +if (MKL_FOUND) + message(STATUS "${MKL_IMPORTED_TARGETS}") + get_target_property(MKLS MKL::MKL INTERFACE_COMPILE_OPTIONS) + message(STATUS "MKL Compile Options: ${MKLS}") + get_target_property(MKLS MKL::MKL INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "MKL Include Directories: ${MKLS}") + target_compile_definitions(fds PRIVATE WITH_MKL) + target_link_libraries(fds PRIVATE MKL::MKL) +endif() + +if (USE_OPENMP) + # Use OpenMP + find_package(OpenMP REQUIRED) + target_link_libraries(fds PUBLIC OpenMP::OpenMP_Fortran) +endif() + +# Set compiler flags for various compilers +if (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + target_compile_options(fds PRIVATE -cpp -std=f2018 -frecursive -ffpe-summary=none -fall-intrinsics) +elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel" OR CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM") + target_compile_options(fds PRIVATE -fpp ) + # When compiling with Intel on Linux, enable the IFPORT library + if (LINUX) + target_compile_definitions(fds PRIVATE USE_IFPORT) + endif() + # When compiling with Intel and not on Windows apply -no-wrap-margin. + if (NOT(WIN32)) + target_compile_definitions(fds PRIVATE -no-wrap-margin) + endif() +endif() + +# If we are using the old Intel Fortran compiler (ifort) suppress the warning +# that this is an old compiler. +if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel" ) + target_compile_options(fds PRIVATE /Qdiag-disable:10448) +endif() + +if(WIN32) + set(BUILD_SHARED_LIBS OFF) +endif() + +# Find or build hypre if the option is set to use it +if(USE_HYPRE) + # Unless forced to use the system version, download and build hypre + if(NOT(USE_SYSTEM_HYPRE)) + include(FetchContent) + # As we are not using the system hypre, we need to choose the version we + # want + set(HYPRE_GIT_VERSION "2.32.0" ) + FetchContent_Declare( + HYPRE + # GIT_REPOSITORY https://github.com/hypre-space/hypre.git + # GIT_TAG b6d8c3085af64988f8f8bc21b7aef12ab49c5430 # v${HYPRE_GIT_VERSION} + + # Currently we need to refer directly to a commit which contains a + # patch to handle HYPRE_FMANGLE + GIT_REPOSITORY https://github.com/JakeOShannessy/hypre.git + GIT_TAG b6d8c3085af64988f8f8bc21b7aef12ab49c5430 # v2.32.0 + SOURCE_SUBDIR src + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(HYPRE) + endif() + + # Use hypre if found + find_package(HYPRE) + if(HYPRE_FOUND) + if(NOT(HYPRE_VERSION)) + # We aren't using the system version so we'll revert to the git + # version + set(HYPRE_VERSION "${HYPRE_GIT_VERSION}") + endif() + target_link_libraries(fds PRIVATE HYPRE) + target_compile_definitions(fds PRIVATE WITH_HYPRE) + target_compile_definitions(fds PRIVATE HYPRE_PP="${HYPRE_VERSION}") + endif() +endif() + +# Find or build sundials if the option is set to use it +if(USE_SUNDIALS) + # Unless forced to use the system version, download and build sundials + if(NOT(USE_SYSTEM_SUNDIALS)) + include(FetchContent) + # As we are not using the system sundials, we need to choose the version + # we want + set(SUNDIALS_GIT_VERSION "6.7.0") + FetchContent_Declare( + SUNDIALS + GIT_REPOSITORY https://github.com/LLNL/sundials.git + GIT_TAG v${SUNDIALS_GIT_VERSION} + OVERRIDE_FIND_PACKAGE + ) + # Set some options for building sundials + set(ENABLE_MPI ON CACHE BOOL "" FORCE) + set(BUILD_FORTRAN_MODULE_INTERFACE ON CACHE BOOL "" FORCE) + set(EXAMPLES_ENABLE_C OFF CACHE BOOL "" FORCE) + set(EXAMPLES_ENABLE_CXX OFF CACHE BOOL "" FORCE) + set(EXAMPLES_ENABLE_F2003 OFF CACHE BOOL "" FORCE) + set(EXAMPLES_INSTALL OFF CACHE BOOL "" FORCE) + set(ENABLE_OPENMP ON CACHE BOOL "" FORCE) + set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) + # if(HYPRE_FOUND) + # set(ENABLE_HYPRE ON CACHE BOOL "" FORCE) + # FetchContent_MakeAvailable(HYPRE SUNDIALS) + # else() + FetchContent_MakeAvailable(SUNDIALS) + # endif() + endif() + + # Use sundials if found + find_package(SUNDIALS) + if(SUNDIALS_FOUND) + if(NOT(SUNDIALS_VERSION)) + # We aren't using the system version so we'll revert to the git + # version + set(SUNDIALS_VERSION "${SUNDIALS_GIT_VERSION}") + endif() + target_link_libraries(fds PRIVATE SUNDIALS::fcvode_mod) + target_link_libraries(fds PRIVATE SUNDIALS::fnvecserial_mod) + target_compile_definitions(fds PRIVATE WITH_SUNDIALS) + target_compile_definitions(fds PRIVATE SUNDIALS_PP="${SUNDIALS_VERSION}") + endif() +endif() + +install(TARGETS fds) + +include(CTest) +enable_testing() +add_test(NAME "FDS Executes" + COMMAND fds)