From fe549e2875fc8e58ea9011c470308b44101990a9 Mon Sep 17 00:00:00 2001 From: fly1d <309400591+fly1d@users.noreply.github.com> Date: Fri, 11 Sep 2026 13:13:38 +0800 Subject: [PATCH] Add Trash resource guide Document Trash creation, deck integration, simulated tip disposal, and cleanup behavior in an executable notebook. Assisted-by: OpenAI Codex --- docs/resources/container/container.rst | 3 +- docs/resources/container/trash/trash.ipynb | 174 +++++++++++++++++++++ docs/resources/index.md | 1 + 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 docs/resources/container/trash/trash.ipynb diff --git a/docs/resources/container/container.rst b/docs/resources/container/container.rst index 4d92a816335..4df46280f02 100644 --- a/docs/resources/container/container.rst +++ b/docs/resources/container/container.rst @@ -1,7 +1,7 @@ Container ========= -Resources that contain liquid are subclasses of :class:`~pylabrobot.resources.container.Container`. This class provides a :class:`~pylabrobot.resources.volume_tracker.VolumeTracker` that helps :class:`~pylabrobot.legacy.liquid_handling.liquid_handler.LiquidHandler` keep track of the liquid in the resource. (For more information on trackers, check out :doc:`/user_guide/machine-agnostic-features/using-trackers`). Examples of subclasses of `Container` are :class:`~pylabrobot.resources.Well` and :class:`~pylabrobot.resources.trough.Trough`. +Resources that contain liquid are subclasses of :class:`~pylabrobot.resources.container.Container`. This class provides a :class:`~pylabrobot.resources.volume_tracker.VolumeTracker` that helps :class:`~pylabrobot.legacy.liquid_handling.liquid_handler.LiquidHandler` keep track of the liquid in the resource. (For more information on trackers, check out :doc:`/user_guide/machine-agnostic-features/using-trackers`). Examples of subclasses of `Container` are :class:`~pylabrobot.resources.Well`, :class:`~pylabrobot.resources.trough.Trough`, and :class:`~pylabrobot.resources.trash.Trash`. It is possible to instantiate a `Container` directly: @@ -20,6 +20,7 @@ It is possible to instantiate a `Container` directly: :hidden: petri-dish/petri-dish + trash/trash trough/trough tube/tube well/well diff --git a/docs/resources/container/trash/trash.ipynb b/docs/resources/container/trash/trash.ipynb new file mode 100644 index 00000000000..8e52dc74e66 --- /dev/null +++ b/docs/resources/container/trash/trash.ipynb @@ -0,0 +1,174 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "trash-intro", + "metadata": {}, + "source": [ + "# Trash\n", + "\n", + "A {class}`~pylabrobot.resources.trash.Trash` represents a disposal area on a liquid-handler\n", + "deck. It inherits the dimensions and positioning behavior of\n", + "{class}`~pylabrobot.resources.container.Container`, while liquid-handling operations treat it\n", + "as a terminal destination: discarded tips and resources are removed from tracked use rather\n", + "than assigned as children of the trash." + ] + }, + { + "cell_type": "markdown", + "id": "create-trash", + "metadata": {}, + "source": [ + "## Creating a trash area\n", + "\n", + "Create a `Trash` with the physical dimensions of the disposal opening. PyLabRobot's default\n", + "length unit is millimeters:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "create-example", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import Coordinate, Trash\n", + "\n", + "trash = Trash(\n", + " name=\"trash\",\n", + " size_x=120.0,\n", + " size_y=80.0,\n", + " size_z=50.0,\n", + ")\n", + "\n", + "assert trash.category == \"trash\"\n", + "assert trash.get_anchor(x=\"center\", y=\"center\", z=\"top\") == Coordinate(60, 40, 50)" + ] + }, + { + "cell_type": "markdown", + "id": "assign-trash", + "metadata": {}, + "source": [ + "## Adding trash to a deck\n", + "\n", + "Many vendor-specific decks include their trash area automatically. For a custom\n", + "{class}`~pylabrobot.resources.deck.Deck`, assign the resource at its measured deck location.\n", + "The standard trash must be named `trash`, because\n", + "{meth}`~pylabrobot.resources.Deck.get_trash_area` uses that name to find it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "assign-example", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import Deck\n", + "\n", + "deck = Deck(name=\"deck\", size_x=600.0, size_y=400.0, size_z=100.0)\n", + "trash_location = Coordinate(470.0, 10.0, 0.0)\n", + "deck.assign_child_resource(trash, location=trash_location)\n", + "\n", + "assert deck.get_trash_area() is trash\n", + "assert trash.get_location_wrt(deck) == trash_location" + ] + }, + { + "cell_type": "markdown", + "id": "discarding", + "metadata": {}, + "source": [ + "## Discarding tips\n", + "\n", + "`LiquidHandler.discard_tips()` uses the deck's standard trash as the drop destination. The\n", + "following example uses the Chatterbox simulation backend, which prints commands without\n", + "connecting to hardware:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "discard-example", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.legacy.liquid_handling import LiquidHandler\n", + "from pylabrobot.legacy.liquid_handling.backends import LiquidHandlerChatterboxBackend\n", + "from pylabrobot.resources import hamilton_96_tiprack_300uL_filter, set_tip_tracking\n", + "\n", + "set_tip_tracking(enabled=True)\n", + "tip_rack = hamilton_96_tiprack_300uL_filter(name=\"tips\")\n", + "deck.assign_child_resource(tip_rack, location=Coordinate(20.0, 150.0, 0.0))\n", + "\n", + "lh = LiquidHandler(backend=LiquidHandlerChatterboxBackend(num_channels=8), deck=deck)\n", + "await lh.setup()\n", + "await lh.pick_up_tips([tip_rack.get_item(\"A1\")])\n", + "await lh.discard_tips(use_channels=[0])\n", + "\n", + "assert not lh.head[0].has_tip\n", + "assert trash.children == []\n", + "await lh.stop()" + ] + }, + { + "cell_type": "markdown", + "id": "clear-deck", + "metadata": {}, + "source": [ + "## Clearing a deck\n", + "\n", + "{meth}`~pylabrobot.resources.Deck.clear` preserves trash by default while removing other\n", + "deck resources. Pass `include_trash=True` when the trash area should also be detached:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "clear-example", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import Resource\n", + "\n", + "work_area = Resource(name=\"work_area\", size_x=100.0, size_y=80.0, size_z=20.0)\n", + "deck.assign_child_resource(work_area, location=Coordinate(20.0, 20.0, 0.0))\n", + "\n", + "deck.clear()\n", + "assert deck.has_resource(\"trash\")\n", + "assert not deck.has_resource(\"work_area\")\n", + "\n", + "deck.clear(include_trash=True)\n", + "assert not deck.has_resource(\"trash\")" + ] + }, + { + "cell_type": "markdown", + "id": "discard-details", + "metadata": {}, + "source": [ + "## Other disposal operations\n", + "\n", + "Use `LiquidHandler.drop_tips()` when you need to target a `Trash` explicitly. Decks with a\n", + "separate 96-head disposal area can expose it through\n", + "{meth}`~pylabrobot.resources.Deck.get_trash_area96` for `LiquidHandler.discard_tips96()`.\n", + "\n", + "A trash area does not retain discarded objects as children or record how many tips it contains.\n", + "Use external waste-capacity tracking when a workflow must stop before the physical bin is full." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/resources/index.md b/docs/resources/index.md index a7e343617c7..9e4df28525a 100644 --- a/docs/resources/index.md +++ b/docs/resources/index.md @@ -75,6 +75,7 @@ PLR's `Resource` subclasses in the inheritance tree are: