{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Using Trackers\n", "\n", "Trackers in PyLabRobot are objects that keep track of the state of the deck throughout a protocol. Two types of trackers currently exist: tip trackers (tracking the presence of tips in tip racks and on the pipetting channels) and volume trackers (tracking the volume in pipetting tips and wells)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Setting up the liquid handler.\n", "Resource deck was assigned to the liquid handler.\n", "Resource trash was assigned to the liquid handler.\n", "Resource trash_core96 was assigned to the liquid handler.\n" ] } ], "source": [ "from pylabrobot.liquid_handling import LiquidHandler\n", "from pylabrobot.liquid_handling.backends.chatterbox import LiquidHandlerChatterboxBackend\n", "from pylabrobot.resources import (\n", " TIP_CAR_480_A00,\n", " HTF,\n", " PLT_CAR_L5AC_A00,\n", " Cor_96_wellplate_360ul_Fb,\n", " set_tip_tracking,\n", " set_volume_tracking\n", ")\n", "from pylabrobot.resources.hamilton import STARLetDeck\n", "\n", "lh = LiquidHandler(backend=LiquidHandlerChatterboxBackend(num_channels=8), deck=STARLetDeck())\n", "await lh.setup()" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "tip_carrier = TIP_CAR_480_A00(name=\"tip carrier\") # initialize a tip carrier" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "plt_carrier = PLT_CAR_L5AC_A00(name=\"plate carrier\") # initialize a plate carrier" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "We enable tip and volume tracking globally using the `set_volume_tracking` and `set_tip_tracking` methods." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "set_volume_tracking(enabled=True)\n", "set_tip_tracking(enabled=True)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Tip trackers\n", "\n", "The tip tracker is a simple class that keeps track of the current tip, and the previous operations that have been performed on an object. This enables features like {meth}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.return_tips` and automated tip type detection." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Initializing tip racks\n", "\n", "Whether or not tip tracking is turned on, spots on a tip rack initialize with a tip tracker that defaults to having a tip. The tip tracker only comes into play with performing operations." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "tip_carrier[0] = tip_rack = HTF(name=\"tip rack\")" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tip_rack.get_item(\"A1\").tracker.has_tip" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "To initialize a tip rack without tips, pass `with_tips=False`:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "tip_carrier[1] = empty_tip_rack = HTF(name=\"empty tip rack\", with_tips=False)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "empty_tip_rack.get_item(\"A1\").tracker.has_tip" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "To \"empty\" a tip rack after initialization, use the {meth}`~pylabrobot.resources.TipRack.empty()` method. To \"fill\" a tip rack after initialization, use the {meth}`~pylabrobot.resources.TipRack.fill()` method." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "empty_tip_rack.fill()\n", "empty_tip_rack.get_item(\"A1\").tracker.has_tip" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "empty_tip_rack.empty()\n", "empty_tip_rack.get_item(\"A1\").tracker.has_tip" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Resource tip carrier was assigned to the liquid handler.\n" ] } ], "source": [ "lh.deck.assign_child_resource(tip_carrier, rails=3)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Tip tracker errors\n", "\n", "The tip tracker is most useful for catching hardware errors before they happen. With tip tracking turned on, the following errors can be raised:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "from pylabrobot.resources.errors import HasTipError, NoTipError" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `NoTipError` when picking up a tip\n", "\n", "This error is raised when the tip tracker is trying to access a spot that has no tip." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_0_0 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "Dropping tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: empty tip rack_tipspot_0_0 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "As expected: Tip spot does not have a tip.\n" ] } ], "source": [ "await lh.pick_up_tips(tip_rack[0])\n", "await lh.drop_tips(empty_tip_rack[0])\n", "\n", "try:\n", " await lh.pick_up_tips(tip_rack[0])\n", "except NoTipError as e:\n", " print(\"As expected:\", e)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `HasTipError` when dropping a tip\n", "\n", "This error is raised when the tip tracker is trying to access a spot that has a tip." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_0_1 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "As expected: Tip spot already has a tip.\n" ] } ], "source": [ "await lh.pick_up_tips(tip_rack[1])\n", "\n", "try:\n", " await lh.drop_tips(empty_tip_rack[0])\n", "except HasTipError as e:\n", " print(\"As expected:\", e)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dropping tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: empty tip rack_tipspot_0_1 0,0,0 HamiltonTip 1065 8 95.1 Yes \n" ] } ], "source": [ "await lh.drop_tips(empty_tip_rack[1])" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `NoTipError` when dropping a tip\n", "\n", "This error is raised when the tip tracker is trying to use a channel that has no tip." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "As expected: Channel 0 does not have a tip.\n" ] } ], "source": [ "try:\n", " await lh.drop_tips(empty_tip_rack[2])\n", "except NoTipError as e:\n", " print(\"As expected:\", e)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `HasTipError` when picking up a tip\n", "\n", "This error is raised when the tip tracker is trying to use a channel that has a tip." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_0_2 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "As expected: Channel has tip\n" ] } ], "source": [ "await lh.pick_up_tips(tip_rack[2])\n", "\n", "try:\n", " await lh.pick_up_tips(tip_rack[3])\n", "except HasTipError as e:\n", " print(\"As expected:\", e)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Disabling the tip tracker\n", "\n", "The tip tracker can be disabled in three different ways, depending on the desired behavior." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### Using a context manager\n", "\n", "The {meth}`pylabrobot.resources.no_tip_tracking` context manager can be used to disable the tip tracker for a set of operations.\n", "\n", "Note that we use the {meth}`pylabrobot.liquid_handling.liquid_handler.LiquidHandler.clear_head_state` method to forget the tips that are currently mounted on the channels. This is needed because even though the tip tracker is disabled, the channels still keep track of the tips that are mounted on them." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "lh.clear_head_state()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_0_4 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p1: tip rack_tipspot_0_4 0,0,0 HamiltonTip 1065 8 95.1 Yes \n" ] } ], "source": [ "from pylabrobot.resources import no_tip_tracking\n", "\n", "with no_tip_tracking():\n", " await lh.pick_up_tips(tip_rack[4])\n", " await lh.pick_up_tips(tip_rack[4], use_channels=[1]) # no error" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### For a single tip spot\n", "\n", "The tip tracker can be disabled for a single object by calling {meth}`pylabrobot.resources.tip_tracker.TipTracker.disable()` on the tracker object." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "lh.clear_head_state()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_0_5 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p1: tip rack_tipspot_0_5 0,0,0 HamiltonTip 1065 8 95.1 Yes \n" ] } ], "source": [ "tip_rack.get_item(5).tracker.disable()\n", "\n", "await lh.pick_up_tips(tip_rack[5])\n", "await lh.pick_up_tips(tip_rack[5], use_channels=[1]) # no error\n", "\n", "tip_rack.get_item(5).tracker.enable()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### For a single tip rack\n", "\n", "Disable the tip tracker for a single tip rack by calling {meth}`pylabrobot.resources.TipRack.disable_tip_trackers()` and {meth}`pylabrobot.resources.TipRack.enable_tip_trackers()` on the tip rack object." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "lh.clear_head_state()" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_0_5 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p1: tip rack_tipspot_0_5 0,0,0 HamiltonTip 1065 8 95.1 Yes \n" ] } ], "source": [ "tip_rack.disable_tip_trackers()\n", "\n", "await lh.pick_up_tips(tip_rack[5])\n", "await lh.pick_up_tips(tip_rack[5], use_channels=[1]) # no error\n", "\n", "tip_rack.enable_tip_trackers()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### Globally\n", "\n", "The tip tracker can be disabled globally by using {meth}`pylabrobot.resources.set_tip_tracking`." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "lh.clear_head_state()" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_0_6 0,0,0 HamiltonTip 1065 8 95.1 Yes \n", "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p1: tip rack_tipspot_0_6 0,0,0 HamiltonTip 1065 8 95.1 Yes \n" ] } ], "source": [ "from pylabrobot.resources import set_tip_tracking\n", "\n", "set_tip_tracking(enabled=False)\n", "\n", "await lh.pick_up_tips(tip_rack[6])\n", "await lh.pick_up_tips(tip_rack[6], use_channels=[1]) # no error\n", "\n", "set_tip_tracking(enabled=True)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Volume trackers\n", "\n", "The volume tracker is a simple class that keeps track of the current volume, and the previous operations that have been performed on an object. This enables features like automated liquid class selection in STAR, and raises errors before they happen on the robot." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Initializing wells\n", "\n", "Wells automatically initialize with a volume tracker that defaults to having no volume." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "plt_carrier[0] = plate = Cor_96_wellplate_360ul_Fb(name=\"plate\")" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plate.get_item(\"A1\").tracker.get_used_volume()" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "360" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plate.get_item(\"A1\").tracker.get_free_volume()" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "from pylabrobot.resources.liquid import Liquid" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(10, 350)" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plate.get_item(\"A1\").tracker.set_liquids([(Liquid.WATER, 10)])\n", "plate.get_item(\"A1\").tracker.get_used_volume(), plate.get_item(\"A1\").tracker.get_free_volume()" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Resource plate carrier was assigned to the liquid handler.\n" ] } ], "source": [ "lh.deck.assign_child_resource(plt_carrier, rails=9)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Inspecting volume tracker operation history" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 10.0 plate_well_0_0 0,0,0 None None None \n" ] }, { "data": { "text/plain": [ "(0, 360)" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "await lh.aspirate(plate[\"A1\"], vols=[10])\n", "plate.get_item(\"A1\").tracker.get_used_volume(), plate.get_item(\"A1\").tracker.get_free_volume()" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dispensing:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 10.0 plate_well_0_0 0,0,0 None None None \n" ] }, { "data": { "text/plain": [ "(10, 350)" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "await lh.dispense(plate[\"A1\"], vols=[10])\n", "plate.get_item(\"A1\").tracker.get_used_volume(), plate.get_item(\"A1\").tracker.get_free_volume()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Volume tracker errors" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "from pylabrobot.resources.volume_tracker import TooLittleLiquidError, TooLittleVolumeError" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `TooLittleLiquidError` when dispensing\n", "\n", "This error is raised when the volume tracker is trying to dispense from a tip that has less liquid than the requested volume." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "As expected: Tracker only has 0uL\n" ] } ], "source": [ "try:\n", " await lh.dispense(plate[\"A1\"], vols=[100]) # this is less liquid than is currently in the tip\n", "except TooLittleLiquidError as e:\n", " print(\"As expected:\", e)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `TooLittleVolumeError` when aspirating\n", "\n", "This error is raised when the volume tracker is trying to aspirate from a tip that has less free volume than the requested volume." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_1_0 0,0,0 HamiltonTip 1065 8 95.1 Yes \n" ] } ], "source": [ "lh.clear_head_state()\n", "await lh.pick_up_tips(tip_rack[8])" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_0 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_1 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_2 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_3 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_4 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_5 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_6 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_7 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_1_0 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_1_1 0,0,0 None None None \n", "As expected: Container has too little volume: 100uL > 65uL.\n" ] } ], "source": [ "# fill the first two columns\n", "for i in range(16):\n", " plate.get_item(i).tracker.set_liquids([(Liquid.WATER, 100)])\n", "\n", "try:\n", " # aspirate from the first two columns - this is more liquid than the tip can hold\n", " for i in range(16):\n", " await lh.aspirate(plate[i], vols=[100])\n", "except TooLittleVolumeError as e:\n", " print(\"As expected:\", e)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `TooLittleLiquidError` when aspirating\n", "\n", "This error is raised when trying to dispense into a well that has less free volume than the requested volume." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "As expected: Tracker only has 0uL\n" ] } ], "source": [ "try:\n", " await lh.aspirate(plate[\"A1\"], vols=[100]) # this is less liquid than is currently in the well\n", "except TooLittleLiquidError as e:\n", " print(\"As expected:\", e)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### `TooLittleVolumeError` when dispensing\n", "\n", "This error is raised when trying to aspirate from a well that has less liquid than the requested volume." ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Picking up tips:\n", "pip# resource offset tip type max volume (µL) fitting depth (mm) tip length (mm) filter \n", " p0: tip rack_tipspot_1_1 0,0,0 HamiltonTip 1065 8 95.1 Yes \n" ] } ], "source": [ "lh.clear_head_state()\n", "await lh.pick_up_tips(tip_rack[9])" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_1 0,0,0 None None None \n", "Dispensing:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_0 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_2 0,0,0 None None None \n", "Dispensing:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_0 0,0,0 None None None \n", "Aspirating:\n", "pip# vol(ul) resource offset flow rate blowout lld_z \n", " p0: 100.0 plate_well_0_3 0,0,0 None None None \n", "As expected: Container has too little volume: 100uL > 60uL.\n" ] } ], "source": [ "# fill the first column\n", "for i in range(8):\n", " plate.get_item(i).tracker.set_liquids([(Liquid.WATER, 100)])\n", "\n", "try:\n", " # aspirate liquid from the first column into the first well\n", " for i in range(1, 8):\n", " await lh.aspirate(plate[i], vols=[100])\n", " await lh.dispense(plate[\"A1\"], vols=[100])\n", "except TooLittleVolumeError as e:\n", " print(\"As expected:\", e)" ] } ], "metadata": { "kernelspec": { "display_name": "env", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.6" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }