diff --git a/.gitignore b/.gitignore index c74533d..54b3b3b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ test.py cuda-* wheelhouse test.ipynb -venv/ \ No newline at end of file +venv/ +.venv/ +**.so +__pycache__/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 82a291f..30410c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,16 @@ cmake_minimum_required(VERSION 3.25.2) -project(rayx-python) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Install prefix" FORCE) +endif() +project(rayx-python) add_subdirectory(extern) add_subdirectory(src) -# create __init__.py and install it in INSTALL_DIR/rayxdata -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" "") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" DESTINATION "rayxdata") +# Install Python package +install(DIRECTORY python/rayx DESTINATION .) + +# Create and install rayxdata package (needed by C++ code) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/rayxdata/__init__.py" "") +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rayxdata" DESTINATION .) diff --git a/pyproject.toml b/pyproject.toml index 201b2e0..e916401 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,6 @@ version = "0.4.1" description = "Python bindings for RAYX" readme = "README.md" license = { file = "LICENSE" } - dependencies = ["numpy >= 2.0.0"] requires-python = ">=3.7" authors = [{ name = "RAYX team", email = "rayx-support@helmholtz-berlin.de" }] @@ -12,10 +11,25 @@ maintainers = [ { name = "RAYX team", email = "rayx-support@helmholtz-berlin.de" }, ] +[project.optional-dependencies] +dev = [ + "pytest>=7.0", + "matplotlib>=3.5", + "ipython", +] +test = [ + "pytest>=7.0", + "pytest-cov", +] + [build-system] requires = ["scikit-build-core", "pybind11"] build-backend = "scikit_build_core.build" +[tool.scikit-build] +wheel.packages = ["python/rayx"] +cmake.build-type = "Release" + [tool.cibuildwheel] archs = ["x86_64"] build = ["*manylinux*"] diff --git a/python/rayx/__init__.py b/python/rayx/__init__.py new file mode 100644 index 0000000..4d2fb2a --- /dev/null +++ b/python/rayx/__init__.py @@ -0,0 +1,46 @@ +""" +RAY-X Python bindings +""" +import sys +from pathlib import Path + +# Import the C++ extension module +try: + from . import _core +except ImportError: + # During development, try to find the built module + import os + build_dir = Path(__file__).parent.parent.parent / "build" / "install" / "rayx" + if build_dir.exists(): + sys.path.insert(0, str(build_dir.parent)) + from rayx import _core + else: + raise ImportError("Cannot find compiled _core module. Did you build the project?") + +# Re-export everything from C++ module +from ._core import * + +# Add Python enhancements +def hello(): + """Test function to verify Python wrapper is included""" + return "Hello from Python wrapper!" + +def get_info(): + """Get information about the RAYX installation""" + info = { + "version": __version__, + "python_wrapper": True, + "cpp_module": str(_core.__file__), + "module_path": str(Path(__file__).parent), + } + return info + +def enhanced_function(): + """Pure Python function using C++ backend""" + # This is a placeholder - customize based on your actual C++ functions + # result = _core.cpp_function() # Call C++ function + # Add Python processing + return "Enhanced function - add your logic here" + +__version__ = "0.4.1" +__all__ = ['hello', 'get_info', 'enhanced_function'] diff --git a/setup_dev.sh b/setup_dev.sh new file mode 100755 index 0000000..8d92e8c --- /dev/null +++ b/setup_dev.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# setup_dev.sh - Development setup with virtual environment + +set -e + +# Ensure we're in the right directory +cd "$(dirname "$0")" + +# Check if venv exists +if [ ! -d ".venv" ]; then + echo "Creating virtual environment with uv..." + uv venv + source .venv/bin/activate + echo "Installing dependencies..." + uv pip install numpy pytest matplotlib +else + source .venv/bin/activate +fi + +# Build C++ extension +echo "Building C++ extension..." +mkdir -p build +cd build + +# Configure with local install prefix +cmake -DCMAKE_INSTALL_PREFIX=./install .. + +# Build +make -j$(nproc) + +# Install to local directory +make install + +cd .. + +# Create symlink +BUILD_DIR="build/install/rayx" +PYTHON_DIR="python/rayx" + +SO_FILE=$(find "$BUILD_DIR" -name "_core.cpython-*.so" 2>/dev/null | head -n 1) + +if [ -f "$SO_FILE" ]; then + ln -sf "../../$SO_FILE" "$PYTHON_DIR/_core.so" + echo "✓ Created symlink: $PYTHON_DIR/_core.so" +else + echo "✗ Error: Could not find _core.cpython-*.so in $BUILD_DIR" + echo "Files in build/install:" + find build/install -type f 2>/dev/null || echo " (directory doesn't exist)" + exit 1 +fi + +# Add python directory to PYTHONPATH for development +export PYTHONPATH="$(pwd)/python:$PYTHONPATH" + +echo "✓ Development environment ready!" +echo "" +echo "To use:" +echo " source .venv/bin/activate" +echo " cd python && python3 -c 'import rayx; print(rayx.__version__)'" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8265e39..ab29de8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,16 +1,17 @@ project(rayxpy LANGUAGES CXX CUDA) - set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CUDA_STANDARD 20) set(CMAKE_CUDA_STANDARD_REQUIRED ON) - set(PYBIND11_FINDPYTHON ON) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF) -pybind11_add_module(rayx main.cpp) -target_link_libraries(rayx PRIVATE rayx-core) -target_include_directories(rayx PRIVATE $ ${CUDA_TOOLKIT_INCLUDE}/cccl) +# Changed from 'rayx' to '_core' to make it a private module +pybind11_add_module(_core main.cpp) +target_link_libraries(_core PRIVATE rayx-core) +target_include_directories(_core PRIVATE + $ + ${CUDA_TOOLKIT_INCLUDE}/cccl) -install(TARGETS rayx DESTINATION .) \ No newline at end of file +# Install to rayx/ subdirectory +install(TARGETS _core LIBRARY DESTINATION rayx) diff --git a/src/main.cpp b/src/main.cpp index 7e6ae05..fe5b35a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -335,7 +335,7 @@ class Module { } }; -PYBIND11_MODULE(rayx, m) { +PYBIND11_MODULE(_core, m) { static Module module_instance; m.doc() = "rayx module"; @@ -483,4 +483,4 @@ PYBIND11_MODULE(rayx, m) { m.def( "import_beamline", [](std::string path) { return rayx::importBeamline(path); }, "Import a beamline from an RML file", pybind11::arg("path")); -} \ No newline at end of file +}