From b948ddab0cbf30baba2384f3d6117362f32ec737 Mon Sep 17 00:00:00 2001 From: fly1d <309400591+fly1d@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:05:39 +0800 Subject: [PATCH] Add TipSpot guide Document TipSpot access, tip state configuration, and custom rack definitions in an executable notebook. Assisted-by: OpenAI Codex --- docs/resources/index.md | 3 +- .../itemized-resource/tiprack/tip-spot.ipynb | 180 ++++++++++++++++++ .../itemized-resource/tiprack/tiprack.rst | 10 +- 3 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 docs/resources/itemized-resource/tiprack/tip-spot.ipynb diff --git a/docs/resources/index.md b/docs/resources/index.md index 847610a431f..a7e343617c7 100644 --- a/docs/resources/index.md +++ b/docs/resources/index.md @@ -88,7 +88,7 @@ PLR's `Resource` subclasses in the inheritance tree are: ├── ItemizedResource │ ├── Plate - │ ├── TipRack + │ ├── TipRack │ └── ContainerRack │ └── TubeRack @@ -97,6 +97,7 @@ PLR's `Resource` subclasses in the inheritance tree are: │ └── PlateHolder + ├── TipSpot ├── Lid ├── PlateAdapter diff --git a/docs/resources/itemized-resource/tiprack/tip-spot.ipynb b/docs/resources/itemized-resource/tiprack/tip-spot.ipynb new file mode 100644 index 00000000000..01d5d078823 --- /dev/null +++ b/docs/resources/itemized-resource/tiprack/tip-spot.ipynb @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "tipspot-intro", + "metadata": {}, + "source": [ + "# TipSpot\n", + "\n", + "A {class}`~pylabrobot.resources.tip_rack.TipSpot` represents one position in a\n", + "{class}`~pylabrobot.resources.TipRack`. The spot is a resource with its own location and\n", + "dimensions, while the disposable {class}`~pylabrobot.resources.tip.Tip` that it may contain is a\n", + "separate object. Each spot owns a {class}`~pylabrobot.resources.tip_tracker.TipTracker` that records\n", + "whether a tip is present." + ] + }, + { + "cell_type": "markdown", + "id": "accessing-spots", + "metadata": {}, + "source": [ + "## Accessing tip spots\n", + "\n", + "Tip spots are normally obtained from a tip rack rather than created directly. Use\n", + "{meth}`~pylabrobot.resources.ItemizedResource.get_item` for one spot and\n", + "{meth}`~pylabrobot.resources.ItemizedResource.get_items` for a range:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "access-example", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import hamilton_96_tiprack_300uL_filter\n", + "\n", + "tip_rack = hamilton_96_tiprack_300uL_filter(name=\"tips\")\n", + "\n", + "a1 = tip_rack.get_item(\"A1\")\n", + "first_column = tip_rack.get_items(\"A1:H1\")\n", + "\n", + "assert [spot.get_identifier() for spot in first_column] == [f\"{row}1\" for row in \"ABCDEFGH\"]\n", + "assert a1.get_identifier() == \"A1\"\n", + "assert a1.has_tip()\n", + "tip = a1.get_tip()" + ] + }, + { + "cell_type": "markdown", + "id": "identifiers", + "metadata": {}, + "source": [ + "The identifiers use transposed spreadsheet notation: rows are letters and columns are numbers.\n", + "For example, `A1:H1` selects the first column from top to bottom. A spot's location is relative\n", + "to its rack, so moving the rack does not change the spot's local coordinates." + ] + }, + { + "cell_type": "markdown", + "id": "initial-state", + "metadata": {}, + "source": [ + "## Setting the initial tip state\n", + "\n", + "Predefined racks are full by default. Pass `with_tips=False` to start with an empty rack, or use\n", + "the rack-level methods to describe a partially filled rack:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "state-example", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack = hamilton_96_tiprack_300uL_filter(name=\"tips\", with_tips=False)\n", + "tip_rack.set_tip_state({\"A1\": True, \"B1\": True})\n", + "\n", + "assert tip_rack.get_item(\"A1\").has_tip()\n", + "assert not tip_rack.get_item(\"C1\").has_tip()" + ] + }, + { + "cell_type": "markdown", + "id": "tracking-guide", + "metadata": {}, + "source": [ + "Use {meth}`~pylabrobot.resources.TipRack.fill` and\n", + "{meth}`~pylabrobot.resources.TipRack.empty` to update every spot. See the\n", + "{doc}`tip-tracking guide ` for how tip state\n", + "is validated and updated during liquid-handling operations." + ] + }, + { + "cell_type": "markdown", + "id": "defining-spots", + "metadata": {}, + "source": [ + "## Defining tip spots\n", + "\n", + "A custom tip rack definition supplies an ordered grid of `TipSpot` objects. The `make_tip`\n", + "callable receives the unique name that the spot assigns to each tip it creates:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "definition-example", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import Tip, TipRack, TipSpot, create_ordered_items_2d\n", + "\n", + "\n", + "def make_tip(name: str) -> Tip:\n", + " return Tip(\n", + " name=name,\n", + " has_filter=False,\n", + " total_tip_length=50.0,\n", + " maximal_volume=300.0,\n", + " fitting_depth=8.0,\n", + " )\n", + "\n", + "\n", + "name = \"custom_tip_rack\"\n", + "tip_rack = TipRack(\n", + " name=name,\n", + " size_x=20.0,\n", + " size_y=20.0,\n", + " size_z=10.0,\n", + " ordered_items=create_ordered_items_2d(\n", + " TipSpot,\n", + " num_items_x=2,\n", + " num_items_y=2,\n", + " dx=1.0,\n", + " dy=1.0,\n", + " dz=0.0,\n", + " item_dx=9.0,\n", + " item_dy=9.0,\n", + " size_x=8.0,\n", + " size_y=8.0,\n", + " make_tip=make_tip,\n", + " name_prefix=name,\n", + " ),\n", + ")\n", + "\n", + "assert tip_rack.get_item(\"A1\").get_tip().name == \"custom_tip_rack_tipspot_A1#0\"\n", + "assert tip_rack.get_item(\"A1\").location.x == 1.0\n", + "assert tip_rack.get_item(\"A1\").location.y == 10.0\n", + "assert tip_rack.get_item(\"B1\").location.y == 1.0" + ] + }, + { + "cell_type": "markdown", + "id": "grid-coordinates", + "metadata": {}, + "source": [ + "The dimensions and offsets are in millimeters. `dx` sets the x coordinate of the leftmost\n", + "column, while `dy` sets the y coordinate of the frontmost row (the bottom row when the rack is\n", + "viewed as a grid). Row A is at the back of the rack, where y is largest. `dz` sets the z\n", + "coordinate for every spot, and `item_dx` and `item_dy` are the origin-to-origin spacing.\n", + "\n", + "See {class}`~pylabrobot.resources.tip_rack.TipSpot` for the complete API reference." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/resources/itemized-resource/tiprack/tiprack.rst b/docs/resources/itemized-resource/tiprack/tiprack.rst index d4b9fb62be6..201828f58e4 100644 --- a/docs/resources/itemized-resource/tiprack/tiprack.rst +++ b/docs/resources/itemized-resource/tiprack/tiprack.rst @@ -1,6 +1,14 @@ TipRack ======= -TODO: write a tutorial +A ``TipRack`` is an :class:`~pylabrobot.resources.itemized_resource.ItemizedResource` whose items +are :class:`~pylabrobot.resources.tip_rack.TipSpot` objects. See the :doc:`TipSpot guide +` for details on accessing spots and defining a custom rack. See :class:`~pylabrobot.resources.tip_rack.TipRack` for the API reference. + +.. toctree:: + :maxdepth: 1 + :hidden: + + tip-spot