I’ve been surprised to see that my Flex 8-channel 50ul appears to hold tips at different heights. For a long time I assumed the difference was in the tips themselves, but I’ve measured them with digital calipers and they’re the same. The difference appears to be that the tip-release metal piece has some molding to release inner tips before outer tips, and since it rests on the tips it slightly pushes inner tips further down. Has anyone else noticed this? I find it relatively shocking and it leads to very bad partial pipetting precision performance.
I’m not sure how obvious it is in this picture (the right tip is lower) but it’s very real:
There was a suggestion to use SINGLE and then just position the pipette head over a partial column of tips, which would cause the head to pick up more than one tip. So I was doing that
Doing that causes this problem because SingleA1 in the pipette definition configures different distances and tipOverlaps for SingleA1 vs eg H1toE1
I wasted a loooooot of time before I dove deep enough into the Opentrons codebase to figure this out (ps thank you Opentrons for having an open source codebase)
Opentrons FAS says that they put in a feature request to add A1toB1 etc but gave no ETA. I doubt it will land any time soon. They also asked why I care, which is a funny question because you cannot use the 8-channel multichannel with only 3 tips on the thermocycler trying to hit well A1 when start="H1". I had thought the why would be fairly obvious but I get the sense that no one else actually uses PARTIAL_COLUMN
Anyway, in the absence of that feature request being implemented, I came up with a hack to get PARTIAL_COLUMN working with start="A1".
def patch_pipette_loading():
from opentrons_shared_data.pipette.pipette_definition import PipetteConfigurations
original_model_validate = PipetteConfigurations.model_validate
def evil_model_validate(obj, *args, **kwargs):
valid_maps = obj["validNozzleMaps"]["maps"]
configurations = obj["pickUpTipConfigurations"]["pressFit"]["configurationsByNozzleMap"]
if obj["channels"] == 8:
start = "A1"
ends = ["B1", "C1", "D1", "E1", "F1", "G1"]
for i, end in enumerate(ends):
key = f"{start}to{end}"
if key in valid_maps:
continue
reverse = f"H1to{ends[len(ends) - 1 - i]}"
if config := configurations.get(reverse):
configurations[key] = config
valid_maps[key] = [start] + ends[:i + 1]
return original_model_validate(obj, *args, **kwargs)
PipetteConfigurations.model_validate = evil_model_validate
patch_pipette_loading()
Which worked great, until I noticed I was getting super wonky z-height accuracy on aspirate/dispense. I used liquid level detection to detect the same liquid heights across every tip configuration on the multi-channel and it seems quite inconsistent (ignore the outliers). I assume it’s something to do with my hack but don’t have the time or energy to debug further right now.