Skip to content

docs(api): lids updates in 8.4 #17914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fb8a39f
chore(shared-data): command schema bump version to 13 (#17889)
y3rsh Mar 26, 2025
a8105b2
fix(protocol-designer): remove PD version number from navigation bar …
jerader Mar 26, 2025
04199aa
refactor(protocol-designer): use windows vs mac symbols for hot keys …
jerader Mar 26, 2025
26144c4
feat(app): generate python file for quick transfer (#17880)
jerader Mar 26, 2025
e434647
feat(protocol-designer): add release notes link to announcement modal…
jerader Mar 26, 2025
16d91d7
feat(api): return FlexStackerShuttleMissing from stacker engine comma…
ahiuchingau Mar 26, 2025
72e8c2c
feature(app): ER for transporter unable to reach final position due t…
TamarZanzouri Mar 26, 2025
ae3b9ae
fix (abr-testing): Remove instances where pipette was returning tips …
rclarke0 Mar 27, 2025
013492c
fix(api): clamp erroneous negative absorbance values to 0. (#17812)
vegano1 Mar 27, 2025
2a862f6
feat (hardware-testing): DVT Flex Stacker Accelerated Lifetime Test (…
rclarke0 Mar 27, 2025
fec98f0
feat (hardware-testing): DVT Flex Stacker Labware Compatibility Lifet…
rclarke0 Mar 27, 2025
2dea9f9
refactor(step-generation, protocol-designer, app): split additionalEq…
jerader Mar 27, 2025
2ab75dc
refactor(protocol-designer): use HydratedFormData union type for form…
jerader Mar 27, 2025
058f6c9
refactor(step-generation): show location string for location arg in g…
jerader Mar 27, 2025
5626e29
refactor(protocol-designer): consolidate CheckboxExapandStepFormField…
ncdiehl11 Mar 27, 2025
b1462fe
feat(api): Implement stacker pool specific overlap offsets (#17794)
CaseyBatten Mar 27, 2025
ca4b0e8
docs(api): lids updates in 8.4
emilyburghardt Mar 27, 2025
253a5d1
moving lids section
emilyburghardt Mar 28, 2025
41bf7e5
changes to moving lids section
emilyburghardt Apr 1, 2025
de110bb
updates to moving lids section
emilyburghardt Apr 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ def run(protocol: ProtocolContext) -> None:
deactivate_modules_bool = protocol.params.deactivate_modules # type: ignore[attr-defined]
plate_type = protocol.params.labware_plate_reader_compatible # type: ignore [attr-defined]
plate_orientation = protocol.params.plate_orientation # type: ignore[attr-defined]
helpers.comment_protocol_version(protocol, "01")

helpers.comment_protocol_version(protocol, "02")
plate_name_str = "hellma_plate_" + str(plate_orientation)

# Protocol Parameters
Expand Down Expand Up @@ -335,6 +334,8 @@ def mixing(well: Well, pip: InstrumentContext, mvol: float, reps: int = 8) -> No

pip.flow_rate.aspirate = 300
pip.flow_rate.dispense = 300
if m1000.current_volume > 0.0:
m1000.dispense(m1000.current_volume, waste)

def lysis(vol: float, source: List[Well]) -> None:
"""Lysis Steps."""
Expand Down Expand Up @@ -512,7 +513,6 @@ def stop_reaction(vol: float, source: Well) -> None:
m1000.blow_out(m.top(-3))
m1000.prepare_to_aspirate()
m1000.air_gap(20)

m1000.drop_tip() if TIP_TRASH else m1000.return_tip()

# Shake for 3 minutes to mix wash with beads
Expand Down Expand Up @@ -559,6 +559,7 @@ def elute(vol: float) -> None:
m1000.aspirate(elution_vol - 10, samples_m[i])
m1000.dispense(elution_vol - 10, samples_m[i].bottom(10))
m1000.flow_rate.dispense = 300
m1000.dispense(m1000.current_volume, waste)
m1000.drop_tip() if TIP_TRASH else m1000.return_tip()

# Shake for 3 minutes to mix wash with beads
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def run(protocol: ProtocolContext) -> None:
"nest_96_wellplate_2ml_deep", str(SLOTS["LABWARE"][0]), "Reservoir 1"
) # Reservoir
sample_plate_2 = protocol.load_labware(
"thermoscientificnunc_96_wellplate_1300ul",
"nest_96_wellplate_2ml_deep",
str(SLOTS["LABWARE"][1]),
"Sample Plate 2",
) # Reservoir
Expand Down
35 changes: 35 additions & 0 deletions api/docs/v2/moving_labware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,13 @@ You can manually move any standard or custom labware. Using the gripper to move
* ``opentrons_flex_96_filtertiprack_50ul``
* ``opentrons_flex_96_filtertiprack_200ul``
* ``opentrons_flex_96_filtertiprack_1000ul``
* - Opentrons labware lids
-
* ``opentrons_tough_pcr_auto_sealing_lid``
* ``opentrons_flex_tiprack_lid``

You can move compatible Opentrons lids manually or with the Flex gripper, but some restrictions apply. For more information, see :ref:`moving-lids`.

The gripper may work with other ANSI/SLAS standard labware, but this is not recommended.

.. note::
Expand Down Expand Up @@ -192,3 +198,32 @@ You can also load labware off-deck, in preparation for a ``move_labware()`` comm
pipette.pick_up_tip()

Using the off-deck location to remove or replace labware lets you continue your workflow in a single protocol, rather than needing to end a protocol, reset the deck, and start a new protocol run.

.. _moving-lids:

Moving Lids
===========

Lids on well plates or tip racks can help prevent contamination on the deck and are required for use with some modules, like the Thermocycler. You can use :py:meth:`.ProtocolContext.move_lid` to move an Opentrons Tough PCR Auto-Sealing Lid or Opentrons Flex Tiprack Lid manually or using the Flex gripper.

An Opentrons Tough PCR Auto-Sealing Lid can be moved between deck slots, lid stacks, or compatible labware, modules, and adapters loaded in your protocol.

.. code-block:: python

## load lid stack on the Flex deck

lid_stack = protocol.load_lid_stack(
load_name="opentrons_tough_pcr_auto_sealing_lid",
location="B2",
quantity="4")

## move an Opentrons Tough PCR Auto-Sealing Lid to a compatible well plate in the Thermocycler
protocol.move_lid(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CaseyBatten do we not specify the load name for the lid being moved?

source_location=("lid_stack", "B2"),
new_location=("opentrons_96_wellplate_200ul_pcr_full_skirt", "tcmod"),
use_gripper= "True")

Use ``move_lid()`` again to move a used Opentrons Tough PCR Auto-Sealing Lid to a waste chute or trash bin loaded in your protocol.

.. note::
You can move an Opentrons Flex Tiprack Lid from a new tip rack to a second tip rack without a lid or to a waste chute or trash bin on the deck, either manually or using the Flex gripper. The tip rack lid must be defined in your protocol using the ``lid`` parameter of ``load_labware``. For more information, see :ref:`loading-lids`.
45 changes: 41 additions & 4 deletions api/docs/v2/new_labware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Throughout this section, we'll use the labware listed in the following table.
- Labware name
- API load name
* - Well plate
- `Corning 96 Well Plate 360 µL Flat <https://labware.opentrons.com/corning_96_wellplate_360ul_flat/>`_
- `Opentrons Tough 96 Well Plate 200 µL PCR Full Skirt <https://labware.opentrons.com/opentrons_96_wellplate_200ul_pcr_full_skirt/>`_
- ``corning_96_wellplate_360ul_flat``
* - Flex tip rack
- `Opentrons Flex 96 Tips 200 µL <https://shop.opentrons.com/opentrons-flex-tips-200-l/>`_
Expand All @@ -63,13 +63,13 @@ Similar to the code sample in :ref:`overview-section-v2`, here's how you use the

#Flex
tiprack = protocol.load_labware("opentrons_flex_96_tiprack_200ul", "D1")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "D2")
plate = protocol.load_labware("opentrons_96_wellplate_200ul_pcr_full_skirt", "D2")

.. code-block:: python

#OT-2
tiprack = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "2")
plate = protocol.load_labware("opentrons_96_wellplate_200ul_pcr_full_skirt", "2")

.. versionadded:: 2.0

Expand All @@ -82,10 +82,47 @@ When the ``load_labware`` method loads labware into your protocol, it returns a
The ``load_labware`` method includes an optional ``label`` argument. You can use it to identify labware with a descriptive name. If used, the label value is displayed in the Opentrons App. For example::

tiprack = protocol.load_labware(
load_name="corning_96_wellplate_360ul_flat",
load_name="opentrons_flex_96_tiprack_200ul",
location="D1",
label="any-name-you-want")


.. _loading-lids:

Loading Lids
============

You can load lids on compatible plates or tip racks. Use the optional ``lid`` parameter of ``load_labware`` to add an Opentrons Tough Auto-Sealing Lid to a PCR plate, or load a new tip rack's lid in your protocol.

.. code-block:: python

plate = protocol.load_labware(
load_name="opentrons_96_wellplate_200ul_pcr_full_skirt",
location="D2",
lid="opentrons_tough_pcr_auto_sealing_lid")


.. code-block:: python

tiprack = protocol.load_labware(
load_name="opentrons_flex_96_tiprack_200ul",
location="D1",
lid="opentrons_flex_tiprack_lid")


You might need multiple lids during your protocol. Use ``load_lid_stack`` to stack up to five Opentrons Tough Auto-Sealing Lids on a deck slot, riser, or compatible adapter.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CaseyBatten is there a maximum of 5? I can't find where I read this originally. If true, should also maybe add to API reference.

Copy link
Contributor

@CaseyBatten CaseyBatten Mar 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a maximum of 5 based on the stackLimit in the labware definition for the Tough Auto-Sealing lids. Technically a labware could have a stack limit of any number, so long as we define it as such. The auto sealing lids become unstable after 5 lids, so we limit them to 5.


.. code-block:: python

def load_lid_stack(
load_name="opentrons_tough_pcr_auto_sealing_lid",
location="B2",
quantity="4")

Tip rack lids can't be stacked or placed on the deck.

.. versionadded:: 2.23

.. _labware-on-adapters:

Loading Labware on Adapters
Expand Down
14 changes: 13 additions & 1 deletion api/src/opentrons/hardware_control/modules/flex_stacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
FlexStackerData,
)

from opentrons_shared_data.errors.exceptions import FlexStackerStallError
from opentrons_shared_data.errors.exceptions import (
FlexStackerStallError,
FlexStackerShuttleMissingError,
)

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -414,6 +417,7 @@ async def _prepare_for_action(self) -> bool:
await self.home_axis(StackerAxis.X, Direction.EXTEND)
await self.home_axis(StackerAxis.Z, Direction.RETRACT)
await self.close_latch()
await self.verify_shuttle_location(PlatformState.EXTENDED)
return True

async def home_all(self, ignore_latch: bool = False) -> None:
Expand All @@ -436,6 +440,14 @@ async def home_all(self, ignore_latch: bool = False) -> None:
await self.home_axis(StackerAxis.Z, Direction.RETRACT)
await self.home_axis(StackerAxis.X, Direction.EXTEND)

async def verify_shuttle_location(self, expected: PlatformState) -> None:
"""Verify the shuttle is present and in the expected location."""
await self._reader.read()
if self.platform_state != expected:
raise FlexStackerShuttleMissingError(
self.device_info["serial"], expected, self.platform_state
)


class FlexStackerReader(Reader):
error: Optional[str]
Expand Down
34 changes: 18 additions & 16 deletions api/src/opentrons/protocol_api/protocol_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,14 +451,14 @@ def load_labware(
choose the adapter's version automatically.

.. versionadded:: 2.15
:param lid: A lid to load on the top of the main labware. Accepts the same
values as the ``load_name`` parameter of :py:meth:`.load_lid_stack`. The
lid will use the same namespace as the labware, and the API will
choose the adapter's version automatically.

.. versionadded:: 2.23
"""
# TODO: re-include in docstring when 2.23 is ready
# :param lid: A lid to load the on top of the main labware. Accepts the same
# values as the ``load_name`` parameter of :py:meth:`.load_lid_stack`. The
# lid will use the same namespace as the labware, and the API will
# choose the lid's version automatically.
#
# .. versionadded:: 2.23

if isinstance(location, OffDeckType) and self._api_version < APIVersion(2, 15):
raise APIVersionError(
api_element="Loading a labware off-deck",
Expand Down Expand Up @@ -1391,13 +1391,13 @@ def load_lid_stack(
version: Optional[int] = None,
) -> Labware:
"""
Load a stack of Lids onto a valid Deck Location or Adapter.
Load a stack of Opentrons Tough Auto-Sealing Lids onto a valid deck location or adapter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Load lid stack can in theory be used by any stackable lids (of which only the auto-sealing lids apply), so we might want to use more general terminology.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like limiting it to what's possible now — we can always update when we add more labware and this becomes a more broadly applicable feature.


:param str load_name: A string to use for looking up a lid definition.
You can find the ``load_name`` for any standard lid on the Opentrons
You can find the ``load_name`` for any compatible lid on the Opentrons
`Labware Library <https://labware.opentrons.com>`_.
:param location: Either a :ref:`deck slot <deck-slots>`,
like ``1``, ``"1"``, or ``"D1"``, or the a valid Opentrons Adapter.
like ``1``, ``"1"``, or ``"D1"``, or a valid Opentrons Adapter.
:param int quantity: The quantity of lids to be loaded in the stack.
:param adapter: An adapter to load the lid stack on top of. Accepts the same
values as the ``load_name`` parameter of :py:meth:`.load_adapter`. The
Expand All @@ -1418,9 +1418,10 @@ def load_lid_stack(
leave this unspecified to let ``load_lid_stack()`` choose a version
automatically.

:return: The initialized and loaded labware object representing the Lid Stack.
:return: The initialized and loaded labware object representing the lid stack.

.. versionadded:: 2.23

:meta private:
"""
if self._api_version < validation.LID_STACK_VERSION_GATE:
raise APIVersionError(
Expand Down Expand Up @@ -1479,9 +1480,9 @@ def move_lid(
pick_up_offset: Optional[Mapping[str, float]] = None,
drop_offset: Optional[Mapping[str, float]] = None,
) -> Labware | None:
"""Move a lid from a valid source to a new location. Can return a Lid Stack if one is created.
"""Move a compatible lid from a valid source to a new location. Can return a lid stack if one is created.

:param source_location: Where to take the lid from. This is either:
:param source_location: The lid's starting location. This is either:

* A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
* A labware or adapter that's already been loaded on the deck
Expand All @@ -1498,7 +1499,7 @@ def move_lid(
with :py:meth:`load_labware` or :py:meth:`load_adapter`.
* The special constant :py:obj:`OFF_DECK`.

:param use_gripper: Whether to use the Flex Gripper for this movement.
:param use_gripper: Whether to use the Flex Gripper to move the lid.

* If ``True``, use the gripper to perform an automatic
movement. This will raise an error in an OT-2 protocol.
Expand All @@ -1515,7 +1516,8 @@ def move_lid(
labware's current and new locations are accessible, i.e., open the Thermocycler lid
or open the Heater-Shaker's labware latch.

:meta private:
.. versionadded:: 2.23

"""
source: Union[LabwareCore, DeckSlotName, StagingSlotName]
if isinstance(source_location, Labware):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
TipPhysicallyAttachedError,
)
from .movement_common import StallOrCollisionError
from .flex_stacker.common import FlexStackerStallOrCollisionError
from .flex_stacker.common import (
FlexStackerStallOrCollisionError,
FlexStackerShuttleError,
)

from . import absorbance_reader
from . import flex_stacker
Expand Down Expand Up @@ -920,6 +923,7 @@
DefinedErrorData[GripperMovementError],
DefinedErrorData[StallOrCollisionError],
DefinedErrorData[FlexStackerStallOrCollisionError],
DefinedErrorData[FlexStackerShuttleError],
]


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ class FlexStackerStallOrCollisionError(ErrorOccurrence):

errorCode: str = ErrorCodes.FLEX_STACKER_STALL_OR_COLLISION_DETECTED.value.code
detail: str = ErrorCodes.FLEX_STACKER_STALL_OR_COLLISION_DETECTED.value.detail


class FlexStackerShuttleError(ErrorOccurrence):
"""Returned when the Flex Stacker Shuttle is not in the correct location."""

isDefined: bool = True
errorType: Literal["flexStackerShuttleMissing"] = "flexStackerShuttleMissing"

errorCode: str = ErrorCodes.FLEX_STACKER_SHUTTLE_MISSING.value.code
detail: str = ErrorCodes.FLEX_STACKER_SHUTTLE_MISSING.value.detail
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
SuccessData,
DefinedErrorData,
)
from ..flex_stacker.common import FlexStackerStallOrCollisionError
from ..flex_stacker.common import (
FlexStackerStallOrCollisionError,
FlexStackerShuttleError,
)
from ...errors import (
ErrorOccurrence,
CannotPerformModuleAction,
Expand All @@ -31,7 +34,10 @@
LoadedLabware,
)
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
from opentrons_shared_data.errors.exceptions import FlexStackerStallError
from opentrons_shared_data.errors.exceptions import (
FlexStackerStallError,
FlexStackerShuttleMissingError,
)
from opentrons.calibration_storage.helpers import uri_from_details

if TYPE_CHECKING:
Expand Down Expand Up @@ -120,7 +126,8 @@ class RetrieveResult(BaseModel):

_ExecuteReturn = Union[
SuccessData[RetrieveResult],
DefinedErrorData[FlexStackerStallOrCollisionError],
DefinedErrorData[FlexStackerStallOrCollisionError]
| DefinedErrorData[FlexStackerShuttleError],
]


Expand Down Expand Up @@ -354,6 +361,20 @@ async def execute(self, params: RetrieveParams) -> _ExecuteReturn:
],
),
)
except FlexStackerShuttleMissingError as e:
return DefinedErrorData(
public=FlexStackerShuttleError(
id=self._model_utils.generate_id(),
createdAt=self._model_utils.get_timestamp(),
wrappedErrors=[
ErrorOccurrence.from_failed(
id=self._model_utils.generate_id(),
createdAt=self._model_utils.get_timestamp(),
error=e,
)
],
),
)

# Update the state to reflect the labware is now in the Flex Stacker slot
# todo(chb, 2025-02-19): This ModuleLocation piece should probably instead be an AddressableAreaLocation
Expand Down
Loading
Loading