Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
56efe67
ci: Run CI on all branches and pin CFD library version
shaia Dec 26, 2025
6685d5c
feat: Update Python bindings for CFD library v0.1.5
shaia Dec 26, 2025
666c64e
fix: Add CFD build include directory for cfd_export.h
shaia Dec 26, 2025
dd27e7f
ci: Fix duplicate workflow runs on push+PR
shaia Dec 26, 2025
5a46f68
fix: Link OpenMP for CFD library parallel backends
shaia Dec 26, 2025
0626078
test: Skip GPU solvers when CUDA not available
shaia Dec 26, 2025
66e23c4
ci: Use CFD fix branch to guard GPU solver registration
shaia Dec 26, 2025
824b7e9
ci: Enable CUDA build on Linux with multi-arch support
shaia Dec 26, 2025
8cda133
ci: Enable CUDA build on Windows
shaia Dec 26, 2025
3b60f47
ci: Update CUDA architectures to Turing+ (75, 80, 86, 89, 90)
shaia Dec 28, 2025
288d3f5
docs: Update migration plan to target CFD library v0.1.6
shaia Dec 28, 2025
7d1aa89
ci: Build CPU-only wheels and target CFD v0.1.6
shaia Dec 29, 2025
46c9bb1
ci: Build dual wheel variants (CPU and CUDA) for cfd-python
shaia Dec 29, 2025
9539828
fix: Link modular CFD libraries for v0.1.6 compatibility
shaia Dec 29, 2025
f995bec
fix: Use pip instead of uv for wheel installation in tests
shaia Dec 29, 2025
255174b
chore: Use CUDA 12.0.0 for better compatibility
shaia Dec 29, 2025
95e7238
fix: Remove non-standard wheel filename renaming
shaia Dec 29, 2025
c6052e7
docs: Add CHANGELOG and update migration plan for v0.1.6
shaia Dec 29, 2025
441f001
fix: Simplify CUDA toolkit installation in CI
shaia Dec 29, 2025
263da63
Move pytest import to module level in test_boundary_conditions.py
shaia Dec 29, 2025
d63ab96
Install GCC before CUDA toolkit to fix installation errors
shaia Dec 29, 2025
6cfd475
Update CHANGELOG with latest CI/build fixes
shaia Dec 29, 2025
2a4c435
docs: Fix version references for consistency with v0.1.6
shaia Dec 30, 2025
3269912
fix: Use network method for CUDA installation to avoid hangs
shaia Dec 30, 2025
f656648
fix: Use local method with override flag for CUDA installation
shaia Dec 30, 2025
075ccc6
fix: Use NVIDIA package repository for CUDA installation
shaia Dec 31, 2025
ce625fe
fix: Install minimal CUDA packages to avoid broken dependencies
shaia Dec 31, 2025
247ff53
fix: Install GCC 12 for CUDA 12.0 compatibility
shaia Dec 31, 2025
fded3e0
fix: Upgrade to CUDA 12.4 for modern compiler compatibility
shaia Dec 31, 2025
d195a08
test: Skip write_csv_timeseries tests pending CFD library fix
shaia Dec 31, 2025
22420ec
fix: Disable uv cache in test job to prevent cache errors
shaia Dec 31, 2025
a49ca71
fix: Use apt for CUDA runtime in Linux test job
shaia Dec 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 131 additions & 18 deletions .github/workflows/build-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,23 @@ on:
workflow_dispatch:
workflow_call:

env:
# CFD C library version to build against
# v0.1.6 introduces modular backend libraries
CFD_VERSION: "v0.1.6"
Comment on lines +12 to +13
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version mismatch: The PR title and description claim to pin CFD library to v0.1.5, but the workflow sets CFD_VERSION to "v0.1.6". The MIGRATION_PLAN.md also targets v0.1.6 with modular backend libraries, which is a different version than advertised. Either update the PR description to reflect v0.1.6, or change the CFD_VERSION to v0.1.5 to match the description.

Suggested change
# v0.1.6 introduces modular backend libraries
CFD_VERSION: "v0.1.6"
# Pin to v0.1.5 as advertised in the PR title and description
CFD_VERSION: "v0.1.5"

Copilot uses AI. Check for mistakes.

jobs:
build_wheel:
name: Build wheel on ${{ matrix.os }}
name: Build ${{ matrix.variant }} wheel on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
variant: [cpu, cuda]
exclude:
# macOS doesn't support CUDA
- os: macos-latest
variant: cuda

steps:
- uses: actions/checkout@v4
Expand All @@ -24,6 +34,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/cfd
ref: ${{ env.CFD_VERSION }}
path: cfd
fetch-depth: 0

Expand All @@ -41,22 +52,100 @@ jobs:
- name: Install build dependencies
run: uv pip install --system build scikit-build-core setuptools-scm

- name: Build CFD library (Unix)
if: runner.os != 'Windows'
# ============ CPU-only builds ============
- name: Build CFD library (Linux - CPU only)
if: runner.os == 'Linux' && matrix.variant == 'cpu'
run: |
cmake -S cfd -B cfd/build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON
# Build CPU-only for maximum compatibility
# Includes: Scalar, SIMD (AVX2), OpenMP backends
# Excludes: CUDA (avoid runtime dependency issues)
cmake -S cfd -B cfd/build \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCFD_ENABLE_CUDA=OFF
cmake --build cfd/build --config Release
echo "=== CFD library built ==="
echo "=== CFD library built (CPU-only) ==="
ls -la cfd/build/lib/

- name: Build CFD library (Windows)
if: runner.os == 'Windows'
- name: Build CFD library (macOS - CPU only)
if: runner.os == 'macOS'
run: |
cmake -S cfd -B cfd/build \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCFD_ENABLE_CUDA=OFF
cmake --build cfd/build --config Release
echo "=== CFD library built (CPU-only) ==="
ls -la cfd/build/lib/

- name: Build CFD library (Windows - CPU only)
if: runner.os == 'Windows' && matrix.variant == 'cpu'
run: |
# Build CPU-only for maximum compatibility
cmake -S cfd -B cfd/build `
-DCMAKE_BUILD_TYPE=Release `
-DBUILD_SHARED_LIBS=OFF `
-DCMAKE_POSITION_INDEPENDENT_CODE=ON `
-DCFD_ENABLE_CUDA=OFF
cmake --build cfd/build --config Release
echo "=== CFD library built (CPU-only) ==="
dir cfd\build\lib\Release

# ============ CUDA builds ============
# Using CUDA 12.4 for compatibility with latest compilers:
# - Windows: MSVC 14.44 requires CUDA 12.4+
# - Linux: GCC 13 requires CUDA 12.4+ (or use GCC 12 with older CUDA)
- name: Install CUDA Toolkit (Linux)
if: runner.os == 'Linux' && matrix.variant == 'cuda'
run: |
# Install minimal CUDA 12.4 packages (avoid nsight tools with broken dependencies)
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt-get update
# Install only compiler and runtime libraries (no profiling tools)
sudo apt-get install -y cuda-nvcc-12-4 cuda-cudart-dev-12-4 cuda-nvrtc-dev-12-4 libcublas-dev-12-4 libcusparse-dev-12-4
echo "/usr/local/cuda-12.4/bin" >> $GITHUB_PATH
echo "CUDA_PATH=/usr/local/cuda-12.4" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH" >> $GITHUB_ENV

- name: Build CFD library (Linux with CUDA)
if: runner.os == 'Linux' && matrix.variant == 'cuda'
run: |
# Build with CUDA for Turing+ architectures (RTX 20 series onwards)
# 75=Turing, 80=Ampere, 86=Ampere, 89=Ada, 90=Hopper
cmake -S cfd -B cfd/build \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCFD_ENABLE_CUDA=ON \
-DCFD_CUDA_ARCHITECTURES="75;80;86;89;90"
cmake --build cfd/build --config Release
echo "=== CFD library built with CUDA ==="
ls -la cfd/build/lib/

- name: Install CUDA Toolkit (Windows)
if: runner.os == 'Windows' && matrix.variant == 'cuda'
uses: Jimver/cuda-toolkit@v0.2.18
with:
cuda: '12.4.0'

- name: Build CFD library (Windows with CUDA)
if: runner.os == 'Windows' && matrix.variant == 'cuda'
run: |
cmake -S cfd -B cfd/build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
# Build with CUDA for Turing+ architectures
cmake -S cfd -B cfd/build `
-DCMAKE_BUILD_TYPE=Release `
-DBUILD_SHARED_LIBS=OFF `
-DCMAKE_POSITION_INDEPENDENT_CODE=ON `
-DCFD_ENABLE_CUDA=ON `
-DCFD_CUDA_ARCHITECTURES="75;80;86;89;90"
cmake --build cfd/build --config Release
echo "=== CFD library built ==="
echo "=== CFD library built with CUDA ==="
dir cfd\build\lib\Release

# ============ Build wheels ============
- name: Build wheel (Unix)
if: runner.os != 'Windows'
env:
Expand Down Expand Up @@ -90,19 +179,27 @@ jobs:
print(name)
"

# Upload wheels with variant in artifact name
# Note: Wheel filenames are standard (no variant suffix) to comply with PEP 427
# The variant (cpu/cuda) is encoded in the artifact name for differentiation
- uses: actions/upload-artifact@v4
with:
name: wheel-${{ matrix.os }}
name: wheel-${{ matrix.os }}-${{ matrix.variant }}
path: dist/*.whl

test_wheel:
name: Test wheel on ${{ matrix.os }} with Python ${{ matrix.python }}
name: Test ${{ matrix.variant }} wheel on ${{ matrix.os }} with Python ${{ matrix.python }}
needs: [build_wheel]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python: ["3.9", "3.13"]
variant: [cpu, cuda]
exclude:
# macOS doesn't have CUDA wheels
- os: macos-latest
variant: cuda

steps:
- name: Set up Python ${{ matrix.python }}
Expand All @@ -113,25 +210,41 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: ""
enable-cache: false

# Install CUDA runtime for CUDA wheel tests (must match build version)
- name: Install CUDA Toolkit (Linux - for testing)
if: runner.os == 'Linux' && matrix.variant == 'cuda'
run: |
# Install CUDA runtime libraries via apt (more reliable than runfile installer)
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt-get update
sudo apt-get install -y cuda-cudart-12-4
echo "LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH" >> $GITHUB_ENV

- name: Install CUDA Toolkit (Windows - for testing)
if: runner.os == 'Windows' && matrix.variant == 'cuda'
uses: Jimver/cuda-toolkit@v0.2.18
with:
cuda: '12.4.0'

- uses: actions/download-artifact@v4
with:
name: wheel-${{ matrix.os }}
name: wheel-${{ matrix.os }}-${{ matrix.variant }}
path: dist

- name: Install wheel (Unix)
if: runner.os != 'Windows'
run: |
uv pip install --system dist/*.whl
uv pip install --system pytest numpy
python -m pip install dist/*.whl
python -m pip install pytest numpy

- name: Install wheel (Windows)
if: runner.os == 'Windows'
run: |
uv pip install --system (Get-ChildItem dist/*.whl).FullName
uv pip install --system pytest numpy
python -m pip install (Get-ChildItem dist/*.whl).FullName
python -m pip install pytest numpy

- name: Test import (Unix)
if: runner.os != 'Windows'
Expand Down
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Dual-variant wheel builds supporting both CPU-only and CUDA-enabled configurations
- Matrix build strategy in CI for separate CPU and CUDA wheel artifacts
- Support for CFD library v0.1.6 modular backend libraries

### Changed
- Updated build system to link modular CFD libraries (cfd_api, cfd_core, cfd_scalar, cfd_simd, cfd_omp, cfd_cuda)
- Migrated to CUDA 12.0.0 from 12.6.2 for better stability and compatibility
- Switched from `uv pip` to standard `pip` for wheel installation in CI tests
- Updated CMakeLists.txt to use GNU linker groups on Linux for circular dependency resolution

### Fixed
- CMake library detection for CFD v0.1.6 static builds
- Wheel installation compatibility with Python stable ABI (abi3) wheels
- Removed non-standard wheel filename modifications for PEP 427 compliance
- CUDA toolkit installation by installing GCC 11 before CUDA on Linux
- Simplified CUDA toolkit installation by removing sub-packages parameter
- Test code style: moved pytest imports to module level for consistency

## [0.1.0] - 2025-12-26

### Added
- Initial Python bindings for CFD library v0.1.5
- Core simulation API bindings (create, step, destroy)
- Solver registry and solver creation
- Grid management functions
- Boundary condition API (periodic, neumann, dirichlet, noslip, inlet, outlet)
- Backend selection for boundary conditions (scalar, SIMD, OpenMP, CUDA)
- Error handling API
- Basic test suite
- GitHub Actions CI/CD pipeline

### Changed
- Updated to CFD library v0.1.5 API (context-bound registry, new type names)
- Migrated from bundled headers to system-installed CFD library

### Technical Details
- Python 3.9+ support using stable ABI (abi3)
- Static linking of CFD library into extension module
- NumPy integration for array handling
- scikit-build-core for modern build system
77 changes: 71 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,63 @@ else()
endif()
message(STATUS "CFD_ROOT_DIR: ${CFD_ROOT_DIR}")

# Source headers in lib/include, generated headers (cfd_export.h) in build/lib/include
set(CFD_INCLUDE_DIR "${CFD_ROOT_DIR}/lib/include")
set(CFD_BUILD_INCLUDE_DIR "${CFD_ROOT_DIR}/build/lib/include")
set(CFD_LIBRARY_DIRS
"${CFD_ROOT_DIR}/build/lib/Release"
"${CFD_ROOT_DIR}/build/lib"
)

find_library(CFD_LIBRARY
NAMES cfd_library cfd_library_static
# For static builds (v0.1.6+), CFD library uses modular backend libraries
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment states v0.1.6+ but some documentation in this PR references v0.1.5+. Ensure version references are consistent throughout the PR to avoid confusion about which CFD library version is actually required.

Suggested change
# For static builds (v0.1.6+), CFD library uses modular backend libraries
# For static builds, the CFD library uses modular backend libraries

Copilot uses AI. Check for mistakes.
# We need to find and link all of them
set(CFD_LIBRARIES "")
set(CFD_MODULAR_LIBS cfd_api cfd_core cfd_scalar cfd_simd cfd_omp)

# Check if CUDA library exists (optional)
find_library(CFD_CUDA_LIB
NAMES cfd_cuda
PATHS ${CFD_LIBRARY_DIRS}
NO_DEFAULT_PATH
)
if(CFD_CUDA_LIB)
list(APPEND CFD_MODULAR_LIBS cfd_cuda)
message(STATUS "Found CFD CUDA library: ${CFD_CUDA_LIB}")
endif()

# Try to find each modular library
foreach(lib_name ${CFD_MODULAR_LIBS})
find_library(CFD_${lib_name}_LIB
NAMES ${lib_name}
PATHS ${CFD_LIBRARY_DIRS}
NO_DEFAULT_PATH
)
if(CFD_${lib_name}_LIB)
list(APPEND CFD_LIBRARIES ${CFD_${lib_name}_LIB})
message(STATUS "Found ${lib_name}: ${CFD_${lib_name}_LIB}")
else()
message(FATAL_ERROR "${lib_name} not found in ${CFD_LIBRARY_DIRS}")
endif()
endforeach()

if(NOT CFD_LIBRARY)
message(FATAL_ERROR "CFD library not found in ${CFD_LIBRARY_DIRS}")
# On Linux, wrap in linker groups due to circular dependencies
# (cfd_scalar/cfd_simd call poisson_solve from cfd_api)
if(UNIX AND NOT APPLE)
set(CFD_LIBRARIES "-Wl,--start-group" ${CFD_LIBRARIES} "-Wl,--end-group")
message(STATUS "Using linker groups for CFD libraries (Linux)")
endif()

message(STATUS "Found CFD library: ${CFD_LIBRARY}")
message(STATUS "CFD libraries: ${CFD_LIBRARIES}")
message(STATUS "CFD include dir: ${CFD_INCLUDE_DIR}")

# Find OpenMP - the CFD library uses OpenMP for parallel backends
find_package(OpenMP)
if(OpenMP_C_FOUND)
message(STATUS "OpenMP found: ${OpenMP_C_FLAGS}")
else()
message(STATUS "OpenMP not found - parallel backends will not be available")
endif()

# Create the Python extension module
# For stable ABI on Windows, we need to manually create the library
# to avoid Python_add_library linking against version-specific python3X.lib
Expand Down Expand Up @@ -113,12 +151,39 @@ endif()

target_include_directories(cfd_python PRIVATE
${CFD_INCLUDE_DIR}
${CFD_BUILD_INCLUDE_DIR}
${Python_INCLUDE_DIRS}
)

target_link_libraries(cfd_python PRIVATE
${CFD_LIBRARY}
${CFD_LIBRARIES}
)

# Link OpenMP if available (required for CFD library's parallel backends)
if(OpenMP_C_FOUND)
target_link_libraries(cfd_python PRIVATE OpenMP::OpenMP_C)
endif()

# Link CUDA runtime if the CFD library was built with CUDA support
# Check if cudart library exists in the CFD build directory
find_library(CUDART_LIBRARY
NAMES cudart cudart_static
PATHS ${CFD_LIBRARY_DIRS}
NO_DEFAULT_PATH
)
if(CUDART_LIBRARY)
message(STATUS "Found CUDA runtime in CFD build: ${CUDART_LIBRARY}")
target_link_libraries(cfd_python PRIVATE ${CUDART_LIBRARY})
else()
# Try system CUDA if available
find_package(CUDAToolkit QUIET)
if(CUDAToolkit_FOUND)
message(STATUS "Found system CUDA toolkit, linking cudart")
target_link_libraries(cfd_python PRIVATE CUDA::cudart)
else()
message(STATUS "CUDA not found - GPU solvers will not be available")
endif()
endif()

# Install the extension module
install(TARGETS cfd_python DESTINATION cfd_python)
Loading
Loading