Skip to content

Commit

Permalink
feat(shared-data): add liquid class definition for water (#16671)
Browse files Browse the repository at this point in the history
Addresses AUTH-833

# Overview

Adds liquid class definition values for water for use with all Flex
pipette and combos

# Follow-ups needed for proper working of the features in this PR:

- remove 'default' keys from all '..ByVolume' properties and require at least one volume point in the dict.
- add a test to verify schema compatibility of
the definition

## Risk assessment

None

---------

Co-authored-by: andySigler <andrewsigler1@gmail.com>
Co-authored-by: Ryan howard <ryan.howard@opentrons.com>
  • Loading branch information
3 people authored Nov 18, 2024
1 parent f0e45b8 commit 9fceef6
Show file tree
Hide file tree
Showing 9 changed files with 2,559 additions and 50 deletions.
6 changes: 4 additions & 2 deletions api/src/opentrons/protocol_api/_liquid_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ def delete_for_volume(self, volume: float) -> None:

def _sort_volume_and_values(self) -> None:
"""Sort volume in increasing order along with corresponding values in matching order."""
self._sorted_volumes, self._sorted_values = zip(
*sorted(self._properties_by_volume.items())
self._sorted_volumes, self._sorted_values = (
zip(*sorted(self._properties_by_volume.items()))
if len(self._properties_by_volume) > 0
else [(), ()]
)


Expand Down
6 changes: 5 additions & 1 deletion api/tests/opentrons/hardware_control/test_ot3_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,11 @@ async def test_pickup_moves(

@pytest.mark.parametrize("load_configs", load_pipette_configs)
@given(blowout_volume=strategies.floats(min_value=0, max_value=10))
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], max_examples=10)
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
max_examples=10,
deadline=400,
)
@example(blowout_volume=0.0)
async def test_blow_out_position(
ot3_hardware: ThreadManager[OT3API],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ def test_build_aspirate_settings() -> None:

assert aspirate_properties.position_reference.value == "well-bottom"
assert aspirate_properties.offset == Coordinate(x=0, y=0, z=-5)
assert aspirate_properties.flow_rate_by_volume.as_dict() == {
"default": 50.0,
10.0: 40.0,
20.0: 30.0,
}
assert aspirate_properties.flow_rate_by_volume.as_dict() == {"default": 50.0}
assert aspirate_properties.pre_wet is True
assert aspirate_properties.mix.enabled is True
assert aspirate_properties.mix.repetitions == 3
Expand Down
43 changes: 19 additions & 24 deletions api/tests/opentrons/protocol_api_integration/test_liquid_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,50 @@
from opentrons.config import feature_flags as ff


@pytest.mark.ot2_only
@pytest.mark.ot3_only
@pytest.mark.parametrize(
"simulated_protocol_context", [("2.20", "OT-2")], indirect=True
"simulated_protocol_context", [("2.20", "Flex")], indirect=True
)
def test_liquid_class_creation_and_property_fetching(
decoy: Decoy,
mock_feature_flags: None,
simulated_protocol_context: ProtocolContext,
) -> None:
"""It should create the liquid class and provide access to its properties."""
decoy.when(ff.allow_liquid_classes(RobotTypeEnum.OT2)).then_return(True)
pipette_left = simulated_protocol_context.load_instrument(
"p20_single_gen2", mount="left"
)
pipette_right = simulated_protocol_context.load_instrument(
"p300_multi", mount="right"
decoy.when(ff.allow_liquid_classes(RobotTypeEnum.FLEX)).then_return(True)
pipette_load_name = "flex_8channel_50"
simulated_protocol_context.load_instrument(pipette_load_name, mount="left")
tiprack = simulated_protocol_context.load_labware(
"opentrons_flex_96_tiprack_50ul", "D1"
)
tiprack = simulated_protocol_context.load_labware("opentrons_96_tiprack_20ul", "1")

glycerol_50 = simulated_protocol_context.define_liquid_class("fixture_glycerol50")
water = simulated_protocol_context.define_liquid_class("water")

assert glycerol_50.name == "fixture_glycerol50"
assert glycerol_50.display_name == "Glycerol 50%"
assert water.name == "water"
assert water.display_name == "Water"

# TODO (spp, 2024-10-17): update this to use pipette's load name instead of pipette.name
# TODO (spp, 2024-10-17): update this to fetch pipette load name from instrument context
assert (
glycerol_50.get_for(
pipette_left.name, tiprack.load_name
water.get_for(
pipette_load_name, tiprack.load_name
).dispense.flow_rate_by_volume.default
== 50
)
assert (
glycerol_50.get_for(
pipette_left.name, tiprack.load_name
).aspirate.submerge.speed
water.get_for(pipette_load_name, tiprack.load_name).aspirate.submerge.speed
== 100
)

with pytest.raises(
ValueError,
match="No properties found for p300_multi in fixture_glycerol50 liquid class",
match="No properties found for non-existent-pipette in water liquid class",
):
glycerol_50.get_for(pipette_right.name, tiprack.load_name)
water.get_for("non-existent-pipette", tiprack.load_name)

with pytest.raises(AttributeError):
glycerol_50.name = "foo" # type: ignore
water.name = "foo" # type: ignore

with pytest.raises(AttributeError):
glycerol_50.display_name = "bar" # type: ignore
water.display_name = "bar" # type: ignore

with pytest.raises(ValueError, match="Liquid class definition not found"):
simulated_protocol_context.define_liquid_class("non-existent-liquid")
Expand All @@ -67,4 +62,4 @@ def test_liquid_class_creation_and_property_fetching(
def test_liquid_class_feature_flag(simulated_protocol_context: ProtocolContext) -> None:
"""It should raise a not implemented error without the allowLiquidClass flag set."""
with pytest.raises(NotImplementedError):
simulated_protocol_context.define_liquid_class("fixture_glycerol50")
simulated_protocol_context.define_liquid_class("water")
Loading

0 comments on commit 9fceef6

Please sign in to comment.