<!-- Source: https://docs.biosimulant.com/runtime/quickstart -->

# 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](/cli). To run in the browser with no install, see the [Studio quickstart](/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

```bash
python -m pip install biosimulant
```

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

```bash
pip install -e .
```

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

```bash
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`:

```python

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:

```bash
python first_simulation.py
```

## Verify the result

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

```text
[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](/runtime/concepts) 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:

```yaml
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](/runtime/model-manifest#execution-policy)
for the rules.

**Info:**

  **Next: run it in the cloud.** The same runtime and package refs also run as durable managed jobs through the [Developer API](/developer-api), with GPUs, artifacts, provenance and webhooks. See [Local versus managed execution](/developer-api#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](/runtime/concepts).

## Next steps

- [How to create a BioModule](/runtime/create-biomodule)
- [BioWorld API](/runtime/bioworld-api)
- [BioModule API](/runtime/biomodule-api)
- [CLI](/cli): pull and run published labs from the terminal.
- [Package and publish](/hub/publish): publish your own model or lab.
