diff --git a/DataSimulations/Introduction_to_Decks.ipynb b/DataSimulations/Introduction_to_Decks.ipynb index 61a4782..1c6ae91 100644 --- a/DataSimulations/Introduction_to_Decks.ipynb +++ b/DataSimulations/Introduction_to_Decks.ipynb @@ -1,144 +1,144 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", - "metadata": {}, - "source": [ - "**Welcome to PyLabRobot!**\n", - "\n", - "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", - "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", - "\n", - "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", - "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", - "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", - "be translatable across many different machines.\n", - "\n", - "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", - "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", - "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", - "the most widely used liquid handling robots." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8b372972-d6dc-454b-95eb-d9941405621b", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", - "await lh.setup()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0324171c-101d-4b3d-9103-a0137c620174", - "metadata": {}, - "outputs": [], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", - "metadata": {}, - "source": [ - "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cos_96_DW_1mL,\n", - " HTF_L\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", - "lh.deck.assign_child_resource(tip_car, rails=15)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", - "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", - "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", - "lh.deck.assign_child_resource(plt_car, rails=8)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} +{ + "cells": [ + { + "cell_type": "markdown", + "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", + "metadata": {}, + "source": [ + "**Welcome to PyLabRobot!**\n", + "\n", + "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", + "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", + "\n", + "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", + "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", + "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", + "be translatable across many different machines.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", + "the most widely used liquid handling robots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.hamilton import STARLetDeck\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b372972-d6dc-454b-95eb-d9941405621b", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "await lh.setup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0324171c-101d-4b3d-9103-a0137c620174", + "metadata": {}, + "outputs": [], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", + "metadata": {}, + "source": [ + "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cos_96_DW_1mL,\n", + " HTF_L\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", + "lh.deck.assign_child_resource(tip_car, rails=15)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", + "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", + "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", + "lh.deck.assign_child_resource(plt_car, rails=8)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Decks/.ipynb_checkpoints/Introduction_to_Decks-checkpoint.ipynb b/Decks/.ipynb_checkpoints/Introduction_to_Decks-checkpoint.ipynb index 61a4782..1c6ae91 100644 --- a/Decks/.ipynb_checkpoints/Introduction_to_Decks-checkpoint.ipynb +++ b/Decks/.ipynb_checkpoints/Introduction_to_Decks-checkpoint.ipynb @@ -1,144 +1,144 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", - "metadata": {}, - "source": [ - "**Welcome to PyLabRobot!**\n", - "\n", - "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", - "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", - "\n", - "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", - "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", - "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", - "be translatable across many different machines.\n", - "\n", - "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", - "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", - "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", - "the most widely used liquid handling robots." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8b372972-d6dc-454b-95eb-d9941405621b", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", - "await lh.setup()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0324171c-101d-4b3d-9103-a0137c620174", - "metadata": {}, - "outputs": [], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", - "metadata": {}, - "source": [ - "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cos_96_DW_1mL,\n", - " HTF_L\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", - "lh.deck.assign_child_resource(tip_car, rails=15)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", - "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", - "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", - "lh.deck.assign_child_resource(plt_car, rails=8)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} +{ + "cells": [ + { + "cell_type": "markdown", + "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", + "metadata": {}, + "source": [ + "**Welcome to PyLabRobot!**\n", + "\n", + "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", + "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", + "\n", + "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", + "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", + "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", + "be translatable across many different machines.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", + "the most widely used liquid handling robots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.hamilton import STARLetDeck\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b372972-d6dc-454b-95eb-d9941405621b", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "await lh.setup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0324171c-101d-4b3d-9103-a0137c620174", + "metadata": {}, + "outputs": [], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", + "metadata": {}, + "source": [ + "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cos_96_DW_1mL,\n", + " HTF_L\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", + "lh.deck.assign_child_resource(tip_car, rails=15)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", + "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", + "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", + "lh.deck.assign_child_resource(plt_car, rails=8)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Decks/.ipynb_checkpoints/OT2_Deck_Intro-checkpoint.ipynb b/Decks/.ipynb_checkpoints/OT2_Deck_Intro-checkpoint.ipynb new file mode 100644 index 0000000..e61bb9d --- /dev/null +++ b/Decks/.ipynb_checkpoints/OT2_Deck_Intro-checkpoint.ipynb @@ -0,0 +1,750 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8dbbadf0-ac05-44a6-a303-e7e71691ed9a", + "metadata": {}, + "source": [ + "# Intro to Decks: OT2\n", + "### In this tutorial, you will see how to instantiate an OT2 Deck, how to add labware, and how to move liquids around.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `OTDeck` that will represent the deck of an OpenTrons OT2, one of\n", + "the most widely used liquid handling robots. \n", + "\n", + "Make sure to also `import opentrons` !" + ] + }, + { + "cell_type": "markdown", + "id": "0b6ee87a-a089-4a5b-9b60-852d1e1f8874", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9faf434a-2fc5-44cf-8dc9-7a68475d47bf", + "metadata": {}, + "outputs": [], + "source": [ + "import pylabrobot" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "069fb033-3ea2-4772-b55b-c800ed17dab5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/mnt/d/Chory Lab/PyLabRobot/pylabrobot/__init__.py'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pylabrobot.__file__" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "63636707-3e3c-4aba-b8eb-1e702875d391", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.opentrons import OTDeck\n", + "\n", + "from pylabrobot.resources.opentrons.load import *\n", + "from pylabrobot.resources.opentrons.plates import *\n", + "\n", + "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", + "set_tip_tracking(True), set_volume_tracking(True)\n", + "\n", + "import opentrons\n", + "import time" + ] + }, + { + "cell_type": "markdown", + "id": "10bcab3e-f908-479a-8e63-1fecfbe9ecf7", + "metadata": {}, + "source": [ + "### Setting up the Deck and Visualizer" + ] + }, + { + "cell_type": "markdown", + "id": "7a612e7b-d246-4c75-874c-1b053ab2aeda", + "metadata": {}, + "source": [ + "First, we will create an instance of the `LiquidHandler` class. This may take some time to set up, so we run the `setup()` function with the `await` keyword." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb5eb1ee-a1ae-4fd3-afff-78967d6e9f95", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Setting up the robot.\n", + "Resource deck was assigned to the robot.\n", + "Resource trash_container was assigned to the robot.\n" + ] + } + ], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=OTDeck())\n", + "\n", + "await lh.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "c044f917-ae75-4e17-80e1-7ad0f13b64cc", + "metadata": {}, + "source": [ + "After initializing our `LiquidHandler`, we want to create an instance of a `Visualizer`. This will allow you to see the Deck and follow your protocol in real time. You can see how tips and liquids move as you run commands. Make sure to open the `Visualizer` in another window." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9ce8270d-91ed-4836-a742-ab5375c4fdaa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Websocket server started at http://127.0.0.1:2122\n", + "File server started at http://127.0.0.1:1338 . Open this URL in your browser.\n" + ] + } + ], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "70c070bd-f7fd-47c4-9c98-65674fa776fc", + "metadata": {}, + "source": [ + "### Adding Labware to the Deck" + ] + }, + { + "cell_type": "markdown", + "id": "4d0024ee-4722-4b99-afad-3bba67388855", + "metadata": {}, + "source": [ + "Now, we are ready to add some labware to the deck. PyLabRobot has many different labware items already defined. Only import the ones that you need for your protocol. A full list of labware can be found in `PyLabRobot\\Resources\\opentrons`. You can also create custom labware, but that is out of scope for this tutorial.\n", + "\n", + "Let's begin by importing a `TubeRack`, a `TipRack`, and a `Plate`.\n", + "
\n", + " Definitions of Labware:\n", + " \n", + "* **TubeRack** = Used to hold various tubes, commonly the 2mL Eppendorfs.\n", + "\n", + "* **TipRack** = Labware that holds pipette tips.\n", + "\n", + "* **Plate** = Well-Plate that one can add liquids to.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "71889711-e068-4e7e-b158-128a9491a1a0", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap,\n", + " opentrons_96_tiprack_300ul,\n", + " corning_96_wellplate_360ul_flat\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "fae79fcb-7dbb-4145-a03a-eef0da7d6e38", + "metadata": {}, + "source": [ + "**Note:** The OT2 has **11 spots** for labware. It also has a built in trash can for discarding tips.\n", + "\n", + "One thing to keep in mind when designing a protocol is `layout efficiency`. The more separated labware is on the deck, the longer your protocol will be because the pipette has to travel farther.\n", + "\n", + "In general, you want to keep your tip racks in the back, your stocks in the middle, and then finally your plates in the front. This allows for `linear movement`.\n", + "\n", + "For example, the arm grabs a **tip from slot 7**, aspirates **stock from slot 4**, and then finally **dispenses in slot 1**. Reducing the travel time of the pipette will decrease the runtime of your protocol.\n", + "\n", + "To place labware on the Deck, call `assign_child_at_slot()`. Pass in the labware you want to place along with the slot you want to place it in." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c4c43470-21ff-4b3f-9a36-cdc3c0d97403", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource tip_rack was assigned to the robot.\n" + ] + } + ], + "source": [ + "# When you instantiate a labware, give it a name that will show when you\n", + "# mouse over it in the visualizer.\n", + "tip_rack = opentrons_96_tiprack_300ul(\"tip_rack\")\n", + "\n", + "lh.deck.assign_child_at_slot(tip_rack, 7)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3740eb57-80bd-498b-95d5-725b9487c196", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource tube_rack was assigned to the robot.\n" + ] + } + ], + "source": [ + "# Stock Solution Tube Rack\n", + "tube_rack = opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap(\"tube_rack\")\n", + "\n", + "lh.deck.assign_child_at_slot(tube_rack, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "fd6a17ea-b41e-4033-a914-81b2d87635b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource prep_plate was assigned to the robot.\n" + ] + } + ], + "source": [ + "# Plate for Sample Preparation\n", + "plate = corning_96_wellplate_360ul_flat(\"prep_plate\")\n", + "\n", + "lh.deck.assign_child_at_slot(plate, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "8f58b38f-d40b-4a07-8162-b31d87fdcb12", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Deck: 624.3mm x 565.2mm\n", + "\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 10: Empty | 11: Empty | 12: trash_co... |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 7: tip_rack | 8: Empty | 9: Empty |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 4: tube_rack | 5: Empty | 6: Empty |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 1: prep_plate | 2: Empty | 3: Empty |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "\n" + ] + } + ], + "source": [ + "print(lh.deck.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "b7c49e37-543c-49b5-86f0-5c9df9bdd5a1", + "metadata": {}, + "source": [ + "### Adding Liquids to the Deck" + ] + }, + { + "cell_type": "markdown", + "id": "b757ebe8-be14-48af-bda6-f377a8769d0b", + "metadata": {}, + "source": [ + "Let's add some liquids to our `tube_rack`. We can add up to the `max_volume` of the tube. If you go over this number, PyLabRobot will throw an error. We shall add 1000µL of 4 different dyes to the first column of our `tube_rack`. This corresponds to wells *A1, B1, C1, and D1*.\n", + "\n", + "To iterate over locations on labware, we use the `traverse()` function. This produces a generator object that we use the `next` keyword on to yield our desired wells.\n", + "\n", + "`traverse()` takes in two arguments: **batch_size** is the amount of wells to return, and **direction** is how to iterate over the wells. In our case, we use `\"down\"` to return the wells column wise.\n", + "\n", + "
\n", + " More info on direction:\n", + "\n", + "* `\"down\"`, `\"snake_down\"`, `\"right\"`, and `\"snake_right\"` start at the top left item **(A1)**.\n", + " \n", + "* `\"up\"` and `\"snake_up\"` start at the bottom left **(H1)**.\n", + " \n", + "* `\"left\"` and `\"snake_left\"` start at the top right **(A12)**.\n", + "\n", + "* The `snake` directions alternate between going in the given direction and going in the opposite direction. For example, `\"snake_down\"` will go from A1 to H1, then H2 to A2, then A3 to H3, etc.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "aca186f8-922b-4226-89ef-e6cfe886b512", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Tube(name=tube_rack_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=tube_rack_B1, location=(018.210, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=tube_rack_C1, location=(018.210, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=tube_rack_D1, location=(018.210, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube)]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first_col_tubes = next(tube_rack.traverse(batch_size=4, direction='down'))\n", + "first_col_tubes" + ] + }, + { + "cell_type": "markdown", + "id": "88f4e079-81e1-441c-923c-63e2a8b1cfb7", + "metadata": {}, + "source": [ + "Use the `tracker.add_liquid()` function to put liquid in a `tube`. The `tracker` class contains all of the methods associated with keeping record of how much/what kind of liquid is in a given container.\n", + "\n", + "Pass in a string for **\"Liquid_Type\"** and a number for **Volume** to this function.\n", + "\n", + "Let's add our dyes to the tubes in `first_col_tubes`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "876af18c-fdde-4a78-8261-b9a61c580988", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(len(first_col_tubes)):\n", + " # [(liquid, volume)]\n", + " first_col_tubes[i].tracker.set_liquids([(f\"Dye_{i}\", 1000)])\n", + " \n", + " #Commit the change\n", + " #first_col_tubes[i].tracker.commit()" + ] + }, + { + "cell_type": "markdown", + "id": "1e0137ea-bd0f-4a8f-9785-3b7aafe5c7dd", + "metadata": {}, + "source": [ + "Here's a Utility function for printing all of the filled spots of a `TubeRack`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ee999023-9b95-41b0-90c4-4e05bc65d629", + "metadata": {}, + "outputs": [], + "source": [ + "def print_filled_spots_of_tubeRack(tube_rack):\n", + "\n", + " all_tubes = tube_rack.get_all_children()\n", + "\n", + " all_empty = True\n", + " \n", + " for tube in all_tubes:\n", + " \n", + " liquid = tube.tracker.liquids\n", + "\n", + " if liquid != []:\n", + " print(f\"Spot {tube.name.split('_')[-1]} contains:\")\n", + "\n", + " name = liquid[0][0]\n", + " vol = liquid[0][1]\n", + " \n", + " print(f\"{vol}uL of {name}\")\n", + "\n", + " all_empty = False\n", + "\n", + " if all_empty:\n", + " print(\"Entire rack is empty!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1832d167-395f-44fe-911b-1584b9a71281", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Spot A1 contains:\n", + "1000uL of Dye_0\n", + "Spot B1 contains:\n", + "1000uL of Dye_1\n", + "Spot C1 contains:\n", + "1000uL of Dye_2\n", + "Spot D1 contains:\n", + "1000uL of Dye_3\n" + ] + } + ], + "source": [ + "print_filled_spots_of_tubeRack(tube_rack)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "c1e08eec-4b28-498b-8374-09a62b4cefa0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Entire rack is empty!\n" + ] + } + ], + "source": [ + "print_filled_spots_of_tubeRack(plate)" + ] + }, + { + "cell_type": "markdown", + "id": "ffceed8a-b9c4-4824-85e4-2676957811f6", + "metadata": {}, + "source": [ + "### Moving Liquids from Point A to Point B" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0e85d828-b3f5-4ad2-9e87-56ccd985720f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.return_tips()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "5f277deb-be20-4ce6-b888-1e5811e6e137", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"A1\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "8101f459-a1ce-4f1e-a482-2998da60e2d8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"A1\", \"B2\", \"C3\", \"D4\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "f1d968f1-1889-474e-8106-a55369d6e412", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n", + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"A1\", \"B2\", \"C3\", \"D4\"])\n", + "time.sleep(2)\n", + "await lh.drop_tips(tip_rack[\"A1\", \"B2\", \"C3\", \"D4\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "52ae208a-cff3-419d-91e4-0293b437871b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"A1\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "0b9a66e5-ac0c-456f-aad0-7c3597925da7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tip_rack[\"A1\"][0].has_tip()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "5f55c2d7-6e9c-4190-a1d7-ef65e7a93915", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating [Aspiration(resource=Tube(name=tube_rack_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=200, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 200)])].\n", + "Dispensing [Dispense(resource=Well(name=prep_plate_A1, location=(014.380, 074.240, 003.550), size_x=4.851, size_y=4.851, size_z=10.67, category=well), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=200, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 200)])].\n", + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.aspirate(tube_rack[\"A1\"], vols=[200])\n", + "time.sleep(2)\n", + "\n", + "await lh.dispense(plate[\"A1\"], vols=[200])\n", + "#plate[\"A1\"][0].tracker.commit()\n", + "time.sleep(2)\n", + "\n", + "await lh.return_tips()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "e51ed207-74e3-4ad1-9aa4-7a79ece40016", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Spot A1 contains:\n", + "800.0uL of Dye_0\n", + "Spot B1 contains:\n", + "1000uL of Dye_1\n", + "Spot C1 contains:\n", + "1000uL of Dye_2\n", + "Spot D1 contains:\n", + "1000uL of Dye_3\n" + ] + } + ], + "source": [ + "print_filled_spots_of_tubeRack(tube_rack)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "7ac2d2e3-dfb0-4d5f-8aee-8769cfd804f1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n", + "Aspirating [Aspiration(resource=Well(name=prep_plate_A1, location=(014.380, 074.240, 003.550), size_x=4.851, size_y=4.851, size_z=10.67, category=well), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 100)])].\n", + "Dispensing [Dispense(resource=Tube(name=tube_rack_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 100)])].\n", + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"A1\"])\n", + "time.sleep(1)\n", + "\n", + "await lh.aspirate(plate[\"A1\"], vols=[100])\n", + "time.sleep(1)\n", + "\n", + "await lh.dispense(tube_rack[\"A1\"], vols=[100])\n", + "time.sleep(1)\n", + "\n", + "await lh.return_tips()" + ] + }, + { + "cell_type": "markdown", + "id": "29adc390-9c6f-49c5-9112-5cdb7ce7b7a1", + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, + "source": [ + "### For future reference, how to export states of labware as .json files" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "22fab8bb-af21-4f79-982e-ca46a11df867", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "' Save the state of this resource and all children to a JSON file.\\n\\n Args:\\n fn: File name. Caution: file will be overwritten.\\n indent: Same as `json.dump`\\'s `indent` argument (for json pretty printing).\\n\\n Examples:\\n Saving to a json file:\\n\\n >>> deck.save_state_to_file(\"my_state.json\")\\n'" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\"\" Save the state of this resource and all children to a JSON file.\n", + "\n", + " Args:\n", + " fn: File name. Caution: file will be overwritten.\n", + " indent: Same as `json.dump`'s `indent` argument (for json pretty printing).\n", + "\n", + " Examples:\n", + " Saving to a json file:\n", + "\n", + " >>> deck.save_state_to_file(\"my_state.json\")\n", + "\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9d2169c-1e13-4abd-8095-6844b856d84e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Decks/Introduction_to_Decks.ipynb b/Decks/Introduction_to_Decks.ipynb index 61a4782..fbade5d 100644 --- a/Decks/Introduction_to_Decks.ipynb +++ b/Decks/Introduction_to_Decks.ipynb @@ -136,7 +136,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/Decks/OT2_Deck_Intro.ipynb b/Decks/OT2_Deck_Intro.ipynb new file mode 100644 index 0000000..8c3dc2d --- /dev/null +++ b/Decks/OT2_Deck_Intro.ipynb @@ -0,0 +1,980 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8dbbadf0-ac05-44a6-a303-e7e71691ed9a", + "metadata": {}, + "source": [ + "# Intro to Decks: OT2\n", + "### In this tutorial, you will see how to instantiate an OT2 Deck, how to add labware, and how to move liquids around.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `OTDeck` that will represent the deck of an OpenTrons OT2, one of\n", + "the most widely used liquid handling robots. \n", + "\n", + "Make sure to also `import opentrons` !" + ] + }, + { + "cell_type": "markdown", + "id": "0b6ee87a-a089-4a5b-9b60-852d1e1f8874", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9faf434a-2fc5-44cf-8dc9-7a68475d47bf", + "metadata": {}, + "outputs": [], + "source": [ + "import pylabrobot" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "069fb033-3ea2-4772-b55b-c800ed17dab5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/mnt/d/Chory Lab/PyLabRobot/pylabrobot/__init__.py'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pylabrobot.__file__" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "63636707-3e3c-4aba-b8eb-1e702875d391", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.opentrons import OTDeck\n", + "\n", + "from pylabrobot.resources.opentrons.load import *\n", + "from pylabrobot.resources.opentrons.plates import *\n", + "\n", + "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", + "set_tip_tracking(True), set_volume_tracking(True)\n", + "\n", + "import opentrons\n", + "import time" + ] + }, + { + "cell_type": "markdown", + "id": "10bcab3e-f908-479a-8e63-1fecfbe9ecf7", + "metadata": {}, + "source": [ + "### Setting up the Deck and Visualizer" + ] + }, + { + "cell_type": "markdown", + "id": "7a612e7b-d246-4c75-874c-1b053ab2aeda", + "metadata": {}, + "source": [ + "First, we will create an instance of the `LiquidHandler` class. This may take some time to set up, so we run the `setup()` function with the `await` keyword." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "cb5eb1ee-a1ae-4fd3-afff-78967d6e9f95", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Setting up the robot.\n", + "Resource deck was assigned to the robot.\n", + "Resource trash_container was assigned to the robot.\n" + ] + } + ], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=OTDeck())\n", + "\n", + "await lh.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "c044f917-ae75-4e17-80e1-7ad0f13b64cc", + "metadata": {}, + "source": [ + "After initializing our `LiquidHandler`, we want to create an instance of a `Visualizer`. This will allow you to see the Deck and follow your protocol in real time. You can see how tips and liquids move as you run commands. Make sure to open the `Visualizer` in another window." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9ce8270d-91ed-4836-a742-ab5375c4fdaa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Websocket server started at http://127.0.0.1:2121\n", + "File server started at http://127.0.0.1:1337 . Open this URL in your browser.\n" + ] + } + ], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "70c070bd-f7fd-47c4-9c98-65674fa776fc", + "metadata": {}, + "source": [ + "### Adding Labware to the Deck" + ] + }, + { + "cell_type": "markdown", + "id": "4d0024ee-4722-4b99-afad-3bba67388855", + "metadata": {}, + "source": [ + "Now, we are ready to add some labware to the deck. **PyLabRobot** has many different labware items already defined. Only import the ones that you need for your protocol. A full list of labware can be found in `PyLabRobot\\Resources\\opentrons`. You can also create custom labware, but that is out of scope for this tutorial.\n", + "\n", + "Let's begin by importing a `TubeRack`, a `TipRack`, and a `Plate`.\n", + "
\n", + " Definitions of Labware:\n", + " \n", + "* **TubeRack** = Used to hold various tubes, commonly the 2mL Eppendorfs.\n", + "\n", + "* **TipRack** = Labware that holds pipette tips.\n", + "\n", + "* **Plate** = Well-Plate that one can add liquids to.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "71889711-e068-4e7e-b158-128a9491a1a0", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap,\n", + " opentrons_96_tiprack_300ul,\n", + " corning_96_wellplate_360ul_flat\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "fae79fcb-7dbb-4145-a03a-eef0da7d6e38", + "metadata": {}, + "source": [ + "**Note:** The OT2 has **11 spots** for labware. It also has a built in trash can for discarding tips.\n", + "\n", + "One thing to keep in mind when designing a protocol is `layout efficiency`. The more separated labware is on the deck, the longer your protocol will be because the pipette has to travel farther.\n", + "\n", + "In general, you want to keep your tip racks in the back, your stocks in the middle, and then finally your plates in the front. This allows for `linear movement`.\n", + "\n", + "For example, the arm grabs a **tip from slot 7**, aspirates **stock from slot 4**, and then finally **dispenses in slot 1**. Reducing the travel time of the pipette will decrease the runtime of your protocol.\n", + "\n", + "To place labware on the Deck, call `assign_child_at_slot()`. Pass in the labware you want to place along with the slot you want to place it in." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c4c43470-21ff-4b3f-9a36-cdc3c0d97403", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource tip_rack was assigned to the robot.\n" + ] + } + ], + "source": [ + "# When you instantiate a labware, give it a name that will show when you\n", + "# mouse over it in the visualizer.\n", + "tip_rack = opentrons_96_tiprack_300ul(\"tip_rack\")\n", + "\n", + "lh.deck.assign_child_at_slot(tip_rack, 7)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3740eb57-80bd-498b-95d5-725b9487c196", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource tube_rack was assigned to the robot.\n" + ] + } + ], + "source": [ + "# Stock Solution Tube Rack\n", + "tube_rack = opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap(\"tube_rack\")\n", + "\n", + "lh.deck.assign_child_at_slot(tube_rack, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "fd6a17ea-b41e-4033-a914-81b2d87635b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource prep_plate was assigned to the robot.\n" + ] + } + ], + "source": [ + "# Plate for Sample Preparation\n", + "plate = corning_96_wellplate_360ul_flat(\"prep_plate\")\n", + "\n", + "lh.deck.assign_child_at_slot(plate, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "8f58b38f-d40b-4a07-8162-b31d87fdcb12", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Deck: 624.3mm x 565.2mm\n", + "\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 10: Empty | 11: Empty | 12: trash_co... |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 7: tip_rack | 8: Empty | 9: Empty |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 4: tube_rack | 5: Empty | 6: Empty |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "| | | |\n", + "| 1: prep_plate | 2: Empty | 3: Empty |\n", + "| | | |\n", + "+-----------------+-----------------+-----------------+\n", + "\n" + ] + } + ], + "source": [ + "print(lh.deck.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "b7c49e37-543c-49b5-86f0-5c9df9bdd5a1", + "metadata": {}, + "source": [ + "### Adding Liquids to the Deck" + ] + }, + { + "cell_type": "markdown", + "id": "b757ebe8-be14-48af-bda6-f377a8769d0b", + "metadata": {}, + "source": [ + "Let's add some liquids to our `tube_rack`. We can add up to the `max_volume` of the tube. If you go over this number, **PyLabRobot** will throw an error. We shall add 1000µL of 4 different dyes to the first column of our `tube_rack`. This corresponds to wells *A1, B1, C1, and D1*.\n", + "\n", + "To iterate over locations on labware, we use the `traverse()` function. This produces a generator object that we use the `next` keyword on to yield our desired wells.\n", + "\n", + "`traverse()` takes in two arguments: **batch_size** is the amount of wells to return, and **direction** is how to iterate over the wells. In our case, we use `\"down\"` to return the wells column wise.\n", + "\n", + "
\n", + " More info on direction:\n", + "\n", + "* `\"down\"`, `\"snake_down\"`, `\"right\"`, and `\"snake_right\"` start at the top left item **(A1)**.\n", + " \n", + "* `\"up\"` and `\"snake_up\"` start at the bottom left **(H1)**.\n", + " \n", + "* `\"left\"` and `\"snake_left\"` start at the top right **(A12)**.\n", + "\n", + "* The `snake` directions alternate between going in the given direction and going in the opposite direction. For example, `\"snake_down\"` will go from A1 to H1, then H2 to A2, then A3 to H3, etc.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "aca186f8-922b-4226-89ef-e6cfe886b512", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Tube(name=tube_rack_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=tube_rack_B1, location=(018.210, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=tube_rack_C1, location=(018.210, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=tube_rack_D1, location=(018.210, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube)]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first_col_tubes = next(tube_rack.traverse(batch_size=4, direction='down'))\n", + "first_col_tubes" + ] + }, + { + "cell_type": "markdown", + "id": "88f4e079-81e1-441c-923c-63e2a8b1cfb7", + "metadata": {}, + "source": [ + "Use the `tracker.set_liquids()` function to put liquid in a `tube`. The `tracker` class contains all of the methods associated with keeping record of how much/what kind of liquid is in a given container.\n", + "\n", + "Pass in a string for **\"Liquid_Type\"** and a number for **Volume** to this function.\n", + "\n", + "Let's add our dyes to the tubes in `first_col_tubes`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "876af18c-fdde-4a78-8261-b9a61c580988", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(len(first_col_tubes)):\n", + " # [(liquid, volume)]\n", + " first_col_tubes[i].tracker.set_liquids([(f\"Dye_{i}\", 2000)])" + ] + }, + { + "cell_type": "markdown", + "id": "1e0137ea-bd0f-4a8f-9785-3b7aafe5c7dd", + "metadata": {}, + "source": [ + "Here's a Utility function for printing all of the filled spots of a `TubeRack`." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "ee999023-9b95-41b0-90c4-4e05bc65d629", + "metadata": {}, + "outputs": [], + "source": [ + "def print_filled_spots_of_tubeRack(tube_rack):\n", + "\n", + " all_tubes = tube_rack.get_all_children()\n", + "\n", + " all_empty = True\n", + " \n", + " for tube in all_tubes:\n", + " \n", + " liquid = tube.tracker.liquids\n", + "\n", + " if liquid != []:\n", + " print(f\"Spot {tube.name.split('_')[-1]} contains:\")\n", + "\n", + " name = liquid[0][0]\n", + " vol = liquid[0][1]\n", + " \n", + " print(f\"{vol}uL of {name}\")\n", + "\n", + " all_empty = False\n", + "\n", + " if all_empty:\n", + " print(\"Entire rack is empty!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "1832d167-395f-44fe-911b-1584b9a71281", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Spot A1 contains:\n", + "2000uL of Dye_0\n", + "Spot B1 contains:\n", + "2000uL of Dye_1\n", + "Spot C1 contains:\n", + "2000uL of Dye_2\n", + "Spot D1 contains:\n", + "2000uL of Dye_3\n" + ] + } + ], + "source": [ + "print_filled_spots_of_tubeRack(tube_rack)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c1e08eec-4b28-498b-8374-09a62b4cefa0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Entire rack is empty!\n" + ] + } + ], + "source": [ + "print_filled_spots_of_tubeRack(plate)" + ] + }, + { + "cell_type": "markdown", + "id": "ffceed8a-b9c4-4824-85e4-2676957811f6", + "metadata": {}, + "source": [ + "### Moving Liquids from Point A to Point B" + ] + }, + { + "cell_type": "markdown", + "id": "4d7e2781-aee6-45a3-82a0-09a56e8452aa", + "metadata": {}, + "source": [ + "Now that we've added some dyes to our tube rack, let's use the robot to move some liquid to our `prep_plate`. The first step of a liquid transfer is acquiring a tip.\n", + "\n", + "You can acquire a tip by calling `lh.pick_up_tips()`, and passing in the `TipSpots` of the tips you want to retrieve. `TipSpots` are indexed the same way that wells are.\n", + "\n", + "When a tip is picked up from a `TipRack`, it's location will turn white on the visualizer. To see what tips are currently on the robot, call `lh.head`. This returns a dictionary where the keys are the indices of the different channels of the main pipettor, and where the values are instances of the `TipTracker` class, allowing you to get information about how tips move on and off of a given channel.\n", + "\n", + "If you want to reset the state of tips on a robot, call `lh.return_tips()`. This function will automatically return the tips to their original locations." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "be4dfa3a-4654-4b1e-917b-91fd163b0a04", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_E7, location=(068.380, 038.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_D3, location=(032.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.return_tips()" + ] + }, + { + "cell_type": "markdown", + "id": "3ad6d617-4927-4898-b3b0-5c6c896367c7", + "metadata": {}, + "source": [ + "Let's try picking up 8 tips along the diagonal." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "f1d968f1-1889-474e-8106-a55369d6e412", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_E5, location=(050.380, 038.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_F6, location=(059.380, 029.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_G7, location=(068.380, 020.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_H8, location=(077.380, 011.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"A1\", \"B2\", \"C3\", \"D4\", \"E5\", \"F6\", \"G7\", \"H8\"])" + ] + }, + { + "cell_type": "markdown", + "id": "7e370bb6-b432-46dc-88ce-851bde0b7d84", + "metadata": {}, + "source": [ + "Rather than use `lh.return_tips()` all of the time, you can also call `lh.drop_tips()` and pass in specifically where to place the tip, and what channel's tip to drop." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "7abad4b7-d3e2-4d45-a33c-651f41b9a590", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_E5, location=(050.380, 038.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_F6, location=(059.380, 029.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_G7, location=(068.380, 020.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_H8, location=(077.380, 011.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.drop_tips(tip_spots = tip_rack[\"A1\", \"B2\", \"C3\", \"D4\", \"E5\", \"F6\", \"G7\", \"H8\"],\n", + " use_channels = [0,1,2,3,4,5,6,7])" + ] + }, + { + "cell_type": "markdown", + "id": "31159c96-4f4a-4abf-9a83-f93bef0da703", + "metadata": {}, + "source": [ + "The order in which you pass in the `tip_spots` and the `use_channels` lists will determine which channel gets which tip. Take a look!" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "52ae208a-cff3-419d-91e4-0293b437871b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_E7, location=(068.380, 038.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_spots = tip_rack[\"A1\", 'C3', \"E7\"], use_channels = [2,0,1])" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "807b3ba9-57db-483e-aca2-419f439e8520", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: TipTracker(Channel 0, is_disabled=False, has_tip=False tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), pending_tip=None),\n", + " 1: TipTracker(Channel 1, is_disabled=False, has_tip=True tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), pending_tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)),\n", + " 2: TipTracker(Channel 2, is_disabled=False, has_tip=False tip=None, pending_tip=None),\n", + " 3: TipTracker(Channel 3, is_disabled=False, has_tip=False tip=None, pending_tip=None),\n", + " 4: TipTracker(Channel 4, is_disabled=False, has_tip=True tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), pending_tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)),\n", + " 5: TipTracker(Channel 5, is_disabled=False, has_tip=False tip=None, pending_tip=None),\n", + " 6: TipTracker(Channel 6, is_disabled=False, has_tip=False tip=None, pending_tip=None),\n", + " 7: TipTracker(Channel 7, is_disabled=False, has_tip=False tip=None, pending_tip=None)}" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lh.head" + ] + }, + { + "cell_type": "markdown", + "id": "e2931c39-74db-4426-9be9-0a702c677489", + "metadata": {}, + "source": [ + "Here's another utility function that will print the status of tips on the pipetter. If there is a tip on a channel, this function will output its origin." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "d579d650-e172-4c58-b9e9-a96262609f60", + "metadata": {}, + "outputs": [], + "source": [ + "def print_channels_tip_origin(lh):\n", + " # Prints the origin location of all tips currently on the robot\n", + " cur_pipetter = lh.head\n", + "\n", + " for channel in cur_pipetter:\n", + " print(f\"Channel {channel}:\")\n", + "\n", + " tip_tracker = lh.head[channel]\n", + " \n", + " if tip_tracker.has_tip == True:\n", + " print(tip_tracker.get_tip_origin())\n", + " else:\n", + " print(\"No tip present.\")\n", + " print()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "35530ae2-b0ff-4554-9da3-1b714357b9d7", + "metadata": {}, + "outputs": [], + "source": [ + "def print_channel_status(lh):\n", + " # Prints the status of liquids, if present, in each channel\n", + " cur_pipetter = lh.head\n", + "\n", + " for channel in cur_pipetter:\n", + " print(f\"Channel {channel}:\")\n", + "\n", + " tip_tracker = lh.head[channel]\n", + " \n", + " if tip_tracker.has_tip == True:\n", + " tip = tip_tracker.get_tip()\n", + " print(tip.tracker.liquids)\n", + " else:\n", + " print(\"No tip present.\")\n", + " print()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "f4019779-4c85-4d46-8c35-960d26b58618", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Channel 0:\n", + "No tip present.\n", + "\n", + "Channel 1:\n", + "TipSpot(name=tip_rack_E7, location=(068.380, 038.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot)\n", + "\n", + "Channel 2:\n", + "No tip present.\n", + "\n", + "Channel 3:\n", + "No tip present.\n", + "\n", + "Channel 4:\n", + "TipSpot(name=tip_rack_D3, location=(032.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot)\n", + "\n", + "Channel 5:\n", + "No tip present.\n", + "\n", + "Channel 6:\n", + "No tip present.\n", + "\n", + "Channel 7:\n", + "No tip present.\n", + "\n" + ] + } + ], + "source": [ + "print_channels_tip_origin(lh)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "8f041cd7-6e04-4156-b253-dc840beac2f0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Channel 0:\n", + "No tip present.\n", + "\n", + "Channel 1:\n", + "[]\n", + "\n", + "Channel 2:\n", + "No tip present.\n", + "\n", + "Channel 3:\n", + "No tip present.\n", + "\n", + "Channel 4:\n", + "[]\n", + "\n", + "Channel 5:\n", + "No tip present.\n", + "\n", + "Channel 6:\n", + "No tip present.\n", + "\n", + "Channel 7:\n", + "No tip present.\n", + "\n" + ] + } + ], + "source": [ + "print_channel_status(lh)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "9c35e2c8-f75a-47ab-a57a-b2f3a26bfa77", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lh.head[1].get_tip().tracker.pending_liquids" + ] + }, + { + "cell_type": "markdown", + "id": "60eecc01-1226-4b5e-8a19-f4f55cbfb15c", + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, + "source": [ + "You also don't need to specify channels in order. If you wanted to skip channel 3, we can do so by just passing in the next channel index and skipping 3." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "62947627-8930-4bc5-ad97-081f5e157ad7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_D3, location=(032.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"D3\"], use_channels = [4])" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "a7b792f0-a30f-43dd-9c39-b230b4f3dd52", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_E7, location=(068.380, 038.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.drop_tips(tip_spots = tip_rack[\"E7\"], use_channels = [2])" + ] + }, + { + "cell_type": "markdown", + "id": "110cbad9-a473-4911-ace3-cdcae2bbbba3", + "metadata": {}, + "source": [ + "If you want to check if a spot in the tip rack has a tip, call `TipSpot.has_tip()`. This could be useful if you are writing a script with many operations and want to automatically calculate where you should grab your next tip from." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "0b9a66e5-ac0c-456f-aad0-7c3597925da7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tip_rack[\"A1\"][0].has_tip()" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "25ff6efc-bf4b-49b2-9b37-4386d2f080af", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating [Aspiration(resource=Tube(name=tube_rack_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=200, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 200)])].\n" + ] + } + ], + "source": [ + "await lh.aspirate(tube_rack[\"A1\"], vols=[200])" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "5f55c2d7-6e9c-4190-a1d7-ef65e7a93915", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating [Aspiration(resource=Tube(name=tube_rack_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=200, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 200)])].\n", + "Dispensing [Dispense(resource=Well(name=prep_plate_A1, location=(014.380, 074.240, 003.550), size_x=4.851, size_y=4.851, size_z=10.67, category=well), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=200, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 200)])].\n" + ] + }, + { + "ename": "HasTipError", + "evalue": "Tip spot already has a tip.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mHasTipError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_727/3932198594.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msleep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0;32mawait\u001b[0m \u001b[0mlh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreturn_tips\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/mnt/d/Chory Lab/PyLabRobot/pylabrobot/liquid_handling/liquid_handler.py\u001b[0m in \u001b[0;36mreturn_tips\u001b[0;34m(self, use_channels, **backend_kwargs)\u001b[0m\n\u001b[1;32m 559\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"No tips have been picked up.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 560\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 561\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdrop_tips\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtip_spots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtip_spots\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0muse_channels\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mchannels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mbackend_kwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 562\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 563\u001b[0m async def discard_tips(\n", + "\u001b[0;32m/mnt/d/Chory Lab/PyLabRobot/pylabrobot/machines/machine.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetup_finished\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"The setup has not finished. See `setup`.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 25\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;32mawait\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 26\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwrapper\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 27\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/mnt/d/Chory Lab/PyLabRobot/pylabrobot/liquid_handling/liquid_handler.py\u001b[0m in \u001b[0;36mdrop_tips\u001b[0;34m(self, tip_spots, use_channels, offsets, allow_nonzero_volume, **backend_kwargs)\u001b[0m\n\u001b[1;32m 489\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdoes_tip_tracking\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresource\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTipSpot\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 490\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresource\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtracker\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_disabled\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 491\u001b[0;31m \u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresource\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtracker\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_tip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtip\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommit\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 492\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhead\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mchannel\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove_tip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 493\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/mnt/d/Chory Lab/PyLabRobot/pylabrobot/resources/tip_tracker.py\u001b[0m in \u001b[0;36madd_tip\u001b[0;34m(self, tip, origin, commit)\u001b[0m\n\u001b[1;32m 82\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Tip tracker is disabled. Call `enable()`.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_pending_tip\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 84\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mHasTipError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{self.thing} already has a tip.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 85\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_pending_tip\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtip\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 86\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mHasTipError\u001b[0m: Tip spot already has a tip." + ] + } + ], + "source": [ + "await lh.aspirate(tube_rack[\"A1\"], vols=[200])\n", + "time.sleep(2)\n", + "\n", + "await lh.dispense(plate[\"A1\"], vols=[200])\n", + "#plate[\"A1\"][0].tracker.commit()\n", + "time.sleep(2)\n", + "\n", + "await lh.return_tips()" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "e51ed207-74e3-4ad1-9aa4-7a79ece40016", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Spot A1 contains:\n", + "200uL of Dye_0\n" + ] + } + ], + "source": [ + "print_filled_spots_of_tubeRack(plate)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "7ac2d2e3-dfb0-4d5f-8aee-8769cfd804f1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n", + "Aspirating [Aspiration(resource=Well(name=prep_plate_A1, location=(014.380, 074.240, 003.550), size_x=4.851, size_y=4.851, size_z=10.67, category=well), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 100)])].\n", + "Dispensing [Dispense(resource=Tube(name=tube_rack_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Dye_0', 100)])].\n", + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack[\"A1\"])\n", + "time.sleep(1)\n", + "\n", + "await lh.aspirate(plate[\"A1\"], vols=[200])\n", + "time.sleep(1)\n", + "\n", + "await lh.dispense(tube_rack[\"A1\"], vols=[200])\n", + "time.sleep(1)\n", + "\n", + "await lh.return_tips()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Decks/pylabrobot-20240514.log b/Decks/pylabrobot-20240514.log new file mode 100644 index 0000000..e69de29 diff --git a/Decks/pylabrobot-20240515.log b/Decks/pylabrobot-20240515.log new file mode 100644 index 0000000..e69de29 diff --git a/Decks/pylabrobot-20240516.log b/Decks/pylabrobot-20240516.log new file mode 100644 index 0000000..e69de29 diff --git a/Decks/pylabrobot-20240517.log b/Decks/pylabrobot-20240517.log new file mode 100644 index 0000000..e69de29 diff --git a/Equipment/Introduction_to_Equipment.ipynb b/Equipment/Introduction_to_Equipment.ipynb index 61a4782..1c6ae91 100644 --- a/Equipment/Introduction_to_Equipment.ipynb +++ b/Equipment/Introduction_to_Equipment.ipynb @@ -1,144 +1,144 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", - "metadata": {}, - "source": [ - "**Welcome to PyLabRobot!**\n", - "\n", - "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", - "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", - "\n", - "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", - "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", - "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", - "be translatable across many different machines.\n", - "\n", - "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", - "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", - "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", - "the most widely used liquid handling robots." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8b372972-d6dc-454b-95eb-d9941405621b", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", - "await lh.setup()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0324171c-101d-4b3d-9103-a0137c620174", - "metadata": {}, - "outputs": [], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", - "metadata": {}, - "source": [ - "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cos_96_DW_1mL,\n", - " HTF_L\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", - "lh.deck.assign_child_resource(tip_car, rails=15)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", - "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", - "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", - "lh.deck.assign_child_resource(plt_car, rails=8)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} +{ + "cells": [ + { + "cell_type": "markdown", + "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", + "metadata": {}, + "source": [ + "**Welcome to PyLabRobot!**\n", + "\n", + "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", + "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", + "\n", + "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", + "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", + "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", + "be translatable across many different machines.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", + "the most widely used liquid handling robots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.hamilton import STARLetDeck\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b372972-d6dc-454b-95eb-d9941405621b", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "await lh.setup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0324171c-101d-4b3d-9103-a0137c620174", + "metadata": {}, + "outputs": [], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", + "metadata": {}, + "source": [ + "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cos_96_DW_1mL,\n", + " HTF_L\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", + "lh.deck.assign_child_resource(tip_car, rails=15)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", + "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", + "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", + "lh.deck.assign_child_resource(plt_car, rails=8)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Introduction_to_PyLabRobot.ipynb b/Introduction_to_PyLabRobot.ipynb index 53e920e..2aecb70 100644 --- a/Introduction_to_PyLabRobot.ipynb +++ b/Introduction_to_PyLabRobot.ipynb @@ -5,12 +5,12 @@ "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", "metadata": {}, "source": [ - "**Welcome to PyLabRobot!**\n", + "# **Welcome to PyLabRobot!**\n", "\n", - "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", + "PyLabRobot *(PLR)* is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", "\n", - "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", + "PLR defines a universal interface class called **LiquidHandler** that provides generic methods for controlling robots\n", "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", "be translatable across many different machines.\n", @@ -21,9 +21,17 @@ "the most widely used liquid handling robots." ] }, + { + "cell_type": "markdown", + "id": "9268a564-0e57-4ace-9b22-68fcffbfcc2f", + "metadata": {}, + "source": [ + "### Imports" + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", "metadata": {}, "outputs": [], @@ -31,31 +39,126 @@ "from pylabrobot.liquid_handling import LiquidHandler\n", "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n" + "from pylabrobot.resources.hamilton import STARLetDeck\n", + "from pylabrobot.resources.opentrons import OTDeck\n", + "\n", + "from pylabrobot.resources.opentrons.load import *\n", + "from pylabrobot.resources.opentrons.plates import *\n", + "\n", + "import opentrons" + ] + }, + { + "cell_type": "markdown", + "id": "3c611cf0-ac3f-4e4d-8f48-b9d2241f5670", + "metadata": {}, + "source": [ + "### Liquid Handler: High level abstraction, thing that takes in aspirate and dispense commands, abstract wrapper for all robot backends\n", + "Give input backend, what robot you are planning to use, PLR is hardware agnostic, you use the abstract liquid handler scaffold and then pass in \n", + "the details of your specific robot\n", + "### Deck: What deck is your robot using, again it is hardware agnostic.\n", + "\n", + "* Restart kernel is your friend when working with PLR" + ] + }, + { + "cell_type": "markdown", + "id": "213a4b8c-098e-4e0e-bb9f-446ba5b1709a", + "metadata": {}, + "source": [ + "## Deck Set-Up" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "8b372972-d6dc-454b-95eb-d9941405621b", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Setting up the robot.\n", + "Resource deck was assigned to the robot.\n", + "Resource trash_container was assigned to the robot.\n" + ] + } + ], "source": [ - "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "# Call setup to instantiate your specific instance of the robot\n", + "\n", + "# Hamilton\n", + "#lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "\n", + "# OpenTrons\n", + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=OTDeck())\n", + "\n", + "# Use await because setup can take many minutes. Use await when you have a long function call that you\n", + "# want to be able to do other things while its happening\n", "await lh.setup()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "0324171c-101d-4b3d-9103-a0137c620174", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Websocket server started at http://127.0.0.1:2121\n", + "File server started at http://127.0.0.1:1337 . Open this URL in your browser.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "----------------------------------------\n", + "Exception occurred during processing of request from ('127.0.0.1', 40734)\n", + "Traceback (most recent call last):\n", + " File \"/usr/lib/python3.10/socketserver.py\", line 316, in _handle_request_noblock\n", + " self.process_request(request, client_address)\n", + " File \"/usr/lib/python3.10/socketserver.py\", line 347, in process_request\n", + " self.finish_request(request, client_address)\n", + " File \"/usr/lib/python3.10/socketserver.py\", line 360, in finish_request\n", + " self.RequestHandlerClass(request, client_address, self)\n", + " File \"/mnt/d/Chory Lab/PyLabRobot/pylabrobot/visualizer/visualizer.py\", line 322, in __init__\n", + " super().__init__(*args, directory=path, **kwargs)\n", + " File \"/usr/lib/python3.10/http/server.py\", line 651, in __init__\n", + " super().__init__(*args, **kwargs)\n", + " File \"/usr/lib/python3.10/socketserver.py\", line 747, in __init__\n", + " self.handle()\n", + " File \"/usr/lib/python3.10/http/server.py\", line 425, in handle\n", + " self.handle_one_request()\n", + " File \"/usr/lib/python3.10/http/server.py\", line 413, in handle_one_request\n", + " method()\n", + " File \"/mnt/d/Chory Lab/PyLabRobot/pylabrobot/visualizer/visualizer.py\", line 343, in do_GET\n", + " self.wfile.write(content.encode(\"utf-8\"))\n", + " File \"/usr/lib/python3.10/socketserver.py\", line 826, in write\n", + " self._sock.sendall(b)\n", + "BrokenPipeError: [Errno 32] Broken pipe\n", + "----------------------------------------\n" + ] + } + ], "source": [ + "# If you want to follow along in real time, instantiate the protocol visualizer\n", "vis = Visualizer(resource=lh)\n", "await vis.setup()" ] }, + { + "cell_type": "markdown", + "id": "42bf486e-b8cb-4918-a2e6-a9955e71c46b", + "metadata": {}, + "source": [ + "### Put some labware on the deck!" + ] + }, { "cell_type": "markdown", "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", @@ -66,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", "metadata": {}, "outputs": [], @@ -75,17 +178,40 @@ " TIP_CAR_480_A00,\n", " PLT_CAR_L5AC_A00,\n", " Cos_96_DW_1mL,\n", - " HTF_L\n", + " HTF_L,\n", + " opentrons_96_tiprack_300ul,\n", + " opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap\n", ")\n" ] }, + { + "cell_type": "markdown", + "id": "8cf826da-7d57-49ce-8303-d40501f50e2b", + "metadata": {}, + "source": [ + "### Hamilton Tip Racks" + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "TypeError", + "evalue": "OTDeck.assign_child_resource() got an unexpected keyword argument 'rails'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_582/3930452725.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mtip_car\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtip_rack4\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mHTF_L\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'tips_04'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwith_tips\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mtip_car\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtip_rack5\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mHTF_L\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'tips_05'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwith_tips\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mlh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdeck\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0massign_child_resource\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtip_car\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrails\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m15\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: OTDeck.assign_child_resource() got an unexpected keyword argument 'rails'" + ] + } + ], "source": [ + "# Hamilton Tips\n", "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", @@ -97,10 +223,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", - "metadata": {}, - "outputs": [], + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Resource with name 'plate carrier' already defined.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_200/3931165138.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mplt_car\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplate_2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mCos_96_DW_1mL\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'plate_02'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mplt_car\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplate_3\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mCos_96_DW_1mL\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'plate_03'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mlh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdeck\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0massign_child_resource\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mplt_car\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrails\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m8\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/mnt/d/Chory Lab/PyLabRobot/pylabrobot/resources/hamilton/hamilton_decks.py\u001b[0m in \u001b[0;36massign_child_resource\u001b[0;34m(self, resource, location, reassign, rails, replace)\u001b[0m\n\u001b[1;32m 113\u001b[0m \u001b[0mcast\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mResource\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_resource\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresource\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munassign\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 114\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 115\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Resource with name '{resource.name}' already defined.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 116\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrails\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: Resource with name 'plate carrier' already defined." + ] + } + ], "source": [ "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", @@ -111,17 +252,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "4421b04c-d894-4aaa-a9f4-7f51a16dc773", "metadata": {}, "outputs": [], "source": [ - "tip_rack1.fill()\n" + "tip_rack_1.fill()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "74928e32-3910-434c-b0af-5bdc9c03a51e", "metadata": {}, "outputs": [], @@ -132,130 +273,651 @@ "tip_rack2.set_tip_state([[True, True, False, False]*3]*8)\n" ] }, + { + "cell_type": "markdown", + "id": "2d2fba32-6be8-4248-88b2-ab74fb2047af", + "metadata": {}, + "source": [ + "### OpenTrons Tip Racks" + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, + "id": "28b14d77-7cc3-4721-9f10-0867d9137a38", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource tip_rack_1 was assigned to the robot.\n", + "Resource tip_rack_2 was assigned to the robot.\n" + ] + } + ], + "source": [ + "# OT2 Tips\n", + "\n", + "tip_rack_1 = opentrons_96_tiprack_300ul(\"tip_rack_1\")\n", + "tip_rack_2 = opentrons_96_tiprack_300ul(\"tip_rack_2\")\n", + "\n", + "lh.deck.assign_child_at_slot(tip_rack_1, 7)\n", + "lh.deck.assign_child_at_slot(tip_rack_2, 8)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7d16498c-5b03-4454-a197-dc1faf8a3a6f", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack2 = lh.deck.get_resource(\"tip_rack_2\")\n", + "tip_rack2.set_tip_state([[True]*6 + [False]*6]*8)" + ] + }, + { + "cell_type": "markdown", + "id": "a06b3c22-5498-43dd-8e1e-ae0203c24f23", + "metadata": {}, + "source": [ + "### OpenTrons Plates" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "16547556-5fd6-4af8-a37b-dd6a1d209981", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource plate_1 was assigned to the robot.\n", + "Resource plate_2 was assigned to the robot.\n" + ] + } + ], + "source": [ + "plate_1 = corning_96_wellplate_360ul_flat('plate_1')\n", + "plate_2 = corning_96_wellplate_360ul_flat('plate_2')\n", + "\n", + "lh.deck.assign_child_at_slot(plate_1, 1)\n", + "lh.deck.assign_child_at_slot(plate_2, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 101, "id": "534179d6-96aa-419f-832e-dbf6b19de0fa", "metadata": {}, "outputs": [], "source": [ - "plate_1_liquids = [[(None, 500)]]*96\n", + "plate_1_liquids = [[('water', 200)]]*96\n", "plate_1.set_well_liquids(plate_1_liquids)\n", - "plate_2_liquids = [[(None, 100)], [(None, 500)]]*(96//2)\n", + "plate_2_liquids = [[(None, 100)], [(None, 100)]]*(96//2)\n", "plate_2.set_well_liquids(plate_2_liquids)\n" ] }, + { + "cell_type": "markdown", + "id": "fa620baf-955e-4671-b86c-8288545c599b", + "metadata": {}, + "source": [ + "### OpenTrons Tube Racks" + ] + }, { "cell_type": "code", - "execution_count": null, - "id": "21e6aa85-cbe1-4c8e-bbe7-1e33eaccdc20", + "execution_count": 17, + "id": "aa3468cd-f660-4c2b-8568-ed3160172174", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource rack_1 was assigned to the robot.\n", + "Resource rack_2 was assigned to the robot.\n" + ] + } + ], + "source": [ + "rack_1 = opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap('rack_1')\n", + "rack_2 = opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap('rack_2')\n", + "\n", + "lh.deck.assign_child_at_slot(rack_1, 4)\n", + "lh.deck.assign_child_at_slot(rack_2, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "b65570bc-1589-43ec-9aa1-ece9c50187cd", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'TubeRack' object has no attribute 'set_liquids'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_962/3721324440.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mrack_1_liquids\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'water'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m200\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mrack_1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_liquids\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mplate_1_liquids\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: 'TubeRack' object has no attribute 'set_liquids'" + ] + } + ], + "source": [ + "rack_1_liquids = [[('water', 200)]]*6\n", + "rack_1.set_liquids(plate_1_liquids)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "id": "02e18147-5337-4c75-9fd4-4243499d3108", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[Tube(name=rack_1_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_B1, location=(018.210, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_C1, location=(018.210, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_D1, location=(018.210, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_A2, location=(038.100, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_B2, location=(038.100, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_C2, location=(038.100, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_D2, location=(038.100, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_A3, location=(057.990, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_B3, location=(057.990, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_C3, location=(057.990, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_D3, location=(057.990, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_A4, location=(077.880, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_B4, location=(077.880, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_C4, location=(077.880, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_D4, location=(077.880, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_A5, location=(097.770, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_B5, location=(097.770, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_C5, location=(097.770, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_D5, location=(097.770, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_A6, location=(117.660, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_B6, location=(117.660, 056.150, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_C6, location=(117.660, 036.870, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube),\n", + " Tube(name=rack_1_D6, location=(117.660, 017.590, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube)]" + ] + }, + "execution_count": 144, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rack_1.get_all_children()" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "id": "23cb1bc9-cb4f-4d70-b0f7-7727a2586561", "metadata": {}, "outputs": [], "source": [ - "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", - "set_tip_tracking(True), set_volume_tracking(True)\n" + "rack_1.enable_volume_trackers()" ] }, { "cell_type": "code", - "execution_count": null, - "id": "7eda3c13-e750-4823-8327-5e5baba6ac2d", + "execution_count": 125, + "id": "a467ab96-e1cb-4932-9e47-731831f29148", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['__class__',\n", + " '__delattr__',\n", + " '__dict__',\n", + " '__dir__',\n", + " '__doc__',\n", + " '__eq__',\n", + " '__format__',\n", + " '__ge__',\n", + " '__getattribute__',\n", + " '__gt__',\n", + " '__hash__',\n", + " '__init__',\n", + " '__init_subclass__',\n", + " '__le__',\n", + " '__lt__',\n", + " '__module__',\n", + " '__ne__',\n", + " '__new__',\n", + " '__reduce__',\n", + " '__reduce_ex__',\n", + " '__repr__',\n", + " '__setattr__',\n", + " '__sizeof__',\n", + " '__str__',\n", + " '__subclasshook__',\n", + " '__weakref__',\n", + " '_call_did_assign_resource_callbacks',\n", + " '_call_did_unassign_resource_callbacks',\n", + " '_call_will_assign_resource_callbacks',\n", + " '_call_will_unassign_resource_callbacks',\n", + " '_check_assignment',\n", + " '_did_assign_resource_callbacks',\n", + " '_did_unassign_resource_callbacks',\n", + " '_name',\n", + " '_resource_state_updated_callbacks',\n", + " '_size_x',\n", + " '_size_y',\n", + " '_size_z',\n", + " '_state_updated',\n", + " '_will_assign_resource_callbacks',\n", + " '_will_unassign_resource_callbacks',\n", + " 'assign_child_resource',\n", + " 'category',\n", + " 'center',\n", + " 'centers',\n", + " 'children',\n", + " 'copy',\n", + " 'deregister_did_assign_resource_callback',\n", + " 'deregister_did_unassign_resource_callback',\n", + " 'deregister_state_update_callback',\n", + " 'deregister_will_assign_resource_callback',\n", + " 'deregister_will_unassign_resource_callback',\n", + " 'deserialize',\n", + " 'get_absolute_location',\n", + " 'get_all_children',\n", + " 'get_resource',\n", + " 'get_size_x',\n", + " 'get_size_y',\n", + " 'get_size_z',\n", + " 'load_all_state',\n", + " 'load_from_json_file',\n", + " 'load_state',\n", + " 'load_state_from_file',\n", + " 'location',\n", + " 'max_volume',\n", + " 'model',\n", + " 'name',\n", + " 'parent',\n", + " 'register_did_assign_resource_callback',\n", + " 'register_did_unassign_resource_callback',\n", + " 'register_state_update_callback',\n", + " 'register_will_assign_resource_callback',\n", + " 'register_will_unassign_resource_callback',\n", + " 'rotate',\n", + " 'rotated',\n", + " 'rotation',\n", + " 'save',\n", + " 'save_state_to_file',\n", + " 'serialize',\n", + " 'serialize_all_state',\n", + " 'serialize_state',\n", + " 'tracker',\n", + " 'unassign',\n", + " 'unassign_child_resource']" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(rack_1['A1'][0])" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "4354a042-96d4-4e93-916e-1cc2a25f2c1e", "metadata": {}, "outputs": [], "source": [ - "await lh.pick_up_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])\n", - "await lh.drop_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])\n" + "tube1 = rack_1[0][0]" ] }, { "cell_type": "code", - "execution_count": null, - "id": "8ca125a2-0fbd-4b1d-850b-29078dc48d9a", + "execution_count": 138, + "id": "8e5166dd-23a4-4d5b-b869-5fd4ca167f77", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "tube1.tracker.set_liquids([(None, 0)])" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "id": "0a1fd4b0-7089-43be-bfde-de3b43d2b44b", "metadata": {}, "outputs": [], "source": [ - "await lh.pick_up_tips96(tip_rack1)\n" + "tube1.tracker.add_liquid(None, 2000)" ] }, { "cell_type": "code", - "execution_count": null, - "id": "464f5dd7-8d85-407b-b4d6-693340f90c5e", + "execution_count": 141, + "id": "cdbb78b9-39d4-4036-924a-a358d8c8c9a8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(None, 2000)]" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tube1.tracker.liquids" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "id": "bebe9703-f00c-4975-9620-5eb3d0416001", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_1_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n", + "Aspirating [Aspiration(resource=Tube(name=rack_1_A1, location=(018.210, 075.430, 041.270), size_x=6.223, size_y=6.223, size_z=39.1, category=tube), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Water', 100)])].\n", + "Dispensing [Dispense(resource=Well(name=plate_2_A1, location=(014.380, 074.240, 003.550), size_x=4.851, size_y=4.851, size_z=10.67, category=well), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('Water', 100)])].\n", + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_1_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack_1[\"A1\"])\n", + "time.sleep(1)\n", + "\n", + "await lh.aspirate(rack_1[\"A1\"], vols=[100])\n", + "time.sleep(1)\n", + "\n", + "await lh.dispense(plate_2[\"A1\"], vols=[100])\n", + "time.sleep(1)\n", + "\n", + "await lh.return_tips()" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "62fe14fa-2c25-413a-a982-db510f6d8c0b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_1_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.return_tips()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "ace711fd-1823-4056-b408-7f2f24be5338", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'list' object has no attribute 'add_liquid'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_962/68121547.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mrack_1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_liquid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'water'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m200\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: 'list' object has no attribute 'add_liquid'" + ] + } + ], + "source": [ + "rack_1.get[0].add_liquid([('water', 200)])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "21e6aa85-cbe1-4c8e-bbe7-1e33eaccdc20", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(None, None)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", + "set_tip_tracking(True), set_volume_tracking(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8055306d-042d-4d83-a685-139a221d8665", "metadata": {}, "outputs": [], "source": [ - "await lh.pick_up_tips(tip_rack1[\"A1\"])\n" + "import time" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, + "id": "7eda3c13-e750-4823-8327-5e5baba6ac2d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_1_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_1_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_1_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Pickup(resource=TipSpot(name=tip_rack_1_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n", + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_1_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_1_B2, location=(023.380, 065.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_1_C3, location=(032.380, 056.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47)), Drop(resource=TipSpot(name=tip_rack_1_D4, location=(041.380, 047.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack_1[\"A1\", \"B2\", \"C3\", \"D4\"])\n", + "time.sleep(2)\n", + "await lh.drop_tips(tip_rack_1[\"A1\", \"B2\", \"C3\", \"D4\"])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "464f5dd7-8d85-407b-b4d6-693340f90c5e", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tip_rack_1_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack_1[\"A1\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, "id": "1e4c9bb2-58e9-4c4d-a9d4-fa65db824f20", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating [Aspiration(resource=Well(name=plate_1_A2, location=(023.380, 074.240, 003.550), size_x=4.851, size_y=4.851, size_z=10.67, category=well), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('water', 100)])].\n" + ] + } + ], "source": [ - "await lh.aspirate(plate_1[\"A2\"], vols=[300])\n" + "await lh.aspirate(plate_1[\"A2\"], vols=[100])" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "ce90182d-607b-4e93-bc9a-1d7dfef80e66", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dispensing [Dispense(resource=Well(name=plate_2_A1, location=(014.380, 074.240, 003.550), size_x=4.851, size_y=4.851, size_z=10.67, category=well), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47), volume=100, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[('water', 100)])].\n" + ] + } + ], "source": [ - "await lh.dispense(plate_2[\"A1\"], vols=[300])\n" + "await lh.dispense(plate_2[\"A1\"], vols=[100])" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "711f3290-e36d-4ae9-88a3-80374709cc36", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tip_rack_1_A1, location=(014.380, 074.240, 005.390), size_x=3.698, size_y=3.698, size_z=0, category=tip_spot), offset=None, tip=Tip(has_filter=False, total_tip_length=59.3, maximal_volume=300.0, fitting_depth=7.47))].\n" + ] + } + ], "source": [ - "await lh.return_tips()\n" + "await lh.return_tips()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "eb52fd76-d92a-43b3-8550-e688eed4c587", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips from tip_rack_1.\n" + ] + } + ], "source": [ - "await lh.pick_up_tips96(tip_rack1)\n" + "await lh.pick_up_tips96(tip_rack_1)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, + "id": "c9cbce02-0f7e-4e51-94e7-2635002d757b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating 100 from Plate(name=plate_1, size_x=127.76, size_y=85.47, size_z=14.22, location=(000.000, 000.000, 000.000)).\n" + ] + } + ], + "source": [ + "await lh.aspirate96(plate_1, volume=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, "id": "ca18de2b-92f4-43c8-a0e7-dab9aa51ec38", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dispensing 100 to Plate(name=plate_2, size_x=127.76, size_y=85.47, size_z=14.22, location=(132.500, 000.000, 000.000)).\n" + ] + } + ], "source": [ - "await lh.dispense96(plate_3, volume=100)\n" + "await lh.dispense96(plate_2, volume=100)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "id": "12ef3847-c203-4332-84bd-6631dd90aea5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips to tip_rack_1.\n" + ] + } + ], "source": [ - "await lh.drop_tips96(tip_rack1)\n" + "await lh.drop_tips96(tip_rack_1)\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", "metadata": {}, "outputs": [], "source": [ "await vis.stop()\n" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8525dc3-7866-4b4d-b870-8c64313bba28", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -274,7 +936,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/Protocols/Introduction_to_Protocols.ipynb b/Protocols/Introduction_to_Protocols.ipynb index 61a4782..1c6ae91 100644 --- a/Protocols/Introduction_to_Protocols.ipynb +++ b/Protocols/Introduction_to_Protocols.ipynb @@ -1,144 +1,144 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", - "metadata": {}, - "source": [ - "**Welcome to PyLabRobot!**\n", - "\n", - "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", - "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", - "\n", - "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", - "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", - "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", - "be translatable across many different machines.\n", - "\n", - "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", - "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", - "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", - "the most widely used liquid handling robots." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8b372972-d6dc-454b-95eb-d9941405621b", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", - "await lh.setup()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0324171c-101d-4b3d-9103-a0137c620174", - "metadata": {}, - "outputs": [], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", - "metadata": {}, - "source": [ - "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cos_96_DW_1mL,\n", - " HTF_L\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", - "lh.deck.assign_child_resource(tip_car, rails=15)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", - "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", - "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", - "lh.deck.assign_child_resource(plt_car, rails=8)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} +{ + "cells": [ + { + "cell_type": "markdown", + "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", + "metadata": {}, + "source": [ + "**Welcome to PyLabRobot!**\n", + "\n", + "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", + "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", + "\n", + "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", + "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", + "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", + "be translatable across many different machines.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", + "the most widely used liquid handling robots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.hamilton import STARLetDeck\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b372972-d6dc-454b-95eb-d9941405621b", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "await lh.setup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0324171c-101d-4b3d-9103-a0137c620174", + "metadata": {}, + "outputs": [], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", + "metadata": {}, + "source": [ + "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cos_96_DW_1mL,\n", + " HTF_L\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", + "lh.deck.assign_child_resource(tip_car, rails=15)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", + "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", + "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", + "lh.deck.assign_child_resource(plt_car, rails=8)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/README.md b/README.md index 64c35fa..0fe2212 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,36 @@ -# PyLabRobot Tutorials BME590 - -## Installation - -[Install Python](https://www.python.org/downloads/release/python-3110/)
-[Install Git](https://git-scm.com/downloads)
- - -From the terminal:
-`git clone https://github.com/PyLabRobot/pylabrobot.git`
-`cd pylabrobot`
-`pip install -e .[extras_visualizer]`
-`pip install libusb_package`
-`pip install websockets`
-`pip install jupyterlab`
-`git clone https://github.com/stefangolas/PyLabRobot_Tutorials_BME590.git`
-`cd PyLabRobot_Tutorials_BME590`
-`jupyter lab`
- -### Installation notes - -* It's possible you already have a version of some of the dependencies that get installed alongside. If you are worried about conflicts with existing packages, you can use a virtual environment. [How to Use Python Virtual Environments](https://realpython.com/python-virtual-environments-a-primer/). - -* You can use standard Jupyter Notebooks, but Jupyter Lab has a file system feature that is very convenient for navigating the repository. - -* If you get an error that says `pip not recognized` on Windows, you just have to add your Python installation path to your environment variables. [Stack Overflow: pip is not recognized as an internal or external command](https://stackoverflow.com/questions/23708898/pip-is-not-recognized-as-an-internal-or-external-command). - - -This will install PyLabRobot in such a way that if you change the source code you just cloned, those changes will be reflected in your working version of the library when you import it into Python. This means that you can break your own installation of PLR, but you can always re-clone from the main repo to start over. - -## Notebook - The last command should have opened the Jupyter Lab notebook shown below. This is an example script that gives an overview of PyLabRobot's capabilities. - There are other scripts that delve into specific topics such as `DataSimulations` that you can access from the same directory. Feel free to change the scripts - to get a sense of what PyLabRobot can do. - - ![image](Readme_Images/screenshot.png) +# PyLabRobot Tutorials BME590 + +## Installation + +[Install Python](https://www.python.org/downloads/release/python-3110/)
+[Install Git](https://git-scm.com/downloads)
+ + +From the terminal:
+`git clone https://github.com/PyLabRobot/pylabrobot.git`
+`cd pylabrobot`
+`pip install -e .[extras_visualizer]`
+`pip install libusb_package`
+`pip install websockets`
+`pip install jupyterlab`
+`git clone https://github.com/stefangolas/PyLabRobot_Tutorials_BME590.git`
+`cd PyLabRobot_Tutorials_BME590`
+`jupyter lab`
+ +### Installation notes + +* It's possible you already have a version of some of the dependencies that get installed alongside. If you are worried about conflicts with existing packages, you can use a virtual environment. [How to Use Python Virtual Environments](https://realpython.com/python-virtual-environments-a-primer/). + +* You can use standard Jupyter Notebooks, but Jupyter Lab has a file system feature that is very convenient for navigating the repository. + +* If you get an error that says `pip not recognized` on Windows, you just have to add your Python installation path to your environment variables. [Stack Overflow: pip is not recognized as an internal or external command](https://stackoverflow.com/questions/23708898/pip-is-not-recognized-as-an-internal-or-external-command). + + +This will install PyLabRobot in such a way that if you change the source code you just cloned, those changes will be reflected in your working version of the library when you import it into Python. This means that you can break your own installation of PLR, but you can always re-clone from the main repo to start over. + +## Notebook + The last command should have opened the Jupyter Lab notebook shown below. This is an example script that gives an overview of PyLabRobot's capabilities. + There are other scripts that delve into specific topics such as `DataSimulations` that you can access from the same directory. Feel free to change the scripts + to get a sense of what PyLabRobot can do. + + ![image](Readme_Images/screenshot.png) diff --git a/SystemsIntegrations/Introduction_to_SystemsIntegrations.ipynb b/SystemsIntegrations/Introduction_to_SystemsIntegrations.ipynb index 61a4782..1c6ae91 100644 --- a/SystemsIntegrations/Introduction_to_SystemsIntegrations.ipynb +++ b/SystemsIntegrations/Introduction_to_SystemsIntegrations.ipynb @@ -1,144 +1,144 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", - "metadata": {}, - "source": [ - "**Welcome to PyLabRobot!**\n", - "\n", - "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", - "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", - "\n", - "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", - "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", - "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", - "be translatable across many different machines.\n", - "\n", - "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", - "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", - "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", - "the most widely used liquid handling robots." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8b372972-d6dc-454b-95eb-d9941405621b", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", - "await lh.setup()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0324171c-101d-4b3d-9103-a0137c620174", - "metadata": {}, - "outputs": [], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", - "metadata": {}, - "source": [ - "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cos_96_DW_1mL,\n", - " HTF_L\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", - "lh.deck.assign_child_resource(tip_car, rails=15)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", - "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", - "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", - "lh.deck.assign_child_resource(plt_car, rails=8)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} +{ + "cells": [ + { + "cell_type": "markdown", + "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", + "metadata": {}, + "source": [ + "**Welcome to PyLabRobot!**\n", + "\n", + "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", + "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", + "\n", + "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", + "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", + "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", + "be translatable across many different machines.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", + "the most widely used liquid handling robots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.hamilton import STARLetDeck\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b372972-d6dc-454b-95eb-d9941405621b", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "await lh.setup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0324171c-101d-4b3d-9103-a0137c620174", + "metadata": {}, + "outputs": [], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", + "metadata": {}, + "source": [ + "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cos_96_DW_1mL,\n", + " HTF_L\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", + "lh.deck.assign_child_resource(tip_car, rails=15)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", + "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", + "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", + "lh.deck.assign_child_resource(plt_car, rails=8)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Worklists/Introduction_to_Worklists.ipynb b/Worklists/Introduction_to_Worklists.ipynb index 61a4782..1c6ae91 100644 --- a/Worklists/Introduction_to_Worklists.ipynb +++ b/Worklists/Introduction_to_Worklists.ipynb @@ -1,144 +1,144 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", - "metadata": {}, - "source": [ - "**Welcome to PyLabRobot!**\n", - "\n", - "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", - "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", - "\n", - "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", - "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", - "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", - "be translatable across many different machines.\n", - "\n", - "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", - "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", - "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", - "the most widely used liquid handling robots." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8b372972-d6dc-454b-95eb-d9941405621b", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", - "await lh.setup()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0324171c-101d-4b3d-9103-a0137c620174", - "metadata": {}, - "outputs": [], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", - "metadata": {}, - "source": [ - "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cos_96_DW_1mL,\n", - " HTF_L\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", - "lh.deck.assign_child_resource(tip_car, rails=15)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", - "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", - "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", - "lh.deck.assign_child_resource(plt_car, rails=8)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} +{ + "cells": [ + { + "cell_type": "markdown", + "id": "baf9fb61-2fc5-446c-9c4a-18801c7d7afa", + "metadata": {}, + "source": [ + "**Welcome to PyLabRobot!**\n", + "\n", + "PyLabRobot is a universal Python interface to liquid-handling robots. Liquid-handling robots aspirate and dispense precise volumes\n", + "of liquid in a Cartesian coordinate system, essentially the same as hand pipetting.\n", + "\n", + "PLR defines a universal interface class called LiquidHandler that provides generic methods for controlling robots\n", + "such as aspirate and dispense. This class can be instantiated with one of several backends (or drivers)\n", + "that convert generic commands to machine-specific commands. This makes it easy to write code that will\n", + "be translatable across many different machines.\n", + "\n", + "First we will import `LiquidHandler`, a backend called `ChatterBoxBackend` that prints the text\n", + "output of our commands, a class `Visualizer` that provides a visualization of the robot deck as we\n", + "run commands, and a class `STARLetDeck` that will represent the deck of a Hamilton Microlab STARLet, one of\n", + "the most widely used liquid handling robots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8bcf094-3c3a-41c4-b0e8-5d2caeee9ca9", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterBoxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.resources.hamilton import STARLetDeck\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b372972-d6dc-454b-95eb-d9941405621b", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterBoxBackend(), deck=STARLetDeck())\n", + "await lh.setup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0324171c-101d-4b3d-9103-a0137c620174", + "metadata": {}, + "outputs": [], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "59b03cfb-8e42-441e-9887-e7a87453d7f7", + "metadata": {}, + "source": [ + "Now we will import a tip carrier, a plate carrier, a plate, and a tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bb36402-2303-4d2e-bd3e-4e10a29e891c", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cos_96_DW_1mL,\n", + " HTF_L\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25faa001-8f09-43af-ac2a-5b6b6b21d47a", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)\n", + "lh.deck.assign_child_resource(tip_car, rails=15)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e7c1218-70e9-4905-b15a-16af3259c79f", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cos_96_DW_1mL(name='plate_01')\n", + "plt_car[1] = plate_2 = Cos_96_DW_1mL(name='plate_02')\n", + "plt_car[2] = plate_3 = Cos_96_DW_1mL(name='plate_03')\n", + "lh.deck.assign_child_resource(plt_car, rails=8)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08fb2161-4fc7-4312-9864-e85a46f02bcc", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pylabrobot-20240513.log b/pylabrobot-20240513.log new file mode 100644 index 0000000..e69de29 diff --git a/pylabrobot-20240514.log b/pylabrobot-20240514.log new file mode 100644 index 0000000..e69de29