diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml
new file mode 100644
index 00000000000..d3e8606e96d
--- /dev/null
+++ b/.github/workflows/build_wheels.yml
@@ -0,0 +1,23 @@
+name: Build
+
+on: [push, pull_request]
+
+jobs:
+ build_wheels:
+ name: Build wheels on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ # macos-13 is an intel runner, macos-14 is apple silicon
+ os: [ubuntu-latest]
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Build wheels
+ uses: pypa/cibuildwheel@v2.20.0
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
+ path: ./wheelhouse/*.whl
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9293e319b42..1415074ad71 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -145,9 +145,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/.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 0f4cc1b527b..ffa682ad111 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,6 +16,11 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Set module path
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
+# Conditionally find Python if building with scikit-build-core
+if(SKBUILD)
+ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
+endif()
+
# Enable correct usage of CXX_EXTENSIONS
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.22)
cmake_policy(SET CMP0128 NEW)
@@ -36,6 +41,7 @@ option(OPENMC_USE_MCPL "Enable MCPL"
option(OPENMC_USE_NCRYSTAL "Enable support for NCrystal scattering" OFF)
option(OPENMC_USE_UWUW "Enable UWUW" OFF)
+
# Warnings for deprecated options
foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh")
if(DEFINED ${OLD_OPT})
@@ -327,6 +333,11 @@ if("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}")
endif()
+if(SKBUILD)
+ set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+ set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+endif()
+
#===============================================================================
# libopenmc
#===============================================================================
@@ -614,3 +625,8 @@ install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES "${CMAKE_BINARY_DIR}/include/openmc/version.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openmc)
+
+if(SKBUILD)
+ install(TARGETS openmc RUNTIME DESTINATION openmc/bin)
+ install(TARGETS libopenmc LIBRARY DESTINATION openmc/bin)
+endif()
diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst
index 7f222f77cb8..3e0778a9eca 100644
--- a/docs/source/quickinstall.rst
+++ b/docs/source/quickinstall.rst
@@ -160,28 +160,19 @@ download and install OpenMC by entering the following commands in a terminal:
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 ..
-
-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:
+ python -m pip install . --global-option="build_ext" --global-option="--" --global-option="-DOPENMC_USE_MPI=ON"
-.. code-block:: sh
-
- python -m pip install .
+The compilion of the ``openmc`` can be customised by specifying CMake arguments.
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..b0fab8c68bc 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,15 +507,15 @@ 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
+you are :ref:`Compiling from Source with CMake `, 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
@@ -527,7 +529,27 @@ distribution/repository, run:
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 566d287068f..32320365d58 100644
--- a/openmc/__init__.py
+++ b/openmc/__init__.py
@@ -34,6 +34,7 @@
from openmc.polynomial import *
from openmc.tracks import *
from .config import *
+from .openmc_exec import main
# Import a few names from the model module
from openmc.model import Model
diff --git a/openmc/openmc_exec.py b/openmc/openmc_exec.py
new file mode 100644
index 00000000000..579eeced24f
--- /dev/null
+++ b/openmc/openmc_exec.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+# helper script that launches the openmc binary
+
+import os
+import sys
+import sysconfig
+from pathlib import Path
+
+
+def main():
+ os.execv(
+ Path(sysconfig.get_path("platlib")) / "openmc" / "bin" / "openmc", sys.argv
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/pyproject.toml b/pyproject.toml
index 39aa261c1b4..c24026300b1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[build-system]
-requires = ["setuptools", "wheel"]
-build-backend = "setuptools.build_meta"
+requires = ["setuptools", "wheel", "numpy", "cython", "scikit-build-core"]
+build-backend = "scikit_build_core.build"
[project]
name = "openmc"
@@ -75,3 +75,17 @@ 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"
+openmc = "openmc.openmc_exec:main"
+
+[tool.scikit-build]
+build.verbose = true
+# cmake args can be passed in here or in the command line via exports or inline with the pip install
+cmake.args = ["-DCMAKE_BUILD_TYPE=RELEASE"]
+
+[tool.cibuildwheel]
+skip = "*-win_*"
+before-all = """
+ yum install -y epel-release
+ yum install -y hdf5 hdf5-devel libpng-devel cmake eigen3-devel gcc gcc-c++ wget
+"""
+manylinux-x86_64-image = "manylinux_2_28"
diff --git a/setup.py b/setup.py
new file mode 100755
index 00000000000..f26b3138274
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+import os
+import numpy as np
+from setuptools import setup, Extension
+
+
+class OpenMCExtension(Extension):
+ def __init__(self, name, cmake_lists_dir=".", sources=[], **kwa):
+ Extension.__init__(self, name, sources=sources, **kwa)
+ self.cmake_lists_dir = os.path.abspath(cmake_lists_dir)
+
+
+kwargs = {
+ 'ext_modules': [OpenMCExtension('libopenmc')],
+ 'include_dirs': [np.get_include()]
+}
+
+setup(**kwargs)
diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py
index f046e863470..da94df1bc2b 100644
--- a/tools/ci/gha-install.py
+++ b/tools/ci/gha-install.py
@@ -1,57 +1,52 @@
import os
-import shutil
import subprocess
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']
+ pip_command = ['pip', 'install', '.[test,vtk,ci]']
+ pip_suffix = ['--config-settings=cmake.args="-DCMAKE_BUILD_TYPE=ON;-DOPENMC_USE_MCPL=ON']
# Turn off OpenMP if specified
if not omp:
- cmake_cmd.append('-DOPENMC_USE_OPENMP=off')
+ pip_suffix.append('-DOPENMC_USE_OPENMP=off')
# Use MPI wrappers when building in parallel
if mpi:
- cmake_cmd.append('-DOPENMC_USE_MPI=on')
+ pip_suffix.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')
+ pip_suffix.append('-DHDF5_PREFER_PARALLEL=ON')
else:
- cmake_cmd.append('-DHDF5_PREFER_PARALLEL=OFF')
+ pip_suffix.append('-DHDF5_PREFER_PARALLEL=OFF')
if dagmc:
- cmake_cmd.append('-DOPENMC_USE_DAGMC=ON')
- cmake_cmd.append('-DCMAKE_PREFIX_PATH=~/DAGMC')
+ pip_suffix.append('-DOPENMC_USE_DAGMC=ON')
+ pip_suffix.append('-DCMAKE_PREFIX_PATH=~/DAGMC')
if libmesh:
- cmake_cmd.append('-DOPENMC_USE_LIBMESH=ON')
+ pip_suffix.append('-DOPENMC_USE_LIBMESH=ON')
libmesh_path = os.environ.get('HOME') + '/LIBMESH'
- cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path)
+ pip_suffix.append('-DCMAKE_PREFIX_PATH=' + libmesh_path)
if ncrystal:
- cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON')
+ pip_suffix.append('-DOPENMC_USE_NCRYSTAL=ON')
ncrystal_cmake_path = os.environ.get('HOME') + '/ncrystal_inst/lib/cmake'
- cmake_cmd.append(f'-DCMAKE_PREFIX_PATH={ncrystal_cmake_path}')
+ pip_suffix.append(f'-DCMAKE_PREFIX_PATH={ncrystal_cmake_path}')
# Build in coverage mode for coverage testing
- cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on')
+ pip_suffix.append('-DOPENMC_ENABLE_COVERAGE=ON"')
+ pip_command.append(';'.join(pip_suffix))
+ pip_command.append('--verbose')
# Build and install
- cmake_cmd.append('..')
- print(' '.join(cmake_cmd))
- subprocess.check_call(cmake_cmd)
- subprocess.check_call(['make', '-j4'])
- subprocess.check_call(['sudo', 'make', 'install'])
+ print(' '.join(pip_command))
+ subprocess.check_call(pip_command)
def main():
# Convert Travis matrix environment variables into arguments for install()
diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh
index cff7dc834f5..e04c61abc33 100755
--- a/tools/ci/gha-install.sh
+++ b/tools/ci/gha-install.sh
@@ -45,6 +45,3 @@ fi
# Build and install OpenMC executable
python tools/ci/gha-install.py
-
-# Install Python API in editable mode
-pip install -e .[test,vtk,ci]