Installation

From source

# Requires: cmake ≥ 3.20, C++17 compiler, Python ≥ 3.10
cd bngsim
pip install .

pip install . uses build isolation, so pip provisions the build backend (scikit-build-core, pybind11) automatically from [build-system].requires.

Pass --no-build-isolation only if you have already installed the build backend into the active environment yourself:

pip install "scikit-build-core>=0.10" "pybind11>=2.13"
pip install --no-build-isolation .

Otherwise --no-build-isolation fails with ModuleNotFoundError: No module named 'scikit_build_core'.

Resource-constrained builds (containers, CI). The C++ build defaults to compiling with all available cores. On a machine with many CPUs but limited RAM (e.g. a 12-core container capped at ~6 GiB), parallel compilation of the vendored SUNDIALS/NFsim translation units can exhaust memory and get OOM-killed. Cap the parallelism with the standard CMake env var:

CMAKE_BUILD_PARALLEL_LEVEL=2 pip install .

Managed builds fetch the pinned SUNDIALS release archive recorded in third_party/sundials/VENDOR.json. ExprTk is vendored directly. Set -DBNGSIM_USE_SYSTEM_SUNDIALS=ON to use an environment-managed SUNDIALS install instead. See scripts/SUNDIALS_VENDORING.md for the supported refresh/check workflow.

Sparse solver (SuiteSparse / KLU) — required for large / genome-scale models

For models with more than ~50 species, the ODE backend routes the CVODE Newton solve to a sparse direct linear solver (SuiteSparse/KLU). Without KLU the build falls back to the dense solver, which factorizes the full N×N Jacobian at O(N³). For a genome-scale model this is the difference between minutes and hours — e.g. a 74,795-species network has a Jacobian that is 99.997% zeros (dense storage ≈ 45 GB), so the dense path is catastrophic.

KLU is discovered automatically at build time from any standard install location, conda prefix, HPC module, or CMAKE_PREFIX_PATH (GH #209). If SuiteSparse is not found, the build still succeeds — dense-only — so verify (below) after installing on a new machine, or pass -DBNGSIM_REQUIRE_KLU=ON to turn a miss into a hard build failure (recommended for HPC/CI deployments).

macOS — found from Homebrew:

brew install suite-sparse

Linux (system package) — found from /usr:

sudo apt-get install libsuitesparse-dev     # Debian/Ubuntu
sudo dnf install suitesparse-devel           # RHEL/Rocky/Fedora

HPC / conda (no root, modules-based clusters). Provide SuiteSparse, then build bngsim from source — discovery honors $CONDA_PREFIX and CMAKE_PREFIX_PATH with no explicit paths:

conda install -c conda-forge suitesparse     # discovered via $CONDA_PREFIX
#   …or on a module cluster:  module load suitesparse

# Build from source. $CONDA_PREFIX is searched automatically; -DBNGSIM_REQUIRE_KLU=ON
# fails the build (instead of going dense-only) if SuiteSparse is somehow not found.
CMAKE_ARGS="-DBNGSIM_REQUIRE_KLU=ON" \
  pip install --no-binary=bngsim --force-reinstall bngsim

If SuiteSparse lives on a non-standard prefix that is neither $CONDA_PREFIX nor on CMAKE_PREFIX_PATH, point CMake at it with -DKLU_ROOT=<prefix> (or -DSUITESPARSE_ROOT, or an explicit -DKLU_INCLUDE_DIR/-DKLU_LIBRARY_DIR). Watch the configure log for BNGsim: sparse Jacobian support ENABLED (KLU) (vs DISABLED (KLU not found)).

Verify KLU is active in any install — no model needed:

python -c "import bngsim; print('KLU available:', bngsim.capabilities()['features']['klu'])"
# -> KLU available: True

If it prints False, the install is dense-only — large models will be orders of magnitude slower, and bngsim.capabilities()['missing']['klu'] carries the rebuild recipe. A large model run on a dense-only install also emits a one-time bngsim.DenseSolverFallbackWarning at run(). See internal#209 for details.

Editable rebuilds for local development

pip install -e for bngsim is only partially in-place:

  • Python modules are imported from bngsim/python/bngsim/ via _bngsim_editable.pth.

  • The compiled runtime extension is imported from your environment’s site-packages/bngsim/_bngsim_core...so.

That means rebuilding bngsim/build/{wheel_tag}/_bngsim_core...so is not enough on its own. Python will keep importing the copy in site-packages/ until you reinstall or run cmake --install against the live environment. The current editable hook is the default scikit-build-core redirect mode with editable.rebuild = false, so imports do not automatically refresh the copied extension.

Use the helper below from the bngsim/ project directory (or via uv run --directory bngsim) to rebuild and reinstall the extension for the current interpreter. On macOS it also reconfigures stale editable caches to the current interpreter architecture and rebuilds only the _bngsim_core extension target:

uv run --directory bngsim python scripts/rebuild_editable.py

Then run a quick regression check against the installed runtime:

uv run --directory bngsim python -m pytest python/tests/test_method_normalization.py -q

Optional dependencies

pip install bngsim            # core package
pip install bngsim[antimony]  # Antimony .ant loading
pip install bngsim[pandas]   # pandas DataFrame support
pip install bngsim[hdf5]     # HDF5 save/load (h5py)
pip install bngsim[dev]      # all of the above + pytest + ruff

Antimony version note

  • Antimony-backed model loading requires bngsim[antimony].

  • For repeated SBML conversion workloads (loadSBMLFile + getSBMLString loops), use Antimony 3.1.2 due an upstream memory-fix patch.

  • Until 3.1.2 is published on PyPI, download the wheel from: https://github.com/sys-bio/antimony/actions/runs/22922160697/

Capability introspection

Downstream tools (PyBNF, PyBioNetGen, etc.) can probe what an installed bngsim actually supports without a try/except dance. Two surfaces:

Module-level boolean flags — match the existing pattern; safe to read unconditionally.

import bngsim
bngsim.HAS_NFSIM       # compiled C++ NFsim backend
bngsim.HAS_RULEMONKEY  # compiled C++ RuleMonkey backend
bngsim.HAS_LIBSBML     # optional Python dependency 'python-libsbml'
bngsim.HAS_ANTIMONY    # optional Python dependency 'antimony'

Aggregatorbngsim.capabilities() returns a structured dict whose schema is stable across releases. Feature names will not be renamed or removed; new features may be added.

import bngsim
caps = bngsim.capabilities()
caps["version"]                       # bngsim package version, e.g. '0.4.1'
caps["features"]["sbml_ssa"]          # bool — can this install run SBML SSA?
caps["features"]["antimony_import"]   # bool — needs both libsbml AND antimony
caps["missing"]                       # dict[str, str] — only contains entries
                                      # for unavailable features

caps["missing"][name] distinguishes the two failure modes a downstream tool needs to report differently:

  • Compiled backend gap — message starts with “ backend not present in this install” and names both the vendored source path and the CMake flag (e.g. -DBNGSIM_BUILD_RULEMONKEY=OFF). NFsim and RuleMonkey are vendored in third_party/ and built by default in source builds, so this typically means the install came from a wheel that excludes the backend (or the user explicitly built with the flag set OFF). Caller should tell users to reinstall a wheel/build that includes the backend.

  • Optional Python dependency — message starts with “optional dependency” and names the PyPI package ('python-libsbml', 'antimony'). Caller should tell users to pip install it.

Stable feature keys: nfsim, rulemonkey, libsbml, antimony, sbml_import, sbml_ssa, sbml_psa, antimony_import, codegen.