RuntimeRuntime 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 serve or run a packaged lab from the terminal, see the CLI reference. To run in the browser with no install, see the Studio quickstart.

Before you start

  • Time: about 10 minutes.
  • Requirements: Python 3.10+ and a virtual environment you can install packages into.
  • Outcome: two connected BioModule objects advancing inside one BioWorld.

Install

python -m pip install biosimulant

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

pip install -e .

The default install includes the local lab web UI used by biosimulant labs serve.

biosimulant labs init ./serve-check --name "Serve Check" --force
biosimulant labs serve ./serve-check

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):
    execution_policy = biosim.ExecutionPolicy.EACH_WINDOW
 
    def __init__(self, initial_count: int = 10, growth_rate: float = 0.2):
        self.count = int(initial_count)
        self.growth_rate = float(growth_rate)
 
    def outputs(self):
        return {"count": biosim.SignalSpec.scalar(dtype="int64", emitted_unit="cells")}
 
    def execute(self, inputs, *, context: biosim.ExecutionContext):
        assert context.window_end is not None
        self.count += max(1, int(self.count * self.growth_rate))
        return {"count": self.count}
 
 
class GrowthReporter(biosim.BioModule):
    execution_policy = biosim.ExecutionPolicy.EACH_WINDOW
 
    def inputs(self):
        return {
            "count": biosim.SignalSpec.scalar(
                dtype="int64",
                max_age=0.2,
                stale_policy="warn",
            )
        }
 
    def execute(self, inputs, *, context: biosim.ExecutionContext):
        assert context.window_end is not None
        signal = inputs.get("count")
        if signal is not None:
            print(f"[reporter] t={context.window_end:.1f}, count={signal.value}")
        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

Verify the result

The output should begin after the first communication boundary and include increasing cell counts:

[reporter] t=0.2, count=12
[reporter] t=0.3, count=14
[reporter] t=0.4, count=16

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.

Declare the policy when you package a model

When you later package GrowthSource as a model with its own model.yaml, repeat its execution policy there so Studio, Desktop and the Hub can tell when it runs without importing your code:

biosim:
  entrypoint: "src.growth:GrowthSource"
  communication_step: 0.1
  execution_policy: each_window

The value must match the class’s execution_policy; the runtime refuses to load the model if they differ. A module that computes once, such as a predictor, uses ExecutionPolicy.ONCE_BEFORE_RUN in Python and once_before_run in model.yaml. See Execution policy for the rules.

Next: run it in the cloud. The same runtime and package refs also run as durable managed jobs through the Developer API, with GPUs, artifacts, provenance and webhooks. See Local versus managed execution to decide which to use.

Troubleshooting

  • ModuleNotFoundError: run python -m pip show biosimulant with the same Python interpreter used for the script.
  • No reporter output: confirm builder.apply() runs before world.run(...) and that the connection uses growth.count.
  • Signal is stale: keep the example’s communication_step and max_age values together while learning the coupling model.
  • Need architecture details: read How Biosimulant works.

Next steps