# HG changeset patch # User tduigou # Date 1639493198 0 # Node ID ed297a952c061a583085ab90374efebe70ad70a5 "planemo upload commit 84e1d541403efed25838475a7bb0e08d9c7d3cf3-dirty" diff -r 000000000000 -r ed297a952c06 test-data/constructs.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/constructs.csv Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,4 @@ +Well,Linker 1,Part 1,Linker 2,Part 2,Linker 3,Part 3,Linker 4,Part 4,Linker 5,Part 5,Linker 6,Part 6,Linker 7,Part 7,Linker 8,Part 8,Linker 9,Part 9,Linker 10,Part 10 +A1,LMS,BASIC_SEVA_37_CmR-p15A.1,LMP,PJ23104_BASIC,U1-RBS2,D5AP78,U3-RBS3,Q1XBU4,U2-RBS1,O66129,,,,,,,,,, +A2,LMS,BASIC_SEVA_37_CmR-p15A.1,LMP,PJ23101_BASIC,U1-RBS1,D2WKD9,U2-RBS3,O48935,U3-RBS1,O66952,,,,,,,,,, +A3,LMS,BASIC_SEVA_37_CmR-p15A.1,LMP,PJ23104_BASIC,U2-RBS2,P48537,U3-RBS2,O07333,U1-RBS2,Q9C446,,,,,,,,,, diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts.tar Binary file test-data/dnabot_scripts.tar has changed diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/1_clip_ot2_APIv1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/1_clip_ot2_APIv1.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,90 @@ +from opentrons import labware, instruments, modules, robot + + +clips_dict={"prefixes_wells": ["B8", "B7", "C5", "C12", "C7", "B7", "C4", "C9", "C10", "B7", "C8", "C11", "C5"], "prefixes_plates": ["2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2"], "suffixes_wells": ["A7", "C1", "C3", "C2", "A8", "C1", "C2", "C3", "A8", "C2", "C3", "C1", "A8"], "suffixes_plates": ["2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2"], "parts_wells": ["A1", "A10", "A3", "A11", "A6", "A9", "A2", "A5", "A7", "A10", "A8", "A4", "A12"], "parts_plates": ["5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5"], "parts_vols": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "water_vols": [7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0]} + + +def clip( + prefixes_wells, + prefixes_plates, + suffixes_wells, + suffixes_plates, + parts_wells, + parts_plates, + parts_vols, + water_vols, + tiprack_type="tiprack-10ul"): + """Implements linker ligation reactions using an opentrons OT-2.""" + + # Constants + INITIAL_TIP = 'A1' + CANDIDATE_TIPRACK_SLOTS = ['3', '6', '9'] + PIPETTE_TYPE = 'P10_Single' + PIPETTE_MOUNT = 'right' + SOURCE_PLATE_TYPE = '4ti-0960_FrameStar' + DESTINATION_PLATE_TYPE = '4ti-0960_FrameStar' + DESTINATION_PLATE_POSITION = '1' + TUBE_RACK_TYPE = 'tube-rack_E1415-1500' + TUBE_RACK_POSITION = '4' + MASTER_MIX_WELL = 'A1' + WATER_WELL = 'A2' + INITIAL_DESTINATION_WELL = 'A1' + MASTER_MIX_VOLUME = 20 + LINKER_MIX_SETTINGS = (1, 3) + PART_MIX_SETTINGS = (4, 5) + + # Tiprack slots + total_tips = 4 * len(parts_wells) + letter_dict = {'A': 0, 'B': 1, 'C': 2, + 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7} + tiprack_1_tips = ( + 13 - int(INITIAL_TIP[1:])) * 8 - letter_dict[INITIAL_TIP[0]] + if total_tips > tiprack_1_tips: + tiprack_num = 1 + (total_tips - tiprack_1_tips) // 96 + \ + (1 if (total_tips - tiprack_1_tips) % 96 > 0 else 0) + else: + tiprack_num = 1 + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + + # Define source plates + source_plates = {} + source_plates_keys = list(set((prefixes_plates + suffixes_plates + parts_plates))) + for key in source_plates_keys: + source_plates[key] = labware.load(SOURCE_PLATE_TYPE, key) + + # Define remaining labware + tipracks = [labware.load(tiprack_type, slot) for slot in slots] + if PIPETTE_TYPE != 'P10_Single': + print('Define labware must be changed to use', PIPETTE_TYPE) + exit() + pipette = instruments.P10_Single(mount=PIPETTE_MOUNT, tip_racks=tipracks) + pipette.start_at_tip(tipracks[0].well(INITIAL_TIP)) + destination_plate = labware.load( + DESTINATION_PLATE_TYPE, DESTINATION_PLATE_POSITION) + tube_rack = labware.load(TUBE_RACK_TYPE, TUBE_RACK_POSITION) + master_mix = tube_rack.wells(MASTER_MIX_WELL) + water = tube_rack.wells(WATER_WELL) + destination_wells = destination_plate.wells( + INITIAL_DESTINATION_WELL, length=int(len(parts_wells))) + + # Transfers + pipette.pick_up_tip() + pipette.transfer(MASTER_MIX_VOLUME, master_mix, + destination_wells, new_tip='never') + pipette.drop_tip() + pipette.transfer(water_vols, water, + destination_wells, new_tip='always') + for clip_num in range(len(parts_wells)): + pipette.transfer(1, source_plates[prefixes_plates[clip_num]].wells(prefixes_wells[clip_num]), + destination_wells[clip_num], mix_after=LINKER_MIX_SETTINGS) + pipette.transfer(1, source_plates[suffixes_plates[clip_num]].wells(suffixes_wells[clip_num]), + destination_wells[clip_num], mix_after=LINKER_MIX_SETTINGS) + pipette.transfer(parts_vols[clip_num], source_plates[parts_plates[clip_num]].wells(parts_wells[clip_num]), + destination_wells[clip_num], mix_after=PART_MIX_SETTINGS) + + +clip(**clips_dict) + +# Robot comments +for c in robot.commands(): + print(c) \ No newline at end of file diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/1_clip_ot2_APIv2.8.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/1_clip_ot2_APIv2.8.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,137 @@ +from opentrons import protocol_api + +# Rename to 'clip_template' and paste into 'template_ot2_scripts' folder in DNA-BOT to use +# Code has been reordered to better group relevant commands and take the constants out of def clip() + +#metadata +metadata = { + 'apiLevel': '2.8', + 'protocolName': 'CLIP_No_Thermocycler', + 'description': 'Implements linker ligation reactions using an opentrons OT-2. This version does not include the Thermocycler module.'} + +# example dictionary produced by DNA-BOT for a single construct containing 5 parts, un-comment and run to test the template +#clips_dict={"prefixes_wells": ["A8", "A7", "C5", "C7", "C10"], "prefixes_plates": ["2", "2", "2", "2", "2"], "suffixes_wells": ["B7", "C1", "C2", "C3", "B8"], "suffixes_plates": ["2", "2", "2", "2", "2"], "parts_wells": ["E2", "F2", "C2", "B2", "D2"], "parts_plates": ["5", "5", "5", "5", "5"], "parts_vols": [1, 1, 1, 1, 1], "water_vols": [7.0, 7.0, 7.0, 7.0, 7.0]} + +# __LABWARES is expected to be redefined by "generate_ot2_script" method +# Test dict +# __LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + +clips_dict={"prefixes_wells": ["B8", "B7", "C5", "C12", "C7", "B7", "C4", "C9", "C10", "B7", "C8", "C11", "C5"], "prefixes_plates": ["2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2"], "suffixes_wells": ["A7", "C1", "C3", "C2", "A8", "C1", "C2", "C3", "A8", "C2", "C3", "C1", "A8"], "suffixes_plates": ["2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2"], "parts_wells": ["A1", "A10", "A3", "A11", "A6", "A9", "A2", "A5", "A7", "A10", "A8", "A4", "A12"], "parts_plates": ["5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5"], "parts_vols": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "water_vols": [7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0]} +__LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + + +def run(protocol: protocol_api.ProtocolContext): +# added run function for API 2.8 + + ### Constants - these have been moved out of the def clip() for clarity + + #Tiprack + tiprack_type=__LABWARES['96_tiprack_20ul']['id'] + INITIAL_TIP = 'A1' + CANDIDATE_TIPRACK_SLOTS = ['3', '6', '9'] + + # Pipettes - pipette instructions in a single location so redefining pipette type is simpler + PIPETTE_TYPE = __LABWARES['p20_single']['id'] + # API 2 supports gen_1 pipettes like the p10_single + PIPETTE_MOUNT = 'right' + ### Load Pipette + # checks if it's a P10 Single pipette + if PIPETTE_TYPE != 'p20_single_gen2': + print('Define labware must be changed to use', PIPETTE_TYPE) + exit() + + # Source Plates + SOURCE_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + # modified from custom labware as API 2 doesn't support labware.create anymore, so the old add_labware script can't be used + + # Destination Plates + DESTINATION_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + DESTINATION_PLATE_POSITION = '1' + # INITIAL_DESTINATION_WELL constant removed, as destination_plate.wells() automatically starts from A1 + + # Tube Rack + TUBE_RACK_TYPE = __LABWARES['24_tuberack_1500ul']['id'] + TUBE_RACK_POSITION = '4' + MASTER_MIX_WELL = 'A1' + WATER_WELL = 'A2' + MASTER_MIX_VOLUME = 20 + + # Mix settings + LINKER_MIX_SETTINGS = (1, 3) + PART_MIX_SETTINGS = (4, 5) + + def clip( + prefixes_wells, + prefixes_plates, + suffixes_wells, + suffixes_plates, + parts_wells, + parts_plates, + parts_vols, + water_vols): + + ### Loading Tiprack + # Calculates whether one, two, or three tipracks are needed, which are in slots 3, 6, and 9 respectively + total_tips = 4 * len(parts_wells) + letter_dict = {'A': 0, 'B': 1, 'C': 2, + 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7} + tiprack_1_tips = ( + 13 - int(INITIAL_TIP[1:])) * 8 - letter_dict[INITIAL_TIP[0]] + if total_tips > tiprack_1_tips: + tiprack_num = 1 + (total_tips - tiprack_1_tips) // 96 + \ + (1 if (total_tips - tiprack_1_tips) % 96 > 0 else 0) + else: + tiprack_num = 1 + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + + # loads the correct number of tipracks + tipracks = [protocol.load_labware(tiprack_type, slot) for slot in slots] + + # Loads pipette according to constants assigned above + pipette = protocol.load_instrument(PIPETTE_TYPE, mount=PIPETTE_MOUNT, tip_racks=tipracks) + + ### Load Destination Plate + # Loads destination plate according to constants assigned above + destination_plate = protocol.load_labware(DESTINATION_PLATE_TYPE, DESTINATION_PLATE_POSITION) + + # Defines where the destination wells are within the destination plate + destination_wells = destination_plate.wells()[0:len(parts_wells)] + + ### Load Tube Rack + # Loads tube rack according to constants assigned above + tube_rack = protocol.load_labware(TUBE_RACK_TYPE, TUBE_RACK_POSITION) + + # Defines positions of master mix and water within the tube rack + master_mix = tube_rack.wells(MASTER_MIX_WELL) + water = tube_rack.wells(WATER_WELL) + + ### Loading Source Plates + # Makes a source plate key for where prefixes, suffixes, and parts are located, according to the dictionary generated by the DNA-BOT + source_plates = {} + source_plates_keys = list(set((prefixes_plates + suffixes_plates + parts_plates))) + + # Loads plates according to the source plate key + for key in source_plates_keys: + source_plates[key]=protocol.load_labware(SOURCE_PLATE_TYPE, key) + + ### Transfers + + # transfer master mix into destination wells + # added blowout into destination wells ('blowout_location' only works for API 2.8 and above) + pipette.pick_up_tip() + pipette.transfer(MASTER_MIX_VOLUME, master_mix, destination_wells, blow_out=True, blowout_location='destination well', new_tip='never') + pipette.drop_tip() + + # transfer water into destination wells + # added blowout into destination wells ('blowout_location' only works for API 2.8 and above) + pipette.transfer(water_vols, water, destination_wells, blow_out=True, blowout_location='destination well', new_tip='always') + + #transfer prefixes, suffixes, and parts into destination wells + # added blowout into destination wells ('blowout_location' only works for API 2.8 and above) + for clip_num in range(len(parts_wells)): + pipette.transfer(1, source_plates[prefixes_plates[clip_num]].wells(prefixes_wells[clip_num]), destination_wells[clip_num], blow_out=True, blowout_location='destination well', new_tip='always', mix_after=LINKER_MIX_SETTINGS) + pipette.transfer(1, source_plates[suffixes_plates[clip_num]].wells(suffixes_wells[clip_num]), destination_wells[clip_num], blow_out=True, blowout_location='destination well', new_tip='always', mix_after=LINKER_MIX_SETTINGS) + pipette.transfer(parts_vols[clip_num], source_plates[parts_plates[clip_num]].wells(parts_wells[clip_num]), destination_wells[clip_num], blow_out=True, blowout_location='destination well', new_tip='always', mix_after=PART_MIX_SETTINGS) + + # the run function will first define the CLIP function, and then run the CLIP function with the dictionary produced by DNA-BOT + clip(**clips_dict) diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/1_clip_ot2_Thermocycler_APIv2.8.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/1_clip_ot2_Thermocycler_APIv2.8.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,152 @@ +from opentrons import protocol_api + +# Rename to 'clip_template' and paste into 'template_ot2_scripts' folder in DNA-BOT to use + +#metadata +metadata = { + 'apiLevel': '2.8', + 'protocolName': 'CLIP_With_Thermocycler', + 'description': 'Implements linker ligation reactions using an opentrons OT-2, including the thermocycler module.'} + + + +# example dictionary produced by DNA-BOT for a single construct containing 5 parts, un-comment and run to test the template +#clips_dict={"prefixes_wells": ["A8", "A7", "C5", "C7", "C10"], "prefixes_plates": ["2", "2", "2", "2", "2"], "suffixes_wells": ["B7", "C1", "C2", "C3", "B8"], "suffixes_plates": ["2", "2", "2", "2", "2"], "parts_wells": ["E2", "F2", "C2", "B2", "D2"], "parts_plates": ["5", "5", "5", "5", "5"], "parts_vols": [1, 1, 1, 1, 1], "water_vols": [7.0, 7.0, 7.0, 7.0, 7.0]} + +# __LABWARES is expected to be redefined by "generate_ot2_script" method +# Test dict +# __LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + +clips_dict={"prefixes_wells": ["B8", "B7", "C5", "C12", "C7", "B7", "C4", "C9", "C10", "B7", "C8", "C11", "C5"], "prefixes_plates": ["2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2"], "suffixes_wells": ["A7", "C1", "C3", "C2", "A8", "C1", "C2", "C3", "A8", "C2", "C3", "C1", "A8"], "suffixes_plates": ["2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2"], "parts_wells": ["A1", "A10", "A3", "A11", "A6", "A9", "A2", "A5", "A7", "A10", "A8", "A4", "A12"], "parts_plates": ["5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5"], "parts_vols": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "water_vols": [7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0]} +__LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + + +def run(protocol: protocol_api.ProtocolContext): +# added run function for API 2.8 + + ### Constants - these have been moved out of the def clip() for clarity + + #Tiprack + tiprack_type=__LABWARES['96_tiprack_20ul']['id'] + INITIAL_TIP = 'A1' + CANDIDATE_TIPRACK_SLOTS = ['3', '6', '9'] + + # Pipettes - pipette instructions in a single location so redefining pipette type is simpler + PIPETTE_TYPE = __LABWARES['p20_single']['id'] + PIPETTE_MOUNT = 'right' + ### Load Pipette + # checks if it's a P10 Single pipette + if PIPETTE_TYPE != 'p20_single_gen2': + print('Define labware must be changed to use', PIPETTE_TYPE) + exit() +#Thermocycler Module + tc_mod = protocol.load_module('Thermocycler Module') +# Destination Plates + DESTINATION_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + # Loads destination plate onto Thermocycler Module + destination_plate = tc_mod.load_labware(DESTINATION_PLATE_TYPE) + + # Source Plates + SOURCE_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + # modified from custom labware as API 2 doesn't support labware.create anymore, so the old add_labware script can't be used + + # Tube Rack + TUBE_RACK_TYPE = __LABWARES['24_tuberack_1500ul']['id'] + # modified from custom labware as API 2 doesn't support labware.create anymore, so the old add_labware script can't be used + TUBE_RACK_POSITION = '4' + MASTER_MIX_WELL = 'A1' + WATER_WELL = 'A2' + MASTER_MIX_VOLUME = 20 + + # Mix settings + LINKER_MIX_SETTINGS = (1, 3) + PART_MIX_SETTINGS = (4, 5) + + def clip( + prefixes_wells, + prefixes_plates, + suffixes_wells, + suffixes_plates, + parts_wells, + parts_plates, + parts_vols, + water_vols): + + ### Loading Tiprack + # Calculates whether one, two, or three tipracks are needed, which are in slots 3, 6, and 9 respectively + total_tips = 4 * len(parts_wells) + letter_dict = {'A': 0, 'B': 1, 'C': 2, + 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7} + tiprack_1_tips = ( + 13 - int(INITIAL_TIP[1:])) * 8 - letter_dict[INITIAL_TIP[0]] + if total_tips > tiprack_1_tips: + tiprack_num = 1 + (total_tips - tiprack_1_tips) // 96 + \ + (1 if (total_tips - tiprack_1_tips) % 96 > 0 else 0) + else: + tiprack_num = 1 + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + + # loads the correct number of tipracks + tipracks = [protocol.load_labware(tiprack_type, slot) for slot in slots] + # changed to protocol.load_labware for API 2.8 + + # Loads pipette according to constants assigned above + pipette = protocol.load_instrument(PIPETTE_TYPE, mount=PIPETTE_MOUNT, tip_racks=tipracks) + + # Defines where the destination wells are within the destination plate + destination_wells = destination_plate.wells()[0:len(parts_wells)] + + ### Load Tube Rack + # Loads tube rack according to constants assigned above + tube_rack = protocol.load_labware(TUBE_RACK_TYPE, TUBE_RACK_POSITION) + + # Defines positions of master mix and water within the tube rack + master_mix = tube_rack.wells(MASTER_MIX_WELL) + water = tube_rack.wells(WATER_WELL) + + ### Loading Source Plates + # Makes a source plate key for where prefixes, suffixes, and parts are located, according to the dictionary generated by the DNA-BOT + source_plates = {} + source_plates_keys = list(set((prefixes_plates + suffixes_plates + parts_plates))) + + # Loads plates according to the source plate key + for key in source_plates_keys: + source_plates[key]=protocol.load_labware(SOURCE_PLATE_TYPE, key) + + ### Transfers + + # transfer master mix into destination wells + # added blowout into destination wells ('blowout_location' only works for API 2.8 and above) + pipette.pick_up_tip() + pipette.transfer(MASTER_MIX_VOLUME, master_mix, destination_wells, blow_out=True, blowout_location='destination well', new_tip='never') + pipette.drop_tip() + + # transfer water into destination wells + # added blowout into destination wells ('blowout_location' only works for API 2.8 and above) + pipette.transfer(water_vols, water, destination_wells, blow_out=True, blowout_location='destination well', new_tip='always') + + #transfer prefixes, suffixes, and parts into destination wells + # added blowout into destination wells ('blowout_location' only works for API 2.8 and above) + for clip_num in range(len(parts_wells)): + pipette.transfer(1, source_plates[prefixes_plates[clip_num]].wells(prefixes_wells[clip_num]), destination_wells[clip_num], blow_out=True, blowout_location='destination well', new_tip='always', mix_after=LINKER_MIX_SETTINGS) + pipette.transfer(1, source_plates[suffixes_plates[clip_num]].wells(suffixes_wells[clip_num]), destination_wells[clip_num], blow_out=True, blowout_location='destination well', new_tip='always', mix_after=LINKER_MIX_SETTINGS) + pipette.transfer(parts_vols[clip_num], source_plates[parts_plates[clip_num]].wells(parts_wells[clip_num]), destination_wells[clip_num], blow_out=True, blowout_location='destination well', new_tip='always', mix_after=PART_MIX_SETTINGS) + + # the run function will first define the CLIP function, and then run the CLIP function with the dictionary produced by DNA-BOT + clip(**clips_dict) + ### PCR Reaction in Thermocycler + + # close lid and set lid temperature, PCR will not start until lid reaches 37C + tc_mod.close_lid() + tc_mod.set_lid_temperature(105) + + # Runs 20 cycles of 37C for 2 minutes and 20C for 1 minute, then holds for 60C for 10 minutes + profile = [ + {'temperature': 37, 'hold_time_minutes': 2}, + {'temperature': 20, 'hold_time_minutes': 1}] + tc_mod.execute_profile(steps=profile, repetitions=20, block_max_volume=30) + tc_mod.set_block_temperature(60, hold_time_minutes=10, block_max_volume=30) + tc_mod.set_block_temperature(4, hold_time_minutes=2, block_max_volume=30) + #Q Does block_max_volume define total volume in block or individual wells? + tc_mod.set_lid_temperature(37) + tc_mod.open_lid() diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/2_purification_ot2_APIv1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/2_purification_ot2_APIv1.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,182 @@ +from opentrons import labware, instruments, modules, robot + + +sample_number=13 +ethanol_well='A11' + + +def magbead( + sample_number, + ethanol_well, + elution_buffer_well, + sample_volume=30, + bead_ratio=1.8, + elution_buffer_volume=40, + incubation_time=5, + settling_time=2, + drying_time=5, + elution_time=2, + sample_offset=0, + tiprack_type="opentrons_96_tiprack_300ul"): + """Implements magbead purification reactions for BASIC assembly using an opentrons OT-2. + + Selected args: + ethanol_well (str): well in reagent container containing ethanol. + elution_buffer_well (str): well in reagent container containing elution buffer. + sample_offset (int): offset the intial sample column by the specified value. + + """ + + # Constants + PIPETTE_ASPIRATE_RATE = 25 + PIPETTE_DISPENSE_RATE = 150 + TIPS_PER_SAMPLE = 9 + CANDIDATE_TIPRACK_SLOTS = ['3', '6', '9', '2', '5'] + MAGDECK_POSITION = '1' + MIX_PLATE_TYPE = '4ti-0960_FrameStar' + MIX_PLATE_POSITION = '4' + REAGENT_CONTAINER_TYPE = '4ti0131_trough-12' + REAGENT_CONTAINER_POSITION = '7' + BEAD_CONTAINER_TYPE = '4ti0136_96_deep-well' + BEAD_CONTAINER_POSITION = '8' + LIQUID_WASTE_WELL = 'A12' + BEADS_WELL = 'A1' + DEAD_TOTAL_VOL = 5 + SLOW_HEAD_SPEEDS = {'x': 600 // 4, 'y': 400 // 4, + 'z': 125 // 10, 'a': 125 // 10} + DEFAULT_HEAD_SPEEDS = {'x': 400, 'y': 400, 'z': 125, 'a': 100} + IMMOBILISE_MIX_REPS = 10 + MAGDECK_HEIGHT = 20 + AIR_VOL_COEFF = 0.1 + ETHANOL_VOL = 150 + WASH_TIME = 0.5 + ETHANOL_DEAD_VOL = 50 + ELUTION_MIX_REPS = 20 + ELUTANT_SEP_TIME = 1 + ELUTION_DEAD_VOL = 2 + + # Errors + if sample_number > 48: + raise ValueError('sample number cannot exceed 48') + + # Tips and pipette + total_tips = sample_number * TIPS_PER_SAMPLE + tiprack_num = total_tips // 96 + (1 if total_tips % 96 > 0 else 0) + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + tipracks = [labware.load(tiprack_type, slot) + for slot in slots] + pipette = instruments.P300_Multi( + mount="left", + tip_racks=tipracks, + aspirate_flow_rate=PIPETTE_ASPIRATE_RATE, + dispense_flow_rate=PIPETTE_DISPENSE_RATE) + + # Define labware + MAGDECK = modules.load('magdeck', MAGDECK_POSITION) + MAGDECK.disengage() + mag_plate = labware.load(MIX_PLATE_TYPE, MAGDECK_POSITION, share=True) + mix_plate = labware.load(MIX_PLATE_TYPE, MIX_PLATE_POSITION) + reagent_container = labware.load( + REAGENT_CONTAINER_TYPE, REAGENT_CONTAINER_POSITION) + bead_container = labware.load(BEAD_CONTAINER_TYPE, BEAD_CONTAINER_POSITION) + col_num = sample_number // 8 + (1 if sample_number % 8 > 0 else 0) + samples = [col for col in mag_plate.cols( + )[0 + sample_offset:col_num + sample_offset]] + output = [col for col in mag_plate.cols( + )[6 + sample_offset:col_num + 6 + sample_offset]] + mixing = [col for col in mix_plate.cols( + )[0 + sample_offset:col_num + sample_offset]] + + # Define reagents and liquid waste + liquid_waste = reagent_container.wells(LIQUID_WASTE_WELL) + beads = bead_container.wells(BEADS_WELL) + ethanol = reagent_container.wells(ethanol_well) + elution_buffer = reagent_container.wells(elution_buffer_well) + + # Define bead and mix volume + bead_volume = sample_volume * bead_ratio + if bead_volume / 2 > pipette.max_volume: + mix_vol = pipette.max_volume + else: + mix_vol = bead_volume / 2 + total_vol = bead_volume + sample_volume + DEAD_TOTAL_VOL + + # Mix beads and PCR samples and incubate + for target in range(int(len(samples))): + # Aspirate beads + pipette.pick_up_tip() + pipette.aspirate(bead_volume, beads) + robot.head_speed(**SLOW_HEAD_SPEEDS, combined_speed=max(SLOW_HEAD_SPEEDS.values())) + + # Transfer and mix on mix_plate + pipette.aspirate(sample_volume + DEAD_TOTAL_VOL, samples[target]) + pipette.dispense(total_vol, mixing[target]) + pipette.mix(IMMOBILISE_MIX_REPS, mix_vol, mixing[target]) + pipette.blow_out() + + # Dispose of tip + robot.head_speed(**DEFAULT_HEAD_SPEEDS, combined_speed=max(DEFAULT_HEAD_SPEEDS.values())) + pipette.drop_tip() + + # Immobilise sample + pipette.delay(minutes=incubation_time) + + # Transfer sample back to magdeck + for target in range(int(len(samples))): + pipette.transfer(total_vol, mixing[target], samples[target], + blow_out=True) + + # Engagae MagDeck and incubate + MAGDECK.engage(height=MAGDECK_HEIGHT) + pipette.delay(minutes=settling_time) + + # Remove supernatant from magnetic beads + for target in samples: + pipette.transfer(total_vol, target, liquid_waste, blow_out=True) + + # Wash beads twice with 70% ethanol + air_vol = pipette.max_volume * AIR_VOL_COEFF + for cycle in range(2): + for target in samples: + pipette.transfer(ETHANOL_VOL, ethanol, target, air_gap=air_vol) + pipette.delay(minutes=WASH_TIME) + for target in samples: + pipette.transfer(ETHANOL_VOL + ETHANOL_DEAD_VOL, target, liquid_waste, + air_gap=air_vol) + + # Dry at RT + pipette.delay(minutes=drying_time) + + # Disengage MagDeck + MAGDECK.disengage() + + # Mix beads with elution buffer + if elution_buffer_volume / 2 > pipette.max_volume: + mix_vol = pipette.max_volume + else: + mix_vol = elution_buffer_volume / 2 + for target in samples: + pipette.transfer(elution_buffer_volume, elution_buffer, + target, mix_after=(ELUTION_MIX_REPS, mix_vol)) + + # Incubate at RT for "elution_time" minutes + pipette.delay(minutes=elution_time) + + # Engagae MagDeck for 1 minute and remain engaged for DNA elution + MAGDECK.engage(height=MAGDECK_HEIGHT) + pipette.delay(minutes=ELUTANT_SEP_TIME) + + # Transfer clean PCR product to a new well + for target, dest in zip(samples, output): + pipette.transfer(elution_buffer_volume - ELUTION_DEAD_VOL, target, + dest, blow_out=False) + + # Disengage MagDeck + MAGDECK.disengage() + + +magbead(sample_number=sample_number, + ethanol_well=ethanol_well, elution_buffer_well='A1') + +for c in robot.commands(): + print(c) diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/2_purification_ot2_APIv2.8.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/2_purification_ot2_APIv2.8.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,261 @@ +from opentrons import protocol_api + +# Rename to 'purification_template' and paste into 'template_ot2_scripts' folder in DNA-BOT to use + +metadata = { + 'apiLevel': '2.8', + 'protocolName': 'purification_template', + 'description': 'Implements magbead purification reactions for BASIC assembly using an opentrons OT-2'} + + + + +# example values produced by DNA-BOT for a single construct containing 5 parts, un-comment and run to test the template: +#sample_number=8 +#ethanol_well='A3' + +# __LABWARES and __PARAMETERS are expected to be redefined by "generate_ot2_script" method +# Test dict +# __LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} +# __PARAMETERS={"purif_magdeck_height": {"value": 20.0}, "purif_wash_time": {"value": 0.5}, "purif_bead_ratio": {"value": 1.8}, "purif_incubation_time": {"value": 5.0}, "purif_settling_time": {"value": 2.0}, "purif_drying_time": {"value": 5.0}, "purif_elution_time": {"value": 2.0}, "transfo_incubation_temp": {"value": 4.0}, "transfo_incubation_time": {"value": 20.0}} + +sample_number=13 +ethanol_well='A11' +__LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} +__PARAMETERS={"purif_magdeck_height": {"value": 20}, "purif_wash_time": {"value": 0}, "purif_bead_ratio": {"value": 1}, "purif_incubation_time": {"value": 5}, "purif_settling_time": {"value": 2}, "purif_drying_time": {"value": 5}, "purif_elution_time": {"value": 2}, "transfo_incubation_temp": {"value": 4}, "transfo_incubation_time": {"value": 20}} + + +def run(protocol: protocol_api.ProtocolContext): +# added run function for API verison 2 + + def magbead( + sample_number, + ethanol_well, + elution_buffer_well='A1', + sample_volume=30, + bead_ratio=__PARAMETERS['purif_bead_ratio']['value'], + elution_buffer_volume=40, + incubation_time=__PARAMETERS['purif_incubation_time']['value'], + settling_time=__PARAMETERS['purif_settling_time']['value'], + # if using Gen 2 magentic module, need to change time! see: https://docs.opentrons.com/v2/new_modules.html + # "The GEN2 Magnetic Module uses smaller magnets than the GEN1 version...this means it will take longer for the GEN2 module to attract beads." + # Recommended Magnetic Module GEN2 bead attraction time: + # Total liquid volume <= 50 uL: 5 minutes + # this template was written with the Gen 1 magnetic module, as it is compatible with API version 2 + drying_time=__PARAMETERS['purif_drying_time']['value'], + elution_time=__PARAMETERS['purif_elution_time']['value'], + sample_offset=0, + tiprack_type=__LABWARES['96_tiprack_300ul']['id']): + + """ + + Selected args: + ethanol_well (str): well in reagent container containing ethanol. + elution_buffer_well (str): well in reagent container containing elution buffer. + sample_offset (int): offset the intial sample column by the specified value. + + """ + + + ### Constants + + # Pipettes + PIPETTE_ASPIRATE_RATE = 25 + PIPETTE_DISPENSE_RATE = 150 + TIPS_PER_SAMPLE = 9 + PIPETTE_TYPE = __LABWARES['p300_multi']['id'] + # new constant for easier swapping between pipette types + + # Tiprack + CANDIDATE_TIPRACK_SLOTS = ['3', '6', '9', '2', '5'] + + # Magnetic Module + MAGDECK_POSITION = '1' + + # Mix Plate + MIX_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_23']['id'] + # modified from custom labware as API 2 doesn't support labware.create anymore, so the old add_labware script can't be used + # also acts as the type of plate loaded onto the magnetic module + MIX_PLATE_POSITION = '4' + + # Reagents + REAGENT_CONTAINER_TYPE = __LABWARES['12_reservoir_21000ul']['id'] + REAGENT_CONTAINER_POSITION = '7' + + # Beads + BEAD_CONTAINER_TYPE = __LABWARES['96_deepwellplate_2ml']['id'] + BEAD_CONTAINER_POSITION = '8' + + # Settings + LIQUID_WASTE_WELL = 'A5' + BEADS_WELL = 'A1' + DEAD_TOTAL_VOL = 5 + SLOW_HEAD_SPEEDS = {'x': 600 // 4, 'y': 400 // 4, 'z': 125 // 10, 'a': 125 // 10} + DEFAULT_HEAD_SPEEDS = {'x': 400, 'y': 400, 'z': 125, 'a': 100} + IMMOBILISE_MIX_REPS = 10 + MAGDECK_HEIGHT = __PARAMETERS['purif_magdeck_height']['value'] + AIR_VOL_COEFF = 0.1 + ETHANOL_VOL = 150 + WASH_TIME = __PARAMETERS['purif_wash_time']['value'] + ETHANOL_DEAD_VOL = 50 + ELUTION_MIX_REPS = 20 + ELUTANT_SEP_TIME = 1 + ELUTION_DEAD_VOL = 2 + + + ### Errors + if sample_number > 48: + raise ValueError('sample number cannot exceed 48') + + + ### Loading Tiprack + + # Calculates whether one/two/three/four/five tipracks are needed, which are in slots 3, 6, 9, 2, and 5 respectively + total_tips = sample_number * TIPS_PER_SAMPLE + tiprack_num = total_tips // 96 + (1 if total_tips % 96 > 0 else 0) + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + tipracks = [protocol.load_labware(tiprack_type, slot) for slot in slots] + # changed to protocol.load_labware for API version 2 + + + ### Loading Pipettes + + pipette = protocol.load_instrument(PIPETTE_TYPE, mount="left", tip_racks=tipracks) + pipette.aspirate_flow_rate=PIPETTE_ASPIRATE_RATE + pipette.dispense_flow_rate=PIPETTE_DISPENSE_RATE + # for reference: default aspirate/dispense flow rate for p300_multi_gen2 is 94 ul/s + + ### Define Labware + + # Magnetic Module + MAGDECK = protocol.load_module(__LABWARES['mag_deck']['id'], MAGDECK_POSITION) + # 'magdeck' is the gen 1 magnetic module, use 'magnetic module gen2' for the gen 2 magentic module + # if using gen 2 module, need to change settling time! (see comments under Constants) + MAGDECK.disengage() + # disengages the magnets when it is turned on + mag_plate = MAGDECK.load_labware(MIX_PLATE_TYPE) + + # Mix Plate + mix_plate = protocol.load_labware(MIX_PLATE_TYPE, MIX_PLATE_POSITION) + + # Reagents + reagent_container = protocol.load_labware(REAGENT_CONTAINER_TYPE, REAGENT_CONTAINER_POSITION) + + # Beads Container + bead_container = protocol.load_labware(BEAD_CONTAINER_TYPE, BEAD_CONTAINER_POSITION) + + + ### Calculating Columns + + # Total number of columns + col_num = sample_number // 8 + (1 if sample_number % 8 > 0 else 0) + + # Columns containing samples in location 1 (magentic module) + # generates a list of lists: [[A1, B1, C1...], [A2, B2, C2...]...] + samples = [col for col in mag_plate.columns()[sample_offset : col_num + sample_offset]] + + # Columns to mix beads and samples in location 4 (mix plate) + mixing = [col for col in mix_plate.columns()[sample_offset:col_num + sample_offset]] + + # Columns to dispense output in location 1 (magnetic module) + # purified parts are dispensed 6 rows to the right of their initial location + # this is why the number of samples cannot exceed 48 + + output = [col for col in mag_plate.columns()[6 + sample_offset:col_num + 6 + sample_offset]] + + ### Defining Wells for Reagents, Liquid Waste, and Beads + + liquid_waste = reagent_container.wells(LIQUID_WASTE_WELL) + ethanol = reagent_container.wells(ethanol_well) + elution_buffer = reagent_container.wells(elution_buffer_well) + beads = bead_container[BEADS_WELL] + + ### Define bead and mix volume + bead_volume = sample_volume * bead_ratio + if bead_volume / 2 > pipette.max_volume: + mix_vol = pipette.max_volume + else: + mix_vol = bead_volume / 2 + total_vol = bead_volume + sample_volume + DEAD_TOTAL_VOL + + + ### Steps + + # Mix beads and parts + for target in range(int(len(samples))): + + # Aspirate beads + pipette.pick_up_tip() + pipette.aspirate(bead_volume, beads) + protocol.max_speeds.update(SLOW_HEAD_SPEEDS) + + # Aspirte samples + pipette.aspirate(sample_volume + DEAD_TOTAL_VOL, samples[target][0]) + + # Transfer and mix on mix_plate + pipette.dispense(total_vol, mixing[target][0]) + # similar to above, added [0] because samples[target] returned a list of every well in column 1 rather than just one well + pipette.mix(IMMOBILISE_MIX_REPS, mix_vol, mixing[target][0]) + # similar to above, added [0] because samples[target] returned a list of every well in column 1 rather than just one well + pipette.blow_out() + + # Dispose of tip + protocol.max_speeds.update(DEFAULT_HEAD_SPEEDS) + pipette.drop_tip() + + # Immobilise sample + protocol.delay(minutes=incubation_time) + + # Transfer beads+samples back to magdeck + for target in range(int(len(samples))): + pipette.transfer(total_vol, mixing[target], samples[target], blow_out=True, blowout_location='destination well') + # added blowout_location=destination well because default location of blowout is waste in API version 2 + + # Engagae MagDeck and incubate + MAGDECK.engage(height=MAGDECK_HEIGHT) + protocol.delay(minutes=settling_time) + + # Remove supernatant from magnetic beads + for target in samples: + pipette.transfer(total_vol, target, liquid_waste, blow_out=True) + + # Wash beads twice with 70% ethanol + air_vol = pipette.max_volume * AIR_VOL_COEFF + for cycle in range(2): + for target in samples: + pipette.transfer(ETHANOL_VOL, ethanol, target, air_gap=air_vol) + protocol.delay(minutes=WASH_TIME) + for target in samples: + pipette.transfer(ETHANOL_VOL + ETHANOL_DEAD_VOL, target, liquid_waste, air_gap=air_vol) + + # Dry at room temperature + protocol.delay(minutes=drying_time) + + # Disengage MagDeck + MAGDECK.disengage() + + # Mix beads with elution buffer + if elution_buffer_volume / 2 > pipette.max_volume: + mix_vol = pipette.max_volume + else: + mix_vol = elution_buffer_volume / 2 + for target in samples: + pipette.transfer(elution_buffer_volume, elution_buffer, target, mix_after=(ELUTION_MIX_REPS, mix_vol)) + + # Incubate at room temperature + protocol.delay(minutes=elution_time) + + # Engage MagDeck (remains engaged for DNA elution) + MAGDECK.engage(height=MAGDECK_HEIGHT) + protocol.delay(minutes=ELUTANT_SEP_TIME) + + # Transfer purified parts to a new well + for target, dest in zip(samples, output): + pipette.transfer(elution_buffer_volume - ELUTION_DEAD_VOL, target, + dest, blow_out=False) + + # Disengage MagDeck + MAGDECK.disengage() + + magbead(sample_number=sample_number, ethanol_well=ethanol_well) + # removed elution buffer well='A1', added that to where the function is defined diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/3_assembly_ot2_APIv1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/3_assembly_ot2_APIv1.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,84 @@ +from opentrons import labware, instruments, modules, robot +import numpy as np + + +final_assembly_dict={"A1": ["A7", "B7", "C7", "D7", "E7"], "B1": ["A7", "F7", "G7", "H7", "A8"], "C1": ["A7", "B8", "C8", "D8", "E8"]} +tiprack_num=1 + + +def final_assembly(final_assembly_dict, tiprack_num, tiprack_type="tiprack-10ul"): + """Implements final assembly reactions using an opentrons OT-2. + + Args: + final_assembly_dict (dict): Dictionary with keys and values corresponding to destination and associated linker-ligated part wells, respectively. + tiprack_num (int): Number of tipracks required during run. + + """ + + # Constants + CANDIDATE_TIPRACK_SLOTS = ['3', '6', '9', '2', '5', '8', '11'] + PIPETTE_MOUNT = 'right' + MAG_PLATE_TYPE = '4ti-0960_FrameStar' + MAG_PLATE_POSITION = '1' + TUBE_RACK_TYPE = 'tube-rack_E1415-1500' + TUBE_RACK_POSITION = '7' + DESTINATION_PLATE_TYPE = 'aluminium-block_4ti-0960_FrameStar' + TEMPDECK_SLOT = '4' + TEMP = 20 + TOTAL_VOL = 15 + PART_VOL = 1.5 + MIX_SETTINGS = (1, 3) + + # Errors + sample_number = len(final_assembly_dict.keys()) + if sample_number > 96: + raise ValueError('Final assembly nummber cannot exceed 96.') + + # Tips and pipette + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + tipracks = [labware.load(tiprack_type, slot) + for slot in slots] + pipette = instruments.P10_Single(mount=PIPETTE_MOUNT, tip_racks=tipracks) + + # Define Labware and set temperature + magbead_plate = labware.load(MAG_PLATE_TYPE, MAG_PLATE_POSITION) + tube_rack = labware.load(TUBE_RACK_TYPE, TUBE_RACK_POSITION) + tempdeck = modules.load('tempdeck', TEMPDECK_SLOT) + destination_plate = labware.load( + DESTINATION_PLATE_TYPE, TEMPDECK_SLOT, share=True) + tempdeck.set_temperature(TEMP) + tempdeck.wait_for_temp() + + # Master mix transfers + final_assembly_lens = [] + for values in final_assembly_dict.values(): + final_assembly_lens.append(len(values)) + unique_assemblies_lens = list(set(final_assembly_lens)) + master_mix_well_letters = ['A', 'B', 'C', 'D'] + for x in unique_assemblies_lens: + master_mix_well = master_mix_well_letters[(x - 1) // 6] + str(x - 1) + destination_inds = [i for i, lens in enumerate( + final_assembly_lens) if lens == x] + destination_wells = np.array( + [key for key, value in list(final_assembly_dict.items())]) + destination_wells = list(destination_wells[destination_inds]) + pipette.pick_up_tip() + pipette.transfer(TOTAL_VOL - x * PART_VOL, tube_rack.wells(master_mix_well), + destination_plate.wells(destination_wells), + new_tip='never') + pipette.drop_tip() + + # Part transfers + for key, values in list(final_assembly_dict.items()): + pipette.transfer(PART_VOL, magbead_plate.wells(values), + destination_plate.wells(key), mix_after=MIX_SETTINGS, + new_tip='always') + + tempdeck.deactivate() + + +final_assembly(final_assembly_dict=final_assembly_dict, + tiprack_num=tiprack_num) + +for c in robot.commands(): + print(c) diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/3_assembly_ot2_APIv2.8.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/3_assembly_ot2_APIv2.8.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,92 @@ +from opentrons import protocol_api +import numpy as np +# metadata +metadata = { +'protocolName': 'My Protocol', +'description': 'Simple protocol to get started using OT2', +'apiLevel': '2.8' +} + +# protocol run function. the part after the colon lets your editor know + + +# test dict can be used for simulation +#final_assembly_dict={ "A1": ['A7', 'B7', 'C7', 'F7'], "B1": ['A7', 'B7', 'D7', 'G7'], "C1": ['A7', 'B7', 'E7', 'H7']} +#tiprack_num=1 + +# __LABWARES is expected to be redefined by "generate_ot2_script" method +# Test dict +# __LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + +final_assembly_dict={"A1": ["A7", "B7", "C7", "D7", "E7"], "B1": ["A7", "F7", "G7", "H7", "A8"], "C1": ["A7", "B8", "C8", "D8", "E8"]} +tiprack_num=1 +__LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + + +def run(protocol: protocol_api.ProtocolContext): + def final_assembly(final_assembly_dict, tiprack_num, tiprack_type=__LABWARES['96_tiprack_20ul']['id']): + # Constants, we update all the labware name in version 2 + #Tiprack + CANDIDATE_TIPRACK_SLOTS = ['3', '6', '9', '2', '5', '8', '11'] + PIPETTE_MOUNT = 'right' + #Plate of sample after purification + MAG_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_23']['id'] + MAG_PLATE_POSITION = '1' + #Tuberack + TUBE_RACK_TYPE = __LABWARES['24_tuberack_1500ul']['id'] + TUBE_RACK_POSITION = '7' + #Destination plate + DESTINATION_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_23']['id'] + #Temperature control plate + TEMPDECK_SLOT = '4' + TEMP = 20 + TOTAL_VOL = 15 + PART_VOL = 1.5 + MIX_SETTINGS = (1, 3) + tiprack_num=tiprack_num+1 + # Errors + sample_number = len(final_assembly_dict.keys()) + if sample_number > 96: + raise ValueError('Final assembly nummber cannot exceed 96.') + + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + tipracks = [protocol.load_labware(tiprack_type, slot) for slot in slots] + pipette = protocol.load_instrument(__LABWARES['p20_single']['id'], PIPETTE_MOUNT, tip_racks=tipracks) + + + # Define Labware and set temperature + magbead_plate = protocol.load_labware(MAG_PLATE_TYPE, MAG_PLATE_POSITION) + tube_rack = protocol.load_labware(TUBE_RACK_TYPE, TUBE_RACK_POSITION) + tempdeck = protocol.load_module('tempdeck', TEMPDECK_SLOT) + destination_plate = tempdeck.load_labware( + DESTINATION_PLATE_TYPE, TEMPDECK_SLOT) + tempdeck.set_temperature(TEMP) + + # Master mix transfers + final_assembly_lens = [] + for values in final_assembly_dict.values(): + final_assembly_lens.append(len(values)) + unique_assemblies_lens = list(set(final_assembly_lens)) + master_mix_well_letters = ['A', 'B', 'C', 'D'] + for x in unique_assemblies_lens: + master_mix_well = master_mix_well_letters[(x - 1) // 6] + str(x - 1) + destination_inds = [i for i, lens in enumerate(final_assembly_lens) if lens == x] + destination_wells = np.array([key for key, value in list(final_assembly_dict.items())]) + destination_wells = list(destination_wells[destination_inds]) + for destination_well in destination_wells:# make tube_rack_wells and destination_plate.wells in the same type + pipette.pick_up_tip() + pipette.transfer(TOTAL_VOL - x * PART_VOL, tube_rack.wells(master_mix_well), + destination_plate.wells(destination_well), new_tip='never')#transfer water and buffer in the pipette + + pipette.drop_tip() + + # Part transfers + for key, values in list(final_assembly_dict.items()): + for value in values:# magbead_plate.wells and destination_plate.wells in the same type + pipette.transfer(PART_VOL, magbead_plate.wells(value), + destination_plate.wells(key), mix_after=MIX_SETTINGS, + new_tip='always')#transfer parts in one tube + + tempdeck.deactivate() #stop increasing the temperature + + final_assembly(final_assembly_dict=final_assembly_dict, tiprack_num=tiprack_num) diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/3_assembly_ot2_Thermocycler_APIv2.8.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/3_assembly_ot2_Thermocycler_APIv2.8.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,107 @@ +from opentrons import protocol_api +import numpy as np +# metadata +metadata = { +'protocolName': 'DNABOT Assembly Thermocycler', +'description': 'DNABOT Assembly Step3 with Thermocycler', +'apiLevel': '2.8' +} + +# It is possible to run 88 assemblies with this new module. The heat block module is removed. +# Assembly reactions is set up on thermocycler module. + + +# test dictionary can be used for simulation 3 or 88 assemblies +#final_assembly_dict={"A1": ['A7', 'B7', 'C7', 'F7'], "B1": ['A7', 'B7', 'D7', 'G7'], "C1": ['A7', 'B7', 'E7', 'H7']} +#tiprack_num=1 + +#final_assembly_dict={"A1": ["A7", "G7", "H7", "A8", "B8"], "B1": ["A7", "D8", "E8", "F8", "G8"], "C1": ["A7", "D8", "H7", "H8", "B9"], "D1": ["A7", "C9", "E9", "G9", "B8"], "E1": ["A7", "H9", "B10", "E9", "D10"], "F1": ["A7", "C9", "H8", "F10", "D10"], "G1": ["A7", "C9", "H10", "E8", "B9"], "H1": ["A7", "H9", "F8", "H10", "B11"], "A2": ["A7", "G7", "E8", "B10", "G8"], "B2": ["A7", "G7", "D11", "A8", "B9"], "C2": ["A7", "C9", "E9", "G9", "B9"], "D2": ["A7", "G7", "H7", "H8", "B8"], "E2": ["A7", "F11", "H11", "H7", "B12"], "F2": ["A7", "C9", "H8", "H11", "D10"], "G2": ["A7", "G7", "D11", "A8", "B8"], "H2": ["B7", "F11", "B10", "H10", "B11"], "A3": ["B7", "D8", "H7", "H8", "B8"], "B3": ["B7", "C9", "H10", "G9", "B8"], "C3": ["B7", "D12", "H8", "H11", "B11"], "D3": ["B7", "D12", "E9", "E8", "B8"], "E3": ["B7", "D12", "E9", "E8", "B9"], "F3": ["B7", "H9", "B10", "H10", "D10"], "G3": ["B7", "G7", "D11", "H8", "B8"], "H3": ["B7", "D12", "H10", "G9", "B9"], "A4": ["B7", "F11", "F10", "D11", "B12"], "B4": ["B7", "G7", "H7", "A8", "B9"], "C4": ["B7", "G7", "E8", "B10", "B12"], "D4": ["B7", "H9", "H11", "H7", "G8"], "E4": ["B7", "D8", "E8", "F8", "B12"], "F4": ["B7", "D12", "E9", "G9", "B8"], "G4": ["C7", "H9", "B10", "E9", "B11"], "H4": ["C7", "F11", "B10", "H10", "D10"], "A5": ["C7", "H9", "F8", "E9", "B11"], "B5": ["C7", "D12", "H8", "F10", "B11"], "C5": ["C7", "F11", "F8", "H10", "B11"], "D5": ["C7", "F11", "H11", "H7", "G8"], "E5": ["C7", "D8", "D11", "A8", "B9"], "F5": ["C7", "H9", "H11", "H7", "B12"], "G5": ["C7", "C9", "H10", "G9", "B9"], "H5": ["C7", "H9", "F10", "H7", "G8"], "A6": ["C7", "D12", "A8", "H11", "D10"], "B6": ["C7", "C9", "A8", "H11", "B11"], "C6": ["C7", "F11", "H11", "D11", "B12"], "D6": ["C7", "D8", "E8", "B10", "G8"], "E6": ["C7", "C9", "H8", "H11", "B11"], "F6": ["D7", "D8", "G9", "F8", "G8"], "G6": ["D7", "C9", "A8", "F10", "B11"], "H6": ["D7", "F11", "F10", "H7", "B12"], "A7": ["D7", "C9", "A8", "F10", "D10"], "B7": ["D7", "H9", "F8", "E9", "D10"], "C7": ["D7", "G7", "G9", "F8", "B12"], "D7": ["D7", "D12", "A8", "H11", "B11"], "E7": ["D7", "D12", "H10", "G9", "B8"], "F7": ["D7", "H9", "H11", "D11", "B12"], "G7": ["D7", "C9", "H8", "F10", "B11"], "H7": ["D7", "D8", "D11", "H8", "B8"], "A8": ["D7", "C9", "E9", "E8", "B9"], "B8": ["D7", "H9", "F10", "D11", "G8"], "C8": ["D7", "H9", "H11", "D11", "G8"], "D8": ["D7", "D12", "A8", "F10", "D10"], "E8": ["E7", "G7", "G9", "F8", "G8"], "F8": ["E7", "D12", "A8", "F10", "B11"], "G8": ["E7", "H9", "F10", "D11", "B12"], "H8": ["E7", "D8", "E8", "B10", "B12"], "A9": ["E7", "C9", "E9", "E8", "B8"], "B9": ["E7", "F11", "B10", "E9", "D10"], "C9": ["E7", "D12", "H8", "F10", "D10"], "D9": ["E7", "H9", "B10", "H10", "B11"], "E9": ["E7", "D8", "G9", "F8", "B12"], "F9": ["E7", "F11", "B10", "E9", "B11"], "G9": ["E7", "F11", "F8", "E9", "C11"], "H9": ["E7", "G7", "G9", "B10", "B12"], "A10": ["E7", "D8", "G9", "B10", "B12"], "B10": ["E7", "D8", "D11", "A8", "B8"], "C10": ["E7", "F11", "F10", "H7", "G8"], "D10": ["F7", "F11", "F8", "E9", "D10"], "E10": ["F7", "H9", "F10", "H7", "B12"], "F10": ["F7", "D12", "H10", "E8", "B9"], "G10": ["F7", "C9", "H10", "E8", "B8"], "H10": ["F7", "F11", "F8", "H10", "D10"], "A11": ["F7", "D12", "H10", "E8", "B8"], "B11": ["F7", "G7", "H7", "H8", "B9"], "C11": ["F7", "G7", "G9", "B10", "G8"], "D11": ["F7", "D12", "H8", "H11", "D10"], "E11": ["F7", "D9", "A8", "H11", "D10"], "F11": ["F7", "G7", "D11", "H8", "B9"], "G11": ["F7", "F11", "A12", "D11", "G8"], "H11": ["F7", "D8", "D11", "A9", "B9"]} +#tiprack_num=5 + +# __LABWARES is expected to be redefined by "generate_ot2_script" method +# Test dict +# __LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + +final_assembly_dict={"A1": ["A7", "B7", "C7", "D7", "E7"], "B1": ["A7", "F7", "G7", "H7", "A8"], "C1": ["A7", "B8", "C8", "D8", "E8"]} +tiprack_num=1 +__LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} + + +def run(protocol: protocol_api.ProtocolContext): + def final_assembly(final_assembly_dict, tiprack_num, tiprack_type=__LABWARES['96_tiprack_20ul']['id']): + # Constants, we update all the labware name in version 2 + #Tiprack + CANDIDATE_TIPRACK_SLOTS = ['2', '3', '5', '6', '9'] + PIPETTE_MOUNT = 'right' + #Plate of sample after purification + MAG_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_23']['id'] + MAG_PLATE_POSITION = '1' + #Tuberack + TUBE_RACK_TYPE = __LABWARES['24_tuberack_1500ul']['id'] + TUBE_RACK_POSITION = '4' + #Destination plate + DESTINATION_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_23']['id'] + TOTAL_VOL = 15 + PART_VOL = 1.5 + MIX_SETTINGS = (1, 3) + tiprack_num=tiprack_num+1 + # Errors + sample_number = len(final_assembly_dict.keys()) + if sample_number > 96: + raise ValueError('Final assembly nummber cannot exceed 96.') + + slots = CANDIDATE_TIPRACK_SLOTS[:tiprack_num] + tipracks = [protocol.load_labware(tiprack_type, slot) for slot in slots] + pipette = protocol.load_instrument(__LABWARES['p20_single']['id'], PIPETTE_MOUNT, tip_racks=tipracks) + + # Define Labware and set temperature + magbead_plate = protocol.load_labware(MAG_PLATE_TYPE, MAG_PLATE_POSITION) + tube_rack = protocol.load_labware(TUBE_RACK_TYPE, TUBE_RACK_POSITION) + + + #Thermocycler Module + tc_mod = protocol.load_module('Thermocycler Module') + destination_plate = tc_mod.load_labware(DESTINATION_PLATE_TYPE) + tc_mod.set_block_temperature(20) + + + # Master mix transfers + final_assembly_lens = [] + for values in final_assembly_dict.values(): + final_assembly_lens.append(len(values)) + unique_assemblies_lens = list(set(final_assembly_lens)) + master_mix_well_letters = ['A', 'B', 'C', 'D'] + + for x in unique_assemblies_lens: + master_mix_well = master_mix_well_letters[(x - 1) // 6] + str(x - 1) + destination_inds = [i for i, lens in enumerate(final_assembly_lens) if lens == x] + destination_wells = np.array([key for key, value in list(final_assembly_dict.items())]) + destination_wells = list(destination_wells[destination_inds]) + + pipette.pick_up_tip() + for destination_well in destination_wells:# make tube_rack_wells and destination_plate.wells in the same type + + pipette.transfer(TOTAL_VOL - x * PART_VOL, tube_rack.wells(master_mix_well), + destination_plate.wells(destination_well), new_tip='never')#transfer water and buffer in the pipette + + pipette.drop_tip() + + # Part transfers + for key, values in list(final_assembly_dict.items()): + for value in values:# magbead_plate.wells and destination_plate.wells in the same type + pipette.transfer(PART_VOL, magbead_plate.wells(value), + destination_plate.wells(key), mix_after=MIX_SETTINGS, + new_tip='always')#transfer parts in one tube + + + + #Thermocycler Module + tc_mod.close_lid() + tc_mod.set_lid_temperature(105) + tc_mod.set_block_temperature(50, hold_time_minutes=45, block_max_volume=15) + tc_mod.set_block_temperature(4, hold_time_minutes=2, block_max_volume=30) + # Increase the hold time at 4 C if necessary + tc_mod.set_lid_temperature(37) + tc_mod.open_lid() + + final_assembly(final_assembly_dict=final_assembly_dict, tiprack_num=tiprack_num) diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/4_transformation_ot2_APIv1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/4_transformation_ot2_APIv1.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,318 @@ +from opentrons import labware, instruments, modules, robot +import numpy as np + + +spotting_tuples=[(('A1', 'B1', 'C1'), ('A1', 'B1', 'C1'), (5, 5, 5))] +soc_well='A1' + + +def generate_transformation_wells(spotting_tuples): + """Evaluates spotting_tuples and returns transformation wells. + + Args: + spotting_tuples (list): Sets of spotting reactions are given + in the form: ((source wells), (target wells), (spotting volumes)). + Each unique transformation well is resuspended once prior to spotting. + + """ + wells = [] + for spotting_tuple in spotting_tuples: + for source_well in spotting_tuple[0]: + wells.append(source_well) + transformation_wells = [well for i, well in enumerate( + wells) if wells.index(well) == i] + return transformation_wells + + +def tiprack_slots( + spotting_tuples, + max_spot_vol=5): + """Calculates p10 and p300 tiprack slots required. + + Args: + spotting_tuples (list): Sets of spotting reactions are given + in the form: ((source wells), (target wells), (spotting volumes)). + Each unique transformation well is resuspended once prior to spotting. + max_spot_vol (float): Maximum volume that is spotted per spot reaction. + + """ + # Reactions' number + transformation_reactions = len( + generate_transformation_wells(spotting_tuples)) + spotting_reactions = 0 + for spotting_tuple in spotting_tuples: + spots = np.array(spotting_tuple[2])/max_spot_vol + np.ceil(spots) + spotting_reactions = spotting_reactions + int(np.sum(spots)) + + # p10 tiprack slots + p10_tips = transformation_reactions + spotting_reactions + p10_tiprack_slots = p10_tips // 96 + 1 if p10_tips % 96 > 0 else p10_tips / 96 + + # p300 tiprack slots + p300_tips = transformation_reactions + spotting_reactions + p300_tiprack_slots = p300_tips // 96 + \ + 1 if p300_tips % 96 > 0 else p300_tips / 96 + return int(p10_tiprack_slots), int(p300_tiprack_slots) + + +def transformation_setup(transformation_wells): + """Sets up transformation reactions + + Args: + transformation_wells (list). + + """ + + # Constants + TEMP = 4 # Incubation temperature. + ASSEMBLY_VOL = 5 # Volume of final assembly added to competent cells. + MIX_SETTINGS = (4, 5) # Mix after setting during final assembly transfers. + INCUBATION_TIME = 20 # Cells and final assembly incubation time. + + # Set temperature deck to 4 °C and load competent cells + tempdeck.set_temperature(TEMP) + tempdeck.wait_for_temp() + robot.pause() + robot.comment('Load competent cells, uncap and resume run') + + # Transfer final assemblies + p10_pipette.transfer(ASSEMBLY_VOL, + assembly_plate.wells(transformation_wells), + transformation_plate.wells( + transformation_wells), new_tip='always', + mix_after=(MIX_SETTINGS)) + + # Incubate for 20 minutes and remove competent cells for heat shock + p10_pipette.delay(minutes=INCUBATION_TIME) + robot.pause() + robot.comment( + 'Remove transformation reactions, conduct heatshock and replace.') + + +def phase_switch(comment='Remove final assembly plate. Introduce agar tray and deep well plate containing SOC media. Resume run.'): + """Function pauses run enabling addition/removal of labware. + + Args: + comment (str): string to be displayed during run following pause. + + """ + robot.pause() + robot.comment(comment) + + +def outgrowth( + cols, + soc_well): + """Outgrows transformed cells. + + Args: + cols (list of str): list of cols in transformation plate containing samples. + soc_well (str): Well containing SOC media in relevant plate. + + """ + + # Constants + SOC_VOL = 125 + SOC_MIX_SETTINGS = (4, 50) + TEMP = 37 + OUTGROWTH_TIME = 60 + SOC_ASPIRATION_RATE = 25 + P300_DEFAULT_ASPIRATION_RATE = 150 + + # Define wells + transformation_cols = transformation_plate.cols(cols) + soc = soc_plate.wells(soc_well) + + # Add SOC to transformed cells + p300_pipette.set_flow_rate(aspirate=SOC_ASPIRATION_RATE) + p300_pipette.transfer(SOC_VOL, soc, transformation_cols, + new_tip='always', mix_after=SOC_MIX_SETTINGS) + p300_pipette.set_flow_rate(aspirate=P300_DEFAULT_ASPIRATION_RATE) + + # Incubate for 1 hour at 37 °C + tempdeck.set_temperature(TEMP) + tempdeck.wait_for_temp() + p300_pipette.delay(minutes=OUTGROWTH_TIME) + tempdeck.deactivate() + + +def spotting_cols(spotting_tuples): + """Evaluates spotting_tuples and returns unique cols (str) + associated with each spotting_tuple's source wells. + + Args: + spotting_tuples (list): Sets of spotting reactions are given + in the form: ((source wells), (target wells), (spotting volumes)). + Each unique transformation well is resuspended once prior to spotting. + + """ + cols_list = [] + for spotting_tuple in spotting_tuples: + source_wells_cols = [source_well[1:] + for source_well in spotting_tuple[0]] + unique_cols = [col for i, col in enumerate( + source_wells_cols) if source_wells_cols.index(col) == i] + cols_list.append(unique_cols) + return cols_list + + +def spot_transformations( + spotting_tuples, + dead_vol=2, + spotting_dispense_rate=0.025, + stabbing_depth=10, + max_spot_vol=5): + """Spots transformation reactions. + + Args: + spotting_tuples (list): Sets of spotting reactions are given + in the form: ((source wells), (target wells), (spotting volumes)). + Each unique source well is resuspended once prior to spotting. + dead_vol (float): Dead volume aspirated during spotting. + spotting_dispense_rate (float): Rate p10_pipette dispenses at during spotting. + stabbing_depth (float): Depth p10_pipette moves into agar during spotting. + max_spot_vol (float): Maximum volume that is spotted per spot reaction. + + """ + + def spot( + source, + target, + spot_vol): + """Spots an individual reaction using the p10 pipette. + + Args: + source (str): Well containing the transformation reaction to be spotted. + target (str): Well transformation reaction is to be spotted to. + spot_vol (float): Volume of transformation reaction to be spotted (uL). + + """ + # Constants + DEFAULT_HEAD_SPEED = {'x': 400, 'y': 400, + 'z': 125, 'a': 125} + SPOT_HEAD_SPEED = {'x': 400, 'y': 400, 'z': 125, + 'a': 125 // 4} + DISPENSING_HEIGHT = 5 + SAFE_HEIGHT = 15 # height avoids collision with agar tray. + + # Spot + p10_pipette.pick_up_tip() + p10_pipette.aspirate(spot_vol + dead_vol, source) + p10_pipette.move_to(target.top(SAFE_HEIGHT)) + p10_pipette.move_to(target.top(DISPENSING_HEIGHT)) + p10_pipette.dispense(volume=spot_vol, rate=spotting_dispense_rate) + robot.head_speed(combined_speed=max( + SPOT_HEAD_SPEED.values()), **SPOT_HEAD_SPEED) + p10_pipette.move_to(target.top(-1 * stabbing_depth)) + robot.head_speed(combined_speed=max( + DEFAULT_HEAD_SPEED.values()), **DEFAULT_HEAD_SPEED) + p10_pipette.move_to(target.top(SAFE_HEIGHT)) + + # Dispose of dead volume and tip + p10_pipette.dispense(dead_vol, spotting_waste) + p10_pipette.blow_out() + p10_pipette.drop_tip() + + def spot_tuple(spotting_tuple): + """Spots all reactions defined by the spotting tuple. Requires the function spot. + + Args: + spotting_tuple (tuple): Spotting reactions given in the form: (source wells), (target wells), (spotting volumes). + + """ + source_wells = spotting_tuple[0] + target_wells = spotting_tuple[1] + spot_vols = list(spotting_tuple[2]) + while max(spot_vols) > 0: + for index, spot_vol in enumerate(spot_vols): + if spot_vol == 0: + pass + else: + vol = spot_vol if spot_vol <= max_spot_vol else max_spot_vol + spot(transformation_plate.wells(source_wells[index]), + agar_plate.wells(target_wells[index]), vol) + spot_vols[index] = spot_vols[index] - vol + + # Constants + TRANSFORMATION_MIX_SETTINGS = [4, 50] + + # Spot transformation reactions + for spotting_tuple in spotting_tuples: + source_wells_cols = [source_well[1:] + for source_well in spotting_tuple[0]] + unique_cols = [col for i, col in enumerate( + source_wells_cols) if source_wells_cols.index(col) == i] + for col in unique_cols: + p300_pipette.pick_up_tip() + p300_pipette.mix(TRANSFORMATION_MIX_SETTINGS[0], + TRANSFORMATION_MIX_SETTINGS[1], + transformation_plate.cols(col)) + p300_pipette.drop_tip() + spot_tuple(spotting_tuple) + + +# Run protocol + +# Constants +CANDIDATE_P10_SLOTS = ['9', '2', '5'] +CANDIDATE_P300_SLOTS = ['3', '6'] +P10_TIPRACK_TYPE = 'tiprack-10ul' +P300_TIPRACK_TYPE = 'opentrons_96_tiprack_300ul' +P10_MOUNT = 'right' +P300_MOUNT = 'left' +ASSEMBLY_PLATE_TYPE = '4ti-0960_FrameStar' +ASSEMBLY_PLATE_SLOT = '8' +TEMPDECK_SLOT = '10' +TRANSFORMATION_PLATE_TYPE = 'Eppendorf_30133366_plate_96' +SOC_PLATE_TYPE = '4ti0136_96_deep-well' +SOC_PLATE_SLOT = '7' +TUBE_RACK_TYPE = 'tube-rack_E1415-1500' +TUBE_RACK_SLOT = '11' +SPOTTING_WASTE_WELL = 'A1' +AGAR_PLATE_TYPE = 'Nunc_Omnitray' +AGAR_PLATE_SLOT = '1' + +# Tiprack slots +p10_p300_tiprack_slots = tiprack_slots(spotting_tuples) +p10_slots = CANDIDATE_P10_SLOTS[ + :p10_p300_tiprack_slots[0]] +p300_slots = CANDIDATE_P300_SLOTS[ + :p10_p300_tiprack_slots[1]] + +# Define labware +p10_tipracks = [labware.load(P10_TIPRACK_TYPE, slot) + for slot in p10_slots] +p300_tipracks = [labware.load(P300_TIPRACK_TYPE, slot) + for slot in p300_slots] +p10_pipette = instruments.P10_Single( + mount=P10_MOUNT, tip_racks=p10_tipracks) +p300_pipette = instruments.P300_Multi( + mount=P300_MOUNT, tip_racks=p300_tipracks) + +assembly_plate = labware.load(ASSEMBLY_PLATE_TYPE, ASSEMBLY_PLATE_SLOT) +tempdeck = modules.load('tempdeck', TEMPDECK_SLOT) +transformation_plate = labware.load(TRANSFORMATION_PLATE_TYPE, + TEMPDECK_SLOT, share=True) +soc_plate = labware.load(SOC_PLATE_TYPE, SOC_PLATE_SLOT) +tube_rack = labware.load(TUBE_RACK_TYPE, TUBE_RACK_SLOT) +spotting_waste = tube_rack.wells(SPOTTING_WASTE_WELL) +agar_plate = labware.load(AGAR_PLATE_TYPE, AGAR_PLATE_SLOT) + +# Register agar_plate for calibration +p10_pipette.transfer(1, agar_plate.wells( + 'A1'), agar_plate.wells('H12'), trash=False) +p10_pipette.start_at_tip(p10_tipracks[0][0]) + +# Run functions +transformation_setup(generate_transformation_wells(spotting_tuples)) +phase_switch() +spotting_tuples_cols = [col for cols in spotting_cols( + spotting_tuples) for col in cols] +unique_cols = [col for i, col in enumerate( + spotting_tuples_cols) if spotting_tuples_cols.index(col) == i] +outgrowth(unique_cols, soc_well=soc_well) +spot_transformations(spotting_tuples) + +for c in robot.commands(): + print(c) \ No newline at end of file diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/4_transformation_ot2_APIv2.8.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/4_transformation_ot2_APIv2.8.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,422 @@ +from opentrons import protocol_api +import numpy as np + + +# Rename to 'purification_template' and paste into 'template_ot2_scripts' folder in DNA-BOT to use + + +metadata = { + 'apiLevel': '2.8', + 'protocolName': 'Transformation', + 'description': 'Transformation reactions using an opentrons OT-2 for BASIC assembly.'} + +# Example output produced by DNA-BOT for a single construct, uncomment and run to test the template +#spotting_tuples=[(('A1','B1','C1'), ('A1','B1', 'C1'), (5,5,5))] +#soc_well='A1' + +# Example output produced by DNA-BOT for 88 constructs, uncomment and run to test the template +#spotting_tuples=[(('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1'), ('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2'), ('A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3'), ('A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4'), ('A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5'), ('A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6'), ('A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7'), ('A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8'), ('A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A9', 'B9', 'C9', 'D9', 'E9', 'F9', 'G9', 'H9'), ('A9', 'B9', 'C9', 'D9', 'E9', 'F9', 'G9', 'H9'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A10', 'B10', 'C10', 'D10', 'E10', 'F10', 'G10', 'H10'), ('A10', 'B10', 'C10', 'D10', 'E10', 'F10', 'G10', 'H10'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11'), ('A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11'), (5, 5, 5, 5, 5, 5, 5, 5))] +#soc_well='A1' + +# __LABWARES is expected to be redefined by "generate_ot2_script" method +# Test dict +# __LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} +# __PARAMETERS={"purif_magdeck_height": {"value": 20.0}, "purif_wash_time": {"value": 0.5}, "purif_bead_ratio": {"value": 1.8}, "purif_incubation_time": {"value": 5.0}, "purif_settling_time": {"value": 2.0}, "purif_drying_time": {"value": 5.0}, "purif_elution_time": {"value": 2.0}, "transfo_incubation_temp": {"value": 4.0}, "transfo_incubation_time": {"value": 20.0}} + +spotting_tuples=[(('A1', 'B1', 'C1'), ('A1', 'B1', 'C1'), (5, 5, 5))] +soc_well='A1' +__LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} +__PARAMETERS={"purif_magdeck_height": {"value": 20}, "purif_wash_time": {"value": 0}, "purif_bead_ratio": {"value": 1}, "purif_incubation_time": {"value": 5}, "purif_settling_time": {"value": 2}, "purif_drying_time": {"value": 5}, "purif_elution_time": {"value": 2}, "transfo_incubation_temp": {"value": 4}, "transfo_incubation_time": {"value": 20}} + + +def run(protocol: protocol_api.ProtocolContext): +# added run function for API version 2 + +# Constants + CANDIDATE_p20_SLOTS = ['9', '2', '5'] + CANDIDATE_P300_SLOTS = ['3', '6'] + p20_TIPRACK_TYPE = __LABWARES['96_tiprack_20ul']['id'] + # changed from 'tiprack-10ul' + P300_TIPRACK_TYPE = __LABWARES['96_tiprack_300ul']['id'] + P20_MOUNT = 'right' + P300_MOUNT = 'left' + ASSEMBLY_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + ASSEMBLY_PLATE_SLOT = '8' + + TRANSFORMATION_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + SOC_PLATE_TYPE = __LABWARES['96_deepwellplate_2ml']['id'] + SOC_PLATE_SLOT = '7' + TUBE_RACK_TYPE = __LABWARES['24_tuberack_1500ul']['id'] + TUBE_RACK_SLOT = '11' + SPOTTING_WASTE_WELL = 'A1' + AGAR_PLATE_TYPE = __LABWARES['agar_plate_step_4']['id'] + # changed from 'Nunc_Omnitray' + # it is a 1 well plate filled with agar; + # but for the Opentron to spot in the locations of a 96 wp, it is defined similar to a 96 wp + # was previously defined in add.labware.py, API version 2 doesn't support labware.create anymore + + + + # custom labware made using Opentron's Labware Creator: + # external dimensions: + # footprint length = 127.76 mm + # footrpint width = 85.48 mm + # footprint height = 15.70 mm + # taken from Thermofisher's documentation for Nunc Omnitray + # https://www.thermofisher.com/document-connect/document-connect.html?url=https%3A%2F%2Fassets.thermofisher.com%2FTFS-Assets%2FLSG%2Fmanuals%2FD03023.pdf&title=VGVjaG5pY2FsIERhdGEgU2hlZXQ6IE51bmMgT21uaXRyYXk= + # well measurements + # depth = 0.01 mm + # diameter = 0.01 mm + # in old add.labware.py, they were defined as 0, but Labware Creator requires a value >0 + # spacing + # x-offset = 14.38 mm + # y-offset = 11.24 mm + # x-spacing = 9.00 mm + # y-spacing) = 9.00 mm + # taken from Nest 96 well plates + # https://labware.opentrons.com/nest_96_wellplate_100ul_pcr_full_skirt/ + # before using protocol, need to upload the 'nuncomnitray_96_wellplate_0.01ul.json' custom labware file into Opentrons app + + AGAR_PLATE_SLOT = '1' + + TEMPDECK_SLOT = '10' + + + + def generate_transformation_wells(spotting_tuples): + """ + Evaluates spotting_tuples and returns transformation wells. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + + """ + + wells = [] + for spotting_tuple in spotting_tuples: + for source_well in spotting_tuple[0]: + wells.append(source_well) + transformation_wells = [well for i, well in enumerate( + wells) if wells.index(well) == i] + return transformation_wells + + + def tiprack_slots(spotting_tuples, max_spot_vol=5): + """ + Calculates p20 and p300 tiprack slots required. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + max_spot_vol (float): Maximum volume that is spotted per spot reaction. + + """ + + # Reactions' number + transformation_reactions = len(generate_transformation_wells(spotting_tuples)) + spotting_reactions = 0 + for spotting_tuple in spotting_tuples: + spots = np.array(spotting_tuple[2])/max_spot_vol + np.ceil(spots) + spotting_reactions = spotting_reactions + int(np.sum(spots)) + + # errrr should be fine lol # REMOVE + + # p20 tiprack slots + p20_tips = transformation_reactions + spotting_reactions + p20_tiprack_slots = p20_tips // 96 + 1 if p20_tips % 96 > 0 else p20_tips / 96 + + # p300 tiprack slots + p300_tips = transformation_reactions + spotting_reactions + p300_tiprack_slots = p300_tips // 96 + \ + 1 if p300_tips % 96 > 0 else p300_tips / 96 + return int(p20_tiprack_slots), int(p300_tiprack_slots) + + + def transformation_setup(transformation_wells): + """ + Sets up transformation reactions + + Args: + transformation_wells (list). + + """ + + # Constants + TEMP = __PARAMETERS['transfo_incubation_temp']['value'] # Incubation temperature. + ASSEMBLY_VOL = 5 # Volume of final assembly added to competent cells. + MIX_SETTINGS = (4, 5) # Mix after setting during final assembly transfers. + INCUBATION_TIME = __PARAMETERS['transfo_incubation_time']['value'] # Cells and final assembly incubation time. + + + + # Set temperature deck to 4 °C and load competent cells + tempdeck.set_temperature(TEMP) + # removed: tempdeck.wait_for_temp() + # API version2 automatically pauses execution until the set temperature is reached + # thus it no longer uses .wait_for_temp() + protocol.pause('Load competent cells, uncap and resume run') + + # Transfer final assemblies + p20_pipette.transfer(ASSEMBLY_VOL, + [assembly_plate.wells_by_name()[well_name] for well_name in transformation_wells], + [transformation_plate.wells_by_name()[well_name] for well_name in transformation_wells], + new_tip='always', + mix_after=(MIX_SETTINGS)) + + + # Incubate for 20 minutes and remove competent cells for heat shock + protocol.delay(minutes=INCUBATION_TIME) + + + protocol.pause('Remove transformation reactions, conduct heatshock and replace.') + + + def phase_switch(): + """ + Function pauses run enabling addition/removal of labware. + + """ + protocol.pause('Remove final assembly plate. Introduce agar tray and deep well plate containing SOC media. Resume run.') + + def outgrowth( + cols, + soc_well): + """ + Outgrows transformed cells. + + Args: + cols (list of str): list of cols in transformation plate containing samples. + soc_well (str): Well containing SOC media in relevant plate. + + """ + + # Constants + SOC_VOL = 125 + SOC_MIX_SETTINGS = (4, 50) + TEMP = 37 + OUTGROWTH_TIME = 60 + SOC_ASPIRATION_RATE = 25 + P300_DEFAULT_ASPIRATION_RATE = 150 + + # Define wells + transformation_cols = [transformation_plate.columns_by_name()[column] for column in cols] + + soc = soc_plate.wells(soc_well) + + # Add SOC to transformed cells + p300_pipette.flow_rate.aspirate = SOC_ASPIRATION_RATE + p300_pipette.transfer(SOC_VOL, soc, transformation_cols, + new_tip='always', mix_after=SOC_MIX_SETTINGS) + p300_pipette.flow_rate.aspirate = P300_DEFAULT_ASPIRATION_RATE + + # Incubate for 1 hour at 37 °C + tempdeck.set_temperature(TEMP) + + protocol.delay(minutes=OUTGROWTH_TIME) + + + tempdeck.deactivate() + + + def spotting_cols(spotting_tuples): + """ + Evaluates spotting_tuples and returns unique cols (str) associated with each spotting_tuple's source wells. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + + """ + cols_list = [] + for spotting_tuple in spotting_tuples: + source_wells_cols = [source_well[1:] for source_well in spotting_tuple[0]] + unique_cols = [col for i, col in enumerate(source_wells_cols) if source_wells_cols.index(col) == i] + cols_list.append(unique_cols) + return cols_list + + + def spot_transformations( + spotting_tuples, + dead_vol=1, + spotting_dispense_rate=0.025, + stabbing_depth=10, + max_spot_vol=5): + """ + Spots transformation reactions. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + dead_vol (float): Dead volume aspirated during spotting. + spotting_dispense_rate (float): Rate p20_pipette dispenses at during spotting. + stabbing_depth (float): Depth p20_pipette moves into agar during spotting. + max_spot_vol (float): Maximum volume that is spotted per spot reaction. + + """ + + def spot( + source, + target, + spot_vol): + """ + Spots an individual reaction using the p20 pipette. + + Args: + source (str): Well containing the transformation reaction to be spotted. + target (str): Well transformation reaction is to be spotted to. + spot_vol (float): Volume of transformation reaction to be spotted (uL). + + """ + + # Constants + DEFAULT_HEAD_SPEED = {'x': 400, 'y': 400,'z': 125, 'a': 125} + SPOT_HEAD_SPEED = {'x': 400, 'y': 400, 'z': 125,'a': 125 // 4} + DISPENSING_HEIGHT = 5 + SAFE_HEIGHT = 7 # height avoids collision with agar tray. + + # Spot + p20_pipette.pick_up_tip() + p20_pipette.aspirate(spot_vol + dead_vol, source[0]) + # old code: + # p20_pipette.aspirate(spot_vol + dead_vol, source) + # returned type error because 'source' was a list containing one item (the well location) + # source[0] takes the location out of the list + + p20_pipette.move_to(target[0].top(SAFE_HEIGHT)) + p20_pipette.move_to(target[0].top(DISPENSING_HEIGHT)) + # old code: + # p20_pipette.move_to(target.top(SAFE_HEIGHT)) + # p20_pipette.move_to(target.top(DISPENSING_HEIGHT)) + # returned attribute error because 'target' was a list containing one item (the well location) + # target[0] takes the location out of the list + + p20_pipette.dispense(volume=spot_vol, rate=spotting_dispense_rate) + + protocol.max_speeds.update(SPOT_HEAD_SPEED) + # old code: + # robot.head_speed(combined_speed=max(SPOT_HEAD_SPEED.values()), **SPOT_HEAD_SPEED) + # robot.head_speed not used in API version 2 + # replaced with protocol.max_speeds + # new code no longer uses the lower value between combined speed or specified speed + # just uses each axis' specified speed directly + p20_pipette.move_to(target[0].top(-1 * stabbing_depth)) + # old code: + # p20_pipette.move_to(target.top(-1*stabbing_depth)) + # returns attribute error because 'target' was a list containing one item (the well location) + protocol.max_speeds.update(DEFAULT_HEAD_SPEED) + # old code: + # robot.head_speed(combined_speed=max(DEFAULT_HEAD_SPEED.values()), **DEFAULT_HEAD_SPEED) + # robot.head_speed not used in API version 2 + # replaced with protocol.max_speeds + # new code no longer uses the lower value between combined speed or specified speed + # just uses each axis' specified speed directly + p20_pipette.move_to(target[0].top(SAFE_HEIGHT)) + # old code: + # p20_pipette.move_to(target[0].top(SAFE_HEIGHT)) + # returns attribute error because 'target' was a list containing one item (the well location) + + + # the code below makes sure that the transformend cells are efficiently reaching to the agar surface + + p20_pipette.blow_out() + + protocol.delay(seconds=10) + + p20_pipette.blow_out() + + protocol.delay(seconds=10) + + # Dispose of dead volume and tip + p20_pipette.dispense(dead_vol, spotting_waste[0]) + + p20_pipette.blow_out() + # the simple .blow_out command blows out at current position (spotting waste) by defualt + # unlike blowout=true in complex commands, which by default will blow out in waste + + p20_pipette.drop_tip() + + def spot_tuple(spotting_tuple): + """ + Spots all reactions defined by the spotting tuple. Requires the function spot. + + Args: + spotting_tuple (tuple): Spotting reactions given in the form: (source wells), (target wells), (spotting volumes). + Each unique source well is resuspended once prior to spotting. + + """ + source_wells = spotting_tuple[0] + target_wells = spotting_tuple[1] + spot_vols = list(spotting_tuple[2]) + while max(spot_vols) > 0: + for index, spot_vol in enumerate(spot_vols): + if spot_vol == 0: + pass + else: + vol = spot_vol if spot_vol <= max_spot_vol else max_spot_vol + spot(source = transformation_plate.wells(source_wells[index]), target = agar_plate.wells(target_wells[index]), spot_vol = vol) + spot_vols[index] = spot_vols[index] - vol + + # Constants + TRANSFORMATION_MIX_SETTINGS = [4, 50] + + # Spot transformation reactions + # Each unique transformation well is resuspended once prior to spotting. + + for spotting_tuple in spotting_tuples: + source_wells_cols = [source_well[1:] for source_well in spotting_tuple[0]] + unique_cols = [col for i, col in enumerate(source_wells_cols) if source_wells_cols.index(col) == i] + for col in unique_cols: + p300_pipette.pick_up_tip() + p300_pipette.mix(TRANSFORMATION_MIX_SETTINGS[0], TRANSFORMATION_MIX_SETTINGS[1],transformation_plate.columns_by_name()[col][0]) + p300_pipette.drop_tip() + spot_tuple(spotting_tuple) + + #Tiprack slots + p20_p300_tiprack_slots = tiprack_slots(spotting_tuples) + p20_slots = CANDIDATE_p20_SLOTS[:p20_p300_tiprack_slots[0]] + p300_slots = CANDIDATE_P300_SLOTS[:p20_p300_tiprack_slots[1]] + # Define labware + p20_tipracks = [protocol.load_labware(p20_TIPRACK_TYPE, slot) for slot in p20_slots] + # changed to protocol.load_labware for API version 2 + p300_tipracks = [protocol.load_labware(P300_TIPRACK_TYPE, slot) for slot in p300_slots] + # changed to protocol.load_labware for API version 2 + p20_pipette = protocol.load_instrument(__LABWARES['p20_single']['id'], P20_MOUNT, tip_racks=p20_tipracks) + # changed to protocol.load_instrument for API version 2 + p300_pipette = protocol.load_instrument(__LABWARES['p300_multi']['id'], P300_MOUNT, tip_racks=p300_tipracks) + # changed to protocol.load_instrument for API version 2 + assembly_plate = protocol.load_labware(ASSEMBLY_PLATE_TYPE, ASSEMBLY_PLATE_SLOT) + # changed to protocol.load_labware for API version 2 + tempdeck = protocol.load_module('tempdeck', TEMPDECK_SLOT) + transformation_plate = tempdeck.load_labware(TRANSFORMATION_PLATE_TYPE, TEMPDECK_SLOT) + # changed to protocol.load_labware for API version 2 + # removed share=True, not required in API version 2 + # removed TEMPDECK_SLOT as it is loaded directly onto temperature module + soc_plate = protocol.load_labware(SOC_PLATE_TYPE, SOC_PLATE_SLOT) + # changed to protocol.load_labware for API version 2 + tube_rack = protocol.load_labware(TUBE_RACK_TYPE, TUBE_RACK_SLOT) + # changed to protocol.load_labware for API version 2 + spotting_waste = tube_rack.wells(SPOTTING_WASTE_WELL) + agar_plate = protocol.load_labware(AGAR_PLATE_TYPE, AGAR_PLATE_SLOT) + # changed to protocol.load_labware for API version 2 + + ### Run protocol + + + # Register agar_plate for calibration + p20_pipette.transfer(1, agar_plate.wells('A1'), agar_plate.wells('H12'), trash=False) + # removed: + # p20_pipette.start_at_tip(p20_tipracks[0][0]) + # pipette automatically starts from 'A1' tiprack location + # if re-adding, need to use p20.pipette.starting_tip() instead of p20.pipette.start_at_tip() + + + # Run functions + # Run functions + transformation_setup(generate_transformation_wells(spotting_tuples)) + phase_switch() + spotting_tuples_cols = [col for cols in spotting_cols(spotting_tuples) for col in cols] + unique_cols = [col for i, col in enumerate(spotting_tuples_cols) if spotting_tuples_cols.index(col) == i] + outgrowth(cols=unique_cols, soc_well=soc_well) + spot_transformations(spotting_tuples) + # Spot again to increase the chances of getting more colonies. Second spotting will only work for small number of assemblies where there is enough space on the deck for tipracks. + # protocol.pause('Let the spotted cells get dry') + # spot_transformations(spotting_tuples) + print(unique_cols) diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/4_transformation_ot2_Thermocycler_APIv2.8.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/4_transformation_ot2_Thermocycler_APIv2.8.py Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,500 @@ +from opentrons import protocol_api +import numpy as np + + +# Rename to 'purification_template' and paste into 'template_ot2_scripts' folder in DNA-BOT to use +# Tip rack positions are limited for this script, up to 48 transformations can be performed. + +metadata = { + 'apiLevel': '2.8', + 'protocolName': 'Transformation', + 'description': 'Transformation reactions using an opentrons OT-2 for BASIC assembly.'} + +# Example output produced by DNA-BOT for a single construct, uncomment and run to test the template +#spotting_tuples=[(('A1','B1','C1'), ('A1','B1', 'C1'), (8,8,8))] +#soc_well='A1' + +# Example output produced by DNA-BOT for 88 constructs, uncomment and run to test the template +#spotting_tuples=[(('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1'), ('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2'), ('A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3'), ('A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4'), ('A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5'), ('A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6'), ('A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7'), ('A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8'), ('A8', 'B8', 'C8', 'D8', 'E8', 'F8', 'G8', 'H8'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A9', 'B9', 'C9', 'D9', 'E9', 'F9', 'G9', 'H9'), ('A9', 'B9', 'C9', 'D9', 'E9', 'F9', 'G9', 'H9'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A10', 'B10', 'C10', 'D10', 'E10', 'F10', 'G10', 'H10'), ('A10', 'B10', 'C10', 'D10', 'E10', 'F10', 'G10', 'H10'), (5, 5, 5, 5, 5, 5, 5, 5)), (('A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11'), ('A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11'), (5, 5, 5, 5, 5, 5, 5, 5))] +#soc_well='A1' + +# __LABWARES is expected to be redefined by "generate_ot2_script" method +# Test dict +# __LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} +# __PARAMETERS={"purif_magdeck_height": {"value": 20.0}, "purif_wash_time": {"value": 0.5}, "purif_bead_ratio": {"value": 1.8}, "purif_incubation_time": {"value": 5.0}, "purif_settling_time": {"value": 2.0}, "purif_drying_time": {"value": 5.0}, "purif_elution_time": {"value": 2.0}, "transfo_incubation_temp": {"value": 4.0}, "transfo_incubation_time": {"value": 20.0}} + +spotting_tuples=[(('A1', 'B1', 'C1'), ('A1', 'B1', 'C1'), (5, 5, 5))] +soc_well='A1' +__LABWARES={"p20_single": {"id": "p20_single_gen2"}, "p300_multi": {"id": "p300_multi_gen2"}, "mag_deck": {"id": "magdeck"}, "96_tiprack_20ul": {"id": "opentrons_96_tiprack_20ul"}, "96_tiprack_300ul": {"id": "opentrons_96_tiprack_300ul"}, "24_tuberack_1500ul": {"id": "e14151500starlab_24_tuberack_1500ul"}, "96_wellplate_200ul_pcr_step_14": {"id": "4ti0960rig_96_wellplate_200ul"}, "96_wellplate_200ul_pcr_step_23": {"id": "4ti0960rig_96_wellplate_200ul"}, "agar_plate_step_4": {"id": "4ti0960rig_96_wellplate_200ul"}, "12_reservoir_21000ul": {"id": "4ti0131_12_reservoir_21000ul"}, "96_deepwellplate_2ml": {"id": "4ti0136_96_wellplate_2200ul"}} +__PARAMETERS={"purif_magdeck_height": {"value": 20}, "purif_wash_time": {"value": 0}, "purif_bead_ratio": {"value": 1}, "purif_incubation_time": {"value": 5}, "purif_settling_time": {"value": 2}, "purif_drying_time": {"value": 5}, "purif_elution_time": {"value": 2}, "transfo_incubation_temp": {"value": 4}, "transfo_incubation_time": {"value": 20}} + + +def run(protocol: protocol_api.ProtocolContext): +# added run function for API version 2 + + # Constants + CANDIDATE_p20_SLOTS = ['3'] + CANDIDATE_P300_SLOTS = ['6'] + P20_TIPRACK_TYPE = __LABWARES['96_tiprack_10ul']['id'] + P300_TIPRACK_TYPE = __LABWARES['96_tiprack_300ul']['id'] + P20_MOUNT = 'right' + P300_MOUNT = 'left' + ASSEMBLY_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + ASSEMBLY_PLATE_SLOT = '2' + + TRANSFORMATION_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + SOC_PLATE_TYPE = __LABWARES['96_deepwellplate_2ml']['id'] + # changed from '4ti0136_96_deep-well' + SOC_PLATE_SLOT = '5' + TUBE_RACK_TYPE = __LABWARES['24_tuberack_1500ul']['id'] + # changed from 'tube-rack_E1415-1500' + TUBE_RACK_SLOT = '9' + SPOTTING_WASTE_WELL = 'A1' + AGAR_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + # changed from 'Nunc_Omnitray' + # it is a 1 well plate filled with agar; + # but for the Opentron to spot in the locations of a 96 wp, it is defined similar to a 96 wp + # was previously defined in add.labware.py, API version 2 doesn't support labware.create anymore + + # !!! CURRENT PLATE IS A PLACEHOLDER FOR RUNNING SIMMULATION WITHOUT CUSTOM LABWARE!!! + # !!! name made with Opentron Labware Creator = 'nuncomnitray_96_wellplate_0.01ul' !!!! + + + # custom labware made using Opentron's Labware Creator: + # external dimensions: + # footprint length = 127.76 mm + # footrpint width = 85.48 mm + # footprint height = 15.70 mm + # taken from Thermofisher's documentation for Nunc Omnitray + # https://www.thermofisher.com/document-connect/document-connect.html?url=https%3A%2F%2Fassets.thermofisher.com%2FTFS-Assets%2FLSG%2Fmanuals%2FD03023.pdf&title=VGVjaG5pY2FsIERhdGEgU2hlZXQ6IE51bmMgT21uaXRyYXk= + # well measurements + # depth = 0.01 mm + # diameter = 0.01 mm + # in old add.labware.py, they were defined as 0, but Labware Creator requires a value >0 + # spacing + # x-offset = 14.38 mm + # y-offset = 11.24 mm + # x-spacing = 9.00 mm + # y-spacing) = 9.00 mm + # taken from Nest 96 well plates + # https://labware.opentrons.com/nest_96_wellplate_100ul_pcr_full_skirt/ + # before using protocol, need to upload the 'nuncomnitray_96_wellplate_0.01ul.json' custom labware file into Opentrons app + + AGAR_PLATE_SLOT = '1' + + TEMPDECK_SLOT = '4' + + + def generate_transformation_wells(spotting_tuples): + """ + Evaluates spotting_tuples and returns transformation wells. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + + """ + + wells = [] + for spotting_tuple in spotting_tuples: + for source_well in spotting_tuple[0]: + wells.append(source_well) + transformation_wells = [well for i, well in enumerate( + wells) if wells.index(well) == i] + return transformation_wells + + + def tiprack_slots(spotting_tuples, max_spot_vol=5): + """ + Calculates p20 and p300 tiprack slots required. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + max_spot_vol (float): Maximum volume that is spotted per spot reaction. + + """ + + # Reactions' number + transformation_reactions = len(generate_transformation_wells(spotting_tuples)) + spotting_reactions = 0 + for spotting_tuple in spotting_tuples: + spots = np.array(spotting_tuple[2])/max_spot_vol + np.ceil(spots) + spotting_reactions = spotting_reactions + int(np.sum(spots)) + + # errrr should be fine lol # REMOVE + + # p20 tiprack slots + p20_tips = transformation_reactions + spotting_reactions + p20_tiprack_slots = p20_tips // 96 + 1 if p20_tips % 96 > 0 else p20_tips / 96 + + # p300 tiprack slots + p300_tips = transformation_reactions + spotting_reactions + p300_tiprack_slots = p300_tips // 96 + \ + 1 if p300_tips % 96 > 0 else p300_tips / 96 + return int(p20_tiprack_slots), int(p300_tiprack_slots) + + + def transformation_setup(transformation_wells): + """ + Sets up transformation reactions + + Args: + transformation_wells (list). + + """ + + # Constants + TEMP = __PARAMETERS['transfo_incubation_temp']['value'] # Incubation temperature. + ASSEMBLY_VOL = 5 # Volume of final assembly added to competent cells. + MIX_SETTINGS = (4, 5) # Mix after setting during final assembly transfers. + INCUBATION_TIME = __PARAMETERS['transfo_incubation_time']['value'] # Cells and final assembly incubation time. + + + + # Set temperature deck to 4 °C and load competent cells + tempdeck.set_temperature(TEMP) + # removed: tempdeck.wait_for_temp() + # API version2 automatically pauses execution until the set temperature is reached + # thus it no longer uses .wait_for_temp() + protocol.pause('Load competent cells, uncap and resume run') + # old code: + # robot.pause() + # robot.comment('Load competent cells, uncap and resume run') + # API version 2 uses 'protocol.' instead of 'robot.' and combines '.pause' and '.comment' + + # Transfer final assemblies + p20_pipette.transfer(ASSEMBLY_VOL, + [assembly_plate.wells_by_name()[well_name] for well_name in transformation_wells], + [transformation_plate.wells_by_name()[well_name] for well_name in transformation_wells], + new_tip='always', + mix_after=(MIX_SETTINGS)) + # old code: + # p20_pipette.transfer(ASSEMBLY_VOL, + # assembly_plate.wells(transformation_wells), + # transformation_plate.wells(transformation_wells), + # new_tip='always', + # mix_after=(MIX_SETTINGS)) + # .wells() doesn't take lists as arguements, newer wells_by_name() returns a dictionary + + + # Incubate for 20 minutes and remove competent cells for heat shock + protocol.delay(minutes=INCUBATION_TIME) + # old code: + # p20_pipette.delay(minutes=INCUBATION_TIME) + # API version 2 no longer has .delay() for pipettes, it uses protocol.delay() to pause the entire protocol + + protocol.pause('Get ready for heat shock when thermocycler reaches to 42 C.') + # old code: + # robot.pause() + # robot.comment('Remove transformation reactions, conduct heatshock in the next heat block and and return the plate.') + # API version 2 uses 'protocol.' instead of 'robot.' and combines '.pause' and '.comment' + + def heat_shock(): + + + #Thermocycler Module + tc_mod = protocol.load_module('Thermocycler Module') + # Destination Plates + DESTINATION_PLATE_TYPE = __LABWARES['96_wellplate_200ul_pcr_step_14']['id'] + # Loads destination plate onto Thermocycler Module + destination_plate = tc_mod.load_labware(DESTINATION_PLATE_TYPE) + tc_mod.set_block_temperature(42, hold_time_minutes=1, block_max_volume=180) + protocol.pause('Place the competent cells on thermocycler and resume run to conduct heat shock.') + protocol.delay(seconds=45) + + + + + def phase_switch(): + """ + Function pauses run enabling addition/removal of labware. + + """ + protocol.pause('Return the transformants to heat block. Remove final assembly plate. Introduce agar tray and deep well plate containing SOC media. Resume run.') + # old code: + # def phase_switch(comment='Remove final assembly plate. Introduce agar tray and deep well plate containing SOC media. Resume run.'): + # robot.pause() + # robot.comment(comment) + # API version 2 uses 'protocol.' instead of 'robot.' and combines '.pause' and '.comment' + + + def outgrowth( + cols, + soc_well): + """ + Outgrows transformed cells. + + Args: + cols (list of str): list of cols in transformation plate containing samples. + soc_well (str): Well containing SOC media in relevant plate. + + """ + + # Constants + SOC_VOL = 125 + SOC_MIX_SETTINGS = (4, 50) + TEMP = 37 + OUTGROWTH_TIME = 60 + SOC_ASPIRATION_RATE = 25 + P300_DEFAULT_ASPIRATION_RATE = 150 + + # Define wells + transformation_cols = [transformation_plate.columns_by_name()[column] for column in cols] + # old code: + # transformation_cols = transformation_plate.cols(cols) + # API version 2 labware use .columns attribute, not .cols + # but .columns() doesn't take lists as arguements, newer columns_by_name() returns a dictionary + + soc = soc_plate.wells(soc_well) + + # Add SOC to transformed cells + p300_pipette.flow_rate.aspirate = SOC_ASPIRATION_RATE + # old code: + # p300_pipette.set_flow_rate(aspirate=SOC_ASPIRATION_RATE) + # flow rates are set directly in API version 2, brackets not required + p300_pipette.transfer(SOC_VOL, soc, transformation_cols, + new_tip='always', mix_after=SOC_MIX_SETTINGS) + p300_pipette.flow_rate.aspirate = P300_DEFAULT_ASPIRATION_RATE + # old code: + # p300_pipette.set_flow_rate(aspirate=P300_DEFAULT_ASPIRATION_RATE) + # API version 2 labware use .columns attribute, not .cols + + # Incubate for 1 hour at 37 °C + tempdeck.set_temperature(TEMP) + # removed: tempdeck.wait_for_temp() + # API version2 automatically pauses execution until the set temperature is reached + # thus it no longer uses .wait_for_temp() + + protocol.delay(minutes=OUTGROWTH_TIME) + # old code: + # p300_pipette.delay(minutes=OUTGROWTH_TIME) + # API version 2 no longer has .delay() for pipettes, it uses protocol.delay() to pause the entire protocol + + tempdeck.deactivate() + + + + + + def spotting_cols(spotting_tuples): + """ + Evaluates spotting_tuples and returns unique cols (str) associated with each spotting_tuple's source wells. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + + """ + cols_list = [] + for spotting_tuple in spotting_tuples: + source_wells_cols = [source_well[1:] for source_well in spotting_tuple[0]] + unique_cols = [col for i, col in enumerate(source_wells_cols) if source_wells_cols.index(col) == i] + cols_list.append(unique_cols) + return cols_list + + + def spot_transformations( + spotting_tuples, + dead_vol=1, + spotting_dispense_rate=0.025, + stabbing_depth=10, + max_spot_vol=5): + """ + Spots transformation reactions. + + Args: + spotting_tuples (list): Sets of spotting reactions are given in the form: ((source wells), (target wells), (spotting volumes)). + dead_vol (float): Dead volume aspirated during spotting. + spotting_dispense_rate (float): Rate p20_pipette dispenses at during spotting. + stabbing_depth (float): Depth p20_pipette moves into agar during spotting. + max_spot_vol (float): Maximum volume that is spotted per spot reaction. + + """ + + def spot( + source, + target, + spot_vol): + """ + Spots an individual reaction using the p20 pipette. + + Args: + source (str): Well containing the transformation reaction to be spotted. + target (str): Well transformation reaction is to be spotted to. + spot_vol (float): Volume of transformation reaction to be spotted (uL). + + """ + + # Constants + DEFAULT_HEAD_SPEED = {'x': 400, 'y': 400,'z': 125, 'a': 125} + SPOT_HEAD_SPEED = {'x': 400, 'y': 400, 'z': 125,'a': 125 // 4} + DISPENSING_HEIGHT = 5 + SAFE_HEIGHT = 7 # height avoids collision with agar tray. + + # Spot + p20_pipette.pick_up_tip() + p20_pipette.aspirate(spot_vol + dead_vol, source[0]) + # old code: + # p20_pipette.aspirate(spot_vol + dead_vol, source) + # returned type error because 'source' was a list containing one item (the well location) + # source[0] takes the location out of the list + + p20_pipette.move_to(target[0].top(SAFE_HEIGHT)) + p20_pipette.move_to(target[0].top(DISPENSING_HEIGHT)) + # old code: + # p20_pipette.move_to(target.top(SAFE_HEIGHT)) + # p20_pipette.move_to(target.top(DISPENSING_HEIGHT)) + # returned attribute error because 'target' was a list containing one item (the well location) + # target[0] takes the location out of the list + + p20_pipette.dispense(volume=spot_vol, rate=spotting_dispense_rate) + + protocol.max_speeds.update(SPOT_HEAD_SPEED) + # old code: + # robot.head_speed(combined_speed=max(SPOT_HEAD_SPEED.values()), **SPOT_HEAD_SPEED) + # robot.head_speed not used in API version 2 + # replaced with protocol.max_speeds + # new code no longer uses the lower value between combined speed or specified speed + # just uses each axis' specified speed directly + p20_pipette.move_to(target[0].top(-1 * stabbing_depth)) + # old code: + # p20_pipette.move_to(target.top(-1*stabbing_depth)) + # returns attribute error because 'target' was a list containing one item (the well location) + protocol.max_speeds.update(DEFAULT_HEAD_SPEED) + # old code: + # robot.head_speed(combined_speed=max(DEFAULT_HEAD_SPEED.values()), **DEFAULT_HEAD_SPEED) + # robot.head_speed not used in API version 2 + # replaced with protocol.max_speeds + # new code no longer uses the lower value between combined speed or specified speed + # just uses each axis' specified speed directly + + #Make sure that the transformed cells drops on the agar plate + + p20_pipette.blow_out() + + protocol.delay(seconds=10) + + p20_pipette.blow_out() + + protocol.delay(seconds=10) + + p20_pipette.blow_out() + + protocol.delay(seconds=10) + + p20_pipette.blow_out() + + + + p20_pipette.move_to(target[0].top(SAFE_HEIGHT)) + # old code: + # p20_pipette.move_to(target[0].top(SAFE_HEIGHT)) + # returns attribute error because 'target' was a list containing one item (the well location) + + + # the code below makes sure that the transformend cells are efficiently reaching to the agar surface + + + + # Dispose of dead volume and tip + p20_pipette.dispense(dead_vol, spotting_waste[0]) + # old code: + # p20_pipette.dispense(dead_vol, spotting_waste) + # returns type error because 'target' was a list containing one item (the well location) + + p20_pipette.blow_out() + # the simple .blow_out command blows out at current position (spotting waste) by defualt + # unlike blowout=true in complex commands, which by default will blow out in waste + + p20_pipette.drop_tip() + + def spot_tuple(spotting_tuple): + """ + Spots all reactions defined by the spotting tuple. Requires the function spot. + + Args: + spotting_tuple (tuple): Spotting reactions given in the form: (source wells), (target wells), (spotting volumes). + Each unique source well is resuspended once prior to spotting. + + """ + source_wells = spotting_tuple[0] + target_wells = spotting_tuple[1] + spot_vols = list(spotting_tuple[2]) + while max(spot_vols) > 0: + for index, spot_vol in enumerate(spot_vols): + if spot_vol == 0: + pass + else: + vol = spot_vol if spot_vol <= max_spot_vol else max_spot_vol + spot(source = transformation_plate.wells(source_wells[index]), target = agar_plate.wells(target_wells[index]), spot_vol = vol) + spot_vols[index] = spot_vols[index] - vol + + # Constants + TRANSFORMATION_MIX_SETTINGS = [4, 50] + + # Spot transformation reactions + # Each unique transformation well is resuspended once prior to spotting. + + for spotting_tuple in spotting_tuples: + source_wells_cols = [source_well[1:] for source_well in spotting_tuple[0]] + unique_cols = [col for i, col in enumerate(source_wells_cols) if source_wells_cols.index(col) == i] + for col in unique_cols: + p300_pipette.pick_up_tip() + p300_pipette.mix(TRANSFORMATION_MIX_SETTINGS[0], TRANSFORMATION_MIX_SETTINGS[1],transformation_plate.columns_by_name()[col][0]) + # old code: + # p300_pipette.mix(TRANSFORMATION_MIX_SETTINGS[0], TRANSFORMATION_MIX_SETTINGS[1],transformation_plate.cols(col)) + # .columns aka .cols doesn't take lists anymore + # replaced with .columns_by_name + # .mix only takes one location, not several locations + # added [0] to specify only the wells in row A + # is identical for the protocol, as this is using a multi-channel pipette + p300_pipette.drop_tip() + spot_tuple(spotting_tuple) + + + # Tiprack slots + + p20_p300_tiprack_slots = tiprack_slots(spotting_tuples) + p20_slots = CANDIDATE_p20_SLOTS[:p20_p300_tiprack_slots[0]] + p300_slots = CANDIDATE_P300_SLOTS[:p20_p300_tiprack_slots[1]] + + # Define labware + p20_tipracks = [protocol.load_labware(P20_TIPRACK_TYPE, slot) for slot in p20_slots] + # changed to protocol.load_labware for API version 2 + p300_tipracks = [protocol.load_labware(P300_TIPRACK_TYPE, slot) for slot in p300_slots] + # changed to protocol.load_labware for API version 2 + p20_pipette = protocol.load_instrument(__LABWARES['p20_single']['id'], P20_MOUNT, tip_racks=p20_tipracks) + # changed to protocol.load_instrument for API version 2 + p300_pipette = protocol.load_instrument(__LABWARES['p300_multi']['id'], P300_MOUNT, tip_racks=p300_tipracks) + # changed to protocol.load_instrument for API version 2 + + assembly_plate = protocol.load_labware(ASSEMBLY_PLATE_TYPE, ASSEMBLY_PLATE_SLOT) + # changed to protocol.load_labware for API version 2 + tempdeck = protocol.load_module('tempdeck', TEMPDECK_SLOT) + transformation_plate = tempdeck.load_labware(TRANSFORMATION_PLATE_TYPE, TEMPDECK_SLOT) + # changed to protocol.load_labware for API version 2 + # removed share=True, not required in API version 2 + # removed TEMPDECK_SLOT as it is loaded directly onto temperature module + soc_plate = protocol.load_labware(SOC_PLATE_TYPE, SOC_PLATE_SLOT) + # changed to protocol.load_labware for API version 2 + tube_rack = protocol.load_labware(TUBE_RACK_TYPE, TUBE_RACK_SLOT) + # changed to protocol.load_labware for API version 2 + spotting_waste = tube_rack.wells(SPOTTING_WASTE_WELL) + agar_plate = protocol.load_labware(AGAR_PLATE_TYPE, AGAR_PLATE_SLOT) + # changed to protocol.load_labware for API version 2 + + + ### Run protocol + + + + + + # Run functions + transformation_setup(generate_transformation_wells(spotting_tuples)) + heat_shock() + phase_switch() + spotting_tuples_cols = [col for cols in spotting_cols(spotting_tuples) for col in cols] + unique_cols = [col for i, col in enumerate(spotting_tuples_cols) if spotting_tuples_cols.index(col) == i] + outgrowth(cols=unique_cols, soc_well=soc_well) + spot_transformations(spotting_tuples) + print(unique_cols) diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/metainformation/dataset_4472_clip_run_info.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/metainformation/dataset_4472_clip_run_info.csv Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,28 @@ +MASTER_MIX +Component,Volume (uL) +"Promega T4 DNA Ligase buffer, 10X",45.0 +Water,232.5 +NEB BsaI-HFv2,15.0 +Promega T4 DNA Ligase,7.5 + +SOURCE_PLATES +Deck position,Source plate,Path +2,dataset_4475.dat,/galaxy/database/files/004/dataset_4475.dat +5,dataset_4476.dat,/galaxy/database/files/004/dataset_4476.dat + +CLIP_REACTIONS +prefixes,parts,suffixes,number,mag_well +LMS-P,BASIC_SEVA_37_CmR-p15A.1,LMP-S,1,"('A7',)" +LMP-P,PJ23104_BASIC,U1-S,1,"('B7',)" +U1-RBS2-P,D5AP78,U3-S,1,"('C7',)" +U3-RBS3-P,Q1XBU4,U2-S,1,"('D7',)" +U2-RBS1-P,O66129,LMS-S,1,"('E7',)" +LMP-P,PJ23101_BASIC,U1-S,1,"('F7',)" +U1-RBS1-P,D2WKD9,U2-S,1,"('G7',)" +U2-RBS3-P,O48935,U3-S,1,"('H7',)" +U3-RBS1-P,O66952,LMS-S,1,"('A8',)" +LMP-P,PJ23104_BASIC,U2-S,1,"('B8',)" +U2-RBS2-P,P48537,U3-S,1,"('C8',)" +U3-RBS2-P,O07333,U1-S,1,"('D8',)" +U1-RBS2-P,Q9C446,LMS-S,1,"('E8',)" + diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/metainformation/dataset_4472_final_assembly_run_info.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/metainformation/dataset_4472_final_assembly_run_info.csv Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,3 @@ +A1,"['A7', 'B7', 'C7', 'D7', 'E7']" +B1,"['A7', 'F7', 'G7', 'H7', 'A8']" +C1,"['A7', 'B8', 'C8', 'D8', 'E8']" diff -r 000000000000 -r ed297a952c06 test-data/dnabot_scripts/output/metainformation/dataset_4472_wells.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/dnabot_scripts/output/metainformation/dataset_4472_wells.txt Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,2 @@ +Magbead ethanol well: A11 +SOC column: 1 \ No newline at end of file diff -r 000000000000 -r ed297a952c06 test-data/linker_parts_coords.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/linker_parts_coords.csv Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,83 @@ +Part/linker,Well,Part concentration (ng/uL) +L1-S,A1, +L1-P,B1, +L2-S,A2, +L2-P,B2, +L3-S,A3, +L3-P,B3, +L4-S,A4, +L4-P,B4, +L5-S,A5, +L5-P,B5, +L6-S,A6, +L6-P,B6, +LMP-S,A7, +LMP-P,B7, +LMS-S,A8, +LMS-P,B8, +U1-S,C1, +U2-S,C2, +U3-S,C3, +U1-RBS1-P,C4, +U1-RBS2-P,C5, +U1-RBS3-P,C6, +U1-A01-P,D1, +U1-A02-P,D2, +U1-A03-P,D3, +U1-A04-P,D4, +U1-A05-P,D5, +U1-A06-P,D6, +U1-A07-P,D7, +U1-A08-P,D8, +U1-A09-P,D9, +U1-A10-P,D10, +U1-A11-P,D11 +U1-A12-P,D12, +U2-RBS1-P,C7, +U2-RBS2-P,C8, +U2-RBS3-P,C9, +U2-A01-P,E1, +U2-A02-P,E2, +U2-A03-P,E3, +U2-A04-P,E4, +U2-A05-P,E5, +U2-A06-P,E6, +U2-A07-P,E7, +U2-A08-P,E8, +U2-A09-P,E9, +U2-A10-P,E10, +U2-A11-P,E11 +U2-A12-P,E12, +U3-RBS1-P,C10, +U3-RBS2-P,C11, +U3-RBS3-P,C12, +U3-A01-P,F1, +U3-A02-P,F2, +U3-A03-P,F3, +U3-A04-P,F4, +U3-A05-P,F5, +U3-A06-P,F6, +U3-A07-P,F7, +U3-A08-P,F8, +U3-A09-P,F9, +U3-A10-P,F10, +U3-A11-P,F11 +U3-A12-P,F12, +U1-AM12-P,A10, +U1-AM24-P,B10, +U2-AM12-P,A11, +U2-AM24-P,B11, +U3-AM12-P,A12, +U3-AM24-P,B12, +LF1-S,A9, +LF1-P,B9, +LF2-S,G1, +LF2-P,H1, +LF3-S,G2, +LF3-P,H2, +LF4-S,G3, +LF4-P,H3, +LF5-S,G4, +LF5-P,H4, +LF6-S,G5, +LF6-P,H5, \ No newline at end of file diff -r 000000000000 -r ed297a952c06 test-data/user_parts_coords.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/user_parts_coords.csv Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,13 @@ +Part/linker,Well,Part concentration (ng/uL) +BASIC_SEVA_37_CmR-p15A.1,A1, +D2WKD9,A2, +D5AP78,A3, +O07333,A4, +O48935,A5, +O66129,A6, +O66952,A7, +P48537,A8, +PJ23101_BASIC,A9, +PJ23104_BASIC,A10, +Q1XBU4,A11, +Q9C446,A12, diff -r 000000000000 -r ed297a952c06 wrap.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wrap.xml Tue Dec 14 14:46:38 2021 +0000 @@ -0,0 +1,126 @@ + + DNA assembly using BASIC on OpenTrons + + dnabot + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + `_ + + +Acknowledgments +--------------- + +* Marko Storch +* Geoff Baldwin + ]]> + + + @article{10.1093/synbio/ysaa010, + author = {Storch, Marko and Haines, Matthew C and Baldwin, Geoff S}, + title = {DNA-BOT: a low-cost, automated DNA assembly platform for synthetic biology}, + journal = {Synthetic Biology}, + volume = {5}, + number = {1}, + year = {2020}, + month = {07}, + issn = {2397-7000}, + doi = {10.1093/synbio/ysaa010}, + url = {https://doi.org/10.1093/synbio/ysaa010}, + note = {ysaa010}, + eprint = {https://academic.oup.com/synbio/article-pdf/5/1/ysaa010/33722340/ysaa010.pdf}, + } + + +
\ No newline at end of file