OverviewLibrary Quickstart (Python)

Library Quickstart (Python)

Install the open-source biosimulant Python package and run a small hello-world growth simulation locally. Modules exchange typed signals at communication-step boundaries; this is the core mental model the rest of Biosimulant builds on.

If you’d rather pull and run a packaged lab from the terminal, see References > CLI or, for the GUI workbench, the Desktop App. To run in the browser with no install, see the Web Quickstart.

Install

Requires Python 3.10+.

pip install biosimulant

If you’re working from a checkout of the library repo:

pip install -e .

Some features need optional extras (for example, the SimUI web interface):

pip install "biosimulant[ui]"

What you’ll build

  • GrowthSource emits a count signal.
  • GrowthReporter consumes the count and prints it.
  • BioWorld advances both modules in fixed communication windows.

Write the simulation

Create first_simulation.py:

import biosimulant as biosim
 
 
class GrowthSource(biosim.BioModule):
    def __init__(self, initial_count: int = 10, growth_rate: float = 0.2):
        self.count = int(initial_count)
        self.growth_rate = float(growth_rate)
        self._outputs = {}
 
    def outputs(self):
        return {"count": biosim.SignalSpec.scalar(dtype="int64", emitted_unit="cells")}
 
    def advance_window(self, start: float, end: float) -> None:
        self.count += max(1, int(self.count * self.growth_rate))
        self._outputs = {
            "count": biosim.ScalarSignal(
                source="growth",
                name="count",
                value=self.count,
                emitted_at=end,
                spec=self.outputs()["count"],
            )
        }
 
    def get_outputs(self):
        return dict(self._outputs)
 
 
class GrowthReporter(biosim.BioModule):
    def __init__(self):
        self._inputs = {}
 
    def inputs(self):
        return {
            "count": biosim.SignalSpec.scalar(
                dtype="int64",
                max_age=0.2,
                stale_policy="warn",
            )
        }
 
    def set_inputs(self, signals):
        self._inputs = dict(signals)
 
    def advance_window(self, start: float, end: float) -> None:
        signal = self._inputs.get("count")
        if signal is not None:
            print(f"[reporter] t={end:.1f}, count={signal.value}")
 
    def get_outputs(self):
        return {}
 
 
world = biosim.BioWorld(communication_step=0.1)
builder = biosim.WiringBuilder(world)
 
builder.add("growth", GrowthSource())
builder.add("reporter", GrowthReporter())
builder.connect("growth.count", ["reporter.count"])
builder.apply()
 
world.run(duration=0.4)

Run it:

python first_simulation.py

What happens during the run

  • In window [0.0, 0.1], GrowthSource publishes count.
  • In window [0.1, 0.2], GrowthReporter consumes the committed count value and prints it.
  • Each later window repeats the same GrowthSource.count -> GrowthReporter.count exchange.

That one-window delay at each connection boundary is the intended sampled-data coupling model of the current kernel. See How Biosimulant Works for the full architecture.

Next steps