def test_papi_execute_json_v3(monkeypatch, loop, get_json_protocol_fixture): protocol_data = get_json_protocol_fixture( '3', 'testAllAtomicSingleV3', False) protocol = parse(protocol_data, None) ctx = ProtocolContext(loop=loop) ctx.home() # Check that we end up executing the protocol ok execute.run_protocol(protocol, True, ctx)
def test_papi_execute_json_v4(monkeypatch, loop, get_json_protocol_fixture): protocol_data = get_json_protocol_fixture('4', 'testModulesProtocol', False) protocol = parse(protocol_data, None) ctx = ProtocolContext(loop=loop) ctx.home() # Check that we end up executing the protocol ok execute.run_protocol(protocol, ctx)
def run(protocol: protocol_api.ProtocolContext): """A function to try out the home commands.""" #load labware plate = protocol.load_labware('biorad_96_wellplate_200ul_pcr', 1, '96plate') #load pipette p300 = protocol.load_instrument('p300_single_gen2', 'right') p300.move_to(plate['A1'].top()) #move to top of well A1 of 96plate protocol.home() #homes gantry, z axes, and plungers p300.move_to(plate['B1'].top()) #move to top of well B1 of 96plate
def run(protocol: protocol_api.ProtocolContext): # define deck positions and labware # tips tiprack_200f = protocol.load_labware('opentrons_96_filtertiprack_200ul', 6) # tube racks, named for deck position tuberack_4 = protocol.load_labware(tuberack_labware, 4) tuberack_1 = protocol.load_labware(tuberack_labware, 1) tuberack_5 = protocol.load_labware(tuberack_labware, 5) tuberack_2 = protocol.load_labware(tuberack_labware, 2) # plates samples = protocol.load_labware('vwr_96_wellplate_1000ul', 3, 'samples') # initialize pipettes pipette_right = protocol.load_instrument('p300_single_gen2', 'right', tip_racks=[tiprack_200f]) # # home instrument protocol.home() # distribute quantity for i, rack in enumerate([tuberack_4, tuberack_5, tuberack_1, tuberack_2]): # Indices to locate source racks # X and Y are indices of tube rack positions # (0 left/top, 1 right/bottom) x = int(i % 2) y = int(floor(i / 2.0)) # Indices to locate destination wells # a and b are from:to columns of the destination plate a = (x * 6) b = ((x + 1) * 6) # i and j are the from:to rows of the destination plate i = (y * 4) j = ((y + 1) * 4) # In this arrangement, the source tube racks are combined into four # quadrants of the destination plate, for a 1:1 spatial relationship # between the tubes on the deck and the wells in the destination plate. d_rows = [c[i:j] for c in samples.columns()] d_wells = d_rows[a:b] pipette_right.transfer(quantity, [w.bottom(z=z_height) for w in rack.wells()], d_wells, new_tip='always', rate=rate, trash=True)
def run(protocol: protocol_api.ProtocolContext): # define deck positions and labware # tips tiprack_200f = protocol.load_labware('opentrons_96_filtertiprack_200ul', 6) # tubes tuberack_4 = protocol.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', 4) tuberack_1 = protocol.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', 1) tuberack_5 = protocol.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', 5) tuberack_2 = protocol.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', 2) # plates samples = protocol.load_labware('biorad_96_wellplate_200ul_pcr', 3, 'samples') # initialize pipettes pipette_right = protocol.load_instrument('p300_single_gen2', 'right', tip_racks=[tiprack_200f]) # # home instrument protocol.home() # distribute quantity for i, rack in enumerate([tuberack_4, tuberack_5, tuberack_1, tuberack_2]): x = int(i % 2) y = int(floor(i/2.0)) # print('x: %s y: %s' % (x,y)) a = (x*6) b = ((x+1)*6) i = (y*4) j = ((y+1)*4) # print('a: %s b: %s' % (a, b)) # print('i: %s j: %s' % (i, j)) d_rows = [c[i:j] for c in samples.columns()] # print(d_rows) d_wells = d_rows[a:b] # print(d_wells) pipette_right.transfer(quantity, [w.bottom(z=z_height) for w in rack.wells()], d_wells, new_tip='always', trash=True)
def _run(self): def on_command(message): if message['$'] == 'before': self.log_append() if message['name'] == command_types.PAUSE: self.set_state('paused') if message['name'] == command_types.RESUME: self.set_state('running') self._reset() _unsubscribe = self._broker.subscribe(command_types.COMMAND, on_command) self.startTime = now() self.set_state('running') try: self.resume() self._pre_run_hooks() if ff.use_protocol_api_v2(): bundled_data = None bundled_labware = None if isinstance(self._protocol, PythonProtocol): bundled_data = self._protocol.bundled_data bundled_labware = self._protocol.bundled_labware self._hardware.cache_instruments() ctx = ProtocolContext(loop=self._loop, broker=self._broker, bundled_labware=bundled_labware, bundled_data=bundled_data) ctx.connect(self._hardware) ctx.home() run_protocol(self._protocol, context=ctx) else: self._hardware.broker = self._broker if isinstance(self._protocol, JsonProtocol): execute_protocol(self._protocol) else: exec(self._protocol.contents, {}) self.set_state('finished') self._hardware.home() except Exception as e: log.exception("Exception during run:") self.error_append(e) self.set_state('error') raise e finally: _unsubscribe()
def run(protocol: protocol_api.ProtocolContext): # ### HackFlex Illumina-compatible library prep protocol # ### Deck # 1. samples; libraries # 2. reagent reservoir # 3. reagent strip tubes # 4. 300 tips (wash); 200f tips (elute) # 5. 10f tips (samples) # 6. i7 primers # 7. waste # 8. 300 tips (reagents) # 9. 10f tips (primers) # 10. mag module # 11. 20 tips (reagents) # 12. trash # define custom labware for strip tubes block # reagent strip tubes: # 1: BLT 150 µL # 2: TSB 150 µL # 3: i5 primers 150 µL # 4: PCR MM 200 µL # 5: PCR MM 200 µL # buffer reservoirs: # 1: TB1 (2.5 mL) # 2: TWB (10 mL) # 3: TWB (10 mL) # 4: H2O (8 mL) # 5: beads (6 mL) # 6: 80% EtOH # 7: 80% EtOH # 8: 80% EtOH # ### Setup protocol.home() # define deck positions and labware # define hardware modules magblock = protocol.load_module('Magnetic Module', 10) magblock.disengage() # tips tiprack_samples = protocol.load_labware('opentrons_96_filtertiprack_10ul', 5) tiprack_buffers = protocol.load_labware('opentrons_96_tiprack_300ul', 8) tiprack_wash = protocol.load_labware('opentrons_96_tiprack_300ul', 4) tiprack_primers = protocol.load_labware('opentrons_96_filtertiprack_10ul', 9) tiprack_reagents = protocol.load_labware('opentrons_96_tiprack_20ul', 11) # reagents # should be new custom labware with strip tubes reagents = protocol.load_labware('opentrons_96_aluminumblock_generic_pcr_strip_200ul', 3, 'reagents') buffers = protocol.load_labware('nest_12_reservoir_15ml', 2, 'wash buffers') waste = protocol.load_labware('nest_1_reservoir_195ml', 7, 'liquid waste') # plates samples = protocol.load_labware('biorad_96_wellplate_200ul_pcr', 1, 'samples') i7_primers = protocol.load_labware('biorad_96_wellplate_200ul_pcr', 6, 'i7 primers') # load plate on magdeck # mag_plate = magblock.load_labware('vwr_96_wellplate_1000ul') mag_plate = magblock.load_labware('biorad_96_wellplate_200ul_pcr') # initialize pipettes pipette_left = protocol.load_instrument('p300_multi', 'left', tip_racks=[tiprack_buffers]) pipette_right = protocol.load_instrument('p10_multi', 'right', tip_racks=[tiprack_reagents]) # TWB wash wells twb_wells = [buffers[x] for x in twb_cols] # PCR MM wells pcr_wells = [reagents[x] for x in pcr_cols] # EtOH wells eth_wells = [buffers[x] for x in eth_cols] # DNA plate # Step 1: Tagmentation # Diluted BLT: 1 mL; 120 (150 µL) per tip # TB1: 2.4 mL; 300 (350 µL) per tip # add TB1. # buffer tips 1 pipette_left.distribute(25, buffers['A1'], [mag_plate[x] for x in cols], touch_tip=False, disposal_volume=10, new_tip='once', trash=True) # add BLT # reagent tips 2 # mix BLT first pipette_right.pick_up_tip() pipette_right.mix(10, 10, reagents['A1']) pipette_right.transfer(10, reagents['A1'], [mag_plate[x] for x in cols], mix_before=(2,10), new_tip='never') pipette_right.drop_tip() # add sample for col in cols: pipette_right.pick_up_tip(tiprack_samples[col]) pipette_right.transfer(10, samples[col], mag_plate[col], mix_after=(5, 10), new_tip='never', trash=False) pipette_right.return_tip() # Prompt user to remove plate and run on thermocycler protocol.pause('Remove plate from magblock, seal, vortex, and run ' 'program TAG on thermocycler. Then spin down, unseal, ' 'and return to magblock.') # Step 2: Stop reaction # TSB: 1 mL; 120 (150 µL) per tip # add TSB to each sample. # Prompt user to remove plate and run on thermocycler ### Is this step going to cross-contaminate? Seems wasteful to take. ### new tip for each sample. z = -1 meant to help. # reagent tips 2 pipette_right.transfer(10, reagents['A2'], [mag_plate[x].top(z=-1) for x in cols], touch_tip=True, new_tip='once') protocol.pause('Remove plate from magblock, seal, vortex, and run ' 'program PTC on thermocycler. Then spin down, unseal, ' 'and return to magblock.') # Step 3: Cleanup # TWB: 20 mL; 1200 (1500 µL) per tip # Magnet wash 2X # bind for specified length of time protocol.comment('Binding beads to magnet.') magblock.engage(height_from_base=mag_engage_height) protocol.delay(seconds=pause_mag) # ### Do first wash: 100 µL TWB # buffer tips 2 protocol.comment('Doing wash #1.') twb_remaining, twb_wells = bead_wash(# global arguments protocol, magblock, pipette_left, mag_plate, cols, # super arguments waste['A1'], tiprack_wash, # wash buffer arguments, twb_wells, 10000/8, # mix arguments tiprack_wash, # optional arguments wash_vol=100, super_vol=60, drop_super_tip=False, mix_n=wash_mix, mix_vol=90, remaining=None) # ### Do second wash: 100 µL TWB # buffer tips 3 protocol.comment('Doing wash #2.') twb_remaining, twb_wells = bead_wash(# global arguments protocol, magblock, pipette_left, mag_plate, cols, # super arguments waste['A1'], tiprack_wash, # wash buffer arguments, twb_wells, 10000/8, # mix arguments tiprack_wash, # optional arguments wash_vol=100, super_vol=100, drop_super_tip=False, mix_n=wash_mix, mix_vol=90, remaining=twb_remaining) # remove supernatant remove_supernatant(pipette_left, mag_plate, cols, tiprack_wash, waste['A1'], super_vol=120, rate=bead_flow, bottom_offset=.5, drop_tip=False) magblock.disengage() # Step 3: amplification # MM: 3 mL; 350 (400 µL) per tip # buffer tips 4 pcr_wells, pcr_remaining = add_buffer(pipette_left, mag_plate, cols, 30, pcr_wells, 200, tip=None, tip_vol=300, remaining=None, drop_tip=True) # plate: primers i5 # reagent tips 3 pipette_right.transfer(10, reagents['A5'], [mag_plate[x].top(z=-1) for x in cols], touch_tip=True, new_tip='once') # plate: primers i7 for col in cols: pipette_right.pick_up_tip(tiprack_primers[col]) pipette_right.transfer(10, i7_primers[col], mag_plate[col], mix_after=(5, 10), touch_tip=True, new_tip='never', trash=False) pipette_right.drop_tip() # Prompt user to remove plate and run on thermocycler protocol.pause('Remove plate from magblock, seal, and run amplification' ' program on thermocycler.') # Step 4: Size selection # H2O: 72 µL per sample; 7.2 mL; 600 per tip # EtOH: 400 µL per sample; 5000 per tip # Beads: 6 mL; 720 µL per tip # protocol.pause('Remove sample plate from position {0}, seal, and store. ' 'Place a new, clean, 96-well BioRad PCR plate in position' ' {0}.'.format(samples.parent)) protocol.pause('Centrifuge sealed plate at 280 xg for one minute.' ' Then unseal and return to magblock.') protocol.comment('Binding beads to magnet.') magblock.engage(height_from_base=mag_engage_height) # Add buffers for large-cut size selection to new plate protocol.comment('Preparing large-cut bead conditions in new plate.') # add 40 µL H2O # buffer tips 5 pipette_left.distribute(40, buffers['A4'], [samples[x] for x in cols], touch_tip=True, disposal_volume=10, new_tip='once') # Add 45 µL SPRI beads # buffer tips 6 pipette_left.pick_up_tip() pipette_left.mix(10, 200, buffers['A5']) pipette_left.distribute(45, buffers['A5'], [samples[x] for x in cols], mix_before=(2,40), touch_tip=True, disposal_volume=10, new_tip='never') pipette_left.drop_tip() # Transfer 45 µL PCR supernatant to new plate for col in cols: pipette_left.pick_up_tip(tiprack_wash.wells_by_name()[col]) pipette_left.transfer(45, [mag_plate[x] for x in cols], [samples[x] for x in cols], mix_after=(10, 100), touch_tip=True, new_tip='never', trash=False) pipette_left.return_tip() protocol.pause('Remove and discard plate from mag block. ' 'Move plate in position {0} to mag block, and replace ' 'with a new, clean 96-well BioRad PCR plate.'.format( samples.parent)) protocol.comment('Binding beads to magnet.') magblock.engage(height_from_base=mag_engage_height) protocol.delay(seconds=pause_mag) # Add buffers for small-cut size selection to new plate # Add 15 µL SPRI beads # buffer tips 7 pipette_left.pick_up_tip() pipette_left.mix(10, 100, buffers['A5']) pipette_left.distribute(15, buffers['A5'], [samples[x] for x in cols], mix_before=(2,15), touch_tip=True, new_tip='never') pipette_left.drop_tip() # Transfer 125 µL large-cut supernatant to new plate for col in cols: pipette_left.pick_up_tip(tiprack_wash.wells_by_name()[col]) pipette_left.transfer(125, [mag_plate[x] for x in cols], [samples[x] for x in cols], mix_after=(10, 100), touch_tip=True, new_tip='never', trash=False) pipette_left.return_tip() protocol.pause('Remove and discard plate from mag block. ' 'Move plate in position {0} to mag block, and replace ' 'with a new, clean 96-well BioRad PCR plate.'.format( samples.parent)) protocol.comment('Binding beads to magnet.') magblock.engage(height_from_base=mag_engage_height) protocol.delay(seconds=pause_mag) # ### Do first wash: 150 µL EtOH # buffer tips 8 protocol.comment('Doing wash #1.') eth_remaining, eth_wells = bead_wash(# global arguments protocol, magblock, pipette_left, mag_plate, cols, # super arguments waste['A1'], tiprack_wash, # wash buffer arguments, eth_wells, 14000/8, # mix arguments tiprack_wash, # optional arguments wash_vol=150, super_vol=125, drop_super_tip=False, mix_n=wash_mix, mix_vol=140, remaining=None) # ### Do first wash: 150 µL EtOH # buffer tips 9 protocol.comment('Doing wash #1.') eth_remaining, eth_wells = bead_wash(# global arguments protocol, magblock, pipette_left, mag_plate, cols, # super arguments waste['A1'], tiprack_wash, # wash buffer arguments, eth_wells, 14000/8, # mix arguments tiprack_wash, # optional arguments wash_vol=150, super_vol=125, drop_super_tip=False, mix_n=wash_mix, mix_vol=140, remaining=eth_remaining) # ### Dry protocol.comment('Removing wash and drying beads.') # This should: # - pick up tip in position 8 # - pick up supernatant from magplate # - dispense in waste, position 11 # - repeat # - trash tip # - leave magnet engaged # remove supernatant remove_supernatant(pipette_left, mag_plate, cols, tiprack_wash, waste['A1'], super_vol=170, rate=bead_flow, bottom_offset=.5, drop_tip=True) # dry protocol.delay(seconds=pause_dry) protocol.pause('Replace empty tiprack in position {0} with new rack of ' '200 µL filter tips.'.format(tiprack_wash.parent)) # ### Elute protocol.comment('Eluting DNA from beads.') # This should: # - disengage magnet # - pick up tip from position 6 # - pick up reagents from column 2 of position 9 # - dispense into magplate # - mix 10 times # - blow out, touch tip # - return tip to position 6 # - wait (5 seconds) # - engage magnet # - wait (5 seconds) # - pick up tip from position 6 # - aspirate from magplate # - dispense to position 3 # - trash tip # transfer elution buffer to mag plate magblock.disengage() # add elution buffer and mix for col in cols: pipette_left.pick_up_tip(tiprack_wash.wells_by_name()[col]) pipette_left.aspirate(32, buffers['A4'], rate=1) pipette_left.dispense(32, mag_plate[col].bottom(z=1)) pipette_left.mix(10, 25, mag_plate[col].bottom(z=1)) pipette_left.blow_out(mag_plate[col].top()) pipette_left.touch_tip() # we'll use these same tips for final transfer pipette_left.return_tip() protocol.delay(seconds=pause_elute) for col in cols: pipette_left.pick_up_tip(tiprack_wash.wells_by_name()[col]) pipette_left.mix(10, 25, mag_plate[col].bottom(z=1)) pipette_left.blow_out(mag_plate[col].top()) pipette_left.touch_tip() # we'll use these same tips for final transfer pipette_left.return_tip() # bind to magnet protocol.comment('Binding beads to magnet.') magblock.engage(height_from_base=mag_engage_height) protocol.delay(seconds=pause_mag) protocol.comment('Transferring eluted DNA to final plate.') for col in cols: pipette_left.pick_up_tip(tiprack_wash.wells_by_name()[col]) pipette_left.aspirate(32, mag_plate[col].bottom(z=2), rate=bead_flow) pipette_left.dispense(32, samples[col]) pipette_left.blow_out(samples[col].top()) pipette_left.touch_tip() # we're done with these tips now pipette_left.drop_tip() magblock.disengage()
def run(ctx: protocol_api.ProtocolContext): w1_tip_pos_list = [] w2_tip_pos_list = [] elution_tip_pos_list = [] ctx.comment('Columnas a utilizar: '+str(num_cols)) STEP = 0 STEPS = { #Dictionary with STEP activation, description, and times 1:{'Execute': True, 'description': 'Transferir bolas magnéticas'}, 2:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 600}, 3:{'Execute': True, 'description': 'Desechar sobrenadante'}, 4:{'Execute': True, 'description': 'Imán OFF'}, 5:{'Execute': True, 'description': 'Transferir primer lavado'}, 6:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 300}, 7:{'Execute': True, 'description': 'Desechar sobrenadante'}, 8:{'Execute': True, 'description': 'Imán OFF'}, 9:{'Execute': True, 'description': 'Transferir segundo lavado'}, 10:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 300}, 11:{'Execute': True, 'description': 'Desechar sobrenadante'}, 12:{'Execute': True, 'description': 'Secado', 'wait_time': 180}, 13:{'Execute': True, 'description': 'Imán OFF'}, 14:{'Execute': True, 'description': 'Transferir elución'}, 15:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 180}, 16:{'Execute': True, 'description': 'Transferir elución a la placa'}, } #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_Extraccion_total_time_log.txt' #Define Reagents as objects with their properties class Reagent: def calc_vol_well(self): if(self.name == 'Sample'): self.num_wells = num_cols return VOLUME_SAMPLE else: trips = math.ceil(self.reagent_volume / self.max_volume_allowed) vol_trip = self.reagent_volume / trips * 8 max_trips_well = math.floor(11000 / vol_trip) total_trips = num_cols * trips self.num_wells = math.ceil(total_trips / max_trips_well) return math.ceil(total_trips / self.num_wells) * vol_trip + self.dead_vol 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, 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.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 = self.calc_vol_well() if reagent_volume * NUM_SAMPLES > 0 else 0 #Reagents and their characteristics Beads = Reagent(name = 'Beads', flow_rate_aspirate = 25, # flow_rate_dispense = 100, # flow_rate_aspirate_mix = 25, # flow_rate_dispense_mix = 100, # 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 volume needed per sample h_cono = 1.95, v_fondo = 695, #1.95 * multi_well_rack_area / 2, #Prismatic tip_recycling = 'A1') Wash_1 = Reagent(name = 'WASH_1', flow_rate_aspirate = 25, flow_rate_dispense = 100, flow_rate_aspirate_mix = 25, flow_rate_dispense_mix = 100, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, rinse = True, max_volume_allowed = 180, reagent_volume = WASH_1_VOLUME_PER_SAMPLE, h_cono = 1.95, v_fondo = 695, #1.95 * multi_well_rack_area / 2, #Prismatic tip_recycling = 'A1') Wash_2 = Reagent(name = 'WASH_2', flow_rate_aspirate = 25, flow_rate_dispense = 100, flow_rate_aspirate_mix = 25, flow_rate_dispense_mix = 100, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, rinse = True, max_volume_allowed = 180, reagent_volume = WASH_2_VOLUME_PER_SAMPLE, h_cono = 1.95, v_fondo = 695, #1.95 * multi_well_rack_area / 2, #Prismatic tip_recycling = 'A1') Elution = Reagent(name = 'Elution', flow_rate_aspirate = 25, flow_rate_dispense = 100, flow_rate_aspirate_mix = 25, flow_rate_dispense_mix = 100, 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, h_cono = 1.95, v_fondo = 695) #1.95*multi_well_rack_area/2) #Prismatic Sample = Reagent(name = 'Sample', flow_rate_aspirate = 5, # Original 0.5 flow_rate_dispense = 100, # Original 1 flow_rate_aspirate_mix = 1, flow_rate_dispense_mix = 1, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, rinse = False, max_volume_allowed = 180, reagent_volume = VOLUME_SAMPLE, h_cono = 4, v_fondo = 4 * math.pi * 4**3 / 3) #Sphere Beads.vol_well = Beads.vol_well_original Wash_1.vol_well = Wash_1.vol_well_original Wash_2.vol_well = Wash_2.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('VALORES DE VARIABLES') ctx.comment(' ') ctx.comment('Número de muestras: ' + str(NUM_SAMPLES)) ctx.comment('Volumen de muestra en el deepwell: ' + str(VOLUME_SAMPLE) + ' ul') ctx.comment('Volumen de solución con bolas magnéticas por muestra: ' + str(BEADS_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen del primer lavado por muestra: ' + str(WASH_1_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen del segundo lavado por muestra: ' + str(WASH_2_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen de elución por muestra: ' + str(ELUTION_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen de elución a retirar del deepwell: ' + str(ELUTION_FINAL_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Número de mezclas en la primera recogida de un canal con bolas magnéticas: ' + str(BEADS_WELL_FIRST_TIME_NUM_MIXES)) ctx.comment('Número de mezclas en el resto de recogidas de un canal con bolas magnéticas: ' + str(BEADS_WELL_NUM_MIXES)) ctx.comment('Número de mezclas con la solución de bolas magnéticas: ' + str(BEADS_NUM_MIXES)) ctx.comment('Número de mezclas con el primer lavado: ' + str(WASH_1_NUM_MIXES)) ctx.comment('Número de mezclas con el segundo lavado: ' + str(WASH_2_NUM_MIXES)) ctx.comment('Número de mezclas con la elución: ' + str(ELUTION_NUM_MIXES)) ctx.comment('Reciclado de puntas en los lavados activado: ' + str(TIP_RECYCLING_IN_WASH)) ctx.comment('Reciclado de puntas en la elución activado: ' + str(TIP_RECYCLING_IN_ELUTION)) ctx.comment('Foto-sensible: ' + str(PHOTOSENSITIVE)) ctx.comment(' ') ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VOLÚMENES PARA ' + str(NUM_SAMPLES) + ' MUESTRAS') ctx.comment(' ') ctx.comment('Beads: ' + str(Beads.num_wells) + ' canales desde el canal 1 en el reservorio de 12 canales con un volumen de ' + str_rounded(Beads.vol_well_original) + ' uL cada uno') ctx.comment('Wash 1: ' + str(Wash_1.num_wells) + ' canales desde el canal 5 en el reservorio de 12 canales con un volumen de ' + str_rounded(Wash_1.vol_well_original) + ' uL cada uno') ctx.comment('Wash 2: ' + str(Wash_2.num_wells) + ' canales desde el canal 7 en el reservorio de 12 canales con un volumen de ' + str_rounded(Wash_2.vol_well_original) + ' uL cada uno') ctx.comment('Elution: ' + str(Elution.num_wells) + ' canales desde el canal 11 en el reservorio de 12 canales con un volumen de ' + str_rounded(Elution.vol_well_original) + ' uL cada uno') 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='Esperando durante ' + str(wait_time) + ' segundos.') def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.4): nonlocal ctx ctx.comment('Volumen útil restante ' + str(reagent.vol_well - reagent.dead_vol) + ' < volumen necesario ' + str(aspirate_volume - Sample.disposal_volume * 8) + '?') if (reagent.vol_well - reagent.dead_vol + 1) < (aspirate_volume - Sample.disposal_volume * 8): ctx.comment('Se debe utilizar el siguiente canal') ctx.comment('Canal anterior: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment(str('Nuevo canal: ' + str(reagent.col))) reagent.vol_well = reagent.vol_well_original ctx.comment('Nuevo volumen:' + 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 - Sample.disposal_volume * 8) ctx.comment('Volumen restante:' + 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 - (Sample.disposal_volume * 8)) ctx.comment('La altura calculada es ' + str(height)) if height < min_height: height = min_height ctx.comment('La altura usada es ' + 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, touch_tip_v_offset = 0, drop_height = -5, aspirate_with_x_scroll = False, dispense_bottom_air_gap_before = False): # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location = source, vol = vol, rounds = 20, blow_out = False, mix_height = 3, offset = 0) # SOURCE 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 if aspirate_with_x_scroll: aspirate_with_x_scrolling(pip = pipet, volume = vol, src = source, pickup_height = pickup_height, rate = reagent.flow_rate_aspirate, start_x_offset_src = 0, stop_x_offset_src = x_offset_source) else: 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 # if wait_time != 0: # ctx.delay(seconds=wait_time, msg='Esperando durante ' + str(wait_time) + ' segundos.') if avoid_droplet == True: # Touch the liquid surface to avoid droplets ctx.comment("Moviendo a: " + str(pickup_height)) 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 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 = touch_tip_v_offset, radius=0.7) if wait_time != 0: ctx.delay(seconds=wait_time, msg='Esperando durante ' + str(wait_time) + ' segundos.') #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 def aspirate_with_x_scrolling(pip, volume, src, pickup_height = 0, rate = 1, start_x_offset_src = 0, stop_x_offset_src = 0): max_asp = volume/pip.min_volume inc_step = (start_x_offset_src - stop_x_offset_src) / max_asp for x in reversed(np.arange(stop_x_offset_src, start_x_offset_src, inc_step)): s = src.bottom(pickup_height).move(Point(x = x)) pip.aspirate(volume = pip.min_volume, location = s, rate = rate) ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up(pip, position = None): nonlocal tip_track #if not ctx.is_simulating(): if recycle_tip: pip.pick_up_tip(tips300[0].wells()[0]) else: 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('Reemplaza las cajas de puntas de ' + str(pip.max_volume) + 'µl antes \ de continuar.') pip.reset_tipracks() tip_track['counts'][pip] = 0 tip_track['num_refills'][pip] += 1 if position is None: pip.pick_up_tip() else: pip.pick_up_tip(position) 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) used_tips = tip_track['num_refills'][m300] * 96 * len(m300.tip_racks) + tip_track['counts'][m300] ctx.comment('Puntas de 200 ul utilizadas: ' + str(used_tips) + ' (' + str(round(used_tips / 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 find_side(col): if col%2 == 0: side = -1 # left else: side = 1 # right return side #################################### # load labware and modules ######## 12 well rack reagent_res = ctx.load_labware('nest_12_reservoir_15ml', '5','reagent deepwell plate') ################################## ####### Elution plate - final plate, goes to C elution_plate = ctx.load_labware('rochemagnapure_96_wellplate_400ul', '1','ROCHE MagnaPure 96 Well Plate 400 uL') ############################################ ######## Deepwell - comes from A magdeck = ctx.load_module('Magnetic Module Gen2', '4') deepwell_plate = magdeck.load_labware('nest_96_wellplate_2ml_deep', 'NEST 96 Deepwell Plate 2mL') # Change to NEST deepwell plate. #################################### ######## Waste reservoir waste_reservoir = ctx.load_labware('nest_1_reservoir_195ml', '7', 'waste reservoir') # Change to our waste reservoir waste = waste_reservoir.wells()[0] # referenced as reservoir #################################### ######### Load tip_racks tips300 = [ctx.load_labware('opentrons_96_tiprack_300ul', slot, '200µl filter tiprack') for slot in ['3', '6', '8', '9', '10', '11']] ############################################################################### #Declare which reagents are in each reservoir as well as deepwell and elution plate Beads.reagent_reservoir = reagent_res.rows()[0][0:4] Wash_1.reagent_reservoir = reagent_res.rows()[0][4:6] Wash_2.reagent_reservoir = reagent_res.rows()[0][6:8] Elution.reagent_reservoir = reagent_res.rows()[0][10:11] work_destinations = deepwell_plate.rows()[0][:Sample.num_wells] final_destinations = elution_plate.rows()[0][:Sample.num_wells] # pipettes. m300 = ctx.load_instrument('p300_multi_gen2', 'left', 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 'num_refills' : {m300 : 0}, 'tips': { m300: [tip for rack in tips300 for tip in rack.rows()[0]]} } ############################################################################### start_run() magdeck.disengage() ############################################################################### # STEP 1 Transferir bolas magnéticas ######## STEP += 1 if STEPS[STEP]['Execute']==True: #Transferir bolas magnéticas start = log_step_start() beads_trips = math.ceil(Beads.reagent_volume / Beads.max_volume_allowed) beads_volume = Beads.reagent_volume / beads_trips beads_transfer_vol = [] for i in range(beads_trips): beads_transfer_vol.append(beads_volume + Beads.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 # transfer_vol_extra = transfer_vol if j > 0 else transfer_vol + 100 # Extra 100 isopropanol for calcs # [pickup_height, change_col] = calc_height(Beads, multi_well_rack_area, transfer_vol_extra * 8) [pickup_height, change_col] = calc_height(Beads, 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('Mezclando nuevo canal del reservorio: ' + str(Beads.col + 1)) custom_mix(m300, Beads, Beads.reagent_reservoir[Beads.col], vol = Beads.max_volume_allowed, rounds = BEADS_WELL_FIRST_TIME_NUM_MIXES, blow_out = False, mix_height = 1.5, offset = 0) first_mix_done = True else: ctx.comment('Mezclando canal del reservorio: ' + str(Beads.col + 1)) mix_height = 1.5 if pickup_height > 1.5 else pickup_height custom_mix(m300, Beads, Beads.reagent_reservoir[Beads.col], vol = Beads.max_volume_allowed, rounds = BEADS_WELL_NUM_MIXES, blow_out = False, mix_height = mix_height, offset = 0) ctx.comment('Aspirando desde la columna del reservorio: ' + str(Beads.col + 1)) ctx.comment('La altura de recogida es ' + str(pickup_height)) move_vol_multi(m300, reagent = Beads, source = Beads.reagent_reservoir[Beads.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 = 0, blow_out = True, touch_tip = False, drop_height = 1) if BEADS_NUM_MIXES > 0: ctx.comment(' ') ctx.comment('Mezclando muestra ') custom_mix(m300, Beads, location = work_destinations[i], vol = Beads.max_volume_allowed, rounds = BEADS_NUM_MIXES, blow_out = False, mix_height = 1, offset = 0, wait_time = 2) m300.move_to(work_destinations[i].top(0)) m300.air_gap(Beads.air_gap_vol_bottom) #air gap if recycle_tip: m300.return_tip() else: m300.drop_tip(home_after = False) tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 1 Transferir bolas magnéticas ######## ############################################################################### # STEP 2 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() ctx.comment(' ') magdeck.engage(height = mag_height) ctx.delay(seconds = STEPS[STEP]['wait_time'], msg = 'Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') ctx.comment(' ') log_step_end(start) ############################################################################### # STEP 2 Incubación con el imán ON ######## ############################################################################### # STEP 3 Desechar sobrenadante ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() supernatant_trips = math.ceil((Sample.reagent_volume + Beads.reagent_volume) / Sample.max_volume_allowed) supernatant_volume = Sample.max_volume_allowed # We try to remove an exceeding amount of supernatant to make sure it is empty supernatant_transfer_vol = [] for i in range(supernatant_trips): supernatant_transfer_vol.append(supernatant_volume + Sample.disposal_volume) x_offset_rs = 2 pickup_height = 0.5 # Original 0.5 for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs x_offset_dest = 0 not_first_transfer = False if not m300.hw_pipette['has_tip']: pick_up(m300) for transfer_vol in supernatant_transfer_vol: ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(pickup_height) ) move_vol_multi(m300, reagent = Sample, source = work_destinations[i], dest = waste, vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, rinse = False, avoid_droplet = False, wait_time = 2, blow_out = True, dispense_bottom_air_gap_before = not_first_transfer, drop_height = waste_drop_height) m300.move_to(waste.top(z = waste_drop_height)) m300.air_gap(Sample.air_gap_vol_bottom) not_first_transfer = True if recycle_tip: m300.return_tip() else: m300.drop_tip(home_after = False) tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 3 Desechar sobrenadante ######## ############################################################################### # STEP 4 MAGNET OFF ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # Imán OFF magdeck.disengage() log_step_end(start) ############################################################################### # STEP 4 MAGNET OFF ######## ############################################################################### # STEP 5 Transferir primer lavado ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() wash_trips = math.ceil(Wash_1.reagent_volume / Wash_1.max_volume_allowed) wash_volume = Wash_1.reagent_volume / wash_trips #136.66 wash_transfer_vol = [] for i in range(wash_trips): wash_transfer_vol.append(wash_volume + Wash_1.disposal_volume) x_offset_rs = 2.5 rinse = False # Not needed for i in range(num_cols): x_offset_source = 0 x_offset_dest = -1 * find_side(i) * x_offset_rs if not m300.hw_pipette['has_tip']: pick_up(m300) if TIP_RECYCLING_IN_WASH: w1_tip_pos_list += [tip_track['tips'][m300][int(tip_track['counts'][m300] / 8)]] for transfer_vol in wash_transfer_vol: #Calculate pickup_height based on remaining volume and shape of container [pickup_height, change_col] = calc_height(Wash_1, multi_well_rack_area, transfer_vol*8) ctx.comment('Aspirando desde la columna del reservorio: ' + str(Wash_1.col)) ctx.comment('La altura de recogida es ' + str(pickup_height)) move_vol_multi(m300, reagent = Wash_1, source = Wash_1.reagent_reservoir[Wash_1.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 = 0, blow_out = False) if WASH_1_NUM_MIXES > 0: custom_mix(m300, Wash_1, location = work_destinations[i], vol = 180, two_thirds_mix_bottom = True, rounds = WASH_1_NUM_MIXES, blow_out = False, mix_height = 1.5, offset = x_offset_dest) m300.move_to(work_destinations[i].top(0)) m300.air_gap(Wash_1.air_gap_vol_bottom) #air gap if recycle_tip or TIP_RECYCLING_IN_WASH: m300.return_tip() else: m300.drop_tip(home_after = False) tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 5 ADD WASH ######## ############################################################################### # STEP 6 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # switch on magnet magdeck.engage(mag_height) ctx.delay(seconds=STEPS[STEP]['wait_time'], msg='Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') log_step_end(start) #################################################################### # STEP 6 Incubación con el imán ON ######## ############################################################################### # STEP 7 Desechar sobrenadante ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() supernatant_trips = math.ceil(Wash_1.reagent_volume / Wash_1.max_volume_allowed) supernatant_volume = Wash_1.max_volume_allowed # We try to remove an exceeding amount of supernatant to make sure it is empty supernatant_transfer_vol = [] for i in range(supernatant_trips): supernatant_transfer_vol.append(supernatant_volume + Sample.disposal_volume) x_offset_rs = 2 pickup_height = 0.5 # Original 0.5 for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs x_offset_dest = 0 drop_height = 15 not_first_transfer = False if not m300.hw_pipette['has_tip']: if TIP_RECYCLING_IN_WASH: pick_up(m300, w1_tip_pos_list[i]) m300.dispense(Wash_1.air_gap_vol_top, work_destinations[i].top(z = 0), rate = Wash_1.flow_rate_dispense) else: pick_up(m300) for transfer_vol in supernatant_transfer_vol: #Pickup_height is fixed here ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(pickup_height) ) move_vol_multi(m300, reagent = Sample, source = work_destinations[i], dest = waste, vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, rinse = False, avoid_droplet = False, wait_time = 2, blow_out = False, dispense_bottom_air_gap_before = not_first_transfer, drop_height = waste_drop_height) m300.move_to(waste.top(z = waste_drop_height)) m300.air_gap(Sample.air_gap_vol_bottom) not_first_transfer = True if recycle_tip: m300.return_tip() else: m300.drop_tip(home_after = False) if not TIP_RECYCLING_IN_WASH: tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 7 Desechar sobrenadante ######## ############################################################################### # STEP 8 MAGNET OFF ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # Imán OFF magdeck.disengage() log_step_end(start) ############################################################################### # STEP 8 MAGNET OFF ######## ############################################################################### # STEP 9 Transferir segundo lavado ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() wash_trips = math.ceil(Wash_2.reagent_volume / Wash_2.max_volume_allowed) wash_volume = Wash_2.reagent_volume / wash_trips #136.66 wash_transfer_vol = [] for i in range(wash_trips): wash_transfer_vol.append(wash_volume + Wash_2.disposal_volume) x_offset_rs = 2.5 rinse = False # Not needed for i in range(num_cols): x_offset_source = 0 x_offset_dest = -1 * find_side(i) * x_offset_rs if not m300.hw_pipette['has_tip']: pick_up(m300) if TIP_RECYCLING_IN_WASH: w2_tip_pos_list += [tip_track['tips'][m300][int(tip_track['counts'][m300] / 8)]] for transfer_vol in wash_transfer_vol: #Calculate pickup_height based on remaining volume and shape of container [pickup_height, change_col] = calc_height(Wash_2, multi_well_rack_area, transfer_vol*8) ctx.comment('Aspirando desde la columna del reservorio: ' + str(Wash_2.col)) ctx.comment('La altura de recogida es ' + str(pickup_height)) move_vol_multi(m300, reagent = Wash_2, source = Wash_2.reagent_reservoir[Wash_2.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 = 0, blow_out = False) if WASH_2_NUM_MIXES > 0: custom_mix(m300, Wash_2, location = work_destinations[i], vol = 180, two_thirds_mix_bottom = True, rounds = WASH_2_NUM_MIXES, blow_out = False, mix_height = 1.5, offset = x_offset_dest) m300.move_to(work_destinations[i].top(0)) m300.air_gap(Wash_2.air_gap_vol_bottom) #air gap if recycle_tip or TIP_RECYCLING_IN_WASH: m300.return_tip() else: m300.drop_tip(home_after = False) tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 9 ADD WASH ######## ############################################################################### # STEP 10 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # switch on magnet magdeck.engage(mag_height) ctx.delay(seconds=STEPS[STEP]['wait_time'], msg='Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') log_step_end(start) #################################################################### # STEP 10 Incubación con el imán ON ######## ############################################################################### # STEP 11 Desechar sobrenadante ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() supernatant_trips = math.ceil(Wash_2.reagent_volume / Wash_2.max_volume_allowed) supernatant_volume = Wash_2.max_volume_allowed # We try to remove an exceeding amount of supernatant to make sure it is empty supernatant_transfer_vol = [] for i in range(supernatant_trips): supernatant_transfer_vol.append(supernatant_volume + Sample.disposal_volume) x_offset_rs = 2 pickup_height = 0.5 # Original 0.5 for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs x_offset_dest = 0 not_first_transfer = False if not m300.hw_pipette['has_tip']: if TIP_RECYCLING_IN_WASH: pick_up(m300, w2_tip_pos_list[i]) m300.dispense(Wash_2.air_gap_vol_top, work_destinations[i].top(z = 0), rate = Wash_2.flow_rate_dispense) else: pick_up(m300) for transfer_vol in supernatant_transfer_vol: #Pickup_height is fixed here ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(pickup_height) ) move_vol_multi(m300, reagent = Sample, source = work_destinations[i], dest = waste, vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, rinse = False, avoid_droplet = False, wait_time = 2, blow_out = False, dispense_bottom_air_gap_before = not_first_transfer, drop_height = waste_drop_height) m300.move_to(waste.top(z = waste_drop_height)) m300.air_gap(Sample.air_gap_vol_bottom) not_first_transfer = True if recycle_tip: m300.return_tip() else: m300.drop_tip(home_after = False) if not TIP_RECYCLING_IN_WASH: tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 11 Desechar sobrenadante ######## ############################################################################### # STEP 12 ALLOW DRY ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() ctx.comment(' ') ctx.delay(seconds=STEPS[STEP]['wait_time'], msg='Dry for ' + format(STEPS[STEP]['wait_time']) + ' segundos.') # ctx.comment(' ') log_step_end(start) ############################################################################### # STEP 12 ALLOW DRY ######## ############################################################################### # STEP 13 MAGNET OFF ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # Imán OFF magdeck.disengage() log_step_end(start) ############################################################################### # STEP 13 MAGNET OFF ######## ############################################################################### # STEP 14 Transferir elución ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() elution_trips = math.ceil(Elution.reagent_volume / Elution.max_volume_allowed) elution_volume = Elution.reagent_volume / elution_trips elution_wash_vol = [] for i in range(elution_trips): elution_wash_vol.append(elution_volume + Sample.disposal_volume) x_offset_rs = 2.5 ######## # Water or elution buffer for i in range(num_cols): x_offset_source = 0 x_offset_dest = -1 * find_side(i) * x_offset_rs # Original 0 if not m300.hw_pipette['has_tip']: pick_up(m300) if TIP_RECYCLING_IN_ELUTION: elution_tip_pos_list += [tip_track['tips'][m300][int(tip_track['counts'][m300] / 8)]] for transfer_vol in elution_wash_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('Aspirando desde la columna del reservorio: ' + str(Elution.col)) ctx.comment('La altura de recogida es ' + str(pickup_height)) move_vol_multi(m300, reagent = Elution, source = Elution.reagent_reservoir[Elution.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 = False, avoid_droplet = False, wait_time = 0, blow_out = False, drop_height = -35) if ELUTION_NUM_MIXES > 0: ctx.comment(' ') ctx.comment('Mezclando muestra with Elution') custom_mix(m300, Elution, work_destinations[i], vol = Elution.reagent_volume, rounds = ELUTION_NUM_MIXES, blow_out = False, mix_height = 1, offset = x_offset_dest, drop_height = -35) m300.move_to(work_destinations[i].top(0)) m300.air_gap(Elution.air_gap_vol_bottom) #air gap if recycle_tip or TIP_RECYCLING_IN_ELUTION: m300.return_tip() else: m300.drop_tip(home_after = False) tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 14 Transferir elución ######## ############################################################################### # STEP 15 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # switch on magnet magdeck.engage(mag_height) ctx.delay(seconds=STEPS[STEP]['wait_time'], msg='Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') log_step_end(start) #################################################################### # STEP 15 Incubación con el imán ON ######## ############################################################################### # STEP 16 TRANSFER TO FINAL PLATES ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() elution_trips = math.ceil(ELUTION_FINAL_VOLUME_PER_SAMPLE / Elution.max_volume_allowed) elution_volume = ELUTION_FINAL_VOLUME_PER_SAMPLE / elution_trips elution_vol = [] for i in range(elution_trips): elution_vol.append(elution_volume + Elution.disposal_volume) x_offset_rs = 2 for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs x_offset_dest = 0 if not m300.hw_pipette['has_tip']: if TIP_RECYCLING_IN_ELUTION: pick_up(m300, elution_tip_pos_list[i]) m300.dispense(Elution.air_gap_vol_top, work_destinations[i].top(z = 0), rate = Elution.flow_rate_dispense) else: pick_up(m300) for transfer_vol in elution_vol: #Pickup_height is fixed here pickup_height = 1 ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(pickup_height) ) move_vol_multi(m300, reagent = Sample, source = work_destinations[i], dest = final_destinations[i], vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, rinse = False, avoid_droplet = False, wait_time = 0, blow_out = True, touch_tip = False, drop_height = 3) m300.move_to(final_destinations[i].top(0)) m300.air_gap(Sample.air_gap_vol_bottom) #air gap if recycle_tip: m300.return_tip() else: m300.drop_tip(home_after = False) if not TIP_RECYCLING_IN_ELUTION: tip_track['counts'][m300] += 8 log_step_end(start) ############################################################################### # STEP 16 TRANSFER TO FINAL PLATES ######## '''if not ctx.is_simulating(): with open(file_path,'w') as outfile: json.dump(STEPS, outfile)''' magdeck.disengage() 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() ############################################################################ finish_run()
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': 'Transferir solución con bolas magnéticas' }, 2: { 'Execute': True, 'description': 'Transferir solución de lavado' }, 3: { 'Execute': True, 'description': 'Transferir etanol' }, 4: { 'Execute': True, 'description': 'Transferir elución' } } #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 + '/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=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 Wash = Reagent( name='Wash', flow_rate_aspirate=25, # Original = 0.5 flow_rate_dispense=100, # 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=270, 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=25, flow_rate_dispense=100, 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=270, 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=280, 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=25, # Original 0.5 flow_rate_dispense=100, # 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=270, 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 # Calculate Beads channel volumes def calc_beads_vol_well_original(beads): # Reajusting Channel volume to fit complete miti-tip dispense trips = math.ceil(BEADS_VOLUME_PER_SAMPLE / beads.max_volume_allowed) total_trips = math.ceil(NUM_SAMPLES / 8) * trips trip_vol = 8 * beads.max_volume_allowed * 1.1 max_trips_per_well = math.floor(11500 / trip_vol) trips_per_well = math.ceil(total_trips / beads.num_wells) # Add an additional channel if needed if trips_per_well > max_trips_per_well: ctx.comment('Adding an additional beads channel') beads.num_wells = beads.num_wells + 1 trips_per_well = math.ceil(total_trips / beads.num_wells) beads.vol_well_original = trips_per_well * trip_vol + DEFAULT_DEAD_VOL ctx.comment('Reajusting Bead Channels volume to: ' + str(beads.vol_well_original) + ' ul') ctx.comment('Number of Beads channels: ' + str(beads.num_wells)) beads.vol_well = beads.vol_well_original return beads.vol_well_original calc_beads_vol_well_original(Beads_PK_Binding) 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('VALORES DE VARIABLES') ctx.comment(' ') ctx.comment('Número de muestras: ' + str(NUM_SAMPLES) + ' (' + str(num_cols) + ' ' + ('columna' if num_cols == 1 else 'columnas') + ')') ctx.comment(' ') ctx.comment('Volumen de beads por muestra: ' + str(BEADS_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen del lavado por muestra: ' + str(WASH_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen del etanol por muestra: ' + str(ETHANOL_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen de elución por muestra: ' + str(ELUTION_VOLUME_PER_SAMPLE) + ' ul') ctx.comment(' ') ctx.comment( 'Número de mezclas en la primera recogida de un canal con bolas magnéticas: ' + str(BEADS_WELL_FIRST_TIME_NUM_MIXES)) ctx.comment( 'Número de mezclas en el resto de recogidas de un canal con bolas magnéticas: ' + str(BEADS_WELL_NUM_MIXES)) ctx.comment('Número de mezclas con la solución de bolas magnéticas: ' + str(BEADS_NUM_MIXES)) ctx.comment(' ') ctx.comment('Repeticiones del sonido final: ' + str(SOUND_NUM_PLAYS)) ctx.comment('Foto-sensible: ' + str(PHOTOSENSITIVE)) ctx.comment(' ') ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VOLÚMENES PARA ' + str(NUM_SAMPLES) + ' MUESTRAS') ctx.comment(' ') ctx.comment( 'Beads + PK + Binding: ' + str(Beads_PK_Binding.num_wells) + (' canal' if Beads_PK_Binding.num_wells == 1 else ' canales') + ' desde el canal 2 en el reservorio de 12 canales con un volumen de ' + str_rounded(Beads_PK_Binding.vol_well_original) + ' uL cada uno') ctx.comment( 'Elution: ' + str(Elution.num_wells) + (' canal' if Elution.num_wells == 1 else ' canales') + ' desde el canal 7 en el reservorio de 12 canales con un volumen de ' + str_rounded(Elution.vol_well_original) + ' uL cada uno') ctx.comment('Wash: en el reservorio 1 (slot 2) con un volumen de ' + str_rounded(Wash.vol_well_original) + ' uL') ctx.comment('Etanol: en el reservorio 2 (slot 3) con un volumen de ' + 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, dispense_bottom_air_gap_before=False): # 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 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 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.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.') 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=drop_height)) 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 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 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 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) # TODO: Añadir refills a los tip_racks # used_tips = tip_track['num_refills'][m300] * 96 * len(m300.tip_racks) + tip_track['counts'][m300] ctx.comment('Puntas de 300 uL utilizadas: ' + str(tip_track['counts'][m300]) + ' (' + str(round(tip_track['counts'][m300] / 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 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, '300µ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 } ############################################################################### ############################################################################### start_run() ############################################################################### # 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 ctx.comment('bead_trips= ' + str(beads_trips)) ctx.comment('beads_volume= ' + str(beads_volume) + 'ul --> trip volume') beads_transfer_vol = [] for i in range(beads_trips): ctx.comment('Trip ' + str(i) + ' --> ' + str(beads_volume + Beads_PK_Binding.disposal_volume) + ' ul') 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): not_first_transfer = False 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_MIX_VOLUME, 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_MIX_VOLUME, 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=False, touch_tip=False, drop_height=5, dispense_bottom_air_gap_before=not_first_transfer) m300.air_gap(Beads_PK_Binding.air_gap_vol_bottom, height=5) not_first_transfer = True ctx.comment(' ') ctx.comment('Mixing sample ') custom_mix(m300, Beads_PK_Binding, location=work_destinations[i], vol=BEADS_MIX_VOLUME, rounds=BEADS_NUM_MIXES, blow_out=False, mix_height=0, offset=0, wait_time=2, two_thirds_mix_bottom=True) m300.air_gap(Beads_PK_Binding.air_gap_vol_bottom, height=0) #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=False) 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=False) 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=False) 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() finish_run(switch_off_lights)
def run(protocol: protocol_api.ProtocolContext): if debug: print(protocol) tiprack_300 = protocol.load_labware('opentrons_96_tiprack_300ul', labwarePositions.tiprack_300, "tiprack 300ul") if debug: print(tiprack_300) pipette_300 = protocol.load_instrument( 'p300_single_gen2' if pipette_300_GEN == 'GEN2' else 'p300_single', pipette_300_location, tip_racks=[tiprack_300]) # pipette_300 = protocol.load_instrument('p300_single', 'right', tip_racks=[tiprack_300]) pipette_300.flow_rate.dispense = default_flow_rate pipette_300.flow_rate.aspirate = default_flow_rate pipette_300.starting_tip = tiprack_300.wells()[tiprack_300_starting_pos - 1] # pipette_300.starting_tip = tiprack_300.well(tiprack_starting_pos['tiprack_300']) if debug: print(pipette_300) black_96 = protocol.load_labware(type_of_96well_plate, labwarePositions.antibodies_plate, type_of_96well_plate) trough12 = protocol.load_labware('parhelia_12trough', labwarePositions.buffers_reservoir, '12-trough buffers reservoir') if (not retrieval): par2 = protocol.load_labware(omniStainer_type, labwarePositions.par2, omniStainer_type) if retrieval: temp_mod = protocol.load_module('temperature module', labwarePositions.heatmodule) par2 = temp_mod.load_labware(omniStainer_type) wellslist = list(par2.wells_by_name().keys()) wellslist = wellslist[1:num_samples + 1] if debug: print(par2) buffer_wells = trough12.wells_by_name() buffers = Object() buffers.retrieval = buffer_wells['A1'] buffers.TBS_wash = buffer_wells['A2'] buffers.water = buffer_wells['A3'] buffers.storage = buffer_wells['A4'] buffers.eth_70perc_ = buffer_wells['A5'] buffers.eth_80perc = buffer_wells['A6'] buffers.eth_95perc = buffer_wells['A7'] buffers.eth_100perc = buffer_wells['A8'] buffers.hematoxylin = buffer_wells['A12'] preblock_wells = black_96.rows()[0] antibody_wells = black_96.rows()[5] enzymeblock_wells = black_96.rows()[1] hrpsecondaryab_wells = black_96.rows()[2] substrate_wells = black_96.rows()[3] DAB_wells = black_96.rows()[4] sample_chambers = [] for well in wellslist: sample_chambers.append(par2.wells_by_name()[well]) if debug: print(sample_chambers) #################PROTOCOL#################### protocol.home() if lid == 'yes': openPar2(protocol, pipette_300, par2) if retrieval: washSamples(pipette_300, buffers.retrieval, buffers.retrieval, 2, 1, extra_bottom_gap + 18) washSamples(pipette_300, buffers.retrieval, sample_chambers, wash_volume, 2, extra_bottom_gap) if lid == 'yes': closePar2(protocol, pipette_300, par2) temp_mod.set_temperature(95) print("retrieval") protocol.delay(minutes=15) # washSamples(pipette_300, buffers.retrieval, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=15) # washSamples(pipette_300, buffers.retrieval, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=15) print("cooling down to RT") temp_mod.set_temperature(25) protocol.delay(minutes=20) if lid == 'yes': openPar2(protocol, pipette_300, par2) # WASHING SAMPLES WITH TBS print("washing in TBS") washSamples(pipette_300, buffers.TBS_wash, buffers.TBS_wash, 2, 1, extra_bottom_gap + 18) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 2, extra_bottom_gap) # Preblocking print("preblocking") print(len(wellslist)) print("puncturing preblock wells") for i in range(len(wellslist)): print(i) washSamples(pipette_300, preblock_wells[i], preblock_wells[i], 2, 1, extra_bottom_gap + 18, keep_tip=True) pipette_300.drop_tip() print("applying the preblock") for i in range(len(wellslist)): print(i) washSamples(pipette_300, preblock_wells[i], sample_chambers[i], ab_volume, 1, extra_bottom_gap) print("preblocking incubation: 15 min") protocol.delay(minutes=15) # APPLYING ANTIBODY COCKTAILS TO SAMPLES print("puncturing and applying abs") for i in range(len(wellslist)): print(i) washSamples(pipette_300, antibody_wells[i], antibody_wells[i], 2, 1, extra_bottom_gap + 18, keep_tip=True) washSamples(pipette_300, antibody_wells[i], sample_chambers[i], ab_volume, 1, extra_bottom_gap) if lid == 'yes': closePar2(protocol, pipette_300, par2) # INCUBATE FOR DESIRED TIME print("staining incubation: " + str(primary_ab_incubation_time_minutes) + "min") protocol.delay(minutes=primary_ab_incubation_time_minutes) if lid == 'yes': openPar2(protocol, pipette_300, par2) # WASHING SAMPLES WITH TBS # three individual repeats below is because they need particular incubation time between them print("washing with TBS") for i in range(5): washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=3) # APPLYING enzyme blocking print("puncturing enzyme blocking wells") for i in range(len(wellslist)): washSamples(pipette_300, enzymeblock_wells[i], enzymeblock_wells[i], 2, 1, extra_bottom_gap + 18, keep_tip=True) pipette_300.drop_tip() print("applying enzyme blocking") for i in range(len(wellslist)): washSamples(pipette_300, enzymeblock_wells[i], sample_chambers[i], ab_volume, 1, extra_bottom_gap) # INCUBATE 10 MIN print("hrp blocking incubation: 10min") protocol.delay(minutes=10) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 3, extra_bottom_gap) # APPLYING HRP SECONDARY ANTIBODY COCKTAILS TO SAMPLES print("puncturing hrpsecondaryab wells") for i in range(len(wellslist)): washSamples(pipette_300, hrpsecondaryab_wells[i], hrpsecondaryab_wells[i], 2, 1, extra_bottom_gap + 18, keep_tip=True) pipette_300.drop_tip() print("applying hrpsecondaryab") for i in range(len(wellslist)): washSamples(pipette_300, hrpsecondaryab_wells[i], sample_chambers[i], ab_volume, 1, extra_bottom_gap) if lid == 'yes': closePar2(protocol, pipette_300, par2) # INCUBATE FOR DESIRED TIME print("staining incubation: " + str(secondary_ab_incubation_time_minutes) + "min") protocol.delay(minutes=secondary_ab_incubation_time_minutes) if lid == 'yes': openPar2(protocol, pipette_300, par2) # three individual repeats below is because they need particular incubation time between them print("washing with TBS") for i in range(3): washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=3) # DILUTING AND APPLYING THE DAB print("puncturing the DAB wells") for i in range(len(wellslist)): washSamples(pipette_300, DAB_wells[i], DAB_wells[i], 2, 1, extra_bottom_gap + 18) print("puncturing the substrate wells") for i in range(len(wellslist)): washSamples(pipette_300, substrate_wells[i], substrate_wells[i], 2, 1, extra_bottom_gap + 18, keep_tip=True) pipette_300.drop_tip() print("applying DAB") for i in range(len(wellslist)): dilute_and_apply_fixative(pipette_300, DAB_wells[i], substrate_wells[i], sample_chambers[i], wash_volume) print("developing substrate") protocol.delay(minutes=10) washSamples(pipette_300, buffers.water, buffers.water, 2, 1, extra_bottom_gap + 18) washSamples(pipette_300, buffers.water, sample_chambers, wash_volume, 5, extra_bottom_gap) if lid == 'yes': closePar2(protocol, pipette_300, par2)
def run(protocol: protocol_api.ProtocolContext): """ Aliquoting water from a 5mL screwcap tube into a 96wells plate """ # ============================================================================= # ======================LOADING LABWARE AND PIPETTES=========================== # ============================================================================= ## For available labware see "labware/list_of_available_labware". ## plate_96 = protocol.load_labware( 'biorad_96_wellplate_200ul_pcr', #labware definition 1, #deck position 'plate_96') #custom name #### !!! OPTION 1: ROBOT tubes_5mL = protocol.load_labware( 'eppendorfscrewcap_15_tuberack_5000ul', #labware def 2, #deck position '5mL_tubes') #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 tips_200 = protocol.load_labware( 'opentrons_96_filtertiprack_200ul', #labware definition 3, #deck position 'tips_200') #custom name ##### Loading pipettes p300 = protocol.load_instrument( 'p300_single_gen2', #instrument definition 'right', #mount position tip_racks=[tips_200]) #assigned tiprack # ============================================================================= # ==========================VARIABLES TO SET#!!!=============================== # ============================================================================= start_vol_1 = 5000 start_vol_2 = 2200 ## The start_vol is the volume (ul) that is in the source labware at ## ## the start of the protocol. ## p300.starting_tip = tips_200.well('E3') ## The starting_tip is the location of first pipette tip in the box ## # ============================================================================= # ==========================PREDIFINED VARIABLES=============================== # ============================================================================= container = 'tube_5mL' # ============================================================================= ##### Variables for volume tracking start_height_1 = cal_start_height(container, start_vol_1) start_height_2 = cal_start_height(container, start_vol_2) # ============================================================================= # ===============================PROTOCOL====================================== # ============================================================================= # # Full 5mL tube # current_height = start_height_1 # for i, well in enumerate(plate_96.wells()): # 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 = volume_tracking( # container, 100, current_height) # ## Make sure that the pipette tip is always submerged by setting ## # ## the current height 2 mm below its actual height ## # if bottom_reached: # protocol.home() # protocol.pause("the tube is empty!") # else: # aspiration_location = tubes_5mL['C1'].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(100, aspiration_location) # ## Aspirate 200µL from the set aspiration location ## # p300.dispense(100, well.top(z=-2)) # ## Dispense 200µL in the destination wel ## # protocol.pause('change plate') # 2200µL in 5mL tube current_height = start_height_2 for i, well in enumerate(plate_96.wells()): 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 = volume_tracking( container, 100, current_height) ## Make sure that the pipette tip is always submerged by setting ## ## the current height 2 mm below its actual height ## if bottom_reached: protocol.home() protocol.pause("the tube is empty!") else: aspiration_location = tubes_5mL['C2'].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(100, aspiration_location) ## Aspirate 200µL from the set aspiration location ## p300.dispense(100, well.top(z=-2))
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(ctx: protocol_api.ProtocolContext): [ _m300_mount, _num_samps, _inc_time, _mag_time, _air_dry, _add_water, _samp_labware, _asp_ht, _fin_wash, _elution_vol ] = get_values( # noqa: F821 (<--- DO NOT REMOVE!) '_m300_mount', '_num_samps', '_inc_time', '_mag_time', '_air_dry', '_add_water', '_samp_labware', '_asp_ht', '_fin_wash', '_elution_vol') if not 1 <= _num_samps <= 96: raise Exception("The 'Number of Samples' should be between 1 and 96") # define all custom variables above here with descriptions m300_mount = _m300_mount # mount for 8-channel p300 pipette num_cols = math.ceil(_num_samps / 8) # number of sample columns samp_labware = _samp_labware # labware containing sample elution_vol = _elution_vol # volume of elution buffer inc_time = _inc_time mag_time = _mag_time air_dry = _air_dry fin_wash = _fin_wash asp_ht = _asp_ht add_water = _add_water mag_deck = ctx.load_module('magnetic module gen2', 7) # load labware rsvr_12 = [ctx.load_labware('nest_12_reservoir_15ml', s) for s in [2, 3]] rsvr_1 = ctx.load_labware('nest_1_reservoir_195ml', 10) pcr_plate = ctx.load_labware('nest_96_wellplate_100ul_pcr_full_skirt', 1) samp_plate = ctx.load_labware(samp_labware, 4) mag_plate = mag_deck.load_labware('nest_96_wellplate_2ml_deep') # load tipracks tips = [ ctx.load_labware('opentrons_96_filtertiprack_200ul', s) for s in [5, 6, 8, 9, 11] ] all_tips = [t for rack in tips for t in rack.rows()[0]] t_start = 0 t_end = int(num_cols) # load instrument m300 = ctx.load_instrument('p300_multi_gen2', m300_mount, tip_racks=tips) # extend well objects for improved liquid handling class WellH(Well): def __init__(self, well, min_height=0.5, comp_coeff=1.1, current_volume=0): super().__init__(well._impl) self.well = well # specified minimum well bottom clearance self.min_height = min_height self.comp_coeff = comp_coeff # specified starting volume in ul self.current_volume = current_volume # cross sectional area if self.diameter is not None: self.radius = self.diameter / 2 cse = math.pi * (self.radius**2) elif self.length is not None: cse = self.length * self.width else: cse = None self.cse = cse # initial liquid level in mm from start vol if cse: self.height = (current_volume / cse) else: raise Exception("""Labware definition must supply well radius or well length and width.""") if self.height < min_height: self.height = min_height elif self.height > well.parent.highest_z: raise Exception("""Specified liquid volume can not exceed the height of the labware.""") def height_dec(self, vol, ppt, bottom=False): # decrement height (mm) dh = (vol / self.cse) * self.comp_coeff # tip immersion (mm) as fraction of tip length mm_immersed = 0.05 * ppt._tip_racks[0].wells()[0].depth # decrement til target reaches specified min clearance self.height = self.height - dh if ( (self.height - dh - mm_immersed) > self.min_height ) else self.min_height + mm_immersed self.current_volume = self.current_volume - vol if ( self.current_volume - vol > 0) else 0 tip_ht = self.height - mm_immersed if bottom is False else bottom return (self.well.bottom(tip_ht)) def height_inc(self, vol, top=False): # increment height (mm) ih = (vol / self.cse) * self.comp_coeff # keep calculated liquid ht between min clearance and well depth self.height = self.min_height if ( self.height < self.min_height) else self.height self.height = (self.height + ih) if ( (self.height + ih) < self.depth) else self.depth # increment self.current_volume += vol if top is False: tip_ht = self.height return (self.well.bottom(tip_ht)) else: return (self.well.top()) # pipette functions # INCLUDE ANY BINDING TO CLASS def aspirate_h(self, vol, source, rate=1, bottom=False): self.aspirate(vol, source.height_dec(vol, self, bottom=bottom), rate=rate) def dispense_h(self, vol, dest, rate=1, top=False): self.dispense(vol, dest.height_inc(vol, top=top), rate=rate) def slow_tip_withdrawal(self, speed_limit, well_location, to_surface=False): if self.mount == 'right': axis = 'A' else: axis = 'Z' previous_limit = None if axis in ctx.max_speeds.keys(): for key, value in ctx.max_speeds.items(): if key == axis: previous_limit = value ctx.max_speeds[axis] = speed_limit if to_surface is False: self.move_to(well_location.top()) else: if isinstance(well_location, WellH): self.move_to(well_location.bottom().move( types.Point(x=0, y=0, z=well_location.height + (20 * (self._tip_racks[0].wells()[0].depth / 88))))) else: self.move_to(well_location.center()) ctx.max_speeds[axis] = previous_limit def custom_pick_up(self, loc=None): nonlocal t_start nonlocal t_end """`custom_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 loc: User can manually specify location for tip pick up """ if loc: self.pick_up_tip(loc) else: try: self.pick_up_tip() except protocol_api.labware.OutOfTipsError: flash_lights() ctx.pause("Replace empty tip racks") self.reset_tipracks() t_start = 0 t_end = int(num_cols) ctx.set_rail_lights(True) self.pick_up_tip() # bind additional methods to pipettes for met in [aspirate_h, dispense_h, slow_tip_withdrawal, custom_pick_up]: setattr(m300, met.__name__, MethodType(met, m300)) # reagents liquid_waste = rsvr_1.wells()[0].top() cspl = [WellH(well) for well in rsvr_12[0].wells()[:6]] for idx in range(num_cols): cspl[idx // 2].height_inc(720 * 8 * 1.1) # helper functions def flash_lights(): for _ in range(19): ctx.set_rail_lights(not ctx.rail_lights_on) ctx.delay(seconds=0.25) def flow_rate(asp=92.86, disp=92.86, blow=92.86): """ This function can be used to quickly modify the flow rates of the m300 If no parameters are entered, the flow rates will be reset. :param asp: Aspiration flow rate, in uL/sec :param disp: Dispense flow rate, in uL/sec """ m300.flow_rate.aspirate = asp m300.flow_rate.dispense = disp m300.flow_rate.blow_out = blow def remove_supernatant(vol, src): w = int(str(src).split(' ')[0][1:]) radi = float(src.width)/4 if src.width is not None else \ float(src.diameter)/4 x0 = radi if w % 2 == 0 else -radi while vol > 180: m300.aspirate(180, src.bottom().move(types.Point(x=x0, y=0, z=1))) m300.dispense(200, liquid_waste) m300.blow_out() m300.aspirate(20, liquid_waste) vol -= 180 m300.aspirate(vol, src.bottom().move(types.Point(x=x0, y=0, z=0.7))) m300.dispense(vol + 20, liquid_waste) m300.blow_out() m300.aspirate(10, liquid_waste) def wash(srcs, msg, sup=540): nonlocal t_start nonlocal t_end if mag_deck.status == 'engaged': mag_deck.disengage() ctx.comment(f'\nPerforming wash step: {msg}\n') flow_rate() for idx, col in enumerate(mag_samps_h): m300.custom_pick_up() src = srcs[idx // 3] for i in range(2): flow_rate(asp=150, disp=150) if i == 1: m300.dispense(20, src.top(-1)) m300.dispense(20, src) m300.mix(2, 200, src) flow_rate() m300.aspirate(200, src) m300.slow_tip_withdrawal(10, src, to_surface=True) m300.dispense(180, col.top(-2)) flow_rate(asp=10) m300.aspirate(20, col.top()) m300.dispense(20, src.top()) flow_rate() m300.mix(1, 140, src) m300.aspirate(140, src) m300.slow_tip_withdrawal(10, src, to_surface=True) m300.dispense(140, col) m300.mix(10, 100, col) ctx.delay(seconds=5) m300.slow_tip_withdrawal(10, col, to_surface=True) m300.blow_out() m300.touch_tip(speed=40) m300.aspirate(10, col.top()) m300.drop_tip() mag_deck.engage() mag_msg = f'\nIncubating on Mag Deck for {mag_time} minutes\n' ctx.delay(minutes=mag_time, msg=mag_msg) # Discard Supernatant ctx.comment(f'\nRemoving supernatant for wash: {msg}\n') t_start += num_cols t_end += num_cols for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]): m300.custom_pick_up() remove_supernatant(sup, src) m300.drop_tip(t_d) # plate, tube rack maps init_samps = samp_plate.rows()[0][:num_cols] mag_samps = mag_plate.rows()[0][:num_cols] mag_samps_h = [WellH(well) for well in mag_samps] pcr_samps = pcr_plate.rows()[0][:num_cols] # protocol ctx.set_rail_lights(True) # # Transfer 700µL CSPL Buffer + 20µL Prot K # ctx.comment('\nTransferring 720uL of CSPL Buffer + Proteinase K\n') # # m300.custom_pick_up() # for idx, col in enumerate(init_samps): # src = cspl[idx//2] # for _ in range(4): # m300.aspirate_h(180, src) # m300.slow_tip_withdrawal(10, src, to_surface=True) # m300.dispense(180, col.top(-2)) # m300.drop_tip() # # flash_lights() # ctx.pause('Please remove samples and incubate at 56C for 30 minutes, \ # then centrifuge at 4000g for 10 minutes. Once complete, please replace \ # samples on the deck and place ensure 12-well reservoirs are filled with \ # necessary reagents in deck slots 2 and 3. When ready, click RESUME.') # ctx.set_rail_lights(True) # Creating reagent variables for second part of protocol rbb = [WellH(well, current_volume=0) for well in rsvr_12[0].wells()[:4]] cspw1 = [WellH(well) for well in rsvr_12[0].wells()[6:10]] cspw2 = [WellH(well) for well in rsvr_12[1].wells()[:4]] spm1 = [WellH(well) for well in rsvr_12[1].wells()[4:8]] spm2 = [WellH(well) for well in rsvr_12[1].wells()[8:]] elution_buffer = [WellH(well) for well in rsvr_12[0].wells()[10:]] for idx in range(num_cols): rbb[idx // 3].height_inc(525 * 8) cspw1[idx // 3].height_inc(500 * 8) cspw2[idx // 3].height_inc(500 * 8) spm1[idx // 3].height_inc(500 * 8) spm2[idx // 3].height_inc(500 * 8) cspw1[idx // 6].height_inc(elution_vol * 8) ctx.comment('\nTransferring 500uL of sample to plate on MagDeck\n') flow_rate(asp=20) for src, dest in zip(init_samps, mag_samps_h): if samp_labware == 'qiagen_96_tuberack_1200ul': src_asp = src.top(-asp_ht) else: src_asp = src m300.custom_pick_up() for i in range(2): m300.aspirate(20, src.top()) m300.aspirate(180, src_asp) m300.slow_tip_withdrawal(10, src) m300.dispense_h(180, dest) m300.slow_tip_withdrawal(10, dest, to_surface=True) m300.dispense(20, dest.bottom(5)) m300.aspirate(20, src.top()) m300.aspirate(140, src_asp) m300.dispense_h(140, dest) m300.slow_tip_withdrawal(10, dest, to_surface=True) m300.dispense(20, dest.bottom(5)) m300.drop_tip() flow_rate() # Transfer 5uL RNAse + 500uL RBB buffer + 20uL Mag-Bind Beads ctx.comment('\nTransferring 5uL RNAse + 500uL RBB buffer + \ 20uL Mag-Bind Beads\n') m300.custom_pick_up() flow_rate(blow=10) for idx, col in enumerate(mag_samps): src = rbb[idx // 2] for _ in range(3): flow_rate(asp=20, disp=20) m300.mix(1, 100, src) m300.aspirate(175, src) m300.slow_tip_withdrawal(10, src, to_surface=True) flow_rate(disp=10) m300.dispense(75, dest.top(-1)) flow_rate(disp=5) m300.dispense(50, dest.top(-1)) ctx.delay(seconds=3) m300.dispense(30, dest.top(-1)) ctx.delay(seconds=3) flow_rate(disp=2) m300.dispense(20, dest.top(-1)) m300.blow_out() for x in range(2): m300.move_to(dest.top(-2)) m300.move_to(dest.top(-1)) m300.drop_tip() flow_rate() incubate_msg = f'\nIncubating at room temperature for {inc_time} minutes\ plus mixing\n' ctx.comment(incubate_msg) num_mixes = math.ceil(2 * inc_time / num_cols) for _ in range(num_mixes): for col, t_d in zip(mag_samps, all_tips[t_start:t_end]): if _ == 0: m300.custom_pick_up() if not m300.has_tip: m300.custom_pick_up(t_d) m300.aspirate(20, col.top()) m300.mix(8, 150, col) ctx.delay(seconds=1) m300.dispense(20, col.top(-2)) m300.blow_out(col.top(-2)) m300.touch_tip() if len(mag_samps) > 1: m300.drop_tip(t_d) if m300.has_tip: m300.drop_tip(t_d) t_start += num_cols t_end += num_cols mag_deck.engage() mag_msg = f'\nIncubating on Mag Deck for {mag_time} minutes\n' ctx.delay(minutes=mag_time, msg=mag_msg) # Discard Supernatant ctx.comment('\nRemoving supernatant\n') for src, t_d in zip(mag_samps_h, all_tips[t_start:t_end]): m300.custom_pick_up() flow_rate(asp=40, disp=40) remove_supernatant(1080, src) m300.drop_tip(t_d) flow_rate() # Wash with 500uL CSPW1 Buffer wash(cspw1, 'CSPW1') # Wash with 500uL CSPW2 Buffer wash(cspw2, 'CSPW2') # Wash with SPM Buffer (1) wash(spm1, 'SPM (first wash)') # Wash with SPM Buffer (2) wash(spm2, 'SPM (second wash)', fin_wash) # Air dry for 10 minutes mag_deck.engage() if add_water: h2o = rsvr_12[0]['A4'] for src in mag_samps_h: m300.custom_pick_up() m300.aspirate(100, h2o) flow_rate(asp=30, disp=30) m300.dispense(80, src) m300.aspirate(100, src) flow_rate() m300.dispense(180, liquid_waste) m300.blow_out() m300.drop_tip() ctx.home() air_dry_msg = f'\nAir drying the beads for {air_dry} minutes. \ Please add elution buffer at 65C to 12-well reservoir.\n' ctx.delay(minutes=air_dry, msg=air_dry_msg) flash_lights() if not ctx.is_simulating(): test_speaker() ctx.pause('Please check the Well Plate') mag_deck.disengage() # Add Elution Buffer ctx.comment(f'\nAdding {elution_vol}uL Elution Buffer to samples\n') for idx, col in enumerate(mag_samps_h): src = elution_buffer[idx // 6] m300.custom_pick_up() m300.aspirate(elution_vol, src) m300.slow_tip_withdrawal(10, src, to_surface=True) m300.dispense_h(elution_vol, col) m300.mix(10, 50, col) m300.slow_tip_withdrawal(10, col, to_surface=True) m300.blow_out(col.bottom(6)) for _ in range(2): m300.move_to(col.bottom(5)) m300.move_to(col.bottom(6)) m300.drop_tip() flash_lights() ctx.home() ctx.pause('Please remove samples and incubate at 65C for 5 minutes.\ When complete, replace samples and click RESUME\n') ctx.set_rail_lights(True) # Transfer elution to PCR plate for col in mag_samps_h: m300.custom_pick_up() m300.mix(10, 50, col) m300.slow_tip_withdrawal(10, col, to_surface=True) m300.blow_out(col.bottom(6)) for _ in range(2): m300.move_to(col.bottom(5)) m300.move_to(col.bottom(6)) m300.drop_tip(home_after=False) mag_deck.engage() mag_msg = '\nIncubating on Mag Deck for 3 minutes\n' ctx.delay(minutes=3, msg=mag_msg) ctx.comment(f'\nTransferring {elution_vol}uL to final PCR plate\n') t_start += num_cols if t_start >= 60: t_start -= 60 flow_rate(asp=20) for src, dest, tip in zip(mag_samps, pcr_samps, all_tips[t_start:]): w = int(str(src).split(' ')[0][1:]) radi = float(src.width)/4 if src.width is not None else \ float(src.diameter)/4 x0 = radi if w % 2 == 0 else -radi m300.custom_pick_up() m300.aspirate(elution_vol, src.bottom().move(types.Point(x=x0, y=0, z=1))) m300.dispense(elution_vol, dest) m300.drop_tip(tip) mag_deck.disengage() ctx.comment('\nProtocol complete! Please store samples at -20C or \ continue processing')
def run(protocol: protocol_api.ProtocolContext): #Lights prep gpio = protocol._hw_manager.hardware._backend._gpio_chardev #labware rack1 = protocol.load_labware( 'nest_32_tuberack_8x15ml_8x15ml_8x15ml_8x15ml', '1') # rack2 = protocol.load_labware('nest_32_tuberack_8x15ml_8x15ml_8x15ml_8x15ml','4') # rack3 = protocol.load_labware('nest_32_tuberack_8x15ml_8x15ml_8x15ml_8x15ml','7') tuberacks = [rack1] tiprack_1000_1 = protocol.load_labware('opentrons_96_tiprack_1000ul', '10') empty_tiprack_1000 = protocol.load_labware( 'opentrons_96_filtertiprack_1000ul', '11') deepwell_96 = protocol.load_labware('nest_96_wellplate_2ml_deep', '3') #Pipettes r_p = protocol.load_instrument('p1000_single_gen2', 'right', tip_racks=[tiprack_1000_1]) # Mapping of Wells sources = [ well for rack in tuberacks[::-1] for row in rack.rows()[:0:-1] for well in row[::-1] ] destination_sets = [ row[i * 5:i * 5 + 5] for i in range(3) for row in deepwell_96.rows()[::-1] ] #Pipette max_speeds r_p.flow_rate.aspirate = 400 r_p.flow_rate.dispense = 400 r_p.flow_rate.blow_out = 400 tip_droptip_count = 0 tip_pickup_count = 0 #Light turns to yellow to indicate protocol is running gpio.set_button_light(red=True, green=True, blue=False) #Pipetting Actions for source, dest_set in zip(sources, destination_sets): r_p.pick_up_tip(tiprack_1000_1.wells()[tip_pickup_count]) #dispense round 1 r_p.aspirate(550, source.top(-92)) r_p.air_gap(20) for i, dest in enumerate( dest_set): # go through the entries for each tube disp_vol = 10 + 105 if i == 0 else 105 tip_pickup_count += 1 r_p.dispense(disp_vol, dest.bottom(5)) r_p.touch_tip(v_offset=-6, speed=40) #dispense round 2 r_p.aspirate(500, source.top(-92)) r_p.air_gap(20) for i, dest in enumerate( dest_set): # go through the entries for each tube disp_vol = 10 + 105 if i == 0 else 105 r_p.dispense(disp_vol, dest.bottom(5)) r_p.touch_tip(v_offset=-6, speed=40) #Home the Z/A mount. Not the pipette r_p.air_gap( 100) # aspirate any liquid that may be leftover inside the tip protocol._implementation.get_hardware().hardware.home_z( r_p._implementation.get_mount()) current_location = protocol._implementation.get_hardware( ).hardware.gantry_position(r_p._implementation.get_mount()) final_location_xy = current_location._replace(y=300, x=300) r_p.move_to(Location(labware=None, point=final_location_xy), force_direct=True) r_p.drop_tip(empty_tiprack_1000.wells()[tip_droptip_count]) tip_droptip_count += 1 gpio.set_button_light(red=False, green=True, blue=False) protocol.pause() protocol.home() gpio.set_button_light(red=False, green=False, blue=True)
def run(protocol: protocol_api.ProtocolContext): # labware: Labware used in the protocol is loaded, format: variable = labware.load('labware name', 'Position on deck') plate_dil_1 = protocol.load_labware('nunc_96_ubottom', '4') plate_vt_1 = protocol.load_labware('valitacell_96_wellplate_150ul', '1') plate_dil_2 = protocol.load_labware('nunc_96_ubottom', '5') plate_vt_2 = protocol.load_labware('valitacell_96_wellplate_150ul', '2') plate_dil_3 = protocol.load_labware('nunc_96_ubottom', '6') plate_vt_3 = protocol.load_labware('valitacell_96_wellplate_150ul', '3') tip200_1 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '7') tip200_2 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '10') tip200_3 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '11') tip200_4 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '9') trough = protocol.load_labware('axygen_12_reservior_22ml', '8') # pipettes and settings p300m = protocol.load_instrument( 'p300_multi', mount='right', tip_racks=[tip200_1, tip200_2, tip200_3, tip200_4]) p300m.flow_rate.aspirate = 100 p300m.flow_rate.dispense = 200 p300m.maximum_volume = 200 p300m.minimum_volume = 15 def VT_SC_Plate(trough_well, plate_dil, plate_vt): #FDefines running of VT_SC_Plate as a function #Step1: Fill Dilution media to cols 2-12 p300m.pick_up_tip() for i in range(11): p300m.aspirate(100, trough.wells()[trough_well]) p300m.move_to(trough.wells()[trough_well].top(-20)) protocol.delay(seconds=1) p300m.dispense(100, plate_dil.wells()[8 * (i + 1)]) protocol.delay(seconds=1) p300m.blow_out() p300m.drop_tip() #Step2: Add 222ul Standard to dil_plate Col-1 p300m.pick_up_tip() p300m.mix(10, 190, trough.wells()[0]) protocol.delay(seconds=1) p300m.blow_out(trough.wells()[0].top()) p300m.transfer(100, trough.wells()[0], plate_dil.wells()[0], new_tip='never') protocol.delay(seconds=1) p300m.blow_out() p300m.transfer(122, trough.wells()[0], plate_dil.wells()[0], new_tip='never') protocol.delay(seconds=1) p300m.blow_out() p300m.drop_tip() #Step3: Dilute at ration of 0.6 accross cols 1-11 p300m.pick_up_tip() for i in range(10): p300m.transfer(122, plate_dil.wells()[8 * i], plate_dil.wells()[8 * (i + 1)], mix_after=(6, 180), new_tip='never') protocol.delay(seconds=1) p300m.blow_out(plate_dil.wells()[8 * (i + 1)].top()) p300m.drop_tip() protocol.home() #Step3: Add 60ul VT-Buff to VT plate p300m.well_bottom_clearance.dispense = 7 p300m.pick_up_tip() for i in range(12): p300m.aspirate(60, trough.wells()[trough_well]) p300m.move_to(trough.wells()[trough_well].top(-20)) protocol.delay(seconds=1) p300m.dispense(60, plate_vt.wells()[8 * i]) protocol.delay(seconds=1) p300m.blow_out(plate_vt.wells()[8 * i].top()) p300m.drop_tip() p300m.well_bottom_clearance.dispense = 1 protocol.home() #Step 4: Add 60ul Dil.Sample to VT plate + mix. for i in range(12): p300m.pick_up_tip() p300m.mix(5, 60, plate_dil.wells()[8 * i]) p300m.blow_out(plate_dil.wells()[8 * i].top()) p300m.transfer(60, plate_dil.wells()[8 * i], plate_vt.wells()[8 * i], mix_after=(6, 80), new_tip='never') protocol.delay(seconds=1) p300m.blow_out(plate_vt.wells()[8 * i].top()) p300m.drop_tip() VT_SC_Plate(2, plate_dil_1, plate_vt_1) #Call function to runn VT_SC_Plate protocol.home() VT_SC_Plate(3, plate_dil_2, plate_vt_2) protocol.home() VT_SC_Plate(4, plate_dil_3, plate_vt_3)
def run(protocol: protocol_api.ProtocolContext): #if not protocol.is_simulating(): protocol.home() # define Labware tiprack = protocol.load_labware('opentrons_96_tiprack_300ul', 10) pipette = protocol.load_instrument('p300_single_gen2', mount='left', tip_racks=[tiprack]) # define Labware colorA_reservoir = protocol.load_labware('opentrons_96_tiprack_300ul', 9) colorBC_reservoir = protocol.load_labware('opentrons_96_tiprack_300ul', 6) mixing_plate = protocol.load_labware('corning_96_wellplate_360ul_flat', 4) dispense_plate = protocol.load_labware('corning_96_wellplate_360ul_flat', 1) water_well = protocol.load_labware('corning_96_wellplate_360ul_flat', 8) cleaning_well = protocol.load_labware('corning_96_wellplate_360ul_flat', 7) # define Source Color Positions colorA_position = colorA_reservoir['F8'] colorA_tip_position = tiprack['B1'] colorB_position = colorBC_reservoir['A2'] colorB_tip_position = tiprack['B2'] colorC_position = colorBC_reservoir['E7'] colorC_tip_position = tiprack['B3'] # the example code below would go here, inside the run function df = pd.read_csv('/data/user_storage/color_test.csv') print(df.columns.values) colorA_amt = df.values[:, 0] print(colorA_amt) colorB_amt = df.values[:, 1] print(colorB_amt) colorC_amt = df.values[:, 2] print(colorC_amt) dispense_position = df.values[:, 3] print(dispense_position) # pipette.pick_up_tip(tiprack['C1']) # for i in range(len(dispense_position)): # pipette.aspirate(270, water_well['C5']) # pipette.dispense(270, mixing_plate[dispense_position[i]].top(5), rate=0.5) # pipette.drop_tip(tiprack['C1']) for amt, src_pos, tip_pos in zip( [colorA_amt, colorB_amt, colorC_amt], [colorA_position, colorB_position, colorC_position], [colorA_tip_position, colorB_tip_position, colorC_tip_position]): pipette.pick_up_tip(tip_pos) amt_sum = sum(amt) #pipette.aspirate(amt_sum, src_pos.top(-35), rate=0.5) for i in range(len(dispense_position)): pipette.aspirate(55, water_well['C5']) pipette.aspirate(amt[i], src_pos.top(-35), rate=0.5) pipette.air_gap(10) pipette.dispense(amt[i] + 55 + 10, mixing_plate[dispense_position[i]]) pipette.blow_out(mixing_plate[dispense_position[i]].top(10)) pipette.mix(1, 300, cleaning_well['C5']) pipette.blow_out() #pipette.blow_out(src_pos.top(30)) pipette.drop_tip(tip_pos) pipette.pick_up_tip(tiprack['C1']) for i in range(len(dispense_position)): pipette.mix(1, 300, mixing_plate[dispense_position[i]]) pipette.aspirate(300, mixing_plate[dispense_position[i]]) pipette.dispense(300, mixing_plate[dispense_position[i]]) pipette.blow_out(mixing_plate[dispense_position[i]].top(10)) pipette.aspirate(80, mixing_plate[dispense_position[i]]) pipette.air_gap(5) pipette.dispense(120, dispense_plate[dispense_position[i]], rate=0.5) pipette.aspirate(50, dispense_plate[dispense_position[i]]) pipette.dispense(120, dispense_plate[dispense_position[i]], rate=0.5) pipette.mix(1, 300, cleaning_well['C5']) pipette.blow_out() pipette.drop_tip(tiprack['C1'])
def run(protocol: protocol_api.ProtocolContext): # labware plate_pel = protocol.load_labware('nunc_96_ubottom', '9') plate_dil = protocol.load_labware('nunc_96_ubottom', '6') plate_ip = protocol.load_labware('iprasense_48_slide', '3') trough = protocol.load_labware('axygen_12_reservior_22ml', '8') tip300_1 = protocol.load_labware('opentrons_96_tiprack_300ul', '7') tip300_2 = protocol.load_labware('opentrons_96_tiprack_300ul', '10') tip200_1 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '11') plate24_1A = protocol.load_labware('nunc_24_pseudo_a', '1') plate24_2B = protocol.load_labware('nunc_24_pseudo_b', '2') plate24_3A = protocol.load_labware('nunc_24_pseudo_a', '4') plate24_4B = protocol.load_labware('nunc_24_pseudo_b', '5') # pipettes (& settings if different to defaults) p300m = protocol.load_instrument('p300_multi', mount='right', tip_racks=[tip300_1, tip300_2]) p50m = protocol.load_instrument('p50_multi', mount='left', tip_racks=[tip200_1]) #Protocol Start!!!: #Step 1: Fill Dilution plate with 30ul media per well p50m.pick_up_tip() for i in range(12): p50m.aspirate(35, trough.wells('A1')) p50m.dispense(30, plate_dil.columns()[i]) protocol.delay(seconds=0.5) p50m.blow_out(trough.wells('A1')) p50m.return_tip() p50m.reset_tipracks() #Step 2: Mix and transfer 300ul 24wp_culture 1 into supernatant plate, transfer 30ul into dilution plate List_plate = [(0, plate24_1A, tip300_1, 0, 0), (3, plate24_1A, tip300_1, 1, 0), (0, plate24_2B, tip300_2, 0, 0), (3, plate24_2B, tip300_2, 1, 0), (0, plate24_3A, tip300_1, 2, 6), (3, plate24_3A, tip300_1, 3, 6), (0, plate24_4B, tip300_2, 2, 6), (3, plate24_4B, tip300_2, 3, 6)] #In list arguments 1-4 are: 24SWP number, 24SWP name, tiprack, column offset adjuster p300m.well_bottom_clearance.aspirate = 1.0 p300m.well_bottom_clearance.dispense = 2.5 for (j, plate24, tip, k, l), i in product(List_plate, range(3)): #'produt' multiplies two variables into a matrix, same as doubble loop. isource = plate24.wells()[4*(i+j)] #isource and isource 2 are the same location, defined by wells or columns idest_intermediate = plate_dil.wells()[8*(i+j+l)] # save the source and destinations to variables based on 'i', 'j' 'k', 'l' idest_wb = plate_pel.wells()[8*k] well_edge_x1 = isource.bottom().move(types.Point(x=5.5, y=0, z=1.5)) # define 4 edges of the well for mixing well_edge_x2 = isource.bottom().move(types.Point(x=-5.5, y=0, z=1.5)) well_edge_y1 = isource.bottom().move(types.Point(x=0, y=3.75, z=1.5)) well_edge_y2 = isource.bottom().move(types.Point(x=0, y=-3.75, z=1.5)) p300m.pick_up_tip(tip['A' + str(i+k*3+1)]) #Chooses tip to pick up, as cant slect only rack p300m.mix(1, 300, isource, rate=2.0) for r in range(2): # 2 loops of mixing each well edge p300m.aspirate(290, well_edge_x1, rate=2.0) p300m.dispense(300, well_edge_x2, rate=3.0) p300m.aspirate(290, well_edge_y1, rate=2.0) p300m.dispense(300, well_edge_y2, rate=3.0) p300m.aspirate(290, well_edge_x2, rate=2.0) p300m.dispense(300, well_edge_x1, rate=3.0) p300m.aspirate(290, well_edge_y2, rate=2.0) p300m.dispense(300, well_edge_y1, rate=3.0) p300m.mix(1, 300, isource, rate=2.0) protocol.delay(seconds=1.0) p300m.blow_out(isource.top()) p300m.transfer(30, isource, idest_intermediate, mix_after=(2, 45), new_tip='never') protocol.delay(seconds=1) p300m.blow_out(idest_intermediate.top()) p300m.mix(1, 300, isource, rate=2.0) protocol.delay(seconds=1.0) p300m.blow_out(isource.top()) p300m.transfer(75, isource, idest_wb, new_tip='never') protocol.delay(seconds=1) p300m.blow_out(idest_wb.top()) p300m.drop_tip() p300m.well_bottom_clearance.aspirate = 1.0 p300m.well_bottom_clearance.dispense = 1.0 #Intervention 1: protocol.home() protocol.pause("ATTENTION: Replace 'tiprack-300-1' in 'slot 7' as follows: Cols 1+2 odd rows, 3+4 even rows, rest of rack full. Replace 'tiprack-300-2' in slot 2 with a full rack. Insert IP slide into slot 3 for odd wells. Add feed to trough.A3") p300m.reset_tipracks() #Step 4: Feed Plates def Feed_Plate(plate_name): p300m.pick_up_tip() for i in range(6): p300m.aspirate(10, trough.wells()[2].top()) p300m.aspirate(140, trough.wells()[2], rate=0.7) p300m.move_to(trough.wells()[2].top(-20)) protocol.delay(seconds=1.0) p300m.dispense(150, plate_name.wells()[4*i]) protocol.delay(seconds=1.0) p300m.blow_out(plate_name.wells()[4*i].top()) p300m.drop_tip() p300m.well_bottom_clearance.aspirate = 1.0 p300m.well_bottom_clearance.dispense = 15.0 Feed_Plate(plate24_1A) Feed_Plate(plate24_3A) Feed_Plate(plate24_2B) Feed_Plate(plate24_4B) p300m.well_bottom_clearance.aspirate = 1.0 p300m.well_bottom_clearance.dispense = 1.0 protocol.home() #Function_create: Load into IP_slide, x indicates column miltipier on 96well plate, value = 0 or 1 def IP_slide_load(x): for i in range(6): p50m.pick_up_tip() p50m.mix(8, 50, plate_dil.wells()[8*(2*i+x)], rate=4.0) protocol.delay(seconds=1) p50m.blow_out(plate_dil.wells()[8*(2*i+x)].top()) p50m.aspirate(10, plate_dil.wells()[8*(2*i+x)].top()) p50m.flow_rate.aspirate = 25 p50m.flow_rate.dispense = 2.5 p50m.aspirate(9.0, plate_dil.wells()[8*(2*i+x)]) p50m.dispense(30, plate_ip.wells()[8*(2*i+x)]) protocol.delay(seconds=1.0) p50m.flow_rate.aspirate = 50 p50m.flow_rate.dispense = 100 p50m.drop_tip() #Step 3: Load into IP_slide 1, swaps slides, load IP_slide 2 IP_slide_load(0) protocol.pause() protocol.comment("ATTENTION: Replace loaded iprasense slide with empty slide. Once complete click resume. Once protocol resumes, run IP_slide one on Iprasense") IP_slide_load(1) protocol.home() protocol.pause() protocol.comment("ATTENTION: Replace dilution plate with empty nunc_96U to take samples for qPCR") #Step 4: Seperate 60ul from WB samples for qPCR for i in range(4): p300m.pick_up_tip() p300m.mix(4, 200, plate_pel.wells()[8*i]) protocol.delay(seconds=1.0) p300m.blow_out(plate_pel.wells()[8*i].top()) p300m.transfer(60, plate_pel.wells()[8*i], plate_dil.wells()[8*i], new_tip='never') protocol.delay(seconds=1) p300m.blow_out(plate_dil.wells()[8*i].top()) p300m.drop_tip() protocol.home() protocol.pause() protocol.comment("ATTENTION: Remove western blot at qPCR plates, pellet cells and return. Add 8ml RNA_Later to trough A5. Add 8ml ice cold PBS to trough A6") #Step 5: Add PBC to western blot samples and mix, Add RNA later to qPCR samples p300m.well_bottom_clearance.aspirate = 1.0 p300m.well_bottom_clearance.dispense = 9.0 p300m.flow_rate.aspirate = 150 p300m.flow_rate.dispense = 50 p300m.pick_up_tip() for i in range(4): p300m.transfer(100, trough.wells('A6'), plate_pel.wells()[8*i], new_tip='never') protocol.delay(seconds=1) p300m.blow_out(plate_pel.wells()[8*i].top()) p300m.drop_tip() p300m.pick_up_tip() for i in range(4): p300m.transfer(100, trough.wells('A5'), plate_dil.wells()[8*i], new_tip='never') protocol.delay(seconds=1) p300m.blow_out(plate_dil.wells()[8*i].top()) p300m.drop_tip() #END SCRIPT!!!
def run(protocol: protocol_api.ProtocolContext): # labware plate_stock = protocol.load_labware('cornering_96_wellplate_500ul', '9') plate_dna = protocol.load_labware('nunc_96_ubottom', '6') plate_nuc = protocol.load_labware('lonza_96_electroporation', '3') trough = protocol.load_labware('axygen_12_reservior_22ml', '8') tip200_1 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '7') tip200_2 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '10') tip200_3 = protocol.load_labware('opentrons_96_filtertiprack_200ul', '11') plate24_1A = protocol.load_labware('nunc_24_pseudo_a', '1') plate24_2B = protocol.load_labware('nunc_24_pseudo_b', '2') plate24_3A = protocol.load_labware('nunc_24_pseudo_a', '4') plate24_4B = protocol.load_labware('nunc_24_pseudo_b', '5') # pipettes (& settings if different to defaults) p300m = protocol.load_instrument('p300_multi', mount='right', tip_racks=[tip200_1, tip200_2]) p300m.flow_rate.aspirate = 100 p300m.flow_rate.dispense = 200 p300m.maximum_volume = 200 p300m.minimum_volume = 15 p50m = protocol.load_instrument('p50_multi', mount='left', tip_racks=[tip200_1, tip200_3]) #Step 1: Distrubute Nuc Solution to DNA plate p50m.pick_up_tip() for i in range(4): p50m.aspirate(45, trough.wells()[0]) p50m.dispense(38, plate_dna.wells()[8 * i]) protocol.delay(seconds=1) p50m.blow_out(trough.wells()[0].top()) p50m.drop_tip() #Step 2: Distrubute DNA/RNA mix to DNA Plate for i in range(4): p50m.pick_up_tip() p50m.mix(3, 50, plate_stock.wells()[8 * i]) protocol.delay(seconds=1) p50m.blow_out(plate_stock.wells()[8 * i].top()) p50m.transfer(7, plate_stock.columns()[i], plate_dna.columns()[i], mix_after=(1, 40), new_tip='never') protocol.delay(seconds=1) p50m.blow_out(plate_dna.wells()[8 * i].top()) p50m.drop_tip() # Intervention 1: Insert pause to comfirm cells ready in trough column 2 protocol.pause() protocol.comment( 'ATTENTION: Ensure below criteris are met prior to resuming protocol. Resuspended cells added to trough, position 8, column 2. Once complete click resume' ) #Step 3: Mix cells + transfer to DNA plate p300m.pick_up_tip() p300m.well_bottom_clearance.aspirate = 2 p300m.well_bottom_clearance.dispense = 10 for j in range(15): p300m.aspirate(190, trough.wells_by_name()['A2'], rate=3.0) p300m.dispense(190, trough.wells_by_name()['A2'], rate=3.0) protocol.delay(seconds=1) p300m.blow_out(trough.wells_by_name()['A2'].top()) p300m.touch_tip() p300m.drop_tip() p300m.flow_rate.aspirate = 150 p300m.flow_rate.dispense = 200 p300m.well_bottom_clearance.aspirate = 1 p300m.well_bottom_clearance.dispense = 1 for i in range(4): p50m.pick_up_tip() p50m.well_bottom_clearance.aspirate = 2 p50m.well_bottom_clearance.dispense = 10 for j in range(5): p50m.aspirate(50, trough.wells_by_name()['A2'], rate=5.0) p50m.dispense(50, trough.wells_by_name()['A2'], rate=5.0) p50m.well_bottom_clearance.aspirate = 1 p50m.well_bottom_clearance.dispense = 1 protocol.delay(seconds=1) p50m.blow_out(trough.wells_by_name()['A2'].top()) p50m.transfer(45, trough.wells('A2'), plate_dna.wells()[8 * i], new_tip='never', mix_after=(2, 50), touch_tip=True) protocol.delay(seconds=1) p50m.blow_out(plate_dna.wells()[8 * i]) p50m.drop_tip() #Step 4: Distribute cells to nucleofection plate for i in range(4): # loop for 4 columns on DNA setup plate p50m.pick_up_tip() p50m.mix(10, 50, plate_dna.wells()[8 * i], rate=5.0) protocol.delay(seconds=1) p50m.blow_out(plate_dna.wells()[8 * i].top()) for j in range(3): # Subloop for 3 replicates p50m.mix(4, (50 - 10 * j), plate_dna.wells()[8 * i], rate=5.0) p50m.blow_out(plate_dna.wells()[8 * i].top()) protocol.delay(seconds=1.0) p50m.well_bottom_clearance.dispense = 2.5 p50m.aspirate(25, plate_dna.wells()[8 * i]) p50m.dispense(20, plate_nuc.wells()[8 * (3 * i + j)]) protocol.delay(seconds=1.5) p50m.well_bottom_clearance.dispense = 1 p50m.blow_out(plate_dna.wells()[8 * i].top()) p50m.drop_tip() #Intervention 3: Insert pause for electroporation protocol.pause() protocol.comment( 'ATTENTION: Ensure below criteris are met prior to resuming protocol. Perform electroporation and return nucleofection plate to position 3. During electroporation, add pregassed media to trough, position 8, col 3. Once complete click resume.' ) #Step 5: Add 80ul media to all wells p300m.well_bottom_clearance.dispense = 12 p300m.pick_up_tip() for i in range(12): p300m.aspirate(80, trough.wells()[2]) p300m.dispense(80, plate_nuc.wells()[8 * i]) protocol.delay(seconds=1) p300m.blow_out(plate_nuc.wells()[8 * i].top()) p300m.drop_tip() p300m.well_bottom_clearance.dispense = 1 protocol.home() #Intervention 4: Reset tipracks for seeding protocol.pause() protocol.comment( 'ATTENTION: Ensure below criteria are met prior to resuming protocol. Please reset tipracks as detailed below ready for seeding of cells. tip200_1 (slot 7) Only odd rows or tips. tip200_2 (slot 10) requires only even rows of tips. Extended details in ReadME section of script. Once complete click resume.' ) p300m.reset_tipracks() # Reset tipracks #Step 8: Seed into 24SWPs List_plate = [(0, plate24_1A, tip200_1), (0, plate24_2B, tip200_2), (6, plate24_3A, tip200_1), (6, plate24_4B, tip200_2)] #In list arguments 1-4 are: 24SWP number, 24SWP name, tiprack, column offset adjuster for (j, plate24, tip), i in product( List_plate, range(6) ): #'produt' multiplies two variables into a matrix, same as doubble loop. isource = plate24.wells()[4 * ( i )] #isource and isource 2 are the same location, defined by wells or columns idest_nuc = plate_nuc.wells()[8 * (i + j)] p300m.well_bottom_clearance.aspirate = 2.5 p300m.well_bottom_clearance.dispense = 2.5 p300m.pick_up_tip(tip['A' + str(i + j + 1)]) for m in range(10): p300m.aspirate(70, idest_nuc, rate=2.0) p300m.dispense(70, idest_nuc, rate=2.0) protocol.delay(seconds=1.0) p300m.blow_out(plate_nuc.wells()[8 * (j + i)].top()) protocol.delay(seconds=1.0) p300m.aspirate(70, idest_nuc, rate=0.5) p300m.well_bottom_clearance.aspirate = 1.0 p300m.well_bottom_clearance.dispense = 1.0 p300m.dispense(70, isource) p300m.mix(2, 190, isource) protocol.delay(seconds=1.0) p300m.blow_out(isource.top()) p300m.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): global MM_TYPE ctx.comment( "Protocollo Preparazione Mastermix Bioer per {} campioni.".format( NUM_SAMPLES)) # check source (elution) labware type tips20 = [ ctx.load_labware('opentrons_96_filtertiprack_20ul', slot) for slot in ['2', '3', '5', '6', '8', '9', '11'] ] tips300 = [ctx.load_labware('opentrons_96_filtertiprack_200ul', '10')] pcr_plate = ctx.load_labware( 'opentrons_96_aluminumblock_biorad_wellplate_200ul', '1', 'PCR plate') mm_strips = ctx.load_labware( 'opentrons_96_aluminumblock_generic_pcr_strip_200ul', '4', 'mastermix strips') tube_block = ctx.load_labware( 'opentrons_24_aluminumblock_nest_2ml_snapcap', '7', '2ml snap 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) sample_dests = pcr_plate.rows()[0][NUM_COLONNA:num_cols + NUM_COLONNA] control_dest1 = pcr_plate.wells()[88] #controlli in posizione A12 e H12 control_dest2 = pcr_plate.wells()[95] 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]: # print('Replace ' + str(pip.max_volume) + 'µl tipracks before resuming.') 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 """ # setup tube mastermix ctx.comment("Mastermix per sample: {}".format(MM_PER_SAMPLE)) ctx.comment("Num samples: {}".format(NUM_SAMPLES)) ctx.comment("Liquid headroom: {}".format(liquid_headroom)) ctx.comment("Tube capacity: {}".format(mm_tube_capacity)) num_mm_tubes = math.ceil( ((MM_PER_SAMPLE * NUM_SAMPLES) + liquid_headroom) / mm_tube_capacity) samples_per_mm_tube = [] for i in range(num_mm_tubes): remaining_samples = NUM_SAMPLES - sum(samples_per_mm_tube) samples_per_mm_tube.append( min(8 * math.ceil(remaining_samples / (8 * (num_mm_tubes - i))), remaining_samples)) NUM_MM = NUM_SAMPLES + 4 mm_per_tube = MM_PER_SAMPLE * NUM_MM * 1.1 mm_tube = tube_block.wells()[:num_mm_tubes] ctx.comment( "Mastermix: caricare {} tube con almeno {}ul di mastermix".format( num_mm_tubes, mm_per_tube)) # setup strips mastermix mm_strip = mm_strips.columns()[:num_mm_tubes] mm_indices = list( chain.from_iterable( repeat(i, ns) for i, ns in enumerate(samples_per_mm_tube))) """START REPEATED SECTION""" p300.flow_rate.aspirate = MM_RATE_ASPIRATE p300.flow_rate.dispense = MM_RATE_DISPENSE m20.flow_rate.aspirate = MM_RATE_ASPIRATE m20.flow_rate.dispense = MM_RATE_DISPENSE for i in range(NUM_SEDUTE): ctx.comment("Seduta {}/{}".format(i + 1, NUM_SEDUTE)) # transfer mastermix to strips pick_up(p300) for mt, ms, ns in zip(mm_tube, mm_strip, samples_per_mm_tube): for strip_i, strip_w in enumerate(ms): p300.transfer((ns // 8 + (1 if strip_i < ns % 8 else 0)) * MM_PER_SAMPLE * 1.1, mt.bottom(0.7), strip_w, new_tip='never') p300.transfer(MM_PER_SAMPLE, mm_tube[1], control_dest1.bottom(2), new_tip='never') p300.transfer(MM_PER_SAMPLE, mm_tube[1], control_dest2.bottom(2), new_tip='never') p300.drop_tip() # transfer mastermix to plate for m_idx, s in zip(mm_indices[::8], sample_dests): pick_up(m20) m20.transfer(MM_PER_SAMPLE, mm_strip[m_idx][0].bottom(3.5), s.bottom(3.5), new_tip='never') m20.drop_tip() if i < NUM_SEDUTE - 1: blight = BlinkingLight(ctx=ctx) blight.start() ctx.home() ctx.pause( "Togliere la pcr plate e preparare l'occorrente per la prossima seduta." ) blight.stop() else: blight = BlinkingLight(ctx=ctx) blight.start() ctx.home() ctx.pause("Togliere la pcr plate.") blight.stop() """END REPEATED SECTION""" # 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): 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(protocol: protocol_api.ProtocolContext): """Function to transfer 100ul from 1 well to another.""" #loading labware (from opentrons labware library) plate = protocol.load_labware('biorad_96_wellplate_200ul_pcr', 1, '96plate') tiprack_1 = protocol.load_labware('opentrons_96_tiprack_300ul', 2, '300tips') #load pipette p300 = protocol.load_instrument('p300_single_gen2', 'right', tip_racks=[tiprack_1]) #run protocol while protocol.door_closed: if not protocol.door_closed: protocol.set_rail_lights(True) protocol.home() protocol.pause('Press resume when you are ready :)!') protocol.set_rail_lights(False) #transfer 100ul from plate well A1 to plate well B1 with default settings p300.pick_up_tip() #aspirate & dispense at default flow rate of 92.86 ul/s p300.aspirate(100, plate['A1']) p300.dispense(100, plate['B1']) p300.blow_out() #blow out in current location p300.drop_tip() #automatically in trash #all following pipetting steps with changed default flow rates #aspirate at 150 ul/s p300.flow_rate.aspirate = 150 #dispense at 300 ul/s p300.flow_rate.dispense = 300 #blow out at 200 ul/s p300.flow_rate.blow_out = 200 p300.pick_up_tip() p300.aspirate(100, plate['C1']) p300.dispense(100, plate['D1']) p300.blow_out(plate['D1']) #blow out in destination well p300.drop_tip() #automatically in trash #all following pipetting steps aspirate & dispense other height (in mm) #aspirate 2 mm from bottom p300.well_bottom_clearance.aspirate = 3 #dispense 10 mm from bottom p300.well_bottom_clearance.dispense = 10 p300.pick_up_tip() p300.aspirate(100, plate['E1']) p300.dispense(100, plate['F1']) p300.blow_out(plate['F1']) #blow out in destination well p300.drop_tip() #automatically in trash #display a comment in the opentrons app protocol.comment('Hello you!') #all following pipetting steps with per-axis speed limits #limit x-axis to 50 mm/s protocol.max_speeds['X'] = 50 #limit y-axis to 50 mm/s protocol.max_speeds['Y'] = 50 #limit a-axis to 50 mm/s protocol.max_speeds['A'] = 50 p300.pick_up_tip() p300.aspirate(100, plate['G1']) p300.dispense(100, plate['H1']) p300.blow_out(plate['H1']) #blow out in destination well p300.drop_tip() #automatically in trash #all following pipetting steps with per-axis speed limits to default #reset x-axis speed limit - delete method del protocol.max_speeds['X'] #reset y-axis speed limit - none method protocol.max_speeds['Y'] = None #reset a-axis speed limit - none method protocol.max_speeds['A'] = None p300.pick_up_tip() p300.aspirate(100, plate['B1']) p300.dispense(100, plate['A1']) p300.blow_out(plate['A1']) #blow out in destination well p300.drop_tip() #automatically in trash break
def run(protocol: protocol_api.ProtocolContext): if debug: print(protocol) tiprack_300 = protocol.load_labware('opentrons_96_tiprack_300ul', labwarePositions.tiprack_300, "tiprack 300ul") if debug: print(tiprack_300) pipette_300 = protocol.load_instrument('p300_single', 'right', tip_racks=[tiprack_300]) pipette_300.flow_rate.dispense = default_flow_rate pipette_300.flow_rate.aspirate = default_flow_rate pipette_300.starting_tip = tiprack_300.well( tiprack_starting_pos['tiprack_300']) if debug: print(pipette_300) black_96 = protocol.load_labware(type_of_96well_plate, labwarePositions.antibodies_plate, type_of_96well_plate) trough12 = protocol.load_labware('parhelia_12trough', labwarePositions.buffers_reservoir, '12-trough buffers reservoir') if (not retreaval): par2 = protocol.load_labware('par2s_9slides_blue_v2', labwarePositions.par2, 'par2s_9slides_blue_v2') if retreaval: temp_mod = protocol.load_module('temperature module', labwarePositions.heatmodule) par2 = temp_mod.load_labware('par2s_9slides_blue_v2') if debug: print(par2) buffer_wells = trough12.wells_by_name() buffers = Object() buffers.retreaval = buffer_wells['A1'] buffers.TBS_wash = buffer_wells['A2'] buffers.water = buffer_wells['A3'] buffers.storage = buffer_wells['A4'] buffers.eth_70perc_ = buffer_wells['A5'] buffers.eth_80perc = buffer_wells['A6'] buffers.eth_95perc = buffer_wells['A7'] buffers.eth_100perc = buffer_wells['A8'] buffers.hematoxylin = buffer_wells['A12'] preblock_wells = black_96.rows()[0] antibody_wells = black_96.rows()[1] enzymeblock_wells = black_96.rows()[2] hrpsecondaryab_wells = black_96.rows()[3] substrate_wells = black_96.rows()[4] DAB_wells = black_96.rows()[5] sample_chambers = [] for well in wellslist: sample_chambers.append(par2.wells_by_name()[well]) if debug: print(sample_chambers) #################PROTOCOL#################### protocol.home() if retreaval: washSamples(pipette_300, buffers.retreaval, buffers.retreaval, 0, 1, extra_bottom_gap) washSamples(pipette_300, buffers.retreaval, sample_chambers, wash_volume, 2, extra_bottom_gap) temp_mod.set_temperature(95) print("retreaval") protocol.delay(minutes=15) washSamples(pipette_300, buffers.retreaval, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=15) washSamples(pipette_300, buffers.retreaval, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=15) print("cooling down to RT") temp_mod.set_temperature(25) protocol.delay(minutes=20) # WASHING SAMPLES WITH TBS print("washing in TBS") washSamples(pipette_300, buffers.TBS_wash, buffers.TBS_wash, 0, 1, extra_bottom_gap) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 2, extra_bottom_gap) # Preblocking print("preblocking") print(len(wellslist)) for i in range(len(wellslist)): print(i) washSamples(pipette_300, preblock_wells[i], sample_chambers[i], wash_volume, 1, extra_bottom_gap) print("preblocking incubation: 15 min") protocol.delay(minutes=15) # APPLYING ANTIBODY COCKTAILS TO SAMPLES print("applying antibodies") for i in range(len(wellslist)): print(i) washSamples(pipette_300, antibody_wells[i], sample_chambers[i], wash_volume, 1, extra_bottom_gap) # INCUBATE FOR DESIRED TIME print("staining incubation: " + str(primary_ab_incubation_time_minutes) + "min") protocol.delay(minutes=primary_ab_incubation_time_minutes) # WASHING SAMPLES WITH TBS # three individual repeats below is because they need particular incubation time between them print("washing with TBS") for i in range(5): washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=3) # APPLYING enzyme blocking print("applying enzyme blocking") for i in range(len(wellslist)): washSamples(pipette_300, enzymeblock_wells[i], sample_chambers[i], wash_volume, 1, extra_bottom_gap) # INCUBATE 10 MIN print("hrp blocking incubation: 10min") protocol.delay(minutes=10) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 3, extra_bottom_gap) # APPLYING HRP SECONDARY ANTIBODY COCKTAILS TO SAMPLES print("applying hrpsecondaryab") for i in range(len(wellslist)): washSamples(pipette_300, hrpsecondaryab_wells[i], sample_chambers[i], wash_volume, 1, extra_bottom_gap) # INCUBATE FOR DESIRED TIME print("staining incubation: " + str(secondary_ab_incubation_time_minutes) + "min") protocol.delay(minutes=secondary_ab_incubation_time_minutes) # three individual repeats below is because they need particular incubation time between them print("washing with TBS") for i in range(3): washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, 1, extra_bottom_gap) protocol.delay(minutes=3) # DILUTING AND APPLYING THE DAB for i in range(len(wellslist)): dilute_and_apply_fixative(pipette_300, DAB_wells[i], substrate_wells[i], sample_chambers[i], 200) print("developing substrate") protocol.delay(minutes=10) washSamples(pipette_300, buffers.water, buffers.water, 0, 1, extra_bottom_gap) washSamples(pipette_300, buffers.water, sample_chambers, wash_volume, 5, extra_bottom_gap) # STORAGE, washing samples every hour washSamples(pipette_300, buffers.storage, buffers.storage, 0, 1, extra_bottom_gap) for i in range(48): washSamples(pipette_300, buffers.storage, sample_chambers, wash_volume / 2, 1, extra_bottom_gap, keep_tip=True) protocol.delay(minutes=60) print("total dispensed volume: ", str(stats.volume))
def run(protocol: protocol_api.ProtocolContext): if debug: print(protocol) tiprack_300 = protocol.load_labware('opentrons_96_tiprack_300ul', labwarePositions.tiprack_300, "tiprack 300ul") if debug: print(tiprack_300) pipette_300 = protocol.load_instrument('p300_single', 'right', tip_racks = [tiprack_300]) pipette_300.flow_rate.dispense = default_flow_rate pipette_300.flow_rate.aspirate = default_flow_rate pipette_300.starting_tip = tiprack_300.well(tiprack_starting_pos['tiprack_300']) if debug: print(pipette_300) par2_slides = protocol.load_labware('par2s_9slides_blue_v2', labwarePositions.par2, 'par2s_9slides_blue_v2') # trough12_def = json.loads(AXYGEN_12well_plate_DEF_JSON) # trough12 = protocol.load_labware_from_definition(trough12_def, labwarePositions.buffers_reservoir, '12-trough buffers reservoir') # custom_96_def = json.loads(CUSTOM_96well_plate_DEF_JSON) custom_96 = protocol.load_labware('parhelia_black_96', labwarePositions.antibodies_plate, 'parhelia_black_96') trough12 = protocol.load_labware('parhelia_12trough', labwarePositions.buffers_reservoir, 'parhelia_12trough') temp_mod = protocol.load_module('temperature module', '8') par2_on_heat_module=temp_mod.load_labware('par2s_9slides_blue_v2') if debug: print(par2) buffer_wells = trough12.wells_by_name() buffers = Object() buffers.retreaval = buffer_wells['A1'] buffers.TBS_wash = buffer_wells['A2'] buffers.water = buffer_wells['A3'] buffers.storage = buffer_wells['A4'] preblock_wells_cycle1 = custom_96.rows()[0] antibody_wells_cycle1 = custom_96.rows()[1] opal_polymer_cycle1 = custom_96.rows()[2] opal_fluorophore1 = custom_96.rows()[3] preblock_wells_cycle2 = custom_96.rows()[4] antibody_wells_cycle2 = custom_96.rows()[5] opal_polymer_cycle2 = custom_96.rows()[6] opal_fluorophore2 = custom_96.rows()[7] sample_chambers = [] for well in wellslist: sample_chambers.append(par2_on_heat_module.wells_by_name()[well]) if debug: print(sample_chambers) #################PROTOCOL#################### protocol.home() ###-------------------- FIRST ROUND #WASHING SAMPLES WITH TBS print("washing in TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) # protocol.delay(minutes=5) print("preblocking") for i in range (len(wellslist)): washSamples(pipette_300, preblock_wells_cycle1[i], sample_chambers[i], wash_volume) #INCUBATE 15 MIN print("preblocking incubation: 15 min") protocol.delay(minutes=15) # protocol.delay(minutes=60) #APPLYING ANTIBODY COCKTAILS TO SAMPLES print("applying antibodies") for i in range (len(wellslist)): washSamples(pipette_300, antibody_wells_cycle1[i], sample_chambers[i], wash_volume) #INCUBATE 90 MIN print("staining incubation 1.5h") protocol.delay(minutes=90) # washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) #WASHING SAMPLES WITH TBS #three individual repeats below is because they need particular incubation time between them print("washing with TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=3) #APPLYING OPAL polymer HRP print("applying opal secondary") for i in range (len(wellslist)): washSamples(pipette_300, opal_polymer_cycle1[i], sample_chambers[i], wash_volume) #INCUBATE 10 MIN print("opal secondary for 10min") protocol.delay(minutes=10) #three individual repeats below is because they need particular incubation time between them print("washing with TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) protocol.delay(minutes=3) #Opal Signal generation print("Opal Signal generation") for i in range (len(wellslist)): washSamples(pipette_300, opal_fluorophore1[i], sample_chambers[i], wash_volume) #INCUBATE 10 MIN print("opal fluorophore1 10min" ) protocol.delay(minutes=10) #WASHING SAMPLES WITH TBS print("washing in TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=2) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=2) washSamples(pipette_300, buffers.retreaval, sample_chambers, wash_volume, num_repeats=3) protocol.delay(minutes=3) temp_mod.set_temperature(95) print("retreaval") protocol.delay(minutes=40) temp_mod.set_temperature(25) # temp_mod.deactivate() print("cooling down") protocol.delay(minutes=20) ###--------cycle 2 #WASHING SAMPLES WITH TBS print("washing in TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) # protocol.delay(minutes=5) # pipette_300.drop_tip() print("preblocking") for i in range (len(wellslist)): washSamples(pipette_300, preblock_wells_cycle2[i], sample_chambers[i], wash_volume) #INCUBATE 15 MIN print("preblocking incubation: 15 min") protocol.delay(minutes=15) # protocol.delay(minutes=60) #APPLYING ANTIBODY COCKTAILS TO SAMPLES print("applying antibodies") for i in range (len(wellslist)): washSamples(pipette_300, antibody_wells_cycle2[i], sample_chambers[i], wash_volume) #INCUBATE 90 MIN print("staining incubation 1.5h") protocol.delay(minutes=90) # washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) #WASHING SAMPLES WITH TBS #three individual repeats below is because they need particular incubation time between them print("washing with TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=3) #APPLYING OPAL polymer HRP print("applying hrpsecondaryab") for i in range (len(wellslist)): washSamples(pipette_300, opal_polymer_cycle2[i], sample_chambers[i], wash_volume) #INCUBATE 10 MIN print("opal secondary for 10min") protocol.delay(minutes=10) #three individual repeats below is because they need particular incubation time between them print("washing with TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) protocol.delay(minutes=3) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=1) protocol.delay(minutes=3) #Opal Signal generation print("Opal Signal generation") for i in range (len(wellslist)): washSamples(pipette_300, opal_fluorophore2[i], sample_chambers[i], wash_volume) #INCUBATE 10 MIN print("opal fluorophore1 10min" ) protocol.delay(minutes=10) #WASHING SAMPLES WITH TBS print("washing in TBS") washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=2) washSamples(pipette_300, buffers.TBS_wash, sample_chambers, wash_volume, num_repeats=2) protocol.delay(minutes=2) washSamples(pipette_300, buffers.retreaval, sample_chambers, wash_volume, num_repeats=3) protocol.delay(minutes=3) temp_mod.set_temperature(95) print("retreaval") protocol.delay(minutes=40) temp_mod.set_temperature(25) temp_mod.deactivate() print("cooling down") protocol.delay(minutes=20) #STORAGE, washing samples every hour for i in range (48): washSamples(pipette_300, buffers.storage,sample_chambers, 100) protocol.delay(minutes=60) print("total dispensed volume: ", str(stats.volume))
def run(ctx: protocol_api.ProtocolContext): import os # Define the STEPS of the protocol STEP = 0 STEPS = { # Dictionary with STEP activation, description, and times 1: {'Execute': True, 'description': 'Add samples ('+str(volume_sample)+'ul)'}, 2: {'Execute': False, 'description': 'Add internal control one by one (10ul)'} # We won't use it as we will do it in KB with the multichannel pipette } for s in STEPS: # Create an empty wait_time if 'wait_time' not in STEPS[s]: STEPS[s]['wait_time'] = 0 if not ctx.is_simulating(): # Folder and file_path for log time folder_path = '/var/lib/jupyter/notebooks/'+run_id if not os.path.isdir(folder_path): os.mkdir(folder_path) file_path = folder_path + '/KA_SampleSetup_viral_time_log.txt' # Define Reagents as objects with their properties class Reagent: def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo, tip_recycling = 'none',rinse_loops = 2): self.name = name self.flow_rate_aspirate = flow_rate_aspirate self.flow_rate_dispense = flow_rate_dispense self.rinse = bool(rinse) self.reagent_reservoir_volume = reagent_reservoir_volume self.delay = delay self.num_wells = num_wells self.col = 0 self.vol_well = 0 self.h_cono = h_cono self.v_cono = v_fondo self.unused=[] self.tip_recycling = tip_recycling self.vol_well_original = reagent_reservoir_volume / num_wells self.rinse_loops = rinse_loops Samples = Reagent(name = 'Samples', flow_rate_aspirate = 1, flow_rate_dispense = 1, rinse = False, delay = 0, reagent_reservoir_volume = 100 * 24, num_wells = 24, # num_cols comes from available columns h_cono = h_cone, v_fondo = volume_cone ) # cone IC = Reagent(name = 'Internal control', flow_rate_aspirate = 1, flow_rate_dispense = 3, rinse = False, delay = 0, reagent_reservoir_volume = $IC_total_volume, num_wells = $IC_wells, # num_cols comes from available columns h_cono = h_cone, v_fondo = volume_cone ) # cone Samples.vol_well = Samples.vol_well_original IC.vol_well = IC.vol_well_original ################## # Custom functions def generate_source_table(source): ''' Concatenate the wells from the different origin racks ''' for rack_number in range(len(source)): if rack_number == 0: s = source[rack_number].wells() else: s = s + source[rack_number].wells() return s def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset, pickup_height, rinse, disp_height, blow_out, touch_tip, post_dispense=False, post_dispense_vol=20, post_airgap=False, post_airgap_vol=10): ''' x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1] pickup_height: height from bottom where volume rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered blow_out, touch_tip: if True they will be done after dispensing ''' # Rinse before aspirating if rinse == True: custom_mix(pipet, reagent, location = source, vol = vol, rounds = 2, blow_out = True, mix_height = 0, x_offset = x_offset) # SOURCE s = source.bottom(pickup_height).move(Point(x = x_offset[0])) pipet.aspirate(vol, s, rate = reagent.flow_rate_aspirate) # aspirate liquid if air_gap_vol != 0: # If there is air_gap_vol, switch pipette to slow speed pipet.aspirate(air_gap_vol, source.top(z = -2), rate = reagent.flow_rate_aspirate) # air gap # GO TO DESTINATION drop = dest.top(z = disp_height).move(Point(x = x_offset[1])) pipet.dispense(vol + air_gap_vol, drop, rate = reagent.flow_rate_dispense) # dispense all ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent if blow_out == True: pipet.blow_out(dest.top(z = -2)) if post_dispense == True: pipet.dispense(post_dispense_vol, dest.top(z = -2)) if touch_tip == True: pipet.touch_tip(speed = 20, v_offset = -5, radius = 0.9) if post_airgap == True: pipet.aspirate(post_airgap_vol, dest.top(z = 5)) def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height, x_offset, source_height = 3, post_airgap=False, post_airgap_vol=10, post_dispense=False, post_dispense_vol=20,): ''' Function for mixing a given [vol] in the same [location] a x number of [rounds]. blow_out: Blow out optional [True,False] x_offset = [source, destination] source_height: height from bottom to aspirate mix_height: height from bottom to dispense ''' if mix_height == 0: mix_height = 3 pipet.aspirate(1, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) for _ in range(rounds): pipet.aspirate(vol, location=location.bottom( z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate) pipet.dispense(vol, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) pipet.dispense(1, location=location.bottom( z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense) if blow_out == True: pipet.blow_out(location.top(z=-2)) # Blow out if post_dispense == True: pipet.dispense(post_dispense_vol, location.top(z = -2)) if post_airgap == True: pipet.dispense(post_airgap_vol, location.top(z = 5)) def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.4, extra_volume = 50): nonlocal ctx ctx.comment('Remaining volume ' + str(reagent.vol_well) + '< needed volume ' + str(aspirate_volume) + '?') if reagent.vol_well < aspirate_volume + extra_volume: reagent.unused.append(reagent.vol_well) ctx.comment('Next column should be picked') ctx.comment('Previous to change: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment(str('After change: ' + str(reagent.col))) reagent.vol_well = reagent.vol_well_original ctx.comment('New volume:' + str(reagent.vol_well)) height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Remaining volume:' + str(reagent.vol_well)) if height < min_height: height = min_height col_change = True else: height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono reagent.vol_well = reagent.vol_well - aspirate_volume ctx.comment('Calculated height is ' + str(height)) if height < min_height: height = min_height ctx.comment('Used height is ' + str(height)) col_change = False return height, col_change ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up(pip): nonlocal tip_track if not ctx.is_simulating(): if tip_track['counts'][pip] == tip_track['maxes'][pip]: ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \ resuming.') pip.reset_tipracks() tip_track['counts'][pip] = 0 pip.pick_up_tip() #################################### # load labware and modules #################################### # Load Sample racks if NUM_SAMPLES < 96: rack_num = math.ceil(NUM_SAMPLES / 24) ctx.comment('Used source racks are ' + str(rack_num)) samples_last_rack = NUM_SAMPLES - rack_num * 24 else: rack_num = 4 source_tube_types={'Screwcap 2ml': ['opentrons_24_tuberack_generic_2ml_screwcap','source tuberack with screwcap'], 'Eppendorf 1.5ml': ['opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap','source tuberack with eppendorf'], } source_racks = [ctx.load_labware( source_tube_types[source_type][0], slot, source_tube_types[source_type][1] + str(i + 1)) for i, slot in enumerate(['4', '1', '6', '3'][:rack_num]) ] ic_source_rack = ctx.load_labware('opentrons_24_tuberack_generic_2ml_screwcap', '9', 'Internal control source') ic_source = ic_source_rack.wells()[0] # internal control comes from 1 bottle ################################## # Destination plate dest_plate = ctx.load_labware( 'kf_96_wellplate_2400ul', '5', 'KF 96well destination plate') #################################### # Load tip_racks # tips20 = [ctx.load_labware('opentrons_96_filtertiprack_20ul', slot, '20µl filter tiprack') # for slot in ['2', '8']] tips300 = [ctx.load_labware('opentrons_96_filtertiprack_200ul', slot, '200µl filter tiprack') for slot in ['10', '11']] tips20 = [ctx.load_labware('opentrons_96_filtertiprack_20ul', slot, '20µl filter tiprack') for slot in ['8']] ################################################################################ # Declare which reagents are in each reservoir as well as deepwell and elution plate # setup samples and destinations sample_sources_full = generate_source_table(source_racks) sample_sources = sample_sources_full[:NUM_SAMPLES] destinations = dest_plate.wells()[:NUM_SAMPLES] p20 = ctx.load_instrument( 'p20_single_gen2', mount='left', tip_racks=tips20) p300 = ctx.load_instrument( 'p300_single_gen2', mount='right', tip_racks=tips300) # load P1000 pipette # used tip counter and set maximum tips available tip_track = { 'counts': {p300: 0, p20: 0}, # p1000: 0}, 'maxes': {p300: len(tips300) * 96, p20: len(tips20)*96} # ,p20: len(tips20)*96, } ############################################################################ # STEP 1: Add Samples ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') # Transfer parameters start = datetime.now() for s, d in zip(sample_sources, destinations): if not p300.hw_pipette['has_tip']: pick_up(p300) # Mix the sample BEFORE dispensing #custom_mix(p1000, reagent = Samples, location = s, vol = volume_sample, rounds = 2, blow_out = True, mix_height = 15) move_vol_multichannel(p300, reagent = Samples, source = s, dest = d, vol=volume_sample, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 0.3, rinse = Samples.rinse, disp_height = -10, blow_out = True, touch_tip = True) # Mix the sample AFTER dispensing #custom_mix(p300, reagent = Samples, location = d, vol = volume_sample, rounds = 2, blow_out = True, mix_height = 15) # Drop tip and update counter p300.drop_tip(home_after=False) tip_track['counts'][p300] += 1 # Time statistics end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # STEP 2: Add internal control ############################################################################ STEP += 1 if STEPS[STEP]['Execute'] == True: ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description']) ctx.comment('###############################################') #BEWARE, everything with the same tip, dispensed from top! # Transfer parameters start = datetime.now() for d in destinations: if not p20.hw_pipette['has_tip']: pick_up(p20) # Mix the sample BEFORE dispensing #custom_mix(p1000, reagent = Samples, location = s, vol = volume_sample, rounds = 2, blow_out = True, mix_height = 15) move_vol_multichannel(p20, reagent = IC, source = ic_source, dest = d, vol = ic_volume, air_gap_vol = air_gap_vol, x_offset = x_offset, pickup_height = 0.4, rinse = IC.rinse, disp_height = -8, blow_out = True, touch_tip = False) # Mix the sample AFTER dispensing #custom_mix(p20, reagent = Samples, location = d, vol = 10, rounds = 2, #blow_out = True, mix_height = 2, x_offset = x_offset) # Drop tip and update counter p20.drop_tip() tip_track['counts'][p20] += 1 # Time statistics end = datetime.now() time_taken = (end - start) ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] + ' took ' + str(time_taken)) STEPS[STEP]['Time:'] = str(time_taken) ############################################################################ # Export the time log to a tsv file if not ctx.is_simulating(): with open(file_path, 'w') as f: f.write('STEP\texecution\tdescription\twait_time\texecution_time\n') for key in STEPS.keys(): row = str(key) for key2 in STEPS[key].keys(): row += '\t' + format(STEPS[key][key2]) f.write(row + '\n') f.close() ############################################################################ # Light flash end of program '''if not ctx.is_simulating(): os.system('mpg123 -f -8000 /etc/audio/speaker-test.mp3 &')''' for i in range(3): ctx._hw_manager.hardware.set_lights(rails=False) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(rails=True) time.sleep(0.3) ctx._hw_manager.hardware.set_lights(rails=False) ctx.home() ctx.comment( 'Finished! \nMove deepwell plate to Station B.') ctx.comment('Used 200µl tips in total: ' + str(tip_track['counts'][p300])) ctx.comment('Used 200ul racks in total: '+str(tip_track['counts'][p300] / 96)) ctx.comment('Used p20 tips in total: ' + str(tip_track['counts'][p20])) ctx.comment('Used p20 racks in total: ' + str(tip_track['counts'][p20] / 96))
def run(ctx: protocol_api.ProtocolContext): w1_tip_pos_list = [] w2_tip_pos_list = [] elution_tip_pos_list = [] STEP = 0 STEPS = { #Dictionary with STEP activation, description, and times 1:{'Execute': True, 'description': 'Transferir bolas magnéticas'}, 2:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 600}, 3:{'Execute': True, 'description': 'Desechar sobrenadante'}, 4:{'Execute': True, 'description': 'Imán OFF'}, 5:{'Execute': True, 'description': 'Transferir primer lavado'}, 6:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 300}, 7:{'Execute': True, 'description': 'Desechar sobrenadante'}, 8:{'Execute': True, 'description': 'Imán OFF'}, 9:{'Execute': True, 'description': 'Transferir segundo lavado'}, 10:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 300}, 11:{'Execute': True, 'description': 'Desechar sobrenadante'}, 12:{'Execute': True, 'description': 'Secado', 'wait_time': 300}, 13:{'Execute': True, 'description': 'Imán OFF'}, 14:{'Execute': True, 'description': 'Transferir elución'}, 15:{'Execute': True, 'description': 'Incubación con el imán ON', 'wait_time': 300}, 16:{'Execute': True, 'description': 'Transferir elución a la placa'}, } #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 + '/time_log.txt' #Define Reagents as objects with their properties class Reagent: def calc_vol_well(self): if(self.name == 'Sample'): self.num_wells = num_cols return VOLUME_SAMPLE elif self.placed_in_multi: trips = math.ceil(self.reagent_volume / self.max_volume_allowed) vol_trip = self.reagent_volume / trips * 8 max_trips_well = math.floor(18000 / vol_trip) total_trips = num_cols * trips self.num_wells = math.ceil(total_trips / max_trips_well) return math.ceil(total_trips / self.num_wells) * vol_trip + self.dead_vol else: self.num_wells = 1 return self.reagent_volume * NUM_SAMPLES 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, reagent_volume, v_fondo, max_volume_allowed = pipette_allowed_capacity, dead_vol = 1400, first_well = None, placed_in_multi = False): 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.max_volume_allowed = max_volume_allowed self.reagent_volume = reagent_volume self.col = 0 self.vol_well = 0 self.v_cono = v_fondo self.dead_vol = dead_vol self.first_well = first_well self.placed_in_multi = placed_in_multi self.vol_well_original = self.calc_vol_well() if reagent_volume * NUM_SAMPLES > 0 else 0 self.vol_well = self.vol_well_original #Reagents and their characteristics Beads = Reagent(name = 'Beads', flow_rate_aspirate = 25, flow_rate_dispense = 100, flow_rate_aspirate_mix = 25, flow_rate_dispense_mix = 100, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, reagent_volume = BEADS_VOLUME_PER_SAMPLE, placed_in_multi = True, dead_vol = 2000, v_fondo = 695) #1.95 * multi_well_rack_area / 2, #Prismatic Wash_1 = Reagent(name = 'Wash 1', flow_rate_aspirate = 25, flow_rate_dispense = 100, flow_rate_aspirate_mix = 25, flow_rate_dispense_mix = 100, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, reagent_volume = WASH_1_VOLUME_PER_SAMPLE, placed_in_multi = True, v_fondo = 695) #1.95 * multi_well_rack_area / 2, #Prismatic) Wash_2 = Reagent(name = 'Wash 2', flow_rate_aspirate = 25, flow_rate_dispense = 100, flow_rate_aspirate_mix = 25, flow_rate_dispense_mix = 100, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, reagent_volume = WASH_2_VOLUME_PER_SAMPLE, placed_in_multi = True, v_fondo = 695) #1.95 * multi_well_rack_area / 2, #Prismatic) Elution = Reagent(name = 'Elution', flow_rate_aspirate = 25, flow_rate_dispense = 100, flow_rate_aspirate_mix = 25, flow_rate_dispense_mix = 100, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, reagent_volume = ELUTION_VOLUME_PER_SAMPLE, placed_in_multi = True, v_fondo = 695) #1.95*multi_well_rack_area/2) #Prismatic Sample = Reagent(name = 'Sample', flow_rate_aspirate = 5, # Original 0.5 flow_rate_dispense = 100, # Original 1 flow_rate_aspirate_mix = 1, flow_rate_dispense_mix = 1, air_gap_vol_bottom = 5, air_gap_vol_top = 0, disposal_volume = 1, reagent_volume = VOLUME_SAMPLE, v_fondo = 4 * math.pi * 4**3 / 3) #Sphere ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VALORES DE VARIABLES') ctx.comment(' ') ctx.comment('Número de muestras: ' + str(NUM_SAMPLES) + ' (' + str(num_cols) + ' ' + ('columna' if num_cols == 1 else 'columnas') + ')') ctx.comment('Capacidad de puntas: ' + txt_tip_capacity) ctx.comment(' ') ctx.comment('Volumen de muestra en el deepwell: ' + str(VOLUME_SAMPLE) + ' ul') ctx.comment('Volumen de beads por muestra: ' + str(BEADS_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen del lavado por muestra: ' + str(WASH_1_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen del etanol por muestra: ' + str(WASH_2_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen de elución por muestra: ' + str(ELUTION_VOLUME_PER_SAMPLE) + ' ul') ctx.comment('Volumen de elución a retirar del deepwell: ' + str(ELUTION_FINAL_VOLUME_PER_SAMPLE) + ' ul') ctx.comment(' ') ctx.comment('Número de mezclas en la primera recogida de un canal con bolas magnéticas: ' + str(BEADS_WELL_FIRST_TIME_NUM_MIXES)) ctx.comment('Número de mezclas en el resto de recogidas de un canal con bolas magnéticas: ' + str(BEADS_WELL_NUM_MIXES)) ctx.comment('Número de mezclas con la solución de bolas magnéticas: ' + str(BEADS_NUM_MIXES)) ctx.comment('Número de mezclas con el lavado: ' + str(WASH_1_NUM_MIXES)) ctx.comment('Número de mezclas con el etanol lavado: ' + str(WASH_2_NUM_MIXES)) ctx.comment('Número de mezclas con la elución: ' + str(ELUTION_NUM_MIXES)) ctx.comment(' ') ctx.comment('Reciclado de puntas en los lavados activado: ' + str(TIP_RECYCLING_IN_WASH)) ctx.comment('Reciclado de puntas en la elución activado: ' + str(TIP_RECYCLING_IN_ELUTION)) ctx.comment(' ') ctx.comment('Activar módulo de temperatura: ' + str(SET_TEMP_ON)) ctx.comment('Valor objetivo módulo de temepratura: ' + str(TEMPERATURE) + ' ºC') ctx.comment(' ') ctx.comment('Repeticiones del sonido final: ' + str(SOUND_NUM_PLAYS)) ctx.comment('Foto-sensible: ' + str(PHOTOSENSITIVE)) ctx.comment(' ') ######### def str_rounded(num): return str(int(num + 0.5)) ################### #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 = 'Esperando durante ' + str(wait_time) + ' segundos.') def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.4): nonlocal ctx ctx.comment('¿Volumen útil restante ' + str(reagent.vol_well - reagent.dead_vol) + ' uL < volumen necesario ' + str(aspirate_volume - reagent.disposal_volume * 8) + ' uL?') if (reagent.vol_well - reagent.dead_vol + 1) < (aspirate_volume - reagent.disposal_volume * 8): ctx.comment('Se debe utilizar el siguiente canal') ctx.comment('Canal anterior: ' + str(reagent.col)) # column selector position; intialize to required number reagent.col = reagent.col + 1 ctx.comment('Nuevo canal: ' + str(reagent.col)) reagent.vol_well = reagent.vol_well_original ctx.comment('Nuevo volumen: ' + str(reagent.vol_well) + ' uL') height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area reagent.vol_well = reagent.vol_well - (aspirate_volume - reagent.disposal_volume * 8) ctx.comment('Volumen restante: ' + str(reagent.vol_well) + ' uL') 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 - (reagent.disposal_volume * 8)) ctx.comment('La altura calculada es ' + str(round(height, 2)) + ' mm') if height < min_height: height = min_height ctx.comment('La altura utilizada es ' + str(round(height, 2)) + ' mm') col_change = False return height, col_change def move_vol_multi(pipet, reagent, source, dest, vol, x_offset_source, x_offset_dest, pickup_height, blow_out, wait_time = 0, touch_tip = False, touch_tip_v_offset = 0, drop_height = -5, aspirate_with_x_scroll = False, dispense_bottom_air_gap_before = False): # SOURCE 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.air_gap(reagent.air_gap_vol_top, height = 0) #air gap if aspirate_with_x_scroll: aspirate_with_x_scrolling(pip = pipet, volume = vol, src = source, pickup_height = pickup_height, rate = reagent.flow_rate_aspirate, start_x_offset_src = 0, stop_x_offset_src = x_offset_source) else: 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.air_gap(reagent.air_gap_vol_bottom, height = 0) #air gap # if wait_time != 0: # ctx.delay(seconds=wait_time, msg='Esperando durante ' + str(wait_time) + ' segundos.') # 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 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 = touch_tip_v_offset, radius=0.7) if wait_time != 0: ctx.delay(seconds=wait_time, msg='Esperando durante ' + str(wait_time) + ' segundos.') #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 def aspirate_with_x_scrolling(pip, volume, src, pickup_height = 0, rate = 1, start_x_offset_src = 0, stop_x_offset_src = 0): max_asp = volume/pip.min_volume inc_step = (start_x_offset_src - stop_x_offset_src) / max_asp for x in reversed(np.arange(stop_x_offset_src, start_x_offset_src, inc_step)): s = src.bottom(pickup_height).move(Point(x = x)) pip.aspirate(volume = pip.min_volume, location = s, rate = rate) ########## # pick up tip and if there is none left, prompt user for a new rack def pick_up_tip(pip, position = None): nonlocal tip_track #if not ctx.is_simulating(): if recycle_tip: pip.pick_up_tip(tips300[0].wells()[0]) else: 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('Reemplaza las cajas de puntas de ' + str(pip.max_volume) + 'µl antes de continuar.') pip.reset_tipracks() tip_track['counts'][pip] = 0 tip_track['num_refills'][pip] += 1 if position is None: pip.pick_up_tip() else: pip.pick_up_tip(position) def drop_tip(pip, recycle = False): nonlocal tip_track #if not ctx.is_simulating(): if recycle or recycle_tip: pip.return_tip() else: pip.drop_tip(home_after = False) if not recycle: tip_track['counts'][pip] += 8 if not ctx.is_simulating() and not recycle_tip and tip_track['counts'][pip] % max_tips_in_trash == 0: play_sound('empty_trash_esp') 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 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 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'][m300] * 96 * len(m300.tip_racks) + tip_track['counts'][m300] ctx.comment('Puntas de ' + txt_tip_capacity + ' 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 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 find_side(col): if col%2 == 0: side = -1 # left else: side = 1 # right return side def assign_wells(reagent, first_well_pos = None): global next_well_index if first_well_pos is not None and first_well_pos > next_well_index: reagent.first_well = first_well_pos else: reagent.first_well = next_well_index + 1 next_well_index = reagent.first_well - 1 + reagent.num_wells reagent.reagent_reservoir = reagent_res.rows()[0][reagent.first_well - 1:next_well_index] ctx.comment(reagent.name + ': ' + str(reagent.num_wells) + (' canal' if reagent.num_wells == 1 else ' canales') + ' desde el canal '+ str(reagent.first_well) +' en el reservorio de 12 canales con un volumen de ' + str_rounded(reagent.vol_well_original) + ' uL cada uno') #################################### # load labware and modules ######## 12 well rack reagent_res = ctx.load_labware('usascientific_12_reservoir_22ml', '5','Reagent 12 Well Reservoir') ################################## ########## tempdeck tempdeck = ctx.load_module('Temperature Module Gen2', '1') ####### Elution plate - final plate, goes to C elution_plate = tempdeck.load_labware('kingfisher_96_aluminumblock_200ul', 'Kingfisher 96 Aluminum Block 200 uL') ############################################ ######## Deepwell - comes from A magdeck = ctx.load_module('Magnetic Module Gen2', '4') deepwell_plate = magdeck.load_labware('kingfisher_96_wellplate_2000ul', 'KingFisher 96 Well Plate 2mL') #################################### ######## Waste reservoir waste_reservoir = ctx.load_labware('nest_1_reservoir_195ml', '7', 'waste reservoir') # Change to our waste reservoir waste = waste_reservoir.wells()[0] # referenced as reservoir #################################### ######### Load tip_racks tips300 = [ctx.load_labware('opentrons_96_tiprack_300ul', slot, txt_tip_capacity + ' filter tiprack') for slot in ['2', '3', '6', '8', '9', '10', '11']] ############################################################################### #Declare which reagents are in each reservoir as well as deepwell and elution plate ctx.comment(' ') ctx.comment('###############################################') ctx.comment('VOLÚMENES PARA ' + str(NUM_SAMPLES) + ' MUESTRAS') ctx.comment(' ') assign_wells(Beads, 1) assign_wells(Wash_1, 5) assign_wells(Wash_2, 9) assign_wells(Elution, 12) ctx.comment('###############################################') ctx.comment(' ') work_destinations = deepwell_plate.rows()[0][:Sample.num_wells] final_destinations = elution_plate.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 'num_refills' : {m300 : 0}, 'tips': { m300: [tip for rack in tips300 for tip in rack.rows()[0]]} } ############################################################################### start_run() magdeck.disengage() ############################################################################### # STEP 1 Transferir bolas magnéticas ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() beads_trips = math.ceil(Beads.reagent_volume / Beads.max_volume_allowed) beads_volume = Beads.reagent_volume / beads_trips beads_transfer_vol = [] for i in range(beads_trips): beads_transfer_vol.append(beads_volume + Beads.disposal_volume) x_offset_source = 0 x_offset_dest = 0 for i in range(num_cols): ctx.comment("Column: " + str(i)) pick_up_tip(m300) for j,transfer_vol in enumerate(beads_transfer_vol): [pickup_height, change_col] = calc_height(Beads, multi_well_rack_area, transfer_vol * 8) if change_col == True or (i == 0 and j == 0): #If we switch column because there is not enough volume left in current reservoir column we mix new column ctx.comment('Mezclando nuevo canal del reservorio: ' + str(Beads.col + 1)) custom_mix(m300, Beads, Beads.reagent_reservoir[Beads.col], vol = Beads.max_volume_allowed, rounds = BEADS_WELL_FIRST_TIME_NUM_MIXES, blow_out = False, mix_height = 1.5, offset = 0) first_mix_done = True else: ctx.comment('Mezclando canal del reservorio: ' + str(Beads.col + 1)) mix_height = 1.5 if pickup_height > 1.5 else pickup_height custom_mix(m300, Beads, Beads.reagent_reservoir[Beads.col], vol = Beads.max_volume_allowed, rounds = BEADS_WELL_NUM_MIXES, blow_out = False, mix_height = mix_height, offset = 0) ctx.comment('Aspirando desde la columna del reservorio: ' + str(Beads.col + 1)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm') move_vol_multi(m300, reagent = Beads, source = Beads.reagent_reservoir[Beads.col], dest = work_destinations[i], vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, blow_out = True, drop_height = deepwell_top_drop_height) if BEADS_NUM_MIXES > 0: ctx.comment(' ') ctx.comment('Mezclando muestra ') mix_volume = min(Beads.max_volume_allowed, Sample.reagent_volume + Beads.reagent_volume) custom_mix(m300, Beads, location = work_destinations[i], vol = mix_volume, rounds = BEADS_NUM_MIXES, blow_out = False, mix_height = 1, offset = 0, wait_time = 2) m300.air_gap(Beads.air_gap_vol_bottom, height = 0) #air gap drop_tip(m300) log_step_end(start) ############################################################################### # STEP 1 Mezclar en deepwell ######## ############################################################################### # STEP 2 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() magdeck.engage(height = mag_height) ctx.delay(seconds = STEPS[STEP]['wait_time'], msg = 'Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') log_step_end(start) #################################################################### # STEP 2 Incubación con el imán ON ######## ############################################################################### # STEP 3 Desechar sobrenadante ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() total_supernatant_volume = Sample.reagent_volume + Beads.reagent_volume supernatant_trips = math.ceil((total_supernatant_volume) / Sample.max_volume_allowed) supernatant_volume = Sample.max_volume_allowed # We try to remove an exceeding amount of supernatant to make sure it is empty supernatant_transfer_vol = [] for i in range(supernatant_trips): supernatant_transfer_vol.append(supernatant_volume + Sample.disposal_volume) pickup_height = 0.5 # Original 0.5 for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs_sn x_offset_dest = 0 if not m300.hw_pipette['has_tip']: pick_up_tip(m300) for j, transfer_vol in enumerate(supernatant_transfer_vol): ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm') move_vol_multi(m300, reagent = Beads, source = work_destinations[i], dest = waste, vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, wait_time = 2, blow_out = True, drop_height = waste_drop_height, dispense_bottom_air_gap_before = not (i == 0 and j == 0)) m300.air_gap(Sample.air_gap_vol_bottom, height = 0) drop_tip(m300) log_step_end(start) ############################################################################### # STEP 3 Desechar sobrenadante ######## ############################################################################### # STEP 4 Imán OFF ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # Imán OFF magdeck.disengage() log_step_end(start) ############################################################################### # STEP 4 Imán OFF ######## ############################################################################### # STEP 5 Transferir primer lavado ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() wash_trips = math.ceil(Wash_1.reagent_volume / Wash_1.max_volume_allowed) wash_volume = Wash_1.reagent_volume / wash_trips #136.66 wash_transfer_vol = [] for i in range(wash_trips): wash_transfer_vol.append(wash_volume + Wash_1.disposal_volume) for i in range(num_cols): x_offset_source = 0 x_offset_dest = -1 * find_side(i) * x_offset_rs_mv if not m300.hw_pipette['has_tip']: pick_up_tip(m300) if TIP_RECYCLING_IN_WASH: w1_tip_pos_list += [tip_track['tips'][m300][int((tip_track['counts'][m300] / 8) + i)]] for transfer_vol in wash_transfer_vol: [pickup_height, change_col] = calc_height(Wash_1, multi_well_rack_area, transfer_vol*8) ctx.comment('Aspirando desde la columna del reservorio: ' + str(Wash_1.first_well + Wash_1.col)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm') move_vol_multi(m300, reagent = Wash_1, source = Wash_1.reagent_reservoir[Wash_1.col], dest = work_destinations[i], vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, drop_height = deepwell_top_drop_height, blow_out = False) if WASH_1_NUM_MIXES > 0: mix_volume = min(Wash_1.max_volume_allowed, Wash_1.reagent_volume) custom_mix(m300, Wash_1, location = work_destinations[i], vol = mix_volume, two_thirds_mix_bottom = True, rounds = WASH_1_NUM_MIXES, blow_out = False, mix_height = 1.5, offset = x_offset_dest) m300.air_gap(Wash_1.air_gap_vol_bottom, height = 0) #air gap drop_tip(m300, recycle = TIP_RECYCLING_IN_WASH) log_step_end(start) ############################################################################### # STEP 5 Transferir primer lavado ######## ############################################################################### # STEP 6 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() magdeck.engage(mag_height) ctx.delay(seconds = STEPS[STEP]['wait_time'], msg = 'Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') log_step_end(start) #################################################################### # STEP 6 Incubación con el imán ON ######## ############################################################################### # STEP 7 Desechar sobrenadante ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() supernatant_trips = math.ceil(Wash_1.reagent_volume / Wash_1.max_volume_allowed) supernatant_volume = Wash_1.max_volume_allowed # We try to remove an exceeding amount of supernatant to make sure it is empty supernatant_transfer_vol = [] for i in range(supernatant_trips): supernatant_transfer_vol.append(supernatant_volume + Sample.disposal_volume) pickup_height = 0.5 # Original 0.5 for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs_sn x_offset_dest = 0 if not m300.hw_pipette['has_tip']: if TIP_RECYCLING_IN_WASH: pick_up_tip(m300, w1_tip_pos_list[i]) m300.dispense(Wash_1.air_gap_vol_top, work_destinations[i].top(z = 0), rate = Wash_1.flow_rate_dispense) else: pick_up_tip(m300) for j, transfer_vol in enumerate(supernatant_transfer_vol): ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm') move_vol_multi(m300, reagent = Sample, source = work_destinations[i], dest = waste, vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, wait_time = 2, blow_out = False, drop_height = waste_drop_height, dispense_bottom_air_gap_before = not (i == 0 and j == 0)) m300.air_gap(Sample.air_gap_vol_bottom, height = 0) drop_tip(m300) log_step_end(start) ############################################################################### # STEP 7 Desechar sobrenadante ######## ############################################################################### # STEP 8 Imán OFF ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # Imán OFF magdeck.disengage() log_step_end(start) ############################################################################### # STEP 8 Imán OFF ######## ############################################################################### # STEP 9 Transferir segundo lavado ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() wash_trips = math.ceil(Wash_2.reagent_volume / Wash_2.max_volume_allowed) wash_volume = Wash_2.reagent_volume / wash_trips #136.66 wash_transfer_vol = [] for i in range(wash_trips): wash_transfer_vol.append(wash_volume + Wash_2.disposal_volume) pickup_height = 0.5 for i in range(num_cols): x_offset_source = 0 x_offset_dest = -1 * find_side(i) * x_offset_rs_mv if not m300.hw_pipette['has_tip']: pick_up_tip(m300) if TIP_RECYCLING_IN_WASH: w2_tip_pos_list += [tip_track['tips'][m300][int((tip_track['counts'][m300] / 8) + i)]] for transfer_vol in wash_transfer_vol: [pickup_height, change_col] = calc_height(Wash_2, multi_well_rack_area, transfer_vol*8) ctx.comment('Aspirando desde la columna del reservorio: ' + str(Wash_2.first_well + Wash_2.col)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm') move_vol_multi(m300, reagent = Wash_2, source = Wash_2.reagent_reservoir[Wash_2.col], dest = work_destinations[i], vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, drop_height = deepwell_top_drop_height, blow_out = False) if WASH_2_NUM_MIXES > 0: mix_volume = min(Wash_2.max_volume_allowed, Wash_2.reagent_volume) custom_mix(m300, Wash_2, location = work_destinations[i], vol = mix_volume, two_thirds_mix_bottom = True, rounds = WASH_2_NUM_MIXES, blow_out = False, mix_height = 1.5, offset = x_offset_dest) m300.air_gap(Wash_2.air_gap_vol_bottom, height = 0) #air gap drop_tip(m300, recycle = TIP_RECYCLING_IN_WASH) log_step_end(start) ############################################################################### # STEP 9 Transferir segundo lavado ######## ############################################################################### # STEP 10 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() magdeck.engage(mag_height) ctx.delay(seconds = STEPS[STEP]['wait_time'], msg = 'Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') log_step_end(start) #################################################################### # STEP 10 Incubación con el imán ON ######## ############################################################################### # STEP 11 Desechar sobrenadante ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() supernatant_trips = math.ceil(Wash_2.reagent_volume / Wash_2.max_volume_allowed) supernatant_volume = Wash_2.max_volume_allowed # We try to remove an exceeding amount of supernatant to make sure it is empty supernatant_transfer_vol = [] for i in range(supernatant_trips): supernatant_transfer_vol.append(supernatant_volume + Sample.disposal_volume) pickup_height = 0.5 # Original 0.5 for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs_sn x_offset_dest = 0 if not m300.hw_pipette['has_tip']: if TIP_RECYCLING_IN_WASH: pick_up_tip(m300, w2_tip_pos_list[i]) m300.dispense(Wash_2.air_gap_vol_top, work_destinations[i].top(z = 0), rate = Wash_2.flow_rate_dispense) else: pick_up_tip(m300) for j, transfer_vol in enumerate(supernatant_transfer_vol): ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm') move_vol_multi(m300, reagent = Sample, source = work_destinations[i], dest = waste, vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, wait_time = 2, blow_out = False, dispense_bottom_air_gap_before = not (i == 0 and j == 0), drop_height = waste_drop_height) m300.air_gap(Sample.air_gap_vol_bottom, height = 0) drop_tip(m300) log_step_end(start) ############################################################################### # STEP 11 Desechar sobrenadante ######## ############################################################################### # STEP 12 Secado ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() ctx.comment(' ') ctx.delay(seconds=STEPS[STEP]['wait_time'], msg='Secado durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') # ctx.comment(' ') log_step_end(start) ############################################################################### # STEP 12 Secado ######## ############################################################################### # STEP 13 Imán OFF ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() # Imán OFF magdeck.disengage() log_step_end(start) ############################################################################### # STEP 13 Imán OFF ######## ############################################################################### # STEP 14 Transferir elución ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() elution_trips = math.ceil(Elution.reagent_volume / Elution.max_volume_allowed) elution_volume = Elution.reagent_volume / elution_trips elution_wash_vol = [] for i in range(elution_trips): elution_wash_vol.append(elution_volume + Sample.disposal_volume) ######## # Water or elution buffer for i in range(num_cols): x_offset_source = 0 x_offset_dest = -1 * find_side(i) * x_offset_rs_mv # Original 0 if not m300.hw_pipette['has_tip']: pick_up_tip(m300) if TIP_RECYCLING_IN_ELUTION: elution_tip_pos_list += [tip_track['tips'][m300][int((tip_track['counts'][m300] / 8) + i)]] for transfer_vol in elution_wash_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('Aspirando desde la columna del reservorio: ' + str(Elution.first_well + Elution.col)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm') move_vol_multi(m300, reagent = Elution, source = Elution.reagent_reservoir[Elution.col], dest = work_destinations[i], vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, blow_out = False, drop_height = -35) if ELUTION_NUM_MIXES > 0: ctx.comment(' ') ctx.comment('Mezclando muestra con Elution') mix_volume = min(Elution.max_volume_allowed, Elution.reagent_volume) custom_mix(m300, Elution, work_destinations[i], vol = mix_volume, rounds = ELUTION_NUM_MIXES, blow_out = False, mix_height = 1, offset = x_offset_dest, drop_height = -35) m300.air_gap(Elution.air_gap_vol_bottom, height = 0) #air gap drop_tip(m300, recycle = TIP_RECYCLING_IN_ELUTION) log_step_end(start) ############################################################################### # STEP 14 Transferir elución ######## ############################################################################### # STEP 15 Incubación con el imán ON ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() magdeck.engage(mag_height) ctx.delay(seconds = STEPS[STEP]['wait_time'], msg = 'Incubación con el imán ON durante ' + format(STEPS[STEP]['wait_time']) + ' segundos.') log_step_end(start) #################################################################### # STEP 15 Incubación con el imán ON ######## ############################################################################### # STEP 16 Transferir elución a la placa ######## STEP += 1 if STEPS[STEP]['Execute']==True: start = log_step_start() elution_trips = math.ceil(ELUTION_FINAL_VOLUME_PER_SAMPLE / Elution.max_volume_allowed) elution_volume = ELUTION_FINAL_VOLUME_PER_SAMPLE / elution_trips elution_vol = [] for i in range(elution_trips): elution_vol.append(elution_volume + Elution.disposal_volume) for i in range(num_cols): x_offset_source = find_side(i) * x_offset_rs_sn x_offset_dest = 0 if not m300.hw_pipette['has_tip']: if TIP_RECYCLING_IN_ELUTION: pick_up_tip(m300, elution_tip_pos_list[i]) m300.dispense(Elution.air_gap_vol_top, work_destinations[i].top(z = 0), rate = Elution.flow_rate_dispense) else: pick_up_tip(m300) for transfer_vol in elution_vol: #Pickup_height is fixed here pickup_height = 1 ctx.comment('Aspirando de la columna del deepwell: ' + str(i+1)) ctx.comment('La altura de recogida es ' + str(round(pickup_height, 2)) + ' mm' ) move_vol_multi(m300, reagent = Sample, source = work_destinations[i], dest = final_destinations[i], vol = transfer_vol, x_offset_source = x_offset_source, x_offset_dest = x_offset_dest, pickup_height = pickup_height, blow_out = True, touch_tip = False, drop_height = -1) m300.air_gap(Sample.air_gap_vol_bottom, height = 0) #air gap drop_tip(m300) if SET_TEMP_ON == True: tempdeck.set_temperature(TEMPERATURE) log_step_end(start) ############################################################################### # STEP 16 Transferir elución a la placa ######## magdeck.disengage() 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() ############################################################################ finish_run(switch_off_lights)