The mix, transfer with mix_before or mix_after args, and aspirate functions throw "Cannot aspirate more than pipette max volume" even if the volume is less than or equal to pipette.max_volume.
The transfer method throws AssertionError: Cannot aspirate more than pipette max volume when mix_before or mix_after is specified, even if I pass in pipette.max_volume as the second parameter of the tuple.
Using the atomic commands mix and aspirate throw the same error.
# throws AssertionError: Cannot aspirate more than pipette max volume
pipette.transfer(v, source, dest,
new_tip='never',
mix_before=(5, pipette.max_volume),
mix_after=(5, pipette.max_volume),
blow_out=True)
or
pipette.mix(repetitions=5, volume=pipette.max_volume)
or
pipette.aspirate(volume=pipette.max_volume, location=source[0])
Error occurs both on the command line using opentrons_simulate on MacOS and on the Opentrons app on Windows.
No errors should be thrown
鈹咺ssue is synchronized with this Wrike Task by Unito
Was not able to reproduce on 3.18.1
metadata = {'apiLevel': '2.0'}
### Run Protocol ###
def run(protocol: protocol_api.ProtocolContext):
# tipracks
tiprack_multi = protocol.load_labware('opentrons_96_tiprack_300ul', '10')
tiprack_single = protocol.load_labware('opentrons_96_tiprack_1000ul', '11')
# labware
plate = protocol.load_labware('nest_96_wellplate_100ul_pcr_full_skirt', '1')
# pipettes
p_multi = protocol.load_instrument('p300_multi', 'right', tip_racks=[tiprack_multi])
p_single = protocol.load_instrument('p1000_single', 'left', tip_racks=[tiprack_single])
# commands
protocol.comment("single Max vol is: {}".format(p_single.max_volume))
protocol.comment("multi Max vol is: {}".format(p_multi.max_volume))
p_single.pick_up_tip()
p_single.aspirate(volume=p_single.max_volume, location=plate['A1'])
p_single.dispense(volume=p_single.max_volume)
p_single.mix(volume=p_single.max_volume, location=plate['A1'], repetitions=1)
p_single.transfer(p_single.max_volume, plate['A1'], plate['A2'],
new_tip='never',
mix_before=(5, p_single.max_volume),
mix_after=(5, p_single.max_volume),
blow_out=True)
p_multi.pick_up_tip()
p_multi.aspirate(volume=p_multi.max_volume, location=plate['A1'])
p_multi.dispense(volume=p_multi.max_volume)
p_multi.mix(volume=p_multi.max_volume, location=plate['A1'], repetitions=1)
p_multi.transfer(p_multi.max_volume, plate['A1'], plate['A2'],
new_tip='never',
mix_before=(5, p_multi.max_volume),
mix_after=(5, p_multi.max_volume),
blow_out=True)
Simulates successfully.
@dany-fu: Could you post your full protocol file? I've seen this error before when the wrong tips were accidentally loaded.
@SyntaxColoring thanks for the hint.
It looks like the problm is that I'm loading opentrons_96_filtertiprack_200ul into a p300_multi_gen2 pipette (I have to use the 200 because the 300 does not have a filtered version). So when I call pipette.max_volume it returns 275 which exceeds the tip's max volume. That was a fundamental misunderstanding on my part about what max_volume actually measured.
I tested this by loading in opentrons_96_tiprack_300ul and my protocol ran without errors.
For now, I manually lowered the max_volume to 175, but is there a programmatic way of getting the tip's max volume? It threw the same error if I specify 200, so I just guessed 175. But more importantly, it's being used in a function that's potentially reused across different tip sizes.
@dany-fu Sorry for the delayed reply!
So when I call pipette.max_volume it returns 275 which exceeds the tip's max volume. That was a fundamental misunderstanding on my part about what max_volume actually measured.
Yeah, we should clarify that in the documentation. I've added it to our to-do list.
is there a programmatic way of getting the tip's max volume?
I don't believe there's an official way, but there might be a hacky way if you're willing to rely on internal, unsupported implementation details. I haven't had a change to look into this very deeply yet.
It threw the same error if I specify 200
I was unable to reproduce that. Here's my test script, tested with opentrons_simulate v3.18.1.
metadata = {"apiLevel": "2.3"}
def run(protocol):
tip_rack = protocol.load_labware("opentrons_96_filtertiprack_200ul", 1)
plate = protocol.load_labware("biorad_96_wellplate_200ul_pcr", 2)
pipette = protocol.load_instrument("p300_multi_gen2", mount="left", tip_racks=[tip_rack])
pipette.pick_up_tip()
pipette.mix(3, 200, plate["A1"])
pipette.return_tip()
This outputs:
Picking up tip from A1 of Opentrons 96 Filter Tip Rack 200 碌L on 1
Mixing 3 times with a volume of 200.0 ul
Aspirating 200.0 uL from A1 of Bio-Rad 96 Well Plate 200 碌L PCR on 2 at 1.0 speed
Dispensing 200.0 uL into A1 of Bio-Rad 96 Well Plate 200 碌L PCR on 2 at 1.0 speed
...
What are we doing differently?
@SyntaxColoring thanks for following up on this. I reran my script with 200 and could not replicate my own error, so the first one I encountered must have been caused by some other bug.
The Pipette class has a working_volume property which would be exactly what I need, anyway I can get access to that? I tried pipette.working_volume() but that just throws 'InstrumentContext' object has no attribute 'working_volume'
Hi @dany-fu! To get the working volume from the Pipette class, you can use pipette.hw_pipette['working_volume'] in your protocol.
To elaborate on that workaround, since it's hw_pipette isn't part of the stable API, I think that it's technically unsupported. It might change without warning on some software update. But we don't "expect" it to.
Same vein as https://github.com/Opentrons/opentrons/issues/4838.
Need additional logs to debug this
@asogluizzo we worked out that this was a misunderstanding on what pipette.max_volume actually measured. I thought it was for the tip but it was actually returning the value for the pipette head itself. I was able to resolve by using pipette.hw_pipette['working_volume'] instead.
@dany-fu awesome!! Thank you for the update and we will close this issue now!
To elaborate on that workaround, since it's
hw_pipetteisn't part of the stable API, I think that it's technically unsupported. It might change without warning on some software update. But we don't "expect" it to.Same vein as #4838.
@SyntaxColoring @ahiuchingau I guess this did change. I can no longer get the tip's max volume by calling pipette.hw_pipette['working_volume']
from opentrons import protocol_api
metadata = {'apiLevel': '2.7'}
def run(protocol: protocol_api.ProtocolContext):
tiprack_200 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '10')
tiprack_10 = protocol.load_labware('opentrons_96_filtertiprack_10ul', '11')
p300 = protocol.load_instrument('p300_multi_gen2', 'right', tip_racks=[tiprack_200])
p20 = protocol.load_instrument('p20_multi_gen2', 'left', tip_racks=[tiprack_10])
protocol.comment("Tip 200 Max vol is: {}".format(p300.hw_pipette['working_volume']))
protocol.comment("Tip 10 Max vol is: {}".format(p20.hw_pipette['working_volume']))
Tip 200 Max vol is: 300
Tip 10 Max vol is: 20
Is there a new workaround for when I use a tip that has a smaller volume than the pipette's max?
@dany-fu I tested that script in v3.21.2 and v3.19.0 (adjusting the apiLevel as necessary) and I didn't see any change in behavior.
But note that if you insert a p20.pick_up_tip(), the P20's reported max volume does drop down to 10. In other words, currently, hw_pipette['working_volume'] returns min(pipette_volume, tip_volume) if there is a tip attached, or pipette_volume if there isn't. Maybe that's the source of the "change"?
As an aside, we remarked on this missing feature recently in https://github.com/Opentrons/opentrons/pull/6741#discussion_r503446404. If you want to open a feature request for a pipette.working_volume property, with some background on how it would help you, I think we'd support that.
@SyntaxColoring
But note that if you insert a
p20.pick_up_tip(), the P20's reported max volume does drop down to 10. In other words, currently,hw_pipette['working_volume']returnsmin(pipette_volume, tip_volume)if there is a tip attached, orpipette_volumeif there isn't. Maybe that's the source of the "change"?
I see.... so it has to pick up tip first because returning the tip's max volume. This is different from before but it makes more sense. Thanks!
Most helpful comment
Hi @dany-fu! To get the working volume from the
Pipetteclass, you can usepipette.hw_pipette['working_volume']in your protocol.