diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5973c3cd48d..5214aed9901 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,6 +98,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 - name: Apt dependencies shell: bash @@ -149,9 +150,7 @@ jobs: - name: test shell: bash - run: | - CTEST_OUTPUT_ON_FAILURE=1 make test -C $GITHUB_WORKSPACE/build/ - $GITHUB_WORKSPACE/tools/ci/gha-script.sh + run: $GITHUB_WORKSPACE/tools/ci/gha-script.sh - name: after_success shell: bash 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..bd9efb69646 --- /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.18.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/.readthedocs.yaml b/.readthedocs.yaml index b7b69f9fe59..bcbea55994e 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,9 +1,14 @@ version: 2 build: - os: "ubuntu-20.04" + os: "ubuntu-22.04" tools: python: "3.10" + apt_packages: + - g++ + - cmake + - libhdf5-dev + - libpng-dev sphinx: configuration: docs/source/conf.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 575e45373ae..36fb0fada6c 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 version numbers @@ -36,6 +36,17 @@ option(OPENMC_USE_MCPL "Enable MCPL" option(OPENMC_USE_NCRYSTAL "Enable support for NCrystal scattering" OFF) option(OPENMC_USE_UWUW "Enable UWUW" OFF) +message(STATUS "OPENMC_USE_OPENMP ${OPENMC_USE_OPENMP}") +message(STATUS "OPENMC_BUILD_TESTS ${OPENMC_BUILD_TESTS}") +message(STATUS "OPENMC_ENABLE_PROFILE ${OPENMC_ENABLE_PROFILE}") +message(STATUS "OPENMC_ENABLE_COVERAGE ${OPENMC_ENABLE_COVERAGE}") +message(STATUS "OPENMC_USE_DAGMC ${OPENMC_USE_DAGMC}") +message(STATUS "OPENMC_USE_LIBMESH ${OPENMC_USE_LIBMESH}") +message(STATUS "OPENMC_USE_MPI ${OPENMC_USE_MPI}") +message(STATUS "OPENMC_USE_MCPL ${OPENMC_USE_MCPL}") +message(STATUS "OPENMC_USE_NCRYSTAL ${OPENMC_USE_NCRYSTAL}") +message(STATUS "OPENMC_USE_UWUW ${OPENMC_USE_UWUW}") + # Warnings for deprecated options foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh") if(DEFINED ${OLD_OPT}) @@ -67,6 +78,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 #=============================================================================== @@ -310,23 +324,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 #=============================================================================== @@ -465,7 +504,7 @@ endif() target_include_directories(libopenmc PUBLIC - $ + $ $ ${HDF5_INCLUDE_DIRS} ) @@ -473,6 +512,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 $) @@ -581,15 +628,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 @@ -598,17 +646,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 4a94e3c0b11..f4c011310c8 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} --depth=1 ${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 b73218af0dc..00000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,48 +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 -include vendor/gsl-lite/include/gsl/gsl -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 b3b901de427..aa276bb7eab 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -5,6 +5,9 @@ find_package(gsl-lite REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../gsl-lite) 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() @@ -23,7 +26,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 d6a3c6ba4f4..3b8cb2044b1 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 @@ -116,15 +116,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: http://git-scm.com/ .. _GitHub: https://github.com/ diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 323cd7fd48d..f56c7b727f6 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -149,38 +149,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 1c0b7fa5b32..f9cbfa6f4dd 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -175,9 +175,9 @@ feature can be used to access the installed packages. .. _install_source: ----------------------- -Installing from Source ----------------------- +-------------------------------- +Compiling from source with CMake +-------------------------------- .. _prerequisites: @@ -367,6 +367,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 ++++++++++++++++++++++ @@ -505,29 +507,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="-OPENMC_USE_MPI=ON;-OPENMC_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 deleted file mode 100644 index 566d287068f..00000000000 --- a/openmc/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -import importlib.metadata -from openmc.arithmetic import * -from openmc.bounding_box import * -from openmc.cell import * -from openmc.checkvalue import * -from openmc.mesh import * -from openmc.element import * -from openmc.geometry import * -from openmc.nuclide import * -from openmc.macroscopic import * -from openmc.material import * -from openmc.plots import * -from openmc.region import * -from openmc.volume import * -from openmc.weight_windows import * -from openmc.surface import * -from openmc.universe import * -from openmc.source import * -from openmc.settings import * -from openmc.lattice import * -from openmc.filter import * -from openmc.filter_expansion import * -from openmc.trigger import * -from openmc.tally_derivative import * -from openmc.tallies import * -from openmc.mgxs_library import * -from openmc.executor import * -from openmc.statepoint import * -from openmc.summary import * -from openmc.particle_restart import * -from openmc.mixin import * -from openmc.plotter import * -from openmc.search import * -from openmc.polynomial import * -from openmc.tracks import * -from .config import * - -# Import a few names from the model module -from openmc.model import Model - -from . import examples - - -__version__ = importlib.metadata.version("openmc") diff --git a/openmc/data/compton_profiles.h5 b/openmc/data/compton_profiles.h5 deleted file mode 100644 index 298ed0340e6..00000000000 Binary files a/openmc/data/compton_profiles.h5 and /dev/null differ diff --git a/openmc/data/density_effect.h5 b/openmc/data/density_effect.h5 deleted file mode 100644 index 77d973517c2..00000000000 Binary files a/openmc/data/density_effect.h5 and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index d0419d550f4..5d1837bec4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,15 @@ [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta" +requires = ["scikit-build-core"] +build-backend = "scikit_build_core.build" [project] name = "openmc" +version = "0.15.1-dev" authors = [ {name = "The OpenMC Development Team", email = "openmc@anl.gov"}, ] -description = "OpenMC" -version = "0.15.1-dev" -requires-python = ">=3.10" +description = "OpenMC is a community-developed Monte Carlo neutron and photon transport code." +readme = "README.md" license = {file = "LICENSE"} classifiers = [ "Development Status :: 4 - Beta", @@ -25,6 +25,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ] +requires-python = ">=3.10" dependencies = [ "numpy", "h5py", @@ -58,20 +59,10 @@ 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*', 'scripts*'] -exclude = ['tests*'] - -[tool.setuptools.package-data] -"openmc.data.effective_dose" = ["**/*.txt"] -"openmc.data" = ["*.txt", "*.DAT", "*.json", "*.h5"] -"openmc.lib" = ["libopenmc.dylib", "libopenmc.so"] - -[project.scripts] -openmc-ace-to-hdf5 = "scripts.openmc_ace_to_hdf5:main" -openmc-plot-mesh-tally = "scripts.openmc_plot_mesh_tally:main" -openmc-track-combine = "scripts.openmc_track_combine:main" -openmc-track-to-vtk = "scripts.openmc_track_to_vtk:main" -openmc-update-inputs = "scripts.openmc_update_inputs:main" -openmc-update-mgxs = "scripts.openmc_update_mgxs:main" -openmc-voxel-to-vtk = "scripts.openmc_voxel_to_vtk:main" +# Scikit-Build Configuration +[tool.scikit-build] +build.verbose = true +logging.level = "INFO" +wheel.install-dir = "openmc" +wheel.packages = ["src/openmc"] +sdist.include = ["*.h5"] diff --git a/src/openmc/__init__.py b/src/openmc/__init__.py new file mode 100644 index 00000000000..9c95c4f0ea2 --- /dev/null +++ b/src/openmc/__init__.py @@ -0,0 +1,87 @@ +import os +import glob +import importlib.metadata +from openmc.arithmetic import * +from openmc.bounding_box import * +from openmc.cell import * +from openmc.checkvalue import * +from openmc.mesh import * +from openmc.element import * +from openmc.geometry import * +from openmc.nuclide import * +from openmc.macroscopic import * +from openmc.material import * +from openmc.plots import * +from openmc.region import * +from openmc.volume import * +from openmc.weight_windows import * +from openmc.surface import * +from openmc.universe import * +from openmc.source import * +from openmc.settings import * +from openmc.lattice import * +from openmc.filter import * +from openmc.filter_expansion import * +from openmc.trigger import * +from openmc.tally_derivative import * +from openmc.tallies import * +from openmc.mgxs_library import * +from openmc.executor import * +from openmc.statepoint import * +from openmc.summary import * +from openmc.particle_restart import * +from openmc.mixin import * +from openmc.plotter import * +from openmc.search import * +from openmc.polynomial import * +from openmc.tracks import * +from .config import * + +# Import a few names from the model module +from openmc.model import Model + +from . import examples + + +__version__ = importlib.metadata.version("openmc") + +def get_core_path(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. + """ + path = os.path.join(__path__[0], "core", subdir) + search_pattern = os.path.join(path, "**", pattern) if recursive else os.path.join(path, pattern) + return glob.glob(search_pattern, recursive=recursive) if os.path.exists(path) else [] + +def get_include_path(): + """Return includes and include path for OpenMC headers.""" + include = get_core_path("include", "*", recursive=True) + include_path = get_core_path("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_core_path(lib, "libopenmc*", recursive=True)] + lib_path = [lib_file for lib in ["lib", "lib64"] for lib_file in get_core_path(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/_xml.py b/src/openmc/_xml.py similarity index 100% rename from openmc/_xml.py rename to src/openmc/_xml.py diff --git a/openmc/arithmetic.py b/src/openmc/arithmetic.py similarity index 100% rename from openmc/arithmetic.py rename to src/openmc/arithmetic.py diff --git a/openmc/bounding_box.py b/src/openmc/bounding_box.py similarity index 100% rename from openmc/bounding_box.py rename to src/openmc/bounding_box.py diff --git a/openmc/cell.py b/src/openmc/cell.py similarity index 100% rename from openmc/cell.py rename to src/openmc/cell.py diff --git a/openmc/checkvalue.py b/src/openmc/checkvalue.py similarity index 100% rename from openmc/checkvalue.py rename to src/openmc/checkvalue.py diff --git a/openmc/cmfd.py b/src/openmc/cmfd.py similarity index 100% rename from openmc/cmfd.py rename to src/openmc/cmfd.py diff --git a/openmc/config.py b/src/openmc/config.py similarity index 100% rename from openmc/config.py rename to src/openmc/config.py diff --git a/openmc/data/BREMX.DAT b/src/openmc/data/BREMX.DAT similarity index 100% rename from openmc/data/BREMX.DAT rename to src/openmc/data/BREMX.DAT diff --git a/openmc/data/__init__.py b/src/openmc/data/__init__.py similarity index 100% rename from openmc/data/__init__.py rename to src/openmc/data/__init__.py diff --git a/openmc/data/ace.py b/src/openmc/data/ace.py similarity index 100% rename from openmc/data/ace.py rename to src/openmc/data/ace.py diff --git a/openmc/data/angle_distribution.py b/src/openmc/data/angle_distribution.py similarity index 100% rename from openmc/data/angle_distribution.py rename to src/openmc/data/angle_distribution.py diff --git a/openmc/data/angle_energy.py b/src/openmc/data/angle_energy.py similarity index 100% rename from openmc/data/angle_energy.py rename to src/openmc/data/angle_energy.py diff --git a/openmc/data/correlated.py b/src/openmc/data/correlated.py similarity index 100% rename from openmc/data/correlated.py rename to src/openmc/data/correlated.py diff --git a/openmc/data/data.py b/src/openmc/data/data.py similarity index 100% rename from openmc/data/data.py rename to src/openmc/data/data.py diff --git a/openmc/data/decay.py b/src/openmc/data/decay.py similarity index 100% rename from openmc/data/decay.py rename to src/openmc/data/decay.py diff --git a/openmc/data/effective_dose/__init__.py b/src/openmc/data/effective_dose/__init__.py similarity index 100% rename from openmc/data/effective_dose/__init__.py rename to src/openmc/data/effective_dose/__init__.py diff --git a/openmc/data/effective_dose/dose.py b/src/openmc/data/effective_dose/dose.py similarity index 100% rename from openmc/data/effective_dose/dose.py rename to src/openmc/data/effective_dose/dose.py diff --git a/openmc/data/effective_dose/icrp116/electrons.txt b/src/openmc/data/effective_dose/icrp116/electrons.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/electrons.txt rename to src/openmc/data/effective_dose/icrp116/electrons.txt diff --git a/openmc/data/effective_dose/icrp116/helium_ions.txt b/src/openmc/data/effective_dose/icrp116/helium_ions.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/helium_ions.txt rename to src/openmc/data/effective_dose/icrp116/helium_ions.txt diff --git a/openmc/data/effective_dose/icrp116/negative_muons.txt b/src/openmc/data/effective_dose/icrp116/negative_muons.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/negative_muons.txt rename to src/openmc/data/effective_dose/icrp116/negative_muons.txt diff --git a/openmc/data/effective_dose/icrp116/negative_pions.txt b/src/openmc/data/effective_dose/icrp116/negative_pions.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/negative_pions.txt rename to src/openmc/data/effective_dose/icrp116/negative_pions.txt diff --git a/openmc/data/effective_dose/icrp116/neutrons.txt b/src/openmc/data/effective_dose/icrp116/neutrons.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/neutrons.txt rename to src/openmc/data/effective_dose/icrp116/neutrons.txt diff --git a/openmc/data/effective_dose/icrp116/photons.txt b/src/openmc/data/effective_dose/icrp116/photons.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/photons.txt rename to src/openmc/data/effective_dose/icrp116/photons.txt diff --git a/openmc/data/effective_dose/icrp116/photons_kerma.txt b/src/openmc/data/effective_dose/icrp116/photons_kerma.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/photons_kerma.txt rename to src/openmc/data/effective_dose/icrp116/photons_kerma.txt diff --git a/openmc/data/effective_dose/icrp116/positive_muons.txt b/src/openmc/data/effective_dose/icrp116/positive_muons.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/positive_muons.txt rename to src/openmc/data/effective_dose/icrp116/positive_muons.txt diff --git a/openmc/data/effective_dose/icrp116/positive_pions.txt b/src/openmc/data/effective_dose/icrp116/positive_pions.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/positive_pions.txt rename to src/openmc/data/effective_dose/icrp116/positive_pions.txt diff --git a/openmc/data/effective_dose/icrp116/positrons.txt b/src/openmc/data/effective_dose/icrp116/positrons.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/positrons.txt rename to src/openmc/data/effective_dose/icrp116/positrons.txt diff --git a/openmc/data/effective_dose/icrp116/protons.txt b/src/openmc/data/effective_dose/icrp116/protons.txt similarity index 100% rename from openmc/data/effective_dose/icrp116/protons.txt rename to src/openmc/data/effective_dose/icrp116/protons.txt diff --git a/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py b/src/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py similarity index 100% rename from openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py rename to src/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py diff --git a/openmc/data/effective_dose/icrp74/neutrons.txt b/src/openmc/data/effective_dose/icrp74/neutrons.txt similarity index 100% rename from openmc/data/effective_dose/icrp74/neutrons.txt rename to src/openmc/data/effective_dose/icrp74/neutrons.txt diff --git a/openmc/data/effective_dose/icrp74/photons.txt b/src/openmc/data/effective_dose/icrp74/photons.txt similarity index 100% rename from openmc/data/effective_dose/icrp74/photons.txt rename to src/openmc/data/effective_dose/icrp74/photons.txt diff --git a/openmc/data/endf.py b/src/openmc/data/endf.py similarity index 100% rename from openmc/data/endf.py rename to src/openmc/data/endf.py diff --git a/openmc/data/energy_distribution.py b/src/openmc/data/energy_distribution.py similarity index 100% rename from openmc/data/energy_distribution.py rename to src/openmc/data/energy_distribution.py diff --git a/openmc/data/fission_energy.py b/src/openmc/data/fission_energy.py similarity index 100% rename from openmc/data/fission_energy.py rename to src/openmc/data/fission_energy.py diff --git a/openmc/data/function.py b/src/openmc/data/function.py similarity index 100% rename from openmc/data/function.py rename to src/openmc/data/function.py diff --git a/openmc/data/grid.py b/src/openmc/data/grid.py similarity index 100% rename from openmc/data/grid.py rename to src/openmc/data/grid.py diff --git a/openmc/data/half_life.json b/src/openmc/data/half_life.json similarity index 100% rename from openmc/data/half_life.json rename to src/openmc/data/half_life.json diff --git a/openmc/data/kalbach_mann.py b/src/openmc/data/kalbach_mann.py similarity index 100% rename from openmc/data/kalbach_mann.py rename to src/openmc/data/kalbach_mann.py diff --git a/openmc/data/laboratory.py b/src/openmc/data/laboratory.py similarity index 100% rename from openmc/data/laboratory.py rename to src/openmc/data/laboratory.py diff --git a/openmc/data/library.py b/src/openmc/data/library.py similarity index 100% rename from openmc/data/library.py rename to src/openmc/data/library.py diff --git a/openmc/data/mass_1.mas20.txt b/src/openmc/data/mass_1.mas20.txt similarity index 100% rename from openmc/data/mass_1.mas20.txt rename to src/openmc/data/mass_1.mas20.txt diff --git a/openmc/data/multipole.py b/src/openmc/data/multipole.py similarity index 100% rename from openmc/data/multipole.py rename to src/openmc/data/multipole.py diff --git a/openmc/data/nbody.py b/src/openmc/data/nbody.py similarity index 100% rename from openmc/data/nbody.py rename to src/openmc/data/nbody.py diff --git a/openmc/data/neutron.py b/src/openmc/data/neutron.py similarity index 100% rename from openmc/data/neutron.py rename to src/openmc/data/neutron.py diff --git a/openmc/data/njoy.py b/src/openmc/data/njoy.py similarity index 100% rename from openmc/data/njoy.py rename to src/openmc/data/njoy.py diff --git a/openmc/data/photon.py b/src/openmc/data/photon.py similarity index 100% rename from openmc/data/photon.py rename to src/openmc/data/photon.py diff --git a/openmc/data/product.py b/src/openmc/data/product.py similarity index 100% rename from openmc/data/product.py rename to src/openmc/data/product.py diff --git a/openmc/data/reaction.py b/src/openmc/data/reaction.py similarity index 100% rename from openmc/data/reaction.py rename to src/openmc/data/reaction.py diff --git a/openmc/data/resonance.py b/src/openmc/data/resonance.py similarity index 100% rename from openmc/data/resonance.py rename to src/openmc/data/resonance.py diff --git a/openmc/data/resonance_covariance.py b/src/openmc/data/resonance_covariance.py similarity index 100% rename from openmc/data/resonance_covariance.py rename to src/openmc/data/resonance_covariance.py diff --git a/openmc/data/thermal.py b/src/openmc/data/thermal.py similarity index 100% rename from openmc/data/thermal.py rename to src/openmc/data/thermal.py diff --git a/openmc/data/thermal_angle_energy.py b/src/openmc/data/thermal_angle_energy.py similarity index 100% rename from openmc/data/thermal_angle_energy.py rename to src/openmc/data/thermal_angle_energy.py diff --git a/openmc/data/uncorrelated.py b/src/openmc/data/uncorrelated.py similarity index 100% rename from openmc/data/uncorrelated.py rename to src/openmc/data/uncorrelated.py diff --git a/openmc/data/urr.py b/src/openmc/data/urr.py similarity index 100% rename from openmc/data/urr.py rename to src/openmc/data/urr.py diff --git a/openmc/deplete/__init__.py b/src/openmc/deplete/__init__.py similarity index 100% rename from openmc/deplete/__init__.py rename to src/openmc/deplete/__init__.py diff --git a/openmc/deplete/_matrix_funcs.py b/src/openmc/deplete/_matrix_funcs.py similarity index 100% rename from openmc/deplete/_matrix_funcs.py rename to src/openmc/deplete/_matrix_funcs.py diff --git a/openmc/deplete/abc.py b/src/openmc/deplete/abc.py similarity index 100% rename from openmc/deplete/abc.py rename to src/openmc/deplete/abc.py diff --git a/openmc/deplete/atom_number.py b/src/openmc/deplete/atom_number.py similarity index 100% rename from openmc/deplete/atom_number.py rename to src/openmc/deplete/atom_number.py diff --git a/openmc/deplete/chain.py b/src/openmc/deplete/chain.py similarity index 100% rename from openmc/deplete/chain.py rename to src/openmc/deplete/chain.py diff --git a/openmc/deplete/coupled_operator.py b/src/openmc/deplete/coupled_operator.py similarity index 100% rename from openmc/deplete/coupled_operator.py rename to src/openmc/deplete/coupled_operator.py diff --git a/openmc/deplete/cram.py b/src/openmc/deplete/cram.py similarity index 100% rename from openmc/deplete/cram.py rename to src/openmc/deplete/cram.py diff --git a/openmc/deplete/helpers.py b/src/openmc/deplete/helpers.py similarity index 100% rename from openmc/deplete/helpers.py rename to src/openmc/deplete/helpers.py diff --git a/openmc/deplete/independent_operator.py b/src/openmc/deplete/independent_operator.py similarity index 100% rename from openmc/deplete/independent_operator.py rename to src/openmc/deplete/independent_operator.py diff --git a/openmc/deplete/integrators.py b/src/openmc/deplete/integrators.py similarity index 100% rename from openmc/deplete/integrators.py rename to src/openmc/deplete/integrators.py diff --git a/openmc/deplete/microxs.py b/src/openmc/deplete/microxs.py similarity index 100% rename from openmc/deplete/microxs.py rename to src/openmc/deplete/microxs.py diff --git a/openmc/deplete/nuclide.py b/src/openmc/deplete/nuclide.py similarity index 100% rename from openmc/deplete/nuclide.py rename to src/openmc/deplete/nuclide.py diff --git a/openmc/deplete/openmc_operator.py b/src/openmc/deplete/openmc_operator.py similarity index 100% rename from openmc/deplete/openmc_operator.py rename to src/openmc/deplete/openmc_operator.py diff --git a/openmc/deplete/pool.py b/src/openmc/deplete/pool.py similarity index 100% rename from openmc/deplete/pool.py rename to src/openmc/deplete/pool.py diff --git a/openmc/deplete/reaction_rates.py b/src/openmc/deplete/reaction_rates.py similarity index 100% rename from openmc/deplete/reaction_rates.py rename to src/openmc/deplete/reaction_rates.py diff --git a/openmc/deplete/results.py b/src/openmc/deplete/results.py similarity index 100% rename from openmc/deplete/results.py rename to src/openmc/deplete/results.py diff --git a/openmc/deplete/stepresult.py b/src/openmc/deplete/stepresult.py similarity index 100% rename from openmc/deplete/stepresult.py rename to src/openmc/deplete/stepresult.py diff --git a/openmc/deplete/transfer_rates.py b/src/openmc/deplete/transfer_rates.py similarity index 100% rename from openmc/deplete/transfer_rates.py rename to src/openmc/deplete/transfer_rates.py diff --git a/openmc/dummy_comm.py b/src/openmc/dummy_comm.py similarity index 100% rename from openmc/dummy_comm.py rename to src/openmc/dummy_comm.py diff --git a/openmc/element.py b/src/openmc/element.py similarity index 100% rename from openmc/element.py rename to src/openmc/element.py diff --git a/openmc/examples.py b/src/openmc/examples.py similarity index 100% rename from openmc/examples.py rename to src/openmc/examples.py diff --git a/openmc/exceptions.py b/src/openmc/exceptions.py similarity index 100% rename from openmc/exceptions.py rename to src/openmc/exceptions.py diff --git a/openmc/executor.py b/src/openmc/executor.py similarity index 100% rename from openmc/executor.py rename to src/openmc/executor.py diff --git a/openmc/filter.py b/src/openmc/filter.py similarity index 100% rename from openmc/filter.py rename to src/openmc/filter.py diff --git a/openmc/filter_expansion.py b/src/openmc/filter_expansion.py similarity index 100% rename from openmc/filter_expansion.py rename to src/openmc/filter_expansion.py diff --git a/openmc/geometry.py b/src/openmc/geometry.py similarity index 100% rename from openmc/geometry.py rename to src/openmc/geometry.py diff --git a/openmc/lattice.py b/src/openmc/lattice.py similarity index 100% rename from openmc/lattice.py rename to src/openmc/lattice.py diff --git a/openmc/lib/__init__.py b/src/openmc/lib/__init__.py similarity index 89% rename from openmc/lib/__init__.py rename to src/openmc/lib/__init__.py index 9bb2efb38af..cd49665a5f3 100644 --- a/openmc/lib/__init__.py +++ b/src/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/openmc/lib/cell.py b/src/openmc/lib/cell.py similarity index 100% rename from openmc/lib/cell.py rename to src/openmc/lib/cell.py diff --git a/openmc/lib/core.py b/src/openmc/lib/core.py similarity index 100% rename from openmc/lib/core.py rename to src/openmc/lib/core.py diff --git a/openmc/lib/error.py b/src/openmc/lib/error.py similarity index 100% rename from openmc/lib/error.py rename to src/openmc/lib/error.py diff --git a/openmc/lib/filter.py b/src/openmc/lib/filter.py similarity index 100% rename from openmc/lib/filter.py rename to src/openmc/lib/filter.py diff --git a/openmc/lib/material.py b/src/openmc/lib/material.py similarity index 100% rename from openmc/lib/material.py rename to src/openmc/lib/material.py diff --git a/openmc/lib/math.py b/src/openmc/lib/math.py similarity index 100% rename from openmc/lib/math.py rename to src/openmc/lib/math.py diff --git a/openmc/lib/mesh.py b/src/openmc/lib/mesh.py similarity index 100% rename from openmc/lib/mesh.py rename to src/openmc/lib/mesh.py diff --git a/openmc/lib/nuclide.py b/src/openmc/lib/nuclide.py similarity index 100% rename from openmc/lib/nuclide.py rename to src/openmc/lib/nuclide.py diff --git a/openmc/lib/plot.py b/src/openmc/lib/plot.py similarity index 100% rename from openmc/lib/plot.py rename to src/openmc/lib/plot.py diff --git a/openmc/lib/settings.py b/src/openmc/lib/settings.py similarity index 100% rename from openmc/lib/settings.py rename to src/openmc/lib/settings.py diff --git a/openmc/lib/tally.py b/src/openmc/lib/tally.py similarity index 100% rename from openmc/lib/tally.py rename to src/openmc/lib/tally.py diff --git a/openmc/lib/weight_windows.py b/src/openmc/lib/weight_windows.py similarity index 100% rename from openmc/lib/weight_windows.py rename to src/openmc/lib/weight_windows.py diff --git a/openmc/macroscopic.py b/src/openmc/macroscopic.py similarity index 100% rename from openmc/macroscopic.py rename to src/openmc/macroscopic.py diff --git a/openmc/material.py b/src/openmc/material.py similarity index 100% rename from openmc/material.py rename to src/openmc/material.py diff --git a/openmc/mesh.py b/src/openmc/mesh.py similarity index 100% rename from openmc/mesh.py rename to src/openmc/mesh.py diff --git a/openmc/mgxs/__init__.py b/src/openmc/mgxs/__init__.py similarity index 100% rename from openmc/mgxs/__init__.py rename to src/openmc/mgxs/__init__.py diff --git a/openmc/mgxs/groups.py b/src/openmc/mgxs/groups.py similarity index 100% rename from openmc/mgxs/groups.py rename to src/openmc/mgxs/groups.py diff --git a/openmc/mgxs/library.py b/src/openmc/mgxs/library.py similarity index 100% rename from openmc/mgxs/library.py rename to src/openmc/mgxs/library.py diff --git a/openmc/mgxs/mdgxs.py b/src/openmc/mgxs/mdgxs.py similarity index 100% rename from openmc/mgxs/mdgxs.py rename to src/openmc/mgxs/mdgxs.py diff --git a/openmc/mgxs/mgxs.py b/src/openmc/mgxs/mgxs.py similarity index 100% rename from openmc/mgxs/mgxs.py rename to src/openmc/mgxs/mgxs.py diff --git a/openmc/mgxs_library.py b/src/openmc/mgxs_library.py similarity index 100% rename from openmc/mgxs_library.py rename to src/openmc/mgxs_library.py diff --git a/openmc/mixin.py b/src/openmc/mixin.py similarity index 100% rename from openmc/mixin.py rename to src/openmc/mixin.py diff --git a/openmc/model/__init__.py b/src/openmc/model/__init__.py similarity index 100% rename from openmc/model/__init__.py rename to src/openmc/model/__init__.py diff --git a/openmc/model/funcs.py b/src/openmc/model/funcs.py similarity index 100% rename from openmc/model/funcs.py rename to src/openmc/model/funcs.py diff --git a/openmc/model/model.py b/src/openmc/model/model.py similarity index 100% rename from openmc/model/model.py rename to src/openmc/model/model.py diff --git a/openmc/model/surface_composite.py b/src/openmc/model/surface_composite.py similarity index 100% rename from openmc/model/surface_composite.py rename to src/openmc/model/surface_composite.py diff --git a/openmc/model/triso.py b/src/openmc/model/triso.py similarity index 100% rename from openmc/model/triso.py rename to src/openmc/model/triso.py diff --git a/openmc/mpi.py b/src/openmc/mpi.py similarity index 100% rename from openmc/mpi.py rename to src/openmc/mpi.py diff --git a/openmc/nuclide.py b/src/openmc/nuclide.py similarity index 100% rename from openmc/nuclide.py rename to src/openmc/nuclide.py diff --git a/openmc/openmoc_compatible.py b/src/openmc/openmoc_compatible.py similarity index 100% rename from openmc/openmoc_compatible.py rename to src/openmc/openmoc_compatible.py diff --git a/openmc/particle_restart.py b/src/openmc/particle_restart.py similarity index 100% rename from openmc/particle_restart.py rename to src/openmc/particle_restart.py diff --git a/openmc/plots.py b/src/openmc/plots.py similarity index 100% rename from openmc/plots.py rename to src/openmc/plots.py diff --git a/openmc/plotter.py b/src/openmc/plotter.py similarity index 100% rename from openmc/plotter.py rename to src/openmc/plotter.py diff --git a/openmc/polynomial.py b/src/openmc/polynomial.py similarity index 100% rename from openmc/polynomial.py rename to src/openmc/polynomial.py diff --git a/openmc/region.py b/src/openmc/region.py similarity index 100% rename from openmc/region.py rename to src/openmc/region.py diff --git a/openmc/search.py b/src/openmc/search.py similarity index 100% rename from openmc/search.py rename to src/openmc/search.py diff --git a/openmc/settings.py b/src/openmc/settings.py similarity index 100% rename from openmc/settings.py rename to src/openmc/settings.py diff --git a/openmc/source.py b/src/openmc/source.py similarity index 100% rename from openmc/source.py rename to src/openmc/source.py diff --git a/openmc/statepoint.py b/src/openmc/statepoint.py similarity index 100% rename from openmc/statepoint.py rename to src/openmc/statepoint.py diff --git a/openmc/stats/__init__.py b/src/openmc/stats/__init__.py similarity index 100% rename from openmc/stats/__init__.py rename to src/openmc/stats/__init__.py diff --git a/openmc/stats/multivariate.py b/src/openmc/stats/multivariate.py similarity index 100% rename from openmc/stats/multivariate.py rename to src/openmc/stats/multivariate.py diff --git a/openmc/stats/univariate.py b/src/openmc/stats/univariate.py similarity index 100% rename from openmc/stats/univariate.py rename to src/openmc/stats/univariate.py diff --git a/openmc/summary.py b/src/openmc/summary.py similarity index 100% rename from openmc/summary.py rename to src/openmc/summary.py diff --git a/openmc/surface.py b/src/openmc/surface.py similarity index 100% rename from openmc/surface.py rename to src/openmc/surface.py diff --git a/openmc/tallies.py b/src/openmc/tallies.py similarity index 100% rename from openmc/tallies.py rename to src/openmc/tallies.py diff --git a/openmc/tally_derivative.py b/src/openmc/tally_derivative.py similarity index 100% rename from openmc/tally_derivative.py rename to src/openmc/tally_derivative.py diff --git a/openmc/tracks.py b/src/openmc/tracks.py similarity index 100% rename from openmc/tracks.py rename to src/openmc/tracks.py diff --git a/openmc/trigger.py b/src/openmc/trigger.py similarity index 100% rename from openmc/trigger.py rename to src/openmc/trigger.py diff --git a/openmc/universe.py b/src/openmc/universe.py similarity index 100% rename from openmc/universe.py rename to src/openmc/universe.py diff --git a/openmc/utility_funcs.py b/src/openmc/utility_funcs.py similarity index 100% rename from openmc/utility_funcs.py rename to src/openmc/utility_funcs.py diff --git a/openmc/volume.py b/src/openmc/volume.py similarity index 100% rename from openmc/volume.py rename to src/openmc/volume.py diff --git a/openmc/weight_windows.py b/src/openmc/weight_windows.py similarity index 100% rename from openmc/weight_windows.py rename to src/openmc/weight_windows.py diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 282389a8f19..43b53adb8d4 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -1,55 +1,64 @@ import os -import shutil import subprocess +import shutil def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrystal=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_args.append('-DOPENMC_USE_DAGMC=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) if ncrystal: - cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') + cmake_args.append('-DOPENMC_USE_NCRYSTAL=ON') ncrystal_path = os.environ.get('HOME') + '/ncrystal_inst' - cmake_cmd.append(f'-DCMAKE_PREFIX_PATH={ncrystal_path}') + cmake_args.append(f'-DCMAKE_PREFIX_PATH={ncrystal_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 cff7dc834f5..9a302c23f0b 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -43,8 +43,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/manylinux.dockerfile b/tools/ci/manylinux.dockerfile new file mode 100644 index 00000000000..e9a3521a9d5 --- /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.3" +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.2" +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