From 18478e205e40e8481a1a0298ac00bd7df72fbfcb Mon Sep 17 00:00:00 2001 From: Derek Maggio Date: Mon, 11 Dec 2023 15:48:26 -0800 Subject: [PATCH] test: rework to support partial tip add pipetting --- ...6_GRIPPER_HS_TM_TC_MB_2_16_MegaProtocol.py | 197 ++++++++++-------- 1 file changed, 115 insertions(+), 82 deletions(-) diff --git a/app-testing/files/protocols/py/OT3_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_MegaProtocol.py b/app-testing/files/protocols/py/OT3_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_MegaProtocol.py index 536535975ec..fff7441048a 100644 --- a/app-testing/files/protocols/py/OT3_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_MegaProtocol.py +++ b/app-testing/files/protocols/py/OT3_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_MegaProtocol.py @@ -10,6 +10,17 @@ "apiLevel": "2.16", } +############# +### FLAGS ### +############# + +# prefer to move off deck, instead of waste chute disposal, if possible +PREFER_MOVE_OFF_DECK = True + +################# +### CONSTANTS ### +################# + HEATER_SHAKER_NAME = "heaterShakerModuleV1" MAGNETIC_BLOCK_NAME = "magneticBlockV1" PCR_PLATE_96_NAME = "nest_96_wellplate_100ul_pcr_full_skirt" @@ -22,33 +33,9 @@ RESET_AFTER_EACH_MOVE = True DONT_RESET_AFTER_EACH_MOVE = False - -LABWARE_MOVEMENT_DECK_SLOT_1 = "D1" -LABWARE_MOVEMENT_DECK_SLOT_2 = "B3" -LABWARE_MOVEMENT_STAGING_AREA_SLOT_3 = "C3" -LABWARE_MOVEMENT_STAGING_AREA_SLOT_4 = "A4" - TIP_RACK_LOCATION_1 = "C3" TIP_RACK_LOCATION_2 = "D2" -# NO STAGING AREA IN ROW A or B because of modules - -# A1 - B1: Thermocycler -# C1: Trash Bin -# D1: EMPTY -# A2: Magnetic Block -# B2: Source PCR Plate -# C2: EMPTY for now, will load from off deck - Dest PCR Plate -# D2: Tip Rack 2 -# A3: Heater-Shaker -# B3: Temperature Module -# C3: Tip Rack 1 -# D3: EMPTY -# A4: EMPTY -# B4: EMPTY -# C4: EMPTY for now, will load from off deck - Staging Area Tip Rack 1 -# D4: EMPTY for now, will load from off deck - Staging Area Tip Rack 2 - def default_well(tiprack: protocol_api.labware) -> protocol_api.labware.Well: return tiprack["A1"] @@ -60,22 +47,20 @@ def run(ctx: protocol_api.ProtocolContext) -> None: ### FIXTURES ### ################ - trash_bin_1 = ctx.load_trash_bin("C1") + trash_bin = ctx.load_trash_bin("B3") waste_chute = ctx.load_waste_chute() ############### ### MODULES ### ############### thermocycler = ctx.load_module(THERMOCYCLER_NAME) # A1 & B1 - magnetic_block = ctx.load_module(MAGNETIC_BLOCK_NAME, "A2") + magnetic_block = ctx.load_module(MAGNETIC_BLOCK_NAME, "C1") heater_shaker = ctx.load_module(HEATER_SHAKER_NAME, "A3") - temperature_module = ctx.load_module(TEMPERATURE_MODULE_NAME, "B3") + temperature_module = ctx.load_module(TEMPERATURE_MODULE_NAME, "D1") thermocycler.open_lid() heater_shaker.open_labware_latch() - modules = [thermocycler, heater_shaker, magnetic_block, temperature_module] - ####################### ### MODULE ADAPTERS ### ####################### @@ -89,31 +74,21 @@ def run(ctx: protocol_api.ProtocolContext) -> None: ### LABWARE ### ############### - source_pcr_plate = ctx.load_labware(PCR_PLATE_96_NAME, "B2") - dest_pcr_plate = ctx.load_labware(PCR_PLATE_96_NAME, protocol_api.OFF_DECK) + source_reservoir = ctx.load_labware("nest_1_reservoir_290ml", "D2") + dest_pcr_plate = ctx.load_labware(PCR_PLATE_96_NAME, "C2") - on_deck_tip_rack_1 = ctx.load_labware( - TIPRACK_96_NAME, TIP_RACK_LOCATION_1, adapter="opentrons_flex_96_tiprack_adapter" + tip_rack_1 = ctx.load_labware( + TIPRACK_96_NAME, "A2", adapter="opentrons_flex_96_tiprack_adapter" ) - tip_rack_adapter_1 = on_deck_tip_rack_1.parent - - on_deck_tip_rack_2 = ctx.load_labware( - TIPRACK_96_NAME, TIP_RACK_LOCATION_2, adapter="opentrons_flex_96_tiprack_adapter" - ) - tip_rack_adapter_2 = on_deck_tip_rack_2.parent - - off_deck_tip_rack_1 = ctx.load_labware(TIPRACK_96_NAME, protocol_api.OFF_DECK) - off_deck_tip_rack_2 = ctx.load_labware(TIPRACK_96_NAME, protocol_api.OFF_DECK) - staging_area_tip_rack_1 = ctx.load_labware(TIPRACK_96_NAME, protocol_api.OFF_DECK) - staging_area_tip_rack_2 = ctx.load_labware(TIPRACK_96_NAME, protocol_api.OFF_DECK) + tip_rack_adapter = tip_rack_1.parent + + tip_rack_2 = ctx.load_labware(TIPRACK_96_NAME, "C3") + tip_rack_3 = ctx.load_labware(TIPRACK_96_NAME, "C4") tip_racks = [ - on_deck_tip_rack_1, - on_deck_tip_rack_2, - # staging_area_tip_rack_1, - # staging_area_tip_rack_2, - off_deck_tip_rack_1, - off_deck_tip_rack_2, + tip_rack_1, + tip_rack_2, + tip_rack_3, ] ########################## @@ -132,7 +107,7 @@ def run(ctx: protocol_api.ProtocolContext) -> None: [ well.load_liquid(liquid=water if i % 2 == 0 else acetone, volume=STARTING_VOL) - for i, column in enumerate(source_pcr_plate.columns()) + for i, column in enumerate(source_reservoir.columns()) for well in column ] @@ -157,7 +132,7 @@ def run(ctx: protocol_api.ProtocolContext) -> None: # Staging Area Slot 4 -> Staging Area Slot 4 # Staging Area Slot 4 -> Staging Area Slot 3 - # Staging Area Slot 4 -> Each Module + # Staging Area Slot 4 -> Each ModuleSTAGING_AREA_SLOT_3_RESET_LOCATION # Staging Area Slot 4 -> Deck # Module -> Staging Area Slot 3 @@ -165,21 +140,18 @@ def run(ctx: protocol_api.ProtocolContext) -> None: # Module -> Deck # Module -> Other Module + def get_disposal_preference(): + return (protocol_api.OFF_DECK, not USING_GRIPPER) if PREFER_MOVE_OFF_DECK else (waste_chute, USING_GRIPPER) + def run_moves(labware, move_sequences, reset_location, use_gripper): def move_to_locations(labware_to_move, move_locations, reset_after_each_move, use_gripper, reset_location): def reset_labware(): - ctx.comment( - f"Moving {labware_to_move.name} back to {reset_location.__str__} from {labware_to_move.parent.__str__}" - ) ctx.move_labware(labware_to_move, reset_location, use_gripper=use_gripper) if len(move_locations) == 0: return for location in move_locations: - ctx.comment( - f"Moving {labware_to_move.name} from {labware_to_move.parent.__str__} to {location.__str__}" - ) ctx.move_labware(labware_to_move, location, use_gripper=use_gripper) if reset_after_each_move: @@ -194,11 +166,9 @@ def reset_labware(): def test_gripper_moves(): def deck_moves(pcr_plate, reset_location): - - # Deck -> Deck deck_move_sequence = [ - ["D1", "C2"], # Deck Moves - ["D3"], # Staging Area Slot 3 Moves + ["B2"], # Deck Moves + ["C3"], # Staging Area Slot 3 Moves ["C4", "D4"], # Staging Area Slot 4 Moves [thermocycler, temperature_module_adapter, heater_shaker_adapter, magnetic_block], # Module Moves ] @@ -206,9 +176,8 @@ def deck_moves(pcr_plate, reset_location): run_moves(pcr_plate, deck_move_sequence, reset_location, USING_GRIPPER) def staging_area_slot_3_moves(labware, reset_location): - staging_area_slot_3_move_sequence = [ - ["D1", "C2", "B2"], # Deck Moves + ["B2", "C2"], # Deck Moves [], # Don't have Staging Area Slot 3 open ["C4", "D4"], # Staging Area Slot 4 Moves [thermocycler, temperature_module_adapter, heater_shaker_adapter, magnetic_block], # Module Moves @@ -219,9 +188,9 @@ def staging_area_slot_3_moves(labware, reset_location): def staging_area_slot_4_moves(labware, reset_location): staging_area_slot_4_move_sequence = [ - ["D1", "C2", "B2"], # Deck Moves - ["D3"], # Staging Area Slot 3 Moves - ["D4"], # Staging Area Slot 4 Moves + ["C2", "B2"], # Deck Moves + ["C3"], # Staging Area Slot 3 Moves + ["C4"], # Staging Area Slot 4 Moves [thermocycler, temperature_module_adapter, heater_shaker_adapter, magnetic_block], # Module Moves ] @@ -230,8 +199,8 @@ def staging_area_slot_4_moves(labware, reset_location): def module_moves(labware, module_locations): module_move_sequence = [ - ["D1", "C2", "B2"], # Deck Moves - ["D3"], # Staging Area Slot 3 Moves + ["C2", "B2"], # Deck Moves + ["C3"], # Staging Area Slot 3 Moves ["C4", "D4"], # Staging Area Slot 4 Moves ] @@ -240,27 +209,91 @@ def module_moves(labware, module_locations): labware_move_to_locations.remove(module_starting_location) all_sequences = module_move_sequence.copy() all_sequences.append(labware_move_to_locations) - ctx.move_labware(source_pcr_plate, module_starting_location, use_gripper=USING_GRIPPER) + ctx.move_labware(labware, module_starting_location, use_gripper=USING_GRIPPER) run_moves(labware, all_sequences, module_starting_location, USING_GRIPPER) - DECK_MOVE_RESET_LOCATION = "B2" - STAGING_AREA_SLOT_3_RESET_LOCATION = "D3" - STAGING_AREA_SLOT_4_RESET_LOCATION = "C4" + DECK_MOVE_RESET_LOCATION = "C2" + STAGING_AREA_SLOT_3_RESET_LOCATION = "C3" + STAGING_AREA_SLOT_4_RESET_LOCATION = "D4" - deck_moves(source_pcr_plate, DECK_MOVE_RESET_LOCATION) + deck_moves(dest_pcr_plate, DECK_MOVE_RESET_LOCATION) - ctx.move_labware(source_pcr_plate, STAGING_AREA_SLOT_3_RESET_LOCATION, use_gripper=USING_GRIPPER) - staging_area_slot_3_moves(source_pcr_plate, STAGING_AREA_SLOT_3_RESET_LOCATION) + ctx.move_labware(dest_pcr_plate, STAGING_AREA_SLOT_3_RESET_LOCATION, use_gripper=USING_GRIPPER) + staging_area_slot_3_moves(dest_pcr_plate, STAGING_AREA_SLOT_3_RESET_LOCATION) - ctx.move_labware(source_pcr_plate, STAGING_AREA_SLOT_4_RESET_LOCATION, use_gripper=USING_GRIPPER) - staging_area_slot_4_moves(source_pcr_plate, STAGING_AREA_SLOT_4_RESET_LOCATION) + ctx.move_labware(dest_pcr_plate, STAGING_AREA_SLOT_4_RESET_LOCATION, use_gripper=USING_GRIPPER) + staging_area_slot_4_moves(dest_pcr_plate, STAGING_AREA_SLOT_4_RESET_LOCATION) module_locations = [thermocycler, magnetic_block] + adapters - module_moves(source_pcr_plate, module_locations) + module_moves(dest_pcr_plate, module_locations) def test_manual_moves(): # In C4 currently - ctx.move_labware(source_pcr_plate, "D4", use_gripper=not USING_GRIPPER) - - test_gripper_moves() + ctx.move_labware(source_reservoir, "D4", use_gripper=not USING_GRIPPER) + + def test_pipetting(): + + def test_partial_tip_pickup_usage(): + pipette_96_channel.configure_nozzle_layout(style=protocol_api.COLUMN, start="A12") + for i in range(1, 13): + pipette_96_channel.pick_up_tip(tip_rack_2[f"A{i}"]) + + pipette_96_channel.aspirate(5, source_reservoir["A1"]) + pipette_96_channel.touch_tip() + + pipette_96_channel.dispense(5, dest_pcr_plate[f"A{i}"]) + pipette_96_channel.drop_tip(trash_bin) + + # leave this dropping in waste chute, do not use get_disposal_preference + # want to test partial drop + ctx.move_labware(tip_rack_2, waste_chute, use_gripper=USING_GRIPPER) + + def test_full_tip_rack_usage(): + pipette_96_channel.configure_nozzle_layout(style=protocol_api.ALL, start="A1") + pipette_96_channel.pick_up_tip(tip_rack_1["A1"]) + + pipette_96_channel.aspirate(5, source_reservoir["A1"]) + pipette_96_channel.touch_tip() + + # Waiting for https://opentrons.atlassian.net/browse/RQA-2056 + # pipette_96_channel.air_gap(height=30) + + pipette_96_channel.blow_out(waste_chute) + + pipette_96_channel.aspirate(10, source_reservoir["A1"]) + pipette_96_channel.touch_tip() + + pipette_96_channel.dispense(10, dest_pcr_plate["A1"]) + pipette_96_channel.mix(repetitions=5, volume=15) + pipette_96_channel.return_tip() + + ctx.move_labware(tip_rack_1, get_disposal_preference()[0], use_gripper=get_disposal_preference()[1]) + ctx.move_labware(tip_rack_3, tip_rack_adapter, use_gripper=USING_GRIPPER) + + pipette_96_channel.pick_up_tip(tip_rack_3["A1"]) + pipette_96_channel.transfer( + volume=10, + source=source_reservoir["A1"], + dest=dest_pcr_plate["A1"], + new_tip="never", + touch_tip=True, + blow_out=True, + blowout_location="trash", + mix_before=(3, 5), + mix_after=(1, 5), + ) + pipette_96_channel.return_tip() + + ctx.move_labware(tip_rack_3, get_disposal_preference()[0], use_gripper=get_disposal_preference()[1]) + + test_partial_tip_pickup_usage() + test_full_tip_rack_usage() + test_manual_moves() + test_pipetting() + test_gripper_moves() + + +# Cannot test in this protocol + # - Waste Chute w/ Lid +