Skip to content

Commit

Permalink
Turn to scikit-build-core
Browse files Browse the repository at this point in the history
Proof-of-concept to support Python 3.12
Supports parallel build
  • Loading branch information
jschueller committed May 16, 2024
1 parent 44af32d commit e638161
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 1 deletion.
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ jobs:
make -j4
sudo make install
- name: Build
run: python3 setup.py install --user --sundials-home=/usr --blas-home=/usr/lib/x86_64-linux-gnu/ --lapack-home=/usr/lib/x86_64-linux-gnu/ --superlu-home=/usr --extra-fortran-compile-flags="-fallow-argument-mismatch"
run: |
cmake -B build -DCMAKE_INSTALL_PREFIX=~/.local .
cmake --build build --target install --parallel 1
- name: Test
run: python3 -m nose --verbose tests/* tests/solvers/*
253 changes: 253 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
cmake_minimum_required (VERSION 3.17)
project (Assimulo LANGUAGES C)

option(USE_FORTRAN "build Fortran modules" ON)
option(USE_SUNDIALS "build SUNDIALS modules" ON)
option(USE_SUPERLU "use SUPERLU lib" ON)
if (USE_FORTRAN)
enable_language(Fortran)
endif ()

find_package (Python COMPONENTS Interpreter Development.Module NumPy REQUIRED)

find_package(LAPACK)

list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

if (USE_SUPERLU)
find_package (SuperLU_MT)
endif ()

if (USE_SUNDIALS)
find_package (SUNDIALS CONFIG)
if (SUNDIALS_FOUND)
message(STATUS "Found SUNDIALS: ${SUNDIALS_DIR} (found version \"${SUNDIALS_VERSION}\")")
endif ()
endif ()

include(GNUInstallDirs)
if (NOT DEFINED PYTHON_SITE_PACKAGES)
if (WIN32)
set (PYTHON_SITE_PACKAGES Lib/site-packages CACHE PATH "site-packages dir")
else ()
set (PYTHON_SITE_PACKAGES ${CMAKE_INSTALL_LIBDIR}/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages CACHE PATH "site-packages dir")
endif ()
endif ()

set (cython_clones)
# copy /src tree into CMAKE_BINARY_DIR/assimulo
file (GLOB_RECURSE cython_sources src/*.pyx src/*.pxd src/*.py)
foreach (file ${cython_sources})
if (EXISTS ${file})
file (RELATIVE_PATH rel_file "${CMAKE_SOURCE_DIR}/src" "${file}")
get_filename_component (rel_path ${rel_file} PATH)
file (MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/assimulo/${rel_path})
set (cython_clone ${CMAKE_BINARY_DIR}/assimulo/${rel_file})
add_custom_command (OUTPUT ${cython_clone}
COMMENT "Copying ${file}"
COMMAND ${CMAKE_COMMAND} -E copy ${file} ${cython_clone}
DEPENDS ${file}
)
list (APPEND cython_clones ${cython_clone})
endif ()
endforeach ()

# copy /thirdparty tree into CMAKE_BINARY_DIR
file (GLOB_RECURSE cython_sources thirdparty/*.pyx thirdparty/*.pxd thirdparty/*.py)
foreach (file ${cython_sources})
if (EXISTS ${file})
file (RELATIVE_PATH rel_file "${CMAKE_SOURCE_DIR}/thirdparty" "${file}")
get_filename_component (rel_path ${rel_file} PATH)
file (MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/thirdparty/${rel_path})
set (cython_clone ${CMAKE_BINARY_DIR}/thirdparty/${rel_file})
add_custom_command (OUTPUT ${cython_clone}
COMMENT "Copying ${file}"
COMMAND ${CMAKE_COMMAND} -E copy ${file} ${cython_clone}
DEPENDS ${file}
)
list (APPEND cython_clones ${cython_clone})
endif ()
endforeach ()

if (CMAKE_C_COMPILER_ID MATCHES "GNU")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing")
endif ()

if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fno-strict-aliasing -fallow-argument-mismatch")
endif ()

macro(assimulo_add_cython_module pyx_file)
cmake_parse_arguments(CMOD "" "DESTINATION" "SOURCES" ${ARGN})
if (NOT DEFINED CMOD_DESTINATION)
set (CMOD_DESTINATION assimulo)
endif ()
get_filename_component(name "${pyx_file}" NAME_WE)
get_filename_component(subdir "${pyx_file}" DIRECTORY)
add_custom_command(
OUTPUT ${name}.c
COMMENT "Making ${name}.c from ${name}.pyx"
COMMAND Python::Interpreter -m cython -o ${name}.c --3str --fast-fail
-I ${CMAKE_CURRENT_SOURCE_DIR}/src
-I ${CMAKE_CURRENT_SOURCE_DIR}/src/lib
${CMAKE_CURRENT_BINARY_DIR}/${pyx_file}
DEPENDS ${cython_clones} VERBATIM)

python_add_library(${name} MODULE ${name}.c ${CMOD_SOURCES} WITH_SOABI)
target_include_directories(${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/${subdir})
target_link_libraries(${name} PRIVATE Python::NumPy)
target_compile_definitions(${name} PRIVATE NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION)
install(TARGETS ${name} DESTINATION ${PYTHON_SITE_PACKAGES}/${CMOD_DESTINATION})
endmacro()

assimulo_add_cython_module(assimulo/explicit_ode.pyx
SOURCES src/ode_event_locator.c)
assimulo_add_cython_module(assimulo/algebraic.pyx)
assimulo_add_cython_module(assimulo/implicit_ode.pyx)
assimulo_add_cython_module(assimulo/ode.pyx)
assimulo_add_cython_module(assimulo/problem.pyx)
assimulo_add_cython_module(assimulo/special_systems.pyx)
assimulo_add_cython_module(assimulo/support.pyx)
assimulo_add_cython_module(assimulo/solvers/euler.pyx
DESTINATION assimulo/solvers)
#if (SUNDIALS_FOUND)
# assimulo_add_cython_module(solvers/sundials.pyx)
#endif ()


assimulo_add_cython_module(thirdparty/radau5/radau5ode.pyx
SOURCES thirdparty/radau5/radau5.c thirdparty/radau5/radau5_io.c
DESTINATION assimulo/lib)

if (SuperLU_MT_FOUND)
target_sources(radau5ode PRIVATE thirdparty/radau5/superlu_double.c thirdparty/radau5/superlu_complex.c thirdparty/radau5/superlu_util.c)
target_link_libraries(radau5ode PRIVATE SuperLU_MT::SuperLU_MT)
target_compile_definitions(radau5ode PRIVATE __RADAU5_WITH_SUPERLU)
endif()

if (USE_FORTRAN)

find_program(F2PY_EXECUTABLE NAMES f2py f2py3 REQUIRED)

if (NOT DEFINED F2PY_INCLUDE_DIR)
execute_process(COMMAND ${Python_EXECUTABLE} -c "from numpy import f2py; print(f2py.get_include())"
OUTPUT_VARIABLE F2PY_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
message(STATUS "Using f2py include dir: ${F2PY_INCLUDE_DIR}")

# object library with common f2py symbols
add_library(fortranobject OBJECT ${F2PY_INCLUDE_DIR}/fortranobject.c)
target_link_libraries(fortranobject PUBLIC Python::NumPy)
target_compile_definitions(fortranobject PUBLIC NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION)
target_include_directories(fortranobject PUBLIC ${F2PY_INCLUDE_DIR})
set_target_properties(fortranobject PROPERTIES POSITION_INDEPENDENT_CODE TRUE)

macro(assimulo_add_fortran_module name pyf_file)
cmake_parse_arguments(FMOD "" "" "SOURCES" ${ARGN})
add_custom_command(
OUTPUT ${name}module.c
COMMENT "Making ${name}module.c from ${pyf_file}"
COMMAND ${F2PY_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${pyf_file}
DEPENDS ${pyf_file} VERBATIM)

# may not be created depending on numpy version
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/${name}-f2pywrappers.f)

python_add_library(${name} MODULE ${FMOD_SOURCES}
${CMAKE_CURRENT_BINARY_DIR}/${name}module.c
${CMAKE_CURRENT_BINARY_DIR}/${name}-f2pywrappers.f
WITH_SOABI)
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${name}module.c ${CMAKE_CURRENT_BINARY_DIR}/${name}-f2pywrappers.f PROPERTIES GENERATED TRUE)

target_link_libraries(${name} PRIVATE fortranobject)
install(TARGETS ${name} DESTINATION ${PYTHON_SITE_PACKAGES}/assimulo/lib)
endmacro()

assimulo_add_fortran_module(dopri5 thirdparty/hairer/dopri5.pyf
SOURCES thirdparty/hairer/dopri5.f)

assimulo_add_fortran_module(rodas thirdparty/hairer/rodas_decsol.pyf
SOURCES thirdparty/hairer/rodas_decsol.f)

assimulo_add_fortran_module(radau5 thirdparty/hairer/radau_decsol.pyf
SOURCES thirdparty/hairer/radau_decsol.f)

assimulo_add_fortran_module(radar5 thirdparty/hairer/radar5.pyf
SOURCES
thirdparty/hairer/contr5.f90
thirdparty/hairer/radar5_int.f90
thirdparty/hairer/radar5.f90
thirdparty/hairer/dontr5.f90
thirdparty/hairer/decsol.f90
thirdparty/hairer/dc_decdel.f90)

assimulo_add_fortran_module(odepack thirdparty/odepack/odepack.pyf
SOURCES
thirdparty/odepack/opkdmain.f
thirdparty/odepack/opkda1.f
thirdparty/odepack/opkda2.f
thirdparty/odepack/odepack_aux.f90)

assimulo_add_fortran_module(odassl thirdparty/odassl/odassl.pyf
SOURCES
thirdparty/odassl/odassl.f
thirdparty/odassl/odastp.f
thirdparty/odassl/odacor.f
thirdparty/odassl/odajac.f
thirdparty/odassl/d1mach.f
thirdparty/odassl/daxpy.f
thirdparty/odassl/ddanrm.f
thirdparty/odassl/ddatrp.f
thirdparty/odassl/ddot.f
thirdparty/odassl/ddwats.f
thirdparty/odassl/dgefa.f
thirdparty/odassl/dgesl.f
thirdparty/odassl/dscal.f
thirdparty/odassl/idamax.f
thirdparty/odassl/xerrwv.f)
if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
target_compile_options(odassl PRIVATE $<$<COMPILE_LANGUAGE:Fortran>:-fdefault-double-8 -fdefault-real-8>)
endif ()

assimulo_add_fortran_module(dasp3dp thirdparty/dasp3/dasp3dp.pyf
SOURCES
thirdparty/dasp3/DASP3.f
thirdparty/dasp3/ANORM.f
thirdparty/dasp3/CTRACT.f
thirdparty/dasp3/DECOMP.f
thirdparty/dasp3/HMAX.f
thirdparty/dasp3/INIVAL.f
thirdparty/dasp3/JACEST.f
thirdparty/dasp3/PDERIV.f
thirdparty/dasp3/PREPOL.f
thirdparty/dasp3/SOLVE.f
thirdparty/dasp3/SPAPAT.f)

if (LAPACK_FOUND)
assimulo_add_fortran_module(glimda thirdparty/glimda/glimda_complete.pyf
SOURCES
thirdparty/glimda/glimda_complete.f)
target_link_libraries(glimda PRIVATE ${LAPACK_LIBRARIES})
endif ()
endif ()

install(FILES src/__init__.py
src/exception.py
src/problem_algebraic.py
DESTINATION ${PYTHON_SITE_PACKAGES}/assimulo)
install(FILES src/lib/__init__.py
src/lib/radau_core.py
DESTINATION ${PYTHON_SITE_PACKAGES}/assimulo/lib)
install(FILES src/solvers/__init__.py
src/solvers/dasp3.py
src/solvers/glimda.py
src/solvers/odassl.py
src/solvers/odepack.py
src/solvers/radar5.py
src/solvers/radau5.py
src/solvers/rosenbrock.py
src/solvers/runge_kutta.py
DESTINATION ${PYTHON_SITE_PACKAGES}/assimulo/solvers)
install(DIRECTORY examples
DESTINATION ${PYTHON_SITE_PACKAGES}/assimulo)
58 changes: 58 additions & 0 deletions cmake/FindSuperLU_MT.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# - Find SuperLU_MT
# SuperLU_MT contains a set of subroutines to solve a sparse linear system
# https://github.com/xiaoyeli/superlu_mt
#
# The module defines the following variables:
# SUPERLUMT_INCLUDE_DIRS, where to find mpc.h, etc.
# SUPERLUMT_LIBRARIES, the libraries needed to use MPC.
# SUPERLUMT_FOUND, If false, do not try to use MPC.
# also defined, but not for general use are
# SUPERLUMT_LIBRARY, where to find the MPC library.
#

find_path (SUPERLUMT_INCLUDE_DIR slu_mt_cdefs.h
PATH_SUFFIXES superlu_mt
)

find_library (SUPERLUMT_LIBRARY
NAMES superlu_mt_OPENMP superlu_mt_THREAD
)

set (SUPERLUMT_LIBRARIES ${SUPERLUMT_LIBRARY})
set (SUPERLUMT_INCLUDE_DIRS ${SUPERLUMT_INCLUDE_DIR})

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SuperLU_MT DEFAULT_MSG SUPERLUMT_LIBRARY SUPERLUMT_INCLUDE_DIRS)

mark_as_advanced (
SUPERLUMT_LIBRARY
SUPERLUMT_LIBRARIES
SUPERLUMT_INCLUDE_DIR
SUPERLUMT_INCLUDE_DIRS)



if(NOT TARGET SuperLU_MT::SuperLU_MT)
add_library(SuperLU_MT::SuperLU_MT UNKNOWN IMPORTED)
set_target_properties(SuperLU_MT::SuperLU_MT
PROPERTIES
IMPORTED_LOCATION ${SUPERLUMT_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES "${SUPERLUMT_INCLUDE_DIR}")

find_package(BLAS QUIET)
if (BLAS_LIBRARIES)
target_link_libraries(SuperLU_MT::SuperLU_MT INTERFACE ${BLAS_LIBRARIES})
endif ()

if (SUPERLUMT_LIBRARY MATCHES OPENMP)
find_package(OpenMP QUIET)
if (OpenMP_FOUND)
target_link_libraries(SuperLU_MT::SuperLU_MT INTERFACE OpenMP::OpenMP_C)
endif ()
endif ()

find_library(MATH_LIBRARY NAMES m)
if (MATH_LIBRARY)
target_link_libraries(SuperLU_MT::SuperLU_MT INTERFACE ${MATH_LIBRARY})
endif ()
endif()
17 changes: 17 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[project]
name = "assimulo"
version = "3.5.0"
dependencies = [
"scipy",
"cython",
]

[build-system]
requires = ["scikit-build-core"]
build-backend = "scikit_build_core.build"

[tool.scikit-build]
logging.level = "DEBUG"

[tool.scikit-build.cmake.define]
CMAKE_VERBOSE_MAKEFILE = "ON"

0 comments on commit e638161

Please sign in to comment.