B-Simulant LibraryOverview

B-Simulant Library

B-Simulant (bsim) is the open-source Python library that powers the bsim platform. Use it for local simulations, custom compositions, and integration into your own workflows.

Installation

pip install bsim

For specific adapters:

# SBML support (requires tellurium)
pip install bsim[tellurium]
 
# ONNX/ML support
pip install bsim[ml]
 
# All adapter extras (tellurium + ml)
pip install bsim[adapters]
 
# SimUI extras
pip install bsim[ui]

Quick Example

import bsim
from bsim.packs.ecology import OrganismPopulation, PredatorPreyInteraction, PopulationMonitor
 
# Create simulation world
world = bsim.BioWorld(solver=bsim.FixedStepSolver())
 
# Build composition
wb = bsim.WiringBuilder(world)
wb.add("prey", OrganismPopulation(name="Prey", initial_count=100, birth_rate=0.1, death_rate=0.02))
wb.add("predator", OrganismPopulation(name="Predator", initial_count=10, birth_rate=0.01, death_rate=0.1))
wb.add("interaction", PredatorPreyInteraction(predation_rate=0.01))
wb.add("monitor", PopulationMonitor())
 
# Wire connections
wb.connect("prey.out.population_state", ["interaction.in.prey_state", "monitor.in.population_state"])
wb.connect("predator.out.population_state", ["interaction.in.predator_state", "monitor.in.population_state"])
wb.connect("interaction.out.predation", ["prey.in.predation"])
wb.connect("interaction.out.food_gained", ["predator.in.food_gained"])
wb.apply()
 
# Run simulation
result = world.simulate(steps=10000, dt=0.01)
print(result)

Core Concepts

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      BioWorld                                │
│              (Central Orchestrator)                          │
│                                                              │
│  Events: LOADED → BEFORE_SIMULATION → STEP* → AFTER_SIM    │
└─────────────────────────────┬───────────────────────────────┘

         ┌────────────────────┼────────────────────┐
         │                    │                    │
         ▼                    ▼                    ▼
┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│   BioModule     │  │   BioModule     │  │   Adapter       │
│  (Neuron Pop)   │  │  (Monitor)      │  │  (SBML Model)   │
│                 │  │                 │  │                 │
│ in: current     │  │ in: spikes      │  │ in: params      │
│ out: spikes     │──│                 │  │ out: species    │
└─────────────────┘  └─────────────────┘  └─────────────────┘
         │                                         │
         └─────────────── BioSignals ──────────────┘

Key Components

BioWorld

The central orchestrator that:

  • Manages simulation lifecycle
  • Coordinates module interactions
  • Handles event flow and messaging
  • Provides cooperative pause/resume/stop controls via request_* methods

BioModule

Base interface for all composable components:

  • Declare input/output ports
  • Subscribe to events
  • Send and receive BioSignals
  • Expose visualization data

Solver

Execution strategies:

  • FixedStepSolver - Basic time-stepping
  • DefaultBioSolver - Extended with biological quantities (temperature, rates)
  • Custom Solvers - Extensible via subclassing

WiringBuilder

Declarative composition API:

  • Add modules by name
  • Connect ports with validation
  • Support for YAML/TOML config files

Features

Event-Driven Architecture

class BioWorldEvent(Enum):
    LOADED = auto()
    BEFORE_SIMULATION = auto()
    STEP = auto()
    AFTER_SIMULATION = auto()
    ERROR = auto()
    PAUSED = auto()

Port Validation

class MyModule(BioModule):
    def inputs(self):
        return {"current", "voltage"}
 
    def outputs(self):
        return {"spikes", "membrane_potential"}

BioSignals

Message passing between modules uses plain payload dicts:

world.publish_biosignal(self, topic="spikes", payload={"t": t, "ids": spike_ids})

Adapters use BioSignal (from bsim.adapters) for cross-adapter data exchange.

Visualization Contract

JSON-based renderer-agnostic output:

def visualize(self) -> dict:
    return {
        "render": "timeseries",
        "data": {
            "title": "Population Over Time",
            "series": [
                {"name": "Prey", "points": self.prey_history},
                {"name": "Predator", "points": self.predator_history}
            ]
        }
    }

Next Steps