def run(protocol: protocol_api.ProtocolContext): tiprack = protocol.load_labware_from_definition(TIPRACK_DEF, TEST_TIPRACK_SLOT, TIPRACK_LABEL) pipette = protocol.load_instrument(PIPETTE_NAME, PIPETTE_MOUNT, tip_racks=[tiprack]) num_cols = len(TIPRACK_DEF.get('ordering', [[]])) num_rows = len(TIPRACK_DEF.get('ordering', [[]])[0]) def set_speeds(rate): protocol.max_speeds.update({ 'X': (600 * rate), 'Y': (400 * rate), 'Z': (125 * rate), 'A': (125 * rate), }) speed_max = max(protocol.max_speeds.values()) for instr in protocol.loaded_instruments.values(): instr.default_speed = speed_max set_speeds(RATE) firstwell = tiprack.well('A1') pipette.move_to(firstwell.top()) protocol.pause("If the pipette is accurate click 'resume'") pipette.pick_up_tip() protocol.pause( "If the pipette went into the center of the tip, click 'resume'") pipette.return_tip() protocol.pause( "If the pipette successfully picked up the tip(s) but does not eject succesfully, pull the tip(s) off by hand and click 'resume'. Do not worry about tip ejection yet" ) last_col = (num_cols * num_rows) - num_rows if (PIPETTE_NAME == 'p20_multi_gen2' or PIPETTE_NAME == 'p300_multi_gen2'): well = tiprack.well(last_col) pipette.move_to(well.top()) protocol.pause("If the position is accurate click 'resume'") pipette.pick_up_tip(well) else: last_well = (num_cols) * (num_rows) well = tiprack.well(last_well - 1) pipette.move_to(well.top()) protocol.pause("If the position is accurate click 'resume'") pipette.pick_up_tip(well) protocol.pause( "If the pipette went to the center of the tip, click 'resume'") pipette.return_tip() protocol.comment( "If the pipette successfully picked up the tip(s) but does not eject succesfully, pull the tip(s) off by hand and click 'resume'. Do not worry about tip ejection yet" )
def run(ctx: protocol_api.ProtocolContext): ctx.comment("Begin {}".format(metadata['protocolName'])) # Turn on rail lights and pause program so user can load robot deck. # ctx.set_rail_lights(True) # ctx.pause("Load Labware onto robot deck and click resume when ready to continue") # ctx.home() ctx.set_rail_lights(False) # TSV file location on OT-2 tsv_file_path = "{0}var{0}lib{0}jupyter{0}notebooks{0}ProcedureFile.tsv".format( os.sep) if not os.path.isfile(tsv_file_path): # Temp TSV file location on Win10 Computers for simulation tsv_file_path = "C:{0}Users{0}{1}{0}Documents{0}TempTSV.tsv".format( os.sep, os.getlogin()) sample_parameters, args = Utilities.parse_sample_template(tsv_file_path) labware_dict, left_tiprack_list, right_tiprack_list = Utilities.labware_parsing( args, ctx) # Pipettes left_pipette = ctx.load_instrument(args.LeftPipette, 'left', tip_racks=left_tiprack_list) right_pipette = ctx.load_instrument(args.RightPipette, 'right', tip_racks=right_tiprack_list) # Set the location of the first tip in box. with suppress(IndexError): left_pipette.starting_tip = left_tiprack_list[0].wells_by_name()[ args.LeftPipetteFirstTip] with suppress(IndexError): right_pipette.starting_tip = right_tiprack_list[0].wells_by_name()[ args.RightPipetteFirstTip] # Dispense Samples and primers sample_dest_dict = dispense_samples(args, sample_parameters, labware_dict, left_pipette, right_pipette) # Add PCR mix to each destination well. add_pcr_mix(args, labware_dict, sample_dest_dict, left_pipette, right_pipette) if not ctx.is_simulating(): os.remove(tsv_file_path) ctx.comment("Program End")
def run(robot: protocol_api.ProtocolContext): # ----------------------------------------------------- # Magnetic module + labware # ----------------------------------------------------- magdeck = robot.load_module('Magnetic Module Gen2', '1') maglab = magdeck.load_labware('nest_96_wellplate_2ml_deep', 'nest_96_wellplate_2ml_deep') #magdeck.engage(height = 7) for i in np.arange(0.0, 25.0, 0.1): magdeck.engage(height=i) robot.comment('Actual height =' + str(i)) robot.pause('Test') magdeck.disengage()
def run(protocol: protocol_api.ProtocolContext): #load tip rack, pipette and labware tip_rack = protocol.load_labware('opentrons_96_tiprack_300ul', 2) pipette_right = protocol.load_instrument('p300_single', 'right', tip_racks=[tip_rack]) #load bioshake - remember to change serial number to match your devices! device = QIDevice(serial_number='19762', deck_position=1, adapter_set_up=1, protocol=protocol) #first, upload Qinstrument's custom labware to the OT-2. Next, make sure you #add the z_offset, item_depth, and item_volume lbw = device.load_labware('2016_1062', z_offset=(11, 5), item_depth=(37.5, 30), item_volume=(1500, 500)) #resetting bioshake just in case device.exec_cmd('resetDevice') #teseting pipette pipette_right.pick_up_tip() pipette_right.aspirate(100, lbw['AA1']) pipette_right.dispense(100, lbw['AA5']) pipette_right.drop_tip() #testing temp at 37C for 20 seconds while shaking still occurs device.exec_cmd('setShakeTargetSpeed500') device.exec_cmd('shakeOn') device.exec_cmd('setTempTarget370', blocking=True) device.exec_cmd('tempOn', polling=True) protocol.delay(seconds=20, msg='shaking at 500rpm and heating at 37C for 20 seconds') device.exec_cmd('shakeOff') #testing pipette with temperature on pipette_right.pick_up_tip() pipette_right.aspirate(100, lbw['AA2']) pipette_right.dispense(100, lbw['AA3']) pipette_right.drop_tip() device.exec_cmd('tempOff') protocol.comment('Protocol complete.')
def run(ctx: protocol_api.ProtocolContext): [num_samp, reaction_plate, p20_mount] = get_values( # noqa: F821 "num_samp", "reaction_plate", "p20_mount") if not 1 <= num_samp <= 96: raise Exception("Enter a sample number 1-96") # LABWARE pcr_plate = ctx.load_labware(reaction_plate, '9', label='the PCR PLATE') sample_racks = [ ctx.load_labware('opentrons_15_tuberack_5000ul', slot, label="the SAMPLE RACK") for slot in ['1', '2', '3', '4', '5', '6', '7'] ] # TIPRACKS tiprack20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', '10', label='20uL TIPRACK') ] # INSTRUMENTS p20 = ctx.load_instrument('p20_single_gen2', p20_mount, tip_racks=tiprack20) # MAPPING all_sample_tubes = [ tube for rack in sample_racks for tube in rack.wells() ][:num_samp] # protocol ctx.comment('\n\n~~~~~~~~MOVING SAMPLES TO PLATE~~~~~~~~~\n') for sample, well in zip(all_sample_tubes, pcr_plate.wells()): p20.pick_up_tip() p20.aspirate(10, sample) p20.dispense(10, well) p20.blow_out() p20.touch_tip() p20.drop_tip()
def run(ctx: protocol_api.ProtocolContext): #Please replace what is between the quotation marks!! NEW_SERIAL_NUMBER = "OT2CEP20201214B12" ctx.comment(f"Setting serial number to {NEW_SERIAL_NUMBER}.") if not ctx.is_simulating(): with open("/var/serial", "w") as serial_number_file: serial_number_file.write(NEW_SERIAL_NUMBER + "\n") with open("/etc/machine-info", "w") as serial_number_file: serial_number_file.write(f"DEPLOYMENT=production\nPRETTY_HOSTNAME={NEW_SERIAL_NUMBER}\n") with open("/etc/hostname", "w") as serial_number_file: serial_number_file.write(NEW_SERIAL_NUMBER + "\n") os.sync() ctx.comment("Done.")
def run(ctx: protocol_api.ProtocolContext): #Please replace what is between the quotation marks!! NEW_SERIAL_NUMBER = "set_your_serial_number_here" ctx.comment(f"Setting serial number to {NEW_SERIAL_NUMBER}.") if not ctx.is_simulating(): with open("/var/serial", "w") as serial_number_file: serial_number_file.write(NEW_SERIAL_NUMBER + "\n") with open("/etc/machine-info", "w") as serial_number_file: serial_number_file.write(f"DEPLOYMENT=production\nPRETTY_HOSTNAME={NEW_SERIAL_NUMBER}\n") with open("/etc/hostname", "w") as serial_number_file: serial_number_file.write(NEW_SERIAL_NUMBER + "\n") os.sync() ctx.comment("Done.")# This script sets an OT-2's serial number. It's meant to be used after you
def run_test(self, ctx: ProtocolContext): """Test protocol""" ctx.comment("Test the custom '{}' rack".format( self.metadata["displayName"])) rack = ctx.load_labware_from_definition(self.labware_definition(), '2', 'custom tuberack') tipracks1000 = [ ctx.load_labware('opentrons_96_filtertiprack_1000ul', '1', '1000µl filter tiprack') ] p1000 = ctx.load_instrument('p1000_single_gen2', 'right', tip_racks=tipracks1000) p1000.pick_up_tip() for w in rack.wells(): ctx.pause("moving to top of {}".format(w)) p1000.move_to(w.top()) ctx.pause("moving to bottom of {} (1 mm high)".format(w)) p1000.move_to(w.bottom(1)) p1000.aspirate(5) p1000.dispense(5) p1000.drop_tip()
def run(ctx: protocol_api.ProtocolContext): # load labware s_racks = [ ctx.load_labware('opentrons_15_tuberack_falcon_15ml_conical', '4') ] d_plate = ctx.load_labware('nest_96_wellplate_200ul_flat', '1', '96-wellplate sample plate') tips300 = [ctx.load_labware('opentrons_96_filtertiprack_200ul', '5')] # load pipette p300 = ctx.load_instrument('p300_single_gen2', 'left', tip_racks=tips300) # transfer sample p300.transfer(SAMPLE_VOLUME, s_racks.wells()['A1'], d_plate.columns()['5'], air_gap=20, mix_before=(2, 200), new_tip='never') p300.air_gap(20) p300.drop_tip() ctx.comment('Terminado.') # track final used tip if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) data = {'tips300': tip_log['count'][p300]} with open(tip_file_path, 'w') as outfile: json.dump(data, outfile)
def run(ctx: protocol_api.ProtocolContext): STEP = 0 STEPS = { # Dictionary with STEP activation, description and times 1: {'Execute': True, 'description': 'Mix and move samples ('+str(VOLUME_SAMPLE)+'ul)'} } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 #Folder and file_path for log time if not ctx.is_simulating(): folder_path = '/var/lib/jupyter/notebooks/'+run_id if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/StationA_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, delay): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.delay = delay # Reagents and their characteristics Samples = Reagent(name = 'Samples', flow_rate_aspirate = 1, flow_rate_dispense = 1, rinse = False, delay = 0 ) ctx.comment(' ') ctx.comment('###############################################') ctx.comment('CONTROL SPACES: ' + str(NUM_CONTROL_SPACES)) ctx.comment('NUM SAMPLES: ' + str(NUM_REAL_SAMPLES)) ctx.comment('###############################################') ctx.comment(' ') ################## # Custom functions def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location = source, vol = vol, rounds = 2, blow_out = True, mix_height = 0, x_offset = x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x = x_offset[0])) pipet.aspirate(vol, s) # aspirate liquid if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z = -2), rate = reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z = disp_height).move(Point(x = x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate = reagent.flow_rate_dispense) # dispense all ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z = -2)) if touch_tip == True: pipet.touch_tip(speed = 20, v_offset = -10) def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height = 5): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height <= 0: mix_height = 3 pipet.aspirate(1, location = location.bottom( z = source_height).move(Point(x = x_offset[0])), rate = reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location = location.bottom( z = source_height).move(Point(x = x_offset[0])), rate = reagent.flow_rate_aspirate) pipet.dispense(vol, location = location.bottom( z = mix_height).move(Point(x = x_offset[1])), rate = reagent.flow_rate_dispense) pipet.dispense(1, location = location.bottom( z = mix_height).move(Point(x = x_offset[1])), rate = reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z = -2)) # Blow out def generate_source_table(source): ''' Concatenate the wells frome the different origin racks ''' num_cols = math.ceil(num_samples / 8) s = [] for i in range(num_cols): if i < 6: s += source[0].columns()[i] + source[1].columns()[i] else: s += source[2].columns()[i - 6] + source[3].columns()[i - 6] return s ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up(pip): nonlocal tip_track if not ctx.is_simulating(): if tip_track['counts'][pip] == tip_track['maxes'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_track['counts'][pip] = 0 pip.pick_up_tip() #################################### # load labware and modules #################################### # Load Sample racks if num_samples <= 48: rack_num = 2 ctx.comment('Used source racks are ' + str(rack_num)) else: rack_num = 4 source_racks = [ctx.load_labware( 'opentrons_24_tuberack_nest_2ml_snapcap', slot, 'source tuberack with snapcap' + str(i + 1)) for i, slot in enumerate(['5', '2', '6', '3'][:rack_num]) ] ################################## # Destination plate dest_plate = ctx.load_labware( 'nest_96_wellplate_2ml_deep', '1', 'NEST 96 Deepwell Plate 2mL') #################################### # Load tip_racks tips1000 = [ctx.load_labware( 'opentrons_96_filtertiprack_1000ul', slot, '1000µl filter tiprack') for slot in ['7']] ################################################################################ # setup samples and destinations sample_sources_full = generate_source_table(source_racks) sample_sources = sample_sources_full[NUM_CONTROL_SPACES:num_samples] destinations = dest_plate.wells()[NUM_CONTROL_SPACES:num_samples] p1000 = ctx.load_instrument( 'p1000_single_gen2', 'right', tip_racks = tips1000) # load P1000 pipette # used tip counter and set maximum tips available tip_track = { 'counts': {p1000: 0}, 'maxes': {p1000: len(tips1000) * 96} } ############################################################################ # STEP 1: MIX AND MOVE SAMPLES ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') start = datetime.now() for s, d in zip(sample_sources, destinations): if not p1000.hw_pipette['has_tip']: pick_up(p1000) # Mix the sample BEFORE dispensing if NUM_MIXES > 0: custom_mix(p1000, reagent = Samples, location = s, vol = volume_mix, rounds = NUM_MIXES, blow_out = True, mix_height = 15, x_offset = x_offset) move_vol_multichannel(p1000, reagent = Samples, source = s, dest = d, vol = VOLUME_SAMPLE, air_gap_vol = air_gap_vol_sample, x_offset = x_offset, pickup_height = 3, rinse = Samples.rinse, disp_height = -10, blow_out = True, touch_tip = True) p1000.drop_tip(home_after = False) tip_track['counts'][p1000] += 1 # Time statistics end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write('STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program # from opentrons.drivers.rpi_drivers import gpio #if not ctx.is_simulating(): #os.system('mpg123 -f -14000 /var/lib/jupyter/notebooks/final.mp3') if not ctx.is_simulating(): fname = ' /var/lib/jupyter/notebooks/finished_process_esp.mp3' if os.path.isfile(fname) is True: subprocess.run( ['mpg123', fname], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) else: ctx.comment(f"Sound file does not exist. Call the technician") for i in range(3): ctx._hw_manager.hardware.set_lights(rails = False) ctx._hw_manager.hardware.set_lights(button = (1, 0 ,0)) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(rails = True) ctx._hw_manager.hardware.set_lights(button = (0, 0 ,1)) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(button = (0, 1 ,0)) ctx.comment('Finished! \nMove deepwell plate (slot 1) to Station B for extraction protocol') ctx.comment('Used p1000 tips in total: ' + str(tip_track['counts'][p1000])) ctx.comment('Used p1000 racks in total: ' + str(tip_track['counts'][p1000] / 96))
def run(ctx: protocol_api.ProtocolContext): from opentrons.drivers.rpi_drivers import gpio gpio.set_rail_lights(False) #Turn off lights (termosensible reagents) ctx.comment('Actual used columns: ' + str(num_cols)) # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: {'Execute': True, 'description': 'Transfer MMIX'}, 2: {'Execute': True, 'description': 'Transfer elution'} } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 #Folder and file_path for log time folder_path = '/var/lib/jupyter/notebooks/'+run_id' if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/KC_qPCR_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo, tip_recycling = 'none'): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.reagent_reservoir_volume = reagent_reservoir_volume self.delay = delay self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.unused=[] self.tip_recycling = tip_recycling self.vol_well_original = reagent_reservoir_volume / num_wells # Reagents and their characteristics MMIX = Reagent(name = 'Master Mix', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = volume_mmix_available, num_wells = 2, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) Samples = Reagent(name='Samples', rinse=False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume=50, delay=0, num_wells=num_cols, # num_cols comes from available columns h_cono=0, v_fondo=0 ) MMIX.vol_well = MMIX.vol_well_original Samples.vol_well = Samples.vol_well_original ################## # Custom functions def divide_destinations(l, n): # Divide the list of destinations in size n lists. for i in range(0, len(l), n): yield l[i:i + n] def distribute_custom(pipette, volume, src, dest, waste_pool, pickup_height, extra_dispensal, disp_height=0): # Custom distribute function that allows for blow_out in different location and adjustement of touch_tip pipette.aspirate((len(dest) * volume) + extra_dispensal, src.bottom(pickup_height)) pipette.touch_tip(speed=20, v_offset=-5) pipette.move_to(src.top(z=5)) pipette.aspirate(20) # air gap for d in dest: pipette.dispense(20, d.top()) drop = d.top(z = disp_height) pipette.dispense(volume, drop) pipette.move_to(d.top(z=5)) pipette.aspirate(20) # air gap try: pipette.blow_out(waste_pool.wells()[0].bottom(pickup_height + 3)) except: pipette.blow_out(waste_pool.bottom(pickup_height + 3)) return (len(dest) * volume) def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location = source, vol = vol, rounds = 2, blow_out = True, mix_height = 0, x_offset = x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x = x_offset[0])) pipet.aspirate(vol, s, rate = reagent.flow_rate_aspirate) # aspirate liquid if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z = -2), rate = reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z = disp_height).move(Point(x = x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate = reagent.flow_rate_dispense) # dispense all ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z = -2)) if touch_tip == True: pipet.touch_tip(speed = 20, v_offset = -5, radius = 0.9) def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height = 3): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height == 0: mix_height = 3 pipet.aspirate(1, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) pipet.dispense(vol, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) pipet.dispense(1, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.5, extra_volume = 50): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if reagent.vol_well < aspirate_volume + extra_volume: reagent.unused.append(reagent.vol_well) ctx.comment('Next column should be picked') ctx.comment('Previous to change: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment(str('After change: ' + str(reagent.col))) reagent.vol_well = reagent.vol_well_original ctx.comment('New volume:' + str(reagent.vol_well)) height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Remaining volume:' + str(reagent.vol_well)) if height < min_height: height = min_height col_change = True else: height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Calculated height is ' + str(height)) if height < min_height: height = min_height ctx.comment('Used height is ' + str(height)) col_change = False return height, col_change #################################### # load labware and modules # 24 well rack tuberack = ctx.load_labware( 'opentrons_24_aluminumblock_generic_2ml_screwcap', '2', 'Bloque Aluminio opentrons 24 screwcaps 2000 µL ') ############################################ # tempdeck tempdeck = ctx.load_module('tempdeck', '4') tempdeck.set_temperature(temperature) ################################## # qPCR plate - final plate, goes to PCR qpcr_plate = tempdeck.load_labware( 'abi_fast_qpcr_96_alum_opentrons_100ul', 'chilled qPCR final plate') ################################## # Sample plate - comes from B source_plate = ctx.load_labware( "kingfisher_std_96_wellplate_550ul", '1', 'chilled KF plate with elutions (alum opentrons)') samples = source_plate.wells()[:NUM_SAMPLES] ################################## # Load Tipracks tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['5'] ] tips200 = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', slot) for slot in ['6'] ] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate MMIX.reagent_reservoir = tuberack.rows()[0][:MMIX.num_wells] # 1 row, 2 columns (first ones) ctx.comment('Wells in: '+ str(tuberack.rows()[0][:MMIX.num_wells]) + ' element: '+str(MMIX.reagent_reservoir[MMIX.col])) # setup up sample sources and destinations samples = source_plate.wells()[:NUM_SAMPLES] samples_multi = source_plate.rows()[0][:num_cols] pcr_wells = qpcr_plate.wells()[:NUM_SAMPLES] pcr_wells_multi = qpcr_plate.rows()[0][:num_cols] # Divide destination wells in small groups for P300 pipette dests = list(divide_destinations(pcr_wells, size_transfer)) # pipettes m20 = ctx.load_instrument( 'p20_multi_gen2', mount='right', tip_racks=tips20) p300 = ctx.load_instrument( 'p300_single_gen2', mount='left', tip_racks=tips200) # used tip counter and set maximum tips available tip_track = { 'counts': {p300: 0, m20: 0} } ############################################################################ # STEP 1: Transfer Master MIX ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() p300.pick_up_tip() used_vol=[] for dest in dests: aspirate_volume=volume_mmix * len(dest) + extra_dispensal [pickup_height,col_change]=calc_height(MMIX, area_section_screwcap, aspirate_volume) used_vol_temp = distribute_custom( p300, volume = volume_mmix, src = MMIX.reagent_reservoir[MMIX.col], dest = dest, waste_pool = MMIX.reagent_reservoir[MMIX.col], pickup_height = pickup_height, extra_dispensal = extra_dispensal) used_vol.append(used_vol_temp) p300.drop_tip() tip_track['counts'][p300]+=1 #MMIX.unused_two = MMIX.vol_well end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 2: TRANSFER Samples ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('pcr_wells') #Loop over defined wells for s, d in zip(samples_multi, pcr_wells_multi): m20.pick_up_tip() #Source samples move_vol_multichannel(m20, reagent = Samples, source = s, dest = d, vol = volume_sample, air_gap_vol = air_gap_sample, x_offset = x_offset, pickup_height = 0.2, disp_height = -10, rinse = False, blow_out=True, touch_tip=False) m20.drop_tip() tip_track['counts'][m20]+=8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write('STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program gpio.set_rail_lights(False) time.sleep(2) #os.system('mpg123 -f -8000 /var/lib/jupyter/notebooks/toreador.mp3 &') for i in range(3): gpio.set_rail_lights(False) gpio.set_button_light(1, 0, 0) time.sleep(0.3) gpio.set_rail_lights(True) gpio.set_button_light(0, 0, 1) time.sleep(0.3) gpio.set_rail_lights(False) gpio.set_button_light(0, 1, 0) ctx.comment('Finished! \nMove plate to PCR') if STEPS[1]['Execute'] == True: total_used_vol = np.sum(used_vol) total_needed_volume = total_used_vol ctx.comment('Total Master Mix used volume is: ' + str(total_used_vol) + '\u03BCl.') ctx.comment('Needed Master Mix volume is ' + str(total_needed_volume + extra_dispensal*len(dests)) +'\u03BCl') ctx.comment('Used Master Mix volumes per run are: ' + str(used_vol) + '\u03BCl.') ctx.comment('Master Mix Volume remaining in tubes is: ' + format(np.sum(MMIX.unused)+extra_dispensal*len(dests)+MMIX.vol_well) + '\u03BCl.') ctx.comment('200 ul Used tips in total: ' + str(tip_track['counts'][p300])) ctx.comment('200 ul Used racks in total: ' + str(tip_track['counts'][p300] / 96)) if STEPS[2]['Execute'] == True: ctx.comment('20 ul Used tips in total: ' + str(tip_track['counts'][m20])) ctx.comment('20 ul Used racks in total: ' + str(tip_track['counts'][m20] / 96))
def run(ctx: protocol_api.ProtocolContext): # load labware dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '2', '96-deepwell sample plate') tipracks1000 = [ ctx.load_labware('opentrons_96_filtertiprack_1000ul', '1', '1000µl filter tiprack') ] # load pipette p1000 = ctx.load_instrument('p1000_single_gen2', 'right', tip_racks=tipracks1000) tip_log = {'count': {}} folder_path = '/data/A' tip_file_path = folder_path + '/tip_log.json' if TIP_TRACK and not ctx.is_simulating(): if os.path.isfile(tip_file_path): with open(tip_file_path) as json_file: data = json.load(json_file) if 'tips1000' in data: tip_log['count'][p1000] = data['tips1000'] else: tip_log['count'][p1000] = 0 else: tip_log['count'] = {p1000: 0} tip_log['tips'] = { p1000: [tip for rack in tipracks1000 for tip in rack.wells()] } tip_log['max'] = {pip: len(tip_log['tips'][pip]) for pip in [p1000]} def pick_up(pip): nonlocal tip_log if tip_log['count'][pip] == tip_log['max'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_log['count'][pip] = 0 pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]]) tip_log['count'][pip] += 1 # pool samples for i in range(math.ceil(NUM_SAMPLES / 2)): if NUM_SAMPLES % 2 != 0 and i == math.ceil(NUM_SAMPLES / 2) - 1: pool_source_set = [dest_plate.wells()[NUM_SAMPLES]] vol = SAMPLE_VOLUME * 2 else: pool_source_set = dest_plate.wells()[i * 2:i * 2 + 2] vol = SAMPLE_VOLUME for s in pool_source_set: pick_up(p1000) p1000.transfer(vol, s, dest_plate.wells()[i + 64], air_gap=20, new_tip='never') p1000.air_gap(100) p1000.drop_tip() ctx.comment('Move deepwell plate (slot 2) to Station B for RNA \ extraction.') # track final used tip if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) data = {'tips1000': tip_log['count'][p1000]} with open(tip_file_path, 'w') as outfile: json.dump(data, outfile)
def run(ctx: protocol_api.ProtocolContext): import os # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: {'Execute': True, 'description': 'Add samples ('+str(volume_sample)+'ul)'}, 2: {'Execute': False, 'description': 'Add internal control one by one (10ul)'} # We won't use it as we will do it in KB with the multichannel pipette } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 if not ctx.is_simulating(): # Folder and file_path for log time folder_path = '/var/lib/jupyter/notebooks/'+run_id if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/KA_SampleSetup_viral_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo, tip_recycling = 'none',rinse_loops = 2): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.reagent_reservoir_volume = reagent_reservoir_volume self.delay = delay self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.unused=[] self.tip_recycling = tip_recycling self.vol_well_original = reagent_reservoir_volume / num_wells self.rinse_loops = rinse_loops Samples = Reagent(name = 'Samples', flow_rate_aspirate = 1, flow_rate_dispense = 1, rinse = False, delay = 0, reagent_reservoir_volume = 100 * 24, num_wells = 24, # num_cols comes from available columns h_cono = h_cone, v_fondo = volume_cone ) # cone IC = Reagent(name = 'Internal control', flow_rate_aspirate = 1, flow_rate_dispense = 3, rinse = False, delay = 0, reagent_reservoir_volume = $IC_total_volume, num_wells = $IC_wells, # num_cols comes from available columns h_cono = h_cone, v_fondo = volume_cone ) # cone Samples.vol_well = Samples.vol_well_original IC.vol_well = IC.vol_well_original ################## # Custom functions def generate_source_table(source): ''' Concatenate the wells from the different origin racks ''' for rack_number in range(len(source)): if rack_number == 0: s = source[rack_number].wells() else: s = s + source[rack_number].wells() return s def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip, post_dispense=False, post_dispense_vol=20, post_airgap=False, post_airgap_vol=10): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location = source, vol = vol, rounds = 2, blow_out = True, mix_height = 0, x_offset = x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x = x_offset[0])) pipet.aspirate(vol, s, rate = reagent.flow_rate_aspirate) # aspirate liquid if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z = -2), rate = reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z = disp_height).move(Point(x = x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate = reagent.flow_rate_dispense) # dispense all ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z = -2)) if post_dispense == True: pipet.dispense(post_dispense_vol, dest.top(z = -2)) if touch_tip == True: pipet.touch_tip(speed = 20, v_offset = -5, radius = 0.9) if post_airgap == True: pipet.aspirate(post_airgap_vol, dest.top(z = 5)) def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height = 3, post_airgap=False, post_airgap_vol=10, post_dispense=False, post_dispense_vol=20,): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height == 0: mix_height = 3 pipet.aspirate(1, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) pipet.dispense(vol, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) pipet.dispense(1, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out if post_dispense == True: pipet.dispense(post_dispense_vol, location.top(z = -2)) if post_airgap == True: pipet.dispense(post_airgap_vol, location.top(z = 5)) def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.4, extra_volume = 50): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if reagent.vol_well < aspirate_volume + extra_volume: reagent.unused.append(reagent.vol_well) ctx.comment('Next column should be picked') ctx.comment('Previous to change: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment(str('After change: ' + str(reagent.col))) reagent.vol_well = reagent.vol_well_original ctx.comment('New volume:' + str(reagent.vol_well)) height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Remaining volume:' + str(reagent.vol_well)) if height < min_height: height = min_height col_change = True else: height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Calculated height is ' + str(height)) if height < min_height: height = min_height ctx.comment('Used height is ' + str(height)) col_change = False return height, col_change ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up(pip): nonlocal tip_track if not ctx.is_simulating(): if tip_track['counts'][pip] == tip_track['maxes'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_track['counts'][pip] = 0 pip.pick_up_tip() #################################### # load labware and modules #################################### # Load Sample racks if NUM_SAMPLES < 96: rack_num = math.ceil(NUM_SAMPLES / 24) ctx.comment('Used source racks are ' + str(rack_num)) samples_last_rack = NUM_SAMPLES - rack_num * 24 else: rack_num = 4 source_tube_types={'Screwcap 2ml': ['opentrons_24_tuberack_generic_2ml_screwcap','source tuberack with screwcap'], 'Eppendorf 1.5ml': ['opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap','source tuberack with eppendorf'], } source_racks = [ctx.load_labware( source_tube_types[source_type][0], slot, source_tube_types[source_type][1] + str(i + 1)) for i, slot in enumerate(['4', '1', '6', '3'][:rack_num]) ] ic_source_rack = ctx.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', '9', 'Internal control source') ic_source = ic_source_rack.wells()[0] # internal control comes from 1 bottle ################################## # Destination plate dest_plate = ctx.load_labware( 'kf_96_wellplate_2400ul', '5', 'KF 96well destination plate') #################################### # Load tip_racks # tips20 = [ctx.load_labware('opentrons_96_filtertiprack_20ul', slot, '20µl filter tiprack') # for slot in ['2', '8']] tips300 = [ctx.load_labware('opentrons_96_filtertiprack_200ul', slot, '200µl filter tiprack') for slot in ['10', '11']] tips20 = [ctx.load_labware('opentrons_96_filtertiprack_20ul', slot, '20µl filter tiprack') for slot in ['8']] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate # setup samples and destinations sample_sources_full = generate_source_table(source_racks) sample_sources = sample_sources_full[:NUM_SAMPLES] destinations = dest_plate.wells()[:NUM_SAMPLES] p20 = ctx.load_instrument( 'p20_single_gen2', mount='left', tip_racks=tips20) p300 = ctx.load_instrument( 'p300_single_gen2', mount='right', tip_racks=tips300) # load P1000 pipette # used tip counter and set maximum tips available tip_track = { 'counts': {p300: 0, p20: 0}, # p1000: 0}, 'maxes': {p300: len(tips300) * 96, p20: len(tips20)*96} # ,p20: len(tips20)*96, } ############################################################################ # STEP 1: Add Samples ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') # Transfer parameters start = datetime.now() for s, d in zip(sample_sources, destinations): if not p300.hw_pipette['has_tip']: pick_up(p300) # Mix the sample BEFORE dispensing #custom_mix(p1000, reagent = Samples, location = s, vol = volume_sample, rounds = 2, blow_out = True, mix_height = 15) move_vol_multichannel(p300, reagent = Samples, source = s, dest = d, vol=volume_sample, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 0.3, rinse = Samples.rinse, disp_height = -10, blow_out = True, touch_tip = True) # Mix the sample AFTER dispensing #custom_mix(p300, reagent = Samples, location = d, vol = volume_sample, rounds = 2, blow_out = True, mix_height = 15) # Drop tip and update counter p300.drop_tip(home_after=False) tip_track['counts'][p300] += 1 # Time statistics end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 2: Add internal control ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') #BEWARE, everything with the same tip, dispensed from top! # Transfer parameters start = datetime.now() for d in destinations: if not p20.hw_pipette['has_tip']: pick_up(p20) # Mix the sample BEFORE dispensing #custom_mix(p1000, reagent = Samples, location = s, vol = volume_sample, rounds = 2, blow_out = True, mix_height = 15) move_vol_multichannel(p20, reagent = IC, source = ic_source, dest = d, vol = ic_volume, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 0.4, rinse = IC.rinse, disp_height = -8, blow_out = True, touch_tip = False) # Mix the sample AFTER dispensing #custom_mix(p20, reagent = Samples, location = d, vol = 10, rounds = 2, #blow_out = True, mix_height = 2, x_offset = x_offset) # Drop tip and update counter p20.drop_tip() tip_track['counts'][p20] += 1 # Time statistics end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write('STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program '''if not ctx.is_simulating(): os.system('mpg123 -f -8000 /etc/audio/speaker-test.mp3 &')''' for i in range(3): ctx._hw_manager.hardware.set_lights(rails=False) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(rails=True) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(rails=False) ctx.home() ctx.comment( 'Finished! \nMove deepwell plate to Station B.') ctx.comment('Used 200µl tips in total: ' + str(tip_track['counts'][p300])) ctx.comment('Used 200ul racks in total: '+str(tip_track['counts'][p300] / 96)) ctx.comment('Used p20 tips in total: ' + str(tip_track['counts'][p20])) ctx.comment('Used p20 racks in total: ' + str(tip_track['counts'][p20] / 96))
def run(ctx: protocol_api.ProtocolContext): ctx.comment('Actual used columns: ' + str(num_cols)) # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: {'Execute': True, 'description': 'Add 300 ul Wash Buffer 1 - Round 1'}, 2: {'Execute': True, 'description': 'Add 300 ul Wash Buffer 1 - Round 2'}, 3: {'Execute': True, 'description': 'Add 450 ul Wash Buffer 2 - Round 1'}, 4: {'Execute': True, 'description': 'Add 450 ul Wash Buffer 2 - Round 2'}, 5: {'Execute': True, 'description': 'Add 50 ul Elution Buffer'}, } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 folder_path = '/var/lib/jupyter/notebooks/'+run_id if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/KB_PlateFilling_pathogen_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo, tip_recycling = 'none'): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.reagent_reservoir_volume = reagent_reservoir_volume self.delay = delay #Delay of reagent in dispense self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.unused=[] self.tip_recycling = tip_recycling self.vol_well_original = reagent_reservoir_volume / num_wells # Reagents and their characteristics WashBuffer1 = Reagent(name='Wash Buffer 1', flow_rate_aspirate=0.75, flow_rate_dispense=1, rinse=True, delay=2, reagent_reservoir_volume=100000, num_wells=1, h_cono=0, v_fondo=0) # Flat surface WashBuffer2 = Reagent(name='Wash Buffer 1', flow_rate_aspirate=0.75, flow_rate_dispense=1, rinse=True, delay=2, reagent_reservoir_volume=100000, num_wells=1, h_cono=0, v_fondo=0) # Flat surface ElutionBuffer = Reagent(name='Elution Buffer', flow_rate_aspirate=1, flow_rate_dispense=1, rinse=False, delay=0, reagent_reservoir_volume=50*NUM_SAMPLES, num_wells=1, h_cono=1.95, v_fondo=695) # Prismatic WashBuffer1.vol_well = WashBuffer1.vol_well_original WashBuffer2.vol_well = WashBuffer2.vol_well_original ElutionBuffer.vol_well = ElutionBuffer.vol_well_original ################## # Custom functions def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location = source, vol = vol, rounds = 2, blow_out = True, mix_height = 0, x_offset = x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x = x_offset[0])) pipet.aspirate(vol, s, rate = reagent.flow_rate_aspirate) # aspirate liquid if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z = -2), rate = reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z = disp_height).move(Point(x = x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate = reagent.flow_rate_dispense) # dispense all ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z = -2)) if touch_tip == True: pipet.touch_tip(radius=0.9, speed = 20, v_offset = -5) #radius here is 0.9 def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height = 3): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height == 0: mix_height = 3 pipet.aspirate(1, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) pipet.dispense(vol, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) pipet.dispense(1, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.5, extra_volume = 50): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if reagent.vol_well < aspirate_volume + extra_volume: reagent.unused.append(reagent.vol_well) ctx.comment('Next column should be picked') ctx.comment('Previous to change: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment(str('After change: ' + str(reagent.col))) reagent.vol_well = reagent.vol_well_original ctx.comment('New volume:' + str(reagent.vol_well)) height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Remaining volume:' + str(reagent.vol_well)) if height < min_height: height = min_height col_change = True else: height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Calculated height is ' + str(height)) if height < min_height: height = min_height ctx.comment('Used height is ' + str(height)) col_change = False return height, col_change ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up(pip): nonlocal tip_track if not ctx.is_simulating(): if tip_track['counts'][pip] == tip_track['maxes'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_track['counts'][pip] = 0 pip.pick_up_tip() ########## def find_side(col): ''' Detects if the current column has the magnet at its left or right side ''' if col % 2 == 0: side = -1 # left else: side = 1 return side #################################### # load labware and modules # 12 well rack #################################### reagent_res = ctx.load_labware( 'nest_12_reservoir_15ml', '3', 'Reservoir 12 channel, column 1') # WashBuffer1 reservoir #################################### WashBuffer1_reservoir = ctx.load_labware( 'nalgene_1_reservoir_300000ul', '2', 'Ethanol 80% reservoir') # WashBuffer2 reservoir #################################### WashBuffer2_reservoir = ctx.load_labware( 'nalgene_1_reservoir_300000ul', '11', 'WashBuffer reservoir') # Wash Buffer 1 300ul Deepwell plate ############################################ WashBuffer1_300ul_plate1 = ctx.load_labware( 'kf_96_wellplate_2400ul', '1', 'Wash Buffer 1 Deepwell plate 1') # Wash Buffer 1 300ul Deepwell plate ############################################ WashBuffer1_300ul_plate2 = ctx.load_labware( 'kf_96_wellplate_2400ul', '4', 'Wash Buffer 1 Deepwell plate 2') # Wash Buffer 2 450ul Deepwell plate ############################################ WashBuffer2_450ul_plate1 = ctx.load_labware( 'kf_96_wellplate_2400ul', '7', 'Wash Buffer 2 Deepwell plate 1') # Wash Buffer 2 450ul Deepwell plate ############################################ WashBuffer2_450ul_plate2 = ctx.load_labware( 'kf_96_wellplate_2400ul', '10', 'Wash Buffer 2 Deepwell plate 2') # Elution Deepwell plate ############################################ ElutionBuffer_50ul_plate = ctx.load_labware( 'kingfisher_std_96_wellplate_550ul', '6', 'Elution Buffer 50 ul STD plate') #################################### # Load tip_racks tips300 = [ctx.load_labware('opentrons_96_tiprack_300ul', slot, '200µl filter tiprack') for slot in ['8']] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate WashBuffer1.reagent_reservoir = WashBuffer1_reservoir.wells()[0] WashBuffer2.reagent_reservoir = WashBuffer2_reservoir.wells()[0] ElutionBuffer.reagent_reservoir = reagent_res.rows()[0][0] # columns in destination plates to be filled depending the number of samples wb1plate1_destination = WashBuffer1_300ul_plate1.rows()[0][:num_cols] wb1plate2_destination = WashBuffer1_300ul_plate2.rows()[0][:num_cols] wb2plate1_destination = WashBuffer2_450ul_plate1.rows()[0][:num_cols] wb2plate2_destination = WashBuffer2_450ul_plate2.rows()[0][:num_cols] elutionbuffer_destination = ElutionBuffer_50ul_plate.rows()[0][:num_cols] # pipette m300 = ctx.load_instrument( 'p300_multi_gen2', 'right', tip_racks=tips300) # Load multi pipette # used tip counter and set maximum tips available tip_track = { 'counts': {m300: 0}, 'maxes': {m300: len(tips300)*96} } ############################################################################ # STEP 1 Filling with WashBuffer1 plate 1 ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') wash_buffer_vol = [150, 150] rinse = False # Only first time ######## # Wash buffer dispense for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(wash_buffer_vol): if (i == 0 and j == 0): rinse = True #Rinse only first transfer else: rinse = False move_vol_multichannel(m300, reagent = WashBuffer1, source = WashBuffer1.reagent_reservoir, dest = wb1plate1_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 1, rinse = rinse, disp_height = -2, blow_out = True, touch_tip = True) m300.drop_tip(home_after=True) tip_track['counts'][m300] += 8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 2 Filling with WashBuffer1 plate 2 ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') wash_buffer_vol = [150, 150] rinse = False # Only first time ######## # Wash buffer dispense for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(wash_buffer_vol): if (i == 0 and j == 0): rinse = True else: rinse = False move_vol_multichannel(m300, reagent = WashBuffer1, source = WashBuffer1.reagent_reservoir, dest = wb1plate2_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 1, rinse = rinse, disp_height = -2, blow_out = True, touch_tip = True) m300.drop_tip(home_after=True) tip_track['counts'][m300] += 8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 3 Filling with WashBuffer2 plate 1 ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') wash_buffer_vol = [150, 150, 150] rinse = False # Only first time ######## # Wash buffer dispense for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(wash_buffer_vol): if (i == 0 and j == 0): rinse = True else: rinse = False move_vol_multichannel(m300, reagent = WashBuffer2, source = WashBuffer2.reagent_reservoir, dest = wb2plate1_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 1, rinse = rinse, disp_height = -2, blow_out = True, touch_tip = True) m300.drop_tip(home_after=True) tip_track['counts'][m300] += 8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 4 Filling with WashBuffer2 plate 2 ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') ethanol_vol = [150, 150, 150] rinse = False # Only first time ######## # Ethanol dispense for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(ethanol_vol): if (i == 0 and j == 0): rinse = True else: rinse = False move_vol_multichannel(m300, reagent = WashBuffer2, source = WashBuffer2.reagent_reservoir, dest = wb2plate2_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 1, rinse = rinse, disp_height = -2, blow_out = True, touch_tip = True) m300.drop_tip(home_after=True) tip_track['counts'][m300] += 8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 5 Transfer Elution buffer ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') # Elution buffer ElutionBuffer_vol = [50] ######## # Water or elution buffer for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) for transfer_vol in ElutionBuffer_vol: # Calculate pickup_height based on remaining volume and shape of container [pickup_height, change_col] = calc_height( ElutionBuffer, multi_well_rack_area, transfer_vol * 8) ctx.comment( 'Aspirate from Reservoir column: ' + str(ElutionBuffer.col)) ctx.comment('Pickup height is ' + str(pickup_height)) move_vol_multichannel(m300, reagent = ElutionBuffer, source = ElutionBuffer.reagent_reservoir, dest = elutionbuffer_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol_elutionbuffer, x_offset = x_offset, pickup_height = pickup_height, rinse = False, disp_height = -2, blow_out = True, touch_tip = False) m300.drop_tip(home_after=True) tip_track['counts'][m300] += 8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write('STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program from opentrons.drivers.rpi_drivers import gpio for i in range(3): gpio.set_rail_lights(False) gpio.set_button_light(1, 0, 0) time.sleep(0.3) gpio.set_rail_lights(True) gpio.set_button_light(0, 0, 1) time.sleep(0.3) gpio.set_button_light(0, 1, 0) ctx.comment( 'Finished! \nMove deepwell plates to KingFisher extractor.') ctx.comment('Used tips in total: ' + str(tip_track['counts'][m300])) ctx.comment('Used racks in total: ' + str(tip_track['counts'][m300] / 96))
def run(ctx: protocol_api.ProtocolContext): [csv, m300_mount, p300_mount, temp_mod_temp, asp_rate_step1, pbs_dispense_rate, incubation_time, first_media_x, second_media_y, track_tips ] = get_values( # noqa: F821 "csv", "m300_mount", "p300_mount", "temp_mod_temp", "asp_rate_step1", "pbs_dispense_rate", "incubation_time", "first_media_x", "second_media_y", "track_tips") # LABWARE temp_mod = ctx.load_module('temperature module gen2', '10') reagents = ctx.load_labware('nest_12_reservoir_15ml', '11') waste_res = ctx.load_labware('nest_12_reservoir_15ml', '7') plate = temp_mod.load_labware( 'corning_96_wellplate_360ul_flat', '10') # TIPRACKS tipracks = [ctx.load_labware('opentrons_96_filtertiprack_200ul', slot) for slot in ['4', '5', '6']] # INSTRUMENTS p300 = ctx.load_instrument('p300_single_gen2', p300_mount, tip_racks=tipracks) m300 = ctx.load_instrument('p300_multi_gen2', m300_mount, tip_racks=tipracks) tips_by_col = [tip for rack in tipracks for col in rack.columns() for tip in col[::-1]] tip_cols = [tips_by_col[i:i+8] for i in range(0, len(tips_by_col), 8)] """ TIP-TRACKING BETWEEN RUNS. """ total_tip_cols = 36 file_path = '/data/csv/tiptracking.json' file_dir = os.path.dirname(file_path) tips_by_col = [tip for rack in tipracks for col in rack.columns() for tip in col[::-1]] tip_cols = [tips_by_col[i:i+8] for i in range(0, len(tips_by_col), 8)] if track_tips and not ctx.is_simulating(): # check for file directory if not os.path.exists(file_dir): os.makedirs(file_dir) # if no file, then use standard tip_chunks definition, and the # end of the code will write to updated tip_chunks list to # the file created. This if statement handles the case in which # tip tracking is selected for the first time. if not os.path.isfile(file_path): tip_chunks = [tips_by_col[i:i+8] for i in range(0, len(tips_by_col), 8)] else: # grab nested list tip_chunks from file. source = open(file_path, 'rb').read() # see below for conversion of tip_chunks nested list to bools. # in order to dump the well objects in tip_chunks to a json file, # they had to be serializable (int, string, bool). The end of the # protocol does this conversion. tip_bool_chunks = json.loads(source) # convert bools back to well objects to use. tip_chunks = [[] for _ in range(total_tip_cols)] for i, (bool_chunk, tip_chunk) in enumerate(zip(tip_bool_chunks, tip_cols)): if len(bool_chunk) == 0: continue for true_tip, tip_loc in zip(bool_chunk, tip_chunk): if true_tip: tip_chunks[i].append(tip_loc) else: continue else: # standard definition of tip_chunks if not tracking tips. tip_chunks = [tips_by_col[i:i+8] for i in range(0, len(tips_by_col), 8)] """PROTOCOL BEGINS """ csv_rows = [[val.strip() for val in line.split(',')] for line in csv.splitlines() if line.split(',')[0].strip()][1:] """FIND INVOLVED WELLS""" values_from_csv = [] wells_from_csv = [] for row in csv_rows: well, value = row[:2] value = int(value) values_from_csv.append(value) wells_from_csv.append(well) # create nested list of all values in csv (by column). value_chunk_cols = [values_from_csv[i:i+8] for i in range(0, len(values_from_csv), 8)] list_well_tips = [] """CREATE A LIST OF # TIPS FOR EACH WELL""" start_point = 0 tip_count = 0 for i, chunk in enumerate(value_chunk_cols): start_point = 0 # check for the values in each column. # if we find a well with a value of 85 or greater, # use that index (j) as the starting point and see how many values # after that are also greater than 85. Once we don't find one, # break. for j, value in enumerate(chunk[start_point:]): if value >= 85: for check_values in chunk[j:]: if check_values >= 85: tip_count += 1 else: break list_well_tips.append(tip_count) tip_count = 0 continue else: list_well_tips.append(0) # create a dictionary which says how many tips go to each well. # For example, if the entire first column has values higher than 85, # the first value in the dictionary will be "A1: 8". If first four wells # in column 2 are greater than 85, "B1:4". dict_tips_per_well = {} tip_ctr = 0 for j, (well, num_tips) in enumerate(zip(wells_from_csv, list_well_tips)): if tip_ctr > 0: tip_ctr -= 1 continue if num_tips > 0: tip_ctr = num_tips - 1 dict_tips_per_well[well] = num_tips # print('\n\n', dict_tips_per_well, '\n\n') """PICKUP FUNCTION""" def pick_up(num_channels_per_pickup): nonlocal tip_chunks if num_channels_per_pickup > 1: pip = m300 else: pip = p300 try: col = 0 # based on the demand of the next well (1-8 tips), this for loop # will find the first available column having adequate number # of tips starting from the first. If the tip pick up order is: # 6, 8, 2 for the first 3 pick ups, then 6 tips will be taken from # column 1, 8 tips from column 2, and 2 tips back from column 1. # efficient tip pick up instead of "throwing away" a whole column # after pick up. for _ in range(36): if num_channels_per_pickup <= len(tip_chunks[col]): break else: col += 1 pip.pick_up_tip(tip_chunks[col][num_channels_per_pickup-1]) # remove as many tips as we picked up in that column # from the 0 index. for _ in range(num_channels_per_pickup): tip_chunks[col].pop(0) # replace tip exception except IndexError: ctx.pause("Replace empty tip racks on slots 4, 5, and 6") pip.reset_tipracks() tip_chunks = [tips_by_col[i:i+8] for i in range(0, len(tips_by_col), 8)] col = 0 for _ in range(36): if num_channels_per_pickup <= len(tip_chunks[col]): break else: col += 1 pip.pick_up_tip(tip_chunks[col][num_channels_per_pickup-1]) for _ in range(num_channels_per_pickup): tip_chunks[col].pop(0) if len(tip_chunks[col]) == 0: tip_chunks.remove(tip_chunks[col]) # DUMP WASTE vol_ctr = 0 waste_well = 0 # move to next well in reservoir once we fill one. def check_waste_vol(vol): nonlocal vol_ctr nonlocal waste_well vol_ctr += vol if vol_ctr > 12000: waste_well += 1 vol_ctr = 0 waste = waste_res.wells()[waste_well] temp_mod.set_temperature(temp_mod_temp) ctx.pause(""" Ensure temperature module is at correct temperature, then, select "Resume" on the Opentrons app. """) # REAGENTS pbs = reagents.wells()[0] trypsin = reagents.wells()[1] media = reagents.wells()[-1] airgap = 20 ctx.comment("MOVING INCLUDED WELLS TO WASTE") for i, well in enumerate(dict_tips_per_well): num_tips = dict_tips_per_well[well] plate_well = plate.wells_by_name()[well] if num_tips > 1: pip = m300 else: pip = p300 pick_up(num_tips) # aspirate from side so as to not disturb cell culture. pip.aspirate(200, plate_well.bottom(z=1).move( Point(x=(plate_well.diameter/2-2))), rate=asp_rate_step1) pip.dispense(200, waste) check_waste_vol(200) pip.air_gap(airgap) pip.drop_tip() ctx.comment('\n') ctx.comment("\n\n\nMOVING PBS TO PLATE") for i, well in enumerate(dict_tips_per_well): num_tips = dict_tips_per_well[well] plate_well = plate.wells_by_name()[well] if num_tips > 1: pip = m300 else: pip = p300 pick_up(num_tips) pip.aspirate(150, pbs, rate=pbs_dispense_rate) pip.dispense(150, plate_well.bottom(z=1).move( Point(x=(plate_well.diameter/2-2)))) pip.air_gap(airgap) pip.drop_tip() ctx.comment('\n') ctx.comment("\n\n\nREMOVING PBS FROM PLATE") for i, well in enumerate(dict_tips_per_well): num_tips = dict_tips_per_well[well] plate_well = plate.wells_by_name()[well] if num_tips > 1: pip = m300 else: pip = p300 pick_up(num_tips) pip.aspirate(175, plate_well.bottom(z=1).move( Point(x=(plate_well.diameter/2-2)))) pip.dispense(175, waste) pip.air_gap(airgap) pip.drop_tip() ctx.comment('\n') ctx.comment("\n\n\nMOVING TRYPSIN TO PLATE") for i, well in enumerate(dict_tips_per_well): num_tips = dict_tips_per_well[well] plate_well = plate.wells_by_name()[well] if num_tips > 1: pip = m300 else: pip = p300 pick_up(num_tips) pip.aspirate(25, trypsin) pip.dispense(25, plate_well) pip.blow_out() pip.touch_tip() pip.air_gap(airgap) pip.drop_tip() ctx.comment('\n') ctx.delay(minutes=incubation_time) ctx.comment("\n\n\nMOVING MEDIA TO PLATE") for i, well in enumerate(dict_tips_per_well): num_tips = dict_tips_per_well[well] plate_well = plate.wells_by_name()[well] if num_tips > 1: pip = m300 else: pip = p300 pick_up(num_tips) pip.aspirate(140, media) pip.dispense(140, plate_well) pip.blow_out() pip.touch_tip() pip.air_gap(airgap) pip.drop_tip() ctx.comment('\n') ctx.comment("\n\n\nASPIRATE FIRST MEDIA FROM PLATE") for i, well in enumerate(dict_tips_per_well): num_tips = dict_tips_per_well[well] plate_well = plate.wells_by_name()[well] if num_tips > 1: pip = m300 else: pip = p300 pick_up(num_tips) pip.aspirate(first_media_x, plate_well.bottom(z=1).move( Point(x=(plate_well.diameter/2-2)))) pip.dispense(first_media_x, waste) pip.air_gap(airgap) pip.drop_tip() ctx.comment('\n') ctx.comment("\n\n\nDISPENSE SECOND MEDIA TO PLATE") for i, well in enumerate(dict_tips_per_well): num_tips = dict_tips_per_well[well] plate_well = plate.wells_by_name()[well] if num_tips > 1: pip = m300 else: pip = p300 pick_up(num_tips) pip.aspirate(second_media_y, media) pip.dispense(second_media_y, plate_well) pip.air_gap(airgap) pip.drop_tip() ctx.comment('\n') tip_data = [] for i, chunk in enumerate(tip_chunks): tip_data.append([]) if len(chunk) > 0: for value in chunk: tip_data[i].append(True) else: continue # write to the ot-2 no matter what in case the user would like to start # tracking tips for the next run if not ctx.is_simulating(): with open(file_path, 'w') as outfile: outfile.write(json.dumps(tip_data))
def run(protocol: protocol_api.ProtocolContext): ################### SETTING UP ################### ## ## ## ヽ༼ຈل͜ຈ༽ノ LABWARE ヽ༼ຈل͜ຈ༽ノ ## ## ## ################################################## # Positions are: # 10 11 TRASH # 7 8 9 # 4 5 6 # 1 2 3 #Modules, plate and MAGNET HEIGHT magneto = protocol.load_module("magneticModuleV2", 6) deepPlate = magneto.load_labware("eppendorf_96_deepwell_2ml", label="Deep well") magnetHeight = 6.7 ######################################################################################################### ## We have tested appropriate height on GEN1 magdeck for different plates, these are the chosen ones ## ## ## ## Zymoresearch - "zymoresearch_96_deepwell_2.4ml" - 12.5mm ## ## Eppendorf - "eppendorf_96_deepwell_2ml" - 11.8 mm ## ## Starlab - "usascientific_96_wellplate_2.4ml_deep" - E2896-1810 11.4mm ## ## Macherey-Nagel - - 10mm ## ######################################################################################################### #Plates reagents = protocol.load_labware("nest_12_reservoir_15ml", 5, label="Reagents reservoir") waste = protocol.load_labware("nest_12_reservoir_15ml", 9, label="Liquid waste reservoir") outplate = protocol.load_labware("eppendorf96_skirted_150ul", 1, label="Output plate") #Tips - Ordered in the way they are used parkingRack = protocol.load_labware("opentrons_96_filtertiprack_200ul", 3) tiprack1 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 2) tiprack4 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 4) tiprack7 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 7) tiprack8 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 8) tiprack10 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 10) tipracks = [tiprack10, tiprack8, tiprack7, tiprack4, tiprack1] availableTips = [] for rack in tipracks: for i in range(1, 13): availableTips.append(rack["A" + str(i)]) #To take a tip, just use availableTips.pop() and voila! #There are enough tips for 6 samples, and a tiprack will end up half-full #Tips are taken from right to left instead of the normal way #I am using this dictionary method because it makes it easier to modify later the script for 96 samples/ #Pipettes p300 = protocol.load_instrument("p300_multi_gen2", "left") ################### SETTING UP ################### ## ## ## ヽ༼ຈل͜ຈ༽ノ BASIC VARIABLES ヽ༼ຈل͜ຈ༽ノ ## ## ## ################################################## #RUN SETTINGS runColumns = 3 # Range 1-6. Samples should be a multiple of 8, or you will waste reagents. mixRepeats = 15 # Used everytime there is mixing, except when mixing beads. beadsMixRepeats = 10 # Used when mixing beads in reservoir. This happens when pipetting column 1, 3 and 6. waterMixRepeats = 20 # Used when mixing elute. #Mixing settings washMixing = 100 # volume (ul) waterMixing = 25 # volume (ul) bottomHeight = 0.5 # Distance relative to real labware's bottom. Used when removing supernatant and moving elution. bottomMixHeight = 0.8 # Distance relative to real labware's bottom. Used when mixing SamplePlusProteinaseHeight = 3 # For a volume of 260.. SamplePlusProteinasePlusBeadsHeight = 5.75 # For a volume of 465... generalHeight = 4 # Used always except when mixing beads in reservoir - Units relative to well bottom (mm). For a vol of 280... beadsHeight = 10 # Used when mixing beads in reservoir - Units relative to well bottom (mm). When mixing beads in the reagent well - Maybe I should modify this and make it depend on runColumns #Incubation times - Minutes incubationProteinase = 10 incubationBeadsNoMagnet = 5 # After adding the beads, we incubate them for 5 min without magnet. incubationBeadsMagnet = 5 * 2 incubationWash = 3 * 2 incubationDry = 10 # After removing final wash, the beads are left to dry for a while. incubationWater = 5 # After mixing. No magnet. incubationWaterMagnet = 1 * 2 # After incubationWater. #Transference volumes - ul originalVol = 140 #This is not used for transfers, it is here mostly to make volumes clearer to understand proteinaseVol = 112 beadsVol = 143.5 washVol = 280 #Used for WBE and Ethanol1 and Ethanol2 dilutionVol = 50 initialSupernatant = originalVol + proteinaseVol + beadsVol #Reagent position in reservoir - Positions go from A1 to A12 (Left to right) proteinase = reagents["A1"] beads = reagents["A2"] WBE = reagents["A3"] ethanol1 = reagents["A4"] ethanol2 = reagents["A5"] water = reagents["A12"] ######################################################################################################### ## Use these formulae to identify how much volume you need based on number of columns ## ## Each reservoir well has dimensions W=8.20, L=127.76, H=31.40. To make sure there is ## ## a pool of extra content with 0.5mm of height, add 292ul (300) extra (8.20*127.76*0.05) ## ## ## ## Proteinase = (896 * runColumns) + 292 ul ## ## Beads = (1152 * Columns) + 292 ul ## ## WBE = (2240 * Columns) + 292 ul ## ## Ethanol1 = (2240 * Columns) + 292 ul ## ## Ethanol2 = (2240 * Columns) + 292 ul ## ## Water = (640 * Columns) + 292 ul ## ######################################################################################################### ################### SETTING UP ################### ## ## ## ヽ༼ຈل͜ຈ༽ノ ADVANCED VARIABLES ヽ༼ຈل͜ຈ༽ノ ## ## ## ################################################## #Column accesion list - As we wont work in all physically available columns, this list makes it easier to manage columnID = ["A1", "A3", "A5", "A7", "A9", "A11"] # These are the columns that can be used columnID = columnID[: runColumns] # Here we trim the list, to get only the number of columns chosen in runColumns #Pipette settings tipVolume = 180 # max volume to be transported in a single trip. These tips are 200ul, but if we use the entire volumen, it might touch the filter topOffset = -5 # I use this to make sure the tip stays inside the well, to avoid it spilling out while dispensing p300.flow_rate.aspirate = 50 # Flow rate in ul / second p300.flow_rate.dispense = 150 p300.flow_rate.blow_out = 200 #Flows are reduced compared to default values to avoid the production of air or foam during handling. #Volume used when mixing beads (ul) if runColumns == 1: beadsMixing = 140 else: beadsMixing = tipVolume ################### SETTING UP ################### ## ## ##(づ。◕‿‿◕。)づ FUNCTIONS (づ。◕‿‿◕。)づ ## ## ## ################################################## def clock(time, stop=False): """ The uncertainty of not knowing how much time is left in an incubation is horrible. This makes it more bearable. This function takes the duration of the incubation and outputs a message every minute to keep track of the time more easily """ while time > 0: time -= 1 protocol.delay(minutes=1) protocol.comment("Only %s minutes more! Hold in there!" % time) if time == 1 and stop == True: protocol.pause() def meneillo(pipette, pos, reps=10, distance=0.25): pipette.move_to(pos) for _ in range(reps): p300.move_to(pos.move(types.Point(x=distance, y=0, z=0))) p300.move_to(pos.move(types.Point(x=-distance, y=0, z=0))) def remove_tip(pipette, tip): """ Originally, I had a special behaviour to drop the tips, but I stopped using it. I keep this function because it makes it easy to change drop_tip() to return_tip() for test runs """ pipette.drop_tip() def retrieve_tip(pipette, tip): """ Originally, the robot took a tip, went to the top of the well it was going to work with, and aspired 20 ul there, but now we are making it aspire 20 ul after taking the tip """ pipette.pick_up_tip(tip) def well_mix(vol, loc, reps, height=generalHeight, moveSide=0, bottomHeight=bottomMixHeight): """ Aspirates <vol> from bottom of well and dispenses it from <height> <reps> times loc1 is a position at 0.3mm over the bottom of the well loc2 is a position in the same x and y posiiton than loc1, but at <height>mm over the bottom of the well The idea here is to take liquid to the very bottom and pour it from a higher point, to mix things """ p300.flow_rate.aspirate = 1000 p300.flow_rate.dispense = 1000 loc1 = loc.bottom().move( types.Point(x=0 + moveSide, y=0, z=bottomHeight)) loc2 = loc.bottom().move(types.Point(x=0 + moveSide, y=0, z=height)) for _ in range(reps): p300.aspirate(vol, loc1) p300.dispense(vol, loc2) p300.dispense(20, loc.top(topOffset)) p300.flow_rate.aspirate = 50 p300.flow_rate.dispense = 150 def remove_supernatant(vol, columnID, wasteID, reagentName="Something", pipette=p300): """ While <vol> is bigger than <tipVolume>, it divides it in ceilling(<vol>/<tipVolume>) trips. (So, if it is 396ul, we have 2 180ul trips and a 36ul trip) Flow rate is in ul/second In the move() function, positive X means 'move to the right'. With the wells we use (Column 1,3,5,7,9 and 11) pellet is placed to the right, so we use a small offset to the left """ p300.flow_rate.aspirate = 20 # In this case we reduce the flow even more to make sure the precipitate is okay. We don't wanna bother the lad protocol.comment( "\n\nREMOVING STEP: Removing %s ul of supernatant (%s) while magnet is still engaged" % (vol, reagentName)) dump = waste[wasteID] for index, ID in enumerate(columnID): currentip = parkingRack[ID] dump = waste[ID] src = deepPlate[ID] retrieve_tip(pipette, currentip) tvol = vol while tvol > tipVolume: p300.dispense(20, src.top(topOffset)) p300.transfer(tipVolume, src.bottom().move( types.Point(x=-1, y=0, z=bottomHeight)), dump.top(topOffset), new_tip="never") #Slightly to the left meneillo( p300, dump.top(topOffset) ) #In case something is TheOppositeOfDense and just drips down p300.dispense( 20 ) #Make sure we expel everything that must be expelled. We dont want to move droplets around. tvol -= tipVolume p300.dispense(20, src.top(topOffset)) p300.transfer(tvol, src.bottom().move( types.Point(x=-1, y=0, z=bottomHeight)), dump.top(topOffset), new_tip="never") meneillo(p300, dump.top(topOffset)) remove_tip(pipette, currentip) p300.flow_rate.aspirate = 50 def slow_transfer(vol, reagent, columnID, reagentName, mixVol=washMixing, repeats=mixRepeats, mixReagent=False, altura=generalHeight, magnetTime=True, incubationTime=incubationWash, moveSide=0, extraVol=0, pipette=p300, removalStepAfter=True): """ Similar to remove_supernatant, but the other way around. It transfers from point A to point B in <tipVol> ul trips and pours liquid from the top, to avoid contaminating the tip while transfering all the necessary volume. It also includes incubation and magnet """ protocol.comment( "\n\nADDING STEP: Transfering %s ul of %s to samples" % (vol, reagentName)) for index, ID in enumerate(columnID): src = reagent to = deepPlate[ID] currentip = availableTips.pop() retrieve_tip(p300, currentip) if (mixReagent == True) and ( index == 0 or index == 2 or index == 5 ): #If the reagent is to be mixed, and we are in column 1, 3 or 6, mix it. We mix three times to make sure we dont have differences protocol.comment("Mixing %s" % reagentName) well_mix( vol=beadsMixing, loc=beads, reps=beadsMixRepeats, height=beadsHeight, moveSide=0 ) #We only do this with magnetic beads, that's why we use those variable names p300.blow_out(beads.top()) tvol = vol while tvol > tipVolume: p300.dispense(20, src.top()) p300.transfer(tipVolume, src.bottom().move( types.Point(x=0, y=0, z=bottomHeight)), to.top(topOffset), new_tip="never", air_gap=extraVol) protocol.delay(seconds=2) p300.blow_out() tvol -= tipVolume p300.dispense(20, src.top()) p300.transfer(tvol, src.bottom().move( types.Point(x=0, y=0, z=bottomHeight)), to.center(), new_tip="never", air_gap=extraVol) protocol.delay(seconds=2) well_mix(vol=mixVol, loc=to, reps=repeats, moveSide=moveSide, height=altura) if removalStepAfter == True: pipette.drop_tip(parkingRack[ID]) else: remove_tip(pipette, currentip) #Incubation if magnetTime == True: protocol.comment("Engaging magnet") magneto.engage(height=magnetHeight) protocol.comment("Incubating for %s minutes" % incubationTime) clock(time=incubationTime) ################# GO, VASILY, GO ################# ## ## ## ୧༼ಠ益ಠ༽୨ PROTOCOL ୧༼ಠ益ಠ༽୨ ## ## ## ################################################## magneto.disengage() #In case it is engaged from previous protocols. protocol.comment("We are working with column IDs: %s" % columnID) protocol.comment("\n\nSamples should have an initial volume of %s ul" % originalVol) #STEP 1: Add Proteinase K/LBF. slow_transfer(vol=proteinaseVol, reagent=proteinase, reagentName="Proteinase K/LBF", incubationTime=incubationProteinase, columnID=columnID, magnetTime=False, altura=SamplePlusProteinaseHeight, removalStepAfter=False) #INCUBATION 1: 10 min [Total: 10 min] #STEP 2: mix magnetic beads, add them to samples and mix sample well. No slow_transfer function for the same reasons as before. protocol.comment("\n\nEnough incubation, time to do s t u f f") slow_transfer(vol=beadsVol, reagent=beads, reagentName="Magnetic beads", incubationTime=incubationBeadsNoMagnet, columnID=columnID, mixReagent=True, magnetTime=False, extraVol=10, altura=SamplePlusProteinasePlusBeadsHeight) #INCUBATION 2: 5 min without magnet [Total: 15 min] protocol.comment( "Engaging magnet and keeping this incubation going for other %s minutes" % incubationBeadsMagnet) magneto.engage(height=magnetHeight) clock(time=incubationBeadsMagnet, stop=True) #INCUBATION 3: 5 min incubation with magnet [Total: 20 min] #STEP 3: Remove magnetic beads supernatant remove_supernatant(vol=initialSupernatant, wasteID="A1", reagentName="beads and proteinase", columnID=columnID) protocol.comment("Disengaging magnet") magneto.disengage() #STEP 4: Add 280 ul of Wash WBE slow_transfer(vol=washVol, reagent=WBE, reagentName="WBE", incubationTime=incubationWash, columnID=columnID) #INCUBATION 4: 3 min incubaton with magnet [Total: 23 min] #STEP 5: Removing WBE Supernatant remove_supernatant(vol=washVol, wasteID="A2", reagentName="WBE", columnID=columnID) protocol.comment("Disengaging magnet") magneto.disengage() #STEP 6: First wash with Eth, tips_transfer)anol slow_transfer(vol=washVol, reagent=ethanol1, reagentName="Ethanol 70% (First time)", incubationTime=incubationWash, columnID=columnID) #INCUBATION 5: 3 min incubaton with magnet [Total: 26 min] #STEP 7: Removing the supernatant of first wash with Ethanol remove_supernatant(vol=washVol, wasteID="A3", reagentName="Ethanol 70% (First time)", columnID=columnID) protocol.comment("Disengaging magnet") magneto.disengage() #STEP 8: Second wash with Ethanol slow_transfer(vol=washVol, reagent=ethanol2, reagentName="Ethanol 70% (Second time)", incubationTime=incubationWash, columnID=columnID) #INCUBATION 6: 3 min incubaton with magnet [Total: 26 min] #STEP 9: Removing the supernatant of second wash with Ethanol remove_supernatant(vol=washVol, wasteID="A4", reagentName="Ethanol 70% (Second time)", columnID=columnID) #INCUBATION 7: 5 min incubaton with magnet [Total: 31 min] protocol.comment( "This time, I do not disengage the magnet and let the beads dry for %s min" % incubationDry) clock(time=incubationDry) #STEP 10: Diluting samples in 80 ul of RNAse free water protocol.comment("Disengaging magnet") magneto.disengage() protocol.comment("Diluting samples in %s ul of RNAse free water" % dilutionVol) slow_transfer(vol=dilutionVol, reagent=water, reagentName="RNAse-free water", incubationTime=incubationWater, columnID=columnID, mixVol=waterMixing, magnetTime=False, repeats=waterMixRepeats) #Moving tip on top of pellet #INCUBATION 8: 5 min incubaton WITHOUT magnet [Total: 36 min] protocol.comment("Engaging magnet now!") magneto.engage(height=magnetHeight) clock(time=incubationWaterMagnet) #INCUBATION 9: 3 min incubaton WITH magnet [Total: 39 min] #STEP 11: Transfering samples to output plate protocol.comment( "Transfering DNA to output plate while magnet is still engaged") p300.flow_rate.aspirate = 20 for index, ID in enumerate(columnID): currentip = availableTips.pop() retrieve_tip(p300, currentip) src = deepPlate[ID] to = outplate[ID] p300.dispense(20, src.top()) p300.transfer(dilutionVol, src.bottom().move(types.Point(x=-1, y=0, z=bottomHeight)), to.bottom(5), new_tip="never") protocol.delay(seconds=2) p300.dispense(20) remove_tip(p300, currentip) magneto.disengage() protocol.comment("\n\nFecho!")
def run(protocol: protocol_api.ProtocolContext): plate = protocol.load_labware("corning_96_wellplate_360ul_flat", 2) tiprack_20 = protocol.load_labware("opentrons_96_tiprack_20ul", 1) tiprack_300 = protocol.load_labware("opentrons_96_tiprack_300ul", 4) tuberack_1 = protocol.load_labware( "opentrons_24_aluminumblock_nest_1.5ml_snapcap", 5) tuberack_2 = protocol.load_labware( "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical", 3) # Pipettes p300 = protocol.load_instrument("p300_single", "Left", tip_racks=[tiprack_300]) p20 = protocol.load_instrument("p20_single_gen2", "Right", tip_racks=[tiprack_20]) # Ensures liquids dispensed 10 mm above bottom of well to prevent contamination p300.well_bottom_clearance.dispense = 10 p20.well_bottom_clearance.dispense = 10 # buffers and enzyme volumes s30_buffer = [ 161, 165, 179, 183, 175, 166, 175, 179, 170, 175, 171, 165, 170, 170, 165, 174, 156, 174, 179, 174, 170, 166, 161, 170, 166, 161, 170 ] pal = [ 5, 10, 1, 1, 1, 10, 5, 5, 10, 5, 5, 1, 5, 1, 10, 1, 10, 10, 1, 1, 1, 5, 10, 10, 5, 10, 5 ] four_cl = [ 10, 10, 5, 1, 5, 5, 5, 1, 1, 1, 5, 10, 10, 5, 1, 1, 10, 1, 1, 10, 10, 10, 5, 5, 5, 10, 1 ] sts = [ 10, 1, 1, 1, 5, 5, 1, 1, 5, 5, 5, 10, 1, 10, 10, 10, 10, 1, 5, 1, 5, 5, 10, 1, 10, 5, 10 ] # S30 buffer p300.transfer( s30_buffer, tuberack_2['A1'], plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'A4', 'B4', 'C4'), touch_tip=True ) # Touches tip on side of the well to ensure no liquid on tip # ATP 2 ul each well p20.transfer(2, tuberack_1['A1'], plate.columns('1', '2', '3'), touch_tip=True) p20.transfer(2, tuberack_1['A1'], plate.wells('A4', 'B4', 'C4'), touch_tip=True) # CoA 2 ul each well p20.transfer(2, tuberack_1['A2'], plate.columns('1', '2', '3'), touch_tip=True) p20.transfer(2, tuberack_1['A2'], plate.wells('A4', 'B4', 'C4'), touch_tip=True) # Malonyl-CoA 8 ul each well p20.transfer(8, tuberack_1['A3'], plate.columns('1', '2', '3'), touch_tip=True) p20.transfer(8, tuberack_1['A3'], plate.wells('A4', 'B4', 'C4'), touch_tip=True) # PAL ul p20.transfer(pal, tuberack_1['A5'], plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'A4', 'B4', 'C4'), touch_tip=True) # 4CL ul p20.transfer(four_cl, tuberack_1['A6'], plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'A4', 'B4', 'C4'), touch_tip=True) # STS ul p20.transfer(sts, tuberack_1['B1'], plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'A4', 'B4', 'C4'), touch_tip=True) # L-tyrosine 2 ul each well p20.transfer( 2, tuberack_1['A4'], plate.columns('1', '2', '3'), mix_after=(3, 20), new_tip='always' ) # no need for touch tip as new tip after mix. Mix_after means liquid in wells mixed p20.transfer(2, tuberack_1['A4'], plate.wells('A4', 'B4', 'C4'), mix_after=(3, 20), new_tip='always') protocol.comment('Protocol complete!')
def run(ctx: protocol_api.ProtocolContext): [ mag_engage_time, n_samples, left_pipette_lname, right_pipette_lname, left_pipette_tipracks_lname, right_pipette_tipracks_lname, reservoir_lname, destination_plate_lname, sample_plate_lname, mag_mod_lname, tube_rack_lname, dest_temp_mod_lname, do_vortex_pause, do_SDS_step, do_DNAse_step, use_NaCl, bead_volume, wash_buf_vol, elution_buf_vol, n_washes, n_wash_mixes, n_elution_mixes, incubation_time, n_bead_mixes, sds_buffer_vol, starting_volume ] = get_values( # noqa: F821 (<--- DO NOT REMOVE!) "mag_engage_time", "n_samples", "left_pipette_lname", "right_pipette_lname", "left_pipette_tipracks_lname", "right_pipette_tipracks_lname", "reservoir_lname", "destination_plate_lname", "sample_plate_lname", "mag_mod_lname", "tube_rack_lname", "dest_temp_mod_lname", "do_vortex_pause", "do_SDS_step", "do_DNAse_step", "use_NaCl", "bead_volume", "wash_buf_vol", "elution_buf_vol", "n_washes", "n_wash_mixes", "n_elution_mixes", "incubation_time", "n_bead_mixes", "sds_buffer_vol", "starting_volume") # define all custom variables above here with descriptions: # Calculate reagent volumes # See docs for calculations # 10:1, v_lysis = v_s/9, e.g. for v_s = 1000 uL, v_lysis = 1000/9=111 uL lysis_buffer_vol = starting_volume / 9 dnaseI_vol = starting_volume / 1000 # 1 uL per mL total_lysis_vol = starting_volume + lysis_buffer_vol + dnaseI_vol nacl_first_step_vol = total_lysis_vol / 7 nacl_wash_buf_vol = wash_buf_vol / 7 total_vol_after_bead_adddn = round(total_lysis_vol + bead_volume, 1) if use_NaCl: total_vol_after_bead_adddn = round( total_vol_after_bead_adddn + nacl_first_step_vol, 1) n_columns = math.ceil(n_samples / 8) # Error checking # If DNAse I is being added from a tube we need to make sure that # a) we have a single pipette loaded and b) that dnaseI_vol > pipette min. # volume if (("single" not in left_pipette_lname and "single" not in right_pipette_lname) and do_DNAse_step): raise Exception("No single channel pipette for DNAse I distribution " + "selected") # More error checking on this in the pipette loading section # load modules ''' Add your modules here with: module_name = ctx.load_module('{module_loadname}', '{slot number}') Note: if you are loading a thermocycler, you do not need to specify a slot number - thermocyclers will always occupy slots 7, 8, 10, and 11. For all other modules, you can load them on slots 1, 3, 4, 6, 7, 9, 10. ''' # Load temperature modules (if any) dest_temp_mod = None if dest_temp_mod_lname: dest_temp_mod = ctx.load_module(dest_temp_mod_lname, '4') # Load the magnetic module mag_mod = ctx.load_module(mag_mod_lname, '1') # Load labware sample_plate = mag_mod.load_labware(sample_plate_lname) sample_well = sample_plate.wells()[0] if total_vol_after_bead_adddn > sample_well.max_volume: raise Exception("The sample wells are too small to handle the " + "volumes of reagents") reservoir = ctx.load_labware(reservoir_lname, '7') dnaseI_tuberack = None if do_DNAse_step: dnaseI_tuberack = ctx.load_labware(tube_rack_lname, '10') dest_plate = None if dest_temp_mod_lname: dest_plate = dest_temp_mod.load_labware(destination_plate_lname) else: dest_plate = ctx.load_labware(destination_plate_lname, '4') # load tipracks tipracks_left, tipracks_right = [None] * 2 if left_pipette_tipracks_lname: tipracks_left = [ ctx.load_labware(left_pipette_tipracks_lname, slot) for slot in ['5', '8', '11'] ] if right_pipette_tipracks_lname: tipracks_right = [ ctx.load_labware(right_pipette_tipracks_lname, slot) for slot in ['6', '9'] ] # load instrument ''' Nomenclature for pipette: use 'p' for single-channel, 'm' for multi-channel, followed by number of microliters. p20, p300, p1000 (single channel pipettes) m20, m300 (multi-channel pipettes) If loading pipette, load with: ctx.load_instrument( '{pipette api load name}', pipette_mount ("left", or "right"), tip_racks=tiprack ) ''' left_pip = None right_pip = None pipette = None # Remains None if there are two pipettes loaded if left_pipette_lname: left_pip = ctx.load_instrument(left_pipette_lname, "left", tip_racks=tipracks_left) if right_pipette_lname: right_pip = ctx.load_instrument(right_pipette_lname, "right", tip_racks=tipracks_right) if not (right_pip and left_pip): if right_pip: pipette = right_pip else: pipette = left_pip # Rank the pipettes by minimum volume s_pip, l_pip = [None] * 2 if pipette: s_pip, l_pip = [pipette, pipette] else: if left_pip.min_volume < right_pip.min_volume: s_pip, l_pip = [left_pip, right_pip] else: s_pip, l_pip = [right_pip, left_pip] # pipette functions # INCLUDE ANY BINDING TO CLASS ''' Define all pipette functions, and class extensions here. These may include but are not limited to: - Custom pickup functions - Custom drop tip functions - Custom Tip tracking functions - Custom Trash tracking functions - Slow tip withdrawal For any functions in your protocol, describe the function as well as describe the parameters which are to be passed in as a docstring below the function (see below). def pick_up(pipette): """`pick_up()` will pause the protocol when all tip boxes are out of tips, prompting the user to replace all tip racks. Once tipracks are reset, the protocol will start picking up tips from the first tip box as defined in the slot order when assigning the labware definition for that tip box. `pick_up()` will track tips for both pipettes if applicable. :param pipette: The pipette desired to pick up tip as definited earlier in the protocol (e.g. p300, m20). """ try: pipette.pick_up_tip() except protocol_api.labware.OutOfTipsError: ctx.pause("Replace empty tip racks") pipette.reset_tipracks() pipette.pick_up_tip() ''' def pick_up(pipette): """`pick_up()` will pause the protocol when all tip boxes are out of tips, prompting the user to replace all tip racks. Once tipracks are reset, the protocol will start picking up tips from the first tip box as defined in the slot order when assigning the labware definition for that tip box. `pick_up()` will track tips for both pipettes if applicable. :param pipette: The pipette desired to pick up tip as definited earlier in the protocol (e.g. p300, m20). """ try: pipette.pick_up_tip() except protocol_api.labware.OutOfTipsError: ctx.pause("Replace empty tip racks") pipette.reset_tipracks() pipette.pick_up_tip() # helper functions ''' Define any custom helper functions outside of the pipette scope here, using the convention seen above. e.g. def remove_supernatant(vol, index): """ function description :param vol: :param index: """ ''' def choose_pipette(vol, multi_action=True): """ Choose pipette selects an appropriate pipette based on the volume involved, and whether the pipette action involves all 8 columns or a single well. :param vol: The volume to use this pipette with :param multi_action: True if the pipette action will be done with an 8-channel pipette, i.e. involving an 8-channel source and/or target """ nonlocal s_pip, l_pip, pipette # Case when only one pipette has been loaded if pipette: if pipette.min_volume < vol: raise Exception( ("\n\nThe loaded pipette has a minimum " "volume of {} which is greater than {}. " + "Consider adding a smaller volume pipette\n").format( pipette.min_volume, vol)) if pipette.channels == 8: return pipette, True else: return pipette, False s_pip_is_multi = True if s_pip.channels == 8 else False l_pip_is_multi = True if l_pip.channels == 8 else False if s_pip.min_volume > vol: raise Exception(("The volume ({} uL)is too small to handle for " + "either pipette, please re-examine your" + "parameters").format(vol)) # The preference is for the multi-channel pipette with the largest # volume capability while the least preferred is the smallest volume # single channel pipette. if multi_action: # Prefer the multi-channel pipettes if available if l_pip_is_multi and l_pip.min_volume < vol: return l_pip, True elif s_pip_is_multi: return s_pip, True # If they are not, let a single channel pipette do the job of a # multi-channel pipette elif not l_pip_is_multi and l_pip.min_volume < vol: return l_pip, False else: return s_pip, False else: if not l_pip_is_multi and l_pip.min_volume < vol: return l_pip, False elif not s_pip_is_multi: return s_pip, False else: raise Exception(("No approriate single channel pipette is " + "loaded for the volume of {} uL".format(vol))) def transfer(pip, volume, source, dest, **kwargs): """ A wrapper function for InstrumentContext.transfer that handles labware.OutOfTipsError by asking the user to refill empty tipracks. :param pip: The pipette to user for the transfer Other parameters: See class opentrons.protocol_api.contexts.InstrumentContext """ try: pip.transfer(volume, source, dest, **kwargs) except protocol_api.labware.OutOfTipsError: ctx.pause("\n\nReplace empty tip racks for {}\n".format(pip.name)) pip.reset_tipracks() pip.transfer(volume, source, dest, **kwargs) def reag_to_wells_or_col_transfer(vol, reag_source_well, dest_plate, multi_transfer=True, **transfer_kwargs): """ function description :param vol: :param index: """ nonlocal n_columns, n_samples pip, pip_is_multi = choose_pipette(vol, multi_transfer) if pip_is_multi: dest_columns = dest_plate.columns()[0:n_columns] for col in dest_columns: transfer(pip, vol, reag_source_well, col[0], **transfer_kwargs) else: # Transfer to each well transfer(pip, vol, reag_source_well, dest_plate.wells()[0:n_samples], **transfer_kwargs) def remove_supernatant(vol, source_plate, multi_transfer=True, **transfer_kwargs): """ Removes a volume from each sample well of the source plate (well 0 to well n_samples) and transfers it to the liquid trash well of the reservoir. :param vol (float): The volume to transfer to the trash. :param source_plate (labware plate): The plate to aspirate from. :param multi_transfer (Boolean): Use 8-channel pipette and transfer a whole column at a time if true, otherwise use a single channel pipette and transfer well by well. :param **transfer_kwargs: Any keyword arguments that you may want to pass into the transfer method calls """ nonlocal n_columns, n_samples pip, pip_is_multi = choose_pipette(vol, multi_transfer) if pip_is_multi: source_columns = source_plate.columns()[0:n_columns] for col in source_columns: transfer(pip, vol, col[0], waste_well, **transfer_kwargs) else: # Transfer from each well transfer(pip, vol, source_plate.wells()[0:n_samples], waste_well, **transfer_kwargs) def transfer_plate_to_plate(vol, source_plate, destination_plate): pip, is_multi = choose_pipette(vol, True) if is_multi: for s_col, d_col in zip(source_plate.columns(), destination_plate.columns()): transfer(pip, vol, s_col[0], d_col[0]) else: transfer(pip, vol, sample_wells, dest_wells) # reagents ''' Define where all reagents are on the deck using the labware defined above. e.g. water = reservoir12.wells()[-1] waste = reservoir.wells()[0] samples = plate.rows()[0][0] dnase = tuberack.wells_by_name()['A4'] ''' lysis_buffer_well = reservoir.wells_by_name()['A1'] wash_buffer_well = reservoir.wells_by_name()['A2'] elution_buffer_well = reservoir.wells_by_name()['A3'] nacl_well = reservoir.wells_by_name()['A4'] bead_well = reservoir.wells_by_name()['A5'] waste_well = reservoir.wells()[-1].top(-2) dnaseI_tube = None if dnaseI_tuberack: dnaseI_tube = dnaseI_tuberack.wells_by_name()['A1'] sds_page_buf_well = None if do_SDS_step: sds_page_buf_well = reservoir.wells_by_name()['A6'] # plate, tube rack maps ''' Define any plate or tube maps here. e.g. plate_wells_by_row = [well for row in plate.rows() for well in row] ''' sample_wells = sample_plate.wells()[0:n_samples] dest_columns = dest_plate.columns()[0:n_columns] dest_wells = dest_plate.wells()[0:n_samples] # protocol ''' Include header sections as follows for each "section" of your protocol. Section can be defined as a step in a bench protocol. e.g. ctx.comment('\n\nMOVING MASTERMIX TO SAMPLES IN COLUMNS 1-6\n') for .... in ...: ... ... ctx.comment('\n\nRUNNING THERMOCYCLER PROFILE\n') ... ... ... ''' ctx.comment('\n\nAdding lysis buffer to samples\n') pip, pip_is_multi = choose_pipette(lysis_buffer_vol) reag_to_wells_or_col_transfer(lysis_buffer_vol, lysis_buffer_well, sample_plate, True) if do_DNAse_step: ctx.comment('\n\nAdding DNAse I to sample wells\n') # Must be single channel, protocol will raise exception otherwise reag_to_wells_or_col_transfer(dnaseI_vol, dnaseI_tube, sample_plate, False) ctx.pause("\n\nPlace sample plate on a shaker and shake for 10-20" + " minutes at room temperature for the lysis reaction" + " to complete\n") # Optional addition of NaCl for improved binding to beads if use_NaCl: ctx.comment("Adding NaCl to samples for a final conc. of 500 mM") reag_to_wells_or_col_transfer(nacl_first_step_vol, nacl_well, sample_plate, True) if do_vortex_pause: ctx.pause("Pause to vortex/resuspend your magnetic beads") # Mixing the beads in the bead well pick_up(l_pip) l_pip.mix(n_bead_mixes, l_pip.max_volume, bead_well) ctx.comment("\n\nTransferring bead solution to sample wells and mixing") pip, pip_is_multi = choose_pipette(bead_volume, True) if pip_is_multi: for col in dest_columns: if not pip.has_tip: pick_up(pip) pip.aspirate(bead_volume, bead_well) pip.dispense(bead_volume, col[0]) mix_vol = (pip.max_volume if total_vol_after_bead_adddn / 2 > pip.max_volume else total_vol_after_bead_adddn / 2) pip.mix(5, mix_vol, col[0]) pip.drop_tip() else: for well in sample_wells: if not pip.has_tip: pick_up(pip) pip.aspirate(bead_volume, bead_well) pip.dispense(bead_volume, well) mix_vol = (pip.max_volume if total_vol_after_bead_adddn / 2 > pip.max_volume else total_vol_after_bead_adddn / 2) pip.mix(5, mix_vol, well) pip.drop_tip() ctx.comment("\n\nIncubating beads\n") ctx.delay(0, incubation_time) ctx.comment("\n\nEngaging magnets\n") mag_mod.engage() ctx.delay(0, mag_engage_time) ctx.comment("\n\nRemoving supernatant\n") remove_supernatant(total_vol_after_bead_adddn, sample_plate) # Wash the beads n_washes times, the standard is 3 washes repetitions = 1 if do_SDS_step else n_washes for i in range(0, repetitions): ctx.comment("\n\nBead wash #{}".format(i + 1)) mag_mod.disengage() # Transfer wash buffer to the sample wells supernatant_volume = (wash_buf_vol + nacl_wash_buf_vol if use_NaCl else wash_buf_vol) half_supernatant_vol = supernatant_volume / 2 mix_vol = (half_supernatant_vol if half_supernatant_vol < l_pip.max_volume else l_pip.max_volume) if use_NaCl: reag_to_wells_or_col_transfer(wash_buf_vol, wash_buffer_well, sample_plate, True) reag_to_wells_or_col_transfer(nacl_wash_buf_vol, nacl_well, sample_plate, True, mix_after=(n_wash_mixes, mix_vol)) else: reag_to_wells_or_col_transfer(wash_buf_vol, wash_buffer_well, sample_plate, True, mix_after=(n_wash_mixes, mix_vol)) mag_mod.engage() ctx.delay(0, mag_engage_time) # Remove supernatant remove_supernatant(supernatant_volume, sample_plate) mag_mod.disengage() if do_SDS_step: ctx.comment("\n\nAdding 1x SDS-PAGE buffer\n") reag_to_wells_or_col_transfer(sds_buffer_vol, sds_page_buf_well, sample_plate, True) mag_mod.engage() ctx.delay(0, mag_engage_time) # Transfer to target plate ctx.comment("\n\nTransferring protein in SDS-buffer to target plate\n") transfer_plate_to_plate(sds_buffer_vol, sample_plate, dest_plate) # Elute else: ctx.comment("\n\nAdding elution buffer and mixing\n") reag_to_wells_or_col_transfer(elution_buf_vol, elution_buffer_well, sample_plate, True, mix_after=(n_elution_mixes, mix_vol)) ctx.comment("\n\nIncubating samples with elution buffer\n") ctx.delay(0, incubation_time) ctx.comment("\n\nAttracting beads to magnets\n") mag_mod.engage() ctx.delay(0, mag_engage_time) ctx.comment("\n\nTransferring protein elution supernatant to" + "target plate\n") transfer_plate_to_plate(elution_buf_vol, sample_plate, dest_plate) ctx.comment("\n\n~~~~~ End of protocol ~~~~~\n")
def run(protocol: protocol_api.ProtocolContext): # which steps should be run? AddBeads = True AddBindingBuffer = True CaptureBinding = True Wash1 = True Wash2 = True Wash2repeat = True Dry = True Elute = True # set tweakable variables elution_volume = 100 # ul of water to add to final beads elution_to_plate = 70 # ul to transfer to final elution plate capture_depth = -3 # depth below ideal bottom of plate to remove supernatants, this may be required as a function of a poor calibration or labware def capture_min = 2 # number of minutes to capture beads on magnets cols_to_extract = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] # which columns should be extracted? nmix = 5 # number of times to pipette to mix drying_min = 20 # number of minutes to evaporate residual ethanol !!!! SET TO 20 trash_speed = 1 # the relative speed to discard of liquids into trash, this is an integer multiplier of normal speed, set higher to clear bubbles on outside of tip # define deck layout MagModule = protocol.load_module('magnetic module gen2', 10) BindingPlate = MagModule.load_labware('nest_96_wellplate_2ml_deep') # use the ElutionPlate = protocol.load_labware('biorad_96_wellplate_200ul_pcr', '3') # an empty biorad 96 well plate BeadsAndWater = protocol.load_labware('usascientific_12_reservoir_22ml', '2') # magbeads in A1 (4 mL), water in A2 (10mL), the end columns will be used for waste BindingBuffer = protocol.load_labware('agilent_1_reservoir_290ml', '7') # reservoir with 70 mL binding buffer MagWash1 = protocol.load_labware('agilent_1_reservoir_290ml', '4') # reservoir with 100 mL magwash1 MagWash2 = protocol.load_labware('agilent_1_reservoir_290ml', '1') # reservoir with 200 mL magwash2 tips_binding = protocol.load_labware('opentrons_96_filtertiprack_200ul', '11') tips_wash1 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '8') tips_wash2 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '5') tips_wash2_repeat = protocol.load_labware('opentrons_96_filtertiprack_200ul', '9') tips_elution = protocol.load_labware('opentrons_96_filtertiprack_200ul', '6') fixed_trash = protocol.fixed_trash['A1'] set_rail_lights = True # define pipettes multichannel = protocol.load_instrument('p300_multi_gen2', 'left', tip_racks=[tips_binding, tips_wash1, tips_wash2, tips_wash2_repeat, tips_elution]) #P1000 = protocol.load_instrument('p1000_single_gen2', 'right', tip_racks=[tips_binding, tips_wash1, tips_wash2, tips_wash2rep]) ### Prerun setup ######################################## MagModule.disengage() ### MAG BINDING ###################################### if AddBindingBuffer: protocol.comment('--------->Adding 600 ul binding buffer') multichannel.pick_up_tip(tips_binding['A1']) for i in cols_to_extract: multichannel.aspirate(200, BindingBuffer['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, BindingBuffer['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, BindingBuffer['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.return_tip() if AddBeads: protocol.comment('--------->Adding 25ul mag beads') multichannel.pick_up_tip(tips_binding['A2']) multichannel.mix(10, 100, BeadsAndWater['A1'].bottom(3)) # mix beads 20 x by pulling up 100ul for i in cols_to_extract: multichannel.aspirate(25, BeadsAndWater['A1']) multichannel.dispense(25, BindingPlate['A'+str(i)].top(4) multichannel.return_tip() for i in cols_to_extract: multichannel.pick_up_tip(tips_binding['A'+str(i)]) multichannel.mix(10, 200, BeadsAndWater['A1'].bottom(3)) multichannel.return_tip() if CaptureBinding: protocol.comment('--------->Removing Binding Buffer') MagModule.engage() protocol.delay(minutes=capture_min) for i in cols_to_extract: multichannel.pick_up_tip(tips_binding['A'+str(i)]) multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.touch_tip() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.touch_tip() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.return_tip() if Wash1: protocol.comment('--------->Doing Wash 1') MagModule.disengage() multichannel.pick_up_tip(tips_wash1['A1']) for i in cols_to_extract: multichannel.aspirate(200, MagWash1['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash1['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash1['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash1['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.return_tip() for i in cols_to_extract: multichannel.pick_up_tip(tips_wash1['A'+str(i)]) multichannel.aspirate(100, MagWash1['A1']) multichannel.dispense(100, BindingPlate['A'+str(i)]) multichannel.mix(nmix, 180, BindingPlate['A'+str(i)].bottom(2)) multichannel.blow_out() multichannel.return_tip() MagModule.engage() protocol.delay(minutes=capture_min) for i in cols_to_extract: multichannel.pick_up_tip(tips_wash1['A'+str(i)]) multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2) multichannel.dispense(200, fixed_trash, rate = trash_speed) multichannel.blow_out() multichannel.return_tip() if Wash2: protocol.comment('--------->Doing Wash 2') MagModule.disengage() multichannel.pick_up_tip(tips_wash2['A1']) for i in cols_to_extract: multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.return_tip() for i in cols_to_extract: multichannel.pick_up_tip(tips_wash2['A'+str(i)]) multichannel.aspirate(100, MagWash2['A1']) multichannel.dispense(100, BindingPlate['A'+str(i)]) multichannel.mix(nmix, 180, BindingPlate['A'+str(i)].bottom(2)) multichannel.blow_out() multichannel.return_tip() MagModule.engage() protocol.delay(minutes=capture_min) for i in cols_to_extract: multichannel.pick_up_tip(tips_wash2['A'+str(i)]) multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) # is this a calibration issue? multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.return_tip() if Wash2repeat: protocol.comment('--------->Repeating Wash 2') MagModule.disengage() multichannel.pick_up_tip(tips_wash2_repeat['A1']) for i in cols_to_extract: multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.aspirate(200, MagWash2['A1']) multichannel.dispense(200, BindingPlate['A'+str(i)].top(4)) #multichannel.blow_out() multichannel.return_tip() for i in cols_to_extract: multichannel.pick_up_tip(tips_wash2_repeat['A'+str(i)]) multichannel.aspirate(100, MagWash2['A1']) multichannel.dispense(100, BindingPlate['A'+str(i)]) multichannel.mix(nmix, 180, BindingPlate['A'+str(i)].bottom(5)) multichannel.blow_out() multichannel.return_tip() MagModule.engage() protocol.delay(minutes=capture_min) for i in cols_to_extract: multichannel.pick_up_tip(tips_wash2_repeat['A'+str(i)]) multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)]) # is this a calibration issue? multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2) multichannel.dispense(200, fixed_trash, rate = trash_speed) #multichannel.blow_out() multichannel.return_tip() if Dry: protocol.comment('--------->Drying DNA') protocol.delay(minutes=drying_min) if Elute: protocol.comment('--------->Eluting DNA') MagModule.disengage() for i in cols_to_extract: multichannel.pick_up_tip(tips_elution['A'+str(i)]) multichannel.aspirate(elution_volume, BeadsAndWater['A2']) multichannel.dispense(elution_volume, BindingPlate['A'+str(i)]) multichannel.mix(nmix, elution_to_plate, BindingPlate['A'+str(i)].bottom(capture_depth)) #multichannel.blow_out() multichannel.return_tip() MagModule.engage() protocol.delay(minutes=capture_min) for i in cols_to_extract: multichannel.pick_up_tip(tips_elution['A'+str(i)]) multichannel.aspirate(elution_to_plate, BindingPlate['A'+str(i)].bottom(capture_depth+1), rate=0.2) multichannel.dispense(elution_to_plate, ElutionPlate['A'+str(i)]) #multichannel.blow_out() multichannel.return_tip()
def run(ctx: protocol_api.ProtocolContext): STEP = 0 STEPS = { # Dictionary with STEP activation, description and times 1: { 'Execute': True, 'description': 'Mezclar y dispensar muestras (' + str(VOLUME_SAMPLE) + 'ul)' } } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 #Folder and file_path for log time if not ctx.is_simulating(): folder_path = '/var/lib/jupyter/notebooks/' + run_id if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/StationA_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, delay): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.delay = delay # Reagents and their characteristics Samples = Reagent(name='Samples', flow_rate_aspirate=25, flow_rate_dispense=100, rinse=False, delay=0) ctx.comment(' ') ctx.comment('###############################################') ctx.comment('CONTROLES: ' + str(NUM_CONTROL_SPACES)) ctx.comment('MUESTRAS: ' + str(NUM_REAL_SAMPLES)) ctx.comment('###############################################') ctx.comment(' ') ################## # Custom functions def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip, shakes=0, shake_v_offset=-45): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location=source, vol=vol, rounds=2, blow_out=True, mix_height=0, x_offset=x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x=x_offset[0])) pipet.aspirate(vol, s, rate=reagent.flow_rate_aspirate) # aspirate liquid if shakes > 0: shake_pipet(pipet, v_offset=shake_v_offset) if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z=-2), rate=reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z=disp_height).move(Point(x=x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate=reagent.flow_rate_dispense) # dispense all ctx.delay( seconds=reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z=disp_height)) if touch_tip == True: pipet.touch_tip(speed=20, v_offset=-10) if air_gap_vol != 0: pipet.air_gap(air_gap_vol, height=disp_height) #air gap def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height=5): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height <= 0: mix_height = 3 pipet.aspirate(1, location=location.bottom(z=source_height).move( Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom(z=source_height).move( Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) pipet.dispense(vol, location=location.bottom(z=mix_height).move( Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) pipet.dispense(1, location=location.bottom(z=mix_height).move( Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out def generate_source_table(source): ''' Concatenate the wells frome the different origin racks ''' num_cols = math.ceil(num_samples / 8) s = [] for i in range(num_cols): if i < 6: s += source[0].columns()[i] + source[1].columns()[i] else: s += source[2].columns()[i - 6] + source[3].columns()[i - 6] return s def shake_pipet(pipet, rounds=2, speed=100, v_offset=0): ctx.comment("Shaking " + str(rounds) + " rounds.") for i in range(rounds): pipet.touch_tip(speed=speed, radius=0.1, v_offset=v_offset) ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up(pip): nonlocal tip_track if not ctx.is_simulating(): if tip_track['counts'][pip] == tip_track['maxes'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_track['counts'][pip] = 0 pip.pick_up_tip() def run_quiet_process(command): subprocess.check_output('{} &> /dev/null'.format(command), shell=True) def play_sound(filename): print('Speaker') print('Next\t--> CTRL-C') try: run_quiet_process('mpg123 {}'.format(path_sounds + filename + '.mp3')) run_quiet_process('mpg123 {}'.format(path_sounds + sonido_defecto)) run_quiet_process('mpg123 {}'.format(path_sounds + filename + '.mp3')) except KeyboardInterrupt: pass print() def start_run(): ctx.comment(' ') ctx.comment('###############################################') ctx.comment('Empezando protocolo') if PHOTOSENSITIVE == False: ctx._hw_manager.hardware.set_lights(button=True, rails=True) else: ctx._hw_manager.hardware.set_lights(button=True, rails=False) now = datetime.now() # dd/mm/YY H:M:S start_time = now.strftime("%Y/%m/%d %H:%M:%S") return start_time def finish_run(switch_off_lights=False): ctx.comment('###############################################') ctx.comment('Protocolo finalizado') ctx.comment(' ') #Set light color to blue ctx._hw_manager.hardware.set_lights(button=True, rails=False) now = datetime.now() # dd/mm/YY H:M:S finish_time = now.strftime("%Y/%m/%d %H:%M:%S") if PHOTOSENSITIVE == False: for i in range(10): ctx._hw_manager.hardware.set_lights(button=False, rails=False) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(button=True, rails=True) time.sleep(0.3) else: for i in range(10): ctx._hw_manager.hardware.set_lights(button=False, rails=False) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(button=True, rails=False) time.sleep(0.3) if switch_off_lights: ctx._hw_manager.hardware.set_lights(button=True, rails=False) used_tips = tip_track['num_refills'][p1000] * 96 * len( p1000.tip_racks) + tip_track['counts'][p1000] ctx.comment('Puntas de 1000 ul utilizadas: ' + str(used_tips) + ' (' + str(round(used_tips / 96, 2)) + ' caja(s))') ctx.comment('###############################################') if not ctx.is_simulating(): for i in range(SOUND_NUM_PLAYS): if i > 0: time.sleep(60) play_sound('finished_process_esp') return finish_time #################################### # load labware and modules #################################### # Load Sample racks if num_samples <= 48: rack_num = 2 ctx.comment('Used source racks are ' + str(rack_num)) else: rack_num = 4 #source_racks = [ctx.load_labware( # 'pcr_24_wellplate_13200ul', slot, # 'source tuberack with snapcap' + str(i + 1)) for i, slot in enumerate(['4', '1', '5', '2'][:rack_num]) #] source_racks = [ ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_1', '4', 'samples grid 1'), ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_2', '1', 'samples grid 2'), ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_3', '5', 'samples grid 3'), ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_4', '2', 'samples grid 4') ] ################################## # Destination plate dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '6', 'NEST 96 Deepwell Plate 2mL') #################################### # Load tip_racks tips1000 = [ ctx.load_labware( 'opentrons_96_filtertiprack_1000ul' if OPENTRONS_TIPS else 'geb_96_tiprack_1000ul', slot, '1000µl filter tiprack') for slot in ['8'] ] ################################################################################ # setup samples and destinations sample_sources_full = generate_source_table(source_racks) sample_sources = sample_sources_full[NUM_CONTROL_SPACES:num_samples] destinations = dest_plate.wells()[NUM_CONTROL_SPACES:num_samples] p1000 = ctx.load_instrument('p1000_single_gen2', 'left', tip_racks=tips1000) # load P1000 pipette # used tip counter and set maximum tips available tip_track = { 'counts': { p1000: 0 }, 'maxes': { p1000: 96 * len(p1000.tip_racks) }, #96 tips per tiprack * number or tipracks in the layout 'num_refills': { p1000: 0 }, 'tips': { p1000: [tip for rack in tips1000 for tip in rack.rows()[0]] } } start_run() ############################################################################ # STEP 1: MIX AND MOVE SAMPLES ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') start = datetime.now() for s, d in zip(sample_sources, destinations): if not p1000.hw_pipette['has_tip']: pick_up(p1000) # Mix the sample BEFORE dispensing if NUM_MIXES > 0: custom_mix(p1000, reagent=Samples, location=s, vol=volume_mix, rounds=NUM_MIXES, blow_out=True, mix_height=5, x_offset=x_offset) move_vol_multichannel(p1000, reagent=Samples, source=s, dest=d, vol=VOLUME_SAMPLE, air_gap_vol=air_gap_vol_sample, x_offset=x_offset, pickup_height=8, rinse=Samples.rinse, disp_height=-10, blow_out=True, touch_tip=False, shakes=SAMPLE_SAKES) p1000.drop_tip(home_after=False) tip_track['counts'][p1000] += 1 # Time statistics end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write( 'STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program # from opentrons.drivers.rpi_drivers import gpio finish_run(switch_off_lights)
def run(protocol: protocol_api.ProtocolContext): """ Aliquoting mastermix from a 5 mL tube to a 96 wells plate; using volume tracking so that the pipette starts aspirating at the starting height of the liquid and goes down as the volume decreases. Adding primers from PCR strips (with 10 uM primer F&R primer mix) the 96 wells plate. """ # ============================================================================= # ======================LOADING LABWARE AND PIPETTES=========================== # ============================================================================= ## For available labware see "labware/list_of_available_labware". ## # Pipette tips tips_200 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 2, #deck position '200tips') #custom name tips_20_1 = protocol.load_labware( 'opentrons_96_filtertiprack_20ul', #labware definition 7, #deck position '20tips_1') #custom name tips_20_2 = protocol.load_labware( 'opentrons_96_filtertiprack_20ul', #labware definition 10, #deck position '20tips_2') #custom name # Tube_racks & plates plate_96 = protocol.load_labware( 'biorad_96_wellplate_200ul_pcr', #labware definition 6, #deck position 'plate_96') #custom name ##### !!! OPTION 1: ROBOT mastermix_tube = protocol.load_labware( 'eppendorfscrewcap_15_tuberack_5000ul', #labware def 3, #deck position 'mastermix_tube') #custom name primer_strips_1 = protocol.load_labware( 'pcrstrips_96_wellplate_200ul', #labware definition 4, #deck position 'primer_strips_1') #custom name primer_strips_2 = protocol.load_labware( 'pcrstrips_96_wellplate_200ul', #labware definition 1, #deck position 'primer_strips_2') #custom name ##### !!! OPTION 2: SIMULATOR # with open("labware/eppendorfscrewcap_15_tuberack_5000ul/" # "eppendorfscrewcap_15_tuberack_5000ul.json") as labware_file: # labware_def_5mL = json.load(labware_file) # mastermix_tube = protocol.load_labware_from_definition( # labware_def_5mL, #variable derived from opening json # 3, #deck position # 'mastermix_tube') #custom name # with open("labware/pcrstrips_96_wellplate_200ul/" # "pcrstrips_96_wellplate_200ul.json") as labware_file: # labware_def_pcrstrips = json.load(labware_file) # primer_strips_1 = protocol.load_labware_from_definition( # labware_def_pcrstrips, #variable derived from opening json # 4, #deck position # 'primer_strips_1') #custom name # primer_strips_2 = protocol.load_labware_from_definition( # labware_def_pcrstrips, #variable derived from opening json # 1, #deck position # 'primer_strips_2') #custom name # Pipettes p300 = protocol.load_instrument( 'p300_single_gen2', #instrument definition 'right', #mount position tip_racks=[tips_200]) #assigned tiprack p20 = protocol.load_instrument( 'p20_single_gen2', #instrument definition 'left', #mount position tip_racks=[tips_20_1, tips_20_2]) #assigned tiprack # ============================================================================= # ==========================VARIABLES TO SET#!!!=============================== # ============================================================================= start_vol = 2520 ## The start_vol is the volume (ul) that is in the source labware at ## ## the start of the protocol. ## dispension_vol = 42 ## The dispension_vol is the volume (ul) that needs to be aliquoted ## ## into the destination wells/tubes. ## primer_vol = 3 ## The primer_vol is the volume (ul) of primer added to the PCR ## ## reaction. ## p300.starting_tip = tips_200.well('F2') p20.starting_tip = tips_20_1.well('H8') ## The starting_tip is the location of first pipette tip in the box ## container = 'tube_5mL' ## The container variable is needed for the volume tracking module. ## ## It tells the module which dimensions to use for the calculations ## ## of the pipette height. It is the source labware from which liquid ## ## is aliquoted. ## ## There are several options to choose from: ## ## 'tube_1.5ml', 'tube_2mL', 'tube_5mL', 'tube_15mL', 'tube_50mL' ## mastermix_source = mastermix_tube['C1'] std_dilution_primer = primer_strips_2['B11'] # Mastermix destination wells================================================== mastermix = [] ## Create an empty list to append wells to ## mastermix_columns = ( [plate_96.columns_by_name()[column_name] for column_name in ['1', '2', '3', '4', '5']] ) ## Make a list of columns, this is a list of lists! ## for column in mastermix_columns: for well in column: mastermix.append(well) ## Separate the columns into wells and append them to list ## mastermix_wells = ( [plate_96.wells_by_name()[well_name] for well_name in ['A6', 'B6', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'A12', 'B12', 'C12', 'D12', 'E12', 'F12']] ) ## Make a list of separate wells ## for well in mastermix_wells: mastermix.append(well) ## Append the wells to the list ## # Primer source tubes========================================================== primers = [] ## Create an empty list to append wells to ## primer_columns = ( [primer_strips_1.columns_by_name()[column_name] for column_name in ['2', '7', '11']] + [primer_strips_2.columns_by_name()[column_name] for column_name in ['2', '7']] ) ## Make a list of columns, this is a list of lists! ## for column in primer_columns: for well in column: primers.append(well) ## Separate the columns into wells and append them to list ## primer_wells = ( [primer_strips_2.wells_by_name()[well_name] for well_name in ['A11', 'B11']] ) ## Make a list of separate wells ## for well in primer_wells: primers.append(well) ## Append the wells to the list ## # Primer destinations - standard dilution series=============================== std_primer_dests = [] ## Create an empty list to append the destination wells to. ## std_wells = ( [plate_96.wells_by_name()[well_name] for well_name in ['A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'A12', 'B12', 'C12', 'D12', 'E12', 'F12']] ) ## make a list of the primer wells ## for well in std_wells: std_primer_dests.append(well) # ============================================================================= # ==========================PREDIFINED VARIABLES=============================== # ============================================================================= aspiration_vol = dispension_vol + (dispension_vol/100*2) ## The aspiration_vol is the volume (ul) that is aspirated from the ## ## container. ## ##### Variables for volume tracking start_height = vt.cal_start_height(container, start_vol) ## Call start height calculation function from volume tracking module.## current_height = start_height ## Set the current height to start height at the beginning of the ## ## protocol. ## # ============================================================================= # ===============================ALIQUOTING MIX================================ # ============================================================================= ## For each column in destination_wells, pick up a tip, than for each ## ## well in these columns pipette mix, and after the+ column drop the tip## ## Repeat untill all columns in the list are done. ## for i, well in enumerate(mastermix): ## Name all the wells in the plate 'well', for all these do: ## if i == 0: p300.pick_up_tip() ## If we are at the first well, start by picking up a tip. ## elif i % 8 == 0: p300.drop_tip() p300.pick_up_tip() ## Then, after every 8th well, drop tip and pick up a new one. ## current_height, pip_height, bottom_reached = vt.volume_tracking( container, dispension_vol, current_height) ## The volume_tracking function needs the arguments container, ## ## dispension_vol, and the current_height which we have set in ## ## this protocol. With those variables, the function updates ## ## the current_height, the pip_height and calculates the ## ## delta_height of the liquid after the next aspiration step. ## if bottom_reached: aspiration_location = mastermix_source.bottom(z=1) #!!! protocol.comment("You've reached the bottom!") else: aspiration_location = mastermix_source.bottom(pip_height) #!!! ## If the level of the liquid in the next run of the loop will ## ## be smaller than 1 we have reached the bottom of the tube. ## ## To prevent the pipette from crashing into the bottom, we ## ## tell it to go home and pause the protocol so that this can ## ## never happen. Set the location of where to aspirate from. ## ## Because we put this in the loop, the location will change ## ## to the newly calculated height after each pipetting step. ## p300.aspirate(aspiration_vol, aspiration_location) ## Aspirate the amount specified in aspiration_vol from the ## ## location specified in aspiration_location. ## p300.dispense(dispension_vol, well) ## Dispense the amount specified in dispension_vol to the ## ## location specified in well (so a new well every time the ## ## loop restarts) ## p300.dispense(10, aspiration_location) ## Alternative for blow-out, make sure the tip doesn't fill ## ## completely when using a disposal volume by dispensing some ## ## of the volume after each pipetting step. (blow-out to many ## ## bubbles) ## p300.drop_tip() # ============================================================================= # =========================ADDING PRIMERS TO SAMPLES=========================== # ============================================================================= ## For the columns in both the source (primers) and the destination ## ## (mix): loop trough the wells in those columns. # for primer_tube, mix_tube in zip(primers, mastermix): p20.pick_up_tip() p20.aspirate(primer_vol, primer_tube) p20.dispense(primer_vol, mix_tube) primer_mix_vol = primer_vol + 3 ## primer_mix_vol = volume for pipetting up and down ## p20.mix(3, primer_mix_vol, mix_tube) p20.dispense(10, mix_tube) p20.drop_tip() # ============================================================================= # ===================ADDING PRIMERS TO STD DILUTION SERIES===================== # ============================================================================= ## For the columns in both the source (primers) and the destination ## ## (mix): loop trough the wells in those columns. # for well in std_primer_dests: p20.pick_up_tip() p20.aspirate(primer_vol, std_dilution_primer) p20.dispense(primer_vol, well) primer_mix_vol = primer_vol + 3 ## primer_mix_vol = volume for pipetting up and down ## p20.mix(3, primer_mix_vol, well) p20.dispense(10, well) p20.drop_tip()
def run(ctx: protocol_api.ProtocolContext): from opentrons.drivers.rpi_drivers import gpio ctx.comment('Actual used columns: ' + str(num_cols)) # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: { 'Execute': True, 'description': 'Add MS2' }, 2: { 'Execute': True, 'description': 'Transfer beads' } } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 folder_path = '/var/lib/jupyter/notebooks' if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/Station_KB_sample_prep_viral_path2_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo, tip_recycling='none'): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.reagent_reservoir_volume = reagent_reservoir_volume self.delay = delay self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.unused = [] self.tip_recycling = tip_recycling self.vol_well_original = reagent_reservoir_volume / num_wells # Reagents and their characteristics Sample = Reagent(name='Sample', flow_rate_aspirate=1, flow_rate_dispense=1, rinse=True, delay=0, reagent_reservoir_volume=460 * 96, num_wells=96, h_cono=1.95, v_fondo=35) Beads = Reagent(name='Magnetic beads and binding solution', flow_rate_aspirate=0.75, flow_rate_dispense=0.75, rinse=True, num_wells=math.ceil(NUM_SAMPLES / 32), delay=2, reagent_reservoir_volume=550 * 8 * num_cols * 1.1, h_cono=1.95, v_fondo=695) # Prismatic MS = Reagent( name='MS2', flow_rate_aspirate=1, flow_rate_dispense=1, rinse=False, reagent_reservoir_volume=total_MS_volume, num_wells=8, delay=0, h_cono=h_cone, v_fondo=volume_cone # V cono ) # Prismatic) Sample.vol_well = Sample.reagent_reservoir_volume Beads.vol_well = Beads.vol_well_original MS.vol_well = MS.reagent_reservoir_volume def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location=source, vol=vol, rounds=2, blow_out=True, mix_height=0, x_offset=x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x=x_offset[0])) pipet.aspirate(vol, s, rate=reagent.flow_rate_aspirate) # aspirate liquid if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z=-2), rate=reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z=disp_height).move(Point(x=x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate=reagent.flow_rate_dispense) # dispense all ctx.delay( seconds=reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z=-2)) if touch_tip == True: pipet.touch_tip(speed=20, v_offset=-5) def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height=3): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height == 0: mix_height = 3 pipet.aspirate(1, location=location.bottom(z=source_height).move( Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom(z=source_height).move( Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) pipet.dispense(vol, location=location.bottom(z=mix_height).move( Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) pipet.dispense(1, location=location.bottom(z=mix_height).move( Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out def calc_height(reagent, cross_section_area, aspirate_volume, min_height=0.5, extra_volume=50): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if reagent.vol_well < aspirate_volume + extra_volume: reagent.unused.append(reagent.vol_well) ctx.comment('Next column should be picked') ctx.comment('Previous to change: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment(str('After change: ' + str(reagent.col))) reagent.vol_well = reagent.vol_well_original ctx.comment('New volume:' + str(reagent.vol_well)) height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Remaining volume:' + str(reagent.vol_well)) if height < min_height: height = min_height col_change = True else: height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Calculated height is ' + str(height)) if height < min_height: height = min_height ctx.comment('Used height is ' + str(height)) col_change = False return height, col_change def divide_destinations(l, n): # Divide the list of destinations in size n lists. for i in range(0, len(l), n): yield l[i:i + n] #################################### # load labware and modules # 12 well rack reagent_res = ctx.load_labware('perkinelmer_12_reservoir_21000ul', '2', 'Reagent deepwell plate') ################################## # Sample prep plate - final plate, goes to Kingfisher sample_plate = ctx.load_labware('kf_96_wellplate_2400ul', '1', 'KF 96 Well 2400ul elution plate') ############################################ # tempdeck tempdeck = ctx.load_module('tempdeck', '4') tempdeck.set_temperature(temperature) ################################## # MS plate - plate with a column containing the internal control MS ms_plate = tempdeck.load_labware('vwr_96_wellplate_200ul_alum_opentrons', 'pcr plate with MS control') #################################### # load labware and modules # 24 well rack aluminium opentrons #tuberack = ctx.load_labware( # 'opentrons_24_aluminumblock_generic_2ml_screwcap', '3', # 'Bloque Aluminio opentrons 24 Screwcaps') ################################## # Load Tipracks tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['6'] ] tips200 = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', slot) for slot in ['3'] ] # pipettes. P1000 currently deactivated m300 = ctx.load_instrument('p300_multi_gen2', 'right', tip_racks=tips200) # Load multi pipette m20 = ctx.load_instrument('p20_multi_gen2', 'left', tip_racks=tips20) #p20 = ctx.load_instrument( # 'p20_single_gen2', 'left', tip_racks=tips20) # load P1000 pipette tip_track = { 'counts': { m300: 0, m20: 0 }, 'maxes': { m300: len(tips200) * 96, m20: len(tips20) * 96 } } # Divide destination wells in small groups for P300 pipette # Declare which reagents are in each reservoir as well as deepwell and elution plate #destinations = list(divide_destinations(sample_plate.wells()[:NUM_SAMPLES], size_transfer)) Beads.reagent_reservoir = reagent_res.rows( )[0][:Beads.num_wells] # 1 row, 4 columns (first ones) work_destinations = sample_plate.wells()[:NUM_SAMPLES] work_destinations_cols = sample_plate.rows()[0][:num_cols] ms_origins = ms_plate.rows()[0][0] # 1 row, 1 columns ############################################################################ # STEP 1: Transfer MS ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('ms_wells') #Loop over defined wells for d in work_destinations_cols: m20.pick_up_tip() #Source samples move_vol_multichannel(m20, reagent=MS, source=ms_origins, dest=d, vol=MS_vol, air_gap_vol=air_gap_vol_MS, x_offset=x_offset, pickup_height=0.2, disp_height=-35, rinse=False, blow_out=True, touch_tip=True) m20.drop_tip() tip_track['counts'][m20] += 8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write( 'STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # STEP 2: TRANSFER BEADS ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: # Transfer parameters start = datetime.now() ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') beads_transfer_vol = [150, 150, 150, 100] # 4 rounds of different volumes rinse = True for i in range(num_cols): if not m300.hw_pipette['has_tip']: m300.pick_up_tip() for j, transfer_vol in enumerate(beads_transfer_vol): # Calculate pickup_height based on remaining volume and shape of container [pickup_height, change_col] = calc_height(Beads, multi_well_rack_area, transfer_vol * 8, min_height=1) if change_col == True: # If we switch column because there is not enough volume left in current reservoir column we mix new column ctx.comment('Mixing new reservoir column: ' + str(Beads.col)) custom_mix(m300, Beads, Beads.reagent_reservoir[Beads.col], vol=170, rounds=10, blow_out=False, mix_height=0, x_offset=x_offset) ctx.comment('Aspirate from reservoir column: ' + str(Beads.col)) ctx.comment('Pickup height is ' + str(pickup_height)) if j != 0: rinse = False move_vol_multichannel( m300, reagent=Beads, source=Beads.reagent_reservoir[Beads.col], dest=work_destinations_cols[i], vol=transfer_vol, air_gap_vol=air_gap_vol, x_offset=x_offset, pickup_height=pickup_height, disp_height=-2, rinse=rinse, blow_out=False, touch_tip=False) m300.drop_tip(home_after=False) tip_track['counts'][m300] += 8 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write( 'STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program gpio.set_rail_lights(False) time.sleep(2) #os.system('mpg123 -f -8000 /var/lib/jupyter/notebooks/toreador.mp3 &') for i in range(3): gpio.set_rail_lights(False) gpio.set_button_light(1, 0, 0) time.sleep(0.3) gpio.set_rail_lights(True) gpio.set_button_light(0, 0, 1) time.sleep(0.3) gpio.set_button_light(0, 1, 0) ctx.comment('Finished! \nMove plate to KingFisher')
def run(protocol: protocol_api.ProtocolContext): ###########################LABWARE SETUP################################# tiprack_300 = protocol.load_labware('opentrons_96_tiprack_300ul', labwarePositions.tiprack_300, 'tiprack 300ul') pipette_300 = protocol.load_instrument( 'p300_single_gen2' if pipette_300_GEN == 'GEN2' else 'p300_single', pipette_300_location, tip_racks=[tiprack_300]) pipette_300.flow_rate.dispense = default_flow_rate pipette_300.flow_rate.aspirate = default_flow_rate pipette_300.starting_tip = tiprack_300.well( tiprack_starting_pos['tiprack_300']) par2 = protocol.load_labware(par2_type, labwarePositions.par2, 'PAR2') trough12 = protocol.load_labware('parhelia_12trough', labwarePositions.buffers_plate, '12-trough buffers reservoir') black_96 = protocol.load_labware('parhelia_black_96', labwarePositions.antibodies_plate, '96-well-plate') buffer_wells = trough12.wells_by_name() buffers = Object() buffers.Hydration_PFA_1pt6pct = buffer_wells['A1'] buffers.Staining = buffer_wells['A2'] buffers.Storage_PFA_4pct = buffer_wells['A3'] buffers.MeOH = buffer_wells['A4'] buffers.PBS = buffer_wells['A5'] buffers.CODEX_buffer_1x = buffer_wells['A6'] buffers.Screening_Buffer = buffer_wells['A7'] buffers.Stripping_buffer = buffer_wells['A8'] buffers.storage = buffer_wells['A9'] preblock_wells = black_96.rows()[0] antibody_wells = black_96.rows()[1] reagent_F_wells = black_96.rows()[2] rendering_wells = black_96.rows()[3] sample_chambers = [] for well in wellslist: sample_chambers.append(par2.wells_by_name()[well]) #################PROTOCOL#################### protocol.comment("Starting the CODEX staining protocol for samples:" + str(sample_chambers)) if not FFPE: #WASHING SAMPLES WITH PFA protocol.comment("first fix") washSamples(pipette_300, buffers.Hydration_PFA_1pt6pct, buffers.Hydration_PFA_1pt6pct, 0, 1, extra_bottom_gap) washSamples(pipette_300, buffers.Hydration_PFA_1pt6pct, sample_chambers, wash_volume, 1, extra_bottom_gap) #INCUBATE protocol.delay(minutes=10, msg="first fix incubation") #WASHING SAMPLES WITH S2 protocol.comment("washing in S2") washSamples(pipette_300, buffers.Staining, buffers.Staining, 0, 1, extra_bottom_gap) washSamples(pipette_300, buffers.Staining, sample_chambers, wash_volume, 2, extra_bottom_gap) #WASHING SAMPLES WITH PREBLOCK protocol.comment("preblocking") for i in range(len(wellslist)): washSamples(pipette_300, preblock_wells[i], sample_chambers[i], wash_volume, 1, extra_bottom_gap) #INCUBATE protocol.delay(minutes=15, msg="preblocking incubation") #APPLYING ANTIBODY COCKTAILS TO SAMPLES protocol.comment("applying antibodies") for i in range(len(wellslist)): washSamples(pipette_300, antibody_wells[i], sample_chambers[i], ab_volume, 1, extra_bottom_gap) #INCUBATE protocol.delay(minutes=ab_incubation_time_minutes, msg="staining incubation") for i in range(2): #WASHING SAMPLES WITH Staining buffer protocol.comment("first washing with Staining buffer") washSamples(pipette_300, buffers.Staining, sample_chambers, wash_volume, 2, extra_bottom_gap) #INCUBATE protocol.delay(minutes=5, msg="first incubation in Staining Buffer") #POST STAINING FIXING SAMPLES WITH PFA protocol.comment("second fix") washSamples(pipette_300, buffers.Storage_PFA_4pct, buffers.Storage_PFA_4pct, 0, 1, extra_bottom_gap) washSamples(pipette_300, buffers.Storage_PFA_4pct, sample_chambers, wash_volume, 1, extra_bottom_gap) #INCUBATE protocol.delay(minutes=5, msg="incubation with fixative") #WASHING SAMPLES WITH PBS protocol.comment("PBS wash") washSamples(pipette_300, buffers.PBS, buffers.PBS, 0, 2, extra_bottom_gap) washSamples(pipette_300, buffers.PBS, sample_chambers, wash_volume, 2, extra_bottom_gap) # FIXING SAMPLES WITH Methanol washSamples(pipette_300, buffers.MeOH, buffers.MeOH, 0, 1, extra_bottom_gap) for i in range(2): protocol.comment("applying MeOH") washSamples(pipette_300, buffers.MeOH, sample_chambers, wash_volume, 1, extra_bottom_gap) # INCUBATE protocol.delay(minutes=2.5, msg="First MeOH incubation") #WASHING SAMPLES WITH PBS protocol.comment("PBS wash") washSamples(pipette_300, buffers.PBS, sample_chambers, wash_volume, 2, extra_bottom_gap) #DILUTING AND APPLYING THE FIXATIVE for i in range(len(wellslist)): dilute_and_apply_fixative(pipette_300, reagent_F_wells[i], buffers.PBS, sample_chambers[i], 150) protocol.comment("third fix incubation") protocol.delay(minutes=10, msg="Reagent F incubation") #WASHING SAMPLES WITH PBS protocol.comment("PBS wash") washSamples(pipette_300, buffers.PBS, sample_chambers, wash_volume, 2, extra_bottom_gap) if Antibody_Screening: washSamples(pipette_300, buffers.Stripping_buffer, buffers.Stripping_buffer, 0, 1, extra_bottom_gap) washSamples(pipette_300, buffers.Screening_Buffer, buffers.Screening_Buffer, 0, 1, extra_bottom_gap) #PRE-CLEARING THE TISSUE for i in range(3): protocol.comment("tissue clearing round" + str(i + 1)) washSamples(pipette_300, buffers.Stripping_buffer, sample_chambers, wash_volume, 2, extra_bottom_gap) protocol.delay(seconds=30) washSamples(pipette_300, buffers.Screening_Buffer, sample_chambers, wash_volume, 1, extra_bottom_gap) washSamples(pipette_300, buffers.CODEX_buffer_1x, sample_chambers, wash_volume, 1, extra_bottom_gap) #Equilibration in rendering buffer protocol.comment("Equilibration in rendering buffer") washSamples(pipette_300, buffers.Screening_Buffer, sample_chambers, wash_volume, 1, extra_bottom_gap) #RENDERING protocol.comment("Applying rendering solution to wells") for i in range(len(wellslist)): washSamples(pipette_300, rendering_wells[i], sample_chambers[i], wash_volume, 1, extra_bottom_gap) #INCUBATE protocol.delay(minutes=10, msg="rendering hybridization") #WASH SAMPLES IN 1x CODEX buffer protocol.comment("Washing with rendering buffer") washSamples(pipette_300, buffers.Screening_Buffer, sample_chambers, wash_volume, 2, extra_bottom_gap) #STORAGE, washing samples every hour for 100 hours washSamples(pipette_300, buffers.storage, buffers.storage, 0, 1, extra_bottom_gap) for i in range(10): washSamples(pipette_300, buffers.storage, sample_chambers, wash_volume / 3, 1, extra_bottom_gap, keep_tip=True) protocol.delay(minutes=90, msg="storing samples in storage buffer")
def run(ctx: protocol_api.ProtocolContext): # Init protocol run run = ProtocolRun(ctx) run.comment("You are about to run %s samples\n STEPS:%s" % (NUM_SAMPLES, steps), add_hash=True) run.pause( "Are you sure the set up is correct? Check the desk before continue") # Define stesp run.add_step( description="Transfer PK A6 - To AW_PLATE Single Slot1 -> Slot2") # 1 run.add_step(description="Transfer MS2 B6 - To AW_PLATE Single 1->2") # 4 run.add_step(description="Transfer Beats 3 - 2 Multi and mix") # 3 # execute avaliaible steps run.init_steps(steps) ################################## # Tube rack tube_rack = ctx.load_labware('opentrons_24_tuberack_nest_1.5ml_screwcap', 4) # Destination plate SLOT 2 if (ctx.is_simulating()): aw_slot = ctx.load_labware( 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', 5) else: aw_slot = ctx.load_labware('axygen_96_wellplate_2000ul', 5) aw_wells = aw_slot.wells()[:NUM_SAMPLES] aw_wells_multi = aw_slot.rows()[0][:num_cols] # Magnetic Beads Pool beads_slot = ctx.load_labware('nest_12_reservoir_15ml', 6) beads_wells_multi = beads_slot.rows()[0][:num_cols] # Mount pippets and set racks # Tipracks20_multi tips20 = ctx.load_labware('opentrons_96_tiprack_20ul', 7) tips300_1 = ctx.load_labware('opentrons_96_filtertiprack_200ul', 8) tips300_2 = ctx.load_labware('opentrons_96_filtertiprack_200ul', 9) run.mount_right_pip('p20_single_gen2', tip_racks=[tips20], capacity=20) run.mount_left_pip('p300_multi_gen2', tip_racks=[tips300_1, tips300_2], capacity=200, multi=True) ############################################################################ # STEP 1: Transfer A6 - To AW_PLATE ############################################################################ if (run.next_step()): run.set_pip("right") # single 20 volumen_move = 5 source = tube_rack.wells("A6")[0] liquid = Reagent( name='Proteinasa K', num_wells=1, # change with num samples flow_rate_aspirate=0.75, # Original 0.5 flow_rate_dispense=3, # Original 1 reagent_reservoir_volume=528, h_cono=4, v_fondo=4 * math.pi * 4**3 / 3) run.pick_up() for dest in aw_wells: [pickup_height, col_change] = run.calc_height(liquid, 4.12 * 4.12 * math.pi, volumen_move) run.move_volume(reagent=liquid, source=source, dest=dest, vol=volumen_move, air_gap_vol=1, pickup_height=pickup_height, disp_height=-10, blow_out=True, post_dispense=True, post_dispense_vol=5) run.drop_tip() run.finish_step() ############################################################################ # STEP 2: Transfer B6 MS2 - To AW_PLATE ############################################################################ if (run.next_step()): run.set_pip("right") # single 20 volumen_move = 5 source = tube_rack.wells("B6")[0] liquid = Reagent( name='MS2', num_wells=1, # change with num samples delay=0, flow_rate_aspirate=3, # Original 0.5 flow_rate_dispense=3, # Original 1 flow_rate_aspirate_mix=15, flow_rate_dispense_mix=25, reagent_reservoir_volume=528, h_cono=4, v_fondo=4 * math.pi * 4**3 / 3) run.pick_up() for dest in aw_wells: [pickup_height, col_change] = run.calc_height(liquid, 4.12 * 4.12 * math.pi, volumen_move) run.move_volume(reagent=liquid, source=source, dest=dest, vol=volumen_move, air_gap_vol=1, pickup_height=pickup_height, disp_height=-10, blow_out=True, post_dispense=True, post_dispense_vol=5) run.drop_tip() run.finish_step() ############################################################################ # STEP 3: Slot 3 -2 beats_PK AW ############################################################################ if (run.next_step()): ############################################################################ # Light flash end of program run.set_pip("left") # p300 multi volume = 275 beads = Reagent(name='Magnetic beads', flow_rate_aspirate=0.5, flow_rate_dispense=0.5, flow_rate_dispense_mix=4, flow_rate_aspirate_mix=4, rinse=True, delay=2, reagent_reservoir_volume=30000, num_wells=3, h_cono=1.95, v_fondo=695, rinse_loops=3) air_gap_vol = 5 disposal_height = -5 pickup_height = 1 beads.reagent_reservoir = beads_slot.rows()[0][0:3] pool_area = 8.3 * 71.1 for destination in aw_wells_multi: run.pick_up() vol = 150 vol_min = 1000 [pickup_height, col_change] = run.calc_height(beads, pool_area, vol * 8, extra_volume=vol_min) run.move_volume(reagent=beads, source=beads.reagent_reservoir[beads.col], dest=destination, vol=vol, air_gap_vol=air_gap_vol, pickup_height=pickup_height, disp_height=disposal_height, rinse=True, blow_out=True) run.change_tip() vol = 125 [pickup_height, col_change] = run.calc_height(beads, pool_area, vol * 8, extra_volume=vol_min) run.move_volume(reagent=beads, source=beads.reagent_reservoir[beads.col], dest=destination, vol=vol, air_gap_vol=air_gap_vol, pickup_height=pickup_height, disp_height=disposal_height, rinse=True, blow_out=True) run.custom_mix(beads, location=destination, vol=150, rounds=3, blow_out=True, mix_height=0) run.drop_tip() run.finish_step() run.log_steps_time() run.blink() for c in robot.commands(): ctx.comment(c) ctx.comment('Finished! \nMove plate to PCR')
def run(ctx: protocol_api.ProtocolContext): # Init protocol run run = ProtocolRun(ctx) # Define stesp run.add_step( description="Transfer PK+MS2 A6 - To AW_PLATE Single Slot1 -> Slot2") # 1 run.add_step(description="Transfer Beats 3 - 2 Multi and mix") # 2 # execute avaliaible steps run.init_steps(steps) ################################## # Tube rack tube_rack = ctx.load_labware( 'opentrons_24_tuberack_nest_1.5ml_screwcap', 4) # Destination plate SLOT 2 if(ctx.is_simulating()): aw_slot = ctx.load_labware( 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', 5) else: aw_slot = ctx.load_labware( 'axygen_96_wellplate_2000ul', 5) aw_wells = aw_slot.wells()[:NUM_SAMPLES] # Mount pippets and set racks # Tipracks20_multi tips20_1 = ctx.load_labware('opentrons_96_tiprack_20ul', 7) tips20_2 = ctx.load_labware('opentrons_96_tiprack_20ul', 8) run.mount_right_pip('p20_single_gen2', tip_racks=[tips20_1,tips20_2], capacity=20) tips300 = ctx.load_labware('opentrons_96_filtertiprack_200ul', 7) run.mount_right_pip('p20_single_gen2', tip_racks=[tips20_1,tips20_2], capacity=20) ############################################################################ # STEP 1: Transfer A6 - To AW_PLATE ############################################################################ if (run.next_step()): run.set_pip("right") # single 20 volumen_move = 5 source = tube_rack.wells("A6")[0] pkms2 = Reagent( name='PK + MS2', num_wells=1, # change with num samples flow_rate_aspirate=0.75, # Original 0.5 flow_rate_dispense=3, # Original 1 reagent_reservoir_volume=vol_pkms2*(NUM_SAMPLES+1), h_cono=4, v_fondo=4 * math.pi * 4 ** 3 / 3 ) pkms2.set_positions([tube_rack.wells("A5"),tube_rack.wells("B5"),tube_rack.wells("B6")]) run.comment(pkms2.get_volumes_fill_print(),add_hash=True) run.pick_up() for dest in aw_wells: pickup_height = run.calc_height( liquid, 4.12*4.12*math.pi, vol_pkms2) run.move_volume(reagent=liquid, source=source, dest=pkms2.get_current_position(), vol=vol_pkms2, air_gap_vol=1, pickup_height=pickup_height, disp_height=-10, blow_out=True, post_dispense=True, post_dispense_vol=5) run.drop_tip() run.finish_step() ############################################################################ # STEP 2: Slot 3 -2 beats_PK AW ############################################################################ if (run.next_step()): ############################################################################ # Light flash end of program run.set_pip("left") # p300 multi volume = 275 beads = Reagent(name='Magnetic beads', flow_rate_aspirate=0.5, flow_rate_dispense=0.5, flow_rate_dispense_mix=4, flow_rate_aspirate_mix=4, rinse=True, delay=2, reagent_reservoir_volume=vol_beads*(NUM_SAMPLES+1), h_cono=1.95, v_fondo=695, rinse_loops=3) beads.set_positions([tube_rack.wells("A6"),tube_rack.wells("B6")]) air_gap_vol = 5 disposal_height = -5 for destination in aw_wells: volumes = beads.divide_volume(vol_beads,180) for vol in volumes: run.pick_up() pickup_height = beads.calc_height( beads, pool_area, vol, extra_volume=vol_min) run.move_volume(reagent=beads, source=beads.get_current_position(), dest=destination, vol=vol, air_gap_vol=air_gap_vol, pickup_height=pickup_height, disp_height=disposal_height, rinse=True, blow_out=True) run.custom_mix(beads, location=destination, vol=vol/2, rounds=3, blow_out=True, mix_height=0) run.drop_tip() run.finish_step() run.log_steps_time() run.blink() for c in robot.commands(): ctx.comment(c) ctx.comment('Finished! \nMove plate to PCR')
def run(ctx: protocol_api.ProtocolContext): ctx.comment('Actual used columns: ' + str(num_cols)) # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: { 'Execute': True, 'description': 'Transfer Mmix' }, 2: { 'Execute': True, 'description': 'Transfer samples' }, 3: { 'Execute': True, 'description': 'Transfer negative control' }, 4: { 'Execute': True, 'description': 'Transfer positive control' } } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 #Folder and file_path for log time folder_path = '/var/lib/jupyter/notebooks' + run_id if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/Station_C_Vitro_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo, tip_recycling='none'): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.reagent_reservoir_volume = reagent_reservoir_volume self.delay = delay self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.unused = [] self.tip_recycling = tip_recycling self.vol_well_original = reagent_reservoir_volume / num_wells # Reagents and their characteristics Mmix = Reagent( name='Mmix', rinse=False, flow_rate_aspirate=3, flow_rate_dispense=3, reagent_reservoir_volume=1800, num_wells=1, #change with num samples delay=0, h_cono=h_cone, v_fondo=volume_cone # V cono ) Samples = Reagent( name='Samples', rinse=False, flow_rate_aspirate=1, flow_rate_dispense=1, reagent_reservoir_volume=50, delay=0, num_wells=num_cols, # num_cols comes from available columns h_cono=0, v_fondo=0) Mmix.vol_well = Mmix.vol_well_original Samples.vol_well = Samples.vol_well_original ################## # Custom functions def divide_volume(volume, max_vol): num_transfers = math.ceil(volume / max_vol) vol_roundup = math.ceil(volume / num_transfers) last_vol = volume - vol_roundup * (num_transfers - 1) vol_list = [vol_roundup for v in range(1, num_transfers)] vol_list.append(last_vol) return vol_list def divide_destinations(l, n): # Divide the list of destinations in size n lists. for i in range(0, len(l), n): yield l[i:i + n] def distribute_custom(pipette, volume, src, dest, waste_pool, pickup_height, extra_dispensal, dest_x_offset, disp_height=0): # Custom distribute function that allows for blow_out in different location and adjustement of touch_tip pipette.aspirate((len(dest) * volume) + extra_dispensal, src.bottom(pickup_height)) pipette.touch_tip(speed=20, v_offset=-5) pipette.move_to(src.top(z=5)) pipette.aspirate(5) # air gap for d in dest: pipette.dispense(5, d.top()) drop = d.top(z=disp_height).move(Point(x=dest_x_offset)) pipette.dispense(volume, drop) pipette.move_to(d.top(z=5)) pipette.aspirate(5) # air gap try: pipette.blow_out(waste_pool.wells()[0].bottom(pickup_height + 3)) except: pipette.blow_out(waste_pool.bottom(pickup_height + 3)) return (len(dest) * volume) def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location=source, vol=vol, rounds=2, blow_out=True, mix_height=0, x_offset=x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x=x_offset[0])) pipet.aspirate(vol, s) # aspirate liquid if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z=-2), rate=reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z=disp_height).move(Point(x=x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate=reagent.flow_rate_dispense) # dispense all ctx.delay( seconds=reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z=-2)) if touch_tip == True: pipet.touch_tip(speed=20, v_offset=-5, radius=0.5) def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height=3): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height <= 0: mix_height = 3 pipet.aspirate(1, location=location.bottom(z=source_height).move( Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom(z=source_height).move( Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) pipet.dispense(vol, location=location.bottom(z=mix_height).move( Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) pipet.dispense(1, location=location.bottom(z=mix_height).move( Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out def calc_height(reagent, cross_section_area, aspirate_volume, min_height=0.5): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if reagent.vol_well < aspirate_volume: reagent.unused.append(reagent.vol_well) ctx.comment('Next column should be picked') ctx.comment('Previous to change: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment(str('After change: ' + str(reagent.col))) reagent.vol_well = reagent.vol_well_original ctx.comment('New volume:' + str(reagent.vol_well)) height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Remaining volume:' + str(reagent.vol_well)) if height < min_height: height = min_height col_change = True else: height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Calculated height is ' + str(height)) if height < min_height: height = min_height ctx.comment('Used height is ' + str(height)) col_change = False return height, col_change #################################### # load labware and modules # 24 well rack tuberack = ctx.load_labware( 'opentrons_24_aluminumblock_generic_2ml_screwcap', '2', 'Opentrons 24 Well Aluminum Block with Generic 2 mL Screwcap') ############################################ # tempdecks tempdeck_orig = ctx.load_module('Temperature Module Gen2', '4') tempdeck_dest = ctx.load_module('Temperature Module Gen2', '1') if SET_TEMP_ON_SLOT_4: tempdeck_orig.set_temperature(TEMPERATURE_SLOT_4) if SET_TEMP_ON_SLOT_1: tempdeck_dest.set_temperature(TEMPERATURE_SLOT_1) ################################## # Sample plate - comes from B source_plate = tempdeck_orig.load_labware( 'kingfisher_96_aluminumblock_200ul', 'Kingfisher 96 Aluminum Block 200 uL') samples = source_plate.wells()[:NUM_SAMPLES] ################################## # qPCR plate - final plate, goes to PCR qpcr_plate = tempdeck_dest.load_labware( 'opentrons_96_aluminumblock_nest_wellplate_100ul', 'Opentrons 96 Well Aluminum Block with NEST Well Plate 100 uL') ################################## # Load Tipracks tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['5'] ] tips200 = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', slot) for slot in ['3'] ] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate Mmix.reagent_reservoir = tuberack.rows()[0][0] # A1 # setup up sample sources and destinations samples = source_plate.wells()[2:NUM_SAMPLES] pcr_wells = qpcr_plate.wells()[:NUM_SAMPLES] pcr_wells_samples = qpcr_plate.wells()[2:NUM_SAMPLES] # Divide destination wells in small groups for P300 pipette dests = list(divide_destinations(pcr_wells, size_transfer)) # pipettes p20 = ctx.load_instrument('p20_single_gen2', mount='right', tip_racks=tips20) p300 = ctx.load_instrument('p300_single_gen2', mount='left', tip_racks=tips200) # used tip counter and set maximum tips available tip_track = { 'counts': { p300: 0, p20: 0 }, 'maxes': { p300: 96 * len(p300.tip_racks), p20: 96 * len(p20.tip_racks) } } ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up(pip): nonlocal tip_track if not ctx.is_simulating(): if tip_track['counts'][pip] == tip_track['maxes'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_track['counts'][pip] = 0 if not pip.hw_pipette['has_tip']: pip.pick_up_tip() ########## ############################################################################ # STEP 1: TRANSFER MMIX ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment(' ') ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') ctx.comment(' ') pick_up(p300) used_vol = [] for dest in dests: aspirate_volume = MMIX_VOL_PER_SAMPLE * len(dest) + extra_dispensal used_vol_temp = distribute_custom( p300, volume=MMIX_VOL_PER_SAMPLE, src=Mmix.reagent_reservoir, dest=dest, waste_pool=Mmix.reagent_reservoir, pickup_height=0.2, extra_dispensal=extra_dispensal, dest_x_offset=2, disp_height=-1) used_vol.append(used_vol_temp) p300.drop_tip(home_after=False) tip_track['counts'][p300] += 1 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 2: TRANSFER SAMPLES ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment(' ') ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') ctx.comment(' ') for s, d in zip(samples, pcr_wells_samples): pick_up(p20) move_vol_multichannel(p20, reagent=Samples, source=s, dest=d, vol=VOLUME_SAMPLE, air_gap_vol=air_gap_sample, x_offset=x_offset, pickup_height=0.2, disp_height=0, rinse=False, blow_out=True, touch_tip=True) p20.drop_tip(home_after=False) tip_track['counts'][p20] += 1 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 3: TRANSFER NEGATIVE CONTROL ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment(' ') ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') ctx.comment(' ') pick_up(p20) s = tuberack.rows()[0][1] # A2 d = qpcr_plate.wells()[1] # B1 move_vol_multichannel(p20, reagent=Samples, source=s, dest=d, vol=VOLUME_SAMPLE, air_gap_vol=air_gap_sample, x_offset=x_offset, pickup_height=0.2, disp_height=0, rinse=False, blow_out=True, touch_tip=True) p20.drop_tip(home_after=False) tip_track['counts'][p20] += 1 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 4: TRANSFER POSITIVE CONTROL ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment(' ') ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') ctx.comment(' ') pick_up(p20) s = tuberack.rows()[0][2] # A3 d = qpcr_plate.wells()[0] # A1 move_vol_multichannel(p20, reagent=Samples, source=s, dest=d, vol=VOLUME_SAMPLE, air_gap_vol=air_gap_sample, x_offset=x_offset, pickup_height=0.2, disp_height=0, rinse=False, blow_out=True, touch_tip=True) p20.drop_tip(home_after=False) tip_track['counts'][p20] += 1 end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write( 'STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program for i in range(3): #ctx._hw_manager.hardware.set_lights(rails=False) ctx._hw_manager.hardware.set_lights(button=(1, 0, 0)) time.sleep(0.3) #ctx._hw_manager.hardware.set_lights(rails=True) ctx._hw_manager.hardware.set_lights(button=(0, 0, 1)) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(button=(0, 1, 0)) ctx.comment('Finished! \nMove plate to PCR') total_used_vol = np.sum(used_vol) total_needed_volume = total_used_vol ctx.comment('Total Mmix used volume is: ' + str(total_used_vol) + '\u03BCl.') ctx.comment('Needed Mmix volume is ' + str(total_needed_volume + extra_dispensal * len(dests)) + '\u03BCl') ctx.comment('Mmix remaining in tubes is: ' + format( np.sum(Mmix.unused) + extra_dispensal * len(dests) + Mmix.vol_well) + '\u03BCl.') ctx.comment('200 ul Used tips in total: ' + str(tip_track['counts'][p300])) ctx.comment('200 ul Used racks in total: ' + str(tip_track['counts'][p300] / 96)) ctx.comment('20 ul Used tips in total: ' + str(tip_track['counts'][p20])) ctx.comment('20 ul Used racks in total: ' + str(tip_track['counts'][p20] / 96))
def run(ctx: protocol_api.ProtocolContext): # load labware ic_pk = ctx.load_labware( 'opentrons_24_aluminumblock_nest_2ml_snapcap', '9', 'chilled tubeblock for internal control and proteinase K (strip 1)' ).wells()[0] source_racks = [ ctx.load_labware( 'opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap', slot, 'source tuberack ' + str(i + 1)) for i, slot in enumerate(['1', '2', '3', '4']) ] dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '8', '96-deepwell sample plate') binding_buffer = ctx.load_labware( 'opentrons_6_tuberack_falcon_50ml_conical', '11', '50ml tuberack for binding buffer (tube B1)').wells('B1') # binding_buffer = ctx.load_labware( # 'biorad_96_wellplate_200ul_pcr', '7', # '50ml tuberack for lysis buffer + PK (tube A1)').wells()[:1] tipracks1000 = [ ctx.load_labware('opentrons_96_filtertiprack_1000ul', slot, '1000µl filter tiprack') for slot in ['10', '7'] ] tipracks20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', '6', '20µl filter tiprack') ] # load pipette s20 = ctx.load_instrument('p20_single_gen2', 'left', tip_racks=tipracks20) p1000 = ctx.load_instrument('p1000_single_gen2', 'right', tip_racks=tipracks1000) # setup samples sources = [well for rack in source_racks for well in rack.wells()][:NUM_SAMPLES] dests_single = dest_plate.wells()[:NUM_SAMPLES] num_cols = math.ceil(NUM_SAMPLES / 8) dests_multi = dest_plate.rows()[0][:num_cols] tip_log = {'count': {}} folder_path = '/data/A' tip_file_path = folder_path + '/tip_log.json' if TIP_TRACK and not ctx.is_simulating(): if os.path.isfile(tip_file_path): with open(tip_file_path) as json_file: data = json.load(json_file) if 'tips1000' in data: tip_log['count'][p1000] = data['tips1000'] else: tip_log['count'][p1000] = 0 if 'tips20' in data: tip_log['count'][s20] = data['tips20'] else: tip_log['count'][s20] = 0 else: tip_log['count'] = {p1000: 0, s20: 0} tip_log['tips'] = { p1000: [tip for rack in tipracks1000 for tip in rack.wells()], #s20: [tip for rack in tipracks20 for tip in rack.rows()[0]] s20: [tip for rack in tipracks20 for tip in rack.wells()] } tip_log['max'] = {pip: len(tip_log['tips'][pip]) for pip in [p1000, s20]} def pick_up(pip): nonlocal tip_log if tip_log['count'][pip] == tip_log['max'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_log['count'][pip] = 0 pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]]) tip_log['count'][pip] += 1 heights = {tube: TUBE50_VOlUME * 1 for tube in binding_buffer} radius = (binding_buffer[0].diameter) / 2 min_h = 5 def h_track(vol, tube): nonlocal heights dh = vol / (math.pi * (radius**2)) if heights[tube] - dh > min_h: heights[tube] = heights[tube] - dh else: heights[tube] = min_h # stop 5mm short of the bottom return heights[tube] p1000.flow_rate.aspirate = 50 p1000.flow_rate.dispense = 60 p1000.flow_rate.blow_out = 100 # transfer internal control + proteinase K pick_up(s20) for d in dests_single: s20.dispense(10, ic_pk.bottom(2)) s20.transfer(ICPK_VOlUME, ic_pk.bottom(2), d.bottom(2), air_gap=5, new_tip='never') s20.air_gap(5) s20.drop_tip() # transfer binding buffer and mix pick_up(p1000) for i, (s, d) in enumerate(zip(sources, dests_single)): source = binding_buffer[ i // 96] # 1 tube of binding buffer can accommodate all samples here h = h_track(275, source) # custom mix p1000.flow_rate.aspirate = 100 p1000.flow_rate.dispense = 100 p1000.dispense(500, source.bottom(h + 20)) for _ in range(4): # p1000.air_gap(500) p1000.aspirate(500, source.bottom(h)) p1000.dispense(500, source.bottom(h + 20)) # p1000.transfer(BB_VOLUME, source.bottom(h), d.bottom(5), air_gap=100, # new_tip='never') p1000.flow_rate.aspirate = 50 p1000.flow_rate.dispense = 100 p1000.aspirate(BB_VOLUME, source.bottom(h)) p1000.air_gap(10) p1000.dispense(BB_VOLUME + 100, d.bottom(10)) p1000.air_gap(10) p1000.drop_tip() ctx.comment('Move deepwell plate (slot 4) to Station B for RNA \ extraction.') # track final used tip if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) data = { 'tips1000': tip_log['count'][p1000], 'tips20': tip_log['count'][s20] } with open(tip_file_path, 'w') as outfile: json.dump(data, outfile)
def run(protocol: protocol_api.ProtocolContext): """ During the qPCR (project_related/Dina/21june_qPCRs_DINA_EVE.py) we take sample, dilute it 100x (pipetting up-and-down 3x with 4.5uL) and then directly transfer the dilution to the PCR plate. With the sample_mix, which was added to the plate 8x I saw a lot of variation in Cq values. I'm under the impression mixing was not sufficient. In this protocol I want to compare some different mixing techniques, to see if that can be improved. """ # ============================================================================= # LOADING LABWARE AND PIPETTES================================================= # ============================================================================= ##### Loading labware tips_200 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 3, #deck position 'tips_200') #custom name tips_20_1 = protocol.load_labware( 'opentrons_96_filtertiprack_20ul', #labware definition 10, #deck position 'tips_20') #custom name tips_20_2 = protocol.load_labware( 'opentrons_96_filtertiprack_20ul', #labware definition 7, #deck position 'tips_20') #custom name plate_96_qPCR = protocol.load_labware( 'biorad_96_wellplate_200ul_pcr', #labware definition 5, #deck position 'plate_96_qPCR') #custom name # ##### !!! FOR ROBOT # sample_strips_1 = protocol.load_labware( # 'pcrstrips_96_wellplate_200ul', #labware definition # 1, #deck position # 'sample_strips_1') #custom name # dilution_strips = protocol.load_labware( # 'pcrstrips_96_wellplate_200ul', #labware definition # 4, #deck position # 'dilution_strips') #custom name # tubes_5mL = protocol.load_labware( # 'eppendorfscrewcap_15_tuberack_5000ul', #labware definition # 6, #deck position # 'tubes_5mL') #custom name #### !!! FOR SIMULATOR with open("labware/pcrstrips_96_wellplate_200ul/" "pcrstrips_96_wellplate_200ul.json") as labware_file: labware_def_pcrstrips = json.load(labware_file) sample_strips_1 = protocol.load_labware_from_definition( labware_def_pcrstrips, #variable derived from opening json 1, 'sample_strips_1') dilution_strips = protocol.load_labware_from_definition( labware_def_pcrstrips, #variable derived from opening json 4, 'dilution_strips') with open("labware/eppendorfscrewcap_15_tuberack_5000ul/" "eppendorfscrewcap_15_tuberack_5000ul.json") as labware_file: labware_def_5mL = json.load(labware_file) tubes_5mL = protocol.load_labware_from_definition( labware_def_5mL, #variable derived from opening json 6, '5mL_tubes') ##### Loading pipettes p300 = protocol.load_instrument( 'p300_single_gen2', #instrument definition 'right', #mount position tip_racks=[tips_200]) #assigned tiprack p20 = protocol.load_instrument( 'p20_single_gen2', #instrument definition 'left', #mount position tip_racks=[tips_20_1, tips_20_2]) #assigned tiprack # ============================================================================= # VARIABLES TO SET#!!!========================================================= # ============================================================================= start_vol_mix = 2178 ## The start_vol_m is the volume (ul) of mix that is in the source ## ## labware at the start of the protocol. ## start_vol_water = 5000 ## The start_vol_w is the volume (ul) of water that is in the source ## ## labware at the start of the protocol. ## ##!!! Fill up to above 5mL line ## dispension_vol_mix = 22 ## The dispension_vol_m is the volume (ul) of mastermix that needs to ## ## be aliquoted into the destination wells/tubes. ## dispension_vol_water = 148.5 ## The dil_vol_w is the volume of water to be pipetted for the ## ## dilution. ## sample_vol_dil = 1.5 ## The dil_vol_s is the volume of sample to be pipetted for the ## ## 100x dilution. ## sample_vol_pcr = 3 ## The sample_vol is the volume (ul) of sample added to the PCR ## ## reaction. ## p300.starting_tip = tips_200.well('A1') p20.starting_tip = tips_20_1.well('A1') ## The starting_tip is the location of first pipette tip in the box ## #### Which wells/tubes are used mastermix = tubes_5mL['C1'] H2O = ([ tubes_5mL.wells_by_name()[well_name] for well_name in ['B1', 'B2', 'B3'] ]) sample = sample_strips_1['A1'] ## where to put mastermix PCR_plate = [] PCR_plate_columns = ([ plate_96_qPCR.columns_by_name()[column_name] for column_name in ['1', '2', '3', '5', '6', '7', '8', '9', '10', '11'] ]) for column in PCR_plate_columns: for well in column: PCR_plate.append(well) for well in ([ plate_96_qPCR.wells_by_name()[well_name] for well_name in ['G12', 'H12'] ]): PCR_plate.append(well) ## where to put water for dilutions dilution_plate = dilution_strips.columns_by_name()['1'] # ============================================================================= # PREDIFINED VARIABLES========================================================= # ============================================================================= container_mix = container_water = 'tube_5mL' ##### Variables for volume tracking start_height_mix = vt.cal_start_height(container_mix, start_vol_mix) start_height_water = vt.cal_start_height(container_water, start_vol_water) # ============================================================================= # ALIQUOTING DILUTION WATER AND MASTERMIX====================================== # ============================================================================= aliquots = ['water', 'PCR_mix'] ## what will be aliquoted in this protocol for aliquot in aliquots: if aliquot == 'PCR_mix': source = mastermix destination = PCR_plate current_height = start_height_mix container = container_mix dispension_vol = dispension_vol_mix elif aliquot == 'water': counter = 0 # how many tubes emptied source = H2O[counter] destination = dilution_plate current_height = start_height_water container = container_water dispension_vol = dispension_vol_water for i, well in enumerate(destination): ## aliquot mix in entire qPCR plate, for each well do the following: aspiration_vol = dispension_vol + (dispension_vol / 100 * 2) ## Set correct variables for volume_tracking if i == 0: p300.pick_up_tip() ## If we are at the first well, start by picking up a tip.## elif i % 8 == 0: p300.drop_tip() p300.pick_up_tip() ## Then, after every 8th well, drop tip and pick up new ## current_height, pip_height, bottom_reached = vt.volume_tracking( container, dispension_vol, current_height) ## call volume_tracking function, obtain current_height, ## ## pip_height and whether bottom_reached. ## if bottom_reached: if aliquot == 'water': ## continue with next tube, reset vt current_height = start_height_water current_height, pip_height, bottom_reached = ( vt.volume_tracking(container, dispension_vol, current_height)) counter = counter + 1 source = H2O[counter] aspiration_location = source.bottom(current_height) protocol.comment("Continue with next tube of water") elif aliquot == 'PCR_mix': aspiration_location = source.bottom(z=1) protocol.comment("You've reached the bottom of the tube!") else: aspiration_location = source.bottom(pip_height) ## Set the location of where to aspirate from. ## #### The actual aliquoting of mastermix p300.aspirate(aspiration_vol, aspiration_location) ## Aspirate the amount specified in aspiration_vol from the ## ## location specified in aspiration_location. ## p300.dispense(dispension_vol, well) ## Dispense the amount specified in dispension_vol to the ## ## location specified in well (looping through plate) ## p300.dispense(10, aspiration_location) ## Alternative for blow-out, make sure the tip doesn't fill ## ## completely when using a disposal volume by dispensing some ## ## of the volume after each pipetting step. (blow-out too many## ## bubbles) ## p300.drop_tip() ## when entire plate is full, drop tip ## # ============================================================================= # DILUTING AND DISTRIBUTING SAMPLE MIX========================================= # ============================================================================= for mix_method in range(8): ## where to dilute dilution = dilution_plate[mix_method] ## set variables per mix_method if mix_method == 0: ## 1: Mixing 3x with sample_vol +3. Distribute in 24 wells mix_vol = sample_vol_dil + 3 number_of_mixes = 3 destination = PCR_plate[:24] elif mix_method == 1: ## 2: Mixing 5x with sample_vol + 3 mix_vol = sample_vol_dil + 3 number_of_mixes = 5 destination = PCR_plate[24:32] elif mix_method == 2: ## 3: Mixing 7x with sample_vol + 3 mix_vol = sample_vol_dil + 3 number_of_mixes = 7 destination = PCR_plate[32:40] elif mix_method == 3: ## 4: Mixing 10x with sample_vol + 3 mix_vol = sample_vol_dil + 3 number_of_mixes = 10 destination = PCR_plate[40:48] elif mix_method == 4: ## 5: Mixing 3x with 20µL mix_vol = 20 number_of_mixes = 3 destination = PCR_plate[48:56] elif mix_method == 5: ## 6: Mixing 5x with 20µL mix_vol = 20 number_of_mixes = 5 destination = PCR_plate[56:64] elif mix_method == 6: ## 7: Mixing 7x with 20µL mix_vol = 20 number_of_mixes = 7 destination = PCR_plate[64:72] elif mix_method == 7: ## 8: Mixing 10x with 20µL mix_vol = 20 number_of_mixes = 10 destination = PCR_plate[72:80] #### diluting sample ## p20.pick_up_tip() ## p20 picks up tip from location of specified starting_tip or ## ## the following. ## p20.aspirate(sample_vol_dil, sample) ## aspirate sample_volume_dil = volume for dil. from sample_mix ## p20.dispense(sample_vol_dil, dilution) ## dispense in dilution tube p20.mix(number_of_mixes, mix_vol, dilution) ## mix according to mix_method p20.dispense(10, dilution) ## instead of blow-out ## p20.drop_tip() ## Drop tip in trashbin on 12. ## #### Distribute from dilution to PCR plate ## for well in destination: p20.pick_up_tip() p20.aspirate(sample_vol_pcr, dilution) ## aspirate sample_vol_pcr from dilution ## p20.dispense(sample_vol_pcr, well) ## dispense into pcr_well ## p20.mix(3, 20, well) ## Mix 3 times up and down with max pipette vol ## p20.dispense(10, well) ## instead of blow-out. ## p20.drop_tip()
def run(ctx: protocol_api.ProtocolContext): # load labware ic_pk = ctx.load_labware( 'opentrons_96_aluminumblock_nest_wellplate_100ul', '9', 'chilled tubeblock for internal control and proteinase K (strip 1)' ).wells()[0] bb = ctx.load_labware('nest_12_reservoir_15ml', '8', 'reagent reservoir Binding Buffer') binding_buffer = bb.wells()[:2] dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '11', '96-deepwell sample plate') # load tips tips300 = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', '5', '200µl filter tiprack') ] tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', '6', '20µl filter tiprack') ] # load pipette m300 = ctx.load_instrument('p300_multi_gen2', 'right', tip_racks=tips300) s20 = ctx.load_instrument('p20_single_gen2', 'left', tip_racks=tips20) m300.flow_rate.aspirate = 50 m300.flow_rate.dispense = 150 m300.flow_rate.blow_out = 300 s20.flow_rate.aspirate = 50 s20.flow_rate.dispense = 100 s20.flow_rate.blow_out = 300 # setup samples num_cols = math.ceil(NUM_SAMPLES / 8) bbs = bb.wells()[:2] dests_single = dest_plate.wells()[:NUM_SAMPLES] dests_multi = dest_plate.rows()[0][:num_cols] tip_log = {'count': {}} folder_path = '/data/A' tip_file_path = folder_path + '/tip_log.json' if TIP_TRACK and not ctx.is_simulating(): if os.path.isfile(tip_file_path): with open(tip_file_path) as json_file: data = json.load(json_file) if 'tips1000' in data: tip_log['count'][m300] = data['tips1000'] else: tip_log['count'][m300] = 0 if 'tips20' in data: tip_log['count'][s20] = data['tips20'] else: tip_log['count'][s20] = 0 else: tip_log['count'] = {m300: 0, s20: 0} tip_log['tips'] = { m300: [tip for rack in tips300 for tip in rack.wells()], s20: [tip for rack in tips20 for tip in rack.rows()[0]] } tip_log['max'] = {pip: len(tip_log['tips'][pip]) for pip in [m300, s20]} def pick_up(pip): nonlocal tip_log if tip_log['count'][pip] == tip_log['max'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_log['count'][pip] = 0 pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]]) tip_log['count'][pip] += 1 heights = {tube: 20 for tube in binding_buffer} # radius = (binding_buffer[0].diameter)/2 min_h = 5 def h_track(vol, tube): nonlocal heights dh = vol / (math.pi * (radius**2)) if heights[tube] - dh > min_h: heights[tube] = heights[tube] - dh else: heights[tube] = min_h # stop 5mm short of the bottom return heights[tube] # transfer internal control + proteinase K for d in dests_single: pick_up(s20) s20.transfer(ICPK_VOlUME, ic_pk.bottom(2), d.bottom(2), air_gap=5, new_tip='never') s20.air_gap(5) s20.drop_tip() # transfer binding buffer for i, e in enumerate(dests_multi): pick_up(m300) m300.transfer(BB_VOLUME, bbs[i // 6].bottom(2), e.bottom(10), air_gap=5, mix_after=(5, 100), new_tip='never') m300.air_gap(100) m300.drop_tip() ctx.comment('Terminado.') # track final used tip if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) data = {'tips300': tip_log['count'][m300]} with open(tip_file_path, 'w') as outfile: json.dump(data, outfile)
def run(protocol: protocol_api.ProtocolContext): """ Pick up 200µL filter tip. Aspirate 200µL from 50mL tube and transfer 5x to 1x 1.5mL tube (1mL aliquots) - repeat for 24x 1.5mL tubes. Drop tip, pick up new tip and repeat. 1x 50mL tube for 48x 1.5mL tubes. """ # ============================================================================= # =====================LOADING LABWARE AND PIPETTES============================ # ============================================================================= # Pipette tips tips_200 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware def 10, #deck position '200tips') #custom name # Tube racks stock_tubes = protocol.load_labware( 'opentrons_6_tuberack_falcon_50ml_conical', #labware def 7, #deck position '50mL_tubes') #custom name aliquot_tubes_1 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 4, #deck position 'aliquot_tubes_1') #custom name aliquot_tubes_2 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 1, #deck position 'aliquot_tubes_2') #custom name aliquot_tubes_3 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 11, #deck position 'aliquot_tubes_3') #custom name aliquot_tubes_4 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 8, #deck position 'aliquot_tubes_4') #custom name aliquot_tubes_5 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 5, #deck position 'aliquot_tubes_5') #custom name aliquot_tubes_6 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 2, #deck position 'aliquot_tubes_6') #custom name aliquot_tubes_7 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 6, #deck position 'aliquot_tubes_7') #custom name aliquot_tubes_8 = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def 3, #deck position 'aliquot_tubes_8') #custom name # Pipettes p300 = protocol.load_instrument( 'p300_single_gen2', #instrument definition 'right', #mount position tip_racks=[tips_200]) #assigned tiprack # ============================================================================= # ==========================VARIABLES TO SET#!!!=============================== # ============================================================================= start_vol = 50000 ## The start_vol is the volume (ul) that is in the source labware at ## ## the start of the protocol. ## dispension_vol = 200 ## The dispension_vol is the volume (ul) that needs to be aliquoted ## ## into the destination wells/tubes. ## p300.starting_tip = tips_200.well('E6') ## The starting_tip is the location of first pipette tip in the box ## container = 'tube_50mL' ## The container variable is needed for the volume tracking module. ## ## It tells the module which dimensions to use for the calculations ## ## of the pipette height. It is the source labware from which liquid ## ## is aliquoted. ## ## There are several options to choose from: ## ## 'tube_1.5ml', 'tube_2mL', 'tube_5mL', 'tube_15mL', 'tube_50mL' ## number_of_source_wells = 3 # source and destination wells================================================= source_wells = [] destination_wells = [] if number_of_source_wells == 1: source_well_1 = stock_tubes['A1'] source_wells.append( source_well_1) destinations = ( aliquot_tubes_1.wells() + aliquot_tubes_2.wells()) for well in destinations: destination_wells.append(well) elif number_of_source_wells == 2: source_well_1 = stock_tubes['A1'] source_well_2 = stock_tubes['A2'] sources = (source_well_1, source_well_2) source_wells.append(sources) destinations = ( aliquot_tubes_1.wells() + aliquot_tubes_2.wells() + aliquot_tubes_3.wells() + aliquot_tubes_4.wells()) for well in destinations: destination_wells.append(well) elif number_of_source_wells == 3: source_well_1 = stock_tubes['A1'] source_well_2 = stock_tubes['A2'] source_well_3 = stock_tubes['B1'] sources = (source_well_1, source_well_2, source_well_3) source_wells.append(sources) destinations = ( aliquot_tubes_1.wells() + aliquot_tubes_2.wells() + aliquot_tubes_3.wells() + aliquot_tubes_4.wells() + aliquot_tubes_5.wells() + aliquot_tubes_6.wells()) for well in destinations: destination_wells.append(well) elif number_of_source_wells == 4: source_well_1 = stock_tubes['A1'] source_well_2 = stock_tubes['A2'] source_well_3 = stock_tubes['B1'] source_well_4 = stock_tubes['B2'] sources = (source_well_1, source_well_2, source_well_3, source_well_4) source_wells.append(sources) destinations = ( aliquot_tubes_1.wells() + aliquot_tubes_2.wells() + aliquot_tubes_3.wells() + aliquot_tubes_4.wells() + aliquot_tubes_5.wells() + aliquot_tubes_6.wells() + aliquot_tubes_7.wells() + aliquot_tubes_8.wells()) for well in destinations: destination_wells.append(well) # ============================================================================= # ==========================PREDIFINED VARIABLES=============================== # ============================================================================= aspiration_vol = dispension_vol ## The aspiration_vol is the volume (ul) that is aspirated from the ## ## container. ## ##### Variables for volume tracking start_height = vt.cal_start_height(container, start_vol) ## Call start height calculation function from volume tracking module.## current_height = start_height ## Set the current height to start height at the beginning of the ## ## protocol. ## # ============================================================================= # =================================ALIQUOTING================================== # ============================================================================= ## For each column in destination_wells, pick up a tip, than for each ## ## well in these columns pipette mix, and after the+ column drop the tip## ## Repeat untill all columns in the list are done. ## for i, well in enumerate(destination_wells): ## Name all the wells in the plate 'well', for all these do: ## if i == 0: p300.pick_up_tip() ## If we are at the first well, start by picking up a tip. ## elif i % 24 == 0: p300.drop_tip() p300.pick_up_tip() ## Then, after every 8th well, drop tip and pick up a new one. ## current_height, pip_height, bottom_reached = vt.volume_tracking( container, dispension_vol, current_height) ## The volume_tracking function needs the arguments container, ## ## dispension_vol, and the current_height which we have set in ## ## this protocol. With those variables, the function updates ## ## the current_height, the pip_height and calculates the ## ## delta_height of the liquid after the next aspiration step. ## if bottom_reached: aspiration_location = source_wells.bottom(z=1) #!!! protocol.comment("You've reached the bottom!") else: aspiration_location = source_wells.bottom(pip_height) #!!! ## If the level of the liquid in the next run of the loop will ## ## be smaller than 1 we have reached the bottom of the tube. ## ## To prevent the pipette from crashing into the bottom, we ## ## tell it to go home and pause the protocol so that this can ## ## never happen. Set the location of where to aspirate from. ## ## Because we put this in the loop, the location will change ## ## to the newly calculated height after each pipetting step. ## p300.aspirate(aspiration_vol, aspiration_location) ## Aspirate the amount specified in aspiration_vol from the ## ## location specified in aspiration_location. ## p300.dispense(dispension_vol, well) ## Dispense the amount specified in dispension_vol to the ## ## location specified in well (so a new well every time the ## ## loop restarts) ## p300.drop_tip()