def run(protocol: protocol_api.ProtocolContext): ####################################################################################################################################### ## SETUP # Load Labware temp_mod = protocol.load_module('Temperature Module', '9') reaction_plate = protocol.load_labware( "biorad_384_wellplate_50ul", "8", label="reaction plate") # where the magic happens # tube_rack = protocol.load_labware("opentrons_96_aluminumblock_generic_pcr_strip_200ul", "6", label = "aluminum block") # CBS and substrate stocks sample_plate = protocol.load_labware( "greiner_96_wellplate_323ul", "6", label="sample plate" ) # CBS and dye stocks (!!! protect dye from light !!!) library_plate = protocol.load_labware( "greiner_96_wellplate_323ul", "5", label="library plate") # compound library # Specify tip racks tiprack1 = protocol.load_labware("opentrons_96_tiprack_10ul", '2') tiprack2 = protocol.load_labware("opentrons_96_tiprack_300ul", '3') # Load pipettes and set parameters p10 = protocol.load_instrument("p10_multi", "right", tip_racks=[tiprack1]) p50 = protocol.load_instrument('p50_multi', "left", tip_racks=[tiprack2]) p10.flow_rate.aspirate = 8 p10.flow_rate.dispense = 8 # 384-well depth is 11.56 mm and max volume is 112 uL # 20 uL is 2 mm high, tandem (middle) wall is 5.1 mm high p50.well_bottom_clearance.dispense = 7 # Specify target wells (ONLY NEED TO EDIT WELLS HERE) samples = ["7", "8", "9"] # columns on sample plate with protein and dye cols_compounds = [6, 7, 8, 9, 10] # library columns to aspirate compounds from cols_woSAM = [1, 2, 3, 4, 5] # destination wells in reaction plate (384-tandem) cols_wSAM = [ 6, 7, 8, 9, 10 ] # destination wells in reaction plate (384-tandem) (30 uM SAM final c) wells_reaction_woSAM = ['B' + str(i) for i in cols_woSAM] wells_reaction_wSAM = ['B' + str(i) for i in cols_wSAM] wells_reaction = ['B' + str(i) for i in (cols_woSAM + cols_wSAM)] ####################################################################################################################################### ## PROCEDURE # Distribute 2.5 uL compound from 96-well library plate into empty well for i in range(0, len(cols_compounds) ): # iterate through every column on the compound plate lib_col = str( cols_compounds[i]) # column of the library plate (single number) wells_target = [ wells_reaction_woSAM[i], wells_reaction_wSAM[i] ] # wells to distribute to (each compound is tested w/ and w/o SAM, so two apart columns) p10.distribute( 2.5, library_plate.columns_by_name()[lib_col], [reaction_plate.wells_by_name()[j] for j in wells_target], new_tip='once') # Transfer 5uL fluorescent dye from sample plate to reaction plate p50.distribute(5, sample_plate.columns_by_name()[samples[2]], [reaction_plate.wells_by_name()[i] for i in wells_reaction], disposal_volume=0, blow_out=False) # Distribute 17.5 protein p50.distribute( 17.5, sample_plate.columns_by_name()[samples[0]], [reaction_plate.wells_by_name()[i] for i in wells_reaction_woSAM], disposal_volume=0, blow_out=False, new_tip='once') p50.distribute( 17.5, sample_plate.columns_by_name()[samples[1]], [reaction_plate.wells_by_name()[i] for i in wells_reaction_wSAM], disposal_volume=0, blow_out=False, new_tip='once') # SAMPLE CODE # p20.transfer(20,reservoir_plate.wells_by_name()["A1"],mix_plate.wells_by_name()["A1"]) # p20.distribute(10,reservoir_plate.wells_by_name()["A2"],[mix_plate.wells_by_name()[well_name] for well_name in ["B1","C1","E1","G1","H1"]]) # p20.pick_up_tip() # p20.transfer(10,mix_plate.wells_by_name()["A1"],mix_plate.wells_by_name()["B1"],mix_after=(3,10),new_tip="never") # p20.transfer(10,mix_plate.wells_by_name()["B1"],mix_plate.wells_by_name()["C1"],mix_after=(3,10),new_tip="never") # p20.transfer(10,mix_plate.wells_by_name()["C1"],mix_plate.wells_by_name()["D1"],mix_after=(3,10),new_tip="never") # p20.transfer(10,mix_plate.wells_by_name()["D1"],mix_plate.wells_by_name()["E1"],mix_after=(3,10),new_tip="never") # p20.drop_tip() # p20.transfer(20,reservoir_plate.wells_by_name()["A3"],mix_plate.wells_by_name()["F1"]) # p20.pick_up_tip() # p20.transfer(10,mix_plate.wells_by_name()["F1"],mix_plate.wells_by_name()["G1"],mix_after=(3,10),new_tip="never") # p20.transfer(10,mix_plate.wells_by_name()["G1"],mix_plate.wells_by_name()["H1"],mix_after=(3,10),new_tip="never") # p20.drop_tip() # p10.transfer(4,mix_plate.wells_by_name()["A1"],mix_plate.wells_by_name()["A2"]) # p20.transfer(36,reservoir_plate.wells_by_name()["A4"],mix_plate.columns_by_name()["2"],new_tip='always') # p10.well_bottom_clearance.dispense = 1 # rows=["A","B","C","D","E","F","G","H"] # reaction_row_dest = "B" # p10.transfer(10,mix_plate.columns_by_name()["2"],[reaction_plate.wells_by_name()[well_name] for well_name in ["B2","B3","B4"]]) # # for row in rows: # # p20.distribute(10,mix_plate.wells_by_name()[(row+"2")],[reaction_plate.wells_by_name()[well_name] for well_name in [chr(ord(row)+1)+"2",chr(ord(row)+1)+"3",chr(ord(row)+1)+"4"]]) # p20.transfer(10,reservoir_plate.wells_by_name()["A4"],[reaction_plate.wells_by_name()[well_name] for well_name in ["J2","J3","J4"]])
def run(ctx: protocol_api.ProtocolContext): # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: {'Execute': HYDRATATE, 'description': 'Hidratate'}, 2: {'Execute': True, 'description': 'Transfer samples'} } 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 + '/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): 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.vol_well = 0 self.unused = [] self.vol_well_original = reagent_reservoir_volume / num_wells # Reagents and their characteristics Hydr = Reagent(name = 'Hydr', rinse = False, flow_rate_aspirate = 3, flow_rate_dispense = 3, reagent_reservoir_volume = 1800, num_wells = 1, delay = 0 ) Samples = Reagent(name = 'Samples', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 50, delay = 0, num_wells = NUM_SAMPLES ) Hydr.vol_well = Hydr.vol_well_original Samples.vol_well = Samples.vol_well_original ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VALORES DE VARIABLES') ctx.comment(' ') ctx.comment('Número de muestras: ' + str(NUM_SAMPLES) + ' las dos primeras son controles.') ctx.comment(' ') ctx.comment('Hidratar muestras: ' + str(HYDRATATE)) if HYDRATATE: ctx.comment('Volumen de Hidratante por muestra: ' + str(HYDR_VOL_PER_SAMPLE) + ' uL') ctx.comment(' ') ctx.comment('Volumen de muestra: ' + str(VOLUME_SAMPLE) + ' uL') ctx.comment(' ') ctx.comment('Foto-sensible: ' + str(PHOTOSENSITIVE)) ctx.comment('Repeticiones del sonido final: ' + str(SOUND_NUM_PLAYS)) ctx.comment(' ') ################## # 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, dest_x_offset, disp_height = 0, touch_tip = False, num_shakes = 0): pipette.aspirate((len(dest) * volume) + extra_dispensal, src.bottom(pickup_height)) if touch_tip : pipette.touch_tip(speed = 20, v_offset = -5) for d in dest: drop = d.top(z = disp_height) pipette.dispense(volume, drop) shake_pipet(pipette, rounds = num_shakes, v_offset = disp_height) 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 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) def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip, num_shakes = 0): ''' 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 shake_pipet(pipet, rounds = num_shakes, v_offset = disp_height) if blow_out == True: pipet.blow_out(dest.top(z = -10)) if touch_tip == True: pipet.touch_tip(speed = 20, v_offset = -10, 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 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')) except KeyboardInterrupt: pass print() 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) ctx.comment('Puntas de 20 uL utilizadas: ' + str(tip_track['counts'][m20]) + ' (' + str(round(tip_track['counts'][m20] / 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('finalizado') return finish_time #################################### # load labware and modules #################################### #################################### # 24 well rack tuberack = ctx.load_labware( 'opentrons_24_aluminumblock_generic_2ml_screwcap', '8', 'Opentrons 24 Well Aluminum Block with Generic 2 mL Screwcap') ################################## # Sample plate - comes from B source_plate = ctx.load_labware( 'biorad_96_wellplate_200ul_pcr', '3', 'Bio-Rad 96 Well Plate 200 µL PCR') ################################## # qPCR plate - final plate, goes to PCR qpcr_plate = ctx.load_labware( 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', '6', 'Opentrons 96 Well Aluminum Block with Generic PCR Strip 200 µL') ################################## # Load Tipracks tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['2'] ] tips200 = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', slot) for slot in ['5'] ] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate Hydr.reagent_reservoir = tuberack.rows()[0][0] # A1 # setup up sample sources and destinations samples = source_plate.rows()[0][:num_cols] pcr_wells = qpcr_plate.wells()[:NUM_SAMPLES] pcr_wells_samples = qpcr_plate.rows()[0][:num_cols] tipCols = tips20[0].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) # load m20 pipette 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}, 'maxes': {p300: 96 * len(p300.tip_racks), m20: 96 * len(m20.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: HIDRATATE ############################################################################ 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 = HYDR_VOL_PER_SAMPLE * len(dest) + extra_dispensal used_vol_temp = distribute_custom(p300, volume = HYDR_VOL_PER_SAMPLE, src = Hydr.reagent_reservoir, dest = dest, touch_tip = False, waste_pool = Hydr.reagent_reservoir, pickup_height = 0.2, extra_dispensal = extra_dispensal, dest_x_offset = 0, disp_height = -15, num_shakes = 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(' ') i = 0 for s, d in zip(samples, pcr_wells_samples): pick_up(m20) 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(home_after = False) tip_track['counts'][m20] += 8 i = i + 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() ############################################################################ finish_run()
def run(protocol: protocol_api.ProtocolContext): tiprack = protocol.load_labware(TIPRACK_LOADNAME, TIPRACK_SLOT) pipette = protocol.load_instrument(PIPETTE_NAME, PIPETTE_MOUNT, tip_racks=[tiprack]) test_labware = protocol.load_labware_from_definition( LABWARE_DEF, TEST_LABWARE_SLOT, LABWARE_LABEL, ) num_cols = len(LABWARE_DEF.get('ordering', [[]])) num_rows = len(LABWARE_DEF.get('ordering', [[]])[0]) well_locs = uniq( ['A1', '{}{}'.format(chr(ord('A') + num_rows - 1), str(num_cols))]) pipette.pick_up_tip() 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) for slot in CALIBRATION_CROSS_SLOTS: coordinate = CALIBRATION_CROSS_COORDS[slot] location = types.Location(point=types.Point(**coordinate), labware=None) pipette.move_to(location) protocol.pause( f"Confirm {PIPETTE_MOUNT} pipette is at slot {slot} calibration cross" ) pipette.home() protocol.pause(f"Place your labware in Slot {TEST_LABWARE_SLOT}") for well_loc in well_locs: well = test_labware.well(well_loc) all_4_edges = [[well._from_center_cartesian(x=-1, y=0, z=1), 'left'], [well._from_center_cartesian(x=1, y=0, z=1), 'right'], [well._from_center_cartesian(x=0, y=-1, z=1), 'front'], [well._from_center_cartesian(x=0, y=1, z=1), 'back']] set_speeds(RATE) pipette.move_to(well.top()) protocol.pause("Moved to the top of the well") for edge_pos, edge_name in all_4_edges: set_speeds(SLOWER_RATE) edge_location = types.Location(point=edge_pos, labware=None) pipette.move_to(edge_location) protocol.pause(f'Moved to {edge_name} edge') # go to bottom last. (If there is more than one well, use the last well first # because the pipette is already at the last well at this point) for well_loc in reversed(well_locs): well = test_labware.well(well_loc) set_speeds(RATE) pipette.move_to(well.bottom()) protocol.pause("Moved to the bottom of the well") pipette.blow_out(well) set_speeds(1.0) pipette.return_tip()
def run(protocol: protocol_api.ProtocolContext): [_source, _no_plates, _start, _vol_dispense, _touch_tip] = get_values( # noqa: F821 "source", "no_plates", "start", "vol_dispense", "touch_tip") protocol.set_rail_lights(False) protocol.set_rail_lights(True) # variables: number of plates/mastermixes to be prepared, dispense volume no_plates = _no_plates vol_dispense = _vol_dispense touch_tip = _touch_tip start = _start source = _source # load tiprack tiprack_20 = protocol.load_labware('opentrons_96_tiprack_20ul', 6) # load pipette p10_multi = protocol.load_instrument("p10_multi", "left", tip_racks=[tiprack_20]) # load labware master_troughs = protocol.load_labware("nest_12_reservoir_15ml", 2, label="master trough") master_plate = protocol.load_labware("96w_pcr_plate2", 1, label="master-plate") pcr_plate = protocol.load_labware("96w_pcr_plate2", 5, label="pcr-plate") # unbound methods def slow_tip_withdrawal(self, speed_limit, well_location, to_center=False): if self.mount == 'right': axis = 'A' else: axis = 'Z' protocol.max_speeds[axis] = speed_limit if to_center is False: self.move_to(well_location.top()) else: self.move_to(well_location.center()) protocol.max_speeds[axis] = None def delay(self, delay_time): protocol.delay(seconds=delay_time) # bind methods to pipette for pipette_object in [p10_multi]: for method in [delay, slow_tip_withdrawal]: setattr(pipette_object, method.__name__, MethodType(method, pipette_object)) # helper functions def touch_right(cur_position: Location): if touch_tip == "right": p10_multi.move_to(cur_position.move(Point(x=2.7, z=-0.75))) p10_multi.move_to(cur_position.move(Point(x=-1, z=0.75))) else: p10_multi.move_to(cur_position.move(Point(x=-2.7, z=-0.75))) p10_multi.move_to(cur_position.move(Point(x=1, z=0.75))) def dispense_mm(vol, primer_no, mm_source): wells = pcr_plate.columns() if mm_source == "trough": mm = master_troughs.wells()[primer_no - 1] else: mm = master_plate.columns()[primer_no - 1][0] p10_multi.pick_up_tip() for col in wells: p10_multi.aspirate(vol, mm) p10_multi.dispense(vol * 2, col[0].top(-1), rate=1.6) touch_right(col[0].top()) p10_multi.delay(0.2) p10_multi.slow_tip_withdrawal(10, col[0]) p10_multi.drop_tip() # protocol: distribute master mixes for mm in range(start, no_plates + 1): if source == "trough": protocol.pause( "When you press resume, the master mix will be dispensed \ into the next plate. Ensure this plate is in position \ (in slot 5) and that at least {}uL of master mix for this \ plate has been added to trough {}.".format( (96 * vol_dispense) + 297, mm)) else: protocol.pause( "When you press resume, the master mix will be dispensed into \ the next plate. Ensure this plate is in position (in slot 5) \ and that at least {}uL of master mix for this plate has been \ added to each well of column {}.".format( (8 * vol_dispense) + 10, mm)) dispense_mm(vol_dispense, mm, source)
def run(ctx: protocol_api.ProtocolContext): STEP = 0 STEPS = { # Dictionary with STEP activation, description and times 1: { 'Execute': True, 'description': 'Dispensar Antibioticos' }, 2: { 'Execute': True, 'description': 'Dispensar caldo y mezclar ' } } 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 + '/time_log.txt' # Define Reagents as objects with their properties #Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, flow_rate_aspirate_mix, flow_rate_dispense_mix, air_gap_vol_bottom, air_gap_vol_top, disposal_volume, rinse, max_volume_allowed, reagent_volume, reagent_reservoir_volume, num_wells, h_cono, v_fondo, tip_recycling='none', dead_vol=DEFAULT_DEAD_VOL): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.flow_rate_aspirate_mix = flow_rate_aspirate_mix self.flow_rate_dispense_mix = flow_rate_dispense_mix self.air_gap_vol_bottom = air_gap_vol_bottom self.air_gap_vol_top = air_gap_vol_top self.disposal_volume = disposal_volume self.rinse = bool(rinse) self.max_volume_allowed = max_volume_allowed self.reagent_volume = reagent_volume self.reagent_reservoir_volume = reagent_reservoir_volume self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.tip_recycling = tip_recycling self.dead_vol = dead_vol self.vol_well_original = (reagent_reservoir_volume / num_wells ) + dead_vol if num_wells > 0 else 0 # Reagents and their characteristics Samples = Reagent( name='Antibioticos', flow_rate_aspirate=3, flow_rate_dispense=3, flow_rate_aspirate_mix=1, flow_rate_dispense_mix=1, air_gap_vol_bottom=5, air_gap_vol_top=0, disposal_volume=0, rinse=True, max_volume_allowed=180, reagent_volume=VOLUME_SAMPLE, reagent_reservoir_volume=NUM_REAL_SAMPLES * VOLUME_SAMPLE * 1.1, num_wells=math.ceil(NUM_REAL_SAMPLES * VOLUME_SAMPLE * 1.1 / 11500), h_cono=1.95, v_fondo=695) #1.95 * multi_well_rack_area / 2, #Prismatic Caldo = Reagent( name='Caldo ', flow_rate_aspirate=2, flow_rate_dispense=2, flow_rate_aspirate_mix=1, flow_rate_dispense_mix=1, air_gap_vol_bottom=6, air_gap_vol_top=0, disposal_volume=0, rinse=True, max_volume_allowed=180, reagent_volume=VOLUME_SAMPLE, reagent_reservoir_volume=NUM_REAL_SAMPLES * VOLUME_SAMPLE * 1.1, num_wells=math.ceil(NUM_REAL_SAMPLES * VOLUME_SAMPLE * 1.1 / 11500), h_cono=1.95, v_fondo=695) #1.95 * multi_well_rack_area / 2, #Prismatic ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VALORES DE VARIABLES') ctx.comment(' ') ctx.comment('Número Antibióticos: ' + str(NUM_SAMPLE_ANTIBIOTIC) + ' (' + str(NUM_FINAL_PLATES) + ' placas)') ctx.comment('Número de muestras: ' + str(NUM_REAL_SAMPLES) + ' (' + str(math.ceil(NUM_REAL_SAMPLES / 8)) + ' columnas)') ctx.comment('Número de mezclas: ' + str(NUM_DILUTION_MIXES)) ctx.comment(' ') ctx.comment('Volumen de muestra a mover al deepwell: ' + str(VOLUME_SAMPLE) + ' ul') ctx.comment(' ') ctx.comment('Número de mezclas en la muestra: ' + str(TUBE_NUM_MIXES)) ctx.comment(' ') ctx.comment('Repeticiones del sonido final: ' + str(SOUND_NUM_PLAYS)) ctx.comment('Foto-sensible: ' + str(PHOTOSENSITIVE)) ctx.comment(' ') ################## # Custom functions def move_multichanel_caldo(dest): if not m300.hw_pipette['has_tip']: pick_up_multi(m300) caldo_trips = math.floor(Caldo.max_volume_allowed / Caldo.reagent_volume) caldo_volume = Caldo.reagent_volume * caldo_trips #136.66 Caldo.max_volume_allowed = caldo_volume caldo_transfer_vol = [] x_offset_source = 0 x_offset_dest = 0 rinse = False # Original: True first_mix_done = False actual_pipette_vol = 0 for i in range(num_cols_caldo): ctx.comment("Column: " + str(i)) #Calculate pickup_height based on remaining volume and shape of container ctx.comment('Aspirate from reservoir column: ' + str(Caldo.col)) if actual_pipette_vol >= VOLUME_SAMPLE: recharge = False else: actual_pipette_vol = Caldo.max_volume_allowed recharge = True move_vol_multi(m300, reagent=Caldo, source=Caldo.reagent_reservoir, dest=dest[i], vol=Caldo.reagent_volume, x_offset_source=x_offset_source, x_offset_dest=x_offset_dest, pickup_height=0, rinse=rinse, wait_time=0.5, blow_out=False, touch_tip=True, drop_height=-3, recharge=recharge) actual_pipette_vol = actual_pipette_vol - VOLUME_SAMPLE m300.blow_out(Caldo.reagent_reservoir.top(z=0)) ctx.delay(seconds=2, msg='Waiting for ' + str(1) + ' seconds.') m300.move_to(Caldo.reagent_reservoir.bottom(2)) m300.blow_out(Caldo.reagent_reservoir.top(z=0)) def move_multichanel_mezcla(origin, dest, pickup_height=2, air_gap_vol=5): x_offset_source = 0 x_offset_dest = 0 first_mix_done = False if not m300.hw_pipette['has_tip']: pick_up(m300) for i in range(num_cols_caldo): ctx.comment("Column: " + str(i)) ctx.comment('Aspirate from reservoir column: ' + str(Caldo.col)) move_vol_multi(m300, reagent=Caldo, source=origin[i], dest=dest[i], vol=Caldo.reagent_volume, multitripCount=1, x_offset_source=x_offset_source, x_offset_dest=x_offset_dest, pickup_height=pickup_height, rinse=False, rinseEnd=True, wait_time=0.5, blow_out=False, touch_tip=False, drop_height=-1, rate_multiplier=0.1) # Aspirar 50 ul de la última columna para igualar volúmenes m300.aspirate(Caldo.reagent_volume, location=dest[num_cols_caldo - 1].bottom(z=pickup_height), rate=Caldo.flow_rate_aspirate) if air_gap_vol > 0: m300.aspirate(air_gap_vol, dest[num_cols_caldo - 1].top(z=0), rate=Caldo.flow_rate_aspirate) # air gap m300.drop_tip(home_after=False) tip_track['counts'][m300] += 8 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 ''' # 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=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 move_vol_multi(pipet, reagent, source, dest, vol, x_offset_source, x_offset_dest, pickup_height, rinse, wait_time, blow_out, touch_tip=False, drop_height=-5, dispense_bottom_air_gap_before=False, rinseEnd=False, recharge=True, multitripCount=3, rate_multiplier=1): # Rinse before aspirating if rinse == True: #pipet.aspirate(air_gap_vol_top, location = source.top(z = -5), rate = reagent.flow_rate_aspirate) #air gap custom_mix(pipet, reagent, location=source, vol=vol * .6, rounds=NUM_DILUTION_MIXES, blow_out=False, mix_height=pickup_height, offset=0) #pipet.dispense(air_gap_vol_top, location = source.top(z = -5), rate = reagent.flow_rate_dispense) # SOURCE if recharge == True: #if dispense_bottom_air_gap_before and reagent.air_gap_vol_bottom: # pipet.dispense(reagent.air_gap_vol_bottom, source.top(z = -2), rate = reagent.flow_rate_dispense) if reagent.air_gap_vol_top != 0: #If there is air_gap_vol, switch pipette to slow speed pipet.move_to(source.top(z=0)) pipet.air_gap(reagent.air_gap_vol_top) #air gap #pipet.aspirate(reagent.air_gap_vol_top, source.top(z = -5), rate = reagent.flow_rate_aspirate) #air gap pipet.blow_out(source.top(z=drop_height)) s = source.bottom(pickup_height).move(Point(x=x_offset_source)) pipet.aspirate(vol * multitripCount, s, rate=reagent.flow_rate_aspirate * rate_multiplier) # aspirate liquid if reagent.air_gap_vol_bottom != 0: #If there is air_gap_vol, switch pipette to slow speed pipet.air_gap(reagent.air_gap_vol_bottom, height=0) #air gap if wait_time != 0: ctx.delay(seconds=wait_time, msg='Waiting for ' + str(wait_time) + ' seconds.') # GO TO DESTINATION d = dest.top(z=drop_height).move(Point(x=x_offset_dest)) pipet.dispense(vol + reagent.air_gap_vol_bottom, d, rate=reagent.flow_rate_dispense * rate_multiplier) if wait_time != 0: ctx.delay(seconds=wait_time, msg='Waiting for ' + str(wait_time) + ' seconds.') if reagent.air_gap_vol_top != 0: pipet.dispense(reagent.air_gap_vol_top, dest.top(z=0), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(dest.top(z=drop_height)) if touch_tip == True: pipet.touch_tip(speed=20, v_offset=-3, radius=0.7) # Rinse after aspirating if rinseEnd == True: #pipet.aspirate(air_gap_vol_top, location = source.top(z = -5), rate = reagent.flow_rate_aspirate) #air gap custom_mix(pipet, reagent, location=dest, vol=vol * .6, rounds=2, blow_out=False, mix_height=pickup_height, offset=0) pipet.blow_out(dest.top(z=drop_height)) #pipet.dispense(air_gap_vol_top, location = source.top(z = -5), rate = reagent.flow_rate_dispense) if reagent.air_gap_vol_bottom != 0 and rinseEnd == False: pipet.move_to(dest.top(z=0)) pipet.air_gap(reagent.air_gap_vol_bottom) #air gap #pipet.aspirate(air_gap_vol_bottom, dest.top(z = 0),rate = reagent.flow_rate_aspirate) #air gap def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, offset, wait_time=0, drop_height=-1, two_thirds_mix_bottom=False): ''' Function for mix in the same location a certain number of rounds. Blow out optional. Offset can set to 0 or a higher/lower value which indicates the lateral movement ''' if mix_height <= 0: mix_height = 1 pipet.aspirate(1, location=location.bottom(z=mix_height), rate=reagent.flow_rate_aspirate_mix) for i in range(rounds): pipet.aspirate(vol, location=location.bottom(z=mix_height), rate=reagent.flow_rate_aspirate_mix) if two_thirds_mix_bottom and i < ((rounds / 3) * 2): pipet.dispense(vol, location=location.bottom(z=5).move( Point(x=offset)), rate=reagent.flow_rate_dispense_mix) else: pipet.dispense(vol, location=location.top(z=drop_height).move( Point(x=offset)), rate=reagent.flow_rate_dispense_mix) pipet.dispense(1, location=location.bottom(z=mix_height), rate=reagent.flow_rate_dispense_mix) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out if wait_time != 0: ctx.delay(seconds=wait_time, msg='Waiting for ' + str(wait_time) + ' seconds.') def generate_atibiotic_source(source_plate): result = [] ''' for i in range (NUM_FINAL_PLATES): result += source_plate.rows()[i][0] ''' result = source_plate.rows()[:NUM_FINAL_PLATES][0] return result def generate_antibiotic_dest(dest_plates): ''' Concatenate cols from all destination plates ''' result = [] #result = dest_plates[0].columns()[0][0] + dest_plates[1].columns()[0] + dest_plates[2].columns()[0] for i in range(NUM_FINAL_PLATES): result += [dest_plates[i].columns()[0][0]] return result def calc_height(reagent, cross_section_area, aspirate_volume, min_height=0.4): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if (reagent.vol_well - reagent.dead_vol) < aspirate_volume: 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.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 pick_up_multi(pip): nonlocal tip_track #if not ctx.is_simulating(): if tip_track['counts'][pip] >= tip_track['maxes'][pip]: 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.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_track['counts'][pip] = 0 tip_track['num_refills'][pip] += 1 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_300 = tip_track['num_refills'][m300] * 96 * len( m300.tip_racks) + tip_track['counts'][m300] ctx.comment('Puntas de 300 ul utilizadas: ' + str(used_tips_300) + ' (' + str(round(used_tips_300 / 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 def validate_parameters(): result = True return result #################################### # load labware and modules #################################### # Load Sample racks source_antibiotic = ctx.load_labware('nest_96_wellplate_2ml_deep', '8', 'NEST 96 Deepwell Plate 2mL') ''' ctx.load_labware( 'opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap', '8', 'Opentrons 24 Tuberack Eppendorf 2ml Safelock Snapcap') ''' source_caldo = ctx.load_labware('nest_1_reservoir_195ml', '1', 'NEST 1 Reservoir 195ul') ################################## # Destination plate # Destination dest_plate1 = ctx.load_labware('nest_96_wellplate_200ul_flat', '3', 'NEST 96 Well Plate 200ul 1') dest_plate2 = ctx.load_labware('nest_96_wellplate_200ul_flat', '5', 'NEST 96 Well Plate 200ul 2') dest_plate3 = ctx.load_labware('nest_96_wellplate_200ul_flat', '7', 'NEST 96 Well Plate 200ul 3') #################################### # Load tip_racks tips200 = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', slot) for slot in ['10'] ] ################################################################################ # setup samples and destinations num_destination_plates = NUM_FINAL_PLATES #sample_antibiotic = source_antibiotic.wells()[:NUM_SAMPLE_ANTIBIOTIC] sample_antibiotic = generate_atibiotic_source(source_antibiotic) Caldo.reagent_reservoir = source_caldo.wells()[0] destinations_antibiotic = generate_antibiotic_dest( [dest_plate1, dest_plate2, dest_plate3]) #[:NUM_SAMPLE_ANTIBIOTIC] destinations1 = dest_plate1.rows()[0][1:] destinations2 = dest_plate2.rows()[0][1:] destinations3 = dest_plate3.rows()[0][1:] sourceMix1 = dest_plate1.rows()[0][:11] sourceMix2 = dest_plate2.rows()[0][:11] sourceMix3 = dest_plate3.rows()[0][:11] destinationsMix1 = dest_plate1.rows()[0][1:12] destinationsMix2 = dest_plate2.rows()[0][1:12] destinationsMix3 = dest_plate3.rows()[0][1:12] m300 = ctx.load_instrument('p300_multi_gen2', 'right', tip_racks=tips200) # load P300 pipette # used tip counter and set maximum tips available tip_track = { 'counts': { m300: 0 }, 'maxes': { m300: 96 * len(m300.tip_racks) }, 'num_refills': { m300: 0 }, 'tips': { m300: [tip for rack in tips200 for tip in rack.rows()[0]] } } if validate_parameters(): start_run() ############################################################################ # STEP 1: Dispensación de Atnibiótico ############################################################################ 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_antibiotic, destinations_antibiotic): # Mix the sample BEFORE dispensing if not m300.hw_pipette['has_tip']: pick_up(m300) move_vol_multichannel(m300, reagent=Samples, source=s, dest=d, vol=VOLUME_ANTBIOTIC, air_gap_vol=air_gap_vol_sample, x_offset=x_offset, pickup_height=3, rinse=Samples.rinse, disp_height=0, blow_out=True, touch_tip=False) m300.drop_tip(home_after=False) tip_track['counts'][m300] += 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: Dilución de antibiótico sobre el caldo ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') start = datetime.now() if num_destination_plates >= 1: move_multichanel_caldo(destinations1) move_multichanel_mezcla(sourceMix1, destinationsMix1) if num_destination_plates >= 2: move_multichanel_caldo(destinations2) move_multichanel_mezcla(sourceMix2, destinationsMix2) if num_destination_plates >= 3: move_multichanel_caldo(destinations3) move_multichanel_mezcla(sourceMix3, destinationsMix3) # 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() ############################################################################ finish_run(switch_off_lights)
def run(ctx: protocol_api.ProtocolContext): ctx.comment('Actual used columns: ' + str(num_cols)) #from opentrons.drivers.rpi_drivers import gpio # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: {'Execute': True, 'description': 'Add 50 ul Elution Buffer'}, 2: {'Execute': True, 'description': 'Add 100 ul Wash Buffer 1 - Round 1'}, 3: {'Execute': True, 'description': 'Add 100 ul Wash Buffer 2 - Round 1 and stop until plate comes from A'}, 4: {'Execute': True, 'description': 'Add 100 ul Lysis Buffer and then move to station A'}, 5: {'Execute': False, 'description': 'Transfer IC'}, 6: {'Execute': True, 'description': 'Transfer ICtwo'}, 7: {'Execute': False, 'description': 'Mix beads'}, 8: {'Execute': False, 'description': 'Transfer beads'}, 9: {'Execute': True, 'description': 'Mix beadstwo'}, 10: {'Execute': True, 'description': 'Transfer beadstwo'} } 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/'+str(run_id) if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/KB_station_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 = 3, flow_rate_dispense_mix = 4, flow_rate_aspirate_mix = 4): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.flow_rate_aspirate_mix = flow_rate_aspirate_mix self.flow_rate_dispense_mix = flow_rate_dispense_mix 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 self.rinse_loops = rinse_loops # Reagents and their characteristics WashBuffer1 = Reagent(name='Wash Buffer 1', flow_rate_aspirate=0.75, flow_rate_dispense=1, rinse=True, rinse_loops=6, delay=3, reagent_reservoir_volume=$Wone_total_volume, #100*NUM_SAMPLES, num_wells=$Wone_wells, h_cono=1.95, v_fondo=695) # Flat surface WashBuffer2 = Reagent(name='Wash Buffer 2', flow_rate_aspirate=0.75, flow_rate_dispense=1, rinse=True, delay=3, reagent_reservoir_volume=$Wtwo_total_volume, #100*NUM_SAMPLES, num_wells=$Wtwo_wells, h_cono=1.95, v_fondo=695) # Flat surface Lysis = Reagent(name='Lysis Buffer', flow_rate_aspirate=0.75, flow_rate_dispense=0.5, rinse=False, rinse_loops=6, delay=2, reagent_reservoir_volume=$Lysis_total_volume, #100*NUM_SAMPLES, num_wells=1, h_cono=1.95, v_fondo=695) # Flat surface ElutionBuffer = Reagent(name='Elution Buffer', flow_rate_aspirate=1, flow_rate_dispense=1, rinse=False, delay=0, reagent_reservoir_volume=$Elution_total_volume,#50*NUM_SAMPLES, num_wells=$Elution_wells, h_cono=1.95, v_fondo=695) # Prismatic IC = Reagent(name='Magnetic beads and Lysis', flow_rate_aspirate=1, flow_rate_dispense=3, rinse=False, num_wells=$IC_wells, delay=2, reagent_reservoir_volume=$IC_total_volume,#20 * NUM_SAMPLES * 1.1, h_cono=1.95, v_fondo=695) # Prismatic ICtwo = Reagent(name='Internal control 2', flow_rate_aspirate=1, flow_rate_dispense=3, rinse=False, num_wells=1, delay=2, reagent_reservoir_volume=$IC_total_volume,#20 * NUM_SAMPLES * 1.1, h_cono=1, v_fondo=10) # Prismatic Beads = Reagent(name='Magnetic beads and Lysis', flow_rate_aspirate=0.5, flow_rate_dispense=0.5, rinse=True, rinse_loops=6, num_wells=$Beads_wells, delay=3, reagent_reservoir_volume=$Beads_total_volume,#20 * NUM_SAMPLES * 1.1, h_cono=1.95, v_fondo=695) # Prismatic Beadstwo = Reagent(name='Magnetic beads 2', flow_rate_aspirate=0.5, flow_rate_dispense=0.5, rinse=True, rinse_loops=4, num_wells=2, delay=3, reagent_reservoir_volume=$Beads_total_volume,#20 * NUM_SAMPLES * 1.1, h_cono=1.95, v_fondo=695) # Prismatic Beads.vol_well = Beads.vol_well_original IC.vol_well = IC.vol_well_original WashBuffer1.vol_well = WashBuffer1.vol_well_original WashBuffer2.vol_well = WashBuffer2.vol_well_original ElutionBuffer.vol_well = ElutionBuffer.vol_well_original Lysis.vol_well = Lysis.vol_well_original Beadstwo.vol_well = Beadstwo.vol_well_original ICtwo.vol_well = ICtwo.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, post_dispense=False, post_dispense_vol=20, post_airgap=True, 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 = reagent.rinse_loops, blow_out = True, mix_height = 0, x_offset = x_offset, post_airgap=False,post_dispense=False, post_dispense_vol=20,post_airgap_vol=10) # 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 = -5)) if post_airgap == True: pipet.aspirate(post_airgap_vol, 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) def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, source_height = 3, post_airgap=True, post_airgap_vol=10, post_dispense=False, post_dispense_vol=20,x_offset = x_offset): ''' 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_mix) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate_mix) pipet.dispense(vol, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense_mix) pipet.dispense(1, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense_mix) 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.aspirate(post_airgap_vol, location.top(z = 5)) def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.2, 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 # IC well rack #################################### #tempdeck = ctx.load_module('tempdeck', '3') ic_reservoir = ctx.load_labware( 'nest_96_wellplate_100ul_pcr_full_skirt','3', 'Wellplate with Beads and IC') # 12 well rack #################################### reagent_res = ctx.load_labware( 'nest_12_reservoir_15ml', '5', 'Reservoir 12 channel, column 1') # Wash Buffer 1 100ul Deepwell plate ############################################ WashBuffer1_100ul_plate1 = ctx.load_labware( 'kf_96_wellplate_2400ul', '1', 'Wash Buffer 1 Deepwell plate 1') # Wash Buffer 2 100ul Deepwell plate ############################################ WashBuffer2_100ul_plate1 = ctx.load_labware( 'kf_96_wellplate_2400ul', '4', 'Wash Buffer 2 Deepwell plate 1') # Lysis buffer 100ul Deepwell plate and later will be the plate with samples ############################################ kf_plate = ctx.load_labware( 'kf_96_wellplate_2400ul', '2', 'Sample Deepwell plate 1') # Elution Deepwell plate ############################################ ElutionBuffer_50ul_plate = ctx.load_labware( 'kingfisher_std_96_wellplate_550ul', '7', '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']] tips20 = [ctx.load_labware('opentrons_96_filtertiprack_20ul', slot, '20µl filter tiprack') for slot in ['6','9']] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate WashBuffer1.reagent_reservoir = reagent_res.rows( )[0][:1] # position 1 WashBuffer2.reagent_reservoir = reagent_res.rows( )[0][1:2] #position 2 ElutionBuffer.reagent_reservoir = reagent_res.rows( )[0][9:10] #position 10 Lysis.reagent_reservoir = reagent_res.rows( )[0][2:3] #position 3 IC.reagent_reservoir = reagent_res.rows( )[0][10:11] #position 4 Beads.reagent_reservoir = reagent_res.rows( )[0][11:] #position 4 ICtwo.reagent_reservoir = ic_reservoir.rows( )[0][:1] #position 1 Beadstwo.reagent_reservoir = ic_reservoir.rows( )[0][1:3] #position 2 to 3 # columns in destination plates to be filled depending the number of samples wb1plate1_destination = WashBuffer1_100ul_plate1.rows()[0][:num_cols] wb2plate1_destination = WashBuffer2_100ul_plate1.rows()[0][:num_cols] elutionbuffer_destination = ElutionBuffer_50ul_plate.rows()[0][:num_cols] kf_destination = kf_plate.rows()[0][:num_cols] # pipette m300 = ctx.load_instrument( 'p300_multi_gen2', 'right', tip_racks=tips300) # Load multi pipette # pipettes. P1000 currently deactivated m20 = ctx.load_instrument( 'p20_multi_gen2', 'left', tip_racks=tips20) # Load p300 multi pipette # used tip counter and set maximum tips available tip_track = { 'counts': {m300: 0, m20: 0}, 'maxes': {m300: len(tips300)*96, m20: len(tips20)*96} } ############################################################################ # STEP 1: Transfer Elution buffer ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') # Elution buffer ElutionBuffer_vol = [Elution_vol] ######## # 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 move_vol_multichannel(m300, reagent = ElutionBuffer, source = ElutionBuffer.reagent_reservoir[ElutionBuffer.col], dest = elutionbuffer_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol_elutionbuffer, x_offset = x_offset, pickup_height = 0.2, rinse = False, disp_height = -2, blow_out = True, touch_tip = True, post_airgap=True) 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) ############################################################################ # STEP 2: Filling with WashBuffer1 ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') WB1 = [WBone_vol] 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(WB1): 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[WashBuffer1.col], dest = wb1plate1_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 0.3, rinse = rinse, disp_height = -2, blow_out = True, touch_tip = False, post_airgap=True) 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) ############################################################################ # STEP 3: Filling with WashBuffer2 ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') WB2 = [WBtwo_vol] 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(WB2): if (i == 0 and j == 0): rinse = True else: rinse = False move_vol_multichannel(m300, reagent = WashBuffer2, source = WashBuffer2.reagent_reservoir[WashBuffer2.col], dest = wb2plate1_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 0.2, rinse = rinse, disp_height = -2, blow_out = True, touch_tip = False, post_airgap=True) m300.drop_tip(home_after=False) tip_track['counts'][m300] += 8 ctx.pause('Bring plate from A in position 2 and click continue. Put IC and Beads in position 3') 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 Lysis ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') lysis_vol = [100] 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(lysis_vol): if (i == 0 and j == 0): rinse = True #Rinse only first transfer else: rinse = False move_vol_multichannel(m300, reagent = Lysis, source = Lysis.reagent_reservoir[Lysis.col], dest = kf_destination[i], vol = transfer_vol, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 0.2, rinse = rinse, disp_height = -2, blow_out = True, touch_tip = False, post_airgap=True,post_dispense=True) 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) ############################################################################ # STEP 5: TRANSFER IC ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: # Transfer parameters start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') IC_transfer_vol = [ic_vol] rinse = True for i in range(num_cols): if not m20.hw_pipette['has_tip']: pick_up(m20) for j, transfer_vol in enumerate(IC_transfer_vol): move_vol_multichannel(m20, reagent=IC, source=IC.reagent_reservoir[IC.col], dest=kf_destination[i], vol=transfer_vol, air_gap_vol=air_gap_ic, x_offset=[0,0], pickup_height=0.2, disp_height = -40.7, rinse=IC.rinse, blow_out = True, touch_tip=False, post_airgap=True) m20.drop_tip(home_after=False) 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) ############################################################################ # STEP 6: TRANSFER IC2 ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: # Transfer parameters start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') IC_transfer_vol = [ic_vol] rinse = True for i in range(num_cols): if not m20.hw_pipette['has_tip']: pick_up(m20) for j, transfer_vol in enumerate(IC_transfer_vol): move_vol_multichannel(m20, reagent=ICtwo, source=ICtwo.reagent_reservoir[IC.col], dest=kf_destination[i], vol=transfer_vol, air_gap_vol=air_gap_ic, x_offset=[0,0], pickup_height=0.5, disp_height = -40.7, rinse=ICtwo.rinse, blow_out = True, touch_tip=False, post_airgap=True) m20.drop_tip(home_after=False) 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) ############################################################################ # STEP 7: PREMIX BEADS ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') if not m300.hw_pipette['has_tip']: pick_up(m300) ctx.comment('Tip picked up') ctx.comment('Mixing ' + Beads.name) # Mixing custom_mix(m300, Beads, Beads.reagent_reservoir[Beads.col], vol=120, rounds=10, blow_out=True, mix_height=10, post_dispense=True, source_height=0.3) ctx.comment('Finished premixing!') ctx.comment('Now, reagents will be transferred to deepwell plate.') 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 8: TRANSFER BEADS ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: # Transfer parameters start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') beads_transfer_vol = [beads_vol] # Two rounds of 130 rinse = True for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(beads_transfer_vol): if (i == 0 and j == 0): rinse = True else: rinse = False # Calculate pickup_height based on remaining volume and shape of container [pickup_height, change_col] = calc_height( reagent = Beads, cross_section_area = multi_well_rack_area, aspirate_volume = transfer_vol * 8, min_height=0.3, extra_volume=10) 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=120, rounds=10, blow_out=True, mix_height=1, post_dispense=True) ctx.comment( 'Aspirate from reservoir column: ' + str(Beads.col)) ctx.comment('Pickup height is ' + str(pickup_height)) move_vol_multichannel(m300, reagent=Beads, source=Beads.reagent_reservoir[Beads.col], dest=kf_destination[i], vol=transfer_vol, air_gap_vol=air_gap_vol, x_offset=x_offset, pickup_height=0.2, disp_height = -8, rinse=rinse, blow_out = True, touch_tip=False, post_airgap=True) '''custom_mix(m300, Beads, work_destinations_cols[i] , vol=70, rounds=10, blow_out=True, mix_height=8, x_offset = x_offset, source_height=0.5, post_dispense=True) m300.drop_tip(home_after=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) ############################################################################ # STEP 9: PREMIX BEADStwo ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') if not m300.hw_pipette['has_tip']: pick_up(m300) ctx.comment('Tip picked up') ctx.comment('Mixing ' + Beadstwo.name) # Mixing custom_mix(m300, Beadstwo, Beadstwo.reagent_reservoir[Beadstwo.col], vol=80, rounds=10, blow_out=True, mix_height=5, post_dispense=True, source_height=0.7) ctx.comment('Finished premixing!') ctx.comment('Now, reagents will be transferred to deepwell plate.') 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 10: TRANSFER BEADStwo ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: # Transfer parameters start = datetime.now() ctx.comment('###############################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') beads_transfer_vol = [beads_vol] # Two rounds of 130 rinse = True for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(beads_transfer_vol): if (i == 0 and j == 0): rinse = True else: rinse = False # Calculate pickup_height based on remaining volume and shape of container [pickup_height, change_col] = calc_height( reagent = Beadstwo, cross_section_area = 15, aspirate_volume = transfer_vol , min_height=0.7, extra_volume=0) # I will consider only aspiration from one well pickup_height=0.7 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(Beadstwo.col)) custom_mix(m300, Beadstwo, Beadstwo.reagent_reservoir[Beadstwo.col], vol=100, rounds=10, blow_out=True, mix_height=0.7, post_dispense=True) ctx.comment( 'Aspirate from reservoir column: ' + str(Beadstwo.col)) ctx.comment('Pickup height is ' + str(pickup_height)) move_vol_multichannel(m300, reagent=Beadstwo, source=Beadstwo.reagent_reservoir[Beadstwo.col], dest=kf_destination[i], vol=transfer_vol, air_gap_vol=air_gap_vol, x_offset=x_offset, pickup_height=pickup_height, disp_height = -8, rinse=rinse, blow_out = True, touch_tip=False, post_airgap=True) '''custom_mix(m300, Beads, work_destinations_cols[i] , vol=70, rounds=10, blow_out=True, mix_height=8, x_offset = x_offset, source_height=0.5, post_dispense=True) m300.drop_tip(home_after=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 if not ctx.is_simulating(): 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 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): STEP = 0 STEPS = { # Dictionary with STEP activation, description and times 1: { 'Execute': True, 'description': 'Dispensar Proteinasa K (' + str(PK_VOL_PER_SAMPLE) + 'ul)' }, 2: { '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, reagent_reservoir_volume, delay, num_wells): 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.vol_well = 0 self.unused = [] self.vol_well_original = reagent_reservoir_volume / num_wells # Reagents and their characteristics Samples = Reagent( name='Samples', flow_rate_aspirate=25, flow_rate_dispense=100, rinse=False, delay=0, reagent_reservoir_volume=0, num_wells=96, ) Pk = Reagent(name='Pk', rinse=False, flow_rate_aspirate=3, flow_rate_dispense=3, reagent_reservoir_volume=1800, num_wells=1, 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 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, touch_tip=False, num_shakes=0): pipette.aspirate((len(dest) * volume) + extra_dispensal, src.bottom(pickup_height)) if touch_tip: pipette.touch_tip(speed=20, v_offset=-5) for d in dest: drop = d.top(z=disp_height) pipette.dispense(volume, drop) shake_pipet(pipette, rounds=num_shakes, v_offset=disp_height) 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, 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') pk_rack = ctx.load_labware('opentrons_24_tuberack_nest_1.5ml_screwcap', '3', 'opentrons_24_tuberack_nest_1.5ml_screwcap') #################################### # 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'] ] tips300 = [ ctx.load_labware('opentrons_96_tiprack_300ul', slot) for slot in ['7'] ] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate Pk.reagent_reservoir = pk_rack.rows()[0][0] # A1 ################################################################################ # 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] # Divide destination wells in small groups for P300 pipette dests = list(divide_destinations(destinations, size_transfer)) p1000 = ctx.load_instrument('p1000_single_gen2', 'left', tip_racks=tips1000) # load P1000 pipette p300 = ctx.load_instrument('p300_single_gen2', mount='right', tip_racks=tips300) # used tip counter and set maximum tips available tip_track = { 'counts': { p1000: 0, p300: 0 }, 'maxes': { p1000: 96 * len(p1000.tip_racks), p300: 96 * len(p300.tip_racks) }, #96 tips per tiprack * number or tipracks in the layout 'num_refills': { p1000: 0, p300: 0 }, 'tips': { p1000: [tip for rack in tips1000 for tip in rack.rows()[0]], p300: [tip for rack in tips300 for tip in rack.rows()[0]] } } start_run() ############################################################################ # STEP 1: DISPENSAR PK ############################################################################ 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: ctx.comment( 'dest length ---------------------------------------------- ' + str(len(dest))) aspirate_volume = PK_VOL_PER_SAMPLE * len(dest) + extra_dispensal used_vol_temp = distribute_custom(p300, volume=PK_VOL_PER_SAMPLE, src=Pk.reagent_reservoir, dest=dest, touch_tip=False, waste_pool=Pk.reagent_reservoir, pickup_height=0.2, extra_dispensal=extra_dispensal, dest_x_offset=0, disp_height=-15, num_shakes=PK_SAKES) 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: 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=5, 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(ctx: protocol_api.ProtocolContext): # load labware and modules tempdeck = ctx.load_module('tempdeck', '1') elution_plate = tempdeck.load_labware( 'opentrons_96_aluminumblock_nest_wellplate_100ul', 'cooled elution plate') reagent_res1 = ctx.load_labware('nest_12_reservoir_15ml', '2', 'reagent reservoir 1') magdeck = ctx.load_module('magdeck', '4') magplate = magdeck.load_labware('usascientific_96_wellplate_2.4ml_deep') reagent_res2 = ctx.load_labware('nest_12_reservoir_15ml', '5', 'reagent reservoir 2') waste = ctx.load_labware('nest_1_reservoir_195ml', '7', 'waste reservoir').wells()[0].top() tips300 = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', slot, '300µl tiprack') for slot in ['3', '6', '8', '9', '10', '11'] ] # reagents and samples num_cols = math.ceil(NUM_SAMPLES / 8) mag_samples_m = [ well for well in magplate.rows()[0][0::2] + magplate.rows()[0][1::2] ][:num_cols] elution_samples_m = [ well for well in elution_plate.rows()[0][0::2] + magplate.rows()[0][1::2] ][:num_cols] viral_dna_rna_buff = reagent_res1.wells()[:3] beads = reagent_res1.wells()[3] wash_1 = reagent_res1.wells()[4:8] wash_2 = reagent_res1.wells()[8:] etoh = reagent_res2.wells()[:8] water = reagent_res2.wells()[-1] # pipettes m300 = ctx.load_instrument('p300_multi', 'left', tip_racks=tips300) m300.flow_rate.aspirate = 150 m300.flow_rate.dispense = 300 tip_counts = {m300: 0} tip_maxes = {m300: len(tips300) * 12} def pick_up(pip): nonlocal tip_counts if tip_counts[pip] == tip_maxes[pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_counts[pip] = 0 tip_counts[pip] += 1 pip.pick_up_tip() def remove_supernatant(vol): m300.flow_rate.aspirate = 30 num_trans = math.ceil(vol / 270) vol_per_trans = vol / num_trans for i, m in enumerate(mag_samples_m): side = -1 if i < 6 == 0 else 1 loc = m.bottom(0.5).move(Point(x=side * 2)) if not m300.hw_pipette['has_tip']: pick_up(m300) for _ in range(num_trans): m300.move_to(m.center()) m300.transfer(vol_per_trans, loc, waste, new_tip='never', air_gap=30) m300.blow_out(waste) m300.drop_tip() m300.flow_rate.aspirate = 150 # transfer viral DNA/RNA buffer for i, m in enumerate(mag_samples_m): pick_up(m300) m300.transfer(400, viral_dna_rna_buff[i // 4], m.top(), new_tip='never') m300.mix(10, 200, m) m300.blow_out(m.top(-2)) m300.drop_tip() # premix, transfer, and mix magnetic beads with sample for i, m in enumerate(mag_samples_m): pick_up(m300) if i == 0: for _ in range(20): m300.aspirate(200, beads.bottom(3)) m300.dispense(200, beads.bottom(20)) m300.transfer(20, beads, m, new_tip='never') m300.mix(10, 200, m) m300.blow_out(m.top(-2)) m300.drop_tip() # incubate on magnet magdeck.engage() ctx.delay(minutes=3, msg='Incubating on magnet for 3 minutes.') # remove supernatant remove_supernatant(630) magdeck.disengage() for wash in [wash_1, wash_2]: # transfer and mix wash for i, m in enumerate(mag_samples_m): pick_up(m300) side = 1 if i < 6 == 0 else -1 loc = m.bottom(0.5).move(Point(x=side * 2)) m300.transfer(500, wash[i // 3], m.top(), new_tip='never') m300.mix(10, 200, loc) m300.blow_out(m.top(-2)) m300.drop_tip() # incubate on magnet magdeck.engage() ctx.delay(minutes=3, msg='Incubating on magnet for 3 minutes.') # remove supernatant remove_supernatant(510) magdeck.disengage() # EtOH washes for wash in range(2): # transfer and mix wash etoh_set = etoh[wash * 4:wash * 4 + 4] pick_up(m300) m300.transfer(500, etoh_set[i // 3], [m.top(3) for m in mag_samples_m], new_tip='never') ctx.delay(seconds=30, msg='Incubating in EtOH for 30 seconds.') # remove supernatant remove_supernatant(510) if wash == 1: ctx.delay(minutes=10, msg='Airdrying on magnet for 10 minutes.') magdeck.disengage() # transfer and mix water for m in mag_samples_m: pick_up(m300) side = 1 if i < 6 == 0 else -1 loc = m.bottom(0.5).move(Point(x=side * 2)) m300.transfer(50, water, m.top(), new_tip='never') m300.mix(10, 30, loc) m300.blow_out(m.top(-2)) m300.drop_tip() # incubate on magnet magdeck.engage() ctx.delay(minutes=3, msg='Incubating on magnet for 3 minutes.') # transfer elution to clean plate m300.flow_rate.aspirate = 30 for s, d in zip(mag_samples_m, elution_samples_m): pick_up(m300) side = -1 if i < 6 == 0 else 1 loc = s.bottom(0.5).move(Point(x=side * 2)) m300.transfer(50, loc, d, new_tip='never') m300.blow_out(d.top(-2)) m300.drop_tip() m300.flow_rate.aspirate = 150
def run(ctx: protocol_api.ProtocolContext): global MM_TYPE # check source (elution) labware type source_plate = ctx.load_labware( 'opentrons_96_aluminumblock_nest_wellplate_100ul', '1', 'chilled elution plate on block from Station B') tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['3', '6', '8', '9', '10', '11'] ] tips300 = [ctx.load_labware('opentrons_96_filtertiprack_200ul', '2')] tempdeck = ctx.load_module('Temperature Module Gen2', '4') pcr_plate = tempdeck.load_labware( 'opentrons_96_aluminumblock_nest_wellplate_100ul', 'PCR plate') mm_strips = ctx.load_labware( 'opentrons_96_aluminumblock_nest_wellplate_100ul', '7', 'mastermix strips') tempdeck.set_temperature(4) tube_block = ctx.load_labware( 'opentrons_24_aluminumblock_nest_2ml_screwcap', '5', '2ml screw tube aluminum block for mastermix + controls') # pipette m20 = ctx.load_instrument('p20_multi_gen2', 'right', tip_racks=tips20) p300 = ctx.load_instrument('p300_single_gen2', 'left', tip_racks=tips300) # setup up sample sources and destinations num_cols = math.ceil(NUM_SAMPLES / 8) sources = source_plate.rows()[0][:num_cols] sample_dests = pcr_plate.rows()[0][:num_cols] tip_log = {'count': {}} folder_path = '/data/C' 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 'tips20' in data: tip_log['count'][m20] = data['tips20'] else: tip_log['count'][m20] = 0 if 'tips300' in data: tip_log['count'][p300] = data['tips300'] else: tip_log['count'][p300] = 0 else: tip_log['count'] = {m20: 0, p300: 0} else: tip_log['count'] = {m20: 0, p300: 0} tip_log['tips'] = { m20: [tip for rack in tips20 for tip in rack.rows()[0]], p300: [tip for rack in tips300 for tip in rack.wells()] } tip_log['max'] = {pip: len(tip_log['tips'][pip]) for pip in [m20, p300]} 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 """ mastermix component maps """ mm_tube = tube_block.wells()[0] mm_dict = { 'volume': 15, 'components': { tube: vol for tube, vol in zip(tube_block.columns()[1][:3], [6.25, 1.25, 7.5]) } } vol_overage = 1.2 if NUM_SAMPLES > 48 else 1.1 # decrease overage for small sample number total_mm_vol = mm_dict['volume'] * (NUM_SAMPLES + 2) * vol_overage # translate total mastermix volume to starting height r = mm_tube.diameter / 2 mm_height = total_mm_vol / (math.pi * (r**2)) - 5 def h_track(vol): nonlocal mm_height dh = 1.1 * vol / (math.pi * (r**2)) # compensate for 10% theoretical volume loss mm_height = mm_height - dh if mm_height - dh > 2 else 2 # stop at 2mm above mm tube bottom return mm_tube.bottom(mm_height) if PREPARE_MASTERMIX: vol_overage = 1.2 if NUM_SAMPLES > 48 else 1.1 for i, (tube, vol) in enumerate(mm_dict['components'].items()): comp_vol = vol * (NUM_SAMPLES) * vol_overage pick_up(p300) num_trans = math.ceil(comp_vol / 160) vol_per_trans = comp_vol / num_trans for _ in range(num_trans): p300.air_gap(20) p300.aspirate(vol_per_trans, tube) ctx.delay(seconds=3) p300.touch_tip(tube) p300.air_gap(20) p300.dispense(20, mm_tube.top()) # void air gap p300.dispense(vol_per_trans, mm_tube.bottom(2)) p300.dispense(20, mm_tube.top()) # void pre-loaded air gap p300.blow_out(mm_tube.top()) p300.touch_tip(mm_tube) if i < len(mm_dict['components'].items( )) - 1: # only keep tip if last component and p300 in use p300.drop_tip() mm_total_vol = mm_dict['volume'] * (NUM_SAMPLES) * vol_overage if not p300.hw_pipette[ 'has_tip']: # pickup tip with P300 if necessary for mixing pick_up(p300) mix_vol = mm_total_vol / 2 if mm_total_vol / 2 <= 200 else 200 # mix volume is 1/2 MM total, maxing at 200µl mix_loc = mm_tube.bottom(20) if NUM_SAMPLES > 48 else mm_tube.bottom(5) p300.mix(7, mix_vol, mix_loc) p300.blow_out(mm_tube.top()) p300.touch_tip() # transfer mastermix to strips mm_strip = mm_strips.columns()[0] if not p300.hw_pipette['has_tip']: pick_up(p300) for i, well in enumerate(mm_strip): if NUM_SAMPLES % 8 == 0 or i < NUM_SAMPLES % 8: vol = num_cols * mm_dict['volume'] * ((vol_overage - 1) / 2 + 1) else: vol = (num_cols - 1) * mm_dict['volume'] * ( (vol_overage - 1) / 2 + 1) p300.transfer(vol, mm_tube, well, new_tip='never') p300.drop_tip() # transfer mastermix to plate mm_vol = mm_dict['volume'] pick_up(m20) m20.transfer(mm_vol, mm_strip[0].bottom(0.5), sample_dests, new_tip='never') m20.drop_tip() # transfer samples to corresponding locations for s, d in zip(sources, sample_dests): pick_up(m20) m20.transfer(SAMPLE_VOL, s.bottom(2), d.bottom(2), new_tip='never') m20.mix(1, 10, d.bottom(2)) m20.blow_out(d.top(-2)) m20.aspirate( 5, d.top(2)) # suck in any remaining droplets on way to trash m20.drop_tip() # track final used tip if TIP_TRACK and not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) data = { 'tips20': tip_log['count'][m20], 'tips300': tip_log['count'][p300] } with open(tip_file_path, 'w') as outfile: json.dump(data, outfile)
def run(ctx: protocol_api.ProtocolContext): [ _num_col, _filter_tips, _mbl5_vol, _mag_height, _m300_mount ] = get_values( # noqa: F821 (<--- DO NOT REMOVE!) "_num_col", "_filter_tips", "_mbl5_vol", "_mag_height", "_m300_mount") # VARIABLES num_col = _num_col filter_tips = _filter_tips m300_mount = _m300_mount mag_height = _mag_height mbl5_vol = int(_mbl5_vol) # load module mag_mod = ctx.load_module('magnetic module gen2', '1') # load labware mag_plate = mag_mod.load_labware('96_squarewell_block_macherey_nagel') res1 = ctx.load_labware('nest_12_reservoir_15ml', '3') res2 = ctx.load_labware('nest_12_reservoir_15ml', '6') waste_res = res2 = ctx.load_labware('nest_12_reservoir_15ml', '9') elute_plate = ctx.load_labware('abgene_96_wellplate_700ul', '2') # load tip_racks tipracks = [ctx.load_labware('opentrons_96_filtertiprack_200ul' if filter_tips else 'opentrons_96_tiprack_300ul', slot) for slot in ['4', '5', '7', '8', '10', '11']] # load instrument m300 = ctx.load_instrument('p300_multi_gen2', m300_mount, tip_racks=tipracks) def pick_up(): try: m300.pick_up_tip() except protocol_api.labware.OutOfTipsError: ctx.pause("Replace all tip racks on Slots 7, 8, 10, and 11") m300.reset_tipracks() m300.pick_up_tip() waste_vol_ctr = 0 waste_well_ctr = 0 def remove_supernatant(): nonlocal waste_vol_ctr nonlocal waste_well_ctr ctx.comment('\n\n\nREMOVING SUPERNATANT\n') for i, col in enumerate(sample_cols): side = -1 if i % 2 == 0 else 1 aspirate_loc = col.bottom(1).move( Point(x=(col.length/2-2)*side)) pick_up() for _ in range(2): m300.aspirate(200 if filter_tips else 300, aspirate_loc, rate=0.6) # noqa E501 m300.dispense(200 if filter_tips else 300, waste_res.wells()[waste_well_ctr]) # noqa E501 m300.blow_out(waste_res.wells()[waste_well_ctr].top()) waste_vol_ctr += 200 if waste_vol_ctr >= 12000: waste_vol_ctr = 0 waste_well_ctr += 1 m300.drop_tip() # map liquids num_samp = num_col*8 bead_buffer_wells = math.ceil(num_samp*162.5/10000) mbl3_wells = math.ceil(num_samp*800/10000) eth_wells = math.ceil(num_samp*400/10000) prok = res1.wells()[0] bead_buffer = res1.wells()[1:1+bead_buffer_wells]*num_col mbl3 = res1.wells()[4:4+mbl3_wells]*num_col ethanol = res2.wells()[:eth_wells]*num_col mbl5 = res2.wells()[-1] sample_cols = mag_plate.rows()[0][:num_col] ctx.comment('\n\n\nDISPENSING PRO-K\n') for col in sample_cols: pick_up() m300.aspirate(50, prok) m300.dispense(50, col) m300.mix(5, 90, col) m300.drop_tip() ctx.comment('\n\n\nDISPENSING BEAD BUFFER\n') airgap = 20 for reagent_col, col in zip(bead_buffer, sample_cols): pick_up() m300.mix(5, 200 if filter_tips else 300, reagent_col, rate=2.0) m300.aspirate(162.5, reagent_col) m300.air_gap(airgap) m300.dispense(162.5+airgap, col) m300.mix(5, 150, col) m300.air_gap(airgap) m300.drop_tip() mag_mod.engage(height_from_base=mag_height) ctx.delay(minutes=3) remove_supernatant() mag_mod.disengage() ctx.comment('\n\n\nDISPENSING MBL3\n') for reagent_col, col in zip(mbl3, sample_cols): pick_up() m300.aspirate(200, reagent_col) m300.dispense(200, col.top()) m300.aspirate(200, reagent_col) m300.dispense(200, col) m300.mix(5, 200 if filter_tips else 300, col) m300.drop_tip() mag_mod.engage(height_from_base=mag_height) ctx.delay(minutes=3) remove_supernatant() mag_mod.disengage() ctx.comment('\n\n\nDISPENSING ETHANOL\n') for reagent_col, col in zip(ethanol, sample_cols): pick_up() m300.aspirate(200, reagent_col) m300.dispense(200, col.top()) m300.aspirate(200, reagent_col) m300.dispense(200, col) m300.mix(5, 200 if filter_tips else 300, col) m300.drop_tip() mag_mod.engage(height_from_base=mag_height) ctx.delay(minutes=3) remove_supernatant() ctx.delay(minutes=20) mag_mod.disengage() ctx.comment('\n\n\nDISPENSING MBL5\n') for col in sample_cols: pick_up() m300.aspirate(mbl5_vol, mbl5) m300.dispense(mbl5_vol, col) m300.mix(5, 0.6*mbl5_vol, col) m300.drop_tip() mag_mod.engage(height_from_base=mag_height) for i, (sample_col, elute_col) in enumerate( zip(sample_cols, elute_plate.rows()[0])): side = -1 if i % 2 == 0 else 1 aspirate_loc = sample_col.bottom(1).move( Point(x=(sample_col.length/2-2)*side)) pick_up() m300.aspirate(mbl5_vol, aspirate_loc, rate=0.6) m300.dispense(mbl5_vol, elute_col) m300.drop_tip()
def run(protocol: protocol_api.ProtocolContext): # LABWARE fuge_rack = protocol.load_labware('vwr_24_tuberack_1500ul', '1') std_rack = protocol.load_labware('vwr_24_tuberack_1500ul', '2') tiprack300 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '8') tiprack20 = protocol.load_labware('opentrons_96_filtertiprack_20ul', '9') tempdeck = protocol.load_module( 'tempdeck', '10') # have this so I don't have to move it off holder_1 = protocol.load_labware( '8wstriptubesonfilterracks_96_aluminumblock_250ul', '3') holder_2 = protocol.load_labware( '8wstriptubesonfilterracks_96_aluminumblock_250ul', '6') stds_plate = tempdeck.load_labware('abi_96_wellplate_250ul') # PIPETTES p300 = protocol.load_instrument('p300_single_gen2', 'left', tip_racks=[tiprack300]) p20 = protocol.load_instrument('p20_single_gen2', 'right', tip_racks=[tiprack20]) # REAGENTS std_1 = std_rack['A1'] # 900ul Water std_2 = std_rack['A2'] # 900ul water std_3 = std_rack['A3'] # 900ul water std_4 = std_rack['A4'] # 900ul water std_5 = std_rack['A5'] # 900ul water std_6 = std_rack['A6'] # 900ul water std_7 = std_rack['B1'] # 900ul water std_8 = std_rack['B2'] # 900ul water std_9 = std_rack['B3'] # 900ul water std_10 = std_rack['B4'] # 900ul water std_11 = std_rack['B5'] # 900ul water std_12 = std_rack['B6'] # 900ul water std_13 = std_rack['C1'] # 900ul water std_14 = std_rack['C2'] # 900ul water std_15 = std_rack['C3'] # 900ul water pos_control = fuge_rack['A1'] # 100-1000ul pos control @1uM mmp_tube = fuge_rack['A2'] #500 uL of master mixs and 400uL of primers waste = fuge_rack['D6'] # waste water = fuge_rack['A3'] # 100 uL water # LISTS std_wells = [std_1, std_2, std_3, std_4, std_5, std_6, std_7, std_8, std_9] std_conc = [std_5, std_6, std_7, std_8, std_9] cols = [1, 3, 5, 7, 9, 11] rows = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] ### COMMANDS ###### # Make std dilution series # Make 10nM pos control, std_1 p300.transfer( 100, pos_control.bottom(2), #1uM std_1.bottom(20), mix_after=(3, 200), # remove residual fluid from tip touch_tip=False) # serial dilutions in microfuge tubes, 10% diliutions for i in range(len(std_wells) - 1): h_mix = 20 p300.pick_up_tip() p300.mix(2, 200, std_wells[i].bottom(8)) # mix low p300.mix(2, 200, std_wells[i].bottom(14)) # mix mid p300.mix(5, 200, std_wells[i].bottom(h_mix)) #mix hi p300.aspirate(100, std_wells[i].bottom(h_mix), rate=0.4) p300.touch_tip() p300.dispense( 100, std_wells[i + 1].bottom(14)) # better mixing with mid dispense p300.blow_out( std_wells[i + 1].bottom(h_mix)) # blow out just below the surface p300.drop_tip() if i == len(std_wells) - 2: # last tube p300.pick_up_tip() p300.mix(2, 200, std_wells[i + 1].bottom(8)) # mix low p300.mix(2, 200, std_wells[i + 1].bottom(14)) # mix mid p300.mix(5, 200, std_wells[i + 1].bottom(h_mix)) #mix hi p300.blow_out( std_wells[i + 1].bottom(h_mix)) # blow out just below the surface p300.drop_tip() #add master mix and primers to PRC tubes for col in cols: p20.pick_up_tip() for row in rows: p20.aspirate(18, mmp_tube) p20.move_to(mmp_tube.top()) protocol.delay(seconds=2) p20.touch_tip(v_offset=-5) p20.dispense(18, holder_1[row + str(col)]) p20.drop_tip() #add first 4 standards to upper half of tubes count = 0 # keep track of standard for row in rows: p20.pick_up_tip() p20.aspirate(14, std_conc[count]) #take from standand p20.touch_tip() for col in cols: p20.dispense(2, holder_1[row + str(col)]) # dispense in PCR tubes p20.touch_tip() p20.dispense(2, waste) p20.blow_out(waste.bottom()) p20.drop_tip() count = count + 1 if count == 4: break #add first 4 standards to lower half of tubes count = 0 #reset count for row in rows[4:]: p20.pick_up_tip() p20.aspirate(8, std_conc[count]) p20.touch_tip() for col in cols[0:3]: p20.dispense(2, holder_1[row + str(col)]) p20.touch_tip() p20.dispense(2, waste) p20.blow_out(waste.bottom()) p20.drop_tip() count = count + 1 if count == 4: break #add final standard for row in rows[4:7]: p20.pick_up_tip() p20.aspirate(20, std_conc[count]) p20.touch_tip() for col in cols[3:]: p20.dispense(2, holder_1[row + str(col)]) p20.touch_tip() p20.dispense(2, waste) p20.blow_out(waste.bottom()) p20.drop_tip() # add water to last 3 wells for row in rows[7:8]: p20.pick_up_tip() p20.aspirate(8, water) p20.touch_tip() for col in cols[3:]: p20.dispense(2, holder_1[row + str(col)]) p20.touch_tip() p20.dispense(2, waste) p20.blow_out(waste.bottom()) p20.drop_tip()
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. """ # ============================================================================= # =====================LOADING LABWARE AND PIPETTES============================ # ============================================================================= tips_200 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware def 10, #deck position '200tips') #custom name 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 ##### Loading pipettes p300 = protocol.load_instrument( 'p300_single_gen2', #instrument definition 'right', #mount position tip_racks=[tips_200]) #assigned tiprack # ============================================================================= # ===================================INITIALIZING============================== # ============================================================================= protocol.set_rail_lights(True) p300.starting_tip = tips_200.well('C1') #!!! p300.flow_rate.aspirate = 300 p300.flow_rate.dispense = 300 # ============================================================================= # =============================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. ## source_well_1 = stock_tubes['A1'] source_well_2 = stock_tubes['A2'] source_well_3 = stock_tubes['B1'] source_well_4 = stock_tubes['B2'] ## Where do you place the 50mL tube # ============================================================================= # ==========================PREDIFINED VARIABLES=============================== # ============================================================================= 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. ## # ============================================================================= ##### 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. ## # ============================================================================= # ==================================PROTOCOL=================================== # ============================================================================= # 1 x 50mL tube================================================================ p300.pick_up_tip() for well in aliquot_tubes_1.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_1.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() start_height = current_height p300.pick_up_tip() for well in aliquot_tubes_2.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_1.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() # ============================================================================= # 2 x 50mL tube================================================================ # reset variables start_vol = start_vol start_height = vt.cal_start_height(container, start_vol) current_height = start_height p300.pick_up_tip() for well in aliquot_tubes_3.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_2.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() start_height = current_height p300.pick_up_tip() for well in aliquot_tubes_4.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_2.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() # ============================================================================= # 3 x 50mL tube================================================================ # reset variables start_vol = start_vol start_height = vt.cal_start_height(container, start_vol) current_height = start_height p300.pick_up_tip() for well in aliquot_tubes_5.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_3.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() start_height = current_height p300.pick_up_tip() for well in aliquot_tubes_6.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_3.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() # ============================================================================= # 4 x 50mL tube================================================================ # reset variables start_vol = start_vol start_height = vt.cal_start_height(container, start_vol) current_height = start_height p300.pick_up_tip() for well in aliquot_tubes_7.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_4.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() start_height = current_height p300.pick_up_tip() for well in aliquot_tubes_8.wells(): for i in range(5): ## Pipette 5 x 200µL for 1mL aliquots current_height, delta_height = vt.volume_tracking( container, 200, 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: protocol.pause("the 50mL tube is empty!") else: aspiration_location = source_well_4.bottom(pip_height) ## 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. ## ## 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. ## p300.aspirate(200, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(200, well.top(z=-2)) ## Dispense 200µL in the destination well p300.drop_tip() # ============================================================================= # ============================================================================= protocol.set_rail_lights(False) # =============================================================================
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): 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', '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 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( 'nest_12_reservoir_15ml', '11', '12-channel reservoir for binding buffer') # binding_buffer = ctx.load_labware( # 'biorad_96_wellplate_200ul_pcr', '11', # '50ml tuberack for lysis buffer + PK (tube A1)').wells()[1] tipracks300 = [ ctx.load_labware('opentrons_96_tiprack_300ul', slot, '300µ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) m300 = ctx.load_instrument('p300_multi_gen2', 'right', tip_racks=tipracks300) # 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 'tips300' in data: tip_log['count'][m300] = data['tips300'] 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 tipracks300 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 [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 = {binding_buffer: TUBE50_VOlUME * 1} # radius = (binding_buffer.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] m300.flow_rate.aspirate = 94 m300.flow_rate.dispense = 94 m300.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(m300) # 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 # m300.flow_rate.aspirate = 100 # m300.flow_rate.dispense = 100 # m300.dispense(500, source.bottom(h+20)) # for _ in range(4): # # m300.air_gap(500) # m300.aspirate(500, source.bottom(h)) # m300.dispense(500, source.bottom(h+20)) pick_up(m300) num_trans = math.ceil(BB_VOLUME / 210) vol_per_trans = BB_VOLUME / num_trans for i, m in enumerate(dests_multi): source = binding_buffer.wells()[i // 4] for i in range(num_trans): if i == 0: m300.mix(MIX_REPETITIONS, MIX_VOLUME, source) m300.transfer(vol_per_trans, source, m, air_gap=20, new_tip='never') m300.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 = { 'tips300': tip_log['count'][m300], 'tips20': tip_log['count'][s20] } with open(tip_file_path, 'w') as outfile: json.dump(data, outfile)
def run(ctx: protocol_api.ProtocolContext): ctx.comment('Columnas a utilizar: ' + str(num_cols)) # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: { 'Execute': True, 'description': 'Transferir muestras a la placa PCR' } } 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_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 Samples = Reagent( name='Samples', rinse=False, flow_rate_aspirate=sample_aspirate_rate, flow_rate_dispense=sample_dispense_rate, reagent_reservoir_volume=50, delay=0, num_wells=num_cols, # num_cols comes from available columns h_cono=0, v_fondo=0) Samples.vol_well = Samples.vol_well_original ################## # Custom functions def log_parameters(): ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VALORES DE VARIABLES') ctx.comment(' ') ctx.comment('Número de muestras: ' + str(NUM_SAMPLES)) ctx.comment('Volumen a transferir a la placa PCR: ' + str(VOLUME_PCR_SAMPLE) + ' ul') ctx.comment('Foto-sensible: ' + str(PHOTOSENSITIVE)) ctx.comment('###############################################') ctx.comment(' ') 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(): 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) ctx._hw_manager.hardware.set_lights(button=True, rails=False) ctx.comment('Puntas de 20 ul utilizadas: ' + str(tip_track['counts'][p20]) + ' (' + str(round(tip_track['counts'][p20] / 96, 2)) + ' caja(s))') ctx.comment('###############################################') return finish_time def log_step_start(): ctx.comment(' ') ctx.comment('###############################################') ctx.comment('PASO ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') ctx.comment(' ') return datetime.now() def log_step_end(start): end = datetime.now() time_taken = (end - start) STEPS[STEP]['Time:'] = str(time_taken) ctx.comment(' ') ctx.comment('Paso ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' hizo un tiempo de ' + str(time_taken)) ctx.comment(' ') def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip, v_offset=-5, radius=0.5): ''' 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=v_offset, radius=radius) 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 #################################### # load labware and modules ################################## # Sample plate - comes from B #source_plate1 = ctx.load_labware( # 'nest_96_wellplate_100ul_pcr_full_skirt', '1', # 'NEST 96 Well Plate 100 uL PCR Full Skirt') source_plate1 = ctx.load_labware( 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', '1', 'Opentrons 96 Well Aluminum Block with Generic PCR Strip 200 µL') source_plate2 = ctx.load_labware( 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', '2', 'Opentrons 96 Well Aluminum Block with Generic PCR Strip 200 µL') samples_source1 = source_plate1.columns()[ 0::2] # Select odd columns from source plate samples_source2 = source_plate2.columns()[ 0::2] # Select odd columns from source plate samples_source1 = [ well for columns in samples_source1 for well in columns ] # list of lists to list samples_source2 = [well for columns in samples_source2 for well in columns] samples = samples_source1 + samples_source2 samples = samples[:NUM_SAMPLES] ################################## # qPCR plate - final plate, goes to PCR qpcr_plate = ctx.load_labware( 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', '4', 'Opentrons 96 Well Aluminum Block with Generic PCR Strip 200 µL') ################################## # Load Tipracks tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['7'] ] ################################################################################ # setup up sample sources and destinations pcr_wells = qpcr_plate.wells()[pcr_plate_well_offset:NUM_SAMPLES + pcr_plate_well_offset] # pipettes p20 = ctx.load_instrument('p20_single_gen2', mount='right', tip_racks=tips20) # used tip counter and set maximum tips available tip_track = {'counts': {p20: 0}, 'maxes': {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('Reemplaza las cajas de puntas de ' + str(pip.max_volume) + 'µl antes \ de continuar.') pip.reset_tipracks() tip_track['counts'][pip] = 0 if not pip.hw_pipette['has_tip']: pip.pick_up_tip() ########## log_parameters() start_run() ############################################################################ # STEP 1: TRANSFER SAMPLES ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = log_step_start() for source, dest in zip(samples, pcr_wells): pick_up(p20) move_vol_multichannel(p20, reagent=Samples, source=source, dest=dest, vol=VOLUME_PCR_SAMPLE + 5, air_gap_vol=air_gap_pcr_sample, x_offset=x_offset, pickup_height=0.1, disp_height=pcr_disp_height, v_offset=pcr_disp_height, rinse=False, blow_out=True, touch_tip=dispense_touch_tip, radius=1) if dispense_touch_tip == False: p20.aspirate(air_gap_vol) if recycle_tip: p20.return_tip() else: p20.drop_tip(home_after=False) tip_track['counts'][p20] += 1 log_step_end(start) ############################################################################ finish_run()
def run(ctx: protocol_api.ProtocolContext): # ------------------------ # Load LabWare # ------------------------ # Tip racks tips = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot, '20µl filter tiprack') for slot in ['10', '11'] ] # Pipette p20 = ctx.load_instrument('p20_single_gen2', 'right', tip_racks=tips) m20 = ctx.load_instrument('p20_multi_gen2', 'left', tip_racks=tips) # Modules tempdeck = ctx.load_module('temperature module', '1') tempdeck.set_temperature(4) # Mastermix mastermix = tempdeck.load_labware( 'opentrons_24_aluminumblock_generic_2ml_screwcap') mastermix_sources = mastermix.wells()[:3] mastermix_volumes = [1200, 485, 485] mastermix_sources = zip(mastermix_sources, mastermix_volumes) mastermix_tube = mastermix.wells()[3] # Destination 1 (and source for the 2nd destination) dest_plate1 = ctx.load_labware('abi_fast_qpcr_96_alum_opentrons_100ul', '2', 'PCR plate') dest_1 = dest_plate1.columns()[0][:num_rows] source_2 = dest_plate1.wells()[0] # Destination 2 (destination of 'dest_plate1') dest_plate2 = ctx.load_labware('abi_fast_qpcr_96_alum_opentrons_100ul', '3', 'PCR plate') dest_2 = dest_plate2.rows()[0][:num_cols] samples_destinations = dest_plate2.wells()[:num_destinations] # Muestras rack_num = math.ceil( num_destinations / NUM_OF_SOURCES_PER_RACK ) if num_destinations < MAX_NUM_OF_SOURCES else MIN_NUM_OF_SOURCES source_racks = [ ctx.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', slot, 'source tuberack with screwcap' + str(i + 1)) for i, slot in enumerate(['8', '9', '5', '6'][:rack_num]) ] sample_sources_full = common.generate_source_table(source_racks) sample_sources = sample_sources_full[:num_destinations] mov = zip(sample_sources, samples_destinations) # ------------------ # Protocol # ------------------ # Hacemos la mastermix for s, v in mastermix_sources: if not p20.hw_pipette['has_tip']: common.pick_up(p20) common.move_vol_multichannel(ctx, p20, reagent=buffer, source=s, dest=mastermix_tube, vol=v, air_gap_vol=air_gap_vol_sample, pickup_height=pickup_height, disp_height=dispense_height, x_offset=x_offset, blow_out=True, touch_tip=True) # Drop pipette tip p20.drop_tip() # TODO: preguntar si hace falta tirar la punta o no if not p20.hw_pipette['has_tip']: common.pick_up(p20) common.custom_mix(p20, reagent=buffer, location=mastermix_tube, vol=vol_to_mix, rounds=rounds, blow_out=True, mix_height=dispense_height, x_offset=x_offset, source_height=dispense_height) # Dispensamos mastermix en tira de pcr y con la multi lo propagamos en la placa pcr del slot 3 if not p20.hw_pipette['has_tip']: common.pick_up(p20) for d in dest_1: common.move_vol_multichannel(ctx, p20, reagent=buffer, source=mastermix_tube, dest=d, vol=mastermix_vol_to_move, air_gap_vol=air_gap_vol_sample, x_offset=x_offset, pickup_height=1, disp_height=-10, blow_out=True, touch_tip=False) p20.drop_tip() if not m20.hw_pipette['has_tip']: common.pick_up(m20) for d in dest_2: common.move_vol_multichannel(ctx, m20, reagent=buffer, source=source_2, dest=d, vol=pcr_final_vol, air_gap_vol=air_gap_vol_sample, x_offset=x_offset, pickup_height=1, disp_height=-10, blow_out=True, touch_tip=True) m20.drop_tip() # Transfer samples from eppendorf to pcr plate for s, d in mov: if not p20.hw_pipette['has_tip']: common.pick_up(p20) common.move_vol_multichannel(ctx, p20, reagent=buffer, source=s, dest=d, vol=sample_vol_to_move, air_gap_vol=air_gap_vol_sample, x_offset=x_offset, pickup_height=1, disp_height=-10, blow_out=True, touch_tip=True) 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': 'Mix beads'}, 3: {'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/'+run_id if not ctx.is_simulating(): if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/Station_KB_sample_prep_pathogen_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 * NUM_SAMPLES, num_wells=96, h_cono=1.95, v_fondo=35) Beads = Reagent(name='Magnetic beads and Lysis', flow_rate_aspirate=1, flow_rate_dispense=3, rinse=True, num_wells=math.ceil(NUM_SAMPLES / 32), delay=2, reagent_reservoir_volume=260 * 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, 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): 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 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) ########## # 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 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( 'nest_12_reservoir_15ml', '2', 'Reagent deepwell plate') ################################## # Elution 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 p300 multi pipette m20 = ctx.load_instrument( 'p20_multi_gen2', 'left', tip_racks=tips20) # load p300 single 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 #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.5, 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) ############################################################################ # STEP 2: PREMIX BEADS ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') if not m300.hw_pipette['has_tip']: pick_up(m300) ctx.comment('Tip picked up') ctx.comment('Mixing ' + Beads.name) # Mixing custom_mix(m300, Beads, Beads.reagent_reservoir[Beads.col], vol=180, rounds=10, blow_out=True, mix_height=0, x_offset = x_offset) ctx.comment('Finished premixing!') ctx.comment('Now, reagents will be transferred to deepwell plate.') 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 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 = [130, 130] # Two rounds of 130 rinse = True for i in range(num_cols): if not m300.hw_pipette['has_tip']: pick_up(m300) 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( reagent = Beads, cross_section_area = multi_well_rack_area, aspirate_volume = 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=180, rounds=10, blow_out=True, 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 only the first round 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 = True, touch_tip=False) m300.aspirate(air_gap_vol, work_destinations_cols[i].top(z = -2), rate = Beads.flow_rate_aspirate) m300.dispense(air_gap_vol, Beads.reagent_reservoir[Beads.col].top()) ctx.comment('Mixing MS with beads ') 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): """ Aliquoting Accustart toughmix with EvaGreen added 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) or 1.5mL tubes (with 10 uM F or 10 uM R) to 96-wells plate (with mastermix). """ # ============================================================================= # ======================LOADING LABWARE AND PIPETTES=========================== # ============================================================================= ## For available labware see "labware/list_of_available_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 5, #deck position 'tips_20') #custom name plate_96 = protocol.load_labware('biorad_96_wellplate_200ul_pcr', 4, 'plate_96') primer_tubes = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap', #labware def 1, #deck position 'primer_tubes') #custom name ##### !!! OPTION 1: ROBOT tubes_5mL = protocol.load_labware( 'eppendorfscrewcap_15_tuberack_5000ul', #labware def 2, #deck position '5mL_tubes') #custom name primer_strips = protocol.load_labware( 'pcrstrips_96_wellplate_200ul', #labware definition 7, #deck position 'primer strips') #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) # tubes_5mL = protocol.load_labware_from_definition( # labware_def_5mL, #variable derived from opening json # 2, #deck position # '5mL_tubes') #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 = protocol.load_labware_from_definition( # labware_def_pcrstrips, #variable derived from opening json # 7, #deck position # 'primer_strips') #custom name ##### 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 = 2226 ## 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_label = 3 ## The primer_vol_label is the volume (ul) of barcoded F&R primer that## ## needs to be added to the reactions that get a barcode. ## primer_vol = 1.5 ## The primer_vol is the volume (ul) of NON barcoded F or R primer ## ## that needs to be added to the reactions that do NOT get a barcode. ## p300.starting_tip = tips_200.well('A2') p20.starting_tip = tips_20_1.well('A1') ## The starting_tip is the location of first pipette tip in the box ## # ============================================================================= # ==========================PREDIFINED VARIABLES=============================== # ============================================================================= 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' ## aspiration_vol = dispension_vol + (dispension_vol / 100 * 2) ## The aspiration_vol is the volume (ul) that is aspirated from the ## ## container. ## # ==========================creating mix well list============================= ## This is a list with wells in plate_96 that should be filled with mastermix## mastermix = [] ## Create an empty list to append wells to for the mastermix wells. ## mm_columns = ([ plate_96.columns_by_name()[column_name] for column_name in ['1', '2', '3', '4', '5', '6'] ]) for column in mm_columns: for well in column: mastermix.append(well) ## Separate the columns into wells and append them to the empty ## ## mastermix wells list ## # ======================create list for labeled primers======================== ## This is a list with the wells in primer_strips where the primers should ## ## be pipetted from. This list is used to pipette the labeled primers ## ## to the corresponding wells in plate_96 (automatically first ~3.5 columns)## labeled_primers = [] ## Create an empty list to append wells to for the primer wells. ## labeled_primer_columns = ([ primer_strips.columns_by_name()[column_name] for column_name in ['2', '7'] ]) ## Make a list of columns for the primers, this is a list of lists! ## for column in labeled_primer_columns: for well in column: labeled_primers.append(well) ## Separate the columns into wells and append them to the empty primer## ## wells list ## labeled_primer_wells = ([ primer_strips.wells_by_name()[well_name] for well_name in ['A11', 'B11', 'C11', 'D11', 'E11'] ]) ## Make a list of the remaining primer wells. ## for well in labeled_primer_wells: labeled_primers.append(well) ## Append the wells to the list of primer wells. ## # =======================create list for unlabeled primers===================== ## This is a list with the wells in plate_96, that should be filled with ## ## unlabeled primers (so the fish samples, NTCs and the std dilution series)## ## --> for the wells that do not get labeled primer ## unlabeled_primer_dest = [] ## Create an empty list to append the wells where the unlabeled ## ## primers should go. ## unlabeled_primer_wells = ([ plate_96.wells_by_name()[well_name] for well_name in ['F3', 'G3', 'H3'] ]) for well in unlabeled_primer_wells: unlabeled_primer_dest.append(well) unlabeled_primer_columns = ([ plate_96.columns_by_name()[column_name] for column_name in ['4', '5', '6'] ]) for column in unlabeled_primer_columns: for well in column: unlabeled_primer_dest.append(well) # ============================================================================= ##### 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 we are at the first well, start by picking up a tip. ## if i == 0: p300.pick_up_tip() ## Then, after every 8th well, drop tip and pick up a new one. ## elif i % 8 == 0: p300.drop_tip() p300.pick_up_tip() 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 and calculates the delta_height of the ## ## liquid after the next aspiration step. ## if bottom_reached: aspiration_location = tubes_5mL['C1'].bottom(z=1) #!!! protocol.comment("You've reached the bottom!") else: aspiration_location = tubes_5mL['C1'].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 LABELED PRIMERS============================= # ============================================================================= ## For the columns in both the source (primers) and the destination: ## ## loop trough the wells in those columns. ## for primer_tube, mix_tube in zip(labeled_primers, mastermix): p20.pick_up_tip() p20.aspirate(primer_vol_label, primer_tube) ## primer_mix_vol = volume for pipetting up and down ## primer_mix_vol = primer_vol_label + 3 p20.mix(3, primer_mix_vol, mix_tube) ## primer_dispense_vol = volume to dispense that was mixed ## primer_dispense_vol = primer_mix_vol + 3 p20.dispense(primer_dispense_vol, mix_tube) p20.drop_tip() # ============================================================================= # ==========================ADDING UNLABELED PRIMERS=========================== # ============================================================================= ## For the wells in unlabeld_primer_dest (the wells with unlabeled primers) do: ##FORWARD for well in unlabeled_primer_dest: p20.pick_up_tip() p20.aspirate(primer_vol, primer_tubes['A1']) ## primer_mix_vol = volume for pipetting up and down ## primer_mix_vol = primer_vol + 3 p20.mix(3, primer_mix_vol, well) ## primer_dispense_vol = volume to dispense that was mixed ## primer_dispense_vol = primer_mix_vol + 3 p20.dispense(primer_dispense_vol, well) p20.drop_tip() ##REVERSE for well in unlabeled_primer_dest: p20.pick_up_tip() p20.aspirate(primer_vol, primer_tubes['B1']) ## primer_mix_vol = volume for pipetting up and down ## primer_mix_vol = primer_vol + 3 p20.mix(3, primer_mix_vol, well) ## primer_dispense_vol = volume to dispense that was mixed ## primer_dispense_vol = primer_mix_vol + 3 p20.dispense(primer_dispense_vol, well) p20.drop_tip()
def run(protocol: protocol_api.ProtocolContext): """ Aliquoting Phusion mastermix from a 5 mL tube to an entire 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 sample (16S positive control) from 1.5 mL tubes (diluted and undiluted) in a few places on the plate. """ # ============================================================================= ##### Loading labware ## For available labware see "labware/list_of_available_labware". ## tips_200 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 7, #deck position '200tips') #custom name tips_20 = protocol.load_labware( 'opentrons_96_filtertiprack_20ul', #labware definition 10, #deck position '20tips') #custom name plate_96 = protocol.load_labware( 'biorad_96_wellplate_200ul_pcr', #labware definition 9, #deck position '96well_plate') #custom name sample_tubes = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap', #labware def 11, #deck position 'sample_tubes') #custom name tubes_5mL = protocol.load_labware( 'eppendorf_15_tuberack_5000ul', #labware definition 8, #deck position '5mL_tubes') #custom name ##### 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]) #assigned tiprack # ============================================================================= # ============================================================================= ##### !!! Variables to set start_vol = 2544 ## The start_vol is the volume (ul) that is in the source labware at ## ## the start of the protocol. ## dispension_vol = 24 ## The dispension_vol is the volume (ul) that needs to be aliquoted ## ## into the destination wells/tubes. ## sample_vol = 1 ## The sample_vol is the volume (ul) of sample added to the PCR ## ## reaction. ## p300.starting_tip = tips_200.well('C8') p20.starting_tip = tips_20.well('D9') ## The starting_tip is the location of first pipette tip in the box ## # ============================================================================= # ============================================================================= ##### Predifined variables 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' ## 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 = 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 the mix p300.pick_up_tip() ## p300 picks up tip from location specified in variable starting_tip ## for well in plate_96.wells(): ## Name all the wells in the plate 'well', for all these do: ## tv = volume_tracking(container, dispension_vol, current_height) current_height, delta_height = tv ## 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 and calculates the delta_height of the liquid ## ## after the next aspiration step. The outcome is stored as tv and## ## then the specific variables are updated. ## pip_height = current_height - 2 ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if current_height - delta_height <= 1: aspiration_location = tubes_5mL['C3'].bottom(z=1) #!!! protocol.comment("You've reached the bottom!") else: aspiration_location = tubes_5mL['C3'].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. ## well_c = str( well) #set location of the well to str (if takes only str) if (well_c == 'A3 of 96well_plate on 9' or well_c == 'A5 of 96well_plate on 9' or well_c == 'A7 of 96well_plate on 9' or well_c == 'A9 of 96well_plate on 9' or well_c == 'A11 of 96well_plate on 9'): p300.drop_tip() p300.pick_up_tip() ## Pick up a new tip every two rows. ## 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() ## Drop the final tip in the trash bin. ## # ============================================================================= # ============================================================================= ##### Transferring samples ## Transfer undiluted sample from specified tube in sample_tubes to ## ## specified well in 96_wells plate. ## p20.transfer(sample_vol, sample_tubes['A1'], [ plate_96.wells_by_name()[well_name] for well_name in [ 'A2', 'D4', 'B5', 'F5', 'D6', 'B7', 'F7', 'D8', 'B9', 'F9', 'A11' ] ], new_tip='always', blow_out=True, blowout_location='destination well', mix_after=(3, 5), air_gap=1) ## Transfer diluted sample from B1-B6 std_tubes to multiple in plate_96 ## p20.transfer(sample_vol, [ sample_tubes.wells_by_name()[well_name] for well_name in ['B1', 'B2', 'B3', 'B4', 'B5', 'B6'] ], [ plate_96.wells_by_name()[well_name] for well_name in ['B2', 'C2', 'D2', 'E2', 'F2', 'G2'] ], new_tip='always', blow_out=True, blowout_location='destination well', mix_after=(3, 5), air_gap=1) p20.transfer(sample_vol, [ sample_tubes.wells_by_name()[well_name] for well_name in ['B1', 'B2', 'B3', 'B4', 'B5', 'B6'] ], [ plate_96.wells_by_name()[well_name] for well_name in ['B11', 'C11', 'D11', 'E11', 'F11', 'G11'] ], new_tip='always', blow_out=True, blowout_location='destination well', mix_after=(3, 5), air_gap=1)
def run(protocol: protocol_api.ProtocolContext, cmdList, deckMap, amtList): #global cmdList, deckMap, amtList ############ LOAD LABWARES ############ tipLocs = [] for i in range(11): #load labware labware_name = deckMap["labware_" + str(i + 1)] if (labware_name != '(empty)' and labware_name != 'TRASH'): deck_position = int(list(deckMap.keys())[i].split('_')[1]) globals()[list(deckMap.keys())[i]] = protocol.load_labware( labware_name, deck_position) #if labware is a tip rack, assign number to tip location(s) if ('tiprack' in labware_name): tipLocs.append(globals()[list(deckMap.keys())[i]]) #load pipettes #single-channel right_pipette = protocol.load_instrument('p300_single', 'right', tip_racks=tipLocs) right_pipette.flow_rate.aspirate = aspirateSpeed right_pipette.flow_rate.dispense = dispenseSpeed #multi-channel left_pipette = protocol.load_instrument('p300_multi', 'left', tip_racks=tipLocs) left_pipette.flow_rate.aspirate = aspirateSpeed left_pipette.flow_rate.dispense = dispenseSpeed #Update Amount List for i in range(len(amtList)): if (float(amtList[i][3]) <= 50): amtList[i][3] = float(amtList[i][3]) * 1000 #get tube type if ('50ml' in deckMap[amtList[i][0]]): amtList[i] = np.append(amtList[i], '50ml_falcon') elif ('15ml' in deckMap[amtList[i][0]]): amtList[i] = np.append(amtList[i], '15ml_falcon') else: amtList[i] = np.append(amtList[i], '1.5ml_eppendorf') ############ EXECUTE COMMANDS ############ #iterate through all command lines current_tipID = 0 #initiate tip ID for i in range(len(cmdList)): #subset cmdRow = cmdList[i] print(cmdRow) #parse all informations source_ware = cmdRow[0] source_well = cmdRow[1].split(', ') target_ware = cmdRow[2] target_well = cmdRow[3].split(', ') transfer_amt = float(cmdRow[4]) #only one transfer amount is allowed if (float(cmdRow[5]) > 0): mix_amt = max(float(cmdRow[5]), 300) else: mix_amt = 0 tipID = int(cmdRow[6]) #each row is performed using a single tip #choose pipette if (len(target_well) == 8 and len(source_well) == 8): #MULTICHANNEL pipette = "left_pipette" #pick up tip if needed if (tipID != current_tipID): left_pipette.pick_up_tip() #pick up tip if tipID changes current_tipID = tipID #update tip id aspHs = [] dspHs = [] mixVols = [] for j in range(1, 7): #get mix volumes mixVol = GetSrcVolume(amtList, cmdRow, source_well[j]) #not annotated mixVols.append(mixVol) #not annotated #update solutions map amtList = Update_Source(amtList, cmdRow, source_well[j]) amtList = Update_Target(amtList, cmdRow, target_well[j], deckMap) #calculate aspirate and dispense height aspH = CalTip_Aspirate(amtList, cmdRow, source_well[j]) dspH = CalTip_Dispense(amtList, cmdRow, target_well[j]) aspHs.append(aspH) dspHs.append(dspH) source_col = source_well[0][1:] target_col = target_well[0][1:] #selecting aspHs/dspHs aspHs = [i for i in aspHs if i > 0] dspHs = [i for i in dspHs if i > 0] mixVols = [i for i in mixVols if i > 0] #not annotated aspHs_selected = min(aspHs) dspHs_selected = min(dspHs) if (max(mixVols) == 0): mix_amt = 0 else: mixVols.append(300) mix_amt = min(mixVols) #Main Transfers source_well_h = source_well[0] target_well_h = target_well[0] remV = transfer_amt while (remV > 0): cur_transfer = min(300, remV) if (remV - cur_transfer < 30 and remV - cur_transfer > 0): cur_transfer = cur_transfer / 2 remV = remV - cur_transfer if (mix_amt == 0): #if no mix left_pipette.transfer( cur_transfer, globals()[source_ware].wells_by_name() [source_well_h].bottom(aspHs_selected), globals()[target_ware].wells_by_name() [target_well_h].bottom(dspHs_selected), new_tip='never', disposal_volume=0) else: #if mix left_pipette.transfer( cur_transfer, globals()[source_ware].wells_by_name() [source_well_h].bottom(aspHs_selected), globals()[target_ware].wells_by_name() [target_well_h].bottom(dspHs_selected), new_tip='never', mix_before=(2, cur_transfer), disposal_volume=0) #blow out on top of the current slot left_pipette.blow_out(globals()[target_ware].wells_by_name() [target_well_h].bottom(dspHs_selected)) #check if tip need to be trashed afterwards if (i == len(cmdList) - 1): #if this is the last operation left_pipette.drop_tip() elif (int(cmdRow[6]) != int(cmdList[i + 1][6])): #drop if different tip id is detected left_pipette.drop_tip() else: #SINGLE CHANNEL pipette = 'right_pipette' cur_source_well = source_well[0] #select only the first source #pick up tip if needed if (tipID != current_tipID): right_pipette.pick_up_tip() #pick up tip if tipID changes current_tipID = tipID #update tip id #iterate through all target wells for j in range(len(target_well)): if (mix_amt > 0): mix_amt = min( GetSrcVolume(amtList, cmdRow, cur_source_well), 300) #update solutions map amtList = Update_Source(amtList, cmdRow, cur_source_well) amtList = Update_Target(amtList, cmdRow, target_well[j], deckMap) #calculate aspirate and dispense height aspH = CalTip_Aspirate(amtList, cmdRow, cur_source_well) dspH = CalTip_Dispense(amtList, cmdRow, target_well[j]) #Main Transfers remV = transfer_amt while (remV > 0): cur_transfer = min(300, remV) if (remV - cur_transfer < 30 and remV - cur_transfer > 0): cur_transfer = cur_transfer / 2 remV = remV - cur_transfer if (mix_amt == 0): #if no mix right_pipette.transfer( cur_transfer, globals()[source_ware].wells_by_name() [cur_source_well].bottom(aspH), globals()[target_ware].wells_by_name()[ target_well[j]].bottom(dspH), new_tip='never', disposal_volume=0) else: #if mix right_pipette.transfer( cur_transfer, globals()[source_ware].wells_by_name() [cur_source_well].bottom(aspH), globals()[target_ware].wells_by_name()[ target_well[j]].bottom(dspH), new_tip='never', mix_before=(3, cur_transfer), disposal_volume=0) #blow out on top of the current slot right_pipette.blow_out( globals()[target_ware].wells_by_name()[ target_well[j]].bottom(dspH)) #check if tip need to be trashed afterwards if (i == len(cmdList) - 1): #if this is the last operation right_pipette.drop_tip() elif (int(cmdRow[6]) != int(cmdList[i + 1][6])): #drop if different tip id is detected right_pipette.drop_tip()
def run(ctx: protocol_api.ProtocolContext): # Initial data global robot global tip_log # Set robot as global var robot = ctx # check if tipcount is being reset if RESET_TIPCOUNT: reset_tipcount() # confirm door is close robot.comment(f"Please, close the door") confirm_door_is_closed() start = start_run() # ##################################################### # Common functions # ##################################################### # ----------------------------------------------------- # Execute step # ----------------------------------------------------- def run_step(step): robot.comment(' ') robot.comment('###############################################') robot.comment('Step ' + str(step) + ': ' + STEPS[step]['Description']) robot.comment('===============================================') # Execute step? if STEPS[step]['Execute']: # Get start info elapsed = datetime.now() for i, key in enumerate(tip_log['used']): val = tip_log['used'][key] if i == 0: cl = val else: cr = val # Execute function step STEPS[step]['Function']() # Wait if STEPS[step].get('wait_time'): robot.comment('===============================================') wait = STEPS[step]['wait_time'] robot.delay(seconds = wait) # Get end info elapsed = datetime.now() - elapsed for i, key in enumerate(tip_log['used']): val = tip_log['used'][key] if i == 0: cl = val - cl else: cr = val - cr # Stats STEPS[step]['Time:'] = str(elapsed) robot.comment('===============================================') robot.comment('Elapsed time: ' + str(elapsed)) for i, key in enumerate(tip_log['used']): if i == 0: robot.comment('Tips "' + str(key) + '" used: ' + str(cl)) else: robot.comment('Tips "' + str(key) + '" used: ' + str(cr)) # Dont execute step else: robot.comment('No ejecutado') # End robot.comment('###############################################') robot.comment(' ') # ##################################################### # 1. Start defining deck # ##################################################### # Labware # Positions are: # 10 11 TRASH # 7 8 9 # 4 5 6 # 1 2 3 # ----------------------------------------------------- # Tips # ----------------------------------------------------- tips20 = [robot.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['11'] ] # ----------------------------------------------------- # Pipettes # ----------------------------------------------------- p20 = robot.load_instrument('p20_single_gen2', 'right', tip_racks=tips20) ## retrieve tip_log retrieve_tip_info(p20,tips20) # ----------------------------------------------------- # Labware # ----------------------------------------------------- primers_rack = ctx.load_labware('opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap', '7', '24_tuberack_starsted source rack') dest_rack = ctx.load_labware('opentrons_96_aluminumblock_generic_pcr_strip_200ul', '8', 'source tuberack') # ----------------------------------------------------- # Reagens # ----------------------------------------------------- positive_control_reagent = Reagent(name = 'Positive control', flow_rate_aspirate = 1, flow_rate_dispense = 300, flow_rate_aspirate_mix = 1, flow_rate_dispense_mix = 300) # ----------------------------------------------------- # Tubes # ----------------------------------------------------- starsted_tube = Tube(name = 'Starsted 1.5 Tube', actual_volume = 400, max_volume = 1500, min_height=0.7, diameter = 8.7, # avl1.diameter base_type = 2, height_base = 4) # ##################################################### # 2. Steps definition # ##################################################### # ----------------------------------------------------- # Step n: .... # ----------------------------------------------------- def step1(): if not p20.hw_pipette['has_tip']: pick_up(p20,tips20) segene_positive_control = primers_rack['A6'] dest = dest_rack.wells()[:50] # transfer buffer to tubes distribute_custom(pip = p20, reagent = positive_control_reagent, tube_type = starsted_tube, volume = 10, src = segene_positive_control, dest = dest, extra_dispensal=0, disp_height=20, touch_tip_aspirate=False, touch_tip_dispense=True) drop(p20) # ----------------------------------------------------- # Execution plan # ----------------------------------------------------- STEPS = { 1:{'Execute': True, 'Function': step1, 'Description': 'Transfer Positive Control 16 first wells/tubes'}, } # ##################################################### # 3. Execute every step!! # ##################################################### for step in STEPS: run_step(step) # track final used tip save_tip_info() # ----------------------------------------------------- # Stats # ----------------------------------------------------- end = finish_run() robot.comment('===============================================') robot.comment('Start time: ' + str(start)) robot.comment('Finish time: ' + str(end)) robot.comment('Elapsed time: ' + str(datetime.strptime(end, "%Y/%m/%d %H:%M:%S") - datetime.strptime(start, "%Y/%m/%d %H:%M:%S"))) for key in tip_log['used']: val = tip_log['used'][key] robot.comment('Tips "' + str(key) + '" used: ' + str(val)) robot.comment('===============================================')
def run(ctx: protocol_api.ProtocolContext): [ input_csv, p20_mount, p300_mount, aspiration_height_plate, dispensing_height_plate, aspiration_height_resv, flow_rate_multiplier ] = get_values( # noqa: F821 "input_csv", "p20_mount", "p300_mount", "aspiration_height_plate", "dispensing_height_plate", "aspiration_height_resv", "flow_rate_multiplier") if 0.1 > aspiration_height_plate: raise Exception("Enter a higher source plate aspiration height") if 0.1 > dispensing_height_plate: raise Exception("Enter a higher destination plate dispensing height") if 0.1 > aspiration_height_resv: raise Exception("Enter a higher reservoir aspiration height") if p20_mount == p300_mount: raise Exception("Both pipettes cannot be mounted in the same mount") # define all custom variables above here with descriptions: sample_plate_lname = 'opentrons_96_aluminumblock_biorad_wellplate_200ul' dest_plate_lname = 'opentrons_96_aluminumblock_biorad_wellplate_200ul' small_tips_loadname = 'opentrons_96_filtertiprack_20ul' large_tips_loadname = 'opentrons_96_filtertiprack_200ul' res_loadname = 'nest_12_reservoir_15ml' # For validating that wells have the format of <A-H><0-12> well_name_validation_regex = re.compile(r'[A-H][0-9][0-2]?') # Check that the volumes look like numbers e.g. 12, or 5.25 volume_validation_regex = re.compile(r'[0-9]+(\.[0-9]+)?') # 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 labware temp_mod_samples = ctx.load_module('temperature module gen2', '1') temp_mod_destination = ctx.load_module('temperature module gen2', '4') ''' Add your labware here with: labware_name = ctx.load_labware('{loadname}', '{slot number}') If loading labware on a module, you can load with: labware_name = module_name.load_labware('{loadname}') where module_name is defined above. ''' sample_plate = temp_mod_samples.load_labware(sample_plate_lname) destination_plate = temp_mod_destination.load_labware(dest_plate_lname) reservoir_12 = ctx.load_labware(res_loadname, '2', label="diluent reservoir") # load tipracks ''' Add your tipracks here as a list: For a single tip rack: tiprack_name = [ctx.load_labware('{loadname}', '{slot number}')] For multiple tip racks of the same type: tiprack_name = [ctx.load_labware('{loadname}', 'slot') for slot in ['1', '2', '3']] If two different tipracks are on the deck, use convention: tiprack[number of microliters] e.g. tiprack10, tiprack20, tiprack200, tiprack300, tiprack1000 ''' small_tipracks = [ ctx.load_labware(small_tips_loadname, slot) for slot in ['7', '10'] ] large_tipracks = [ ctx.load_labware(large_tips_loadname, slot) for slot in ['5', '8'] ] # 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 ) ''' p20 = ctx.load_instrument('p20_single_gen2', p20_mount, tip_racks=small_tipracks) p300 = ctx.load_instrument('p300_single_gen2', p300_mount, tip_racks=large_tipracks) # 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() ''' # 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: """ ''' class VolTracker: def __init__(self, labware, well_vol, start=0, end=8, mode='reagent', pip_type='single', msg='Reset labware volumes'): """ Voltracker tracks the volume(s) used in a piece of labware :param labware: The labware to track :param well_vol: The volume of the liquid in the wells :param pip_type: The pipette type used 'single' or 'multi' :param mode: 'reagent' or 'waste' :param start: The starting well :param end: The ending well :param msg: Message to send to the user when all wells are empty """ self.labware_wells = dict.fromkeys(labware.wells()[start - 1:end], 0) self.labware_wells_backup = self.labware_wells.copy() self.well_vol = well_vol self.pip_type = pip_type self.mode = mode self.start = start self.end = end self.msg = msg # Parameter error checking if not (pip_type == 'single' or pip_type == 'multi'): raise Exception('Pipette type must be single or multi') if not (mode == 'reagent' or mode == 'waste'): raise Exception('mode must be reagent or waste') def track(self, vol): '''track() will track how much liquid was used up per well. If the volume of a given well is greater than self.well_vol it will remove it from the dictionary and iterate to the next well which will act as the reservoir.''' well = next(iter(self.labware_wells)) vol = vol * 8 if self.pip_type == 'multi' else vol if self.labware_wells[well] + vol >= self.well_vol: del self.labware_wells[well] if len(self.labware_wells) < 1: ctx.pause(self.msg) self.labware_wells = self.labware_wells_backup.copy() well = next(iter(self.labware_wells)) self.labware_wells[well] += vol if self.mode == 'waste': ctx.comment('{}: {} ul of total waste'.format( well, int(self.labware_wells[well]))) else: ctx.comment('{} uL of liquid used from {}'.format( int(self.labware_wells[well]), well)) return well # 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'] ''' diluent = VolTracker(reservoir_12, 14 * 10**3, start=1, end=4, mode='reagent', pip_type='single', msg='Refill diluent wells') # 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_by_name() dest_wells = destination_plate.wells_by_name() # 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') ... ... ... ''' # parse data = [[val.strip().upper() for val in line.split(',')] for line in input_csv.splitlines()[1:] if line and line.split(',')[0]] # Validate csv input i = 1 for line in data: i += 1 if not len(line) == 4: raise Exception( "Line #{} \"{}\" has the wrong number of entries".format( i, line)) # check well formatting if well_name_validation_regex.fullmatch(line[0]) is None: raise Exception(("Line #{}: The source plate well name \"{}\" " "has the wrong format").format(i, line[0])) if well_name_validation_regex.fullmatch(line[1]) is None: raise Exception(("Line #{}: The dest. plate well name \"{}\" " "has the wrong format").format(i, line[1])) if volume_validation_regex.fullmatch(line[2]) is None: raise Exception(("Line #{}: The sample volume \"{}\" " + "has the wrong format").format(i, line[2])) if volume_validation_regex.fullmatch(line[3]) is None: raise Exception(("Line #{}: The diluent volume \"{}\" " + "has the wrong format").format(i, line[3])) # perform normalization - Transfer all the diluent first before # transferring any sample: use the same pipette tip # Steps 1-4 # d - dest well, vol_d - diluent volume ctx.comment("\n\nTransferring diluent to the target plate\n") for _, d, _, vol_d in data: vol_d = float(vol_d) d_well = dest_wells[d] pip = p300 if vol_d > 20 else p20 if not pip.has_tip: pip.pick_up_tip() pip.transfer(vol_d, diluent.track(vol_d).bottom(aspiration_height_resv), d_well.bottom(dispensing_height_plate), new_tip='never') pip.blow_out(d_well.top(-2)) # Debugging info # print("diluent reservoir position {}". # format(diluent.track(0).bottom(aspiration_height_resv))) # Step 5: drop tips ctx.comment("\n\nDiluent transfer complete: Droppping tips") for pip in [p20, p300]: if pip.has_tip: pip.drop_tip() # Step 7-10: Transfer samples from the sample plate to the dest. plate ctx.comment("\n\nTransferring samples to the target plate\n") for s, d, vol_s, _ in data: vol_s = float(vol_s) s_well = sample_wells[s].bottom(aspiration_height_plate) d_well = dest_wells[d].bottom(dispensing_height_plate) blow_out_loc = dest_wells[d].top(-2) # Debugging info # print("Aspiration height s_well: {}".format(s_well)) # print("Dispensing height d_well: {}".format(d_well)) # Transfer sample pip = p300 if vol_s > 20 else p20 pip.pick_up_tip() pip.aspirate(vol_s, s_well, flow_rate_multiplier) pip.dispense(vol_s, d_well, flow_rate_multiplier) pip.blow_out(blow_out_loc) pip.drop_tip()
def run(ctx: protocol_api.ProtocolContext): #Change light to red ctx._hw_manager.hardware.set_lights(button=(1, 0, 0)) ctx.comment('Actual used columns: ' + str(num_cols)) STEP = 0 STEPS = { #Dictionary with STEP activation, description, and times 1: { 'Execute': True, 'description': 'Transfer beads + PK + binding' }, 2: { 'Execute': True, 'description': 'Transfer wash' }, 3: { 'Execute': True, 'description': 'Transfer ethanol' }, 4: { 'Execute': True, 'description': 'Transfer elution' } } #Folder and file_path for log time import os 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_B_Preparacion_Kingfisher_time_log.txt' #Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, flow_rate_aspirate_mix, flow_rate_dispense_mix, air_gap_vol_bottom, air_gap_vol_top, disposal_volume, rinse, max_volume_allowed, reagent_volume, reagent_reservoir_volume, num_wells, h_cono, v_fondo, tip_recycling='none', dead_vol=700): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.flow_rate_aspirate_mix = flow_rate_aspirate_mix self.flow_rate_dispense_mix = flow_rate_dispense_mix self.air_gap_vol_bottom = air_gap_vol_bottom self.air_gap_vol_top = air_gap_vol_top self.disposal_volume = disposal_volume self.rinse = bool(rinse) self.max_volume_allowed = max_volume_allowed self.reagent_volume = reagent_volume self.reagent_reservoir_volume = reagent_reservoir_volume self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.tip_recycling = tip_recycling self.dead_vol = dead_vol self.vol_well_original = (reagent_reservoir_volume / num_wells ) + dead_vol if num_wells > 0 else 0 #Reagents and their characteristics Wash = Reagent( name='Wash', flow_rate_aspirate=5, # Original = 0.5 flow_rate_dispense=5, # Original = 1 flow_rate_aspirate_mix= 1, # Liquid density very high, needs slow aspiration flow_rate_dispense_mix= 1, # Liquid density very high, needs slow dispensation air_gap_vol_bottom=5, air_gap_vol_top=0, disposal_volume=1, rinse=True, max_volume_allowed=180, reagent_volume= WASH_VOLUME_PER_SAMPLE, # reagent volume needed per sample reagent_reservoir_volume=(NUM_SAMPLES + 5) * WASH_VOLUME_PER_SAMPLE, #70000, #51648 num_wells=1, h_cono=1.95, v_fondo=695) #1.95 * multi_well_rack_area / 2, #Prismatic Ethanol = Reagent( name='Ethanol', flow_rate_aspirate=5, flow_rate_dispense=5, flow_rate_aspirate_mix=1, flow_rate_dispense_mix=1, air_gap_vol_bottom=5, air_gap_vol_top=0, disposal_volume=1, rinse=True, max_volume_allowed=180, reagent_volume=ETHANOL_VOLUME_PER_SAMPLE, reagent_reservoir_volume=(NUM_SAMPLES + 5) * ETHANOL_VOLUME_PER_SAMPLE, num_wells=1, h_cono=1.95, v_fondo=695) #1.95 * multi_well_rack_area / 2, #Prismatic Beads_PK_Binding = Reagent( name='Magnetic beads + PK + Binding', flow_rate_aspirate=0.5, flow_rate_dispense=0.5, flow_rate_aspirate_mix=0.5, flow_rate_dispense_mix=0.5, air_gap_vol_bottom=5, air_gap_vol_top=0, disposal_volume=1, rinse=True, max_volume_allowed=180, reagent_volume=BEADS_VOLUME_PER_SAMPLE, reagent_reservoir_volume=NUM_SAMPLES * BEADS_VOLUME_PER_SAMPLE * 1.1, num_wells=math.ceil(NUM_SAMPLES * BEADS_VOLUME_PER_SAMPLE * 1.1 / 11500), h_cono=1.95, v_fondo=695) #1.95 * multi_well_rack_area / 2, #Prismatic Elution = Reagent( name='Elution', flow_rate_aspirate=3, # Original 0.5 flow_rate_dispense=3, # Original 1 flow_rate_aspirate_mix=15, flow_rate_dispense_mix=25, air_gap_vol_bottom=5, air_gap_vol_top=0, disposal_volume=1, rinse=False, max_volume_allowed=180, reagent_volume=ELUTION_VOLUME_PER_SAMPLE, reagent_reservoir_volume=(NUM_SAMPLES + 5) * ELUTION_VOLUME_PER_SAMPLE, num_wells=math.ceil( (NUM_SAMPLES + 5) * ELUTION_VOLUME_PER_SAMPLE / 13000), h_cono=1.95, v_fondo=695) #1.95 * multi_well_rack_area / 2, #Prismatic Sample = Reagent( name='Sample', flow_rate_aspirate=3, # Original 0.5 flow_rate_dispense=3, # Original 1 flow_rate_aspirate_mix=15, flow_rate_dispense_mix=25, air_gap_vol_bottom=5, air_gap_vol_top=0, disposal_volume=1, rinse=False, max_volume_allowed=150, reagent_volume=50, reagent_reservoir_volume=(NUM_SAMPLES + 5) * 50, #14800, num_wells=num_cols, #num_cols comes from available columns h_cono=4, v_fondo=4 * math.pi * 4**3 / 3) #Sphere Wash.vol_well = Wash.vol_well_original Ethanol.vol_well = Ethanol.vol_well_original Beads_PK_Binding.vol_well = Beads_PK_Binding.vol_well_original Elution.vol_well = Elution.vol_well_original Sample.vol_well = 350 # Arbitrary value def str_rounded(num): return str(int(num + 0.5)) ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VOLUMES FOR ' + str(NUM_SAMPLES) + ' SAMPLES') ctx.comment(' ') ctx.comment('Beads + PK + Binding: ' + str(Beads_PK_Binding.num_wells) + ' wells from well 2 in multi reservoir with volume ' + str_rounded(Beads_PK_Binding.vol_well_original) + ' uL each one') ctx.comment('Elution: ' + str(Elution.num_wells) + ' wells from well 7 in multi reservoir with volume ' + str_rounded(Elution.vol_well_original) + ' uL each one') ctx.comment('Wash: in reservoir 1 with volume ' + str_rounded(Wash.vol_well_original) + ' uL') ctx.comment('Etanol: in reservoir 2 with volume ' + str_rounded(Ethanol.vol_well_original) + ' uL') ctx.comment('###############################################') ctx.comment(' ') ################### #Custom functions def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, offset, wait_time=0, drop_height=-1, two_thirds_mix_bottom=False): ''' Function for mix in the same location a certain number of rounds. Blow out optional. Offset can set to 0 or a higher/lower value which indicates the lateral movement ''' if mix_height <= 0: mix_height = 1 pipet.aspirate(1, location=location.bottom(z=mix_height), rate=reagent.flow_rate_aspirate_mix) for i in range(rounds): pipet.aspirate(vol, location=location.bottom(z=mix_height), rate=reagent.flow_rate_aspirate_mix) if two_thirds_mix_bottom and i < ((rounds / 3) * 2): pipet.dispense(vol, location=location.bottom(z=5).move( Point(x=offset)), rate=reagent.flow_rate_dispense_mix) else: pipet.dispense(vol, location=location.top(z=drop_height).move( Point(x=offset)), rate=reagent.flow_rate_dispense_mix) pipet.dispense(1, location=location.bottom(z=mix_height), rate=reagent.flow_rate_dispense_mix) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out if wait_time != 0: ctx.delay(seconds=wait_time, msg='Waiting for ' + str(wait_time) + ' seconds.') def calc_height(reagent, cross_section_area, aspirate_volume, min_height=0.4): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if (reagent.vol_well - reagent.dead_vol) < aspirate_volume: 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.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 move_vol_multi(pipet, reagent, source, dest, vol, x_offset_source, x_offset_dest, pickup_height, rinse, avoid_droplet, wait_time, blow_out, touch_tip=False, drop_height=-5): # Rinse before aspirating if rinse == True: #pipet.aspirate(air_gap_vol_top, location = source.top(z = -5), rate = reagent.flow_rate_aspirate) #air gap custom_mix(pipet, reagent, location=source, vol=vol, rounds=20, blow_out=False, mix_height=3, offset=0) #pipet.dispense(air_gap_vol_top, location = source.top(z = -5), rate = reagent.flow_rate_dispense) # SOURCE if reagent.air_gap_vol_top != 0: #If there is air_gap_vol, switch pipette to slow speed pipet.move_to(source.top(z=0)) pipet.air_gap(reagent.air_gap_vol_top) #air gap #pipet.aspirate(reagent.air_gap_vol_top, source.top(z = -5), rate = reagent.flow_rate_aspirate) #air gap s = source.bottom(pickup_height).move(Point(x=x_offset_source)) pipet.aspirate(vol, s, rate=reagent.flow_rate_aspirate) # aspirate liquid if reagent.air_gap_vol_bottom != 0: #If there is air_gap_vol, switch pipette to slow speed pipet.move_to(source.top(z=0)) pipet.air_gap(reagent.air_gap_vol_bottom) #air gap #pipet.aspirate(air_gap_vol_bottom, source.top(z = -5), rate = reagent.flow_rate_aspirate) #air gap if wait_time != 0: ctx.delay(seconds=wait_time, msg='Waiting for ' + str(wait_time) + ' seconds.') if avoid_droplet == True: # Touch the liquid surface to avoid droplets ctx.comment("Moving to: " + str(round(pickup_height, 2)) + ' mm') pipet.move_to(source.bottom(pickup_height)) # GO TO DESTINATION d = dest.top(z=drop_height).move(Point(x=x_offset_dest)) pipet.dispense(vol - reagent.disposal_volume + reagent.air_gap_vol_bottom, d, rate=reagent.flow_rate_dispense) if wait_time != 0: ctx.delay(seconds=wait_time, msg='Waiting for ' + str(wait_time) + ' seconds.') if reagent.air_gap_vol_top != 0: pipet.dispense(reagent.air_gap_vol_top, dest.top(z=0), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(dest.top(z=-5)) if touch_tip == True: pipet.touch_tip(speed=20, v_offset=-10, radius=0.7) #if reagent.air_gap_vol_bottom != 0: #pipet.move_to(dest.top(z = 0)) #pipet.air_gap(reagent.air_gap_vol_bottom) #air gap #pipet.aspirate(air_gap_vol_bottom, dest.top(z = 0),rate = reagent.flow_rate_aspirate) #air gap ########## # 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): if col % 2 == 0: side = -1 # left else: side = 1 # right return side # load labware and modules ############################################ ######## Sample plate - comes from A deepwell_plate_samples = ctx.load_labware('nest_96_wellplate_2ml_deep', '1', 'NEST 96 Deepwell Plate 2mL 1') deepwell_plate_wash = ctx.load_labware('nest_96_wellplate_2ml_deep', '5', 'NEST 96 Deepwell Plate 2mL 2') deepwell_plate_ethanol = ctx.load_labware('nest_96_wellplate_2ml_deep', '6', 'NEST 96 Deepwell Plate 2mL 3') deepwell_plate_elution = ctx.load_labware( 'biorad_96_wellplate_200ul_pcr', '7', 'Bio-Rad 96 Well Plate 200 µL PCR') #################################### ######## 12 well rack reagent_multi_res = ctx.load_labware('nest_12_reservoir_15ml', '4', 'Reagent deepwell plate') #################################### ######## Single reservoirs reagent_res_1 = ctx.load_labware('nest_1_reservoir_195ml', '2', 'Single reagent reservoir 1') res_1 = reagent_res_1.wells()[0] reagent_res_2 = ctx.load_labware('nest_1_reservoir_195ml', '3', 'Single reagent reservoir 2') res_2 = reagent_res_2.wells()[0] #################################### ######### Load tip_racks tips300 = [ ctx.load_labware('opentrons_96_tiprack_300ul', slot, '200µl filter tiprack') for slot in ['8', '9'] ] ############################################################################### #Declare which reagents are in each reservoir as well as deepwell and sample plate Wash.reagent_reservoir = res_1 Ethanol.reagent_reservoir = res_2 Beads_PK_Binding.reagent_reservoir = reagent_multi_res.rows()[0][1:5] Elution.reagent_reservoir = reagent_multi_res.rows()[0][6:7] work_destinations = deepwell_plate_samples.rows()[0][:Sample.num_wells] wash_destinations = deepwell_plate_wash.rows()[0][:Sample.num_wells] ethanol_destinations = deepwell_plate_ethanol.rows()[0][:Sample.num_wells] elution_destinations = deepwell_plate_elution.rows()[0][:Sample.num_wells] # pipettes. 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: 96 * len(m300.tip_racks) } #96 tips per tiprack * number or tipracks in the layout } ############################################################################### ############################################################################### ############################################################################### # STEP 1 TRANSFER BEADS + PK + Binding ######## 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(' ') beads_trips = math.ceil(Beads_PK_Binding.reagent_volume / Beads_PK_Binding.max_volume_allowed) beads_volume = Beads_PK_Binding.reagent_volume / beads_trips #136.66 beads_transfer_vol = [] for i in range(beads_trips): beads_transfer_vol.append(beads_volume + Beads_PK_Binding.disposal_volume) x_offset_source = 0 x_offset_dest = 0 rinse = False # Original: True first_mix_done = False for i in range(num_cols): ctx.comment("Column: " + str(i)) if not m300.hw_pipette['has_tip']: pick_up(m300) 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_PK_Binding, multi_well_rack_area, transfer_vol * 8) if change_col == True or not first_mix_done: #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_PK_Binding.col)) custom_mix(m300, Beads_PK_Binding, Beads_PK_Binding.reagent_reservoir[ Beads_PK_Binding.col], vol=Beads_PK_Binding.max_volume_allowed, rounds=BEADS_WELL_FIRST_TIME_NUM_MIXES, blow_out=False, mix_height=0.5, offset=0) first_mix_done = True else: ctx.comment('Mixing reservoir column: ' + str(Beads_PK_Binding.col)) custom_mix(m300, Beads_PK_Binding, Beads_PK_Binding.reagent_reservoir[ Beads_PK_Binding.col], vol=Beads_PK_Binding.max_volume_allowed, rounds=BEADS_WELL_NUM_MIXES, blow_out=False, mix_height=0.5, offset=0) ctx.comment('Aspirate from reservoir column: ' + str(Beads_PK_Binding.col)) ctx.comment('Pickup height is ' + str(round(pickup_height, 2)) + ' mm') #if j!=0: # rinse = False move_vol_multi(m300, reagent=Beads_PK_Binding, source=Beads_PK_Binding.reagent_reservoir[ Beads_PK_Binding.col], dest=work_destinations[i], vol=transfer_vol, x_offset_source=x_offset_source, x_offset_dest=x_offset_dest, pickup_height=pickup_height, rinse=rinse, avoid_droplet=False, wait_time=2, blow_out=True, touch_tip=True, drop_height=-1) ctx.comment(' ') ctx.comment('Mixing sample ') custom_mix(m300, Beads_PK_Binding, location=work_destinations[i], vol=Beads_PK_Binding.max_volume_allowed, rounds=BEADS_NUM_MIXES, blow_out=False, mix_height=0, offset=0, wait_time=2, two_thirds_mix_bottom=True) # m300.move_to(work_destinations[i].top(0)) # m300.air_gap(Beads_PK_Binding.air_gap_vol_bottom) #air gap if recycle_tip == True: m300.return_tip() else: 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) ctx.comment('Used tips in total: ' + str(tip_track['counts'][m300])) ############################################################################### # STEP 1 TRANSFER BEADS + PK + Binding ######## ############################################################################### # STEP 2 TRANSFER WASH ######## 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(' ') wash_trips = math.ceil(Wash.reagent_volume / Wash.max_volume_allowed) wash_volume = Wash.reagent_volume / wash_trips #136.66 wash_transfer_vol = [] for i in range(wash_trips): wash_transfer_vol.append(wash_volume + Wash.disposal_volume) x_offset_source = 0 x_offset_dest = 0 rinse = False for i in range(num_cols): ctx.comment("Column: " + str(i)) if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(wash_transfer_vol): ctx.comment('Aspirate from reservoir 1') #if j!=0: # rinse = False move_vol_multi(m300, reagent=Wash, source=Wash.reagent_reservoir, dest=wash_destinations[i], vol=transfer_vol, x_offset_source=x_offset_source, x_offset_dest=x_offset_dest, pickup_height=2, rinse=rinse, avoid_droplet=False, wait_time=0, blow_out=True, touch_tip=True) ctx.comment(' ') if recycle_tip == True: m300.return_tip() else: 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) ctx.comment('Used tips in total: ' + str(tip_track['counts'][m300])) ############################################################################### # STEP 2 TRANSFER WASH ######## ############################################################################### # STEP 3 TRANSFER ETHANOL ######## 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(' ') ethanol_trips = math.ceil(Ethanol.reagent_volume / Ethanol.max_volume_allowed) ethanol_volume = Ethanol.reagent_volume / ethanol_trips #136.66 ethanol_transfer_vol = [] for i in range(ethanol_trips): ethanol_transfer_vol.append(ethanol_volume + Ethanol.disposal_volume) x_offset_source = 0 x_offset_dest = 0 rinse = False for i in range(num_cols): ctx.comment("Column: " + str(i)) if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(ethanol_transfer_vol): ctx.comment('Aspirate from reservoir 2') #if j!=0: # rinse = False move_vol_multi(m300, reagent=Ethanol, source=Ethanol.reagent_reservoir, dest=ethanol_destinations[i], vol=transfer_vol, x_offset_source=x_offset_source, x_offset_dest=x_offset_dest, pickup_height=2, rinse=rinse, avoid_droplet=False, wait_time=0, blow_out=True, touch_tip=True) if recycle_tip == True: m300.return_tip() else: 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) ctx.comment('Used tips in total: ' + str(tip_track['counts'][m300])) ############################################################################### # STEP 3 TRANSFER ETHANOL ######## ############################################################################### # STEP 4 TRANSFER ELUTION ######## 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(' ') elution_trips = math.ceil(Elution.reagent_volume / Elution.max_volume_allowed) elution_volume = Elution.reagent_volume / elution_trips #136.66 elution_transfer_vol = [] for i in range(elution_trips): elution_transfer_vol.append(elution_volume + Elution.disposal_volume) x_offset_source = 0 x_offset_dest = 0 rinse = False for i in range(num_cols): ctx.comment("Column: " + str(i)) if not m300.hw_pipette['has_tip']: pick_up(m300) for j, transfer_vol in enumerate(elution_transfer_vol): #Calculate pickup_height based on remaining volume and shape of container [pickup_height, change_col] = calc_height(Elution, multi_well_rack_area, transfer_vol * 8) ctx.comment('Aspirate from reservoir column: ' + str(Elution.col)) ctx.comment('Pickup height is ' + str(round(pickup_height, 2)) + ' mm') #if j!=0: # rinse = False move_vol_multi(m300, reagent=Elution, source=Elution.reagent_reservoir[Elution.col], dest=elution_destinations[i], vol=transfer_vol, x_offset_source=x_offset_source, x_offset_dest=x_offset_dest, pickup_height=pickup_height, rinse=rinse, avoid_droplet=False, wait_time=0, blow_out=False, touch_tip=True) ctx.comment(' ') if recycle_tip == True: m300.return_tip() else: 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) ctx.comment('Used tips in total: ' + str(tip_track['counts'][m300])) ############################################################################### # STEP 4 TRANSFER ELUTION ######## ctx.comment(' ') ctx.comment('###############################################') ctx.comment('Homing robot') ctx.comment('###############################################') ctx.comment(' ') ctx.home() ############################################################################### # 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 import os #os.system('mpg123 /etc/audio/speaker-test.mp3') 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 plates Kingfisher robot.') ctx.comment('Used tips 200uL in total: ' + str(tip_track['counts'][m300])) ctx.comment('Used racks 200uL in total: ' + str(tip_track['counts'][m300] / 96)) ctx.comment('Available 200uL tips: ' + str(tip_track['maxes'][m300]))
def run(ctx: protocol_api.ProtocolContext): [ _samp_cols, # column numbers containing samples _m300_mount # mount for p300-Multi ] = get_values( # noqa: F821 (<--- DO NOT REMOVE!) '_samp_cols', '_m300_mount') # custom variables if type(_samp_cols) is int: samp_cols = [_samp_cols] else: samp_cols = _samp_cols.split(",") m300_mount = _m300_mount # load modules mag_deck = ctx.load_module('magnetic module gen2', '1') # load labware mag_plate = mag_deck.load_labware( 'nest_96_wellplate_100ul_pcr_full_skirt', 'Sample Plate on MagDeck') elution_plate = ctx.load_labware( 'nest_96_wellplate_100ul_pcr_full_skirt', '3', 'Elution Plate') res12 = ctx.load_labware( 'nest_12_reservoir_15ml', '2', '12-Well Reservoir with Reagents') # load tipracks tips = [ctx.load_labware( 'opentrons_96_filtertiprack_200ul', s) for s in ['5', '6']] # load instrument m300 = ctx.load_instrument('p300_multi_gen2', m300_mount, tip_racks=tips) # helper functions # to take vol and return estimated liq height def liq_height(well): if well.diameter is not None: radius = well.diameter / 2 cse = math.pi*(radius**2) elif well.length is not None: cse = well.length*well.width else: cse = None if cse: return well.liq_vol / cse else: raise Exception("""Labware definition must supply well radius or well length and width.""") def incubate(min): ctx.comment(f'\nIncubating for {min} minutes\n') ctx.delay(minutes=min) def remove_supernatant(vol): ctx.comment(f'\nTransferring {vol}uL from wells to liquid waste\n') m300.flow_rate.aspirate = 15 for col in samp_cols: m300.pick_up_tip() m300.aspirate(vol, mag_plate['A'+str(col).strip()]) m300.dispense(vol, waste) m300.drop_tip() m300.flow_rate.aspirate = 94 # reagents beads = res12['A1'] beads.liq_vol = 45 * len(samp_cols) * 1.05 etoh = res12['A3'] etoh.liq_vol = 350 * len(samp_cols) te = res12['A5'] te.liq_vol = 50 * len(samp_cols) * 1.05 waste = res12['A11'].top(-2) # protocol # Transfer Bead Solution Transfer ctx.comment(f'\nTransferring 45uL Bead Solution \ to samples in columns {samp_cols}\n') for col in samp_cols: m300.pick_up_tip() beads.liq_vol -= 45 bead_ht = liq_height(beads) - 2 if liq_height(beads) - 2 > 1 else 1 m300.mix(3, 40, beads.bottom(bead_ht)) m300.aspirate(45, beads.bottom(bead_ht)) m300.dispense(45, mag_plate['A'+str(col).strip()]) m300.mix(5, 60, mag_plate['A'+str(col).strip()]) ctx.delay(seconds=1) m300.drop_tip() # Incubate for 5 minutes, engage magnet, incubate for 2 minutes incubate(5) mag_deck.engage() incubate(2) # remove supernatant remove_supernatant(75) mag_deck.disengage() # Perform 2 ethanol washes for i in range(2): ctx.comment(f'\nPerforming EtOH Wash {i+1}\n') for col in samp_cols: m300.pick_up_tip() etoh.liq_vol -= 150 et_ht = liq_height(etoh) - 2 if liq_height(etoh) - 2 > 1 else 1 m300.aspirate(150, etoh.bottom(et_ht)) m300.dispense(150, mag_plate['A'+str(col).strip()]) m300.mix(5, 100, mag_plate['A'+str(col).strip()]) ctx.delay(seconds=1) m300.blow_out() m300.drop_tip() mag_deck.engage() incubate(2) remove_supernatant(150) mag_deck.disengage() incubate(2) # Transfer elution buffer and elutes ctx.comment(f'\nTransferring 50uL Low TE \ to samples in columns {samp_cols}\n') for col in samp_cols: m300.pick_up_tip() te.liq_vol -= 50 te_ht = liq_height(te) - 2 if liq_height(te) - 2 > 1 else 1 m300.aspirate(50, te.bottom(te_ht)) m300.dispense(50, mag_plate['A'+str(col).strip()]) m300.mix(5, 25, mag_plate['A'+str(col).strip()]) ctx.delay(seconds=1) m300.drop_tip() incubate(2) mag_deck.engage() incubate(2) ctx.comment('\nTransferring samples to Elution Plate\n') m300.flow_rate.aspirate = 30 for col, dest in zip(samp_cols, elution_plate.rows()[0]): m300.pick_up_tip() m300.aspirate(50, mag_plate['A'+str(col).strip()]) m300.dispense(50, dest) m300.drop_tip() ctx.comment('\nProtocol complete!')
def run(protocol: protocol_api.ProtocolContext): # LABWARE tiprack300 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '8') tiprack20 = protocol.load_labware('opentrons_96_filtertiprack_20ul', '9') # tempdeck = protocol.load_module('tempdeck', '4') # plate = tempdeck.load_labware('opentrons_96_aluminumblock_generic_pcr_strip_200ul') # plate = tempdeck.load_labware('abi_96_wellplate_250ul') sectempdeck = protocol.load_module('tempdeck', '10') fuge_rack = sectempdeck.load_labware( 'opentrons_24_aluminumblock_generic_2ml_screwcap') holder_1 = protocol.load_labware( '8wstriptubesonfilterracks_96_aluminumblock_250ul', '3') holder_2 = protocol.load_labware( '8wstriptubesonfilterracks_96_aluminumblock_250ul', '6') # PIPETTES p300 = protocol.load_instrument('p300_single_gen2', 'left', tip_racks=[tiprack300]) p20 = protocol.load_instrument('p20_single_gen2', 'right', tip_racks=[tiprack20]) # REAGENTS SAMP_1mix = fuge_rack['B5'] SAMP_2mix = fuge_rack['D1'] SAMP_3mix = fuge_rack['D2'] SAMP_4mix = fuge_rack['D3'] SAMP_5mix = fuge_rack['B6'] SAMP_6mix = fuge_rack['C1'] SAMP_7mix = fuge_rack['C2'] SAMP_8mix = fuge_rack['C3'] # user inputs # num_of_sample_reps is another way of stating number of strips num_of_sample_reps = 6 holderList = [holder_1] # lists # ALL_SAMPs = [samp_1, samp_2, samp_3, samp_4, samp_5, samp_6, samp_7, WATER] SAMP_mixes = [ SAMP_1mix, SAMP_2mix, SAMP_3mix, SAMP_4mix, SAMP_5mix, SAMP_6mix, SAMP_7mix, SAMP_8mix ] # SAMP_mixes = [SAMP_1mix, SAMP_2mix, SAMP_3mix, SAMP_4mix, SAMP_5mix, SAMP_6mix, SAMP_7mix, WATER] SAMP_wells = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] # #### COMMANDS ###### # Add sample DNA, mix, distribute to strip tubes for i, mixtube in enumerate(SAMP_mixes): for y in range(0, len(holderList)): p300.pick_up_tip() p300.move_to(mixtube.bottom(40)) p300.aspirate(num_of_sample_reps * 20 * 1.08, mixtube.bottom(2)) # 6*20*1.08 = 130 protocol.delay(seconds=1) #equilibrate p300.touch_tip(v_offset=-3) holderPos = y holder = holderList[holderPos] start = 6 * y stop = num_of_sample_reps if num_of_sample_reps <= 6 * y + 6 else 6 * y + 6 # This is max it can go in cycle; can't go above e.g. A13 !<> for x in range(start, stop): # samples in 1-6, 7-12, 13-18 increments # print ("start: ", start, "stop: ", stop) row = SAMP_wells[i] dest = row + str( 2 * x + 1 - 12 * holderPos) # need +1 offset for col p300.move_to( holder[dest].bottom(40)) #move across holder in +4cm pos p300.dispense( 20, holder[dest].bottom(6), rate=0.75) # more height so tip doesn't touch pellet # p300.move_to(holder[dest].bottom(8)) # p300.blow_out(holder[dest]. bottom(8)) p300.touch_tip() p300.move_to(holder[dest].top( )) # centers tip so tip doesn't lift tubes after touch p300.move_to( holder[dest].bottom(40)) #move across holder in +4cm pos # p300.move_to(holder[dest].bottom(40)) #move back holder in +4cm pos # p300.move_to(mixtube.bottom(40)) #return to tube at +4cm so no crash into lyo tubes p300.drop_tip()
def run(ctx: protocol_api.ProtocolContext): import os 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': False, 'description': 'Make MMIX'}, 2: {'Execute': False, 'description': 'Transfer MMIX with P300'}, 3: {'Execute': True, 'description': 'Transfer MMIX with P20'}, 4: {'Execute': True, 'description': 'Clean up NC and PC wells'}, 5: {'Execute': True, 'description': 'Transfer elution'}, 6: {'Execute': True, 'description': 'Transfer PC'}, 7: {'Execute': True, 'description': 'Transfer NC'} } if STEPS[2]['Execute']==True: STEPS[3]['Execute']=False # just to make sure if P300 is being used for the MMIX, do not load P20 single 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/'+str(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 = 2, flow_rate_dispense = 4, reagent_reservoir_volume = $MMIX_total_volume, # volume_mmix_available, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) NC = Reagent(name = 'Negative control', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, # volume_mmix_available, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) PC = Reagent(name = 'Positive control', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, # volume_mmix_available, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) Elution = Reagent(name='Elution', rinse=False, flow_rate_aspirate = 1, flow_rate_dispense = 2, reagent_reservoir_volume=50, delay=1, num_wells=num_cols, # num_cols comes from available columns h_cono=0, v_fondo=0 ) tackpath = Reagent(name = 'MMIX_multiplex_tackpath', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) covid_assay = Reagent(name = 'Covid19_assay', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) mmix_water = Reagent(name = 'nuclease_free_water', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) component4 = Reagent(name = 'component4', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) component5 = Reagent(name = 'component5', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) component6 = Reagent(name = 'component6', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) component7 = Reagent(name = 'component7', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) component8 = Reagent(name = 'component8', rinse = False, flow_rate_aspirate = 1, flow_rate_dispense = 1, reagent_reservoir_volume = 1000, num_wells = 1, #change with num samples delay = 0, h_cono = h_cone, v_fondo = volume_cone # V cono ) MMIX.vol_well = MMIX.vol_well_original PC.vol_well = PC.vol_well_original NC.vol_well = NC.vol_well_original Elution.vol_well = Elution.vol_well_original tackpath.vol_well = tackpath.vol_well_original covid_assay.vol_well = covid_assay.vol_well_original mmix_water.vol_well = mmix_water.vol_well_original component4.vol_well = component4.vol_well_original component5.vol_well = component5.vol_well_original component6.vol_well = component6.vol_well_original component7.vol_well = component7.vol_well_original component8.vol_well = component8.vol_well_original ################## Assign class type reactives to a summary MMIX_components=[tackpath, covid_assay, mmix_water, component4, component5, component6, component7, component8] MMIX_components=MMIX_components[:len(MMIX_make[mmix_selection])] ################## ################## # 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. a=[] for i in range(0, len(l), n): a.append( l[i:i + n]) return a def distribute_custom(pipette, reagent, 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 air_gap=10 pipette.aspirate((len(dest) * volume) + extra_dispensal, src.bottom(pickup_height), rate = reagent.flow_rate_aspirate) pipette.touch_tip(speed=20, v_offset=-5) pipette.move_to(src.top(z=5)) pipette.aspirate(air_gap, rate = reagent.flow_rate_aspirate) # air gap for d in dest: pipette.dispense(air_gap, d.top(), rate = reagent.flow_rate_dispense) drop = d.top(z = disp_height) pipette.dispense(volume, drop, rate = reagent.flow_rate_dispense) ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent pipette.move_to(d.top(z=5)) pipette.aspirate(air_gap,d.top(z=5), rate = reagent.flow_rate_aspirate) # 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, 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), rate = 2) 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,touch_tip=False): ''' 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)) if touch_tip == True: pipet.touch_tip(speed = 20, v_offset = -5, radius = 0.9) def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.3, extra_volume = 30): 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 ############################################ # tempdeck tempdeck = ctx.load_module('tempdeck', '1') #temdeck for qpcr samples tempdeck.set_temperature(temperature) tempdeck_two = ctx.load_module('tempdeck', '4') #tempdeck for MMIX, PC and NC tempdeck_two.set_temperature(temperature) #################################### # load labware and modules # 24 well rack tuberack = tempdeck_two.load_labware( 'opentrons_24_aluminumblock_generic_2ml_screwcap', 'Bloque Aluminio opentrons 24 screwcaps 2000 µL ') ################################## # qPCR plate - final plate, goes to PCR qpcr_plate = tempdeck.load_labware( 'abi_fast_qpcr_96_alum_opentrons_100ul', 'chilled qPCR final plate') ################################## # waste reservoir waste_reservoir = ctx.load_labware( 'nest_1_reservoir_195ml', '9', 'waste reservoir') waste = waste_reservoir.wells()[0] # referenced as reservoir ################################## # Sample plate - comes from B source_plate = ctx.load_labware( "kingfisher_std_96_wellplate_550ul", '2', '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'] ] # pipettes m20 = ctx.load_instrument( 'p20_multi_gen2', mount='left', tip_racks=tips20) if STEPS[2]['Execute']==True: p300 = ctx.load_instrument( 'p300_single_gen2', mount='right', tip_racks=tips200) # used tips counter tip_track = { 'counts': {p300: 0, m20: 0}, 'maxes': {p300: len(tips200) * 96, m20: len(tips20)*96} } else: tips20mmix = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['8'] ] p20 = ctx.load_instrument( 'p20_single_gen2', mount='right', tip_racks=tips20mmix) # used tips counter tip_track = { 'counts': {p20: 0, m20: 0}, 'maxes': {p20: len(tips200) * 96, m20: len(tips20)*96} } ################################################################################ # 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) MMIX_components_location=tuberack.wells()[MMIX_make_location:(MMIX_make_location + len(MMIX_make[mmix_selection]))] ctx.comment('Wells in: '+ str(tuberack.rows()[0][:MMIX.num_wells]) + ' element: '+str(MMIX.reagent_reservoir[MMIX.col])) PC.reagent_reservoir = tuberack.rows()[1][:1] NC.reagent_reservoir = tuberack.rows()[2][:1] # 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] pc_well_old = source_plate.wells()[(NUM_SAMPLES-2):(NUM_SAMPLES-1)][0] nc_well_old = source_plate.wells()[(NUM_SAMPLES-1):(NUM_SAMPLES)][0] pc_well = qpcr_plate.wells()[(NUM_SAMPLES-2):(NUM_SAMPLES-1)][0] nc_well = qpcr_plate.wells()[(NUM_SAMPLES-1):(NUM_SAMPLES)][0] # Divide destination wells in small groups for P300 pipette dests = list(divide_destinations(pcr_wells, size_transfer)) ########## # 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: Make Master MIX ############################################################################ ctx._hw_manager.hardware.set_lights(rails=False) # set lights off when using MMIX STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() # Check if among the pipettes, p300_single is installed used_vol=[] ctx.comment('#######################################################') ctx.comment('Selected MMIX: '+MMIX_available[mmix_selection]) ctx.comment('#######################################################') for i,[source, vol] in enumerate(zip(MMIX_components_location, MMIX_make[mmix_selection])): pick_up(p300) ctx.comment('#######################################################') ctx.comment('Add component: '+MMIX_components[i].name) ctx.comment('#######################################################') if (vol + air_gap_vol) > pipette_allowed_capacity: # because 200ul is the maximum volume of the tip we will choose 180 # calculate what volume should be transferred in each step vol_list=divide_volume(vol, pipette_allowed_capacity) for vol in vol_list: move_vol_multichannel(p300, reagent=MMIX_components[i], source=source, dest=MMIX.reagent_reservoir[0], vol=vol, air_gap_vol=air_gap_vol, x_offset = x_offset,pickup_height=1, # should be changed with picku_up_height calculation rinse=False, disp_height=-10,blow_out=True, touch_tip=True) else: move_vol_multichannel(p300, reagent=MMIX_components[i], source=source, dest=MMIX.reagent_reservoir[0], vol=vol, air_gap_vol=air_gap_vol, x_offset=x_offset, pickup_height=1, # should be changed with picku_up_height calculation rinse=False, disp_height=-10,blow_out=True, touch_tip=True) if i+1<len(MMIX_components): p300.drop_tip() else: ctx.comment('#######################################################') ctx.comment('Final mix') ctx.comment('#######################################################') custom_mix(p300, reagent = MMIX, location = MMIX.reagent_reservoir[0], vol = 180, rounds = 5, blow_out = True, mix_height = 2, x_offset = x_offset) p300.drop_tip() tip_track['counts'][p300]+=1 end = datetime.now() time_taken = (end - start) ctx.comment('#######################################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) ctx.comment('#######################################################') STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 2: Transfer Master MIX with P300 ############################################################################ ctx._hw_manager.hardware.set_lights(rails=False) # set lights off when using MMIX STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() p300.pick_up_tip() for dest in pcr_wells: [pickup_height, col_change] = calc_height(MMIX, area_section_screwcap, volume_mmix) move_vol_multichannel(p300, reagent = MMIX, source = MMIX.reagent_reservoir[MMIX.col], dest = dest, vol = volume_mmix, air_gap_vol = air_gap_mmix, x_offset = x_offset, pickup_height = pickup_height, disp_height = -10, rinse = False, blow_out=True, touch_tip=False) #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('#######################################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) ctx.comment('#######################################################') STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 3: Transfer Master MIX with P20 ############################################################################ ctx._hw_manager.hardware.set_lights(rails=False) # set lights off when using MMIX STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() p20.pick_up_tip() for dest in pcr_wells: [pickup_height, col_change] = calc_height(MMIX, area_section_screwcap, volume_mmix) move_vol_multichannel(p20, reagent = MMIX, source = MMIX.reagent_reservoir[MMIX.col], dest = dest, vol = volume_mmix, air_gap_vol = 0, x_offset = x_offset, pickup_height = pickup_height, disp_height = -10, rinse = False, blow_out=True, touch_tip=True) #used_vol.append(used_vol_temp) p20.drop_tip() tip_track['counts'][p20]+=1 #MMIX.unused_two = MMIX.vol_well end = datetime.now() time_taken = (end - start) ctx.comment('#######################################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) ctx.comment('#######################################################') STEPS[STEP]['Time:'] = str(time_taken) ctx.pause('Put samples please') ############################################################################ # STEP 3: Clean up PC and NC well ############################################################################ ctx._hw_manager.hardware.set_lights(rails=False) # set lights off when using MMIX STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() clean_up_wells=[pc_well_old,nc_well_old] p20.pick_up_tip() for src in clean_up_wells: for i in range(3): move_vol_multichannel(p20, reagent = PC, source = src, dest = waste, vol = 20, air_gap_vol = 0, x_offset = [0,0], pickup_height = 0.2, disp_height = 5, rinse = False, blow_out=True, touch_tip=False,post_airgap=False, post_airgap_vol=0) p20.drop_tip() tip_track['counts'][p20]+=1 #MMIX.unused_two = MMIX.vol_well end = datetime.now() time_taken = (end - start) ctx.comment('#######################################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) ctx.comment('#######################################################') STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 4: TRANSFER Samples ############################################################################ ctx._hw_manager.hardware.set_lights(rails=False) # set lights off when using MMIX 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 = Elution, source = s, dest = d, vol = volume_sample, air_gap_vol = air_gap_sample, x_offset = x_offset, pickup_height = 0.3, disp_height = -10, rinse = False, blow_out=True, touch_tip=False, post_airgap=False) # Mixing custom_mix(m20, Elution, d, vol=5, rounds=2, blow_out=True, mix_height=6, post_dispense=True, source_height=0.5, x_offset=[0,0],touch_tip=True) m20.drop_tip() tip_track['counts'][m20]+=8 end = datetime.now() time_taken = (end - start) ctx.comment('#######################################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) ctx.comment('#######################################################') STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 5: Transfer PC with P20 ############################################################################ ctx._hw_manager.hardware.set_lights(rails=False) # set lights off when using MMIX STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() p20.pick_up_tip() #[pickup_height, col_change] = calc_height(PC, area_section_screwcap, volume_mmix) move_vol_multichannel(p20, reagent = PC, source = PC.reagent_reservoir[PC.col], dest = pc_well, vol = volume_pc, air_gap_vol = 0, x_offset = x_offset, pickup_height = 0.2, disp_height = -10, 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('#######################################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) ctx.comment('#######################################################') STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 6: Transfer NC with P20 ############################################################################ ctx._hw_manager.hardware.set_lights(rails=False) # set lights off when using MMIX STEP += 1 if STEPS[STEP]['Execute'] == True: start = datetime.now() p20.pick_up_tip() move_vol_multichannel(p20, reagent = NC, source = NC.reagent_reservoir[NC.col], dest = nc_well, vol = volume_nc, air_gap_vol = 0, x_offset = x_offset, pickup_height = 0.2, disp_height = -10, rinse = False, blow_out=True, touch_tip=True) p20.drop_tip(home_after=False) tip_track['counts'][p20]+=1 #MMIX.unused_two = MMIX.vol_well end = datetime.now() time_taken = (end - start) ctx.comment('#######################################################') ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) ctx.comment('#######################################################') STEPS[STEP]['Time:'] = str(time_taken) tempdeck.deactivate() tempdeck_two.deactivate() ############################################################################ # 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 ctx.home() time.sleep(2) import os #os.system('mpg123 -f -8000 /etc/audio/speaker-test.mp3 &') '''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[3]['Execute'] == True & STEPS[4]['Execute'] == True): ctx.comment('20 ul Used tips in total: ' + str(tip_track['counts'][m20]+tip_track['counts'][p20])) ctx.comment('20 ul Used racks in total: ' + str((tip_track['counts'][m20]+tip_track['counts'][p20]) / 96)) if (STEPS[2]['Execute'] == True & STEPS[4]['Execute'] == True): 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'][m20])) ctx.comment('20 ul Used racks in total: ' + str((tip_track['counts'][m20] / 96))) if (STEPS[2]['Execute'] == True & STEPS[5]['Execute'] == False): 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[3]['Execute'] == False & STEPS[4]['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))) ctx.comment('Finished! \nMove plate to PCR')
def run(protocol: protocol_api.ProtocolContext): #global cmdList, deckMap, amtList os.chdir(mainwd) amtList, cmdList, deckMap = ReadCSV_Dat(mainInput) skip_rows = input('Skip rows ... ') skip_rows = int(skip_rows) ############ LOAD LABWARES ############ tipLocs = [] for i in range(11): #load labware labware_name = deckMap["labware_"+str(i+1)] if(('empty' not in labware_name) and labware_name != 'TRASH'): deck_position = int(list(deckMap.keys())[i].split('_')[1]) globals()[list(deckMap.keys())[i]] = protocol.load_labware(labware_name, deck_position) #if labware is a tip rack, assign number to tip location(s) if('tiprack' in labware_name): tipLocs.append(globals()[list(deckMap.keys())[i]]) #load pipettes #single-channel right_pipette = protocol.load_instrument( 'p300_single', 'right', tip_racks=tipLocs) right_pipette.flow_rate.aspirate=aspirateSpeed right_pipette.flow_rate.dispense=dispenseSpeed #multi-channel -- deprecated; to be switched to p1000 single channel left_pipette = protocol.load_instrument( 'p1000_single_gen2', 'left', tip_racks=[]) left_pipette.flow_rate.aspirate=aspirateSpeed left_pipette.flow_rate.dispense=dispenseSpeed #Update Amount List for i in range(len(amtList)): if(float(amtList[i][3])<=50): amtList[i][3] = float(amtList[i][3])*1000 #get tube type if('50ml' in deckMap[amtList[i][0]]): amtList[i] = np.append(amtList[i], '50ml_falcon') elif('15ml' in deckMap[amtList[i][0]]): amtList[i] = np.append(amtList[i], '15ml_falcon') else: amtList[i] = np.append(amtList[i], '1.5ml_eppendorf') ############ EXECUTE COMMANDS ############ #iterate through all command lines current_tipID = 0 #initiate tip ID iterateMarker = -1 progressMax = len(cmdList) for i in range(len(cmdList)): #subset cmdRow = cmdList[i] if(i+1 < skip_rows): extraReport = "-- SKIP!" else: extraReport = "" print("Progress\t: " + str(int(round(i/progressMax*100))) + "% " + extraReport) print(cmdRow) #parse all informations source_ware = cmdRow[0] source_well = cmdRow[1].split(', ') target_ware = cmdRow[2] target_well = cmdRow[3].split(', ') transfer_amt = float(cmdRow[4]) #only one transfer amount is allowed if(float(cmdRow[5]) > 0): mix_amt = max(float(cmdRow[5]), 300) else: mix_amt = 0 tipID = int(cmdRow[6]) #choose pipette -- multichannel block deprecated if(len(target_well)==8 and len(source_well)==8): #operations for multichannel pipette pipette = "left_pipette" #pick up tip if needed if(tipID != current_tipID): left_pipette.pick_up_tip() #pick up tip if tipID changes current_tipID = tipID #update tip id #Main Transfers source_well_h = source_well[0] target_well_h = target_well[0] remV = transfer_amt while(remV>0): cur_transfer = min(300, remV) if(remV-cur_transfer < 30 and remV-cur_transfer>0): cur_transfer = cur_transfer/2 remV = remV - cur_transfer ########### NOT ANNOTATED ############# / BLOCK1 // MULTIPIPETTE HEIGHT aspHs = [] dspHs = [] mixVols = [] for j in range(1, 7): #get mix volumes mixVol = GetSrcVolume(amtList, cmdRow, source_well[j]) #not annotated mixVols.append(mixVol) #not annotated #update solutions map amtList = Update_Source(amtList, cmdRow, source_well[j], cur_transfer) amtList = Update_Target(amtList, cmdRow, target_well[j], deckMap, cur_transfer) #calculate aspirate and dispense height aspH = CalTip_Aspirate(amtList, cmdRow, source_well[j]) dspH = CalTip_Dispense(amtList, cmdRow, target_well[j]) aspHs.append(aspH) dspHs.append(dspH) source_col = source_well[0][1:] target_col = target_well[0][1:] #selecting aspHs/dspHs aspHs = [i for i in aspHs if i>0] dspHs = [i for i in dspHs if i>0] mixVols = [i for i in mixVols if i>0] #not annotated aspHs_selected = min(aspHs) dspHs_selected = min(dspHs) ## temporary measure for deep wells AND 96-well plates -- fix well heights aspHs_selected = 3 #3 mm from bottom dspHs_selected = 15 #15 mm from bottom ##### #not annotated if(max(mixVols)==0): mix_amt = 0 else: mixVols.append(300) mix_amt = min(mixVols) #################################################### if(mix_amt==0): #if no mix left_pipette.transfer(cur_transfer, globals()[source_ware].wells_by_name()[source_well_h].bottom(aspHs_selected), globals()[target_ware].wells_by_name()[target_well_h].bottom(dspHs_selected), new_tip='never', disposal_volume=0) else: #if mix left_pipette.transfer(cur_transfer, globals()[source_ware].wells_by_name()[source_well_h].bottom(aspHs_selected), globals()[target_ware].wells_by_name()[target_well_h].bottom(dspHs_selected), new_tip='never', mix_before = (2, cur_transfer), disposal_volume=0) #blow out on top of the current slot left_pipette.blow_out(globals()[target_ware].wells_by_name()[target_well_h].bottom(dspHs_selected)) #check if tip need to be trashed afterwards if(i == len(cmdList)-1): #if this is the last operation left_pipette.drop_tip() elif(int(cmdRow[6]) != int(cmdList[i+1][6])): #drop if different tip id is detected left_pipette.drop_tip() else: #IF NOT MULTI PIPETTE pipette = 'right_pipette' cur_source_well = source_well[0] #select only the first source #pick up tip if needed if(i+1 >= skip_rows): if(tipID != current_tipID or (i+1 == skip_rows)): right_pipette.pick_up_tip() #pick up tip if tipID changes current_tipID = tipID #update tip id #iterate through all target wells for j in range(len(target_well)): if(mix_amt>0): mix_amt = min(GetSrcVolume(amtList, cmdRow, cur_source_well), 300) #Main Transfers remV = transfer_amt while(remV>0): #Calculate current transfer amount cur_transfer = min(300, remV) if(remV-cur_transfer < 30 and remV-cur_transfer>0): cur_transfer = cur_transfer/2 remV = remV - cur_transfer #update solutions map amtList = Update_Source(amtList, cmdRow, cur_source_well, cur_transfer) amtList = Update_Target(amtList, cmdRow, target_well[j], deckMap, cur_transfer) #calculate aspirate and dispense height aspH = CalTip_Aspirate(amtList, cmdRow, cur_source_well) dspH = CalTip_Dispense(amtList, cmdRow, target_well[j]) #Mix boolean if(i+1 >= skip_rows): if(mix_amt==0): #if no mix right_pipette.transfer(cur_transfer, globals()[source_ware].wells_by_name()[cur_source_well].bottom(aspH), globals()[target_ware].wells_by_name()[target_well[j]].bottom(dspH), new_tip='never', disposal_volume=0) else: #if mix right_pipette.transfer(cur_transfer, globals()[source_ware].wells_by_name()[cur_source_well].bottom(aspH), globals()[target_ware].wells_by_name()[target_well[j]].bottom(dspH), new_tip='never', mix_before = (3, cur_transfer), disposal_volume=0) #blow out on top of the current slot right_pipette.blow_out(globals()[target_ware].wells_by_name()[target_well[j]].bottom(dspH)) #check if tip need to be trashed afterwards if(i+1 >= skip_rows): if(i == len(cmdList)-1): #if this is the last operation right_pipette.drop_tip() elif(int(cmdRow[6]) != int(cmdList[i+1][6])): #drop if different tip id is detected right_pipette.drop_tip()
def run(protocol: protocol_api.ProtocolContext): # LABWARE fuge_rack = protocol.load_labware('vwr_24_tuberack_1500ul', '1') stds_rack = protocol.load_labware('vwr_24_tuberack_1500ul', '2') tiprack300 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '8') tiprack20 = protocol.load_labware('opentrons_96_filtertiprack_20ul', '9') tempdeck = protocol.load_module('tempdeck', '10') # plate = tempdeck.load_labware('amplifyt_96_aluminumblock_300ul') plate = tempdeck.load_labware('abi_96_wellplate_250ul') # PIPETTES p300 = protocol.load_instrument('p300_single_gen2', 'left', tip_racks=[tiprack300]) p20 = protocol.load_instrument('p20_single_gen2', 'right', tip_racks=[tiprack20]) # REAGENTS # sds_rack sN_mix = stds_rack['D1'] # empty; receives BPW_mix and water for stds std_1 = stds_rack['A3'] # 990ul Water std_2 = stds_rack['A4'] # 900ul water std_3 = stds_rack['A5'] # 900ul water std_4 = stds_rack['A6'] # 900ul water std_5 = stds_rack['B3'] # 900ul water std_6 = stds_rack['B4'] # 900ul water std_7 = stds_rack['B5'] # 900ul water water = stds_rack['B6'] # 1000ul water std_1mix = stds_rack['C3'] # empty std_2mix = stds_rack['C4'] # empty std_3mix = stds_rack['C5'] # empty std_4mix = stds_rack['C6'] # empty std_5mix = stds_rack['D3'] # empty std_6mix = stds_rack['D4'] # empty std_7mix = stds_rack['D5'] # empty NTC_mix = stds_rack['D6'] # empty, receives sN_mix and water as NTC #fuge_rack # bpwd_mix = fuge_rack['A1'] #empty # liquid_trash = fuge_rack['B1'] MIX_bw = fuge_rack['D1'] # see sheet, but gen around 1705 ul; use 2mL tube probe_10uM = fuge_rack['C1'] fwd_10uM = fuge_rack['C2'] # min 300ul rev_10uM = fuge_rack['D2'] # min 300ul tube_upp = fuge_rack['A4'] # e.g. 0.625uM # empty tube_mid = fuge_rack['A5'] # e.g. 1.25uM # empty tube_low = fuge_rack['A6'] # e.g. 2.5uM # empty probe_mix_1 = fuge_rack['C4'] # e.g. 0.625uM # empty probe_mix_2 = fuge_rack['C5'] # e.g. 1.25uM # empty probe_mix_3 = fuge_rack['C6'] # e.g. 2.5uM # empty probe_mix_4 = fuge_rack['D4'] # e.g. 5.0uM # empty probe_mix_5 = fuge_rack['D5'] # e.g. 7.5uM # empty probe_mix_6 = fuge_rack['D6'] # e.g. 10uM # empty # user inputs p300_max_vol = 200 orig_F_conc = 10 # What is the starting F primer concentration? (in uM) orig_R_conc = 10 # What is the starting R primer concentration? (in uM) orig_P_conc = 10 # What is probe starting concentration? (in uM) P_conc = 300 # What is the probe concentration for the stds? P_50 = 50 # Probe at lowest conc. P_100 = 100 # Probe at conc P_200 = 200 # Probe at conc P_400 = 400 # Probe at conc P_600 = 600 # Probe at conc P_800 = 800 # Probe at highest conc. sample_reps = 24 # How many samples at each F, R conc will be run? tot_stds = 21 # How many wells of standards will be run? (in # wells) tot_NTCs = 3 # How many wells of NTCs will be run? (in # wells) tot_samp = 72 # How many samples with varying conditions will be run? (in # wells) rxn_base = 8.86 # Everything in PCR buffer (Mg2+, dNTPs, polymerase, enhancers, stabilizers, sugars, etc.). (in uL) rxn_base_plus_water = 14.8 # This value set from other experiments. tot_rxn_vol = 20 # What is total volume of PCR reaction in plate/tube? (in ul) F_upp = 500 # F primer at highest concentration. (in nM) F_mid = 400 # What is the constant F primer concentration for standards? This should be guess or from literature or empirically determined. (in nM) F_low = 300 # F primer at concentration. (in nM) R_upp = 500 # R primer at lowest concentration. (in nM) R_mid = 400 # What is the constant R primer concentration for standards? This should be guess or from literature or empirically determined. (in nM) R_low = 300 # R primer at concentration. (in nM) dna_per_rxn = 2 # How much standard, positive control or NTC to add per well. std_NTC_reps = 3 # How many standard and NTC replicates? (int) P_reps = 12 # How many wells will use probe primer at particular concentration? (int) P_vol_rxn = 1.6 # What vol of probe will be added to reaction? (in ul) P_int_vol = 50 # What is the volume of F intermediate primer in new tube? (in ul) percent_waste = 0.2 # What percentage waste? (decimal) sN_mix_waste_offset = 0.05 # How much percent_waste offset should sN_mix use? This calculated as percent_waste-sN_mix_overage = percent_waste for sN_mix_overage e.g. (20-7=13%) Should not be 0 otherwise offset = percent_waste. (decimal) samp_int_mix_waste_offset = 0.05 # How much percent_waste offset should R_mix use? This calculated as percent_waste-R_mix_overage = percent_waste for R_mix_overage e.g. (20-=13%) If 0, then offset = percent_waste. (decimal) std_NTC_waste_offset = 0.07 # How much percent_waste offset should std_NTC use? (decimal) mix_samp_XFR_well_waste_offset = 0.08 #How much should offset be from each sample tube, upper, mid, low to the plate wells? Will receive P, be mixed and then aliquoted to 3 wells. # calcs tot_rxns = tot_stds + tot_NTCs + tot_samp # Calc what is total # rxns. (int) tot_stds_NTC = tot_stds + tot_NTCs # Calc number of standards and nontemplate controls. (int) rxn_vol_no_dna = tot_rxn_vol - dna_per_rxn # Calc what is volume of PCR rxn with no DNA added. (in ul) mix_bw_tot = rxn_base_plus_water * tot_rxns * ( 1 + percent_waste) # Tube with mix containing base and water. mix_bw_XFR_mix_sn = rxn_base_plus_water * tot_stds_NTC * ( 1 + percent_waste - sN_mix_waste_offset ) # Transfer this amount from MIX_bw to new tube to receive F, R, P for std and NTC prep std_vol_F_per_rxn = F_low / 1000 * tot_rxn_vol / orig_F_conc # Calc adding this much F primer to each std rxn std_vol_R_per_rxn = R_low / 1000 * tot_rxn_vol / orig_R_conc # Calc adding this much R primer to each std rxn std_vol_P_per_rxn = P_conc / 1000 * tot_rxn_vol / orig_P_conc # Calc adding this much Probe to each std rxn std_vol_water_per_rxn = 18 - ( rxn_base_plus_water + std_vol_F_per_rxn + std_vol_R_per_rxn + std_vol_P_per_rxn ) # How much water is added to the std_ntc reactions. Should be 18ul to receive 2ul DNA. (in ul) std_vol_F_mix = std_vol_F_per_rxn * tot_stds_NTC * ( 1 + percent_waste - sN_mix_waste_offset ) # How much F primer to add to MIX_sn? std_vol_R_mix = std_vol_R_per_rxn * tot_stds_NTC * ( 1 + percent_waste - sN_mix_waste_offset ) # How much R primer to add to MIX_sn? std_vol_P_mix = std_vol_P_per_rxn * tot_stds_NTC * ( 1 + percent_waste - sN_mix_waste_offset ) # How much P primer to add to MIX_sn? std_vol_water_mix = std_vol_water_per_rxn * tot_stds_NTC * ( 1 + percent_waste - sN_mix_waste_offset ) # How much water to add to MIX_sn? mix_sn_tot = mix_bw_XFR_mix_sn + std_vol_F_mix + std_vol_R_mix + std_vol_P_mix + std_vol_water_mix # Mix containing base+water+F,R,P. Needs to be aliquoted to std_int tubes mix_sn_XFR_to_std_int = std_NTC_reps * rxn_vol_no_dna * ( 1 + percent_waste - std_NTC_waste_offset ) # transfer this amount to std_int tubes std_dna_XFR_to_std_int = 6.72 #transfer this amount DNA to std_int_tubes to mix and aliquot to 3 wells dna_10x_dna_per_rxn = dna_per_rxn / 10 # Dilute DNA vol added by 10x and use std_4 instead of std_5 to save vol for P and F, R at upp mix_bw_XFR_samp_int = sample_reps * rxn_base_plus_water * ( 1 + percent_waste - samp_int_mix_waste_offset ) # transfer this amount from mix_bw to a new sample intermediate tube for diff values of F, R conc. Need to add 2ul from DNA dna_XFR_samp_int = sample_reps * dna_10x_dna_per_rxn * ( 1 + percent_waste - samp_int_mix_waste_offset ) # Add this DNA to a tube and mix before aliquoting to samp_int F_mid_rxn = F_mid / 1000 * tot_rxn_vol / orig_F_conc # How much F @ mid conc to add to rxn. F_mid_mix = F_mid_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much F to add to mix R_mid_rxn = R_mid / 1000 * tot_rxn_vol / orig_R_conc # How much R @ mid conc to add to rxn. R_mid_mix = R_mid_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much R to add to mix F_low_rxn = F_low / 1000 * tot_rxn_vol / orig_F_conc # How much F @ low conc to add to rxn. F_low_mix = F_low_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much F to add to mix R_low_rxn = R_low / 1000 * tot_rxn_vol / orig_R_conc # How much R @ low conc to add to rxn. R_low_mix = R_low_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much R to add to mix F_upp_rxn = F_upp / 1000 * tot_rxn_vol / orig_F_conc # How much F @ upp conc to add to rxn. F_upp_mix = F_upp_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much F to add to mix R_upp_rxn = R_upp / 1000 * tot_rxn_vol / orig_R_conc # How much R @ upp conc to add to rxn. R_upp_mix = R_upp_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much R to add to mix water_upp_rxn = tot_rxn_vol - (rxn_base_plus_water + dna_10x_dna_per_rxn + F_upp_rxn + R_upp_rxn + P_vol_rxn ) # How much water to add to upp rxn water_upp_mix = water_upp_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much water to add to upp mix water_low_rxn = 20 - (dna_10x_dna_per_rxn + F_low_rxn + R_low_rxn + rxn_base_plus_water + P_vol_rxn ) # How much water to add to low rxn water_low_mix = water_low_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # How much water to add to low mix water_mid_rxn = 20 - (rxn_base_plus_water + P_vol_rxn + dna_10x_dna_per_rxn + F_mid_rxn + R_mid_rxn ) # how much water to add to mid rxn water_mid_mix = water_mid_rxn * sample_reps * ( 1 + percent_waste - samp_int_mix_waste_offset ) # how much water to add to mid mix MIX_upp_samp_int = mix_bw_XFR_samp_int + dna_XFR_samp_int + F_upp_mix + R_upp_mix + water_upp_mix # How much in upper intermediate sample tube? MIX_mid_samp_int = mix_bw_XFR_samp_int + dna_XFR_samp_int + F_mid_mix + R_mid_mix + water_mid_mix # How much in middle intermediate sample tube? MIX_low_samp_int = mix_bw_XFR_samp_int + dna_XFR_samp_int + F_low_mix + R_low_mix + water_low_mix # How much in lower intermediate sample tube? mix_samp_XFR_to_well = 82.432 # How much to add to plate wells P_50_int_conc = P_50 / 1000 * tot_rxn_vol / P_vol_rxn # What intermediate (int) concentration of Probe is needed such that by adding 1.6ul I obtain a final concentration of 50, 100, 200…etc in rxn well? (in uM) P_100_int_conc = P_100 / 1000 * tot_rxn_vol / P_vol_rxn # What intermediate (int) concentration of Probe is needed such that by adding 1.6ul I obtain a final concentration of 50, 100, 200…etc in rxn well? (in uM) P_200_int_conc = P_200 / 1000 * tot_rxn_vol / P_vol_rxn # What intermediate (int) concentration of Probe is needed such that by adding 1.6ul I obtain a final concentration of 50, 100, 200…etc in rxn well? (in uM) P_400_int_conc = P_400 / 1000 * tot_rxn_vol / P_vol_rxn # What intermediate (int) concentration of Probe is needed such that by adding 1.6ul I obtain a final concentration of 50, 100, 200…etc in rxn well? (in uM) P_600_int_conc = P_600 / 1000 * tot_rxn_vol / P_vol_rxn # What intermediate (int) concentration of Probe is needed such that by adding 1.6ul I obtain a final concentration of 50, 100, 200…etc in rxn well? (in uM) P_800_int_conc = P_800 / 1000 * tot_rxn_vol / P_vol_rxn # What intermediate (int) concentration of Probe is needed such that by adding 1.6ul I obtain a final concentration of 50, 100, 200…etc in rxn well? (in uM) P_50_int_primer = P_50_int_conc * P_int_vol / orig_P_conc # What amount of primer should be added to generate int F primer conc? (in ul) P_100_int_primer = P_100_int_conc * P_int_vol / orig_P_conc # What amount of water should be added to generate int F primer conc? (in ul) P_200_int_primer = P_200_int_conc * P_int_vol / orig_P_conc # What amount of primer should be added to generate int F primer conc? (in ul) P_400_int_primer = P_400_int_conc * P_int_vol / orig_P_conc # What amount of water should be added to generate int F primer conc? (in ul) P_600_int_primer = P_600_int_conc * P_int_vol / orig_P_conc # What amount of primer should be added to generate int F primer conc? (in ul) P_800_int_primer = P_800_int_conc * P_int_vol / orig_P_conc # What amount of water should be added to generate int F primer conc? (in ul) P_50_int_water = P_int_vol - P_50_int_primer # What amount of primer should be added to generate int F primer conc? (in ul) P_100_int_water = P_int_vol - P_100_int_primer # What amount of water should be added to generate int F primer conc? (in ul) P_200_int_water = P_int_vol - P_200_int_primer # What amount of primer should be added to generate int F primer conc? (in ul) P_400_int_water = P_int_vol - P_400_int_primer # What amount of water should be added to generate int F primer conc? (in ul) P_600_int_water = P_int_vol - P_600_int_primer # What amount of primer should be added to generate int F primer conc? (in ul) P_800_int_water = P_int_vol - P_800_int_primer # What amount of water should be added to generate int F primer conc? (in ul) p_int_XFR_to_well = P_vol_rxn * 4 * ( 1 + percent_waste - mix_samp_XFR_well_waste_offset ) # What amount of probe intermediate to add to bolus in wells? #checks print("mix_bw_tot", mix_bw_tot) print("mix_bw_tot", mix_sn_tot) print("mix_bw_XFR_samp_int", mix_bw_XFR_samp_int) print("MIX_upp_samp_int", MIX_upp_samp_int) print("MIX_low_samp_int", MIX_low_samp_int) print("MIX_low_samp_int", MIX_low_samp_int) # lists # plate_col = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] F_samp_vols = [F_upp_mix, F_mid_mix, F_low_mix] R_samp_vols = [R_upp_mix, R_mid_mix, R_low_mix] plate_rows = ['A', 'B', 'C', 'D', 'E', 'F'] # all_fwd = [fwd_1, fwd_2, fwd_3, fwd_4, fwd_5, fwd_6] std_tubes = [std_1, std_2, std_3, std_4, std_5, std_6, std_7, water] std_mixes = [ std_1mix, std_2mix, std_3mix, std_4mix, std_5mix, std_6mix, std_7mix, NTC_mix ] std_wells = ['G1', 'G4', 'G7', 'G10', 'H1', 'H4', 'H7', 'H10'] samp_tubes = [tube_upp, tube_mid, tube_low] probe_mixes = [ probe_mix_1, probe_mix_2, probe_mix_3, probe_mix_4, probe_mix_5, probe_mix_6 ] P_in_mix = [ P_50_int_primer, P_100_int_primer, P_200_int_primer, P_400_int_primer, P_600_int_primer, P_800_int_primer ] W_in_mix = [ P_50_int_water, P_100_int_water, P_200_int_water, P_400_int_water, P_600_int_water, P_800_int_water ] # #### COMMANDS ###### # prepare sN_mix # add MIX_bw to sN_mix tube p300.pick_up_tip() p300.flow_rate.aspirate = 92.86 #default p300.flow_rate.dispense = 92.86 #default MIX_bw_heights = tip_heights( mix_bw_tot, len(split_asp(mix_bw_XFR_mix_sn, p300_max_vol)), split_asp(mix_bw_XFR_mix_sn, p300_max_vol)[0]) p300.mix(3, 200, MIX_bw.bottom(MIX_bw_heights[0])) p300.flow_rate.aspirate = 40 #default p300.flow_rate.dispense = 40 #default for j in range(len(split_asp(mix_bw_XFR_mix_sn, p300_max_vol))): amt = split_asp(mix_bw_XFR_mix_sn, p300_max_vol)[j] p300.aspirate(amt, MIX_bw.bottom(MIX_bw_heights[j])) h = tip_heights(amt + amt * j, 1, 0)[0] p300.dispense(amt, sN_mix.bottom(h + 5)) p300.blow_out(sN_mix.bottom(20)) # want to be above liquid level p300.touch_tip() p300.drop_tip() # transfer water to sN__mix p300.flow_rate.aspirate = 40 #default p300.flow_rate.dispense = 40 #default p300.transfer( std_vol_water_mix, # 27.6ul water.bottom(20), sN_mix.bottom(tip_heights(mix_bw_XFR_mix_sn, 1, 0)[0]), blow_out=True, blowout_location='destination well') # transfer F primer @ std conditions to sN__mix p300.transfer( std_vol_F_mix, #22.08ul fwd_10uM.bottom(3), sN_mix.bottom(tip_heights(mix_bw_XFR_mix_sn, 1, 0)[0]), blow_out=True, mix_after=(2, 30), blowout_location='destination well') # transfer R primer @ std conditions to sN__mix p300.transfer( #some resid fluid on outside std_vol_R_mix, #22.08ul rev_10uM.bottom(3), sN_mix.bottom(tip_heights(mix_bw_XFR_mix_sn, 1, 0)[0]), blow_out=True, mix_after=(2, 30), blowout_location='destination well') # transfer Probe @ std conditions to sN__mix p20.flow_rate.aspirate = 4 p20.flow_rate.dispense = 4 p20.transfer( #some resid fluid on outside std_vol_P_mix, #16.56ul probe_10uM.bottom(3), sN_mix.bottom(tip_heights(mix_bw_XFR_mix_sn, 1, 0)[0]), blow_out=True, mix_after=(2, 20), blowout_location='destination well') p300.flow_rate.aspirate = 92.86 #default p300.flow_rate.dispense = 92.86 #default p20.flow_rate.aspirate = 7.56 p20.flow_rate.dispense = 7.56 # transfer sN_mix to intermediate tubes (std_mixes) std_mix_heights = tip_heights(mix_sn_tot, len(std_mixes), mix_sn_XFR_to_std_int) #[13,11,8,6,4,2,0] print(mix_bw_XFR_mix_sn) print(mix_sn_tot) print(std_mix_heights) p300.pick_up_tip() p300.mix(7, 200, sN_mix.bottom(std_mix_heights[0])) #10mm from bottom p300.flow_rate.aspirate = 30 p300.flow_rate.dispense = 40 # p300.well_bottom_clearance.aspirate = std_mix_heights[0] #mm for tube, h in zip(std_mixes, std_mix_heights): # p300.well_bottom_clearance.aspirate = h #mm p300.aspirate(mix_sn_XFR_to_std_int, sN_mix.bottom(h)) # 18 * 3 * 1.12-0.05= 54 + 6 =60ul protocol.delay(seconds=2) #tip equilibrate p300.move_to(sN_mix.bottom(35)) # excess tip fluid condense protocol.delay(seconds=3) #tip droplets slide p300.touch_tip() p300.dispense(mix_sn_XFR_to_std_int, tube) p300.drop_tip() p300.flow_rate.aspirate = 92.86 #reset to default p300.flow_rate.dispense = 92.86 #reset to default p300.well_bottom_clearance.aspirate = 10 #mm default # transfer std DNA into intermediate std_mixes tubes and then to plate for std, intTube, well in zip(std_tubes, std_mixes, std_wells): p20.pick_up_tip() p300.pick_up_tip() p20.flow_rate.aspirate = 4 p20.flow_rate.dispense = 4 p20.aspirate( std_dna_XFR_to_std_int, std ) #aspirate from std_1 into std_mix (intermediate tube) e.g. 6.42 ul protocol.delay(seconds=3) #equilibrate p20.touch_tip() p20.dispense(std_dna_XFR_to_std_int, intTube) # p20.move_to(intTube.bottom(3)) p20.flow_rate.aspirate = 7.56 p20.flow_rate.dispense = 7.56 p20.mix(2, 20, intTube.bottom(3)) #ensure vol in tip in intTube and washed p20.blow_out() p300.move_to( intTube.bottom(40)) #prevent tip from crashing into tube cap p300.mix(7, 50, intTube.bottom(1)) protocol.delay(seconds=2) # p300.move_to(intTube.bottom(10)) #prevent air bubbles in mmix during blow out p300.blow_out(intTube.bottom(10)) p20.move_to(intTube.bottom(40)) p20.flow_rate.aspirate = 4 p20.flow_rate.dispense = 4 for x in range(0, 3): # need int 1, 2, and 3 p20.aspirate(20, intTube) protocol.delay(seconds=2) #equilibrate # find digits in well, G1 and G10 and puts into list findNums = [int(i) for i in well.split()[0] if i.isdigit()] # joins nums from list [1, 0] -> 10 type = string colNum = ''.join(map(str, findNums)) # this finds row row = well.split()[0][0] dest = row + str(int(colNum) + x) # row + neighbor well i.e. 1, 2 p20.dispense(20, plate[dest].bottom(1)) p20.move_to(plate[dest].bottom(4)) p20.blow_out() # p20.touch_tip() p300.drop_tip() p20.drop_tip() p20.flow_rate.aspirate = 7.56 p20.flow_rate.dispense = 7.56 p20.well_bottom_clearance.dispense = 1 p20.well_bottom_clearance.aspirate = 1 # create samp_int tube mixes. sample intermediate # first add DNA from a std tube e.g. std_4 p20.transfer( dna_XFR_samp_int * 3, # ~3 sets of 24 samples std_6.bottom(2), MIX_bw.bottom(tip_heights(mix_bw_tot - mix_bw_XFR_mix_sn, 1, 0)[0]), mix_after=(2, dna_XFR_samp_int * 3), blow_out=True, blowout_location='destination well') # Three tubes created for upper, mid, lower F,R concentrations p300.pick_up_tip() p300.flow_rate.aspirate = 92.86 #default p300.flow_rate.dispense = 92.86 #default h = tip_heights(mix_bw_tot - mix_bw_XFR_mix_sn, len(split_asp(mix_bw_XFR_samp_int, p300_max_vol)), 408.48) #split_asp(mix_bw_XFR_samp_int, p300_max_vol)[0]) p300.mix(2, 200, MIX_bw.bottom(4)) # low; need to thoroughly mix DNA in tube p300.mix(2, 200, MIX_bw.bottom(8)) # mid; need to thoroughly mix DNA in tube p300.mix(7, 200, MIX_bw.bottom(h[0] - 10)) # need to thoroughly mix DNA in tube for tube in samp_tubes: for j in range( len(split_asp(mix_bw_XFR_samp_int, p300_max_vol)) ): # split_asp is a function that returns equally divided aspirations p300.flow_rate.aspirate = 40 #default p300.flow_rate.dispense = 40 #default amt = split_asp(mix_bw_XFR_samp_int, p300_max_vol)[j] p300.aspirate(amt, MIX_bw.bottom(2)) protocol.delay(seconds=2) # h = tip_heights(amt+amt*j, 1, 0)[0] # adjust tip height depending on dispenses p300.dispense( amt, tube.bottom(3)) # want tip to be just a little above dispense p300.blow_out(tube.bottom(20)) # want to be above liquid level p300.touch_tip() p300.drop_tip() p300.well_bottom_clearance.aspirate = 1 #mm p300.well_bottom_clearance.dispense = 1 #mm # add F, R primers into samp_int tubes first with upper, mid (p300 vols), then with low (p20 vols) for i in range(2): h = tip_heights(mix_bw_XFR_samp_int, 1, 0)[0] p300.transfer(F_samp_vols[i], fwd_10uM.bottom(2), samp_tubes[i].bottom(h), mix_after=(2, 100), new_tip='always') p300.transfer(R_samp_vols[i], rev_10uM.bottom(2), samp_tubes[i].bottom(h), mix_after=(2, R_samp_vols[i]), new_tip='always') # F primer to low tube p20.transfer(F_samp_vols[-1], fwd_10uM.bottom(2), samp_tubes[-1].bottom( tip_heights(mix_bw_XFR_samp_int, 1, 0)[0]), mix_after=(2, F_samp_vols[-1]), new_tip='always') # R primer to low tube p20.transfer(R_samp_vols[-1], rev_10uM.bottom(2), samp_tubes[-1].bottom( tip_heights(mix_bw_XFR_samp_int, 1, 0)[0]), mix_after=(2, R_samp_vols[-1]), new_tip='always') # add water, dispense samp_int tubes containing F,R, water into plate # into each 1st well in plate, A1, B1..F1 then tube_mid into A5, B5..F5 for i, (tube, wvol) in enumerate( zip(samp_tubes, list([water_upp_mix, water_mid_mix, water_low_mix]))): p300.pick_up_tip() p300.flow_rate.aspirate = 40 #default p300.flow_rate.dispense = 40 #default p300.aspirate(wvol, water.bottom(10)) p300.dispense(wvol, tube.bottom(10)) p300.flow_rate.aspirate = 92.86 #default p300.flow_rate.dispense = 92.86 #default p300.mix(2, 200, tube.bottom(4)) p300.mix(2, 200, tube.bottom(10)) p300.mix(5, 200, tube.bottom(16.5)) for j, row in enumerate(plate_rows): h = tip_heights(MIX_upp_samp_int, len(plate_rows), mix_samp_XFR_to_well ) #3rd asp is low correct with small tot vol print(h) dest = row + str(4 * i + 1) p300.flow_rate.aspirate = 30 p300.flow_rate.dispense = 40 p300.aspirate(mix_samp_XFR_to_well, tube.bottom(h[j])) protocol.delay(seconds=2) # tip equilibrate p300.move_to(tube.bottom(25)) protocol.delay(seconds=2) p300.touch_tip( ) # bpwd_rxn*row reps (12) * waste = 16.8*12*(1+.12-0.05)= p300.dispense(mix_samp_XFR_to_well, plate[dest].bottom(1)) protocol.delay(seconds=1) p300.blow_out(plate[dest].bottom(10)) p300.touch_tip() p300.drop_tip() # now that PCR reactions are chillin' at 4C, make Probe int tubes # Mix 50ul 'probe' dilutions in tube by adding water and "probe" tube as shown in schedule # add water p300.pick_up_tip() p20.flow_rate.aspirate = 7.56 #default p20.flow_rate.dispense = 7.56 #default p300.flow_rate.aspirate = 92.86 #default p300.flow_rate.dispense = 92.86 #default for tube, wvol in zip(probe_mixes[0:4], W_in_mix[0:4]): # first 4 have vol > 20 p300.transfer( wvol, water.bottom(15), tube.bottom(1), blow_out=False, #this was creating air bubbles blowout_location='destination well', new_tip='never') p300.drop_tip() p20.pick_up_tip() # for tube, wvol in probe_mixes[4], W_in_mix[4]: # 5th item has vol < 20ul p20.transfer(W_in_mix[4], water.bottom(15), probe_mixes[4].bottom(1), blow_out=False, blowout_location='destination well', new_tip='never') p20.drop_tip() # add primer p20.pick_up_tip() for tube, pvol in zip(probe_mixes[0:3], P_in_mix[0:3]): # first three tubes have vol < 20ul p20.transfer(pvol, probe_10uM.bottom(2), tube.bottom(3), blow_out=False, mix_after=(2, pvol), blowout_location='destination well', new_tip='never') p20.drop_tip() # next tubes have vol > 20 p300.pick_up_tip() p300.flow_rate.aspirate = 20 #default p300.flow_rate.dispense = 40 #default for tube, pvol in zip(probe_mixes[3:], P_in_mix[3:]): # item 3 till end p300.transfer(pvol, probe_10uM.bottom(2), tube.bottom(2), blow_out=False, blowout_location='destination well', new_tip='never') p300.drop_tip() p300.flow_rate.aspirate = 92.86 #default p300.flow_rate.dispense = 92.86 #default p20.flow_rate.aspirate = 7.56 p20.flow_rate.dispense = 7.56 # Add probe to wells. mix, aliquot to adjacent wells P_mix_h = tip_heights(P_int_vol, 1, 0) for i, (tube, row) in enumerate(zip(probe_mixes, plate_rows)): p300.pick_up_tip() p20.pick_up_tip() p300.flow_rate.aspirate = 60 p300.flow_rate.dispense = 60 #don't want air bubbles p300.mix(3, 30, tube.bottom(P_mix_h[0])) # mix F primer tube, 100ul in tube p300.blow_out(tube.bottom(P_mix_h[0] + 6)) p300.touch_tip() for j in range(2): # asp and disp into wells p20.flow_rate.aspirate = 4 p20.flow_rate.dispense = 4 p20.move_to(tube.bottom(40)) if j == 0: # wells A1 and A5 dest = row + str(j + 1) nextWell = row + str(j + 5) p20.aspirate( p_int_XFR_to_well * 2, tube.bottom(2)) # ~7ul aspirate from P int tube to wells protocol.delay(seconds=2) p20.move_to( tube.bottom(2)) # relieve pressure if tip against tube p20.dispense(p_int_XFR_to_well, plate[dest].bottom(2)) protocol.delay(seconds=1) p20.touch_tip() p20.dispense(p_int_XFR_to_well, plate[nextWell].bottom(2)) protocol.delay(seconds=2) p20.blow_out(plate[nextWell].bottom(6)) p20.touch_tip() p20.drop_tip() p20.pick_up_tip() else: # well A9 dest = row + str(j + 8) p20.aspirate( p_int_XFR_to_well, tube.bottom(1)) # ~7ul aspirate from P int tube to wells p20.move_to( tube.bottom(2)) # relieve pressure if tip against tube protocol.delay(seconds=2) p20.touch_tip() p20.dispense(p_int_XFR_to_well, plate[dest]) protocol.delay(seconds=2) p20.blow_out(plate[dest].bottom(6)) p20.touch_tip() for k in range( 3): # need int 0, 1, 2. Looping through bolus in row (3) swell = row + str(4 * k + 1) #source well: A1, A5, A9 p300.move_to(plate[swell].bottom(30)) p300.flow_rate.aspirate = 92.86 p300.flow_rate.dispense = 92.86 p300.mix(3, 70, plate[swell].bottom(2), rate=0.75) p300.flow_rate.aspirate = 40 p300.flow_rate.dispense = 20 p300.mix(1, 70, plate[swell].bottom(2), rate=0.6) #slow mix to avoid/remove bubbles p300.blow_out(plate[swell].bottom(10)) for m in range(1, 4): # want int 1, 2, 3 p20.flow_rate.aspirate = 7.56 p20.flow_rate.dispense = 7.56 dwell = row + str( 4 * k + 1 + m) # loop through dispensing wells p20.move_to(plate[swell].bottom(40)) p20.aspirate(20, plate[swell].bottom(1)) protocol.delay(seconds=1) p20.dispense(20, plate[dwell].bottom(2)) protocol.delay(seconds=1) p20.move_to(plate[dwell].bottom(6)) p20.blow_out() p20.touch_tip() p20.drop_tip() p300.drop_tip()
def run(protocol: protocol_api.ProtocolContext): """ Aliquoting Illumina primers from 1 tube rack filled with 1.5 mL tubes, to 3 PCR strips in a BioRad 96-well plate, calibrated with Westburg PCR strips. """ # ============================================================================= # =====================LOADING LABWARE AND PIPETTES============================ # ============================================================================= ## For available labware see "labware/list_of_available_labware". ## tips_200_1 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 10, #deck position '200tips') #custom name tips_200_2 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 7, #deck position '200tips') #custom name tips_200_3 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 4, #deck position '200tips') #custom name tips_200_4 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 1, #deck position '200tips') #custom name primer_tubes = protocol.load_labware( 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap', #labware def 3, #deck position 'primer_tubes') #custom name ##### !!! OPTION 1: ROBOT pcr_strips = protocol.load_labware( 'pcrstrips_96_wellplate_200ul', #labware definition 6, #deck position 'pcr_strips') #custom name ##### !!! OPTION 2: SIMULATOR # with open("labware/pcrstrips_96_wellplate_200ul/" # "pcrstrips_96_wellplate_200ul.json") as labware_file: # labware_def_pcrstrips = json.load(labware_file) # pcr_strips = protocol.load_labware_from_definition( # labware_def_pcrstrips, #variable derived from opening json # 6, # 'pcr_strips') #Load the labware using load_labware_from_definition() instead of ## #load_labware(). Then use the variable you just set with the opened## #json file to define which labware to use. ## p300 = protocol.load_instrument( 'p300_single_gen2', #instrument definition 'right', #mount position tip_racks=[tips_200_1, tips_200_2, tips_200_3, tips_200_4]) #as tiprack # ============================================================================= # ===========================VARIABLES TO SET#!!!============================== # ============================================================================= p300.starting_tip = tips_200_1.well('G5') primer_volume = 30 # ============================================================================= # ============================ALIQUOTING PRIMERS=============================== # ============================================================================= protocol.set_rail_lights(True) protocol.pause('Put F primers F1 to F47 in slot 3, and ' 'three empty PCR strips in columns 2, 7, and 11 with the ' 'caps to the right in slot 6.') # ============================================================================= # F1 to F47 + corresponding R primers========================================== for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): ## simultanious loop through primer_tubes and PCR_strips ## ## From wells to columns doesn't work, therefore all PCRstrip ## ## wells are given. ## p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() ## air_gap to suck up any liquid that remains in the tip ## p300.drop_tip() ## Used aspirate/dipense instead of transfer, to allow for more ## ## customization. ## protocol.pause('Remove F primers and put corresponding' ' R primers on slot 3.') for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause('Remove R primers and PCR strips, ' 'put new strips in columns 2, 7 and 11 with caps to ' 'the right and put F49 to F95 on slot 3. Empty waste bin!!') # ============================================================================= # F49 to F95 + corresponding R primers========================================= for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause('Remove F primers and put corresponding' ' R primers on slot 3.') for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause( 'Remove R primers and PCR strips, ' 'put new strips in columns 2, 7 and 11 with caps to ' 'the right and put F97 to F143 on slot 3. Empty waste bin!!') # ============================================================================= # F97 to F143 + corresponding R primers======================================== for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause('Remove F primers and put corresponding' ' R primers on slot 3.') for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause( 'Remove R primers and PCR strips, ' 'put new strips in columns 2, 7 and 11 with caps to ' 'the right and put F145 to F191 on slot 3. Empty waste bin!!') # ============================================================================= # F145 to F191 + corresponding R primers======================================= for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause('Remove F primers and put corresponding' ' R primers on slot 3.') for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause( 'Remove R primers and PCR strips, ' 'put new strips in columns 2, 7 and 11 with caps to ' 'the right and put F193 to F239 on slot 3. Empty waste bin!!') # ============================================================================= # F193 to F239 + corresponding R primers======================================== for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause('Remove F primers and put corresponding' ' R primers on slot 3.') for primer_tube, pcr_strip_tube in zip(primer_tubes.wells(), [ pcr_strips.wells_by_name()[well_name] for well_name in [ 'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7', 'A11', 'B11', 'C11', 'D11', 'E11', 'F11', 'G11', 'H11' ] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() protocol.pause( 'Remove R primers and PCR strips, ' 'put new strips in column 7 with caps to ' 'the right and put F241 to F255 in columns 1+2' ' + corresponding R primers in columns 5+6 on slot 3. Empty waste bin!!' ) # ============================================================================= # F241 to F255 + corresponding R primers============================================================================= #Forward: for primer_tube, pcr_strip_tube in zip([ primer_tubes.wells_by_name()[well_name] for well_name in ['A1', 'B1', 'C1', 'D1', 'A2', 'B2', 'C2', 'D2'] ], [ pcr_strips.wells_by_name()[well_name] for well_name in ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7'] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() #Reverse: for primer_tube, pcr_strip_tube in zip([ primer_tubes.wells_by_name()[well_name] for well_name in ['A5', 'B5', 'C5', 'D5', 'A6', 'B6', 'C6', 'D6'] ], [ pcr_strips.wells_by_name()[well_name] for well_name in ['A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'G7', 'H7'] ]): p300.pick_up_tip() p300.aspirate(primer_volume, primer_tube) p300.air_gap(10) p300.dispense(primer_volume + 50, pcr_strip_tube) p300.air_gap() p300.drop_tip() # ============================================================================= protocol.set_rail_lights(False)
def run(ctx: protocol_api.ProtocolContext): # ------------------------ # Load LabWare # ------------------------ # Tip racks tips = [ ctx.load_labware('opentrons_96_filtertiprack_1000ul', slot, '1000µl filter tiprack') for slot in ['10', '11'] ] # Pipette p20 = ctx.load_instrument('p20_single_gen2', 'right', tip_racks=tips) # Source Samples rack_num = math.ceil( num_samples / NUM_OF_SOURCES_PER_RACK ) if num_samples < MAX_NUM_OF_SOURCES else MIN_NUM_OF_SOURCES source_racks = [ ctx.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', slot, 'source tuberack with screwcap' + str(i + 1)) for i, slot in enumerate(['1', '2', '4', '5'][:rack_num]) ] sample_sources_full = common.generate_source_table(source_racks) sample_sources = sample_sources_full[:num_samples] # Source TRIS tris = ctx.load_labware('opentrons_6_tuberack_falcon_50ml_conical', '6', 'Buffer tuberack in Falcon tube') tris_phalcon = tris.wells()[0] # Destination (in this case 96 well plate) dest_plate = ctx.load_labware('abi_fast_qpcr_96_alum_opentrons_100ul', '3', 'PCR final plate') destinations = dest_plate.wells()[:num_destinations] # ------------------ # Protocol # ------------------ if not p20.hw_pipette['has_tip']: common.pick_up(p20) # Dispense 4mM of sample in PCR plate for s, d in zip(sample_sources, destinations): if not p20.hw_pipette['has_tip']: common.pick_up(p20) sample_volume_to_be_transferred = vi.pop() # Calculate pickup_height based on remaining volume and shape of container common.move_vol_multichannel(ctx, p20, reagent=sample, source=s, dest=d, vol=sample_volume_to_be_transferred, air_gap_vol=air_gap_vol_sample, pickup_height=pickup_height, disp_height=dispense_height, x_offset=x_offset, blow_out=True, touch_tip=True) # Drop pipette tip p20.drop_tip() # Dispense rest volume of TRIS in each sample of PCR for d in destinations: if not p20.hw_pipette['has_tip']: common.pick_up(p20) tris_volume_to_be_transferred = vt.pop() common.move_vol_multichannel(ctx, p20, reagent=sample, source=tris_phalcon, dest=d, vol=tris_volume_to_be_transferred, air_gap_vol=air_gap_vol_sample, pickup_height=pickup_height, disp_height=dispense_height, x_offset=x_offset, blow_out=True, touch_tip=True) # Drop pipette tip p20.drop_tip()