Skip to content

Expose Well.bottom() & Well.top() #141

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

Closed

Conversation

BioCam
Copy link
Contributor

@BioCam BioCam commented May 25, 2024

Hi everyone,

Let's expose some of that Opentrons wisdom to PLR:

In this PR I added two small methods to the Well class in resources/well.py:

  1. Well.bottom(z_offset=0.0) ... convenient method to compute the absolute Coordinate of the 2D central bottom of a well, with the added functionality that a z_offset can directly be declared.
  2. Well.top(z_offset=0.0) ... similar as above just for the 2D central top of a well.

Why

With Well.get_absolute_location() already in place there is a valid question as to why generate these two methods.
The main reason is convenience and clarity to facilitate plate definition creation.

Currently, when defining labware there is no PLR probe_z_height_using_channel function (which I created in #69) that works with standard plates and tubes because probe_z_height_using_channel detects items using capacitative liquid level detection but cannot detect plates due to their material being highly electrically resistant.

As a consequence, the dimensions of a plate have to be manually evaluated. In particular the depth of a well represents a small but important challenge: it is arguably one of the most important definitions to get right to avoid crashes and minimise dead volume.

It is therefore important that the machine used enables moving a tip quickly to the bottom of a well and to the top of a well, back and forth, with the ability to efficiently add a z_offset to each Coordinate.
(A laminated piece of paper is very effective at testing whether Well.top() is indeed the top of the well, and when moving the tip to Well.bottom(), the tip can be slightly moved by hand, sensing slight resistance to the move, i.e. the tip hasn't crashed.)

Example Use Case

A step-by-step guide to conveniently and transparently use these little methods:

# Define plate carrier & assign to deck
plt_carrier_1 = PLT_CAR_L5AC_A00(name='plt_carrier_1')
lh.deck.assign_child_resource(plt_carrier_1, rails=1)

# Define & assign plate to carrier site
plt_carrier_1[0] = ThermoScientific_96_1200ul_Rd_1 = ThermoScientific_96_1200ul_Rd(name="ThermoScientific_96_1200ul_Rd")

# Pick up tip (any way you want to, there are many options to automate tip selection)

# Safety first :)
await lh.backend.move_all_channels_in_z_safety() # safe z height

y_pos_start = 10    # safe y positions (for an 8-channel STAR system, be careful with 12- & 16-channel systems)
for channel_idx in reversed(range(0,8)):
    print(f"channel {channel_idx} -> {y_pos_start}")
    await lh.backend.move_channel_y(y=y_pos_start, channel=channel_idx)
    y_pos_start += 9
# channel 7 -> 10
# channel 6 -> 19
# channel 5 -> 28
# channel 4 -> 37
# channel 3 -> 46
# channel 2 -> 55
# channel 1 -> 64
# channel 0 -> 73

# Move tip to plate well for definition testing
test_well_bottom_coordinate = ThermoScientific_96_1200ul_Rd['H1'][0].bottom()
# Coordinate(x=117.75, y=82.95, z=186.35)

test_well_top_coordinate = ThermoScientific_96_1200ul_Rd_1['H1'][0].top()
# Coordinate(x=117.75, y=82.95, z=206.85)

# note channel_0 (most backwards channel) can currently move in y to any well without crashing into another channel
# do not use any other channel, because it will crash :)

await lh.backend.move_channel_x(x=test_well_bottom_coordinate.x, channel=0)
await lh.backend.move_channel_y(y=test_well_bottom_coordinate.y, channel=0)


# Probe correctness of the top of the well: place laminated piece of paper on top of well and
# move channel_0 incrementally until paper cannot be moved without some resistance

# await lh.backend.move_channel_z(z=test_well_top_coordinate.z+2, channel=0)
# await lh.backend.move_channel_z(z=test_well_top_coordinate.z+1, channel=0)
# await lh.backend.move_channel_z(z=test_well_top_coordinate.z, channel=0)
...
await lh.backend.move_channel_z(z=test_well_top_coordinate.z-3.2, channel=0)

# => you found that the top of the well is actually 3.2 mm below where PLR thinks it is


# Probe correctness of the bottom of the well: move tip a couple of mm above the bottom
# and incrementally move tip down in z until manual tip "jiggling" encounters some resistance

# await lh.backend.move_channel_z(z=test_well_bottom_coordinate.z+2, channel=0)
# await lh.backend.move_channel_z(z=test_well_bottom_coordinate.z+1, channel=0)
await lh.backend.move_channel_z(z=test_well_bottom_coordinate.z, channel=0)

# => you found that the bottom of the well is correctly defined

# Calculate the true depth of the well
(test_well_top_coordinate.z-3.2) - test_well_bottom_coordinate.z

# => update Plate definition based on your labware testing results 
# & submit a pull request to share your findings with the community

I hope this helps PLR users to create more robust labware definitions.

@BioCam BioCam closed this May 25, 2024
@BioCam BioCam force-pushed the Implement-Well.bottom-&-Well.top branch from a2d0af5 to f84b561 Compare May 25, 2024 19:01
@BioCam
Copy link
Contributor Author

BioCam commented May 25, 2024

Git struggled to resolve the changes from the previous PR which was merged 1h ago with the current additions, so I am creating a new PR for it.

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.

1 participant