diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 1888a085..53b91298 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -33,7 +33,7 @@ jobs: if: ${{ matrix.py == '3.9'}} # Ensure these versions are consistent with the minimal version requirements # in pyproject.toml - run: pip install numpy==1.22 scipy==1.11.1 + run: pip install numpy==2.0 scipy==1.13.0 - name: Install development version run: | # Need editable mode in order to include the test files diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..edd7d395 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,101 @@ +# CMakeLists.txt — Build C extension for GBasis libcint integration (Issue: #229) + +# Usage: +# Default (all platforms): pip install qc-gbasis +# qcint (x86+AVX2 only): pip install qc-gbasis -C cmake.args="-DUSE_QCINT=ON" + + +cmake_minimum_required(VERSION 3.17) +project(gbasis_libcint C) + +# Python and NumPy are required to build the C extension module + +find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) +find_package(Python COMPONENTS NumPy REQUIRED) + +# Get NumPy include path for C extension (numpy/arrayobject.h) + +execute_process( + COMMAND "${Python_EXECUTABLE}" "-c" + "import numpy; print(numpy.get_include())" + OUTPUT_VARIABLE NumPy_INCLUDE_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# ── Platform Detection — x86 vs ARM ────────────────────── +# Detect platform: use qcint on x86+AVX2, libcint on ARM/non-AVX +# qcint is an optimized version of libcint for x86 with SIMD support +include(CheckCCompilerFlag) + +if(MSVC) + check_c_compiler_flag("/arch:AVX2" HAS_AVX2) +else() + check_c_compiler_flag("-mavx2" HAS_AVX2) +endif() + +# Respect user override via -DUSE_QCINT=ON/OFF; otherwise auto-detect +if(NOT DEFINED USE_QCINT) + if(HAS_AVX2 AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "arm") + set(USE_QCINT ON CACHE BOOL "Use qcint instead of libcint" FORCE) + message(STATUS "Architecture: x86 + AVX2 detected — use qcint") + else() + set(USE_QCINT OFF CACHE BOOL "Use qcint instead of libcint" FORCE) + message(STATUS "Architecture: ARM/non-AVX detected — use libcint") + endif() +endif() + + +# Download libcint or qcint based on platform detection +# Both are pinned to v6.1.2 for stability +# qcint: optimized for x86+AVX2 +# libcint: generic, works on all platforms + +include(FetchContent) + +if(USE_QCINT) + FetchContent_Declare( + libcint + GIT_REPOSITORY https://github.com/sunqm/qcint.git + GIT_TAG v6.1.2 + ) +else() + FetchContent_Declare( + libcint + GIT_REPOSITORY https://github.com/sunqm/libcint.git + GIT_TAG v6.1.2 + ) +endif() + +# Compile libcint — Fortran disabled, range-separated Coulomb enabled + +set(WITH_FORTRAN OFF CACHE BOOL "" FORCE) +# Enable range-separated Coulomb integrals +set(WITH_RANGE_COULOMB ON CACHE BOOL "" FORCE) + +# Redirect libcint's install to build directory to avoid permission issues +set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/local" CACHE PATH "" FORCE) +set(SKIP_INSTALL_ALL ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(libcint) + +# Build Python C extension module from libcint_wrap.c +python_add_library(libcint_bindings MODULE + gbasis/integrals/src/libcint_wrap.c + WITH_SOABI +) + +# Include NumPy and libcint headers +target_include_directories(libcint_bindings PRIVATE + ${NumPy_INCLUDE_DIRS} + ${libcint_SOURCE_DIR}/include +) + +# Link C extension with libcint +target_link_libraries(libcint_bindings PRIVATE cint) + +# Prevent libcint from installing to system directories +set(CINT_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/cint_install CACHE PATH "" FORCE) + +# Install only the Python extension into the wheel +install(TARGETS libcint_bindings + LIBRARY DESTINATION ${SKBUILD_PLATLIB_DIR}/gbasis/integrals/lib +) \ No newline at end of file diff --git a/gbasis/integrals/src/libcint_wrap.c b/gbasis/integrals/src/libcint_wrap.c new file mode 100644 index 00000000..4eb6e1da --- /dev/null +++ b/gbasis/integrals/src/libcint_wrap.c @@ -0,0 +1,6 @@ + + + /* libcint Python/C API bindings. + * The full bindings will be implemented in a follow-up PR. + */ + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 82727343..717403d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,12 @@ +# Replaced setuptools with scikit-build-core +# Reason: use a CMake-based build (via scikit-build-core) to compile and bundle libcint/qcint +# as part of the Python wheel build + [build-system] -requires = ["setuptools>=64", "setuptools-scm>=8"] -build-backend = "setuptools.build_meta" +requires = ["numpy>=2.0", "scikit-build-core>=0.9","setuptools-scm>=8"] +build-backend = "scikit_build_core.build" + + [project] name = "qc-gbasis" @@ -31,18 +37,38 @@ classifiers = [ "Programming Language :: Python :: 3.12", ] dependencies = [ - # Ensure the minimal versions are kept consistent with those in .github/workflows/pytest.yaml - "numpy >=1.22, <2.0.0; platform_system=='Windows'", - "numpy >=1.22; platform_system=='Linux'", - "scipy>=1.11.1", + # Updated to numpy>=2.0 required for scikit-build-core C extension support + # Note: Keep consistent with .github/workflows/pytest.yaml + "numpy>=2.0", + "scipy>=1.13.0", "importlib_resources", "sympy", ] dynamic = ["version"] +[tool.scikit-build] +# Build type: Release for optimized production build (no debug symbols) +cmake.build-type = "Release" + +# Replaces [tool.setuptools.packages.find] from old setuptools config +# Only pack gbasis folder in the wheel +wheel.packages = ["gbasis"] + + + +[tool.scikit-build.metadata.version] +# Automatically get version from git tags via setuptools_scm +provider = "scikit_build_core.metadata.setuptools_scm" + +# Required to activate setuptools_scm for automatic versioning from git tags [tool.setuptools_scm] + + [project.optional-dependencies] + +# Install with: pip install qc-gbasis[dev] + dev = [ "tox", "pre-commit", @@ -56,6 +82,9 @@ dev = [ "sphinx_autodoc_typehints", "sphinx-copybutton", ] + +# Install with: pip install qc-gbasis[doc] + doc = [ "numpydoc", "sphinx_copybutton", @@ -63,22 +92,26 @@ doc = [ "nbsphinx", "sphinx_rtd_theme", "sphinx_autodoc_typehints", - "docutils==0.16", # Needed to show bullet points in sphinx_rtd_theme + "docutils==0.16", # Needed to show bullet points in sphinx_rtd_theme "nbsphinx-link" ] + # iodata = [ # "qc-iodata@git+https://github.com/theochem/iodata.git@main" # ] + +# Install with: pip install qc-gbasis[iodata] + iodata = [ "qc-iodata>=1.0.0a5" ] + +# Install with: pip install qc-gbasis[pyscf] + pyscf = [ "pyscf>=1.6.1" ] -[tool.setuptools.packages.find] -where = ["."] # list of folders that contain the packages (["."] by default) -include = ["gbasis"] # package names should match these glob patterns (["*"] by default) [project.urls] Documentation = "https://gbasis.qcdevs.org" @@ -112,7 +145,4 @@ addopts = "-v" #[tool.setuptools_scm] #write_to = "src/gbasis/_version.py" #version_scheme = "post-release" -#local_scheme = "no-local-version" - -[tool.setuptools.package-data] -gbasis = ["integrals/include/cint*.h", "integrals/lib/libcint.so*"] +#local_scheme = "no-local-version" \ No newline at end of file