diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38a49b60219..009c5904a15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,7 @@ jobs: run: | echo "OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml" >> $GITHUB_ENV echo "OPENMC_ENDF_DATA=$HOME/endf-b-vii.1" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV # get the sha of the last branch commit # for push and workflow_dispatch events, use the current reference head BRANCH_SHA=HEAD diff --git a/.github/workflows/docker-publish-manylinux.yml b/.github/workflows/docker-publish-manylinux.yml new file mode 100644 index 00000000000..8dacdd556b6 --- /dev/null +++ b/.github/workflows/docker-publish-manylinux.yml @@ -0,0 +1,111 @@ +name: Build & Publish Manylinux Images + +on: + # allows us to run workflows manually + workflow_dispatch: + push: + paths: + - "tools/ci/manylinux.dockerfile" + - ".github/workflows/docker-publish-manylinux.yml" + +jobs: + build-dependency-and-test-img: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + manylinux_image: [ + manylinux_2_28_x86_64 + ] + compiler: [ + gcc, + openmpi + ] + + name: Installing Dependencies, Building OpenMC and running tests + steps: + - name: default environment + run: | + echo "tag-latest-on-default=false" >> "$GITHUB_ENV" + + - name: condition on trigger parameters + if: ${{ github.repository_owner == 'openmc-dev' && github.ref == 'refs/heads/develop' }} + run: | + echo "tag-latest-on-default=true" >> "$GITHUB_ENV" + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Configure docker image tag + run: | + image_base_tag=ghcr.io/${{ github.repository_owner }}/ + image_base_tag+=openmc-${{ matrix.manylinux_image }} + image_base_tag+=-${{ matrix.compiler}} + echo "image_base_tag=${image_base_tag}" >> "$GITHUB_ENV" + + - name: Installing Dependencies in Docker image + uses: firehed/multistage-docker-build-action@v1.8 + with: + repository: ${{ env.image_base_tag }} + stages: base, dependencies, python-dependencies + server-stage: openmc + quiet: false + parallel: true + tag-latest-on-default: ${{ env.tag-latest-on-default }} + dockerfile: tools/ci/manylinux.dockerfile + build-args: > + MANYLINUX_IMAGE=${{ matrix.manylinux_image }}, + COMPILER=${{ matrix.compiler }} + push_stable_ci_img: + needs: [build-dependency-and-test-img] + runs-on: ubuntu-latest + + strategy: + matrix: + manylinux_image: [ + manylinux_2_28_x86_64 + ] + compiler: [ + gcc, + openmpi + ] + + name: Pushing final images + steps: + - name: Log in to the Container registry + if: ${{ github.repository_owner == 'openmc-dev' }} + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure docker image tag + if: ${{ github.repository_owner == 'openmc-dev' }} + run: | + image_base_tag=ghcr.io/${{ github.repository_owner }}/ + image_base_tag+=openmc-${{ matrix.manylinux_image }} + image_base_tag+=-${{ matrix.compiler}} + echo "image_base_tag=${image_base_tag}" >> "$GITHUB_ENV" + + - name: Push Image as latest img + if: ${{ github.repository_owner == 'openmc-dev' && github.ref == 'refs/heads/develop' }} + uses: akhilerm/tag-push-action@v2.1.0 + with: + src: ${{ env.image_base_tag }}/openmc:latest + dst: ${{ env.image_base_tag }}:latest + + - name: Push Image as latest img + if: ${{ github.repository_owner == 'openmc-dev' && github.ref == 'refs/heads/develop' }} + uses: akhilerm/tag-push-action@v2.1.0 + with: + src: ${{ env.image_base_tag }}:latest + dst: ${{ env.image_base_tag }}:stable diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 00000000000..76eb556644d --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,57 @@ +name: Publish wheels to PyPI + +on: + # allows running workflows manually + workflow_dispatch: + + release: + types: [published] + +jobs: + main: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + arch: manylinux_x86_64 + - os: macos-13 + arch: macosx_x86_64 + - os: macos-14 + arch: macosx_arm64 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Build wheels + uses: pypa/cibuildwheel@v2.22.0 + env: + CIBW_BUILD: "cp*-${{ matrix.arch }}" + CIBW_ARCHS_LINUX: "x86_64" + CIBW_MANYLINUX_X86_64_IMAGE: ghcr.io/${{ github.repository_owner }}/openmc-manylinux_2_28_x86_64-gcc/python-dependencies:latest + CIBW_ARCHS_MACOS: "native" + CIBW_BEFORE_BUILD_MACOS: | + brew install llvm cmake xtensor hdf5 python libomp libpng + CIBW_ENVIRONMENT_MACOS: > + MACOSX_DEPLOYMENT_TARGET=${{ matrix.os == 'macos-14' && '14.0' || '13.0' }} + DYLD_LIBRARY_PATH=/usr/local/opt/gcc/lib/gcc/current/:$DYLD_LIBRARY_PATH + CXX=$(brew --prefix llvm)/bin/clang++ + HDF5_ROOT=$(brew --prefix hdf5) + CIBW_BUILD_FRONTEND: "build" + with: + package-dir: openmc + output-dir: wheelhouse + config-file: "{package}/pyproject.toml" + + - name: Publish package to PyPI + if: success() + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: | + twine upload wheelhouse/*.whl \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3b2a24a4dbc..dc30c60ce93 100644 --- a/.gitignore +++ b/.gitignore @@ -43,9 +43,6 @@ results_test.dat # Test .pytest_cache/ -# HDF5 files -*.h5 - # Build files src/CMakeCache.txt src/CMakeFiles/ diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 3578144b254..4c97633bbbd 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -4,6 +4,10 @@ build: os: "ubuntu-24.04" tools: python: "3.12" + apt_packages: + - g++ + - libhdf5-dev + - libpng-dev jobs: post_checkout: - git fetch --unshallow || true diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cb03a1a1da..eac7be1b102 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16 FATAL_ERROR) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) project(openmc C CXX) # Set module path @@ -80,6 +80,9 @@ foreach(OLD_BLD in ITEMS "debug" "optimize") endif() endforeach() +# Include the GenerateScript.cmake file +include(cmake/GenerateScript.cmake) + #=============================================================================== # Set a default build configuration if not explicitly specified #=============================================================================== @@ -299,23 +302,48 @@ include(GNUInstallDirs) # installed one in CMAKE_INSTALL_PREFIX. Ref: # https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/RPATH-handling -# use, i.e. don't skip the full RPATH for the build tree -set(CMAKE_SKIP_BUILD_RPATH FALSE) -# when building, don't use the install RPATH already -# (but later on when installing) -set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +if(SKBUILD) + # By default, scikit-build will install everything to ${SKBUILD_PLATLIB_DIR}/openmc + # Install include and bin directories to ${SKBUILD_PLATLIB_DIR}/openmc/SKBUILD_SUBDIR + set(SKBUILD_SUBDIR core/) + set(CMAKE_INSTALL_BINDIR ${SKBUILD_SUBDIR}${CMAKE_INSTALL_BINDIR}) + set(CMAKE_INSTALL_INCLUDEDIR ${SKBUILD_SUBDIR}${CMAKE_INSTALL_INCLUDEDIR}) + set(CMAKE_INSTALL_DATADIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_DATADIR}) + set(CMAKE_INSTALL_DOCDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_DOCDIR}) + set(CMAKE_INSTALL_INFODIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_INFODIR}) + set(CMAKE_INSTALL_MANDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_MANDIR}) + set(CMAKE_INSTALL_LOCALEDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_LOCALEDIR}) + set(CMAKE_INSTALL_LOCALSTATEDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_LOCALSTATEDIR}) + set(CMAKE_INSTALL_RUNSTATEDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_RUNSTATEDIR}) + + # Set RPATH + if(APPLE) + set(CMAKE_MACOSX_RPATH ON) + set(OPENMC_LIBRARY_RPATH "@loader_path") + set(OPENMC_BINARY_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR}") + elseif(UNIX) + set(OPENMC_LIBRARY_RPATH "$ORIGIN") + set(OPENMC_BINARY_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}") + endif() +else() + # use, i.e. don't skip the full RPATH for the build tree + set(CMAKE_SKIP_BUILD_RPATH FALSE) -# add the automatically determined parts of the RPATH -# which point to directories outside the build tree to the install RPATH -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + # when building, don't use the install RPATH already + # (but later on when installing) + set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) -# the RPATH to be used when installing, but only if it's not a system directory -list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemDir) -if("${isSystemDir}" STREQUAL "-1") - set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") + # the RPATH to be used when installing, but only if it's not a system directory + list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemDir) + if("${isSystemDir}" STREQUAL "-1") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") + endif() endif() +# add the automatically determined parts of the RPATH +# which point to directories outside the build tree to the install RPATH +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) #=============================================================================== # libopenmc #=============================================================================== @@ -459,7 +487,7 @@ endif() target_include_directories(libopenmc PUBLIC - $ + $ $ ${HDF5_INCLUDE_DIRS} ) @@ -467,6 +495,14 @@ target_include_directories(libopenmc # Set compile flags target_compile_options(libopenmc PRIVATE ${cxxflags}) +if(OPENMC_LIBRARY_RPATH) + set_target_properties( + libopenmc + PROPERTIES + INSTALL_RPATH "${OPENMC_LIBRARY_RPATH}" + ) +endif() + # Add include directory for configured version file target_include_directories(libopenmc PUBLIC $) @@ -558,15 +594,16 @@ target_compile_features(openmc PUBLIC cxx_std_17) target_compile_features(libopenmc PUBLIC cxx_std_17) set_target_properties(openmc libopenmc PROPERTIES CXX_EXTENSIONS OFF) -#=============================================================================== -# Python package -#=============================================================================== +generate_and_install_python_script(openmc) -add_custom_command(TARGET libopenmc POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - $ - ${CMAKE_CURRENT_SOURCE_DIR}/openmc/lib/$ - COMMENT "Copying libopenmc to Python module directory") +# Set rpath for openmc executable +if(OPENMC_BINARY_RPATH) + set_target_properties( + openmc + PROPERTIES + INSTALL_RPATH "${OPENMC_BINARY_RPATH}" + ) +endif() #=============================================================================== # Install executable, scripts, manpage, license @@ -575,17 +612,35 @@ add_custom_command(TARGET libopenmc POST_BUILD configure_file(cmake/OpenMCConfig.cmake.in "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfig.cmake" @ONLY) configure_file(cmake/OpenMCConfigVersion.cmake.in "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfigVersion.cmake" @ONLY) -set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) +if(SKBUILD) + set(INSTALL_CONFIGDIR ${SKBUILD_DATA_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) +else() + set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) +endif() + install(TARGETS openmc libopenmc EXPORT openmc-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${SKBUILD_SUBDIR}${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${SKBUILD_SUBDIR}${CMAKE_INSTALL_LIBDIR} ) + +set(INSTALL_TARGETDIR ${SKBUILD_SUBDIR}${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) install(EXPORT openmc-targets FILE OpenMCTargets.cmake NAMESPACE OpenMC:: - DESTINATION ${INSTALL_CONFIGDIR}) + DESTINATION ${INSTALL_TARGETDIR} +) + +# Collect scripts +file(GLOB SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*") + +# Install scripts to bin directory +if(SKBUILD) + install(PROGRAMS ${SCRIPTS} DESTINATION ${SKBUILD_SCRIPTS_DIR}) +else() + install(PROGRAMS ${SCRIPTS} DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() install(FILES "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfig.cmake" diff --git a/Dockerfile b/Dockerfile index 1ae615a50f8..dac9cb8b822 100644 --- a/Dockerfile +++ b/Dockerfile @@ -195,40 +195,21 @@ ENV LIBMESH_INSTALL_DIR=$HOME/LIBMESH # clone and install openmc RUN mkdir -p ${HOME}/OpenMC && cd ${HOME}/OpenMC \ && git clone --shallow-submodules --recurse-submodules --single-branch -b ${openmc_branch} ${OPENMC_REPO} \ - && mkdir build && cd build ; \ - if [ ${build_dagmc} = "on" ] && [ ${build_libmesh} = "on" ]; then \ - cmake ../openmc \ - -DCMAKE_CXX_COMPILER=mpicxx \ - -DOPENMC_USE_MPI=on \ - -DHDF5_PREFER_PARALLEL=on \ - -DOPENMC_USE_DAGMC=on \ - -DOPENMC_USE_LIBMESH=on \ - -DCMAKE_PREFIX_PATH="${DAGMC_INSTALL_DIR};${LIBMESH_INSTALL_DIR}" ; \ + && cd openmc ; \ + export SKBUILD_CMAKE_ARGS="-DCMAKE_CXX_COMPILER=mpicxx; \ + -DOPENMC_USE_MPI=on; \ + -DHDF5_PREFER_PARALLEL=on" \ + if [ ${build_dagmc} = "on" ]; then \ + SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS}; \ + -DOPENMC_USE_DAGMC=on; \ + -DCMAKE_PREFIX_PATH=${DAGMC_INSTALL_DIR}" ; \ fi ; \ - if [ ${build_dagmc} = "on" ] && [ ${build_libmesh} = "off" ]; then \ - cmake ../openmc \ - -DCMAKE_CXX_COMPILER=mpicxx \ - -DOPENMC_USE_MPI=on \ - -DHDF5_PREFER_PARALLEL=on \ - -DOPENMC_USE_DAGMC=ON \ - -DCMAKE_PREFIX_PATH=${DAGMC_INSTALL_DIR} ; \ + if [ ${build_libmesh} = "on" ]; then \ + SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS}; \ + -DOPENMC_USE_LIBMESH=on; \ + -DCMAKE_PREFIX_PATH=${LIBMESH_INSTALL_DIR}" ; \ fi ; \ - if [ ${build_dagmc} = "off" ] && [ ${build_libmesh} = "on" ]; then \ - cmake ../openmc \ - -DCMAKE_CXX_COMPILER=mpicxx \ - -DOPENMC_USE_MPI=on \ - -DHDF5_PREFER_PARALLEL=on \ - -DOPENMC_USE_LIBMESH=on \ - -DCMAKE_PREFIX_PATH=${LIBMESH_INSTALL_DIR} ; \ - fi ; \ - if [ ${build_dagmc} = "off" ] && [ ${build_libmesh} = "off" ]; then \ - cmake ../openmc \ - -DCMAKE_CXX_COMPILER=mpicxx \ - -DOPENMC_USE_MPI=on \ - -DHDF5_PREFER_PARALLEL=on ; \ - fi ; \ - make 2>/dev/null -j${compile_cores} install \ - && cd ../openmc && pip install .[test,depletion-mpi] \ + pip -v install .[test,depletion-mpi] \ && python -c "import openmc" FROM build AS release diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index cdc7e2abcf0..00000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,47 +0,0 @@ -include CMakeLists.txt -include LICENSE -include CODE_OF_CONDUCT.md -include CODEOWNERS -include CONTRIBUTING.md -include Dockerfile -include schemas.xml -include pyproject.toml -include pytest.ini -include docs/source/_templates/layout.html -global-include *.cmake -global-include *.cmake.in -global-include *.rst -recursive-include docs *.css -recursive-include docs *.dia -recursive-include docs *.png -recursive-include docs *.py -recursive-include docs *.svg -recursive-include docs *.tex -recursive-include docs *.txt -recursive-include docs Makefile -recursive-include examples *.cpp -recursive-include examples *.py -recursive-include examples *.xml -recursive-include include *.h -recursive-include include *.h.in -recursive-include include *.hh -recursive-include man *.1 -recursive-include src *.cc -recursive-include src *.cpp -recursive-include src *.rnc -recursive-include src *.rng -recursive-include tests *.dat -recursive-include tests *.h5 -recursive-include tests *.h5m -recursive-include tests *.py -recursive-include tests *.xml -recursive-include vendor CMakeLists.txt -recursive-include vendor *.cc -recursive-include vendor *.cpp -recursive-include vendor *.h -recursive-include vendor *.hh -recursive-include vendor *.hpp -recursive-include vendor *.pc.in -recursive-include vendor *.natvis -prune docs/build -prune docs/source/pythonapi/generated/ diff --git a/cmake/GenerateScript.cmake b/cmake/GenerateScript.cmake new file mode 100644 index 00000000000..553c3dc1627 --- /dev/null +++ b/cmake/GenerateScript.cmake @@ -0,0 +1,33 @@ +# Function to generate and install a Python script +# This function creates a Python script with a specified name, writes content to it, +# and installs it to a specified location. +# +# Arguments: +# SCRIPT_NAME - The name of the script to be generated (e.g., 'my_script'). +# +function(generate_and_install_python_script SCRIPT_NAME) + if(SKBUILD) + # Strip any leading/trailing whitespace from the script name + string(STRIP "${SCRIPT_NAME}" CLEAN_SCRIPT_NAME) + + # Define the output path for the generated script using the cleaned name + set(GENERATED_SCRIPT "${CMAKE_BINARY_DIR}/${CLEAN_SCRIPT_NAME}") + + # Generate the script content directly + file(WRITE "${GENERATED_SCRIPT}" "#!/usr/bin/env python3\n\n") + file(APPEND "${GENERATED_SCRIPT}" "import os\n") + file(APPEND "${GENERATED_SCRIPT}" "import sys\n") + file(APPEND "${GENERATED_SCRIPT}" "import sysconfig\n\n") + file(APPEND "${GENERATED_SCRIPT}" "if __name__ == '__main__':\n") + file(APPEND "${GENERATED_SCRIPT}" " os.execv(\n") + file(APPEND "${GENERATED_SCRIPT}" " os.path.join(sysconfig.get_path('platlib'), '${SKBUILD_PROJECT_NAME}', '${CMAKE_INSTALL_BINDIR}', '${CLEAN_SCRIPT_NAME}'),\n") + file(APPEND "${GENERATED_SCRIPT}" " sys.argv,\n") + file(APPEND "${GENERATED_SCRIPT}" " )\n") + + # Install the generated script + install( + PROGRAMS "${GENERATED_SCRIPT}" + DESTINATION "${SKBUILD_SCRIPTS_DIR}" + ) + endif() +endfunction() diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index 5cab4790abc..994cde0e293 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -4,6 +4,9 @@ find_package(fmt REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../fmt) find_package(pugixml REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../pugixml) find_package(xtl REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtl) find_package(xtensor REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtensor) + +find_package(HDF5 REQUIRED COMPONENTS C HL) + if(@OPENMC_USE_DAGMC@) find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@) endif() @@ -17,7 +20,48 @@ endif() find_package(PNG) -if(NOT TARGET OpenMC::libopenmc) +if(@SKBUILD@) + # Find the Python interpreter and ensure it's available. + find_package(Python COMPONENTS Interpreter REQUIRED) + + # Function to run Python commands and validate their execution. + function(run_python_command output_var command) + execute_process( + COMMAND ${Python_EXECUTABLE} -c "${command}" + OUTPUT_VARIABLE ${output_var} + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE result + ) + # Check if the command was successful + if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to run Python command: ${command}") + else() + # Add the output variable to the parent scope + set(${output_var} "${${output_var}}" PARENT_SCOPE) + endif() + endfunction() + + # Extract MOAB include paths, library paths, and extra libraries + run_python_command(OpenMC_INCLUDE_DIRS "import openmc; print(' '.join(openmc.include_path))") + run_python_command(OpenMC_LIBRARY_DIRS "import openmc; print(' '.join(openmc.lib_path))") + run_python_command(OpenMC_EXTRA_LIBRARIES "import openmc; print(' '.join(openmc.extra_lib))") + + # Check if the wheel was repaired using auditwheel or delocate + if(OpenMC_EXTRA_LIBRARIES) + message(FATAL_ERROR + "This build of OpenMC is not supported. " + "It appears that the wheel was repaired using tools like auditwheel or delocate, " + "that modifies the shared libraries, which may cause problems.\n" + "OpenMC_EXTRA_LIBRARIES is not empty: ${OpenMC_EXTRA_LIBRARIES}.\n" + "To resolve this, please build OpenMC from scratch. " + "For more information, visit: https://docs.openmc.org/" + ) + endif() + + # Add OpenMC targets + file(TO_CMAKE_PATH "${OpenMC_LIBRARY_DIRS}/cmake/OpenMC/OpenMCTargets.cmake" OpenMC_TARGETS_FILE) + include(${OpenMC_TARGETS_FILE}) +elseif(NOT TARGET OpenMC::libopenmc) include("${OpenMC_CMAKE_DIR}/OpenMCTargets.cmake") endif() diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index c49326a2090..8fea63ea033 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -63,9 +63,9 @@ features and bug fixes. The general steps for contributing are as follows: .. code-block:: sh - git clone --recurse-submodules git@github.com:yourusername/openmc.git - cd openmc - git checkout -b newbranch develop + git clone --recurse-submodules git@github.com:yourusername/openmc.git + cd openmc + git checkout -b newbranch develop 3. Run ``tools/dev/install-commit-hooks.sh`` to install a post-commit hook that runs clang-format on C++ files to apply :ref:`automatic code formatting @@ -140,15 +140,14 @@ pip_. From the root directory of the OpenMC repository, run: .. code-block:: sh - python -m pip install -e .[test] + python -m pip install --no-build-isolation \ + -Ceditable.rebuild=true \ + -Ccmake.build-type=Debug \ + -Cbuild-dir=build \ + -ve ".[test]" -This installs the OpenMC Python package in `"editable" mode -`_ so that 1) -it can be imported from a Python interpreter and 2) any changes made are -immediately reflected in the installed version (that is, you don't need to keep -reinstalling it). While the same effect can be achieved using the -:envvar:`PYTHONPATH` environment variable, this is generally discouraged as it -can interfere with virtual environments. +This feature allows for rebuilding on initial import, providing +flexibility for iterative development or testing changes to the codebase. .. _git: https://git-scm.com/ .. _GitHub: https://github.com/ diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 21526242f18..1e7b9d2afab 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -128,38 +128,66 @@ source below. Building Source on Linux or macOS --------------------------------- -All OpenMC source code is hosted on `GitHub -`_. If you have `git -`_, a modern C++ compiler, `CMake `_, -and `HDF5 `_ installed, you can -download and install OpenMC by entering the following commands in a terminal: +All OpenMC source code is hosted on GitHub (`OpenMC GitHub `_). +Depending on your needs, you can either: -.. code-block:: sh +- Build only the OpenMC executable (using CMake). +- Build both the OpenMC executable and Python package (using pip). + +1. Building the OpenMC Executable Only (CMake) +============================================== + +If you only need the OpenMC executable without Python bindings, you can build it using +the following steps. You will need `git `_, a modern C++ compiler, +`CMake `_, and `HDF5 `_ installed: + +.. code-block:: bash git clone --recurse-submodules https://github.com/openmc-dev/openmc.git cd openmc - mkdir build && cd build - cmake .. - make - sudo make install + python -m pip install . -This will build an executable named ``openmc`` and install it (by default in -/usr/local/bin). If you do not have administrator privileges, the cmake command -should specify an installation directory where you have write access, e.g. +The easiest way to install it is using `pip `_. +This `pip` command will install the `openmc` Python package and compile an executable named ``openmc`` +and install it (by default in the bin folder of the Python package directory). .. code-block:: sh cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local .. + make + make install -The :mod:`openmc` Python package must be installed separately. The easiest way -to install it is using `pip `_. -From the root directory of the OpenMC repository, run: +2. Building the OpenMC Executable with Python Support (pip) +=========================================================== + +If you also want to install the OpenMC Python package,you can use +`pip `_ to build both the executable and +the Python package at the same time. This requires a Python environment +and pip installed. From the root directory of the OpenMC repository, run: .. code-block:: sh python -m pip install . +This will build the ``openmc`` executable and install it along with the Python bindings. +There is no need to manually run ``cmake`` or ``make``, as the pip command handles +both the C++ and Python build processes. + +Custom Build Options +~~~~~~~~~~~~~~~~~~~~ + +If you need to customize the build options (e.g., enabling MPI, DAGMC, or LibMesh), +you can pass CMake arguments using the ``SKBUILD_CMAKE_ARGS`` environment variable +before running pip. For example: + +.. code-block:: bash + + export SKBUILD_CMAKE_ARGS="-DOPENMC_USE_MPI=on;-DOPENMC_USE_DAGMC=on" + python -m pip install ".[test,depletion-mpi]" + +This allows you to configure your build just like you would with CMake. + By default, OpenMC will be built with multithreading support. To build -distributed-memory parallel versions of OpenMC using MPI or to configure other -options, directions can be found in the :ref:`detailed installation instructions +distributed-memory parallel versions of OpenMC using MPI the above command can be run. +There are other options that can be set, more details can be found in the :ref:`detailed installation instructions `. diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index d294770e0e0..576136449b7 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -154,9 +154,9 @@ feature can be used to access the installed packages. .. _install_source: ----------------------- -Installing from Source ----------------------- +-------------------------------- +Compiling from source with CMake +-------------------------------- .. _prerequisites: @@ -345,6 +345,8 @@ Note that first a build directory is created as a subdirectory of the source directory. The Makefile in the top-level directory will automatically perform an out-of-source build with default options. +.. _cmake_arguemnts: + CMakeLists.txt Options ++++++++++++++++++++++ @@ -480,29 +482,72 @@ To run the test suite, you will first need to download a pre-generated cross section library along with windowed multipole data. Please refer to our :ref:`devguide_tests` documentation for further details. ---------------------- -Installing Python API ---------------------- +----------------------------------------------- +Installing Python API and compiling from source +----------------------------------------------- If you installed OpenMC using :ref:`Conda `, no further steps are -necessary in order to use OpenMC's :ref:`Python API `. However, if -you are :ref:`installing from source `, the Python API is not -installed by default when ``make install`` is run because in many situations it -doesn't make sense to install a Python package in the same location as the -``openmc`` executable (for example, if you are installing the package into a -`virtual environment `_). The -easiest way to install the :mod:`openmc` Python package is to use pip_, which is -included by default in Python 3.4+. From the root directory of the OpenMC -distribution/repository, run: +necessary to use OpenMC's :ref:`Python API `. However, if you are +:ref:`installing from source `, the Python API is not installed +by default when building OpenMC because, in many cases, it doesn't make sense to +install a Python package in the same location as the OpenMC executable (for example, +if you are using a virtual environment `virtualenv `_). -.. code-block:: sh +To install OpenMC and its Python API, the recommended method is to use pip, +which includes both the Python package and the OpenMC executable. +From the root directory of the OpenMC repository, run: + +.. code-block:: bash + + python -m pip install ".[test,depletion-mpi]" + +This command installs both the OpenMC executable and the Python API together. +There's no need to manually run ``cmake`` or ``make install`` as pip automatically +handles the build process, including any CMake configuration. + +Custom Build Options +==================== + +If you need to customize the build (for example, to enable MPI, DAGMC, or LibMesh), +you can pass the necessary CMake arguments through the ``SKBUILD_CMAKE_ARGS`` +environment variable before running pip install: + +.. code-block:: bash + + export SKBUILD_CMAKE_ARGS="-DOPENMC_USE_MPI=on;-DOPENMC_USE_DAGMC=on" + python -m pip install ".[test,depletion-mpi]" + +Alternatively, pip provides additional ways to configure the build using +``--config-settings`` or ``-C``: - python -m pip install . +.. code-block:: bash + + python -m pip install . --config-settings=cmake.args="-DOPENMC_USE_MPI=ON;-DOPENMC_USE_MCPL=ON" pip will first check that all :ref:`required third-party packages ` have been installed, and if they are not present, they will be installed by downloading the appropriate packages from the Python -Package Index (`PyPI `_). +Package Index (`PyPI `_). The pip command will also compile +an executable named ``openmc`` and install it (by default in the bin folder of +the Python package directory). + +Passing CMake arguments via pip +-------------------------------- + +If you need to pass CMake options to the build process, you can do so by +running pip install with some additional options. All the CMake arguments +covered in the :ref:`CMakeLists.txt Options` are supported. +For example, to build OpenMC with MPI support, you can run: + +.. code-block:: sh + + python -m pip install . --config-settings=cmake.args=-DOPENMC_USE_MPI=ON + +To build OpenMC with DAGMC support two CMake arguments are needed, you can run: + +.. code-block:: sh + + python -m pip install . --config-settings=cmake.args="-DOPENMC_USE_DAGMC=ON;DDAGMC_ROOT=/path/to/dagmc/installation" Installing in "Development" Mode -------------------------------- diff --git a/openmc/__init__.py b/openmc/__init__.py index bb972b4e6ad..2f562b8f6dc 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -1,3 +1,5 @@ +import os +import glob import importlib.metadata from openmc.arithmetic import * from openmc.bounding_box import * @@ -43,3 +45,61 @@ __version__ = importlib.metadata.version("openmc") + +try: + OPENMC_CORE_BASE_PATH = os.path.join(__path__[0], "core") +except NameError: + OPENMC_CORE_BASE_PATH = None + +if not OPENMC_CORE_BASE_PATH or not os.path.exists(OPENMC_CORE_BASE_PATH): + import sysconfig + OPENMC_CORE_BASE_PATH = os.path.join(sysconfig.get_path("platlib"), "openmc", "core") + if not os.path.exists(OPENMC_CORE_BASE_PATH): + raise ImportError("OpenMC is not installed. Please run 'pip install openmc'.") + warnings.warn( + "It seems OpenMC is being run from its source directory. " + "This setup is not recommended as it may lead to unexpected behavior, " + "such as conflicts between source and installed versions. " + "Please run your script from outside the OpenMC source tree.", + RuntimeWarning + ) + +def get_paths(subdir, pattern="*", recursive=False): + """ + Helper function to return paths that match a given pattern within a subdirectory. + + Args: + subdir (str): The subdirectory within the 'core' directory. + pattern (str): The pattern to match files or directories. + recursive (bool): Whether to search recursively in subdirectories. + + Returns: + list: A list of matched paths. + """ + search_pattern = os.path.join(OPENMC_CORE_BASE_PATH, subdir, "**", pattern) if recursive else os.path.join(OPENMC_CORE_BASE_PATH, subdir, pattern) + return glob.glob(search_pattern, recursive=recursive) + +def get_include_path(): + """Return includes and include path for OpenMC headers.""" + include = get_paths("include", "*", recursive=True) + include_path = get_paths("include", "", recursive=False) + return include, include_path + +def get_core_libraries(): + """Return libraries and library paths for OpenMC.""" + lib = [lib_file for lib in ["lib", "lib64"] for lib_file in get_paths(lib, "libopenmc*", recursive=True)] + lib_path = [lib_file for lib in ["lib", "lib64"] for lib_file in get_paths(lib, "", recursive=False)] + return lib, lib_path + +def get_extra_libraries(): + """Return the extra libraries installed by auditwheel or delocate.""" + libs_path = os.path.join(__path__[0], ".dylibs") if sys.platform == "darwin" else os.path.normpath(os.path.join(__path__[0], "..", "openmc.libs")) + return (glob.glob(os.path.join(libs_path, "*")), libs_path) if os.path.exists(libs_path) else ([], []) + +# Setup variables +include, include_path = get_include_path() +lib, lib_path = get_core_libraries() +extra_lib, extra_lib_path = get_extra_libraries() + +# Export variables for easy access +__all__ = ["include", "include_path", "lib", "lib_path", "extra_lib", "extra_lib_path"] \ No newline at end of file diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 15642b42be3..5d51c0ac7a6 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -13,20 +13,12 @@ """ from ctypes import CDLL, c_bool, c_int -import importlib.resources import os -import sys - - -# Determine shared-library suffix -if sys.platform == 'darwin': - _suffix = 'dylib' -else: - _suffix = 'so' if os.environ.get('READTHEDOCS', None) != 'True': # Open shared library - _filename = importlib.resources.files(__name__) / f'libopenmc.{_suffix}' + import openmc + _filename = openmc.lib[0] _dll = CDLL(str(_filename)) # TODO: Remove str() when Python 3.12+ else: # For documentation builds, we don't actually have the shared library diff --git a/pyproject.toml b/pyproject.toml index 6e8ed798e78..7294c8bd400 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["setuptools", "setuptools-scm", "wheel"] -build-backend = "setuptools.build_meta" +requires = ["scikit-build-core", "setuptools-scm"] +build-backend = "scikit_build_core.build" [project] name = "openmc" @@ -58,13 +58,12 @@ Documentation = "https://docs.openmc.org" Repository = "https://github.com/openmc-dev/openmc" Issues = "https://github.com/openmc-dev/openmc/issues" -[tool.setuptools.packages.find] -include = ['openmc*'] -exclude = ['tests*'] - -[tool.setuptools.package-data] -"openmc.data.effective_dose" = ["**/*.txt"] -"openmc.data" = ["*.txt", "*.DAT", "*.json", "*.h5"] -"openmc.lib" = ["libopenmc.dylib", "libopenmc.so"] +# Scikit-Build Configuration +[tool.scikit-build] +build.verbose = true +logging.level = "INFO" +wheel.install-dir = "openmc" +wheel.packages = ["openmc"] +sdist.include = ["*.h5"] [tool.setuptools_scm] diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 1cc792f8d78..09cfe0727b5 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -4,48 +4,57 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): - # Create build directory and change to it - shutil.rmtree('build', ignore_errors=True) - os.mkdir('build') - os.chdir('build') - - # Build in debug mode by default with support for MCPL - cmake_cmd = ['cmake', '-DCMAKE_BUILD_TYPE=Debug', '-DOPENMC_USE_MCPL=on'] + # List to store the CMake arguments + cmake_args = ['-DCMAKE_BUILD_TYPE=Debug', '-DOPENMC_USE_MCPL=on'] # Turn off OpenMP if specified if not omp: - cmake_cmd.append('-DOPENMC_USE_OPENMP=off') + cmake_args.append('-DOPENMC_USE_OPENMP=off') # Use MPI wrappers when building in parallel if mpi: - cmake_cmd.append('-DOPENMC_USE_MPI=on') + cmake_args.append('-DOPENMC_USE_MPI=on') # Tell CMake to prefer parallel HDF5 if specified if phdf5: if not mpi: - raise ValueError('Parallel HDF5 must be used in ' - 'conjunction with MPI.') - cmake_cmd.append('-DHDF5_PREFER_PARALLEL=ON') + raise ValueError('Parallel HDF5 must be used in conjunction with MPI.') + cmake_args.append('-DHDF5_PREFER_PARALLEL=ON') else: - cmake_cmd.append('-DHDF5_PREFER_PARALLEL=OFF') + cmake_args.append('-DHDF5_PREFER_PARALLEL=OFF') if dagmc: - cmake_cmd.append('-DOPENMC_USE_DAGMC=ON') - cmake_cmd.append('-DOPENMC_USE_UWUW=ON') + cmake_args.append('-DOPENMC_USE_DAGMC=ON') + cmake_args.append('-DOPENMC_USE_UWUW=ON') dagmc_path = os.environ.get('HOME') + '/DAGMC' - cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + dagmc_path) + cmake_args.append('-DCMAKE_PREFIX_PATH=' + dagmc_path) if libmesh: - cmake_cmd.append('-DOPENMC_USE_LIBMESH=ON') + cmake_args.append('-DOPENMC_USE_LIBMESH=ON') libmesh_path = os.environ.get('HOME') + '/LIBMESH' - cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) + cmake_args.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) # Build in coverage mode for coverage testing - cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') + cmake_args.append('-DOPENMC_ENABLE_COVERAGE=on') - # Build and install - cmake_cmd.append('..') + # Set environment variable for SKBUILD + os.environ['SKBUILD_CMAKE_ARGS'] = ';'.join(cmake_args) + + # Run pip to build and install + pip_suffix = '--config-settings=cmake.args="' + ';'.join(cmake_args) + '"' + subprocess.check_call(['pip', '-v', 'install', '.[test,vtk,ci]', pip_suffix]) + + # Using standard CMake method + # Create build directory and change to it + shutil.rmtree('build', ignore_errors=True) + os.mkdir('build') + os.chdir('build') + + # Add CMake arguments for standard method + cmake_cmd = ['cmake', '..'] + cmake_args print(' '.join(cmake_cmd)) + + # Run CMake and build subprocess.check_call(cmake_cmd) subprocess.check_call(['make', '-j4']) subprocess.check_call(['sudo', 'make', 'install']) diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index d8a3a7600e4..6799ed842c2 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -42,8 +42,6 @@ if [[ $MPI == 'y' ]]; then pip install --no-binary=h5py h5py fi -# Build and install OpenMC executable +# Build and install OpenMC python tools/ci/gha-install.py -# Install Python API in editable mode -pip install -e .[test,vtk,ci] diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index c7c634ffa33..263ff24f5ac 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -14,5 +14,11 @@ if [[ $EVENT == 'y' ]]; then args="${args} --event " fi +# Rename openmc to openmc-test +mv openmc openmc-test + # Run regression and unit tests pytest --cov=openmc -v $args tests + +# Rename to openmc back +mv openmc-test openmc diff --git a/tools/ci/manylinux.dockerfile b/tools/ci/manylinux.dockerfile new file mode 100644 index 00000000000..218d771a6ed --- /dev/null +++ b/tools/ci/manylinux.dockerfile @@ -0,0 +1,438 @@ +# ---------------------------------------------------------------------------- +# Dockerfile for building the OpenMC project with support for various dependencies +# and configurations. This Dockerfile allows you to build OpenMC with support +# for GCC or OpenMPI compilers, along with several libraries like NJOY2016, HDF5, +# NetCDF, MOAB, EMBREE, Double Down, DAGMC, NCrystal, PyBind11, Xtensor, Vectfit, +# libMesh, and MCPL. Each of these dependencies is installed from their respective +# repositories and tags. + +# The build process is split into stages: +# 1. Base Stage: Sets up a Manylinux base image and installs necessary dependencies. +# 2. Compiler Configuration: Defines the compilers (GCC or OpenMPI) to be used. +# 3. Dependencies Stage: Downloads and builds all external dependencies. +# 4. Python Dependencies Stage: Downloads and installs Python dependencies. +# 5. OpenMC Stage: Copies OpenMC source code, build wheel with specific +# flags and installs it in the container. Then, runs tests. + +# Arguments and environment variables can be customized for different compiler and +# dependency versions. +# +# To build the Docker image, use the following command from the repository's root: +# docker build -t openmc -f tools/ci/manylinux.dockerfile . +# +# For more information about each step, refer to the inline comments. +# ---------------------------------------------------------------------------- + +# Configure base image +ARG MANYLINUX_IMAGE=manylinux_2_28_x86_64 + +# Configure Compiler to use (gcc or openmpi) +ARG COMPILER="gcc" + +# Configure Python ABI to use +ARG Python_ABI="cp312-cp312" + +# OpenMC options +ARG OPENMC_USE_OPENMP="ON" +ARG OPENMC_BUILD_TESTS="ON" +ARG OPENMC_ENABLE_PROFILE="OFF" +ARG OPENMC_ENABLE_COVERAGE="OFF" +ARG OPENMC_USE_DAGMC="ON" +ARG OPENMC_USE_LIBMESH="ON" +ARG OPENMC_USE_MCPL="ON" +ARG OPENMC_USE_NCRYSTAL="ON" +ARG OPENMC_USE_UWUW="OFF" + +# Configure dependencies tags +ARG GSL_LITE_TAG="v0.41.0" +ARG XTL_TAG="0.7.7" +ARG XTENSOR_TAG="0.25.0" +ARG XTENSOR_BLAS_TAG="0.21.0" +ARG CATCH2_TAG="v3.7.1" +ARG NJOY2016_TAG="2016.76" +ARG HDF5_TAG="hdf5_1.14.4.3" +ARG NETCDF_TAG="v4.9.2" +ARG MOAB_TAG="5.5.1" +ARG EMBREE_TAG="v4.3.3" +ARG DD_TAG="v1.1.0" +ARG DAGMC_TAG="v3.2.4" +ARG NCrystal_TAG="v3.9.7" +ARG PYBIND_TAG="v2.13.6" +ARG XTENSOR_PYTHON_TAG="0.27.0" +ARG VECTFIT_TAG="master" +ARG LIBMESH_TAG="v1.7.7" +ARG MCPL_TAG="v1.6.2" + + +# Base stage +FROM quay.io/pypa/${MANYLINUX_IMAGE} AS base + +# Set timezone +ENV TZ=America/Chicago +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# Set Home directory +ENV HOME=/root +WORKDIR $HOME + +# Setup Epel repository and install build dependencies +RUN yum install -y epel-release && \ + yum config-manager --enable epel && \ + yum install -y \ + wget \ + git \ + gcc \ + gcc-c++ \ + gcc-gfortran \ + make \ + python3.12-devel \ + zlib-devel \ + curl-devel \ + eigen3-devel \ + lapack-devel \ + libpng-devel \ + pugixml-devel \ + fmt-devel && \ + yum clean all + +# Set up environment variables for shared libraries +ENV LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64:$LD_LIBRARY_PATH + +# Compiler configuration stage: gcc +FROM base AS compiler-gcc + +ENV CC=gcc +ENV CXX=g++ +ENV FC=gfortran +ENV F77=gfortran + + +# Compiler configuration stage: openmpi +FROM base AS compiler-openmpi + +# Install OpenMPI +RUN yum install -y \ + openmpi-devel && \ + yum clean all + +ENV CC=mpicc +ENV CXX=mpicxx +ENV FC=mpif90 +ENV F77=mpif77 + +# Set up OpenMPI environment variables +ENV PATH=/usr/lib64/openmpi/bin:$PATH +ENV LD_LIBRARY_PATH=/usr/lib64/openmpi/lib:$LD_LIBRARY_PATH + + +# Dependencies stage +FROM compiler-${COMPILER} AS dependencies + +ARG COMPILER + +# Build and install gsl-lite +ARG GSL_LITE_TAG +RUN git clone --depth 1 -b ${GSL_LITE_TAG} https://github.com/gsl-lite/gsl-lite.git gsl-lite && \ + cd gsl-lite && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf gsl-lite + +# Build and install xtl +ARG XTL_TAG +RUN git clone --depth 1 -b ${XTL_TAG} https://github.com/xtensor-stack/xtl.git xtl && \ + cd xtl && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf xtl + +# Build and install xtensor +ARG XTENSOR_TAG +RUN git clone --depth 1 -b ${XTENSOR_TAG} https://github.com/xtensor-stack/xtensor.git xtensor && \ + cd xtensor && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf xtensor + +# Build and install xtensor-blas +ARG XTENSOR_BLAS_TAG +RUN git clone --depth 1 -b ${XTENSOR_BLAS_TAG} https://github.com/xtensor-stack/xtensor-blas.git xtensor-blas && \ + cd xtensor-blas && \ + mkdir build && cd build && \ + cmake .. && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf xtensor-blas + +# Build and install Catch2 +ARG CATCH2_TAG +RUN git clone --depth 1 -b ${CATCH2_TAG} https://github.com/catchorg/Catch2.git catch2 && \ + cd catch2 && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf catch2 + +# Build and install NJOY2016 +ARG NJOY2016_TAG +RUN git clone --depth 1 -b ${NJOY2016_TAG} https://github.com/njoy/njoy2016.git njoy && \ + cd njoy && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -Dstatic=ON && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf njoy + +# Build and install HDF5 +ARG HDF5_TAG +RUN git clone --depth 1 -b ${HDF5_TAG} https://github.com/HDFGroup/hdf5.git hdf5 && \ + cd hdf5 && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DHDF5_ENABLE_PARALLEL=$([ ${COMPILER} == "openmpi" ] && echo "ON" || echo "OFF") \ + -DHDF5_BUILD_HL_LIB=ON \ + -DBUILD_SHARED_LIBS=ON && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf hdf5 + +# Build and install NetCDF +ARG NETCDF_TAG +RUN git clone --depth 1 -b ${NETCDF_TAG} https://github.com/Unidata/netcdf-c.git netcdf && \ + cd netcdf && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DBUILD_SHARED_LIBS=ON \ + -DENABLE_DAP=ON \ + -DENABLE_TESTS=OFF && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf netcdf + +# Build and install MOAB +ARG MOAB_TAG +RUN git clone --depth 1 -b ${MOAB_TAG} https://bitbucket.org/fathomteam/moab.git moab && \ + cd moab && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DENABLE_MPI=$([ ${COMPILER} == "openmpi" ] && echo "ON" || echo "OFF") \ + -DENABLE_HDF5=ON \ + -DHDF5_ROOT=/usr/local \ + -DENABLE_NETCDF=ON \ + -DNETCDF_ROOT=/usr/local \ + -DBUILD_SHARED_LIBS=ON \ + -DENABLE_BLASLAPACK=OFF \ + -DENABLE_PYMOAB=OFF && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf moab + +# Build and install Embree +ARG EMBREE_TAG +RUN git clone --depth 1 -b ${EMBREE_TAG} https://github.com/embree/embree.git embree && \ + cd embree && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DEMBREE_TASKING_SYSTEM=INTERNAL \ + -DEMBREE_ISPC_SUPPORT=OFF \ + -DEMBREE_TUTORIALS=OFF && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf embree + +# Build and install Double Down +ARG DD_TAG +RUN git clone --depth 1 -b ${DD_TAG} https://github.com/pshriwise/double-down.git dd && \ + cd dd && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf dd + +# Build and install DAGMC +ARG DAGMC_TAG +RUN git clone --depth 1 -b ${DAGMC_TAG} https://github.com/svalinn/DAGMC.git dagmc && \ + cd dagmc && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DMOAB_DIR=/usr/local \ + -Ddd_ROOT=/usr/local \ + -DBUILD_TALLY=ON \ + -DBUILD_UWUW=ON \ + -DDOUBLE_DOWN=ON \ + -DBUILD_STATIC_LIBS=OFF \ + -DBUILD_RPATH=OFF && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf dagmc + +# Build and install libMesh +ARG LIBMESH_TAG +RUN git clone --depth 1 -b ${LIBMESH_TAG} https://github.com/libMesh/libmesh.git libmesh && \ + cd libmesh && \ + git submodule update --init --recursive && \ + mkdir build && cd build && \ + export METHODS="opt" && \ + ../configure \ + $([ ${COMPILER} = 'openmpi' ] && echo '--enable-mpi' || echo '--disable-mpi') \ + --prefix=/usr/local \ + --enable-exodus \ + --disable-netcdf-4 \ + --disable-eigen \ + --disable-lapack && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf libmesh + +# Build and install MCPL +ARG MCPL_TAG +RUN git clone --depth 1 -b ${MCPL_TAG} https://github.com/mctools/mcpl.git mcpl && \ + cd mcpl && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf mcpl + +# Download and extract HDF5 data +RUN wget -q -O - https://anl.box.com/shared/static/teaup95cqv8s9nn56hfn7ku8mmelr95p.xz | tar -C $HOME -xJ +ENV OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml + +# Download and extract ENDF/B-VII.1 distribution +RUN wget -q -O - https://anl.box.com/shared/static/4kd2gxnf4gtk4w1c8eua5fsua22kvgjb.xz | tar -C $HOME -xJ +ENV OPENMC_ENDF_DATA=$HOME/endf-b-vii.1 + + +# Python dependencies stage +FROM dependencies AS python-dependencies + +ARG Python_ABI + +# Use Python from manylinux as the default Python +ENV PYTHONHOME="/opt/python/${Python_ABI}" +ENV PATH="/opt/python/${Python_ABI}/bin:${PATH}" + +# Build and install NCrystal +ARG NCrystal_TAG +RUN git clone --depth 1 -b ${NCrystal_TAG} https://github.com/mctools/ncrystal.git ncrystal && \ + cd ncrystal && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DBUILD_SHARED_LIBS=ON \ + -DNCRYSTAL_NOTOUCH_CMAKE_BUILD_TYPE=ON \ + -DNCRYSTAL_MODIFY_RPATH=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DNCRYSTAL_ENABLE_EXAMPLES=OFF \ + -DNCRYSTAL_ENABLE_SETUPSH=OFF \ + -DNCRYSTAL_ENABLE_DATA=EMBED \ + -DPython3_EXECUTABLE=$(which python) && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf ncrystal + +# Build and install pybind +ARG PYBIND_TAG +RUN git clone --depth 1 -b ${PYBIND_TAG} https://github.com/pybind/pybind11.git pybind11 && \ + cd pybind11 && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local && \ + make -j$(nproc) && make install && \ + cd .. && \ + python -m pip install . && \ + cd .. && \ + rm -rf pybind11 + +# Build and install xtensor-python +ARG XTENSOR_PYTHON_TAG +RUN git clone --depth 1 -b ${XTENSOR_PYTHON_TAG} https://github.com/xtensor-stack/xtensor-python.git xtensor-python && \ + cd xtensor-python && \ + mkdir build && cd build && \ + python -m pip install numpy && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DNUMPY_INCLUDE_DIRS=$(python -c "import numpy; print(numpy.get_include())") && \ + make -j$(nproc) && make install && \ + cd ../.. && \ + rm -rf xtensor-python + +# Build and install vectfit +ARG VECTFIT_TAG +RUN git clone --depth 1 -b ${VECTFIT_TAG} https://github.com/liangjg/vectfit.git vectfit && \ + cd vectfit && \ + python -m pip install . && \ + cd .. && \ + rm -rf vectfit + + +# OpenMC stage +FROM python-dependencies AS openmc + +ARG COMPILER +ARG Python_ABI +ARG OPENMC_USE_OPENMP +ARG OPENMC_BUILD_TESTS +ARG OPENMC_ENABLE_PROFILE +ARG OPENMC_ENABLE_COVERAGE +ARG OPENMC_USE_DAGMC +ARG OPENMC_USE_LIBMESH +ARG OPENMC_USE_MCPL +ARG OPENMC_USE_NCRYSTAL +ARG OPENMC_USE_UWUW + +# Copy OpenMC source to docker image +COPY . $HOME/openmc + +# Configure SKBUILD CMake arguments +RUN export SKBUILD_CMAKE_ARGS="-DOPENMC_USE_MPI=$([ ${COMPILER} == 'openmpi' ] && echo 'ON' || echo 'OFF'); \ + -DOPENMC_USE_OPENMP=${OPENMC_USE_OPENMP}; \ + -DOPENMC_BUILD_TESTS=${OPENMC_BUILD_TESTS}; \ + -DOPENMC_ENABLE_PROFILE=${OPENMC_ENABLE_PROFILE}; \ + -DOPENMC_ENABLE_COVERAGE=${OPENMC_ENABLE_COVERAGE}; \ + -DOPENMC_USE_DAGMC=${OPENMC_USE_DAGMC}; \ + -DOPENMC_USE_LIBMESH=${OPENMC_USE_LIBMESH}; \ + -DOPENMC_USE_MCPL=${OPENMC_USE_MCPL}; \ + -DOPENMC_USE_NCRYSTAL=${OPENMC_USE_NCRYSTAL}; \ + -DOPENMC_USE_UWUW=${OPENMC_USE_UWUW}" && \ + cd $HOME/openmc && \ + python -m build . -w + +# Install OpenMC wheel +RUN python -m pip install \ + "$(echo $HOME/openmc/dist/*.whl)[$([ ${COMPILER} == 'openmpi' ] && echo 'depletion-mpi,')test,ci,vtk]" + +# Test OpenMC +RUN cd $HOME/openmc && \ + eval $(ncrystal-config --setup) && \ + nctool --test && \ + pytest --cov=openmc -v $([ ${COMPILER} == 'openmpi' ] && echo '--mpi') --event tests + +# Repair wheel +RUN auditwheel repair $HOME/openmc/dist/openmc-*.whl -w $HOME/openmc/dist/ + +# Test repaired wheel +RUN python -m pip uninstall openmc -y && \ + python -m pip install $HOME/openmc/dist/*manylinux**.whl && \ + openmc --version \ No newline at end of file