Skip to content
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

feat: Tracking Liquid Level of Labware #7101

Open
MarcMarc94 opened this issue Dec 4, 2020 · 6 comments
Open

feat: Tracking Liquid Level of Labware #7101

MarcMarc94 opened this issue Dec 4, 2020 · 6 comments
Labels
feature-request A request for a new feature or a change that isn't a bug. May require further triage or scoping. robot-svcs Falls under the purview of the Robot Services squad (formerly CPX, Core Platform Experience).

Comments

@MarcMarc94
Copy link

Overview

Track liquid level in various containers so pipettes are not submerged in liquid.

Implementation details

We’ve got a problem with the most recent version of the robot. We used to assign additional properties to our labware which doesn’t work anymore (#7048). Before we go through the very laborious process to rewrite all our protocols, I thought it might be worth describing our intentions to add these additional properties in the first place and maybe you have a much better idea to address the problem.

The main problem is that we need to track liquid levels in the various containers. E.g. we need to know how full a 15 mL falcon tube is. Otherwise the robot will always go straight to the bottom. If the falcon is full however, this leads to the whole pipette being submerged and also the pipette spilling the contents all over the robot. It is also not possible to tell the robot to always go to the top since the liquid level drops over the course of the protocol and at some point it couldn’t aspirate anymore because the tip wouldn’t reach the liquid. That’s why we implemented a dynamic liquid tracking and height calculation to tell the robot always the correct height to aspirate and dispense. To achieve that we assigned additional properties to all the labware. And as mentioned before, this does not work anymore.
My question is now, is there anything in the most recent API that could help us solve this problem? E.g. is there a liquid level tracking function or something similar implemented?

Acceptance criteria

Robot tracks liquid levels of all containers (i.e. Falcon tubes, Eppendorf tubes etc.). According to this liquid level, pipettes are moving always just underneath the surface of the liquid to aspirate and don't move completely down.

@MarcMarc94 MarcMarc94 added the feature-request A request for a new feature or a change that isn't a bug. May require further triage or scoping. label Dec 4, 2020
@mcous
Copy link
Contributor

mcous commented Dec 4, 2020

Thanks for the feature request @MarcMarc94! This is something that's been on our minds for a long time, but is prohibitively difficult in the current protocol running architecture. Work on a replacement architecture, called the Protocol Engine has started, but is still in its very early stages.

Depending on which liquid handling methods you are using, with various wrapper functions you might be able to implement the same sort of functionality in user-land before the functionality lands in the core. The broad stokes of how liquid level tracking could/would work in the new architecture:

  1. The Protocol Engine maintains a dict mapping amount of liquid in each well by reference
  2. The Protocol API has functionality added to allow the initial liquid state of a given labware to be specified to populate that mapping at the beginning of the protocol
  3. Aspirates, dispenses, etc. result in updates to the liquid map
  4. Helper functions exist to calculate the liquid top:
    • Given labware / well reference
    • Pull well geometry from the labware defintion
    • Pull liquid amount from the liquid map
    • Calculate a liquid height estimate based on well shape (circular or rectangular) and 2D dimensions

TL;DR: This is an important feature that we feel technically blocked on, so we're working on revamping our protocol running core to unblock this feature. Once it's unblocked, we'll be able to prioritize / estimate actually building it. Until then, given the complexity of your protocols, this may be something you can implement yourself

@JRT3
Copy link

JRT3 commented Dec 21, 2020

#Hello - I have implemented a simple version of this in my protocols using the following functions:

#These functions roughly estimate the heights of the water columns in 2ml snap-cap and pcr strip tubes so that the tip can be lowered as the water level drops.
#This also helps keep the robot from dunking the tip, which reduces chance of unwanted carryover
#For 2ml snap cap tubes that are approximately a cylinder Height = volume ul / pi * radius^2
#Note that the equation for V-bottom PCR tubes could be better, but I didn't have time to find an equation with better fit.

import math
res_vol = 1000

#Below function tracks water volume it 2ml snap-cap tubes initial volume must be specified volume must be updated with #each pipette aspiration.

def res_tip_height(res_vol):
height = (res_vol/(3.14159*((8/2)**2)))-5
if height >= 2:
	return height
else:
	return 2

#Below function tells robot how far down tip should go in PCR tubes when transferring volumes this was needed to prevent
#spill-over when transferring large volumes (~>150ul).

def pcr_tip_height(vol):
    height = math.log2(vol/(3.14159*((1.8/2)**2)))*2.5    
    if height >= 1:
        return height
    else:
        return 1

#example for implementing during aspiration and dispense methods:
pipette20.pick_up_tip()
pipette20.aspirate(vol, plate1[res_loc_list[res_num-1]].bottom(res_tip_height(res_vol)), rate=2.0)
pipette20.dispense(vol, plate2[loc].bottom(pcr_tip_height(vol)), rate=2.0)
pipette20.blow_out()
pipette20.drop_tip() #drop in the fixed trash on the deck
res_vol-=vol #this line updates reservoir volume

@James-Kitson
Copy link

James-Kitson commented Jan 12, 2021

I'm also interested in this sort of feature as I've got lots of plate filling type operations I'd like to do (prefilling plates of SPRI beads, bulk setup of premade PCR plates etc).

I've been implementing something similar to @JRT3 where I create values for the barrel length and tip length of wells then model them as either a cylinder plus a cone or a cuboid plus a pyramid and calculate the liquid height change based on the aspirate volume. I then reset the aspirate height for the next loop iteration. This generally works well for a series of aspirates and dispenses but not distribute. The main problems with doing this across plates using a series of aspirates and dispenses are:

  1. Speed
  2. Targeting the dispense wells across lots of plates (e.g. the code below does not work with aspirate and dispense as the location generates a TypeError)

As an interim suggestion for the next minor update, rather than maintaining a dict of liquid volumes that get updated, could there be an option in distribute to step down by a percentage of the well height for each aspirate that simply stops at the normal bottom position? It's not perfect as sometime's it'll drop a bit more than you'd like and it requires a little bit of thought to set up (i.e. the user will have to think about how many aspirates they will likely generate from the command and set the values accordingly) but it would be a useful improvement.

E.g. if I want to distribute to 5 plates:

spri_plate_name = "whatever_Im_distributing_to"
dest_plates = [protocol.load_labware(spri_plate_name, str(slot))
      for slot in [available_slots[i] for i in range(number_of_destination_plates)]]

all_dests = [well for plate in dest_plates for well in plate.rows('A')]

for d in all_dests:
        left_pipette.distribute(spri_vol, beads, d, touch_tip = True, aspirate_height = -0.1)

Where aspirate_height = -0.1 tells the opentrons to drop the aspirate height by 10% of the well height each aspirate starting at whatever is supplied by left_pipette.well_bottom_clearance.aspirate =

@mcous mcous added the robot-svcs Falls under the purview of the Robot Services squad (formerly CPX, Core Platform Experience). label Feb 4, 2021
@JohnGoertz
Copy link

I have the same issue, albeit a slightly different use-case. I assemble PCR master mixes in the 2mL snapcaps, mix them, then dispense. I need to ensure the pipette tip is near the liquid level to allow efficient mixing, but not so high that it ends up aspirating air. I also need to ensure that, during dispense, the tip doesn't descend too deep into the liquid, as this causes the liquid to bead up around the tip, too high and too slow to be knocked off with "touch tip".

If anyone finds it useful, until an official update comes around, here are the wrappers I've used to enable rudimentary volume and height tracking: https://gist.github.com/JohnGoertz/71f8e8a2fd5aee056983b75609a97d5a

There are also other utility functions in there, such as retrieving a range of wells (e.g, A1 through D8) and relative navigation between wells (A1 + 5 = A6, A6 - 5 = A1, A1 * 2 = C1, C1 / 2 = A1).

@silvtal
Copy link

silvtal commented Oct 20, 2021

I made this function for tracking volume on a 15 mL Falcon, I guess it's similar to those posted before. Bear in mind when updating the volume that this function takes mL

    def falcon_tip_height(vol, min_height = 0.5, r = 15/2, depth = 20):
        # vol is in mL
        # r is the radius in mm
        # min_height is the minimum height in mm
        # depth is how deep you want the tip to enter the liquid (mm)

        if (vol < 2):
            return min_height

        else:        
            base = 27 # the height in mm from 0 to 2 mL is different
            # (that's why we substract 2 here:)
            vol2 = (vol - 2) * 1000 # from mL to mm**3

            # now we do V = h * pi * r**2    (plus the base height)
            height = base + vol2/(3.14159*((r)**2)) - depth

            if (height >= min_height):
                return height
            else:
                return min_height    

@danolson1
Copy link

As a temporary measure, what about including a current_volume parameter in the well object? It would avoid the need to have a separate data structure to keep track of well volumes for people who are implementing their own volume/level tracking, and could be extended in the future.

@mcous, what is the "Liquid map" you referred to in your post?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request A request for a new feature or a change that isn't a bug. May require further triage or scoping. robot-svcs Falls under the purview of the Robot Services squad (formerly CPX, Core Platform Experience).
Projects
None yet
Development

No branches or pull requests

7 participants