|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +libvcell is a Python package that wraps a subset of VCell (Virtual Cell) Java algorithms as a native shared library via GraalVM native-image. It provides Python functions for converting between VCML, SBML, and finite volume solver input formats, plus VCell-to-Python math expression translation. |
| 8 | + |
| 9 | +## Build & Development Commands |
| 10 | + |
| 11 | +### Setup |
| 12 | +```bash |
| 13 | +make install # Install poetry env + pre-commit hooks |
| 14 | +``` |
| 15 | + |
| 16 | +### Build the native shared library (requires GraalVM JDK 23 with native-image) |
| 17 | +```bash |
| 18 | +scripts/local_build_native.sh # Full local native build (macOS) |
| 19 | +poetry build # Build Python wheel (triggers build.py which builds Java/native) |
| 20 | +``` |
| 21 | + |
| 22 | +### Quality checks |
| 23 | +```bash |
| 24 | +make check # Runs: poetry check --lock, pre-commit, mypy, deptry |
| 25 | +``` |
| 26 | + |
| 27 | +### Tests |
| 28 | +```bash |
| 29 | +poetry run pytest # Run all tests |
| 30 | +poetry run pytest tests/test_libvcell.py # Run specific test file |
| 31 | +poetry run pytest tests/test_libvcell.py::test_name # Run single test |
| 32 | +poetry run pytest --cov --cov-config=pyproject.toml --cov-report=xml # With coverage |
| 33 | +``` |
| 34 | + |
| 35 | +The Java/native unit tests (`vcell-native/src/test/`) run separately via Maven and exercise the entry points against vcell-core directly (no native-image step): |
| 36 | +```bash |
| 37 | +mvn -o test -f vcell-native # All vcell-native Java tests (offline) |
| 38 | +mvn -o test -f vcell-native -Dtest=MiscTests # Single test class |
| 39 | +``` |
| 40 | +These require the submodule artifacts to already be installed in `.m2` (see Key dependencies); otherwise compilation fails with errors like `cannot find symbol` for vcell-core methods. The CI `quality` job is what runs these (it's the first to fail on a broken vcell-native test). Avoid asserting against full exception stack traces in these tests — frames embed vcell-core line numbers (drift on submodule bumps) and differ between IDE and Surefire runners. |
| 41 | + |
| 42 | +### Type checking & linting |
| 43 | +```bash |
| 44 | +poetry run mypy # Type check (strict mode, covers libvcell/, tests/, build.py) |
| 45 | +``` |
| 46 | + |
| 47 | +Pre-commit hooks run ruff (lint + format) and prettier automatically. |
| 48 | + |
| 49 | +## Architecture |
| 50 | + |
| 51 | +### Two-layer design: Python wrapper over GraalVM native library |
| 52 | + |
| 53 | +**Python layer** (`libvcell/`): |
| 54 | +- `__init__.py` — Public API: `vcml_to_finite_volume_input`, `sbml_to_finite_volume_input`, `sbml_to_vcml`, `vcml_to_sbml`, `vcml_to_vcml`, `vcell_infix_to_python_infix`, `vcell_infix_to_num_expr_infix`. Also exposes `__version__` (read from installed package metadata; `"0.0.0"` when running from source tree) |
| 55 | +- `solver_utils.py` / `model_utils.py` — Thin wrappers that instantiate `VCellNativeCalls` and delegate to native methods |
| 56 | +- `_internal/native_utils.py` — Loads the platform-specific shared library (`.so`/`.dylib`/`.dll`) from `libvcell/lib/` via ctypes; defines `IsolateManager` context manager for GraalVM isolate lifecycle |
| 57 | +- `_internal/native_calls.py` — ctypes FFI calls to the native library entry points; handles GraalVM isolate creation/teardown per call, JSON deserialization of `ReturnValue` |
| 58 | + |
| 59 | +**Native/Java layer** (`vcell-native/`): |
| 60 | +- `Entrypoints.java` — `@CEntryPoint` methods exposed as C symbols (`vcmlToFiniteVolumeInput`, `sbmlToFiniteVolumeInput`, `vcmlToSbml`, `sbmlToVcml`, `vcmlToVcml`, `vcellInfixToPythonInfix`) |
| 61 | +- `ModelUtils.java` / `SolverUtils.java` — Java implementation using vcell-core from the `vcell_submodule` |
| 62 | +- Built with Maven, then compiled to a shared library via GraalVM `native-maven-plugin` using the `shared-dll` profile |
| 63 | +- `MainRecorder.java` — Used with `native-image-agent` to record dynamic reflection/resource configs before native compilation |
| 64 | + |
| 65 | +**Build pipeline** (`build.py`): |
| 66 | +1. `mvn clean install -DskipTests` on `vcell_submodule/` (full VCell Java project) |
| 67 | +2. `mvn clean install` on `vcell-native/` (builds the shaded JAR) |
| 68 | +3. Run JAR with `native-image-agent` to record native-image config into `target/recording/` |
| 69 | +4. `mvn package -P shared-dll` to produce the native shared library |
| 70 | +5. Copy resulting `libvcell.{so,dylib,dll}` into `libvcell/lib/` |
| 71 | + |
| 72 | +Linux wheels are built inside the `docker/Dockerfile_manylinux_*` images (manylinux 2_28 and 2_34, for both `aarch64` and `x86_64`), which provide the GraalVM toolchain needed for native compilation in CI. |
| 73 | + |
| 74 | +### Key dependencies |
| 75 | +- `vcell_submodule/` — Git submodule pointing to the full VCell Java repository (provides vcell-core). After cloning or pulling a submodule pointer bump, run `git submodule update --init --recursive` — git does NOT auto-update the submodule working tree, so it can sit at an older commit than the recorded pointer (shows as `M vcell_submodule` in `git status`). A stale checkout causes `vcell-native` to fail compiling against vcell-core. To make vcell-core/math available for a local `vcell-native` build, install them into `.m2` first: `mvn -DskipTests clean install -f vcell_submodule` (or run the full `scripts/local_build_native.sh`). |
| 76 | +- GraalVM JDK 23 with `native-image` tool required for building native library (`.java-version` pins `graalvm64-23.0.2`) |
| 77 | +- Python >=3.10,<4.0, pydantic for data models |
| 78 | + |
| 79 | +### FFI pattern |
| 80 | +Each Python API call: creates `VCellNativeCalls` → loads native lib → creates GraalVM isolate → calls C entry point → receives JSON string → deserializes to `ReturnValue(success, message)` → tears down isolate. The `IsolateManager` context manager handles isolate lifecycle. |
| 81 | + |
| 82 | +### Test fixtures |
| 83 | +Test data lives in `tests/fixtures/data/` (VCML and SBML XML files). Fixtures are defined in `tests/fixtures/data_fixtures.py` and imported via `tests/conftest.py`. |
| 84 | + |
| 85 | +## CI |
| 86 | + |
| 87 | +GitHub Actions (`.github/workflows/main.yml`) runs on push to main and PRs: |
| 88 | +- Quality checks (pre-commit, mypy, deptry) on ubuntu |
| 89 | +- Tests + type checking across matrix: macOS (Intel + ARM), Windows, Ubuntu |
| 90 | +- All CI jobs require GraalVM setup for native library compilation |
0 commit comments