Skip to content
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

docs(api): liquid meniscus in API 2.23 #17971

Open
wants to merge 5 commits into
base: chore_release-8.4.0
Choose a base branch
from

Conversation

emilyburghardt
Copy link
Collaborator

@emilyburghardt emilyburghardt commented Apr 2, 2025

Overview

Adding liquid meniscus changes in API 2.23

Test Plan and Hands on Testing

sandbox: http://sandbox.docs.opentrons.com/docs-liquid-meniscus/v2/

Changelog

  • describing use of measure_liquid_height in API 2.23
  • aspirate or dispense by well or location: adding a Well.meniscus as a location
  • new Measure Liquids section in Liquid Control
  • adding meniscus as a location in a well
  • adding description of meniscus to Default Positions
  • updated meniscus API reference entry

Review requests

  • Are descriptions of target and measure_liquid_height clear enough?
  • Are code blocks correct?
  • Anywhere else we should add a meniscus example?

Risk assessment

low.

@emilyburghardt emilyburghardt requested a review from a team as a code owner April 2, 2025 23:26
pipette.measure_liquid_height(plate["B1"])
pipette.dispense(200, location=plate["B1"].meniscus(z=-1)) #dispenses 1 mm below the liquid meniscus

The API will raise an error if no liquid is present in the well.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

is this true? the API raises an error if no liquid is present to measure in the well?

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this was fixed in #17957

Measure Liquids
===============

The :py:meth:`.measure_liquid_height` method tells a Flex pipette to measure the height of liquid in a well. It returns the height of liquid in the well in mm. When ``measure_liquid_height()`` finds an empty well, it raises and error and pauses the protocol to let you resove the problem.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

double check that this is 1) Flex-only and 2) requires a tip to measure liquid. fix "resove" typo in my next commit

- Specifying your starting liquid volume with :py:meth:`~Labware.load_liquid_by_well`, or
- Using :py:meth:`.measure_liquid_height` to define liquid volume in the well.

Detecting liquid in a well requires pipette sensors, so you can only measure liquid height with Flex pipettes. However...
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

load_liquid_by_well isn't OT-2 specific, so an OT-2 user could get this far and then.... the API would raise an error? or is liquid meniscus supported for OT-2?

pipette.aspirate(100, location=plate["A1"].meniscus(z=-2)) # aspirates 2 mm below the detected liquid meniscus

.. versionadded:: 2.23

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

maybe add a Flex vs OT-2 sentence here if applicable.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need this section here — as you say, you need to specify meniscus-relative every time, and this section is about what happens if you don't specify a location.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree. I was reading this as "well, this is one way you can change aspiration and dispensing height, here's another" but it's really "here's a way to change that height for many aspirations and dispenses, basically like changing the default" of which meniscus-relative pipetting is not that.

@sfoster1 sfoster1 changed the title (docs)api: liquid meniscus in API 2.23 docs(api): liquid meniscus in API 2.23 Apr 3, 2025
Copy link
Contributor

@ecormany ecormany left a comment

Choose a reason for hiding this comment

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

Bunch of little comments. Happy to walk through them and decide whether it's best to update docstrings (beyond the minimum to get the docs building) here or in a follow-up PR.

@@ -16,10 +16,12 @@ Position Relative to Labware

When the robot positions itself relative to a piece of labware, where it moves is determined by the labware definition, the actions you want it to perform, and the labware offsets for a specific deck slot. This section describes how these positional components are calculated and how to change them.

Top, Bottom, and Center
-----------------------
Top, Bottom, Center, and Meniscus
Copy link
Contributor

Choose a reason for hiding this comment

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

Getting kinda long. Maybe we can call this section "Well positions" now.

@@ -2523,7 +2523,6 @@ def measure_liquid_height(self, well: labware.Well) -> LiquidTrackingType:

:returns: The height, in mm, of the liquid from the deck.

:meta private:

This is intended for Opentrons internal use only and is not a guaranteed API.
Copy link
Contributor

Choose a reason for hiding this comment

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

Becoming public now, so this line should be removed.

Comment on lines 271 to 272
:param target: The relative position inside the well to target when performing a liquid handling operation.
:return: A :py:class:`~opentrons.types.Location` that indicates location is meniscus and that holds the ``z`` offset in its point.z field.
Copy link
Contributor

Choose a reason for hiding this comment

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

These need some improvement, either in this PR or separately.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated this today. Started to document start and end more, but remembered that we want to hold off on mentions of dynamic. Can ticket beefing this reference up for API 2.24 docs.

Other than that, roughly followed the structure of the other well position API refs.

@@ -48,6 +48,19 @@ You can also aspirate from a location along the center vertical axis within a we
depth = plate["A1"].bottom(z=2) # tip is 2 mm above well bottom
pipette.aspirate(200, depth)


Use the :py:meth:`~.Labware.Well.mensicus` method to aspirate from the meniscus of liquid in a well with a Flex pipette. First, you'll need to define the volume of liquid in your well:
Copy link
Contributor

Choose a reason for hiding this comment

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

"relative to the meniscus"? "from" sounds like the exact location of the meniscus (which is not the usual behavior).

pipette.measure_liquid_height(plate["A2"])
pipette.aspirate(200, location=plate["A2"].meniscus(target="end", z=-1)) # aspirates at 1 mm below the liquid meniscus

The liquid meniscus changes when you aspirate liquid from a well. Set ``target`` to the ending position of the liquid within a well to ensure the pipette stays submerged while aspirating. For more information, see :ref:`well-meniscus`.
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll sometimes shorten things by putting the parameter and value together in running text:

Suggested change
The liquid meniscus changes when you aspirate liquid from a well. Set ``target`` to the ending position of the liquid within a well to ensure the pipette stays submerged while aspirating. For more information, see :ref:`well-meniscus`.
The liquid meniscus changes when you aspirate liquid from a well. Set ``target=end`` to ensure the pipette stays submerged while aspirating. For more information, see :ref:`well-meniscus`.

Meniscus
^^^^^^^^

Let's look at the :py:meth:`~.Labware.well-meniscus` method. It returns a position at the surface of liquid, or meniscus, inside a well. Like the `.Well.top` and `.Well.bottom` methods, you can adjust the height of the meniscus with the optional argument ``z``, which is measured in mm. Positive numbers move the position up, negative ``z`` numbers move it down.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Let's look at the :py:meth:`~.Labware.well-meniscus` method. It returns a position at the surface of liquid, or meniscus, inside a well. Like the `.Well.top` and `.Well.bottom` methods, you can adjust the height of the meniscus with the optional argument ``z``, which is measured in mm. Positive numbers move the position up, negative ``z`` numbers move it down.
Let's look at the :py:meth:`~.Labware.well-meniscus` method. It returns a position at the surface of liquid, or meniscus, inside a well. Like the `.Well.top` and `.Well.bottom` methods, you can adjust the height of the meniscus with the optional argument ``z``, which is measured in mm. Positive ``z`` values move the position up, and negative ones move it down.


The liquid meniscus in a well changes during aspirating or dispensing, so you'll also need to specify a ``target`` position for the pipette:
- Set ``target= "end"`` to ensure the pipette stays submerged while aspirating.
- Set ``target= "start"`` or ``end`` to choose where the pipette begins dispensing.
Copy link
Contributor

Choose a reason for hiding this comment

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

might be useful to split these out and describe how they would create a contact or non-contact dispense.

- Specifying your starting liquid volume with :py:meth:`~.Labware.load_liquid`, or
- Using :py:meth:`~.InstrumentContext.measure_liquid_height` to define liquid volume in the well.

Detecting liquid in a well requires pipette sensors, so you can only measure liquid height with a Flex pipette.
Copy link
Contributor

Choose a reason for hiding this comment

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

i think this should also be inside the note (indented)

pipette.aspirate(100, location=plate["A1"].meniscus(z=-2)) # aspirates 2 mm below the detected liquid meniscus

.. versionadded:: 2.23

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need this section here — as you say, you need to specify meniscus-relative every time, and this section is about what happens if you don't specify a location.

@@ -169,7 +207,7 @@ Move To

The :py:meth:`.InstrumentContext.move_to` method moves a pipette to any reachable location on the deck. If the pipette has picked up a tip, it will move the end of the tip to that position; if it hasn't, it will move the pipette nozzle to that position.

The :py:meth:`~.InstrumentContext.move_to` method requires the :py:class:`.Location` argument. The location can be automatically generated by methods like ``Well.top()`` and ``Well.bottom()`` or one you've created yourself, but you can't move a pipette to a well directly:
The :py:meth:`~.InstrumentContext.move_to` method requires the :py:class:`.Location` argument. The location can be automatically generated by methods like ``Well.top()``, ``Well.bottom()``, and ``Well.mensicus``, or one you've created yourself. However, you can't move a pipette to a well directly:
Copy link
Contributor

Choose a reason for hiding this comment

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

parens again

Suggested change
The :py:meth:`~.InstrumentContext.move_to` method requires the :py:class:`.Location` argument. The location can be automatically generated by methods like ``Well.top()``, ``Well.bottom()``, and ``Well.mensicus``, or one you've created yourself. However, you can't move a pipette to a well directly:
The :py:meth:`~.InstrumentContext.move_to` method requires the :py:class:`.Location` argument. The location can be automatically generated by methods like ``Well.top()``, ``Well.bottom()``, and ``Well.mensicus()``, or one you've created yourself. However, you can't move a pipette to a well directly:

@ecormany ecormany added docs papi-v2 Python API V2 labels Apr 7, 2025

The liquid meniscus in a well changes during aspirating or dispensing, so you'll also need to specify a ``target`` position relative to the meniscus. Each position target is useful in different scenarios:

- Set ``target= "start"`` to target the existing liquid meniscus in the destination well before an aspirate or dispense.
Copy link
Contributor

@caila-marashaj caila-marashaj Apr 11, 2025

Choose a reason for hiding this comment

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

I would say take aspirate out of aspirate or dispense here, and write a note around here telling people not to aspirate with target='start' since that'll cause the pipette to be too high during the aspirate at some point.

If this is useful info trying to do this will cause you to fail analysis

@caila-marashaj caila-marashaj requested review from a team as code owners April 11, 2025 18:49
@caila-marashaj caila-marashaj requested review from smb2268 and removed request for a team April 11, 2025 18:49
Copy link

codecov bot commented Apr 11, 2025

Codecov Report

Attention: Patch coverage is 2.00000% with 686 lines in your changes missing coverage. Please review.

Project coverage is 27.59%. Comparing base (8e31989) to head (08ec6d2).
Report is 30 commits behind head on chore_release-8.4.0.

Files with missing lines Patch % Lines
.../ErrorRecoveryFlows/hooks/useFailedLabwareUtils.ts 0.00% 50 Missing ⚠️
app/src/organisms/ErrorRecoveryFlows/constants.ts 0.00% 47 Missing ⚠️
...yFlows/RecoveryOptions/FillWellAndRetryNewTips.tsx 0.00% 38 Missing ⚠️
...ms/Desktop/Devices/HistoricalProtocolRunDrawer.tsx 0.00% 37 Missing ⚠️
...veryFlows/RecoveryOptions/SelectRecoveryOption.tsx 0.00% 34 Missing ⚠️
...EditOffset/PrepareLabware/PlaceItemInstruction.tsx 0.00% 34 Missing ⚠️
.../ErrorRecoveryFlows/RecoveryOptions/ManageTips.tsx 0.00% 33 Missing ⚠️
...rrorRecoveryFlows/shared/LeftColumnLabwareInfo.tsx 0.00% 33 Missing ⚠️
...sms/ErrorRecoveryFlows/shared/RetryWithNewTips.tsx 0.00% 31 Missing ⚠️
...ms/ErrorRecoveryFlows/shared/RetryWithSameTips.tsx 0.00% 31 Missing ⚠️
... and 42 more
Additional details and impacted files

Impacted file tree graph

@@                   Coverage Diff                   @@
##           chore_release-8.4.0   #17971      +/-   ##
=======================================================
- Coverage                27.64%   27.59%   -0.05%     
=======================================================
  Files                     3113     3119       +6     
  Lines                   236867   237304     +437     
  Branches                 19220    19217       -3     
=======================================================
+ Hits                     65482    65486       +4     
- Misses                  171367   171800     +433     
  Partials                    18       18              
Flag Coverage Δ
protocol-designer 18.78% <2.00%> (-0.03%) ⬇️
step-generation 4.34% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...rrorRecoveryFlows/RecoveryOptions/RetryNewTips.tsx 0.00% <ø> (ø)
...anisms/ErrorRecoveryFlows/RecoveryOptions/index.ts 0.00% <ø> (ø)
...p/src/organisms/ErrorRecoveryFlows/shared/index.ts 0.00% <ø> (ø)
system-server/system_server/app_setup.py 95.00% <ø> (-0.24%) ⬇️
...nHeaderModalContainer/hooks/useRunHeaderDropTip.ts 0.00% <0.00%> (ø)
...rotocolRun/SetupModuleAndDeck/SetupModulesList.tsx 0.00% <0.00%> (ø)
...rorRecoveryFlows/RecoveryOptions/RetrySameTips.tsx 0.00% <0.00%> (ø)
...c/organisms/ErrorRecoveryFlows/hooks/useERUtils.ts 0.00% <0.00%> (ø)
...isms/ErrorRecoveryFlows/hooks/useRecoveryToasts.ts 0.00% <0.00%> (ø)
.../organisms/LabwareOffsetsTable/AccordionHeader.tsx 0.00% <0.00%> (ø)
... and 46 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs papi-v2 Python API V2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants