<!-- Source: https://docs.biosimulant.com/overview/library-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 [References > CLI](/references/cli) or, for the GUI workbench, the [Desktop App](/overview/desktop-cli). To run in the browser with no install, see the [Web Quickstart](/overview/web-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):
    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:

```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](/references/library/concepts) for the full architecture.

**Info:**

  **Next: run it in the cloud.** The same runtime and package refs also run as durable managed jobs — GPU, artifacts, provenance, and webhooks — through the [Developer API](/developer-api). See [Local versus managed execution](/developer-api#local-versus-managed-execution) for a side-by-side of when to use each.

## 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](/references/library/concepts).

## Next steps

- [How to Create a BioModule](/how-to/library/create-biomodule)
- [BioWorld API](/references/library/bioworld-api)
- [BioModule API](/references/library/biomodule-api)
- [CLI](/references/cli): pull and run published labs from the terminal.
- [Desktop App](/overview/desktop-cli): the GUI workbench for editing and running labs locally.
- [Package & Publish](/how-to/library/package-and-publish): publish your own model or lab.
