Composition Tracking

Composition Tracking#

robotools automagically tracks the composition of well contents across liquid handling operations. This comes in handy for tasks such as media mixing, dilution series, or checking if the final concentrations of assay components are as planned.

The composition tracking defaults to unique well-wise identifiers, but can be configured to name contents of wells explicitly.

import robotools

Let’s assume we have three labwares:

  • A Trough of water.

  • A 5-column Trough with 10xMedia, 1000xAntibiotics and two empty columns

  • Two Eppis with biomass of different microorganisms

From this we will prepare culture broths in the two empty trough colums to test the effectivity of the antibiotics.

minmax25 = dict(min_volume=1000, max_volume=25_000)

water = robotools.Trough("water", 1, 1, **minmax25, initial_volumes=25_000)

troughs = robotools.Trough(
    "troughs", 1, columns=5, **minmax25,
    initial_volumes=[10_000, 5_000, 0, 0, 0],
    # Trough contents are named like this:
    column_names=["10xMedia", "100xAntibiotics", None, None, None]
)

eppis = robotools.Labware(
    "eppis", 2, 1, min_volume=50, max_volume=1500,
    initial_volumes=500,
    # Multi-well labware contents are named with a dict:
    component_names={
        "A01": "E.coli",
        "B01": "Y.pestis",
    }
)

The Labware.composition property is a dictionary that holds the fractional composition of each well, indexed by the name of the component:

troughs.composition
{'10xMedia': array([[1., 0., 0., 0., 0.]]),
 '100xAntibiotics': array([[0., 1., 0., 0., 0.]])}

We can use two of the empty trough columns to prepare and inoculate a culture broth:

V_MEDIUM = 10_000
V_FINAL = 4_000

with robotools.FluentWorklist() as wl:
    wells_medium = troughs.wells[:, 2]
    wells_strain_A = troughs.wells[:, 3]
    wells_strain_B = troughs.wells[:, 4]

    # Prepare the medium
    wl.transfer(
        troughs, troughs.wells[:, 0],
        troughs, wells_medium,
        volumes=V_MEDIUM / 10,
        label="transfer 10x media"
    )
    wl.transfer(
        troughs, troughs.wells[:, 1],
        troughs, wells_medium,
        volumes=V_MEDIUM / 100,
        label="add antibiotics"
    )
    wl.transfer(
        water, water.wells,
        troughs, wells_medium,
        volumes=(V_MEDIUM * 0.95) - troughs.volumes[:, 2],
        label="add water up to 95 % of the final volume"
    )
    
    # Split the medium into the two empty troughs
    for target in [wells_strain_A, wells_strain_B]:
        wl.transfer(
            troughs, wells_medium,
            troughs, target,
            volumes=0.95 * V_FINAL,
            label="Transfer medium"
        )
    
    # Add inoculum from the eppis
    wl.transfer(
        eppis, "A01",
        troughs, wells_strain_A,
        volumes=0.05 * V_FINAL,
        label="Inoculate A"
    )
    wl.transfer(
        eppis, "B01",
        troughs, wells_strain_B,
        volumes=0.05 * V_FINAL,
        label="Inoculate B"
    )

We can see from the .composition property of the troughs that there are new components:

troughs.composition
{'10xMedia': array([[1.        , 0.        , 0.10526316, 0.1       , 0.1       ]]),
 '100xAntibiotics': array([[0.        , 1.        , 0.01052632, 0.01      , 0.01      ]]),
 'water': array([[0.        , 0.        , 0.88421053, 0.84      , 0.84      ]]),
 'E.coli': array([[0.  , 0.  , 0.  , 0.05, 0.  ]]),
 'Y.pestis': array([[0.  , 0.  , 0.  , 0.  , 0.05]])}

The composition of the individual culture broth wells is often easier to read. We can see that the 10xMedia component indeed makes up 10 % of the final volume:

troughs.get_well_composition("A04")
{'10xMedia': 0.1,
 '100xAntibiotics': 0.009999999999999997,
 'water': 0.84,
 'E.coli': 0.05}
troughs.get_well_composition("A05")
{'10xMedia': 0.1,
 '100xAntibiotics': 0.009999999999999997,
 'water': 0.84,
 'Y.pestis': 0.05}
%load_ext watermark
%watermark -idu
Last updated: 2024-04-25T09:21:09.110232+02:00