Skip to content

Commit

Permalink
Add python wrapper (#299)
Browse files Browse the repository at this point in the history
  • Loading branch information
mscroggs authored Sep 17, 2024
1 parent cc7df07 commit 413f162
Show file tree
Hide file tree
Showing 14 changed files with 482 additions and 24 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,33 @@ jobs:
chmod +x examples.sh
./examples.sh
run-tests-python:
name: Run Python tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: actions/checkout@v3
- name: Install uv
run: pip install uv "maturin>=1.7"
- name: Make virtual environment
run: |
uv venv .venv
uv pip install pip pytest
- name: Install python package
run: |
source .venv/bin/activate
maturin develop
- name: Run Python tests
run: |
source .venv/bin/activate
python -m pytest python/test
check-dependencies:
name: Check dependencies
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-weekly-tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: 🧪
name: 🧪📅

on:
schedule:
Expand Down
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

# nano swp files
.*.swp

*.pyc
python/bempp/_bempprs

examples.sh

Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ lazy_static = "1.4"
ndelement = { git = "https://github.com/bempp/ndelement.git" }
ndgrid = { git = "https://github.com/bempp/ndgrid.git" }
rayon = "1.9"
rlst = "0.2"
rlst = { version = "0.2.0", default-features = false }
green-kernels = "0.2.0"

[dev-dependencies]
Expand All @@ -37,6 +37,9 @@ cauchy = "0.4.*"
criterion = { version = "0.5.*", features = ["html_reports"]}
# kifmm = { version = "1.0" }

[build-dependencies]
cbindgen = "0.27.0"

[[bench]]
name = "assembly_benchmark"
harness = false
Expand Down
31 changes: 31 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

fn main() {
let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());

// Determine the target directory within the workspace root
let target_dir = env::var("CARGO_TARGET_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| crate_dir.join("target"));

// Ensure the target directory exists
fs::create_dir_all(&target_dir).expect("Unable to create target directory");

// Create the header file path
let header_path = Path::new(&target_dir).join("include").join("_bempprs.h");

let config_path = Path::new(&crate_dir).join("cbindgen.toml");
let config = cbindgen::Config::from_file(config_path).expect("Unable to load cbindgen config");

// Generate the bindings
let bindings = cbindgen::Builder::new()
.with_crate(crate_dir)
.with_config(config)
.generate()
.expect("Unable to generate bindings");

// Write the bindings to the header file
bindings.write_to_file(header_path);
}
7 changes: 7 additions & 0 deletions cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language = "C"

[export]
exclude = []

[enum]
prefix_with_name = true
7 changes: 3 additions & 4 deletions find_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
import os
import argparse

parser = argparse.ArgumentParser(description='Parse inputs.')
parser.add_argument('--features', default=None,
help='feature flags to pass to the examples')
parser = argparse.ArgumentParser(description="Parse inputs.")
parser.add_argument("--features", default=None, help="feature flags to pass to the examples")

raw_features = parser.parse_args().features

Expand Down Expand Up @@ -61,7 +60,7 @@
if options is None:
options = ""
if "--features" in options:
a, b = options.split("--features \"")
a, b = options.split('--features "')
options = f"{a}--features \"{','.join(features)},{b}"
else:
options += f" --features \"{','.join(features)}\""
Expand Down
50 changes: 50 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"

[project]
name = "bempp-rs"
version = "0.1.0-dev"
description = "Boundary element method library"
readme = "README.md"
requires-python = ">=3.8"
license = { file = "LICENSE" }
authors = [
{name = "Timo Betcke", email = "[email protected]"},
{name = "Srinath Kailasa", email = "[email protected]"},
{name = "Matthew Scroggs", email = "[email protected]"}
]
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
]
dependencies = [
"maturin>=1.7",
"numpy",
"cffi",
'patchelf; platform_system == "Linux"',
"ndelement",
"ndgrid"
]
packages = ["bempp"]

[project.urls]
homepage = "https://github.com/bempp/bempp-rs"
repository = "https://github.com/bempp/bempp-rs"

[tool.maturin]
python-source = "python"
module-name = "bempp._bempprs"

[tool.ruff]
line-length = 100
indent-width = 4

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]

[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.mypy]
ignore_missing_imports = true
3 changes: 3 additions & 0 deletions python/bempp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Bempp."""

from bempp import function_space
33 changes: 33 additions & 0 deletions python/bempp/function_space.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Function space."""

from bempp._bempprs import lib as _lib, ffi as _ffi
from ndgrid.grid import Grid
from ndelement.ciarlet import ElementFamily


class FunctionSpace(object):
"""Function space."""

def __init__(self, rs_space):
"""Initialise."""
self._rs_space = rs_space

def __del__(self):
"""Delete."""
_lib.free_space(self._rs_space)

@property
def local_size(self) -> int:
"""Number of DOFs on current process."""
return _lib.space_local_size(self._rs_space)

@property
def global_size(self) -> int:
"""Number of DOFs on all processes."""
return _lib.space_global_size(self._rs_space)


def function_space(grid: Grid, family: ElementFamily) -> FunctionSpace:
return FunctionSpace(
_lib.space_new(_ffi.cast("void*", grid._rs_grid), _ffi.cast("void*", family._rs_family))
)
40 changes: 40 additions & 0 deletions python/test/test_function_space.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import pytest
from bempp.function_space import function_space
from ndgrid.shapes import regular_sphere
from ndelement.ciarlet import create_family, Family, Continuity
from ndelement.reference_cell import ReferenceCellType


@pytest.mark.parametrize("level", range(4))
def test_create_space_dp0(level):
grid = regular_sphere(level)
element = create_family(Family.Lagrange, 0, Continuity.Discontinuous)

space = function_space(grid, element)

assert space.local_size == grid.entity_count(ReferenceCellType.Triangle)
assert space.local_size == space.global_size


@pytest.mark.parametrize("level", range(4))
def test_create_space_p1(level):
grid = regular_sphere(level)
element = create_family(Family.Lagrange, 1)

space = function_space(grid, element)

assert space.local_size == grid.entity_count(ReferenceCellType.Point)
assert space.local_size == space.global_size


@pytest.mark.parametrize("level", range(4))
def test_create_space_p2(level):
grid = regular_sphere(level)
element = create_family(Family.Lagrange, 2)

space = function_space(grid, element)

assert space.local_size == grid.entity_count(ReferenceCellType.Point) + grid.entity_count(
ReferenceCellType.Interval
)
assert space.local_size == space.global_size
Loading

0 comments on commit 413f162

Please sign in to comment.