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

feat(engine): consolidate_liquid engine core implementation #17458

Open
wants to merge 8 commits into
base: edge
Choose a base branch
from

Conversation

jbleon95
Copy link
Contributor

@jbleon95 jbleon95 commented Feb 6, 2025

Overview

Closes AUTH-1066.

This PR adds the implementation for consolidate_liquid for use with liquid classes, using a similar pattern to transfer_liquid, but instead built around a many-to-one transfer. Like the legacy consolidate, we will attempt to aspirate as much liquid from as many wells as we can fit in a single tip, taking into account the air gap that may be added between movements, followed by a dispense of all liquid in the tip. This will continue until all wells have been consolidated into the destination.

Certain transfer properties will not be taken into account during consolidate, even if they are enabled, such as pre_wet and any mix. Another incompatible property is setting a blowout location to be source, as since it is impossible to consistently choose a source from the many different possible combinations of source wells given. This will raise an error. Another similar error will be raised if the transfer tip policy is set to PER_SOURCE

Test Plan and Hands on Testing

Tested on a robot with the following protocol. Integration tests have also been added, and unit tests will be added in a future PR after distribute is done and refactor work can be done to clean up the liquid class functions.

requirements = {
	"robotType": "Flex",
	"apiLevel": "2.23"
}

metadata = {
    "protocolName":'Consolidate Liquid Test',
}

def run(protocol_context):
	# Define labware, trash and pipette
	tiprack = protocol_context.load_labware("opentrons_flex_96_tiprack_200ul", "C2")
	trash = protocol_context.load_trash_bin('A3')
	pipette_1k = protocol_context.load_instrument("flex_1channel_1000", "left", tip_racks=[tiprack])
	nest_plate = protocol_context.load_labware("nest_96_wellplate_2ml_deep", "D1")
	arma_plate = protocol_context.load_labware("armadillo_96_wellplate_200ul_pcr_full_skirt", "D3")

	# Define water liquid class
	water_class = protocol_context.define_liquid_class("water")


	# This should aspirate 4 wells, dispense, aspirate 4 more, dispense
	pipette_1k.consolidate_liquid(
		liquid_class=water_class,
		volume=50,
		source=nest_plate.columns()[0],
		dest=arma_plate.wells()[0],
		new_tip="once",
		trash_location=trash,
	)

	# This should aspirate 3 wells, dispense, replace tip, aspirate 3 more, dispense, replace tip aspirate 2 more, dispense
	pipette_1k.consolidate_liquid(
		liquid_class=water_class,
		volume=55,
		source=nest_plate.columns()[0],
		dest=arma_plate.wells()[0],
		new_tip="always",
		trash_location=trash,
	)

Changelog

  • Added engine core implementation for consolidate_liquid

Review requests

Should have pre_wet or mix enabled be an error, or should we just skip those steps as it is currently written?

Also general look through of the logic for correctness.

Risk assessment

Low, new function and changes to common transfer code are gated behind the MANY_TO_ONE transfer type consolidate uses.

@jbleon95 jbleon95 requested a review from a team as a code owner February 6, 2025 21:54
@jbleon95 jbleon95 requested a review from sanni-t February 6, 2025 21:54
Copy link
Member

@sanni-t sanni-t left a comment

Choose a reason for hiding this comment

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

Mostly looks good!
We should add tests for the changes in transfer_components_executor too

Comment on lines +1250 to +1257
if (
blow_out_properties.enabled
and blow_out_properties.location == BlowoutLocation.SOURCE
):
raise RuntimeError(
'Blowout location "source" incompatible with consolidate liquid.'
' Please choose "destination" or "trash".'
)
Copy link
Member

Choose a reason for hiding this comment

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

We should highlight this and mix, pre-wet behavior in docs. I guess it's okay to raise this error instead of simply skipping the blowout, as long as our built-in classes don't have blowout with source location enabled by default, because otherwise a lot of protocols will run into errors due to no fault of the users. Liquid classes are just supposed to work without having to modify for each transfer.

Comment on lines +220 to +223
if (
not mix_properties.enabled
or self._transfer_type == TransferType.MANY_TO_ONE
):
Copy link
Member

Choose a reason for hiding this comment

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

Sorry, this is my fault; the docstring I wrote is misleading. I think we do want to run the post-dispense mix, while skipping the pre-aspirate mix. One way to do that is do the gating inside InstrumentCore.aspirate_liquid() and InstrumentCore.dispense_liquid() and the other is to somehow pass this function (or the class) the info that this mix is a part of aspirate/dispense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants