@@ -1205,6 +1205,7 @@ def transfer_with_liquid_class( # noqa: C901
1205
1205
starting_tip : Optional [WellCore ],
1206
1206
trash_location : Union [Location , TrashBin , WasteChute ],
1207
1207
return_tip : bool ,
1208
+ keep_last_tip : bool ,
1208
1209
) -> None :
1209
1210
"""Execute transfer using liquid class properties.
1210
1211
@@ -1404,15 +1405,14 @@ def _pick_up_tip() -> WellCore:
1404
1405
transfer_type = tx_comps_executor .TransferType .ONE_TO_ONE ,
1405
1406
tip_contents = post_asp_tip_contents ,
1406
1407
add_final_air_gap = (
1407
- False
1408
- if is_last_step and new_tip == TransferTipPolicyV2 .NEVER
1409
- else True
1408
+ False if is_last_step and keep_last_tip else True
1410
1409
),
1411
1410
trash_location = trash_location ,
1412
1411
)
1413
1412
prev_src = step_source
1414
1413
prev_dest = step_destination
1415
- if new_tip != TransferTipPolicyV2 .NEVER :
1414
+
1415
+ if not keep_last_tip :
1416
1416
_drop_tip ()
1417
1417
1418
1418
# TODO(spp, 2025-02-25): wire up return tip
@@ -1422,11 +1422,16 @@ def distribute_with_liquid_class( # noqa: C901
1422
1422
volume : float ,
1423
1423
source : Tuple [Location , WellCore ],
1424
1424
dest : List [Tuple [Location , WellCore ]],
1425
- new_tip : Literal [TransferTipPolicyV2 .NEVER , TransferTipPolicyV2 .ONCE ],
1425
+ new_tip : Literal [
1426
+ TransferTipPolicyV2 .NEVER ,
1427
+ TransferTipPolicyV2 .ONCE ,
1428
+ TransferTipPolicyV2 .ALWAYS ,
1429
+ ],
1426
1430
tip_racks : List [Tuple [Location , LabwareCore ]],
1427
1431
starting_tip : Optional [WellCore ],
1428
1432
trash_location : Union [Location , TrashBin , WasteChute ],
1429
1433
return_tip : bool ,
1434
+ keep_last_tip : bool ,
1430
1435
) -> None :
1431
1436
"""Execute a distribution using liquid class properties.
1432
1437
@@ -1438,7 +1443,10 @@ def distribute_with_liquid_class( # noqa: C901
1438
1443
dest: List of destination wells, with each well represented as a tuple of
1439
1444
types.Location and WellCore.
1440
1445
types.Location is only necessary for saving the last accessed location.
1441
- new_tip: Whether the transfer should use a new tip 'once' or 'never'.
1446
+ new_tip: Whether the transfer should use a new tip 'once', 'always' or 'never'.
1447
+ 'never': the transfer will never pick up a new tip
1448
+ 'once': the transfer will pick up a new tip once at the start of transfer
1449
+ 'always': the transfer will pick up a new tip before every aspirate
1442
1450
tiprack_uri: The URI of the tiprack that the transfer settings are for.
1443
1451
tip_drop_location: Location where the tip will be dropped (if appropriate).
1444
1452
@@ -1454,7 +1462,11 @@ def distribute_with_liquid_class( # noqa: C901
1454
1462
raise RuntimeError (
1455
1463
"No tipracks found for pipette in order to perform transfer"
1456
1464
)
1457
- assert new_tip in [TransferTipPolicyV2 .NEVER , TransferTipPolicyV2 .ONCE ]
1465
+ assert new_tip in [
1466
+ TransferTipPolicyV2 .NEVER ,
1467
+ TransferTipPolicyV2 .ONCE ,
1468
+ TransferTipPolicyV2 .ALWAYS ,
1469
+ ]
1458
1470
1459
1471
tiprack_uri_for_transfer_props = tip_racks [0 ][1 ].get_uri ()
1460
1472
working_volume = min (
@@ -1510,6 +1522,7 @@ def distribute_with_liquid_class( # noqa: C901
1510
1522
starting_tip = starting_tip ,
1511
1523
trash_location = trash_location ,
1512
1524
return_tip = return_tip ,
1525
+ keep_last_tip = keep_last_tip ,
1513
1526
)
1514
1527
return
1515
1528
@@ -1661,9 +1674,21 @@ def _pick_up_tip() -> WellCore:
1661
1674
and new_tip != TransferTipPolicyV2 .NEVER
1662
1675
and is_first_step
1663
1676
):
1677
+ # Do probing only once, regardless of whether you are coming back to refill
1678
+ # with a fresh tip since there's only one source well
1664
1679
enable_lpd = True
1665
1680
else :
1666
1681
enable_lpd = False
1682
+
1683
+ if not is_first_step and new_tip == TransferTipPolicyV2 .ALWAYS :
1684
+ _drop_tip ()
1685
+ last_tip_picked_up_from = _pick_up_tip ()
1686
+ tip_contents = [
1687
+ tx_comps_executor .LiquidAndAirGapPair (
1688
+ liquid = 0 ,
1689
+ air_gap = 0 ,
1690
+ )
1691
+ ]
1667
1692
with self .lpd_for_transfer (enable = enable_lpd ):
1668
1693
# Aspirate the total volume determined by the loop above
1669
1694
tip_contents = self .aspirate_liquid_class (
@@ -1693,9 +1718,7 @@ def _pick_up_tip() -> WellCore:
1693
1718
transfer_type = tx_comps_executor .TransferType .ONE_TO_MANY ,
1694
1719
tip_contents = tip_contents ,
1695
1720
add_final_air_gap = (
1696
- False
1697
- if is_last_step and new_tip == TransferTipPolicyV2 .NEVER
1698
- else True
1721
+ False if is_last_step and keep_last_tip else True
1699
1722
),
1700
1723
trash_location = trash_location ,
1701
1724
)
@@ -1708,17 +1731,15 @@ def _pick_up_tip() -> WellCore:
1708
1731
transfer_type = tx_comps_executor .TransferType .ONE_TO_MANY ,
1709
1732
tip_contents = tip_contents ,
1710
1733
add_final_air_gap = (
1711
- False
1712
- if is_last_step and new_tip == TransferTipPolicyV2 .NEVER
1713
- else True
1734
+ False if is_last_step and keep_last_tip else True
1714
1735
),
1715
1736
trash_location = trash_location ,
1716
1737
conditioning_volume = conditioning_vol ,
1717
1738
disposal_volume = disposal_vol ,
1718
1739
)
1719
1740
is_first_step = False
1720
1741
1721
- if new_tip != TransferTipPolicyV2 . NEVER :
1742
+ if not keep_last_tip :
1722
1743
_drop_tip ()
1723
1744
1724
1745
def _tip_can_hold_volume_for_multi_dispensing (
@@ -1748,17 +1769,28 @@ def consolidate_with_liquid_class( # noqa: C901
1748
1769
volume : float ,
1749
1770
source : List [Tuple [Location , WellCore ]],
1750
1771
dest : Union [Tuple [Location , WellCore ], TrashBin , WasteChute ],
1751
- new_tip : Literal [TransferTipPolicyV2 .NEVER , TransferTipPolicyV2 .ONCE ],
1772
+ new_tip : Literal [
1773
+ TransferTipPolicyV2 .NEVER ,
1774
+ TransferTipPolicyV2 .ONCE ,
1775
+ TransferTipPolicyV2 .ALWAYS ,
1776
+ ],
1752
1777
tip_racks : List [Tuple [Location , LabwareCore ]],
1753
1778
starting_tip : Optional [WellCore ],
1754
1779
trash_location : Union [Location , TrashBin , WasteChute ],
1755
1780
return_tip : bool ,
1781
+ keep_last_tip : bool ,
1756
1782
) -> None :
1757
1783
if not tip_racks :
1758
1784
raise RuntimeError (
1759
1785
"No tipracks found for pipette in order to perform transfer"
1760
1786
)
1761
- assert new_tip in [TransferTipPolicyV2 .NEVER , TransferTipPolicyV2 .ONCE ]
1787
+ # NOTE: Tip option of "always" in consolidate is equivalent to "after every dispense",
1788
+ # or more specifically, "before the next chunk of aspirates".
1789
+ assert new_tip in [
1790
+ TransferTipPolicyV2 .NEVER ,
1791
+ TransferTipPolicyV2 .ONCE ,
1792
+ TransferTipPolicyV2 .ALWAYS ,
1793
+ ]
1762
1794
tiprack_uri_for_transfer_props = tip_racks [0 ][1 ].get_uri ()
1763
1795
try :
1764
1796
transfer_props = liquid_class .get_for (
@@ -1860,7 +1892,7 @@ def _pick_up_tip() -> WellCore:
1860
1892
)
1861
1893
return tip_well
1862
1894
1863
- if new_tip == TransferTipPolicyV2 .ONCE :
1895
+ if new_tip in [ TransferTipPolicyV2 .ONCE , TransferTipPolicyV2 . ALWAYS ] :
1864
1896
last_tip_picked_up_from = _pick_up_tip ()
1865
1897
1866
1898
tip_contents = [
@@ -1872,6 +1904,7 @@ def _pick_up_tip() -> WellCore:
1872
1904
next_step_volume , next_source = next (source_per_volume_step )
1873
1905
is_first_step = True
1874
1906
is_last_step = False
1907
+ prev_src : Optional [tuple [Location , WellCore ]] = None
1875
1908
while not is_last_step :
1876
1909
total_dispense_volume = 0.0
1877
1910
vol_aspirate_combo = []
@@ -1895,6 +1928,21 @@ def _pick_up_tip() -> WellCore:
1895
1928
and is_first_step
1896
1929
):
1897
1930
enable_lpd = True
1931
+ elif not is_first_step and new_tip == TransferTipPolicyV2 .ALWAYS :
1932
+ _drop_tip ()
1933
+ last_tip_picked_up_from = _pick_up_tip ()
1934
+ tip_contents = [
1935
+ tx_comps_executor .LiquidAndAirGapPair (
1936
+ liquid = 0 ,
1937
+ air_gap = 0 ,
1938
+ )
1939
+ ]
1940
+ # Enable LPD if it's globally enabled, as long as
1941
+ # the next source is different from previous source
1942
+ enable_lpd = (
1943
+ self .get_liquid_presence_detection ()
1944
+ and prev_src != vol_aspirate_combo [0 ][1 ]
1945
+ )
1898
1946
else :
1899
1947
enable_lpd = False
1900
1948
@@ -1912,6 +1960,7 @@ def _pick_up_tip() -> WellCore:
1912
1960
),
1913
1961
current_volume = total_aspirated_volume ,
1914
1962
)
1963
+ prev_src = step_source
1915
1964
total_aspirated_volume += step_volume
1916
1965
is_first_step = False
1917
1966
enable_lpd = False
@@ -1922,14 +1971,11 @@ def _pick_up_tip() -> WellCore:
1922
1971
transfer_properties = transfer_props ,
1923
1972
transfer_type = tx_comps_executor .TransferType .MANY_TO_ONE ,
1924
1973
tip_contents = tip_contents ,
1925
- add_final_air_gap = (
1926
- False
1927
- if is_last_step and new_tip == TransferTipPolicyV2 .NEVER
1928
- else True
1929
- ),
1974
+ add_final_air_gap = (False if is_last_step and keep_last_tip else True ),
1930
1975
trash_location = trash_location ,
1931
1976
)
1932
- if new_tip != TransferTipPolicyV2 .NEVER :
1977
+
1978
+ if not keep_last_tip :
1933
1979
_drop_tip ()
1934
1980
1935
1981
def _get_location_and_well_core_from_next_tip_info (
@@ -2011,10 +2057,7 @@ def aspirate_liquid_class(
2011
2057
location = prep_location ,
2012
2058
)
2013
2059
last_liquid_and_airgap_in_tip .air_gap = 0
2014
- if (
2015
- transfer_type != tx_comps_executor .TransferType .MANY_TO_ONE
2016
- and self .get_liquid_presence_detection ()
2017
- ):
2060
+ if self .get_liquid_presence_detection ():
2018
2061
self .liquid_probe_with_recovery (
2019
2062
well_core = source_well , loc = prep_location
2020
2063
)
0 commit comments