How Biosimulant Works
Biosimulant is an open-source Python engine for composing biological simulators
and finite computations from modules with explicit typed ports. BioWorld drains
before/after computation and advances temporal modules through atomic
communication windows.
If you want a runnable introduction, start with the Library Quickstart (Python). This page explains the architecture behind that example.
The five core primitives
| Primitive | Role | Reference |
|---|---|---|
BioModule | A runnable temporal or finite-computation component | BioModule API |
BioWorld | The communication-step orchestrator | BioWorld API |
SignalSpec | A declared port contract | BioSignal & SignalSpec |
Typed BioSignal subclasses | Runtime payloads crossing module boundaries | BioSignal & SignalSpec |
WiringBuilder | A validated graph builder for named module ports | WiringBuilder API |
BioWorld: communication windows
Create a world with a required communication step:
world = biosim.BioWorld(communication_step=0.1)ONCE_BEFORE_RUN modules run first, in dependency order. Then, for each window
[t, t + communication_step]:
- the world reads committed signals at
t - it delivers those inputs to every connected target module
- every
EACH_WINDOWmodule advances or executes across the same positive window - the world commits all outputs atomically at
t + communication_step
After the final window commits, ONCE_AFTER_RUN modules run in dependency order.
This means:
- signal exchange is synchronized at shared communication boundaries
- state signals use hold-last-value semantics until replaced
- event signals are delivered once per connection per source timestamp
- stale reads are checked from the consuming port’s
SignalSpec
The kernel does not expose an execution-order scheduling contract, a global external tick parameter, rollback, or algebraic-loop solving.
A module sees another module’s output on the communication turn after it is
committed. Final outputs are committed at the last boundary, so a downstream
module that needs them should use ONCE_AFTER_RUN. Older temporal modules that
implement advance_window() can use settle_steps / world.settle() instead.