diff --git a/pylabrobot/hamilton/liquid_class_resolver.py b/pylabrobot/hamilton/liquid_class_resolver.py index 20acc47b396..de64a2c8a77 100644 --- a/pylabrobot/hamilton/liquid_class_resolver.py +++ b/pylabrobot/hamilton/liquid_class_resolver.py @@ -33,6 +33,7 @@ DISPENSE_CLASS_ATTRIBUTES: Dict[str, Callable[[HamiltonLiquidClass], float]] = { "flow_rates": lambda hlc: hlc.dispense_flow_rate, "transport_air_volumes": lambda hlc: hlc.dispense_air_transport_volume, + "cut_off_speeds": lambda hlc: hlc.dispense_stop_flow_rate, "stop_back_volumes": lambda hlc: hlc.dispense_stop_back_volume, "blow_out_air_volumes": lambda hlc: hlc.dispense_blow_out_volume, "settling_times": lambda hlc: hlc.dispense_settling_time, diff --git a/pylabrobot/hamilton/star/driver/features/head96.py b/pylabrobot/hamilton/star/driver/features/head96.py index 81d9babed1b..b43fce5ec22 100644 --- a/pylabrobot/hamilton/star/driver/features/head96.py +++ b/pylabrobot/hamilton/star/driver/features/head96.py @@ -2730,7 +2730,7 @@ async def dispense( bottom when None. transport_air_volume: air pushed out before the liquid, in uL. The class's, else 0.0, when None. - cut_off_speed: the flow the dispense ends at, in uL/s. 5.0 when None. + cut_off_speed: the flow the dispense ends at, in uL/s. The class's, else 5.0, when None. stop_back_volume: drawn back after the dispense, in uL. The class's, else 0.0, when None. surface_following_distance: how far the tips follow the rising surface, in mm. auto_surface_following: follow by how far the dispensed liquid raises the surface found, or @@ -2800,6 +2800,7 @@ def by_class(name: str, given: Optional[float]) -> Optional[float]: flow_rate = by_class("flow_rates", flow_rate) transport_air = by_class("transport_air_volumes", transport_air_volume) or 0.0 + cut_off_speed = by_class("cut_off_speeds", cut_off_speed) stop_back = by_class("stop_back_volumes", stop_back_volume) or 0.0 blow_out_air = by_class("blow_out_air_volumes", blow_out_air_volume) or 0.0 settling_time = by_class("settling_times", settling_time) diff --git a/pylabrobot/hamilton/star/driver/features/head_tests.py b/pylabrobot/hamilton/star/driver/features/head_tests.py index 420f16e6169..94309181b34 100644 --- a/pylabrobot/hamilton/star/driver/features/head_tests.py +++ b/pylabrobot/hamilton/star/driver/features/head_tests.py @@ -1136,6 +1136,18 @@ async def test_capacitive_dispense_into_a_plate_books_each_well(self): others = {w.tracker.get_used_volume() for w in self.plate.get_all_items()[1:]} self.assertEqual(others, {200.0}) + async def test_the_cut_off_speed_is_the_class_s_unless_given(self): + from pylabrobot.hamilton.star.liquid_classes import get_star_liquid_class + from pylabrobot.resources.liquid import Liquid + + jet = get_star_liquid_class(300.0, True, True, True, Liquid.WATER, True, True) + assert jet is not None and jet.dispense_stop_flow_rate != 5.0 + await self.head.aspirate(self.plate, piston_volume=150.0) + await self.head.dispense(self.plate, 30.0, jet=True, blow_out=True) + self.assertEqual(self.sent[1][1]["cut_off_speed"], round(jet.dispense_stop_flow_rate * 10)) + await self.head.dispense(self.plate, 30.0, jet=True, blow_out=True, cut_off_speed=4.5) + self.assertEqual(self.sent[2][1]["cut_off_speed"], 45) + async def test_no_liquid_under_capacitive_raises_and_the_head_goes_up(self): with self.assertRaises(RuntimeError): await self.head.aspirate(self.trough, piston_volume=20.0, lld_mode=LLDMode.CAPACITIVE) diff --git a/pylabrobot/hamilton/star/driver/features/pipettes.py b/pylabrobot/hamilton/star/driver/features/pipettes.py index 931221ef832..cb1793aca38 100644 --- a/pylabrobot/hamilton/star/driver/features/pipettes.py +++ b/pylabrobot/hamilton/star/driver/features/pipettes.py @@ -6897,7 +6897,7 @@ async def dispense( cavity bottom plus the offset's z when None. transport_air_volumes: air drawn after each dispense, in uL, pushed out ahead of the next. The class's, else 0.0, when None. - cut_off_speeds: the flow each dispense ends at, in uL/s. 5.0 when None. + cut_off_speeds: the flow each dispense ends at, in uL/s. The class's, else 5.0, when None. stop_back_volumes: drawn back after each dispense, in uL. The class's, else 0.0, when None. surface_following_distances: how far each tip follows the rising surface, in mm. 0.0 when None. @@ -7004,7 +7004,7 @@ def by_class(name: str, given: Optional[Sequence[Any]]) -> Optional[List[Any]]: per_container_settings = { "flow_rates": by_class("flow_rates", flow_rates), "transport_air_volumes": by_class("transport_air_volumes", transport_air_volumes), - "cut_off_speeds": per_container("cut_off_speeds", cut_off_speeds, n), + "cut_off_speeds": by_class("cut_off_speeds", cut_off_speeds), "stop_back_volumes": by_class("stop_back_volumes", stop_back_volumes), "blow_out_air_volumes": by_class("blow_out_air_volumes", blow_out_air_volumes), "immersion_depths": per_container("immersion_depths", immersion_depths, n), diff --git a/pylabrobot/hamilton/star/driver/features/pipettes_tests.py b/pylabrobot/hamilton/star/driver/features/pipettes_tests.py index 5f6b854bf1d..53ddd0ba22c 100644 --- a/pylabrobot/hamilton/star/driver/features/pipettes_tests.py +++ b/pylabrobot/hamilton/star/driver/features/pipettes_tests.py @@ -1746,6 +1746,25 @@ async def test_300_and_1000_uL_tips_draw_and_dispense_by_their_class(self): ): self.assertEqual(self._field(sent[2], name), [round(value * 10)] * 2, name) + async def test_the_cut_off_speed_is_the_class_s_unless_given(self): + from pylabrobot.hamilton.star.liquid_classes import get_star_liquid_class + from pylabrobot.resources.hamilton import hamilton_96_tiprack_1000uL + from pylabrobot.resources.liquid import Liquid + + driver, plate, sent = await self._channels_carrying(hamilton_96_tiprack_1000uL) + pipettes = driver.pipettes + assert pipettes is not None + jet = get_star_liquid_class(1000.0, False, True, False, Liquid.WATER, True, True) + assert jet is not None and jet.dispense_stop_flow_rate != 5.0 + wells = [plate.get_well("A1"), plate.get_well("B1")] + await pipettes.aspirate(wells, piston_volumes=[300.0, 300.0], liquid_heights=[3.0, 3.0]) + await pipettes.dispense(wells, [50.0, 50.0], jet=[True, True], blow_out=[True, True]) + self.assertEqual(self._field(sent[1], "ss"), [round(jet.dispense_stop_flow_rate * 10)] * 2) + await pipettes.dispense( + wells, [50.0, 50.0], jet=[True, True], blow_out=[True, True], cut_off_speeds=[4.5, 4.5] + ) + self.assertEqual(self._field(sent[2], "ss"), [45, 45]) + async def test_a_50_uL_tip_has_no_class_without_blow_out(self): from pylabrobot.resources.hamilton import hamilton_96_tiprack_50uL