Skip to main content

plr-ivoryos

PyPI version

The native PyLabRobot experience, powered by IvoryOS.

plr-ivoryos puts PyLabRobot's liquid handlers and other machines into IvoryOS with no wrapper code: every step keeps PyLabRobot's own name and arguments, the worktable is a file rather than code, and in IvoryOS NextGen the Labware panel shows that worktable live and lets you build it by dragging.

IvoryOS NextGen running a dye dilution on a simulated Hamilton STARlet, with the Labware panel beside the run

A dye dilution on PyLabRobot's simulated Hamilton STARlet. One spreadsheet row per sample, batch size 8: each transfer moves a column of eight wells. The Labware panel (right) shows the worktable on its rails, what each well now holds and which tips are left.


Quick Start (Simulation)

Develop and test workflows with no hardware: PyLabRobot's simulator tracks every tip and every microlitre.

from plr_ivoryos import LiquidHandler, Scale

lh = LiquidHandler(simulated=True)    # its worktable is worktable.json, written on the first edit
scale = Scale(simulated=True)

if __name__ == "__main__":
    # IvoryOS NextGen, with the Labware panel beside every page
    import ivoryos_edge
    from plr_ivoryos.labware_view import plugin as labware_view
    ivoryos_edge.run(__name__, plugins=[labware_view])
    # The original IvoryOS instead: import ivoryos; ivoryos.run(__name__)

Every step can also be called from plain Python: lh.transfer(...) blocks until it is done.

From the IvoryOS desktop app

No script at all:

  1. In the Hub, add a liquid handler (simulated, Opentrons OT-2, Hamilton STAR, Nimbus or Vantage, Tecan EVO) together with the Labware (PyLabRobot) plugin. Fill in the robot's own connection settings; leave deck_json empty.
  2. Start the deck. A real robot starts with its own empty worktable, the simulator with a ready-made one, and the Labware panel says how to begin.
  3. Edit layout: drag carriers onto the rails and labware onto them, fill wells, choose the model. Or Import a layout… you already have.
  4. Restart the deck when the panel asks: steps then offer the labware you placed.

The worktable is a file

Which robot it is and what sits where is configuration, not code, so one script serves every robot and the IvoryOS Hub can ship the file with an install:

{
  "deck_type": "OTDeck",
  "resources": [
    {"name": "tips_300", "type": "opentrons_96_tiprack_300ul", "slot": 1},
    {"name": "reservoir", "type": "nest_12_troughplate_15000uL_Vb", "slot": 2},
    {"name": "assay_plate", "type": "cor_96_wellplate_360uL_Fb", "slot": 5},
    {"name": "plate_carrier", "type": "PLT_CAR_L5AC_A00", "rails": 15,
     "children": [{"name": "sample_plate", "type": "cor_96_wellplate_360uL_Fb", "site": 0}]}
  ],
  "liquids": [{"labware": "reservoir", "wells": "A1", "liquid": "buffer", "volume_ul": 14000}]
}
  • deck_type: OTDeck, STARLetDeck, STARDeck, NimbusDeck, VantageDeck_1.3, EVO100Deck, EVO150Deck, EVO200Deck.
  • type: any labware definition in pylabrobot.resources, placed by slot (Opentrons), rails (Hamilton, Tecan) or location ({x, y, z} mm). Carriers list what they hold in children, by site.
  • liquids: what a person put on the worktable before the run. With tracking on (the default), an aspirate from a well nobody filled is refused; fill it here or with a load_liquid step.

Layout files written for 0.1 still load.

deck_json is optional. Without it the file is worktable.json in the deck's data folder when IvoryOS runs it (IVORYOS_DATA_DIR, which the desktop app sets), else in the working directory; a relative deck_json is in that folder too. A file that does not exist yet is not an error: the simulator starts from a ready-made worktable (tips, a reservoir with buffer and dye, two plates), a real robot from its own empty one (deck_type says which model, e.g. STARDeck rather than the default STARlet), and the file is written on the first change.

Beside it, plr-ivoryos keeps PyLabRobot's own description of the same worktable up to date, so a plain PyLabRobot script (or anyone without this package) uses the layout as designed:

from pylabrobot.resources import Deck
deck = Deck.load_from_json_file("worktable.pylabrobot.json")      # every labware, sized and placed
deck.load_state_from_file("worktable.pylabrobot-state.json")      # full tip racks, starting liquids

Those two are written whenever the worktable is loaded or changed, from the file as a run starts (not from a deck a run has used tips from). Edit worktable.json; the others are output. PyLabRobot 0.2.2 cannot read back its own Tecan, Nimbus or Vantage decks, so those worktables get none (the Labware panel says so).

A real robot is the same line with its backend:

from pylabrobot.liquid_handling.backends import OpentronsOT2Backend
lh = LiquidHandler(backend=OpentronsOT2Backend(host="10.0.0.5"))

The connection opens on the first step, so a deck starts with the robot switched off.


Steps

The steps keep PyLabRobot's names and arguments. Where PyLabRobot takes Well objects, they take the same thing written as text: assay_plate[A1:H1] is PyLabRobot's assay_plate["A1:H1"]. Volumes are µL, flow rates µL/s.

Step PyLabRobot What is added
transfer(source, targets, source_vol, ratios, target_vols, aspiration_flow_rate, dispense_flow_rates, ...) Same arguments, same source_vol / ratios / target_vols rule Uses one channel per target (PyLabRobot's uses one channel for all); source may give one well per target; tip_rack + new_tip fetch tips (without them, the tips on the head, as in PyLabRobot); blow_out_air_volume, mix_after_*. Returns what each target got.
aspirate(resources, vols, use_channels, flow_rates, blow_out_air_volume, ...) / dispense(...) Same mix_volume / mix_repetitions / mix_flow_rate for PyLabRobot's mix; from one trough well, several vols use several channels
pick_up_tips(tip_spots, use_channels), drop_tips(tip_spots, use_channels), return_tips(), discard_tips() Same A bare rack name (tips_300) picks the next unused tips
move_plate(plate, to) Same to is a slot ("5") or carrier position ("plate_carrier-1")
mix(resources, vols, repetitions), load_liquid(resources, liquid, vols), read_volumes(resources), tips_left(tip_rack) Not steps there Up and down in place; what is where
# PyLabRobot
await lh.transfer(reservoir["A1"][0], plate["B1:C1"], source_vol=60, ratios=[2, 1])
# plr-ivoryos (and an IvoryOS step)
lh.transfer(source="reservoir[A1]", targets="assay_plate[B1:C1]", source_vol=60, ratios=[2, 1], tip_rack="tips_300")

Inside the brackets: A1, A1:H1 (down a column), A1:A12 (along a row), A1:H3 (a rectangle, column by column: PyLabRobot's plate["A1:B2"] goes row by row, but column order is what a multichannel head works in), all, or several separated by commas. A bare name is the whole labware; several references are separated by commas (p1[A1:H1], p2[A1]). Per-well values are one number or one per well ("100, 50, 25" works too).

In IvoryOS NextGen

The arguments are marked with Annotated[...] (plr_ivoryos.wells), and the worktable is reported through __ivoryos_labware__(). IvoryOS reads both by duck typing; this package imports nothing from it. From that, with no configuration:

  • wells are picked on a drawing of the plate, choosing the plate in the same picker;
  • a well that is not on the plate is refused before a run starts;
  • a batch step is given every row of its batch in one call: one spreadsheet row per sample, batch size 8, and each transfer moves a column of eight;
  • the Labware panel shows the worktable live: what each well holds, which tips are left, and the wells a step is working on.

Picking wells for a spreadsheet column on a drawing of the plate

Picking the wells for a column of samples: the plate is chosen in the same picker, and the numbers are the order the samples will be visited in.

The original IvoryOS shows these arguments as text fields.

The Labware panel

"plugins": ["plr_ivoryos.labware_view:plugin"]

in a deck file (or ivoryos_edge.run(__name__, plugins=[plr_ivoryos.labware_view.plugin])) adds it beside every page. Click a plate to look at it well by well.

The Labware panel zoomed on a plate: each well coloured by the buffer and dye it holds Edit layout on a Hamilton STARlet: carriers on numbered rails, the robot choice, Import a layout and PyLabRobot's catalogue

Left: after the run above, each well coloured by what it holds (buffer blue, dye red). Right: Edit layout on the STARlet, with its carriers on the numbered rails and PyLabRobot's catalogue to drag from.

Edit layout changes the worktable file as you go, with nothing to confirm:

  • drag labware from PyLabRobot's catalogue onto an empty slot or carrier position, and on a Hamilton or Tecan a carrier onto the rails (it snaps to the nearest one); the catalogue shows the robot's own tip racks and carriers;
  • drag anything already there to move it, × to take it off, and rename it in the list;
  • click a plate or reservoir, pick wells (click or drag), and Fill them with a liquid and a volume: that is the liquids a run starts from, and it applies at once;
  • Robot switches the worktable: on the simulator between every robot (OT-2, STARlet, STAR, Nimbus, Vantage, EVO 100/150/200), on a real robot between its own models (a STAR or a STARlet, an EVO 100, 150 or 200). Each keeps its own worktable in the file, so switching back loses nothing;
  • Import a layout… takes a layout you already have: a worktable file, a 0.1 layout, or PyLabRobot's own deck file (deck.save(...) or lh.save(...)). Labware PyLabRobot names by definition is kept by name; custom labware is kept exactly as serialized. The worktable it replaces is kept as worktable.before-import.json. A real robot only takes its own kind of worktable, and only before it is connected (restart the deck first).

Steps offer added, moved or renamed labware once the deck restarts (the panel offers the restart). The panel is the only part of this package that imports IvoryOS, and only IvoryOS NextGen loads it.


Supported Devices

Device Type Class Name Simulation Shortcut Common Backends
Liquid Handler LiquidHandler simulated=True Hamilton STAR / STARlet, Nimbus, Vantage; Opentrons OT-2; Tecan EVO
Balance Scale simulated=True Mettler Toledo
Pumps Pump simulated=True Cole-Parmer Masterflex
Heater/Shaker HeaterShaker simulated=True Inheco ThermoShake
Centrifuge Centrifuge simulated=True VSpin
Plate Reader PlateReader simulated=True CLARIOstar, Cytation5
Fans Fan simulated=True Hamilton HEPA
Thermocycler Thermocycler simulated=True Any PLR-supported TC

The liquid handler in 0.2 has been tested on PyLabRobot's simulator; it has not yet been run on a real robot.


Installation

pip install plr-ivoryos

Python 3.10 or newer, PyLabRobot 0.2.2 or newer. Or from source:

pip install .

How it Works

Async Bridge

PyLabRobot is built on asyncio. plr-ivoryos keeps one event loop on a background thread for the life of the process, and every step runs there: a robot connection opened in setup() has to stay on the loop that opened it. Steps are therefore ordinary synchronous calls, from IvoryOS (either version) or from plain Python.

What changed in 0.2

  • Breaking for 0.1 workflows: steps take PyLabRobot's own argument names, with wells written plate[A1:H1]. 0.1's plate_name + resources, tip_rack_name + tip_spots, and transfer's source_plate / source_well / dest_plate / dest_well are gone, so saved liquid-handler steps need redoing. Layout files from 0.1 still load.
  • LiquidHandler is an ordinary class (0.1 returned a new class per deck, so its steps could only be found by building one). The runtime Enum registry is gone: labware choices come from the worktable.
  • The worktable is a file you can leave out, with PyLabRobot's own copy written beside it; the Labware panel edits it and imports existing layouts. Nimbus and Vantage worktables are new.
  • simulated=True with no layout works again (0.1 placed two plates on one spot). Scale.read_weight works with PyLabRobot 0.2.2.

License

MIT

Metadata

Release files for plr-ivoryos 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for plr-ivoryos 0.2.0
File Size Uploaded
plr_ivoryos-0.2.0.tar.gz 68.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for plr-ivoryos 0.2.0
File Interpreter ABI Platform
plr_ivoryos-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 124.8 kB

Release files / plr_ivoryos-0.2.0.tar.gz

Download URL plr_ivoryos-0.2.0.tar.gz
Size 68.9 kB
Tags Source
SHA-256 checksum
How to use checksums
53fcbc4a64c0c5f766e6430fbfaddb15949f2b0326f6cb20d2ef1eafa953d235
BLAKE2b-256 checksum
How to use checksums
29bbc71b3f43b04ccba20285bcaa7a059c13ec787e3dbabce9ffa846d29cfbc1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / plr_ivoryos-0.2.0-py3-none-any.whl

Download URL plr_ivoryos-0.2.0-py3-none-any.whl
Size 55.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
91e7ad08e505d63b898c89ddf5f89d9ed1dc98de5fe0d7609e06709571aa2bee
BLAKE2b-256 checksum
How to use checksums
e514c857b4a06d27081731ba2c1efd348fb5da4d7b5744a4fc19ed5ef7e221f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page