This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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.
make install # Install poetry env + pre-commit hooksscripts/local_build_native.sh # Full local native build (macOS)
poetry build # Build Python wheel (triggers build.py which builds Java/native)make check # Runs: poetry check --lock, pre-commit, mypy, deptrypoetry run pytest # Run all tests
poetry run pytest tests/test_libvcell.py # Run specific test file
poetry run pytest tests/test_libvcell.py::test_name # Run single test
poetry run pytest --cov --cov-config=pyproject.toml --cov-report=xml # With coverageThe 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):
mvn -o test -f vcell-native # All vcell-native Java tests (offline)
mvn -o test -f vcell-native -Dtest=MiscTests # Single test classThese 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.
poetry run mypy # Type check (strict mode, covers libvcell/, tests/, build.py)Pre-commit hooks run ruff (lint + format) and prettier automatically.
Python layer (libvcell/):
__init__.py— Public API:vcml_to_finite_volume_input,sbml_to_finite_volume_input,vcml_to_moving_boundary_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)solver_utils.py/model_utils.py— Thin wrappers that instantiateVCellNativeCallsand delegate to native methods_internal/native_utils.py— Loads the platform-specific shared library (.so/.dylib/.dll) fromlibvcell/lib/via ctypes; definesIsolateManagercontext manager for GraalVM isolate lifecycle_internal/native_calls.py— ctypes FFI calls to the native library entry points; handles GraalVM isolate creation/teardown per call, JSON deserialization ofReturnValue
Native/Java layer (vcell-native/):
Entrypoints.java—@CEntryPointmethods exposed as C symbols (vcmlToFiniteVolumeInput,sbmlToFiniteVolumeInput,vcmlToMovingBoundaryInput,vcmlToSbml,sbmlToVcml,vcmlToVcml,vcellInfixToPythonInfix)ModelUtils.java/SolverUtils.java— Java implementation using vcell-core from thevcell_submodulesolvers/LocalFVSolverStandalone.java/solvers/LocalMovingBoundarySolverStandalone.java— thin subclasses of the vcell-core solvers that expose an input-only write path (they skip the native-executable lookup that the stockinitialize()performs), so libvcell can generate solver input files without the solver binaries. The Moving Boundary input is aMovingBoundarySetupXML consumed downstream by thevcell-mbsolverpackage (MovingBoundarySolver.from_xml)- Built with Maven, then compiled to a shared library via GraalVM
native-maven-pluginusing theshared-dllprofile MainRecorder.java— Used withnative-image-agentto record dynamic reflection/resource configs before native compilation
Build pipeline (build.py):
mvn clean install -DskipTestsonvcell_submodule/(full VCell Java project)mvn clean installonvcell-native/(builds the shaded JAR)- Run JAR with
native-image-agentto record native-image config intotarget/recording/ mvn package -P shared-dllto produce the native shared library- Copy resulting
libvcell.{so,dylib,dll}intolibvcell/lib/
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.
vcell_submodule/— Git submodule pointing to the full VCell Java repository (provides vcell-core). After cloning or pulling a submodule pointer bump, rungit 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 asM vcell_submoduleingit status). A stale checkout causesvcell-nativeto fail compiling against vcell-core. To make vcell-core/math available for a localvcell-nativebuild, install them into.m2first:mvn -DskipTests clean install -f vcell_submodule(or run the fullscripts/local_build_native.sh).- GraalVM JDK 23 with
native-imagetool required for building native library (.java-versionpinsgraalvm64-23.0.2) - Python >=3.10,<4.0, pydantic for data models
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.
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.
GitHub Actions (.github/workflows/main.yml) runs on push to main and PRs:
- Quality checks (pre-commit, mypy, deptry) on ubuntu
- Tests + type checking across matrix: macOS (Intel + ARM), Windows, Ubuntu
- All CI jobs require GraalVM setup for native library compilation