diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index e1d79a6..b5ecebb 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -20,8 +20,6 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 - with: - submodules: 'true' - uses: actions/setup-python@v5 with: python-version: 3.11 @@ -56,7 +54,7 @@ jobs: CMAKE_GENERATOR_PLATFORM=x64 CMAKE_BUILD_PARALLEL_LEVEL=${{ steps.get_num_cores.outputs.count }} CIBW_ARCHS: AMD64 - CIBW_BEFORE_BUILD: python -m pip install setuptools wheel delvewheel # skip CasADi and CMake + CIBW_BEFORE_BUILD: python -m pip install scikit-build-core delvewheel # skip CasADi and CMake CIBW_REPAIR_WHEEL_COMMAND: delvewheel repair --add-path C:/Windows/System32 -w {dest_dir} {wheel} CIBW_TEST_EXTRAS: "dev" CIBW_TEST_COMMAND: | @@ -73,9 +71,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - submodules: 'true' - - uses: actions/setup-python@v5 with: python-version: 3.11 @@ -86,8 +81,8 @@ jobs: CIBW_ARCHS_LINUX: x86_64 CIBW_BEFORE_ALL_LINUX: > yum -y install openblas-devel lapack-devel && - bash install_sundials.sh - CIBW_BEFORE_BUILD_LINUX: python -m pip install cmake casadi setuptools wheel + python install_KLU_Sundials.py + CIBW_BEFORE_BUILD_LINUX: python -m pip install casadi scikit-build-core cmake pybind11 CIBW_REPAIR_WHEEL_COMMAND_LINUX: auditwheel repair -w {dest_dir} {wheel} CIBW_TEST_EXTRAS: "dev" CIBW_TEST_COMMAND: | @@ -110,8 +105,6 @@ jobs: fail-fast: false steps: - uses: actions/checkout@v4 - with: - submodules: 'true' - uses: actions/setup-python@v5 with: python-version: '3.11' @@ -211,7 +204,7 @@ jobs: # 10.13 for Intel (macos-13), 11.0 for Apple Silicon (macos-14 and macos-latest) MACOSX_DEPLOYMENT_TARGET: ${{ matrix.os == 'macos-14' && '11.0' || '10.13' }} CIBW_ARCHS_MACOS: auto - CIBW_BEFORE_BUILD: python -m pip install cmake casadi setuptools wheel delocate + CIBW_BEFORE_BUILD: python -m pip install casadi scikit-build-core delocate cmake pybind11 CIBW_REPAIR_WHEEL_COMMAND: | if [[ $(uname -m) == "x86_64" ]]; then delocate-listdeps {wheel} && delocate-wheel -v -w {dest_dir} {wheel} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index bea2abc..80facb4 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -13,7 +13,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - submodules: 'true' - uses: actions/setup-python@v4 - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 8caedbf..a98f68e 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -20,9 +20,6 @@ jobs: steps: - uses: actions/checkout@v4 - with: - submodules: 'true' - - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: diff --git a/.gitignore b/.gitignore index 7caf519..69c6400 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ setup.log install_KLU_Sundials .idaklu build +dist +*.so # Extra directories for local work workspace diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8e1aff6..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "pybind11"] - path = pybind11 - url = git@github.com:pybind/pybind11.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 377b371..3b0b238 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,14 +36,17 @@ endif() # casadi seems to compile without the newer versions of std::string add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) -if(NOT PYBIND11_DIR) - set(PYBIND11_DIR pybind11) -endif() -add_subdirectory(${PYBIND11_DIR}) +find_package(pybind11 CONFIG REQUIRED) + +find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) # Check Casadi build flag if(NOT DEFINED PYBAMM_IDAKLU_EXPR_CASADI) - set(PYBAMM_IDAKLU_EXPR_CASADI ON) + if(DEFINED ENV{PYBAMM_IDAKLU_EXPR_CASADI}) + set(PYBAMM_IDAKLU_EXPR_CASADI "$ENV{PYBAMM_IDAKLU_EXPR_CASADI}") + else() + set(PYBAMM_IDAKLU_EXPR_CASADI ON) + endif() endif() message("PYBAMM_IDAKLU_EXPR_CASADI: ${PYBAMM_IDAKLU_EXPR_CASADI}") @@ -59,7 +62,11 @@ endif() # Check IREE build flag if(NOT DEFINED PYBAMM_IDAKLU_EXPR_IREE) - set(PYBAMM_IDAKLU_EXPR_IREE OFF) + if(DEFINED ENV{PYBAMM_IDAKLU_EXPR_IREE}) + set(PYBAMM_IDAKLU_EXPR_IREE "$ENV{PYBAMM_IDAKLU_EXPR_IREE}") + else() + set(PYBAMM_IDAKLU_EXPR_IREE OFF) + endif() endif() message("PYBAMM_IDAKLU_EXPR_IREE: ${PYBAMM_IDAKLU_EXPR_IREE}") @@ -116,39 +123,40 @@ pybind11_add_module(idaklu ${IDAKLU_EXPR_IREE_SOURCE_FILES} ) -if (NOT DEFINED USE_PYTHON_CASADI) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(USE_PYTHON_CASADI FALSE) +else() set(USE_PYTHON_CASADI TRUE) endif() +# Check if CASADI_PATH is set in the environment +if(NOT DEFINED ENV{CASADI_PATH}) + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" -c + "import os; import sysconfig; print(os.path.join(sysconfig.get_path('purelib'), 'casadi', 'cmake'))" + OUTPUT_VARIABLE CASADI_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) +else() + set(CASADI_DIR $ENV{CASADI_PATH}) + message("it is alr set: ${CASADI_DIR}") +endif() -execute_process( - COMMAND "${PYTHON_EXECUTABLE}" -c - "import os; import sysconfig; print(os.path.join(sysconfig.get_path('purelib'), 'casadi', 'cmake'))" - OUTPUT_VARIABLE CASADI_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE) - -if (CASADI_DIR) +if(CASADI_DIR) file(TO_CMAKE_PATH ${CASADI_DIR} CASADI_DIR) - message("Found Python casadi path: ${CASADI_DIR}") + message("Found CasADi path: ${CASADI_DIR}") else() - message(FATAL_ERROR "Did not find casadi path}") + message(FATAL_ERROR "Did not find CasADi path") endif() if(${USE_PYTHON_CASADI}) - message("Trying to link against Python casadi package in ${CASADI_DIR}") + message("Trying to link against Python CasADi package in ${CASADI_DIR}") find_package(casadi CONFIG PATHS ${CASADI_DIR} REQUIRED NO_DEFAULT_PATH) else() - message("Trying to link against any casadi package apart from the Python one") + message("Trying to link against any CasADi package apart from the Python one") set(CMAKE_IGNORE_PATH "${CASADI_DIR}/cmake") find_package(casadi CONFIG REQUIRED) endif() -set_target_properties( - idaklu PROPERTIES - INSTALL_RPATH "${CASADI_DIR}" - INSTALL_RPATH_USE_LINK_PATH TRUE -) - # openmp if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") execute_process( @@ -167,6 +175,14 @@ if(OpenMP_CXX_FOUND) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) + +if(NOT DEFINED ENV{INSTALL_DIR}) + set(INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.idaklu") +else() + set(INSTALL_DIR $ENV{INSTALL_DIR}) +endif() +message(STATUS "Library path is set: ${INSTALL_DIR}") + # Sundials find_package(SUNDIALS REQUIRED) message("SUNDIALS found in ${SUNDIALS_INCLUDE_DIR}: ${SUNDIALS_LIBRARIES}") @@ -185,6 +201,21 @@ endif() include_directories(${SuiteSparse_INCLUDE_DIRS}) target_link_libraries(idaklu PRIVATE ${SuiteSparse_LIBRARIES}) +if(DEFINED ENV{CIBUILDWHEEL} AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(SUNDIALS_LIBRARY_DIR "${INSTALL_DIR}/lib64") + set(SUITESPARSE_LIBRARY_DIR "${INSTALL_DIR}/lib64") +else() + set(SUNDIALS_LIBRARY_DIR "${INSTALL_DIR}/lib") + set(SUITESPARSE_LIBRARY_DIR "${INSTALL_DIR}/lib") +endif() + +set_target_properties(idaklu PROPERTIES + INSTALL_RPATH "${CASADI_DIR};${SUNDIALS_LIBRARY_DIR};${SUITESPARSE_LIBRARY_DIR}" + INSTALL_RPATH_USE_LINK_PATH TRUE + BUILD_RPATH "${CASADI_DIR};${SUNDIALS_LIBRARY_DIR};${SUITESPARSE_LIBRARY_DIR}" +) +message("NOTE: RPATHS linking SUNDIALS, SuiteSparse and casADi are set") + # IREE (MLIR compiler and runtime library) build settings if(${PYBAMM_IDAKLU_EXPR_IREE} STREQUAL "ON" ) set(IREE_BUILD_COMPILER ON) @@ -197,3 +228,9 @@ if(${PYBAMM_IDAKLU_EXPR_IREE} STREQUAL "ON" ) target_link_libraries(idaklu PRIVATE iree_compiler_bindings_c_loader) target_link_libraries(idaklu PRIVATE iree_runtime_runtime) endif() + +if(DEFINED ENV{CIBUILDWHEEL}) + install(TARGETS idaklu DESTINATION "pybammsolvers") +else() + install(TARGETS idaklu DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/src/pybammsolvers") +endif() diff --git a/FindSUNDIALS.cmake b/FindSUNDIALS.cmake index 4f8c52b..7995648 100644 --- a/FindSUNDIALS.cmake +++ b/FindSUNDIALS.cmake @@ -41,6 +41,7 @@ find_path(SUNDIALS_INCLUDE_DIR include PATHS ${SUNDIALS_ROOT} + ${INSTALL_DIR} ) set(SUNDIALS_WANT_COMPONENTS @@ -74,6 +75,7 @@ foreach(LIB ${SUNDIALS_WANT_COMPONENTS}) Lib PATHS ${SUNDIALS_ROOT} + ${INSTALL_DIR} ) set(SUNDIALS_${LIB}_FOUND FALSE) diff --git a/FindSuiteSparse.cmake b/FindSuiteSparse.cmake index c5275c0..6e3bb01 100644 --- a/FindSuiteSparse.cmake +++ b/FindSuiteSparse.cmake @@ -37,6 +37,20 @@ # system paths. # +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") + enable_language(Fortran) + set(IDAKLU_BLAS_VENDOR + "OpenBLAS" + CACHE + STRING + "Sets the BLAS/LAPACK vendor. See https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors." + ) + set(BLA_VENDOR ${IDAKLU_BLAS_VENDOR}) + + # openBLAS: include a "lib" prefix in its names + set(CMAKE_FIND_LIBRARY_PREFIXES "" lib) +endif() + find_package(BLAS QUIET) # look for desired componenents @@ -60,7 +74,9 @@ endif() # look for library at positions given by the user find_library(SUITESPARSE_CONFIG_LIB NAMES "suitesparseconfig" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "lib" "lib32" "lib64" "Lib" NO_DEFAULT_PATH ) @@ -73,7 +89,9 @@ find_library(SUITESPARSE_CONFIG_LIB #look for header files at positions given by the user find_path(SUITESPARSE_INCLUDE_DIR NAMES "SuiteSparse_config.h" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "SuiteSparse_config" "SuiteSparse_config/include" "suitesparse" "include" "src" "SuiteSparse_config/Include" NO_DEFAULT_PATH ) @@ -89,7 +107,9 @@ foreach(_component ${SUITESPARSE_COMPONENTS}) #look for library at positions given by the user find_library(${_component}_LIBRARY NAMES "${_componentLower}" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "lib" "lib32" "lib64" "${_component}" "${_component}/Lib" NO_DEFAULT_PATH ) @@ -102,7 +122,9 @@ foreach(_component ${SUITESPARSE_COMPONENTS}) #look for header files at positions given by the user find_path(${_component}_INCLUDE_DIR NAMES "${_componentLower}.h" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "${_componentLower}" "include/${_componentLower}" "suitesparse" "include" "src" "${_component}" "${_component}/Include" NO_DEFAULT_PATH ) @@ -117,7 +139,9 @@ endforeach() #look for header files at positions given by the user find_path(SPQR_INCLUDE_DIR NAMES "SuiteSparseQR.hpp" - PATHS ${SuiteSparse_ROOT} + PATHS + ${SuiteSparse_ROOT} + ${INSTALL_DIR} PATH_SUFFIXES "spqr" "include/spqr" "suitesparse" "include" "src" "SPQR" "SPQR/Include" NO_DEFAULT_PATH ) diff --git a/install_sundials.sh b/install_sundials.sh deleted file mode 100644 index 655d47f..0000000 --- a/install_sundials.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -# This script installs both SuiteSparse -# (https://people.engr.tamu.edu/davis/suitesparse.html) and SUNDIALS -# (https://computing.llnl.gov/projects/sundials) from source. For each -# two library: -# - Archive downloaded and source code extracted in current working -# directory. -# - Library is built and installed. -# -# Usage: ./install_sundials.sh suitesparse_version sundials_version - -function prepend_python_bin_dir_to_PATH { - python_bin_dir_cmd="print(os.path.split(sys.executable)[0])" - python_bin_dir=$(python -c "import sys, os;$python_bin_dir_cmd") - export PATH=$python_bin_dir:$PATH -} - -function download { - ROOT_ADDR=$1 - FILENAME=$2 - - wget -q $ROOT_ADDR/$FILENAME -} - -function extract { - tar -xf $1 -} - -SUITESPARSE_VERSION=6.0.3 -SUNDIALS_VERSION=6.5.0 - -SUITESPARSE_ROOT_ADDR=https://github.com/DrTimothyAldenDavis/SuiteSparse/archive -SUNDIALS_ROOT_ADDR=https://github.com/LLNL/sundials/releases/download/v$SUNDIALS_VERSION - -SUITESPARSE_ARCHIVE_NAME=v$SUITESPARSE_VERSION.tar.gz -SUNDIALS_ARCHIVE_NAME=sundials-$SUNDIALS_VERSION.tar.gz - -yum -y update -yum -y install wget -download $SUITESPARSE_ROOT_ADDR $SUITESPARSE_ARCHIVE_NAME -download $SUNDIALS_ROOT_ADDR $SUNDIALS_ARCHIVE_NAME -extract $SUITESPARSE_ARCHIVE_NAME -extract $SUNDIALS_ARCHIVE_NAME - -# Build in parallel wherever possible -export MAKEFLAGS="-j$(nproc)" -export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) - -### Compile and install SUITESPARSE ### -# SuiteSparse is required to compile SUNDIALS's -# KLU solver. - -SUITESPARSE_DIR=SuiteSparse-$SUITESPARSE_VERSION -for dir in SuiteSparse_config AMD COLAMD BTF KLU -do - make -C $SUITESPARSE_DIR/$dir library - make -C $SUITESPARSE_DIR/$dir install INSTALL=/usr -done - -### Compile and install SUNDIALS ### - -# Building cmake requires cmake >= 3.5 -python -m pip install cmake -prepend_python_bin_dir_to_PATH - -# Building SUNDIALS requires a BLAS library -yum -y install openblas-devel - -mkdir -p build_sundials -cd build_sundials -KLU_INCLUDE_DIR=/usr/local/include -KLU_LIBRARY_DIR=/usr/local/lib -SUNDIALS_DIR=sundials-$SUNDIALS_VERSION -cmake -DENABLE_LAPACK=ON\ - -DSUNDIALS_INDEX_SIZE=32\ - -DEXAMPLES_ENABLE:BOOL=OFF\ - -DENABLE_KLU=ON\ - -DENABLE_OPENMP=ON\ - -DKLU_INCLUDE_DIR=$KLU_INCLUDE_DIR\ - -DKLU_LIBRARY_DIR=$KLU_LIBRARY_DIR\ - -DCMAKE_INSTALL_PREFIX=/usr\ - ../$SUNDIALS_DIR -make install diff --git a/pyproject.toml b/pyproject.toml index da35f93..9d22000 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,22 +1,22 @@ [build-system] requires = [ - "setuptools>=64", - "wheel", + "scikit-build-core>=0.10", # On Windows, use the CasADi vcpkg registry and CMake bundled from MSVC "casadi>=3.6.7; platform_system!='Windows'", # Note: the version of CasADi as a build-time dependency should be matched # cross platforms, so updates to its minimum version here should be accompanied # by a version bump in https://github.com/pybamm-team/casadi-vcpkg-registry. - "cmake; platform_system!='Windows'", + "pybind11>=2.13.4", ] -build-backend = "setuptools.build_meta" +build-backend = "scikit_build_core.build" [project] name = "pybammsolvers" description = "Python interface for the IDAKLU solver" requires-python = ">=3.9,<3.13" -license = {file = "LICENSE"} -dynamic = ["version", "readme"] +license = { file = "LICENSE" } +version = "0.0.1" +dynamic = ["readme"] dependencies = [ "casadi", "numpy<2.0" @@ -25,14 +25,27 @@ dependencies = [ [project.optional-dependencies] dev = [ "pytest", - "setuptools", - "wheel", + "scikit-build-core", ] -[tool.setuptools.packages.find] -where = ["src"] -include = ["pybammsolvers"] +[tool.scikit_build] +minimum-version = "build-system.requires" +build-dir = "build/{wheel_tag}" +build.verbose = true +metadata.readme.provider = "scikit_build_core.metadata.fancy_pypi_readme" -[tool.setuptools.dynamic] -version = {attr = "pybammsolvers.version.__version__"} -readme = {file = ["README.md"], content-type = "text/markdown"} +[tool.scikit-build.logging] +level = "INFO" + +[tool.scikit-build.cmake] +version = ">=3.13" +build-type = "Release" + +[tool.scikit-build.sdist] +include = ["src/pybammsolvers"] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] +path = "README.md"