Skip to content

Commit b4a03e2

Browse files
authored
support non-full tip racks with 96 head pickup (#548)
1 parent 4b9257c commit b4a03e2

File tree

6 files changed

+50
-10
lines changed

6 files changed

+50
-10
lines changed

pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,9 +2181,15 @@ async def pick_up_tips96(
21812181
"""Pick up tips using the 96 head."""
21822182
assert self.core96_head_installed, "96 head must be installed"
21832183
tip_spot_a1 = pickup.resource.get_item("A1")
2184-
tip_a1 = tip_spot_a1.get_tip()
2185-
assert isinstance(tip_a1, HamiltonTip), "Tip type must be HamiltonTip."
2186-
ttti = await self.get_or_assign_tip_type_index(tip_a1)
2184+
prototypical_tip = None
2185+
for tip_spot in pickup.resource.get_all_items():
2186+
if tip_spot.has_tip():
2187+
prototypical_tip = tip_spot.get_tip()
2188+
break
2189+
if prototypical_tip is None:
2190+
raise ValueError("No tips found in the tip rack.")
2191+
assert isinstance(prototypical_tip, HamiltonTip), "Tip type must be HamiltonTip."
2192+
ttti = await self.get_or_assign_tip_type_index(prototypical_tip)
21872193
position = tip_spot_a1.get_absolute_location() + tip_spot_a1.center() + pickup.offset
21882194
z_deposit_position += round(pickup.offset.z * 10)
21892195

@@ -2213,8 +2219,8 @@ async def drop_tips96(
22132219
"""Drop tips from the 96 head."""
22142220
assert self.core96_head_installed, "96 head must be installed"
22152221
if isinstance(drop.resource, TipRack):
2216-
tip_a1 = drop.resource.get_item("A1")
2217-
position = tip_a1.get_absolute_location() + tip_a1.center() + drop.offset
2222+
tip_spot_a1 = drop.resource.get_item("A1")
2223+
position = tip_spot_a1.get_absolute_location() + tip_spot_a1.center() + drop.offset
22182224
else:
22192225
position = self._position_96_head_in_resource(drop.resource) + drop.offset
22202226

pylabrobot/liquid_handling/backends/hamilton/STAR_tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
Lid,
2525
ResourceStack,
2626
no_volume_tracking,
27+
set_tip_tracking,
2728
)
2829
from pylabrobot.resources.hamilton import STF, STARLetDeck
2930

@@ -264,6 +265,8 @@ def __init__(self, name: str):
264265
self.STAR._iswap_parked = True
265266
await self.lh.setup()
266267

268+
set_tip_tracking(enabled=False)
269+
267270
async def asyncTearDown(self):
268271
await self.lh.stop()
269272

pylabrobot/liquid_handling/backends/hamilton/vantage_backend.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -946,9 +946,15 @@ async def pick_up_tips96(
946946
):
947947
# assert self.core96_head_installed, "96 head must be installed"
948948
tip_spot_a1 = pickup.resource.get_item("A1")
949-
tip_a1 = tip_spot_a1.get_tip()
950-
assert isinstance(tip_a1, HamiltonTip), "Tip type must be HamiltonTip."
951-
ttti = await self.get_or_assign_tip_type_index(tip_a1)
949+
prototypical_tip = None
950+
for tip_spot in pickup.resource.get_all_items():
951+
if tip_spot.has_tip():
952+
prototypical_tip = tip_spot.get_tip()
953+
break
954+
if prototypical_tip is None:
955+
raise ValueError("No tips found in the tip rack.")
956+
assert isinstance(prototypical_tip, HamiltonTip), "Tip type must be HamiltonTip."
957+
ttti = await self.get_or_assign_tip_type_index(prototypical_tip)
952958
position = tip_spot_a1.get_absolute_location() + tip_spot_a1.center() + pickup.offset
953959
offset_z = pickup.offset.z
954960

pylabrobot/liquid_handling/backends/hamilton/vantage_tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
TIP_CAR_480_A00,
1111
Coordinate,
1212
Cor_96_wellplate_360ul_Fb,
13+
set_tip_tracking,
1314
)
1415
from pylabrobot.resources.hamilton import VantageDeck
1516

@@ -259,6 +260,8 @@ async def asyncSetUp(self):
259260

260261
await self.lh.setup()
261262

263+
set_tip_tracking(enabled=False)
264+
262265
async def asyncTearDown(self):
263266
await self.lh.stop()
264267

pylabrobot/liquid_handling/liquid_handler.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,8 +1243,11 @@ async def pick_up_tips96(
12431243
for i, tip_spot in enumerate(tip_rack.get_all_items()):
12441244
if not does_tip_tracking() and self.head96[i].has_tip:
12451245
self.head96[i].remove_tip()
1246-
self.head96[i].add_tip(tip_spot.get_tip(), origin=tip_spot, commit=False)
1247-
if does_tip_tracking() and not tip_spot.tracker.is_disabled:
1246+
# only add tips where there is one present.
1247+
# it's possible only some tips are present in the tip rack.
1248+
if tip_spot.has_tip():
1249+
self.head96[i].add_tip(tip_spot.get_tip(), origin=tip_spot, commit=False)
1250+
if does_tip_tracking() and not tip_spot.tracker.is_disabled and tip_spot.has_tip():
12481251
tip_spot.tracker.remove_tip()
12491252

12501253
pickup_operation = PickupTipRack(resource=tip_rack, offset=offset)
@@ -1302,6 +1305,9 @@ async def drop_tips96(
13021305

13031306
# queue operation on all tip trackers
13041307
for i in range(96):
1308+
# it's possible not every channel on this head has a tip.
1309+
if not self.head96[i].has_tip:
1310+
continue
13051311
tip = self.head96[i].get_tip()
13061312
if tip.tracker.get_used_volume() > 0 and not allow_nonzero_volume:
13071313
error = f"Cannot drop tip with volume {tip.tracker.get_used_volume()} on channel {i}"

pylabrobot/liquid_handling/liquid_handler_tests.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,22 @@ async def test_aspirate_single_reservoir(self):
10161016
await self.lh.pick_up_tips96(self.tip_rack)
10171017
await self.lh.aspirate96(reagent_reservoir.get_item("A1"), volume=100)
10181018

1019+
async def test_pick_up_tips96_incomplete_rack(self):
1020+
set_tip_tracking(enabled=True)
1021+
1022+
# Test that picking up tips from an incomplete rack works
1023+
self.tip_rack.fill()
1024+
self.tip_rack.get_item("A1").tracker.remove_tip()
1025+
1026+
await self.lh.pick_up_tips96(self.tip_rack)
1027+
1028+
# Check that the tips were picked up correctly
1029+
self.assertFalse(self.lh.head96[0].has_tip)
1030+
for i in range(1, 96):
1031+
self.assertTrue(self.lh.head96[i].has_tip)
1032+
1033+
set_tip_tracking(enabled=False)
1034+
10191035

10201036
class TestLiquidHandlerVolumeTracking(unittest.IsolatedAsyncioTestCase):
10211037
async def asyncSetUp(self):

0 commit comments

Comments
 (0)