Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mis = "mis"
RHE = "RHE"
"ASEND" = "ASEND"
caf = "caf"
Occured = "Occured"

[files]
extend-exclude = [
Expand Down
39 changes: 35 additions & 4 deletions docs/api/pylabrobot.capabilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ Devices
Bulk Dispensing - Peristaltic
-----------------------------

.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.peristaltic.peristaltic
.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.peristaltic.peristaltic8

.. autosummary::
:toctree: _autosummary
Expand All @@ -296,7 +296,7 @@ Bulk Dispensing - Peristaltic

PeristalticDispensing8

.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.peristaltic.backend
.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.peristaltic.backend8

.. autosummary::
:toctree: _autosummary
Expand All @@ -309,7 +309,7 @@ Bulk Dispensing - Peristaltic
Bulk Dispensing - Syringe
-------------------------

.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.syringe.syringe
.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.syringe.syringe8

.. autosummary::
:toctree: _autosummary
Expand All @@ -318,7 +318,7 @@ Bulk Dispensing - Syringe

SyringeDispensing8

.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.syringe.backend
.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.syringe.backend8

.. autosummary::
:toctree: _autosummary
Expand All @@ -328,6 +328,37 @@ Bulk Dispensing - Syringe
SyringeDispensingBackend8


Bulk Dispensing - Diaphragm
---------------------------

.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.diaphragm.diaphragm

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

DiaphragmDispenser

.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.diaphragm.backend

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

DiaphragmDispenserBackend

.. currentmodule:: pylabrobot.capabilities.bulk_dispensers.diaphragm.chatterbox

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

DiaphragmDispenserChatterboxBackend


Liquid Handling - PIP (Independent Channels)
--------------------------------------------

Expand Down
40 changes: 40 additions & 0 deletions docs/api/pylabrobot.formulatrix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.. currentmodule:: pylabrobot.formulatrix

pylabrobot.formulatrix package
==============================

Mantis
------

.. currentmodule:: pylabrobot.formulatrix.mantis.mantis

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

Mantis

.. currentmodule:: pylabrobot.formulatrix.mantis.driver

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

MantisDriver

.. currentmodule:: pylabrobot.formulatrix.mantis.diaphragm_dispenser_backend

.. autosummary::
:toctree: _autosummary
:nosignatures:
:recursive:

MantisDiaphragmDispenserBackend

.. autoclass:: pylabrobot.formulatrix.mantis.diaphragm_dispenser_backend.MantisDiaphragmDispenserBackend.DispenseParams
:members:

.. autoclass:: pylabrobot.formulatrix.mantis.diaphragm_dispenser_backend.MantisDiaphragmDispenserBackend.PrimeParams
:members:
1 change: 1 addition & 0 deletions docs/api/pylabrobot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Manufacturers
pylabrobot.bmg_labtech
pylabrobot.brooks
pylabrobot.byonoy
pylabrobot.formulatrix
pylabrobot.hamilton
pylabrobot.inheco
pylabrobot.liconic
Expand Down
89 changes: 89 additions & 0 deletions docs/user_guide/capabilities/dispensing/diaphragm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Diaphragm dispensing

{class}`~pylabrobot.capabilities.bulk_dispensers.diaphragm.diaphragm.DiaphragmDispenser` controls chip-based contactless dispensing into individual containers.

A diaphragm dispenser uses a disposable silicon chip with microvalves driven by pressurized air to deliver nanoliter-to-microliter volumes into a single well at a time.

`DiaphragmDispenser` is the **variable** head-format variant of the diaphragm capability — it addresses one container per dispense op, so callers pass two parallel lists, one of containers and one of volumes, in the order to be dispensed. Future 8-channel (`DiaphragmDispensing8`) and 96-channel (`DiaphragmDispensing96`) variants will follow the same naming convention as the peristaltic and syringe capabilities. See [the bulk dispensing index](index) for the full mechanism × head-format matrix.

## Walkthrough

```python
from pylabrobot.capabilities.bulk_dispensers.diaphragm import (
DiaphragmDispenser,
DiaphragmDispenserChatterboxBackend,
)
from pylabrobot.resources.corning.plates import Cor_96_wellplate_360ul_Fb

dispenser = DiaphragmDispenser(backend=DiaphragmDispenserChatterboxBackend())
await dispenser._on_setup()

plate = Cor_96_wellplate_360ul_Fb("demo_plate")
```

### Dispensing

Pass parallel `containers` and `volumes` lists. Each `volumes[i]` (in uL) is dispensed into `containers[i]`, in order.

```python
# Same volume to a few wells
await dispenser.dispense(
containers=plate["A1:C1"],
volumes=[5.0, 5.0, 5.0],
)

# Different volumes per container
await dispenser.dispense(
containers=plate["A1:A3"],
volumes=[1.0, 2.5, 10.0],
)
```

The capability validates that both lists have the same length and that all volumes are positive.

### Priming

Most diaphragm dispensers need to be primed before they can deliver accurate volumes. Backends typically also re-prime automatically before dispensing if no chip has been primed yet — see your device's docs for the exact behavior.

```python
await dispenser.prime()
```

### Backend parameters

Device-specific settings (chip number, dispense Z-height, prime volume, etc.) are passed as a `BackendParams` instance defined by the concrete backend. For example, on the Mantis:

```python
from pylabrobot.formulatrix.mantis.diaphragm_dispenser_backend import (
MantisDiaphragmDispenserBackend,
)

await dispenser.dispense(
containers=plate["A1"],
volumes=[5.0],
backend_params=MantisDiaphragmDispenserBackend.DispenseParams(
chip=3,
dispense_z=44.331,
prime_volume=20.0,
),
)
```

See the device's user guide page for the full list of parameters.

## Tips and gotchas

- **`containers` and `volumes` must be the same length** and ordered to match: index `i` in one corresponds to index `i` in the other.
- **All volumes must be positive.** Zero or negative volumes raise `ValueError`.
- **Containers are visited in list order.** If your hardware optimizes path planning, sort the list yourself before calling `dispense`.
- **Chip and Z-height belong in `backend_params`.** Different chips may be loaded for different volume ranges, and the dispense Z-height depends on the plate. Set these per call.

## Supported hardware

| Device | Manufacturer |
|--------|-------------|
| [Mantis](../../formulatrix/mantis/hello-world) | Formulatrix |

## API reference

See {class}`~pylabrobot.capabilities.bulk_dispensers.diaphragm.diaphragm.DiaphragmDispenser` and {class}`~pylabrobot.capabilities.bulk_dispensers.diaphragm.backend.DiaphragmDispenserBackend`.
66 changes: 41 additions & 25 deletions docs/user_guide/capabilities/dispensing/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,67 @@

Bulk dispensers deliver reagent into microplates at high throughput. They are used for filling plates with media, adding assay reagents, wash steps, and any operation where many wells need the same (or similar) volumes.

PLR supports two dispensing mechanisms, each with its own capability:
Bulk-dispensing capabilities vary along two independent axes:

| Capability | Mechanism | Typical use |
|---|---|---|
| **[Peristaltic dispensing](peristaltic)** | Peristaltic pump | Media, wash buffer, large-volume reagents |
| **[Syringe dispensing](syringe)** | Syringe pump | Detection reagents, substrates, low-volume precision |
* **Mechanism** — how the fluid is moved. PLR currently models three:
* **Peristaltic** — a rotating pump head pushes fluid through flexible tubing. Fast, large volumes; needs priming and purging.
* **Syringe** — a fixed volume is aspirated into a syringe barrel and dispensed under positive pressure. High accuracy at low volumes.
* **Diaphragm** — a disposable silicon chip with microvalves driven by pressurized air delivers nanoliter-to-microliter doses without contacting the liquid.
* **Head format** — how targets are addressed:
* **Variable** (no suffix) — one container at a time. Callers pass parallel `containers` and `volumes` lists.
* **8-channel** (`8` suffix) — one column at a time. Callers pass a dict mapping 1-indexed column numbers to volumes in uL.
* **96-channel** (`96` suffix) — the full plate at once.

Both capabilities share the same `volumes` interface: a dict mapping **1-indexed column numbers** to volumes in uL. Device-specific settings (pump speed, cassette type, flow rate, etc.) are passed as `backend_params`.
Each mechanism × head format combination is a separate capability class. For example, the 8-channel peristaltic capability is {class}`~pylabrobot.capabilities.bulk_dispensers.peristaltic.peristaltic8.PeristalticDispensing8`. Not every combination exists yet — what's currently implemented:

Some devices (like the BioTek EL406) have both systems on a single instrument. Use the one that matches your volume and accuracy requirements.
| Mechanism | Variable | 8-channel | 96-channel |
|---|:---:|:---:|:---:|
| [Peristaltic](peristaltic) | -- | {class}`~pylabrobot.capabilities.bulk_dispensers.peristaltic.peristaltic8.PeristalticDispensing8` | -- |
| [Syringe](syringe) | -- | {class}`~pylabrobot.capabilities.bulk_dispensers.syringe.syringe8.SyringeDispensing8` | -- |
| [Diaphragm](diaphragm) | {class}`~pylabrobot.capabilities.bulk_dispensers.diaphragm.diaphragm.DiaphragmDispenser` | -- | -- |

## Peristaltic vs syringe
The mechanism name in the first column links to its walkthrough page; the cells link to the API reference for each implemented variant. More variants will land as devices need them — the class name suffix tells you which head format you're getting, and the unsuffixed name is always the variable (per-container) variant.

| | Peristaltic | Syringe |
|---|---|---|
| **Volume range** | Medium--high | Low--medium |
| **Accuracy** | Good | High |
| **Throughput** | High | Lower |
| **Purge needed** | Yes | No |
Device-specific settings (pump speed, cassette type, flow rate, chip number, dispense Z, etc.) are passed as `backend_params` for all variants. Some devices (like the BioTek EL406) host more than one mechanism on a single instrument — pick the capability that matches your volume and accuracy requirements.

Peristaltic dispensers push fluid through flexible tubing using a rotating pump head. They are fast and handle large volumes well, but require priming before use and purging after to clear the lines. Syringe dispensers aspirate a fixed volume into a barrel and dispense it with high precision. They are slower but more accurate at low volumes.
## Choosing a mechanism

| | Peristaltic | Syringe | Diaphragm |
|---|---|---|---|
| **Volume range** | Medium--high | Low--medium | Nanoliter--microliter |
| **Accuracy** | Good | High | Very high (contactless) |
| **Throughput** | High | Lower | Lower (per-container) |
| **Prime needed** | Yes | Optional | Yes (per chip) |
| **Purge needed** | Yes | No | No |

Peristaltic dispensers push fluid through flexible tubing using a rotating pump head — fast, large volumes, but require priming and purging. Syringe dispensers aspirate a fixed volume into a barrel and dispense under positive pressure — slower but more accurate at low volumes. Diaphragm dispensers use a disposable chip with microvalves driven by pressurized air — contactless, very precise at small volumes, and address one container at a time.

## Tips and gotchas

- **Always prime before dispensing** (peristaltic). Air in the tubing causes inaccurate volumes.
- **Purge after dispensing** to prevent reagent from drying in the lines.
- **Columns are 1-indexed.** `{1: 50.0}` sets column 1, not column 0.
- **Only columns in the dict are set.** Columns not in `volumes` retain their previous setting on the instrument. If in doubt, explicitly set all columns.
- **Always prime before dispensing** (peristaltic, diaphragm). Air in the lines causes inaccurate volumes.
- **Purge after dispensing** (peristaltic) to prevent reagent from drying in the lines.
- **8-channel head formats are 1-indexed by column.** `{1: 50.0}` sets column 1, not column 0. Columns not in the dict retain their previous setting on the instrument — when in doubt, set them all.
- **Variable head formats take parallel lists.** `containers[i]` gets `volumes[i]` uL, in order. The capability validates that the lengths match and the volumes are positive.

## Supported hardware

| Device | Manufacturer | Peristaltic | Syringe |
|--------|-------------|:-----------:|:-------:|
| [Multidrop Combi](../../thermo_fisher/multidrop_combi/hello-world) | Thermo Fisher | yes | -- |
| [EL406](../../agilent/biotek/el406/hello-world) | BioTek (Agilent) | yes | yes |
| Device | Manufacturer | Peristaltic | Syringe | Diaphragm |
|--------|-------------|:-----------:|:-------:|:---------:|
| [Multidrop Combi](../../thermo_fisher/multidrop_combi/hello-world) | Thermo Fisher | 8-channel | -- | -- |
| [EL406](../../agilent/biotek/el406/hello-world) | BioTek (Agilent) | 8-channel | 8-channel | -- |
| [Mantis](../../formulatrix/mantis/hello-world) | Formulatrix | -- | -- | variable |

```{toctree}
:maxdepth: 1
:hidden:

peristaltic
syringe
diaphragm
```

## API reference

- {class}`~pylabrobot.capabilities.bulk_dispensers.peristaltic.peristaltic8.PeristalticDispensing8` / {class}`~pylabrobot.capabilities.bulk_dispensers.peristaltic.backend8.PeristalticDispensingBackend8`
- {class}`~pylabrobot.capabilities.bulk_dispensers.syringe.syringe8.SyringeDispensing8` / {class}`~pylabrobot.capabilities.bulk_dispensers.syringe.backend8.SyringeDispensingBackend8`
- {class}`~pylabrobot.capabilities.bulk_dispensers.peristaltic.peristaltic8.PeristalticDispensing8` / {class}`~pylabrobot.capabilities.bulk_dispensers.peristaltic.backend8.PeristalticDispensingBackend8` (peristaltic, 8-channel)
- {class}`~pylabrobot.capabilities.bulk_dispensers.syringe.syringe8.SyringeDispensing8` / {class}`~pylabrobot.capabilities.bulk_dispensers.syringe.backend8.SyringeDispensingBackend8` (syringe, 8-channel)
- {class}`~pylabrobot.capabilities.bulk_dispensers.diaphragm.diaphragm.DiaphragmDispenser` / {class}`~pylabrobot.capabilities.bulk_dispensers.diaphragm.backend.DiaphragmDispenserBackend` (diaphragm, variable)
7 changes: 7 additions & 0 deletions docs/user_guide/formulatrix/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Formulatrix

```{toctree}
:maxdepth: 1

mantis/hello-world
```
Loading
Loading