Labware Basics

Labware Basics#

In robotools there are two important types of objects: Labware and worklists.

A Labware represents an array of wells, such as a microtiter plate (MTP), one or more troughs or even an arrangement of eppis or falcon tubes.

The worklists help you to perform liquid handling operations on Labware objects while automatically creating a Gemini worklist file (*.gwl) with the corresponding pipetting instructions. These files contain things like source/destination, volume and can be executed by a Tecan EVO or Fluent liquid handlers.

But before we’ll get creating worklists, this example introduces how robotools generally deals with liquid handling.

import robotools

The Labware object#

Each Labware object describes a $R\times C$ grid (array) of wells. It has a name and min_volume/max_volume to prevent you from pipetting impossible volumes.

The following cell creates a Labware with the name "24-well plate".

When creating worklists, this name should match the labware defined on the liquid handler worktable.

plate = robotools.Labware(
    "24-well plate",
    rows=4, columns=6, 
    min_volume=100, max_volume=1_000,
    initial_volumes=300
)
print(plate)
24-well plate
[[300. 300. 300. 300. 300. 300.]
 [300. 300. 300. 300. 300. 300.]
 [300. 300. 300. 300. 300. 300.]
 [300. 300. 300. 300. 300. 300.]]

The Labware has a lot of useful properties. Most importantly: wells and volumes

# These are the well IDs as they are typically printed on the plastic:
plate.wells
array([['A01', 'A02', 'A03', 'A04', 'A05', 'A06'],
       ['B01', 'B02', 'B03', 'B04', 'B05', 'B06'],
       ['C01', 'C02', 'C03', 'C04', 'C05', 'C06'],
       ['D01', 'D02', 'D03', 'D04', 'D05', 'D06']], dtype='<U3')
# In the same orientation, these are the current filling volumes:
plate.volumes
array([[300., 300., 300., 300., 300., 300.],
       [300., 300., 300., 300., 300., 300.],
       [300., 300., 300., 300., 300., 300.],
       [300., 300., 300., 300., 300., 300.]])

If you are familiar with Python already, you probably know how NumPy slicing works.

Because .wells and .volumes are NumPy ndarrays, you can use NumPy slicing to select particular ranges:

# This slice selects all wells except the first/last column/row:
plate.wells[1:-1, 1:-1]
array([['B02', 'B03', 'B04', 'B05'],
       ['C02', 'C03', 'C04', 'C05']], dtype='<U3')

Labwares have methods such as .add(), .remove() for performing virtual liquid handling operations.

You won’t typically work with .add() or .remove() directly, because most things you do with a worklist will call these methods under the hood.

One job of the .add() and .remove() methods is to raise VolumeOverflowError or VolumeUnderflowError when the minimum/maximum working volumes are violated:

try:
    plate.add(['A01', 'B01', 'C01'], [200, 200, 2000])
except robotools.VolumeViolationException as ex:
    print(ex)
Too much volume for "24-well plate".C01: 300.0 + 2000 > 1000

Another job is to record a history of all liquid handling operations performed over the labware’s lifetime:

plate.add(plate.wells[1:-1, 1:-1], volumes=55, label="Add 55 µL to the center wells")

print(plate.report)
24-well plate
initial
[[300. 300. 300. 300. 300. 300.]
 [300. 300. 300. 300. 300. 300.]
 [300. 300. 300. 300. 300. 300.]
 [300. 300. 300. 300. 300. 300.]]

Add 55 µL to the center wells
[[500. 300. 300. 300. 300. 300.]
 [500. 355. 355. 355. 355. 300.]
 [300. 355. 355. 355. 355. 300.]
 [300. 300. 300. 300. 300. 300.]]

Troughs#

Troughs are a little weird, because for the EVOware, they have multiple rows, even though it’s actually just one big well. Nevertheless, a Trough is just a special type of Labware that has virtual_rows. For the Tecan Fluent, the virtual_rows setting is irrelevant.

The following example emulates an arrangement of 4 troughs next to each other. They have virtual_rows=8, so there’s enough space for 8 tips (A01 through H01), but in reality it’s just one well per column:

quadruple_trough = robotools.Trough(
    "4xTrough", 8, 4,
    min_volume=1000, max_volume=30_000,
    initial_volumes=[20_000, 10_000, 10_000, 10_000]
)
print(quadruple_trough)
4xTrough
[[20000. 10000. 10000. 10000.]]

Let’s say we want to aspirate 11x 100 µL from the first trough, operating all 8 pipettes in parallel. So we need 8 different virtual well IDs (A01-H01), but 11 in total.

The robotools.get_trough_wells helper function returns such a well list:

vwells = robotools.get_trough_wells(
    n=11,
    trough_wells=quadruple_trough.wells[:, 0]  # select the wells from the first trough
)

quadruple_trough.remove(vwells, 100)

print(f"""
Virtual wells:
{vwells}

Result:
{quadruple_trough}
""")
Virtual wells:
['A01', 'B01', 'C01', 'D01', 'E01', 'F01', 'G01', 'H01', 'A01', 'B01', 'C01']

Result:
4xTrough
[[18900. 10000. 10000. 10000.]]
%load_ext watermark
%watermark -idu
Last updated: 2024-04-23T13:11:50.287028+02:00