Example #1
0
def run(protocol: protocol_api.ProtocolContext):
    tiprack = protocol.load_labware_from_definition(TIPRACK_DEF,
                                                    TEST_TIPRACK_SLOT,
                                                    TIPRACK_LABEL)
    pipette = protocol.load_instrument(PIPETTE_NAME,
                                       PIPETTE_MOUNT,
                                       tip_racks=[tiprack])

    num_cols = len(TIPRACK_DEF.get('ordering', [[]]))
    num_rows = len(TIPRACK_DEF.get('ordering', [[]])[0])

    def set_speeds(rate):
        protocol.max_speeds.update({
            'X': (600 * rate),
            'Y': (400 * rate),
            'Z': (125 * rate),
            'A': (125 * rate),
        })

        speed_max = max(protocol.max_speeds.values())

        for instr in protocol.loaded_instruments.values():
            instr.default_speed = speed_max

    set_speeds(RATE)
    firstwell = tiprack.well('A1')
    pipette.move_to(firstwell.top())
    protocol.pause("If the pipette is accurate click 'resume'")
    pipette.pick_up_tip()
    protocol.pause(
        "If the pipette went into the center of the tip, click 'resume'")
    pipette.return_tip()
    protocol.pause(
        "If the pipette successfully picked up the tip(s) but does not eject succesfully, pull the tip(s) off by hand and click 'resume'. Do not worry about tip ejection yet"
    )

    last_col = (num_cols * num_rows) - num_rows
    if (PIPETTE_NAME == 'p20_multi_gen2' or PIPETTE_NAME == 'p300_multi_gen2'):
        well = tiprack.well(last_col)
        pipette.move_to(well.top())
        protocol.pause("If the position is accurate click 'resume'")
        pipette.pick_up_tip(well)
    else:
        last_well = (num_cols) * (num_rows)
        well = tiprack.well(last_well - 1)
        pipette.move_to(well.top())
        protocol.pause("If the position is accurate click 'resume'")
        pipette.pick_up_tip(well)

    protocol.pause(
        "If the pipette went to the center of the tip, click 'resume'")
    pipette.return_tip()
    protocol.comment(
        "If the pipette successfully picked up the tip(s) but does not eject succesfully, pull the tip(s) off by hand and click 'resume'. Do not worry about tip ejection yet"
    )
def run(ctx: protocol_api.ProtocolContext):
    ctx.comment("Begin {}".format(metadata['protocolName']))

    # Turn on rail lights and pause program so user can load robot deck.
    # ctx.set_rail_lights(True)
    # ctx.pause("Load Labware onto robot deck and click resume when ready to continue")
    # ctx.home()
    ctx.set_rail_lights(False)

    # TSV file location on OT-2
    tsv_file_path = "{0}var{0}lib{0}jupyter{0}notebooks{0}ProcedureFile.tsv".format(
        os.sep)
    if not os.path.isfile(tsv_file_path):
        # Temp TSV file location on Win10 Computers for simulation
        tsv_file_path = "C:{0}Users{0}{1}{0}Documents{0}TempTSV.tsv".format(
            os.sep, os.getlogin())

    sample_parameters, args = Utilities.parse_sample_template(tsv_file_path)
    labware_dict, left_tiprack_list, right_tiprack_list = Utilities.labware_parsing(
        args, ctx)

    # Pipettes
    left_pipette = ctx.load_instrument(args.LeftPipette,
                                       'left',
                                       tip_racks=left_tiprack_list)
    right_pipette = ctx.load_instrument(args.RightPipette,
                                        'right',
                                        tip_racks=right_tiprack_list)

    # Set the location of the first tip in box.
    with suppress(IndexError):
        left_pipette.starting_tip = left_tiprack_list[0].wells_by_name()[
            args.LeftPipetteFirstTip]
    with suppress(IndexError):
        right_pipette.starting_tip = right_tiprack_list[0].wells_by_name()[
            args.RightPipetteFirstTip]

    # Dispense Samples and primers
    sample_dest_dict = dispense_samples(args, sample_parameters, labware_dict,
                                        left_pipette, right_pipette)

    # Add PCR mix to each destination well.
    add_pcr_mix(args, labware_dict, sample_dest_dict, left_pipette,
                right_pipette)

    if not ctx.is_simulating():
        os.remove(tsv_file_path)

    ctx.comment("Program End")
def run(robot: protocol_api.ProtocolContext):

    # -----------------------------------------------------
    # Magnetic module + labware
    # -----------------------------------------------------
    magdeck = robot.load_module('Magnetic Module Gen2', '1')
    maglab = magdeck.load_labware('nest_96_wellplate_2ml_deep',
                                  'nest_96_wellplate_2ml_deep')
    #magdeck.engage(height = 7)

    for i in np.arange(0.0, 25.0, 0.1):
        magdeck.engage(height=i)
        robot.comment('Actual height =' + str(i))
        robot.pause('Test')
    magdeck.disengage()
Example #4
0
def run(protocol: protocol_api.ProtocolContext):
    #load tip rack, pipette and labware
    tip_rack = protocol.load_labware('opentrons_96_tiprack_300ul', 2)

    pipette_right = protocol.load_instrument('p300_single',
                                             'right',
                                             tip_racks=[tip_rack])

    #load bioshake - remember to change serial number to match your devices!
    device = QIDevice(serial_number='19762',
                      deck_position=1,
                      adapter_set_up=1,
                      protocol=protocol)

    #first, upload Qinstrument's custom labware to the OT-2. Next, make sure you
    #add the z_offset, item_depth, and item_volume
    lbw = device.load_labware('2016_1062',
                              z_offset=(11, 5),
                              item_depth=(37.5, 30),
                              item_volume=(1500, 500))
    #resetting bioshake just in case
    device.exec_cmd('resetDevice')

    #teseting pipette
    pipette_right.pick_up_tip()
    pipette_right.aspirate(100, lbw['AA1'])
    pipette_right.dispense(100, lbw['AA5'])
    pipette_right.drop_tip()

    #testing temp at 37C for 20 seconds while shaking still occurs
    device.exec_cmd('setShakeTargetSpeed500')
    device.exec_cmd('shakeOn')
    device.exec_cmd('setTempTarget370', blocking=True)
    device.exec_cmd('tempOn', polling=True)
    protocol.delay(seconds=20,
                   msg='shaking at 500rpm and heating at 37C for 20 seconds')
    device.exec_cmd('shakeOff')

    #testing pipette with temperature on
    pipette_right.pick_up_tip()
    pipette_right.aspirate(100, lbw['AA2'])
    pipette_right.dispense(100, lbw['AA3'])
    pipette_right.drop_tip()

    device.exec_cmd('tempOff')

    protocol.comment('Protocol complete.')
Example #5
0
def run(ctx: protocol_api.ProtocolContext):

    [num_samp, reaction_plate, p20_mount] = get_values(  # noqa: F821
        "num_samp", "reaction_plate", "p20_mount")

    if not 1 <= num_samp <= 96:
        raise Exception("Enter a sample number 1-96")

    # LABWARE
    pcr_plate = ctx.load_labware(reaction_plate, '9', label='the PCR PLATE')
    sample_racks = [
        ctx.load_labware('opentrons_15_tuberack_5000ul',
                         slot,
                         label="the SAMPLE RACK")
        for slot in ['1', '2', '3', '4', '5', '6', '7']
    ]

    # TIPRACKS
    tiprack20 = [
        ctx.load_labware('opentrons_96_filtertiprack_20ul',
                         '10',
                         label='20uL TIPRACK')
    ]

    # INSTRUMENTS
    p20 = ctx.load_instrument('p20_single_gen2',
                              p20_mount,
                              tip_racks=tiprack20)

    # MAPPING
    all_sample_tubes = [
        tube for rack in sample_racks for tube in rack.wells()
    ][:num_samp]

    # protocol

    ctx.comment('\n\n~~~~~~~~MOVING SAMPLES TO PLATE~~~~~~~~~\n')
    for sample, well in zip(all_sample_tubes, pcr_plate.wells()):
        p20.pick_up_tip()
        p20.aspirate(10, sample)
        p20.dispense(10, well)
        p20.blow_out()
        p20.touch_tip()
        p20.drop_tip()
Example #6
0
def run(ctx: protocol_api.ProtocolContext):

    #Please replace what is between the quotation marks!!

    NEW_SERIAL_NUMBER = "OT2CEP20201214B12"


    ctx.comment(f"Setting serial number to {NEW_SERIAL_NUMBER}.")

    if not ctx.is_simulating():
        with open("/var/serial", "w") as serial_number_file:
            serial_number_file.write(NEW_SERIAL_NUMBER + "\n")
        with open("/etc/machine-info", "w") as serial_number_file:
            serial_number_file.write(f"DEPLOYMENT=production\nPRETTY_HOSTNAME={NEW_SERIAL_NUMBER}\n")
        with open("/etc/hostname", "w") as serial_number_file:
            serial_number_file.write(NEW_SERIAL_NUMBER + "\n")
        
        os.sync()
        
        
        ctx.comment("Done.")
Example #7
0
def run(ctx: protocol_api.ProtocolContext):

    #Please replace what is between the quotation marks!!

    NEW_SERIAL_NUMBER = "set_your_serial_number_here"


    ctx.comment(f"Setting serial number to {NEW_SERIAL_NUMBER}.")

    if not ctx.is_simulating():
        with open("/var/serial", "w") as serial_number_file:
            serial_number_file.write(NEW_SERIAL_NUMBER + "\n")
        with open("/etc/machine-info", "w") as serial_number_file:
            serial_number_file.write(f"DEPLOYMENT=production\nPRETTY_HOSTNAME={NEW_SERIAL_NUMBER}\n")
        with open("/etc/hostname", "w") as serial_number_file:
            serial_number_file.write(NEW_SERIAL_NUMBER + "\n")
        
        os.sync()
        
        
        ctx.comment("Done.")# This script sets an OT-2's serial number.  It's meant to be used after you
Example #8
0
    def run_test(self, ctx: ProtocolContext):
        """Test protocol"""
        ctx.comment("Test the custom '{}' rack".format(
            self.metadata["displayName"]))

        rack = ctx.load_labware_from_definition(self.labware_definition(), '2',
                                                'custom tuberack')
        tipracks1000 = [
            ctx.load_labware('opentrons_96_filtertiprack_1000ul', '1',
                             '1000µl filter tiprack')
        ]
        p1000 = ctx.load_instrument('p1000_single_gen2',
                                    'right',
                                    tip_racks=tipracks1000)

        p1000.pick_up_tip()
        for w in rack.wells():
            ctx.pause("moving to top of {}".format(w))
            p1000.move_to(w.top())
            ctx.pause("moving to bottom of {} (1 mm high)".format(w))
            p1000.move_to(w.bottom(1))
            p1000.aspirate(5)
            p1000.dispense(5)
        p1000.drop_tip()
Example #9
0
def run(ctx: protocol_api.ProtocolContext):

    # load labware

    s_racks = [
        ctx.load_labware('opentrons_15_tuberack_falcon_15ml_conical', '4')
    ]
    d_plate = ctx.load_labware('nest_96_wellplate_200ul_flat', '1',
                               '96-wellplate sample plate')

    tips300 = [ctx.load_labware('opentrons_96_filtertiprack_200ul', '5')]

    # load pipette

    p300 = ctx.load_instrument('p300_single_gen2', 'left', tip_racks=tips300)

    # transfer sample

    p300.transfer(SAMPLE_VOLUME,
                  s_racks.wells()['A1'],
                  d_plate.columns()['5'],
                  air_gap=20,
                  mix_before=(2, 200),
                  new_tip='never')
    p300.air_gap(20)
    p300.drop_tip()

    ctx.comment('Terminado.')

    # track final used tip
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        data = {'tips300': tip_log['count'][p300]}
        with open(tip_file_path, 'w') as outfile:
            json.dump(data, outfile)
Example #10
0
def run(ctx: protocol_api.ProtocolContext):
    STEP = 0
    STEPS = {  # Dictionary with STEP activation, description and times
        1: {'Execute': True, 'description': 'Mix and move samples ('+str(VOLUME_SAMPLE)+'ul)'}
    }
    for s in STEPS:  # Create an empty wait_time
        if 'wait_time' not in STEPS[s]:
            STEPS[s]['wait_time'] = 0

    #Folder and file_path for log time
    if not ctx.is_simulating():
        folder_path = '/var/lib/jupyter/notebooks/'+run_id
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        file_path = folder_path + '/StationA_time_log.txt'

    # Define Reagents as objects with their properties
    class Reagent:
        def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse, delay):
            self.name               = name
            self.flow_rate_aspirate = flow_rate_aspirate
            self.flow_rate_dispense = flow_rate_dispense
            self.rinse              = bool(rinse)
            self.delay              = delay 

    # Reagents and their characteristics
    Samples = Reagent(name                  = 'Samples',
                      flow_rate_aspirate    = 1,
                      flow_rate_dispense    = 1,
                      rinse                 = False,
                      delay                 = 0
                      ) 

    ctx.comment(' ')
    ctx.comment('###############################################')
    ctx.comment('CONTROL SPACES: ' + str(NUM_CONTROL_SPACES))  
    ctx.comment('NUM SAMPLES: ' + str(NUM_REAL_SAMPLES)) 
    ctx.comment('###############################################')
    ctx.comment(' ')

    ##################
    # Custom functions
    def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset,
                       pickup_height, rinse, disp_height, blow_out, touch_tip):
        '''
        x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1]
        pickup_height: height from bottom where volume
        rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer
        disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered
        blow_out, touch_tip: if True they will be done after dispensing
        '''
        # Rinse before aspirating
        if rinse == True:
            custom_mix(pipet, reagent, location = source, vol = vol,
                       rounds = 2, blow_out = True, mix_height = 0,
                       x_offset = x_offset)

        # SOURCE
        s = source.bottom(pickup_height).move(Point(x = x_offset[0]))
        pipet.aspirate(vol, s)  # aspirate liquid
        if air_gap_vol != 0:  # If there is air_gap_vol, switch pipette to slow speed
            pipet.aspirate(air_gap_vol, source.top(z = -2),
                           rate = reagent.flow_rate_aspirate)  # air gap

        # GO TO DESTINATION
        drop = dest.top(z = disp_height).move(Point(x = x_offset[1]))
        pipet.dispense(vol + air_gap_vol, drop,
                       rate = reagent.flow_rate_dispense)  # dispense all

        ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent

        if blow_out == True:
            pipet.blow_out(dest.top(z = -2))
        if touch_tip == True:
            pipet.touch_tip(speed = 20, v_offset = -10)

    def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height,
    x_offset, source_height = 5):
        '''
        Function for mixing a given [vol] in the same [location] a x number of [rounds].
        blow_out: Blow out optional [True,False]
        x_offset = [source, destination]
        source_height: height from bottom to aspirate
        mix_height: height from bottom to dispense
        '''
        if mix_height <= 0:
            mix_height = 3

        pipet.aspirate(1, location = location.bottom(
                        z = source_height).move(Point(x = x_offset[0])), rate = reagent.flow_rate_aspirate)

        for _ in range(rounds):
            pipet.aspirate(vol, location = location.bottom(
                z = source_height).move(Point(x = x_offset[0])), rate = reagent.flow_rate_aspirate)
            pipet.dispense(vol, location = location.bottom(
                z = mix_height).move(Point(x = x_offset[1])), rate = reagent.flow_rate_dispense)

        pipet.dispense(1, location = location.bottom(
            z = mix_height).move(Point(x = x_offset[1])), rate = reagent.flow_rate_dispense)

        if blow_out == True:
            pipet.blow_out(location.top(z = -2))  # Blow out

    def generate_source_table(source):
        '''
        Concatenate the wells frome the different origin racks
        '''
        num_cols = math.ceil(num_samples / 8)
        s = []
        for i  in range(num_cols):
            if i < 6:
                s += source[0].columns()[i] + source[1].columns()[i]
            else:
                s += source[2].columns()[i - 6] + source[3].columns()[i - 6]
        return s

    ##########
    # pick up tip and if there is none left, prompt user for a new rack
    def pick_up(pip):
        nonlocal tip_track
        if not ctx.is_simulating():
            if tip_track['counts'][pip] == tip_track['maxes'][pip]:
                ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \
                resuming.')
                pip.reset_tipracks()
                tip_track['counts'][pip] = 0
        pip.pick_up_tip()

    ####################################
    # load labware and modules

    ####################################
    # Load Sample racks
    if num_samples <= 48:
        rack_num = 2
        ctx.comment('Used source racks are ' + str(rack_num))
    else:
        rack_num = 4

    source_racks = [ctx.load_labware(
        'opentrons_24_tuberack_nest_2ml_snapcap', slot,
        'source tuberack with snapcap' + str(i + 1)) for i, slot in enumerate(['5', '2', '6', '3'][:rack_num])
    ]

    ##################################
    # Destination plate
    dest_plate = ctx.load_labware(
        'nest_96_wellplate_2ml_deep', '1',
        'NEST 96 Deepwell Plate 2mL')

    ####################################
    # Load tip_racks
    tips1000 = [ctx.load_labware(
        'opentrons_96_filtertiprack_1000ul', slot, 
        '1000µl filter tiprack') for slot in ['7']]

    ################################################################################
    # setup samples and destinations
    sample_sources_full = generate_source_table(source_racks)
    sample_sources      = sample_sources_full[NUM_CONTROL_SPACES:num_samples]
    destinations        = dest_plate.wells()[NUM_CONTROL_SPACES:num_samples]

    p1000 = ctx.load_instrument(
        'p1000_single_gen2', 'right', 
        tip_racks = tips1000) # load P1000 pipette

    # used tip counter and set maximum tips available
    tip_track = {
        'counts': {p1000: 0},
        'maxes': {p1000: len(tips1000) * 96}
    }

    ############################################################################
    # STEP 1: MIX AND MOVE SAMPLES
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')

        start = datetime.now()
        for s, d in zip(sample_sources, destinations):
            if not p1000.hw_pipette['has_tip']:
                pick_up(p1000)

            # Mix the sample BEFORE dispensing
            if NUM_MIXES > 0:
                custom_mix(p1000, reagent = Samples, location = s, vol = volume_mix, 
                    rounds = NUM_MIXES, blow_out = True, mix_height = 15, x_offset = x_offset)

            move_vol_multichannel(p1000, reagent = Samples, source = s, dest = d,
                vol = VOLUME_SAMPLE, air_gap_vol = air_gap_vol_sample, x_offset = x_offset,
                pickup_height = 3, rinse = Samples.rinse, disp_height = -10,
                blow_out = True, touch_tip = True)

            p1000.drop_tip(home_after = False)
            tip_track['counts'][p1000] += 1

        # Time statistics
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)


    # Export the time log to a tsv file
    if not ctx.is_simulating():
        with open(file_path, 'w') as f:
            f.write('STEP\texecution\tdescription\twait_time\texecution_time\n')
            for key in STEPS.keys():
                row = str(key)
                for key2 in STEPS[key].keys():
                    row += '\t' + format(STEPS[key][key2])
                f.write(row + '\n')
        f.close()

    ############################################################################
    # Light flash end of program
    # from opentrons.drivers.rpi_drivers import gpio
    #if not ctx.is_simulating():
        #os.system('mpg123 -f -14000 /var/lib/jupyter/notebooks/final.mp3')
    if not ctx.is_simulating():
        fname = ' /var/lib/jupyter/notebooks/finished_process_esp.mp3'
        if os.path.isfile(fname) is True:
                subprocess.run(
                    ['mpg123', fname],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
        else:
            ctx.comment(f"Sound file does not exist. Call the technician")
    for i in range(3):
        ctx._hw_manager.hardware.set_lights(rails = False)
        ctx._hw_manager.hardware.set_lights(button = (1, 0 ,0))
        time.sleep(0.3)
        ctx._hw_manager.hardware.set_lights(rails = True)
        ctx._hw_manager.hardware.set_lights(button = (0, 0 ,1))
        time.sleep(0.3)
    ctx._hw_manager.hardware.set_lights(button = (0, 1 ,0))
    ctx.comment('Finished! \nMove deepwell plate (slot 1) to Station B for extraction protocol')
    ctx.comment('Used p1000 tips in total: ' + str(tip_track['counts'][p1000]))
    ctx.comment('Used p1000 racks in total: ' + str(tip_track['counts'][p1000] / 96))
def run(ctx: protocol_api.ProtocolContext):
    from opentrons.drivers.rpi_drivers import gpio
    gpio.set_rail_lights(False) #Turn off lights (termosensible reagents)
    ctx.comment('Actual used columns: ' + str(num_cols))

    # Define the STEPS of the protocol
    STEP = 0
    STEPS = {  # Dictionary with STEP activation, description, and times
        1: {'Execute': True, 'description': 'Transfer MMIX'},
        2: {'Execute': True, 'description': 'Transfer elution'}
    }

    for s in STEPS:  # Create an empty wait_time
        if 'wait_time' not in STEPS[s]:
            STEPS[s]['wait_time'] = 0

    #Folder and file_path for log time
    folder_path = '/var/lib/jupyter/notebooks/'+run_id'
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        file_path = folder_path + '/KC_qPCR_time_log.txt'

    # Define Reagents as objects with their properties
    class Reagent:
        def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse,
                     reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo,
                      tip_recycling = 'none'):
            self.name = name
            self.flow_rate_aspirate = flow_rate_aspirate
            self.flow_rate_dispense = flow_rate_dispense
            self.rinse = bool(rinse)
            self.reagent_reservoir_volume = reagent_reservoir_volume
            self.delay = delay
            self.num_wells = num_wells
            self.col = 0
            self.vol_well = 0
            self.h_cono = h_cono
            self.v_cono = v_fondo
            self.unused=[]
            self.tip_recycling = tip_recycling
            self.vol_well_original = reagent_reservoir_volume / num_wells

    # Reagents and their characteristics
    MMIX = Reagent(name = 'Master Mix',
                      rinse = False,
                      flow_rate_aspirate = 1,
                      flow_rate_dispense = 1,
                      reagent_reservoir_volume = volume_mmix_available,
                      num_wells = 2, #change with num samples
                      delay = 0,
                      h_cono = h_cone,
                      v_fondo = volume_cone  # V cono
                      )

    Samples = Reagent(name='Samples',
                      rinse=False,
                      flow_rate_aspirate = 1,
                      flow_rate_dispense = 1,
                      reagent_reservoir_volume=50,
                      delay=0,
                      num_wells=num_cols,  # num_cols comes from available columns
                      h_cono=0,
                      v_fondo=0
                      )

    MMIX.vol_well = MMIX.vol_well_original
    Samples.vol_well = Samples.vol_well_original

    ##################
    # Custom functions


    def divide_destinations(l, n):
        # Divide the list of destinations in size n lists.
        for i in range(0, len(l), n):
            yield l[i:i + n]

    def distribute_custom(pipette, volume, src, dest, waste_pool, pickup_height, extra_dispensal, disp_height=0):
        # Custom distribute function that allows for blow_out in different location and adjustement of touch_tip
        pipette.aspirate((len(dest) * volume) +
                         extra_dispensal, src.bottom(pickup_height))
        pipette.touch_tip(speed=20, v_offset=-5)
        pipette.move_to(src.top(z=5))
        pipette.aspirate(20)  # air gap
        for d in dest:
            pipette.dispense(20, d.top())
            drop = d.top(z = disp_height)
            pipette.dispense(volume, drop)
            pipette.move_to(d.top(z=5))
            pipette.aspirate(20)  # air gap
        try:
            pipette.blow_out(waste_pool.wells()[0].bottom(pickup_height + 3))
        except:
            pipette.blow_out(waste_pool.bottom(pickup_height + 3))
        return (len(dest) * volume)

    def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset,
                       pickup_height, rinse, disp_height, blow_out, touch_tip):
        '''
        x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1]
        pickup_height: height from bottom where volume
        rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer
        disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered
        blow_out, touch_tip: if True they will be done after dispensing
        '''
        # Rinse before aspirating
        if rinse == True:
            custom_mix(pipet, reagent, location = source, vol = vol,
                       rounds = 2, blow_out = True, mix_height = 0,
                       x_offset = x_offset)
        # SOURCE
        s = source.bottom(pickup_height).move(Point(x = x_offset[0]))
        pipet.aspirate(vol, s, rate = reagent.flow_rate_aspirate)  # aspirate liquid
        if air_gap_vol != 0:  # If there is air_gap_vol, switch pipette to slow speed
            pipet.aspirate(air_gap_vol, source.top(z = -2),
                           rate = reagent.flow_rate_aspirate)  # air gap
        # GO TO DESTINATION
        drop = dest.top(z = disp_height).move(Point(x = x_offset[1]))
        pipet.dispense(vol + air_gap_vol, drop,
                       rate = reagent.flow_rate_dispense)  # dispense all
        ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent
        if blow_out == True:
            pipet.blow_out(dest.top(z = -2))
        if touch_tip == True:
            pipet.touch_tip(speed = 20, v_offset = -5, radius = 0.9)


    def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height,
    x_offset, source_height = 3):
        '''
        Function for mixing a given [vol] in the same [location] a x number of [rounds].
        blow_out: Blow out optional [True,False]
        x_offset = [source, destination]
        source_height: height from bottom to aspirate
        mix_height: height from bottom to dispense
        '''
        if mix_height == 0:
            mix_height = 3
        pipet.aspirate(1, location=location.bottom(
            z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate)
        for _ in range(rounds):
            pipet.aspirate(vol, location=location.bottom(
                z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate)
            pipet.dispense(vol, location=location.bottom(
                z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense)
        pipet.dispense(1, location=location.bottom(
            z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense)
        if blow_out == True:
            pipet.blow_out(location.top(z=-2))  # Blow out

    def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.5, extra_volume = 50):
        nonlocal ctx
        ctx.comment('Remaining volume ' + str(reagent.vol_well) +
                    '< needed volume ' + str(aspirate_volume) + '?')
        if reagent.vol_well < aspirate_volume + extra_volume:
            reagent.unused.append(reagent.vol_well)
            ctx.comment('Next column should be picked')
            ctx.comment('Previous to change: ' + str(reagent.col))
            # column selector position; intialize to required number
            reagent.col = reagent.col + 1
            ctx.comment(str('After change: ' + str(reagent.col)))
            reagent.vol_well = reagent.vol_well_original
            ctx.comment('New volume:' + str(reagent.vol_well))
            height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area
                    #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Remaining volume:' + str(reagent.vol_well))
            if height < min_height:
                height = min_height
            col_change = True
        else:
            height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Calculated height is ' + str(height))
            if height < min_height:
                height = min_height
            ctx.comment('Used height is ' + str(height))
            col_change = False
        return height, col_change

    ####################################
    # load labware and modules
    # 24 well rack
    tuberack = ctx.load_labware(
        'opentrons_24_aluminumblock_generic_2ml_screwcap', '2',
        'Bloque Aluminio opentrons 24 screwcaps 2000 µL ')

    ############################################
    # tempdeck
    tempdeck = ctx.load_module('tempdeck', '4')
    tempdeck.set_temperature(temperature)

    ##################################
    # qPCR plate - final plate, goes to PCR
    qpcr_plate = tempdeck.load_labware(
        'abi_fast_qpcr_96_alum_opentrons_100ul',
        'chilled qPCR final plate')

    ##################################
    # Sample plate - comes from B
    source_plate = ctx.load_labware(
        "kingfisher_std_96_wellplate_550ul", '1',
        'chilled KF plate with elutions (alum opentrons)')
    samples = source_plate.wells()[:NUM_SAMPLES]

    ##################################
    # Load Tipracks
    tips20 = [
        ctx.load_labware('opentrons_96_filtertiprack_20ul', slot)
        for slot in ['5']
    ]

    tips200 = [
        ctx.load_labware('opentrons_96_filtertiprack_200ul', slot)
        for slot in ['6']
    ]

    ################################################################################
    # Declare which reagents are in each reservoir as well as deepwell and elution plate
    MMIX.reagent_reservoir = tuberack.rows()[0][:MMIX.num_wells] # 1 row, 2 columns (first ones)
    ctx.comment('Wells in: '+ str(tuberack.rows()[0][:MMIX.num_wells]) + ' element: '+str(MMIX.reagent_reservoir[MMIX.col]))
    # setup up sample sources and destinations
    samples = source_plate.wells()[:NUM_SAMPLES]
    samples_multi = source_plate.rows()[0][:num_cols]
    pcr_wells = qpcr_plate.wells()[:NUM_SAMPLES]
    pcr_wells_multi = qpcr_plate.rows()[0][:num_cols]
    # Divide destination wells in small groups for P300 pipette
    dests = list(divide_destinations(pcr_wells, size_transfer))


    # pipettes
    m20 = ctx.load_instrument(
        'p20_multi_gen2', mount='right', tip_racks=tips20)
    p300 = ctx.load_instrument(
        'p300_single_gen2', mount='left', tip_racks=tips200)

    # used tip counter and set maximum tips available
    tip_track = {
        'counts': {p300: 0,
                   m20: 0}
    }

    ############################################################################
    # STEP 1: Transfer Master MIX
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        p300.pick_up_tip()

        used_vol=[]
        for dest in dests:
            aspirate_volume=volume_mmix * len(dest) + extra_dispensal
            [pickup_height,col_change]=calc_height(MMIX, area_section_screwcap, aspirate_volume)
            used_vol_temp = distribute_custom(
            p300, volume = volume_mmix, src = MMIX.reagent_reservoir[MMIX.col], dest = dest,
            waste_pool = MMIX.reagent_reservoir[MMIX.col], pickup_height = pickup_height,
            extra_dispensal = extra_dispensal)
            used_vol.append(used_vol_temp)
        p300.drop_tip()
        tip_track['counts'][p300]+=1
        #MMIX.unused_two = MMIX.vol_well

        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' +
                    STEPS[STEP]['description'] + ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 2: TRANSFER Samples
    ############################################################################

    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        ctx.comment('pcr_wells')
        #Loop over defined wells
        for s, d in zip(samples_multi, pcr_wells_multi):
            m20.pick_up_tip()
            #Source samples
            move_vol_multichannel(m20, reagent = Samples, source = s, dest = d,
            vol = volume_sample, air_gap_vol = air_gap_sample, x_offset = x_offset,
                   pickup_height = 0.2, disp_height = -10, rinse = False,
                   blow_out=True, touch_tip=False)
            m20.drop_tip()
            tip_track['counts'][m20]+=8

        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' +
                    STEPS[STEP]['description'] + ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    # Export the time log to a tsv file
    if not ctx.is_simulating():
        with open(file_path, 'w') as f:
            f.write('STEP\texecution\tdescription\twait_time\texecution_time\n')
            for key in STEPS.keys():
                row = str(key)
                for key2 in STEPS[key].keys():
                    row += '\t' + format(STEPS[key][key2])
                f.write(row + '\n')
        f.close()

    ############################################################################
    # Light flash end of program
    gpio.set_rail_lights(False)
    time.sleep(2)
    #os.system('mpg123 -f -8000 /var/lib/jupyter/notebooks/toreador.mp3 &')
    for i in range(3):
        gpio.set_rail_lights(False)
        gpio.set_button_light(1, 0, 0)
        time.sleep(0.3)
        gpio.set_rail_lights(True)
        gpio.set_button_light(0, 0, 1)
        time.sleep(0.3)
        gpio.set_rail_lights(False)
    gpio.set_button_light(0, 1, 0)
    ctx.comment('Finished! \nMove plate to PCR')

    if STEPS[1]['Execute'] == True:
        total_used_vol = np.sum(used_vol)
        total_needed_volume = total_used_vol
        ctx.comment('Total Master Mix used volume is: ' + str(total_used_vol) + '\u03BCl.')
        ctx.comment('Needed Master Mix volume is ' +
                    str(total_needed_volume + extra_dispensal*len(dests)) +'\u03BCl')
        ctx.comment('Used Master Mix volumes per run are: ' + str(used_vol) + '\u03BCl.')
        ctx.comment('Master Mix Volume remaining in tubes is: ' +
                    format(np.sum(MMIX.unused)+extra_dispensal*len(dests)+MMIX.vol_well) + '\u03BCl.')
        ctx.comment('200 ul Used tips in total: ' + str(tip_track['counts'][p300]))
        ctx.comment('200 ul Used racks in total: ' + str(tip_track['counts'][p300] / 96))

    if STEPS[2]['Execute'] == True:
        ctx.comment('20 ul Used tips in total: ' + str(tip_track['counts'][m20]))
        ctx.comment('20 ul Used racks in total: ' + str(tip_track['counts'][m20] / 96))
def run(ctx: protocol_api.ProtocolContext):

    # load labware
    dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '2',
                                  '96-deepwell sample plate')
    tipracks1000 = [
        ctx.load_labware('opentrons_96_filtertiprack_1000ul', '1',
                         '1000µl filter tiprack')
    ]

    # load pipette
    p1000 = ctx.load_instrument('p1000_single_gen2',
                                'right',
                                tip_racks=tipracks1000)

    tip_log = {'count': {}}
    folder_path = '/data/A'
    tip_file_path = folder_path + '/tip_log.json'
    if TIP_TRACK and not ctx.is_simulating():
        if os.path.isfile(tip_file_path):
            with open(tip_file_path) as json_file:
                data = json.load(json_file)
                if 'tips1000' in data:
                    tip_log['count'][p1000] = data['tips1000']
                else:
                    tip_log['count'][p1000] = 0
    else:
        tip_log['count'] = {p1000: 0}

    tip_log['tips'] = {
        p1000: [tip for rack in tipracks1000 for tip in rack.wells()]
    }
    tip_log['max'] = {pip: len(tip_log['tips'][pip]) for pip in [p1000]}

    def pick_up(pip):
        nonlocal tip_log
        if tip_log['count'][pip] == tip_log['max'][pip]:
            ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \
resuming.')
            pip.reset_tipracks()
            tip_log['count'][pip] = 0
        pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]])
        tip_log['count'][pip] += 1

    # pool samples
    for i in range(math.ceil(NUM_SAMPLES / 2)):
        if NUM_SAMPLES % 2 != 0 and i == math.ceil(NUM_SAMPLES / 2) - 1:
            pool_source_set = [dest_plate.wells()[NUM_SAMPLES]]
            vol = SAMPLE_VOLUME * 2
        else:
            pool_source_set = dest_plate.wells()[i * 2:i * 2 + 2]
            vol = SAMPLE_VOLUME
        for s in pool_source_set:
            pick_up(p1000)
            p1000.transfer(vol,
                           s,
                           dest_plate.wells()[i + 64],
                           air_gap=20,
                           new_tip='never')
            p1000.air_gap(100)
            p1000.drop_tip()

    ctx.comment('Move deepwell plate (slot 2) to Station B for RNA \
extraction.')

    # track final used tip
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        data = {'tips1000': tip_log['count'][p1000]}
        with open(tip_file_path, 'w') as outfile:
            json.dump(data, outfile)
Example #13
0
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))
Example #14
0
def run(ctx: protocol_api.ProtocolContext):
    ctx.comment('Actual used columns: ' + str(num_cols))
    # Define the STEPS of the protocol
    STEP = 0
    STEPS = {  # Dictionary with STEP activation, description, and times

        1: {'Execute': True, 'description': 'Add 300 ul Wash Buffer 1 - Round 1'},
        2: {'Execute': True, 'description': 'Add 300 ul Wash Buffer 1 - Round 2'},
        3: {'Execute': True, 'description': 'Add 450 ul Wash Buffer 2 - Round 1'},
        4: {'Execute': True, 'description': 'Add 450 ul Wash Buffer 2 - Round 2'},
        5: {'Execute': True, 'description': 'Add 50 ul Elution Buffer'},
    }

    for s in STEPS:  # Create an empty wait_time
        if 'wait_time' not in STEPS[s]:
            STEPS[s]['wait_time'] = 0
    folder_path = '/var/lib/jupyter/notebooks/'+run_id
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        file_path = folder_path + '/KB_PlateFilling_pathogen_time_log.txt'

    # Define Reagents as objects with their properties
    class Reagent:
        def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse,
                     reagent_reservoir_volume, delay, num_wells, h_cono, v_fondo,
                      tip_recycling = 'none'):
            self.name = name
            self.flow_rate_aspirate = flow_rate_aspirate
            self.flow_rate_dispense = flow_rate_dispense
            self.rinse = bool(rinse)
            self.reagent_reservoir_volume = reagent_reservoir_volume
            self.delay = delay #Delay of reagent in dispense
            self.num_wells = num_wells
            self.col = 0
            self.vol_well = 0
            self.h_cono = h_cono
            self.v_cono = v_fondo
            self.unused=[]
            self.tip_recycling = tip_recycling
            self.vol_well_original = reagent_reservoir_volume / num_wells


    # Reagents and their characteristics
    WashBuffer1 = Reagent(name='Wash Buffer 1',
                          flow_rate_aspirate=0.75,
                          flow_rate_dispense=1,
                          rinse=True,
                          delay=2,
                          reagent_reservoir_volume=100000,
                          num_wells=1,
                          h_cono=0,
                          v_fondo=0)  # Flat surface

    WashBuffer2 = Reagent(name='Wash Buffer 1',
                          flow_rate_aspirate=0.75,
                          flow_rate_dispense=1,
                          rinse=True,
                          delay=2,
                          reagent_reservoir_volume=100000,
                          num_wells=1,
                          h_cono=0,
                          v_fondo=0)  # Flat surface

    ElutionBuffer = Reagent(name='Elution Buffer',
                            flow_rate_aspirate=1,
                            flow_rate_dispense=1,
                            rinse=False,
                            delay=0,
                            reagent_reservoir_volume=50*NUM_SAMPLES,
                            num_wells=1,
                            h_cono=1.95,
                            v_fondo=695)  # Prismatic

    WashBuffer1.vol_well = WashBuffer1.vol_well_original
    WashBuffer2.vol_well = WashBuffer2.vol_well_original
    ElutionBuffer.vol_well = ElutionBuffer.vol_well_original

    ##################
    # Custom functions
    def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol, x_offset,
                       pickup_height, rinse, disp_height, blow_out, touch_tip):
        '''
        x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1]
        pickup_height: height from bottom where volume
        rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer
        disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered
        blow_out, touch_tip: if True they will be done after dispensing
        '''
        # Rinse before aspirating
        if rinse == True:
            custom_mix(pipet, reagent, location = source, vol = vol,
                       rounds = 2, blow_out = True, mix_height = 0,
                       x_offset = x_offset)
        # SOURCE
        s = source.bottom(pickup_height).move(Point(x = x_offset[0]))
        pipet.aspirate(vol, s, rate = reagent.flow_rate_aspirate)  # aspirate liquid
        if air_gap_vol != 0:  # If there is air_gap_vol, switch pipette to slow speed
            pipet.aspirate(air_gap_vol, source.top(z = -2),
                           rate = reagent.flow_rate_aspirate)  # air gap
        # GO TO DESTINATION
        drop = dest.top(z = disp_height).move(Point(x = x_offset[1]))
        pipet.dispense(vol + air_gap_vol, drop,
                       rate = reagent.flow_rate_dispense)  # dispense all
        ctx.delay(seconds = reagent.delay) # pause for x seconds depending on reagent
        if blow_out == True:
            pipet.blow_out(dest.top(z = -2))
        if touch_tip == True:
            pipet.touch_tip(radius=0.9, speed = 20, v_offset = -5) #radius here is 0.9


    def custom_mix(pipet, reagent, location, vol, rounds, blow_out, mix_height,
    x_offset, source_height = 3):
        '''
        Function for mixing a given [vol] in the same [location] a x number of [rounds].
        blow_out: Blow out optional [True,False]
        x_offset = [source, destination]
        source_height: height from bottom to aspirate
        mix_height: height from bottom to dispense
        '''
        if mix_height == 0:
            mix_height = 3
        pipet.aspirate(1, location=location.bottom(
            z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate)
        for _ in range(rounds):
            pipet.aspirate(vol, location=location.bottom(
                z=source_height).move(Point(x=x_offset[0])), rate=reagent.flow_rate_aspirate)
            pipet.dispense(vol, location=location.bottom(
                z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense)
        pipet.dispense(1, location=location.bottom(
            z=mix_height).move(Point(x=x_offset[1])), rate=reagent.flow_rate_dispense)
        if blow_out == True:
            pipet.blow_out(location.top(z=-2))  # Blow out

    def calc_height(reagent, cross_section_area, aspirate_volume, min_height = 0.5, extra_volume = 50):
        nonlocal ctx
        ctx.comment('Remaining volume ' + str(reagent.vol_well) +
                    '< needed volume ' + str(aspirate_volume) + '?')
        if reagent.vol_well < aspirate_volume + extra_volume:
            reagent.unused.append(reagent.vol_well)
            ctx.comment('Next column should be picked')
            ctx.comment('Previous to change: ' + str(reagent.col))
            # column selector position; intialize to required number
            reagent.col = reagent.col + 1
            ctx.comment(str('After change: ' + str(reagent.col)))
            reagent.vol_well = reagent.vol_well_original
            ctx.comment('New volume:' + str(reagent.vol_well))
            height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area
                    #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Remaining volume:' + str(reagent.vol_well))
            if height < min_height:
                height = min_height
            col_change = True
        else:
            height = (reagent.vol_well - aspirate_volume - reagent.v_cono) / cross_section_area #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Calculated height is ' + str(height))
            if height < min_height:
                height = min_height
            ctx.comment('Used height is ' + str(height))
            col_change = False
        return height, col_change

    ##########
    # pick up tip and if there is none left, prompt user for a new rack
    def pick_up(pip):
        nonlocal tip_track
        if not ctx.is_simulating():
            if tip_track['counts'][pip] == tip_track['maxes'][pip]:
                ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \
                resuming.')
                pip.reset_tipracks()
                tip_track['counts'][pip] = 0
        pip.pick_up_tip()
    ##########

    def find_side(col):
        '''
        Detects if the current column has the magnet at its left or right side
        '''
        if col % 2 == 0:
            side = -1  # left
        else:
            side = 1
        return side


####################################
    # load labware and modules

    # 12 well rack
    ####################################
    reagent_res = ctx.load_labware(
        'nest_12_reservoir_15ml', '3', 'Reservoir 12 channel, column 1')

    # WashBuffer1 reservoir
    ####################################
    WashBuffer1_reservoir = ctx.load_labware(
        'nalgene_1_reservoir_300000ul', '2', 'Ethanol 80% reservoir')

    # WashBuffer2 reservoir
    ####################################
    WashBuffer2_reservoir = ctx.load_labware(
        'nalgene_1_reservoir_300000ul', '11', 'WashBuffer reservoir')

    # Wash Buffer 1 300ul Deepwell plate
    ############################################
    WashBuffer1_300ul_plate1 = ctx.load_labware(
        'kf_96_wellplate_2400ul', '1', 'Wash Buffer 1 Deepwell plate 1')

    # Wash Buffer 1 300ul Deepwell plate
    ############################################
    WashBuffer1_300ul_plate2 = ctx.load_labware(
        'kf_96_wellplate_2400ul', '4', 'Wash Buffer 1 Deepwell plate 2')

    # Wash Buffer 2 450ul Deepwell plate
    ############################################
    WashBuffer2_450ul_plate1 = ctx.load_labware(
        'kf_96_wellplate_2400ul', '7', 'Wash Buffer 2 Deepwell plate 1')

    # Wash Buffer 2 450ul Deepwell plate
    ############################################
    WashBuffer2_450ul_plate2 = ctx.load_labware(
        'kf_96_wellplate_2400ul', '10', 'Wash Buffer 2 Deepwell plate 2')

    # Elution Deepwell plate
    ############################################
    ElutionBuffer_50ul_plate = ctx.load_labware(
        'kingfisher_std_96_wellplate_550ul', '6', 'Elution Buffer 50 ul STD plate')


####################################
    # Load tip_racks
    tips300 = [ctx.load_labware('opentrons_96_tiprack_300ul', slot, '200µl filter tiprack')
               for slot in ['8']]

################################################################################
    # Declare which reagents are in each reservoir as well as deepwell and elution plate
    WashBuffer1.reagent_reservoir = WashBuffer1_reservoir.wells()[0]
    WashBuffer2.reagent_reservoir = WashBuffer2_reservoir.wells()[0]
    ElutionBuffer.reagent_reservoir = reagent_res.rows()[0][0]

    # columns in destination plates to be filled depending the number of samples
    wb1plate1_destination = WashBuffer1_300ul_plate1.rows()[0][:num_cols]
    wb1plate2_destination = WashBuffer1_300ul_plate2.rows()[0][:num_cols]
    wb2plate1_destination = WashBuffer2_450ul_plate1.rows()[0][:num_cols]
    wb2plate2_destination = WashBuffer2_450ul_plate2.rows()[0][:num_cols]
    elutionbuffer_destination = ElutionBuffer_50ul_plate.rows()[0][:num_cols]

    # pipette
    m300 = ctx.load_instrument(
        'p300_multi_gen2', 'right', tip_racks=tips300)  # Load multi pipette

    # used tip counter and set maximum tips available
    tip_track = {
        'counts': {m300: 0},
        'maxes': {m300: len(tips300)*96}
    }

    ############################################################################
    # STEP 1 Filling with WashBuffer1 plate 1
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()

        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')

        wash_buffer_vol = [150, 150]
        rinse = False  # Only first time

        ########
        # Wash buffer dispense
        for i in range(num_cols):
            if not m300.hw_pipette['has_tip']:
                pick_up(m300)
            for j, transfer_vol in enumerate(wash_buffer_vol):
                if (i == 0 and j == 0):
                    rinse = True #Rinse only first transfer
                else:
                    rinse = False
                move_vol_multichannel(m300, reagent = WashBuffer1, source = WashBuffer1.reagent_reservoir,
                               dest = wb1plate1_destination[i], vol = transfer_vol,
                               air_gap_vol = air_gap_vol, x_offset = x_offset,
                               pickup_height = 1, rinse = rinse, disp_height = -2,
                               blow_out = True, touch_tip = True)
        m300.drop_tip(home_after=True)
        tip_track['counts'][m300] += 8
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' +
                    STEPS[STEP]['description'] + ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 2 Filling with WashBuffer1 plate 2
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()

        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')

        wash_buffer_vol = [150, 150]
        rinse = False  # Only first time

        ########
        # Wash buffer dispense
        for i in range(num_cols):
            if not m300.hw_pipette['has_tip']:
                pick_up(m300)
            for j, transfer_vol in enumerate(wash_buffer_vol):
                if (i == 0 and j == 0):
                    rinse = True
                else:
                    rinse = False
                move_vol_multichannel(m300, reagent = WashBuffer1, source = WashBuffer1.reagent_reservoir,
                               dest = wb1plate2_destination[i], vol = transfer_vol,
                               air_gap_vol = air_gap_vol, x_offset = x_offset,
                               pickup_height = 1, rinse = rinse, disp_height = -2,
                               blow_out = True, touch_tip = True)
        m300.drop_tip(home_after=True)
        tip_track['counts'][m300] += 8
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' +
                    STEPS[STEP]['description'] + ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 3 Filling with WashBuffer2 plate 1
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()

        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')

        wash_buffer_vol = [150, 150, 150]
        rinse = False  # Only first time

        ########
        # Wash buffer dispense
        for i in range(num_cols):
            if not m300.hw_pipette['has_tip']:
                pick_up(m300)
            for j, transfer_vol in enumerate(wash_buffer_vol):
                if (i == 0 and j == 0):
                    rinse = True
                else:
                    rinse = False
                move_vol_multichannel(m300, reagent = WashBuffer2, source = WashBuffer2.reagent_reservoir,
                               dest = wb2plate1_destination[i], vol = transfer_vol,
                               air_gap_vol = air_gap_vol, x_offset = x_offset,
                               pickup_height = 1, rinse = rinse, disp_height = -2,
                               blow_out = True, touch_tip = True)
        m300.drop_tip(home_after=True)
        tip_track['counts'][m300] += 8
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' +
                    STEPS[STEP]['description'] + ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 4 Filling with WashBuffer2 plate 2
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()

        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')

        ethanol_vol = [150, 150, 150]
        rinse = False  # Only first time

        ########
        # Ethanol dispense
        for i in range(num_cols):
            if not m300.hw_pipette['has_tip']:
                pick_up(m300)
            for j, transfer_vol in enumerate(ethanol_vol):
                if (i == 0 and j == 0):
                    rinse = True
                else:
                    rinse = False
                move_vol_multichannel(m300, reagent = WashBuffer2, source = WashBuffer2.reagent_reservoir,
                              dest = wb2plate2_destination[i], vol = transfer_vol,
                              air_gap_vol = air_gap_vol, x_offset = x_offset,
                              pickup_height = 1, rinse = rinse, disp_height = -2,
                              blow_out = True, touch_tip = True)
        m300.drop_tip(home_after=True)
        tip_track['counts'][m300] += 8
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' +
                    STEPS[STEP]['description'] + ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 5 Transfer Elution buffer
    ############################################################################

    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')
        # Elution buffer
        ElutionBuffer_vol = [50]

        ########
        # Water or elution buffer
        for i in range(num_cols):
            if not m300.hw_pipette['has_tip']:
                pick_up(m300)
            for transfer_vol in ElutionBuffer_vol:
                # Calculate pickup_height based on remaining volume and shape of container
                [pickup_height, change_col] = calc_height(
                    ElutionBuffer, multi_well_rack_area, transfer_vol * 8)
                ctx.comment(
                    'Aspirate from Reservoir column: ' + str(ElutionBuffer.col))
                ctx.comment('Pickup height is ' + str(pickup_height))
                move_vol_multichannel(m300, reagent = ElutionBuffer, source = ElutionBuffer.reagent_reservoir,
                              dest = elutionbuffer_destination[i], vol = transfer_vol,
                              air_gap_vol = air_gap_vol_elutionbuffer, x_offset = x_offset,
                              pickup_height = pickup_height, rinse = False, disp_height = -2,
                              blow_out = True, touch_tip = False)
        m300.drop_tip(home_after=True)
        tip_track['counts'][m300] += 8
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' +
                    STEPS[STEP]['description'] + ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    # Export the time log to a tsv file
    if not ctx.is_simulating():
        with open(file_path, 'w') as f:
            f.write('STEP\texecution\tdescription\twait_time\texecution_time\n')
            for key in STEPS.keys():
                row = str(key)
                for key2 in STEPS[key].keys():
                    row += '\t' + format(STEPS[key][key2])
                f.write(row + '\n')
        f.close()

    ############################################################################
    # Light flash end of program
    from opentrons.drivers.rpi_drivers import gpio
    for i in range(3):
        gpio.set_rail_lights(False)
        gpio.set_button_light(1, 0, 0)
        time.sleep(0.3)
        gpio.set_rail_lights(True)
        gpio.set_button_light(0, 0, 1)
        time.sleep(0.3)
    gpio.set_button_light(0, 1, 0)
    ctx.comment(
        'Finished! \nMove deepwell plates to KingFisher extractor.')
    ctx.comment('Used tips in total: ' + str(tip_track['counts'][m300]))
    ctx.comment('Used racks in total: ' + str(tip_track['counts'][m300] / 96))
Example #15
0
def run(ctx: protocol_api.ProtocolContext):

    [csv, m300_mount, p300_mount, temp_mod_temp, asp_rate_step1,
     pbs_dispense_rate,
     incubation_time, first_media_x, second_media_y, track_tips
     ] = get_values(  # noqa: F821
        "csv", "m300_mount", "p300_mount", "temp_mod_temp",
        "asp_rate_step1", "pbs_dispense_rate",
        "incubation_time", "first_media_x", "second_media_y", "track_tips")

    # LABWARE
    temp_mod = ctx.load_module('temperature module gen2', '10')
    reagents = ctx.load_labware('nest_12_reservoir_15ml', '11')
    waste_res = ctx.load_labware('nest_12_reservoir_15ml', '7')
    plate = temp_mod.load_labware(
                'corning_96_wellplate_360ul_flat', '10')

    # TIPRACKS
    tipracks = [ctx.load_labware('opentrons_96_filtertiprack_200ul', slot)
                for slot in ['4', '5', '6']]

    # INSTRUMENTS
    p300 = ctx.load_instrument('p300_single_gen2',
                               p300_mount,
                               tip_racks=tipracks)
    m300 = ctx.load_instrument('p300_multi_gen2',
                               m300_mount,
                               tip_racks=tipracks)

    tips_by_col = [tip for rack in tipracks
                   for col in rack.columns() for tip in col[::-1]]
    tip_cols = [tips_by_col[i:i+8] for i in range(0, len(tips_by_col), 8)]

    """ TIP-TRACKING BETWEEN RUNS. """
    total_tip_cols = 36

    file_path = '/data/csv/tiptracking.json'
    file_dir = os.path.dirname(file_path)

    tips_by_col = [tip for rack in tipracks
                   for col in rack.columns() for tip in col[::-1]]
    tip_cols = [tips_by_col[i:i+8] for i in range(0, len(tips_by_col), 8)]

    if track_tips and not ctx.is_simulating():
        # check for file directory
        if not os.path.exists(file_dir):
            os.makedirs(file_dir)
        # if no file, then use standard tip_chunks definition, and the
        # end of the code will write to updated tip_chunks list to
        # the file created. This if statement handles the case in which
        # tip tracking is selected for the first time.
        if not os.path.isfile(file_path):
            tip_chunks = [tips_by_col[i:i+8] for i in range(0,
                          len(tips_by_col), 8)]
        else:
            # grab nested list tip_chunks from file.
            source = open(file_path, 'rb').read()
            # see below for conversion of tip_chunks nested list to bools.
            # in order to dump the well objects in tip_chunks to a json file,
            # they had to be serializable (int, string, bool). The end of the
            # protocol does this conversion.
            tip_bool_chunks = json.loads(source)

            # convert bools back to well objects to use.
            tip_chunks = [[] for _ in range(total_tip_cols)]
            for i, (bool_chunk, tip_chunk) in enumerate(zip(tip_bool_chunks,
                                                            tip_cols)):
                if len(bool_chunk) == 0:
                    continue
                for true_tip, tip_loc in zip(bool_chunk, tip_chunk):
                    if true_tip:
                        tip_chunks[i].append(tip_loc)
                    else:
                        continue

    else:
        # standard definition of tip_chunks if not tracking tips.
        tip_chunks = [tips_by_col[i:i+8] for i in range(0,
                      len(tips_by_col), 8)]
    """PROTOCOL BEGINS """
    csv_rows = [[val.strip() for val in line.split(',')]
                for line in csv.splitlines()
                if line.split(',')[0].strip()][1:]

    """FIND INVOLVED WELLS"""
    values_from_csv = []
    wells_from_csv = []
    for row in csv_rows:
        well, value = row[:2]
        value = int(value)
        values_from_csv.append(value)
        wells_from_csv.append(well)

    # create nested list of all values in csv (by column).
    value_chunk_cols = [values_from_csv[i:i+8]
                        for i in range(0, len(values_from_csv), 8)]
    list_well_tips = []

    """CREATE A LIST OF # TIPS FOR EACH WELL"""
    start_point = 0
    tip_count = 0
    for i, chunk in enumerate(value_chunk_cols):
        start_point = 0

        # check for the values in each column.
        # if we find a well with a value of 85 or greater,
        # use that index (j) as the starting point and see how many values
        # after that are also greater than 85. Once we don't find one,
        # break.
        for j, value in enumerate(chunk[start_point:]):
            if value >= 85:
                for check_values in chunk[j:]:
                    if check_values >= 85:
                        tip_count += 1
                    else:
                        break

                list_well_tips.append(tip_count)
                tip_count = 0
                continue

            else:
                list_well_tips.append(0)

    # create a dictionary which says how many tips go to each well.
    # For example, if the entire first column has values higher than 85,
    # the first value in the dictionary will be "A1: 8". If first four wells
    # in column 2 are greater than 85, "B1:4".
    dict_tips_per_well = {}
    tip_ctr = 0
    for j, (well, num_tips) in enumerate(zip(wells_from_csv,
                                             list_well_tips)):
        if tip_ctr > 0:
            tip_ctr -= 1
            continue

        if num_tips > 0:
            tip_ctr = num_tips - 1
            dict_tips_per_well[well] = num_tips

    # print('\n\n', dict_tips_per_well, '\n\n')

    """PICKUP FUNCTION"""
    def pick_up(num_channels_per_pickup):
        nonlocal tip_chunks
        if num_channels_per_pickup > 1:
            pip = m300
        else:
            pip = p300
        try:
            col = 0
            # based on the demand of the next well (1-8 tips), this for loop
            # will find the first available column having adequate number
            # of tips starting from the first. If the tip pick up order is:
            # 6, 8, 2 for the first 3 pick ups, then 6 tips will be taken from
            # column 1, 8 tips from column 2, and 2 tips back from column 1.
            # efficient tip pick up instead of "throwing away" a whole column
            # after pick up.
            for _ in range(36):
                if num_channels_per_pickup <= len(tip_chunks[col]):
                    break
                else:
                    col += 1
            pip.pick_up_tip(tip_chunks[col][num_channels_per_pickup-1])

            # remove as many tips as we picked up in that column
            # from the 0 index.
            for _ in range(num_channels_per_pickup):
                tip_chunks[col].pop(0)

        # replace tip exception
        except IndexError:
            ctx.pause("Replace empty tip racks on slots 4, 5, and 6")
            pip.reset_tipracks()
            tip_chunks = [tips_by_col[i:i+8] for i in range(0,
                          len(tips_by_col), 8)]
            col = 0
            for _ in range(36):
                if num_channels_per_pickup <= len(tip_chunks[col]):
                    break
                else:
                    col += 1

            pip.pick_up_tip(tip_chunks[col][num_channels_per_pickup-1])

            for _ in range(num_channels_per_pickup):
                tip_chunks[col].pop(0)

                if len(tip_chunks[col]) == 0:
                    tip_chunks.remove(tip_chunks[col])

    # DUMP WASTE
    vol_ctr = 0
    waste_well = 0

    # move to next well in reservoir once we fill one.
    def check_waste_vol(vol):
        nonlocal vol_ctr
        nonlocal waste_well
        vol_ctr += vol
        if vol_ctr > 12000:
            waste_well += 1
            vol_ctr = 0
    waste = waste_res.wells()[waste_well]
    temp_mod.set_temperature(temp_mod_temp)

    ctx.pause("""
    Ensure temperature module is at correct temperature, then,
    select "Resume" on the Opentrons app.
    """)

    # REAGENTS
    pbs = reagents.wells()[0]
    trypsin = reagents.wells()[1]
    media = reagents.wells()[-1]

    airgap = 20

    ctx.comment("MOVING INCLUDED WELLS TO WASTE")
    for i, well in enumerate(dict_tips_per_well):
        num_tips = dict_tips_per_well[well]
        plate_well = plate.wells_by_name()[well]
        if num_tips > 1:
            pip = m300
        else:
            pip = p300

        pick_up(num_tips)
        # aspirate from side so as to not disturb cell culture.
        pip.aspirate(200, plate_well.bottom(z=1).move(
                Point(x=(plate_well.diameter/2-2))), rate=asp_rate_step1)
        pip.dispense(200, waste)
        check_waste_vol(200)
        pip.air_gap(airgap)
        pip.drop_tip()
        ctx.comment('\n')
    ctx.comment("\n\n\nMOVING PBS TO PLATE")
    for i, well in enumerate(dict_tips_per_well):
        num_tips = dict_tips_per_well[well]
        plate_well = plate.wells_by_name()[well]
        if num_tips > 1:
            pip = m300
        else:
            pip = p300

        pick_up(num_tips)
        pip.aspirate(150, pbs, rate=pbs_dispense_rate)
        pip.dispense(150, plate_well.bottom(z=1).move(
                Point(x=(plate_well.diameter/2-2))))
        pip.air_gap(airgap)
        pip.drop_tip()
        ctx.comment('\n')

    ctx.comment("\n\n\nREMOVING PBS FROM PLATE")
    for i, well in enumerate(dict_tips_per_well):
        num_tips = dict_tips_per_well[well]
        plate_well = plate.wells_by_name()[well]
        if num_tips > 1:
            pip = m300
        else:
            pip = p300

        pick_up(num_tips)
        pip.aspirate(175, plate_well.bottom(z=1).move(
                Point(x=(plate_well.diameter/2-2))))
        pip.dispense(175, waste)
        pip.air_gap(airgap)
        pip.drop_tip()
        ctx.comment('\n')

    ctx.comment("\n\n\nMOVING TRYPSIN TO PLATE")
    for i, well in enumerate(dict_tips_per_well):

        num_tips = dict_tips_per_well[well]
        plate_well = plate.wells_by_name()[well]
        if num_tips > 1:
            pip = m300
        else:
            pip = p300

        pick_up(num_tips)
        pip.aspirate(25, trypsin)
        pip.dispense(25, plate_well)
        pip.blow_out()
        pip.touch_tip()
        pip.air_gap(airgap)
        pip.drop_tip()
        ctx.comment('\n')

    ctx.delay(minutes=incubation_time)

    ctx.comment("\n\n\nMOVING MEDIA TO PLATE")
    for i, well in enumerate(dict_tips_per_well):

        num_tips = dict_tips_per_well[well]
        plate_well = plate.wells_by_name()[well]
        if num_tips > 1:
            pip = m300
        else:
            pip = p300

        pick_up(num_tips)
        pip.aspirate(140, media)
        pip.dispense(140, plate_well)
        pip.blow_out()
        pip.touch_tip()
        pip.air_gap(airgap)
        pip.drop_tip()
        ctx.comment('\n')

    ctx.comment("\n\n\nASPIRATE FIRST MEDIA FROM PLATE")
    for i, well in enumerate(dict_tips_per_well):

        num_tips = dict_tips_per_well[well]
        plate_well = plate.wells_by_name()[well]
        if num_tips > 1:
            pip = m300
        else:
            pip = p300

        pick_up(num_tips)
        pip.aspirate(first_media_x, plate_well.bottom(z=1).move(
                Point(x=(plate_well.diameter/2-2))))
        pip.dispense(first_media_x, waste)
        pip.air_gap(airgap)
        pip.drop_tip()
        ctx.comment('\n')

    ctx.comment("\n\n\nDISPENSE SECOND MEDIA TO PLATE")
    for i, well in enumerate(dict_tips_per_well):

        num_tips = dict_tips_per_well[well]
        plate_well = plate.wells_by_name()[well]
        if num_tips > 1:
            pip = m300
        else:
            pip = p300

        pick_up(num_tips)
        pip.aspirate(second_media_y, media)
        pip.dispense(second_media_y, plate_well)
        pip.air_gap(airgap)
        pip.drop_tip()
        ctx.comment('\n')

    tip_data = []
    for i, chunk in enumerate(tip_chunks):
        tip_data.append([])
        if len(chunk) > 0:
            for value in chunk:
                tip_data[i].append(True)
        else:
            continue

    # write to the ot-2 no matter what in case the user would like to start
    # tracking tips for the next run
    if not ctx.is_simulating():
        with open(file_path, 'w') as outfile:
            outfile.write(json.dumps(tip_data))
def run(protocol: protocol_api.ProtocolContext):

    ################### SETTING UP ###################
    ##                                              ##
    ##     ヽ༼ຈل͜ຈ༽ノ LABWARE ヽ༼ຈل͜ຈ༽ノ               ##
    ##                                              ##
    ##################################################

    # Positions are:
    # 10    11      TRASH
    # 7     8       9
    # 4     5       6
    # 1     2       3

    #Modules, plate and MAGNET HEIGHT
    magneto = protocol.load_module("magneticModuleV2", 6)
    deepPlate = magneto.load_labware("eppendorf_96_deepwell_2ml",
                                     label="Deep well")
    magnetHeight = 6.7
    #########################################################################################################
    ##  We have tested appropriate height on GEN1 magdeck for different plates, these are the chosen ones  ##
    ##                                                                                                     ##
    ##  Zymoresearch - "zymoresearch_96_deepwell_2.4ml" - 12.5mm                                           ##
    ##  Eppendorf - "eppendorf_96_deepwell_2ml" - 11.8 mm                                                  ##
    ##  Starlab - "usascientific_96_wellplate_2.4ml_deep" - E2896-1810 11.4mm                              ##
    ##  Macherey-Nagel - - 10mm                                                                            ##
    #########################################################################################################

    #Plates
    reagents = protocol.load_labware("nest_12_reservoir_15ml",
                                     5,
                                     label="Reagents reservoir")
    waste = protocol.load_labware("nest_12_reservoir_15ml",
                                  9,
                                  label="Liquid waste reservoir")
    outplate = protocol.load_labware("eppendorf96_skirted_150ul",
                                     1,
                                     label="Output plate")
    #Tips - Ordered in the way they are used
    parkingRack = protocol.load_labware("opentrons_96_filtertiprack_200ul", 3)
    tiprack1 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 2)
    tiprack4 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 4)
    tiprack7 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 7)
    tiprack8 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 8)
    tiprack10 = protocol.load_labware("opentrons_96_filtertiprack_200ul", 10)

    tipracks = [tiprack10, tiprack8, tiprack7, tiprack4, tiprack1]
    availableTips = []
    for rack in tipracks:
        for i in range(1, 13):
            availableTips.append(rack["A" + str(i)])
    #To take a tip, just use availableTips.pop() and voila!
    #There are enough tips for 6 samples, and a tiprack will end up half-full
    #Tips are taken from right to left instead of the normal way
    #I am using this dictionary method because it makes it easier to modify later the script for 96 samples/

    #Pipettes
    p300 = protocol.load_instrument("p300_multi_gen2", "left")

    ################### SETTING UP ###################
    ##                                              ##
    ## ヽ༼ຈل͜ຈ༽ノ BASIC VARIABLES ヽ༼ຈل͜ຈ༽ノ             ##
    ##                                              ##
    ##################################################

    #RUN SETTINGS
    runColumns = 3  # Range 1-6. Samples should be a multiple of 8, or you will waste reagents.
    mixRepeats = 15  # Used everytime there is mixing, except when mixing beads.
    beadsMixRepeats = 10  # Used when mixing beads in reservoir. This happens when pipetting column 1, 3 and 6.
    waterMixRepeats = 20  # Used when mixing elute.

    #Mixing settings
    washMixing = 100  # volume (ul)
    waterMixing = 25  # volume (ul)
    bottomHeight = 0.5  # Distance relative to real labware's bottom. Used when removing supernatant and moving elution.
    bottomMixHeight = 0.8  # Distance relative to real labware's bottom. Used when mixing
    SamplePlusProteinaseHeight = 3  # For a volume of 260..
    SamplePlusProteinasePlusBeadsHeight = 5.75  # For a volume of 465...
    generalHeight = 4  # Used always except when mixing beads in reservoir - Units relative to well bottom (mm). For a vol of 280...
    beadsHeight = 10  # Used when mixing beads in reservoir - Units relative to well bottom (mm). When mixing beads in the reagent well - Maybe I should modify this and make it depend on runColumns

    #Incubation times - Minutes
    incubationProteinase = 10
    incubationBeadsNoMagnet = 5  # After adding the beads, we incubate them for 5 min without magnet.
    incubationBeadsMagnet = 5 * 2
    incubationWash = 3 * 2
    incubationDry = 10  # After removing final wash, the beads are left to dry for a while.
    incubationWater = 5  # After mixing. No magnet.
    incubationWaterMagnet = 1 * 2  # After incubationWater.

    #Transference volumes - ul
    originalVol = 140  #This is not used for transfers, it is here mostly to make volumes clearer to understand
    proteinaseVol = 112
    beadsVol = 143.5
    washVol = 280  #Used for WBE and Ethanol1 and Ethanol2
    dilutionVol = 50
    initialSupernatant = originalVol + proteinaseVol + beadsVol

    #Reagent position in reservoir - Positions go from A1 to A12 (Left to right)
    proteinase = reagents["A1"]
    beads = reagents["A2"]
    WBE = reagents["A3"]
    ethanol1 = reagents["A4"]
    ethanol2 = reagents["A5"]
    water = reagents["A12"]
    #########################################################################################################
    ##           Use these formulae to identify how much volume you need based on number of columns        ##
    ##           Each reservoir well has dimensions W=8.20, L=127.76, H=31.40. To make sure there is       ##
    ##           a pool of extra content with 0.5mm of height, add 292ul (300) extra (8.20*127.76*0.05)    ##
    ##                                                                                                     ##
    ##  Proteinase = (896 * runColumns) + 292 ul                                                           ##
    ##  Beads = (1152 * Columns) + 292 ul                                                                  ##
    ##  WBE = (2240 * Columns) + 292 ul                                                                    ##
    ##  Ethanol1 = (2240 * Columns) + 292 ul                                                               ##
    ##  Ethanol2 = (2240 * Columns) + 292 ul                                                               ##
    ##  Water = (640 * Columns) + 292 ul                                                                   ##
    #########################################################################################################

    ################### SETTING UP ###################
    ##                                              ##
    ## ヽ༼ຈل͜ຈ༽ノ ADVANCED VARIABLES ヽ༼ຈل͜ຈ༽ノ          ##
    ##                                              ##
    ##################################################

    #Column accesion list - As we wont work in all physically available columns, this list makes it easier to manage
    columnID = ["A1", "A3", "A5", "A7", "A9",
                "A11"]  # These are the columns that can be used
    columnID = columnID[:
                        runColumns]  # Here we trim the list, to get only the number of columns chosen in runColumns

    #Pipette settings
    tipVolume = 180  # max volume to be transported in a single trip. These tips are 200ul, but if we use the entire volumen, it might touch the filter
    topOffset = -5  # I use this to make sure the tip stays inside the well, to avoid it spilling out while dispensing
    p300.flow_rate.aspirate = 50  # Flow rate in ul / second
    p300.flow_rate.dispense = 150
    p300.flow_rate.blow_out = 200
    #Flows are reduced compared to default values to avoid the production of air or foam during handling.

    #Volume used when mixing beads (ul)
    if runColumns == 1:
        beadsMixing = 140
    else:
        beadsMixing = tipVolume

    ################### SETTING UP ###################
    ##                                              ##
    ##(づ。◕‿‿◕。)づ    FUNCTIONS    (づ。◕‿‿◕。)づ ##
    ##                                              ##
    ##################################################
    def clock(time, stop=False):
        """
        The uncertainty of not knowing how much time is left in an incubation is horrible. This makes it more bearable.
        This function takes the duration of the incubation and outputs a message every minute to keep track of the time more easily
        """
        while time > 0:
            time -= 1
            protocol.delay(minutes=1)
            protocol.comment("Only %s minutes more! Hold in there!" % time)
            if time == 1 and stop == True:
                protocol.pause()

    def meneillo(pipette, pos, reps=10, distance=0.25):
        pipette.move_to(pos)
        for _ in range(reps):
            p300.move_to(pos.move(types.Point(x=distance, y=0, z=0)))
            p300.move_to(pos.move(types.Point(x=-distance, y=0, z=0)))

    def remove_tip(pipette, tip):
        """
        Originally, I had a special behaviour to drop the tips, but I stopped using it.
        I keep this function because it makes it easy to change drop_tip() to return_tip() for test runs
        """
        pipette.drop_tip()

    def retrieve_tip(pipette, tip):
        """
        Originally, the robot took a tip, went to the top of the well it was going to work with, and aspired 20 ul there, but now we are making it aspire 20 ul after taking the tip
        """
        pipette.pick_up_tip(tip)

    def well_mix(vol,
                 loc,
                 reps,
                 height=generalHeight,
                 moveSide=0,
                 bottomHeight=bottomMixHeight):
        """
        Aspirates <vol> from bottom of well and dispenses it from <height> <reps> times
        loc1 is a position at 0.3mm over the bottom of the well
        loc2 is a position in the same x and y posiiton than loc1, but at <height>mm over the bottom of the well
        The idea here is to take liquid to the very bottom and pour it from a higher point, to mix things
        """
        p300.flow_rate.aspirate = 1000
        p300.flow_rate.dispense = 1000
        loc1 = loc.bottom().move(
            types.Point(x=0 + moveSide, y=0, z=bottomHeight))
        loc2 = loc.bottom().move(types.Point(x=0 + moveSide, y=0, z=height))
        for _ in range(reps):
            p300.aspirate(vol, loc1)
            p300.dispense(vol, loc2)
        p300.dispense(20, loc.top(topOffset))
        p300.flow_rate.aspirate = 50
        p300.flow_rate.dispense = 150

    def remove_supernatant(vol,
                           columnID,
                           wasteID,
                           reagentName="Something",
                           pipette=p300):
        """
        While <vol> is bigger than <tipVolume>, it divides it in ceilling(<vol>/<tipVolume>) trips. (So, if it is 396ul, we have 2 180ul trips and a 36ul trip)
        Flow rate is in ul/second
        In the move() function, positive X means 'move to the right'. With the wells we use (Column 1,3,5,7,9 and 11) pellet is placed to the right, so we use a small offset to the left
        """
        p300.flow_rate.aspirate = 20  # In this case we reduce the flow even more to make sure the precipitate is okay. We don't wanna bother the lad
        protocol.comment(
            "\n\nREMOVING STEP: Removing %s ul of supernatant (%s) while magnet is still engaged"
            % (vol, reagentName))
        dump = waste[wasteID]
        for index, ID in enumerate(columnID):
            currentip = parkingRack[ID]
            dump = waste[ID]
            src = deepPlate[ID]
            retrieve_tip(pipette, currentip)
            tvol = vol
            while tvol > tipVolume:
                p300.dispense(20, src.top(topOffset))
                p300.transfer(tipVolume,
                              src.bottom().move(
                                  types.Point(x=-1, y=0, z=bottomHeight)),
                              dump.top(topOffset),
                              new_tip="never")  #Slightly to the left
                meneillo(
                    p300, dump.top(topOffset)
                )  #In case something is TheOppositeOfDense and just drips down
                p300.dispense(
                    20
                )  #Make sure we expel everything that must be expelled. We dont want to move droplets around.
                tvol -= tipVolume
            p300.dispense(20, src.top(topOffset))
            p300.transfer(tvol,
                          src.bottom().move(
                              types.Point(x=-1, y=0, z=bottomHeight)),
                          dump.top(topOffset),
                          new_tip="never")
            meneillo(p300, dump.top(topOffset))
            remove_tip(pipette, currentip)
        p300.flow_rate.aspirate = 50

    def slow_transfer(vol,
                      reagent,
                      columnID,
                      reagentName,
                      mixVol=washMixing,
                      repeats=mixRepeats,
                      mixReagent=False,
                      altura=generalHeight,
                      magnetTime=True,
                      incubationTime=incubationWash,
                      moveSide=0,
                      extraVol=0,
                      pipette=p300,
                      removalStepAfter=True):
        """
        Similar to remove_supernatant, but the other way around. It transfers from point A to point B in <tipVol> ul trips and pours liquid
        from the top, to avoid contaminating the tip while transfering all the necessary volume.
        It also includes incubation and magnet
        """
        protocol.comment(
            "\n\nADDING STEP: Transfering %s ul of %s to samples" %
            (vol, reagentName))
        for index, ID in enumerate(columnID):
            src = reagent
            to = deepPlate[ID]
            currentip = availableTips.pop()
            retrieve_tip(p300, currentip)
            if (mixReagent == True) and (
                    index == 0 or index == 2 or index == 5
            ):  #If the reagent is to be mixed, and we are in column 1, 3 or 6, mix it. We mix three times to make sure we dont have differences
                protocol.comment("Mixing %s" % reagentName)
                well_mix(
                    vol=beadsMixing,
                    loc=beads,
                    reps=beadsMixRepeats,
                    height=beadsHeight,
                    moveSide=0
                )  #We only do this with magnetic beads, that's why we use those variable names
                p300.blow_out(beads.top())
            tvol = vol
            while tvol > tipVolume:
                p300.dispense(20, src.top())
                p300.transfer(tipVolume,
                              src.bottom().move(
                                  types.Point(x=0, y=0, z=bottomHeight)),
                              to.top(topOffset),
                              new_tip="never",
                              air_gap=extraVol)
                protocol.delay(seconds=2)
                p300.blow_out()
                tvol -= tipVolume
            p300.dispense(20, src.top())
            p300.transfer(tvol,
                          src.bottom().move(
                              types.Point(x=0, y=0, z=bottomHeight)),
                          to.center(),
                          new_tip="never",
                          air_gap=extraVol)
            protocol.delay(seconds=2)
            well_mix(vol=mixVol,
                     loc=to,
                     reps=repeats,
                     moveSide=moveSide,
                     height=altura)
            if removalStepAfter == True:
                pipette.drop_tip(parkingRack[ID])
            else:
                remove_tip(pipette, currentip)

        #Incubation
        if magnetTime == True:
            protocol.comment("Engaging magnet")
            magneto.engage(height=magnetHeight)
        protocol.comment("Incubating for %s minutes" % incubationTime)
        clock(time=incubationTime)

    ################# GO, VASILY, GO #################
    ##                                              ##
    ##      ୧༼ಠ益ಠ༽୨    PROTOCOL    ୧༼ಠ益ಠ༽୨        ##
    ##                                              ##
    ##################################################

    magneto.disengage()  #In case it is engaged from previous protocols.
    protocol.comment("We are working with column IDs: %s" % columnID)
    protocol.comment("\n\nSamples should have an initial volume of %s ul" %
                     originalVol)

    #STEP 1: Add Proteinase K/LBF.
    slow_transfer(vol=proteinaseVol,
                  reagent=proteinase,
                  reagentName="Proteinase K/LBF",
                  incubationTime=incubationProteinase,
                  columnID=columnID,
                  magnetTime=False,
                  altura=SamplePlusProteinaseHeight,
                  removalStepAfter=False)
    #INCUBATION 1: 10 min [Total: 10 min]

    #STEP 2: mix magnetic beads, add them to samples and mix sample well. No slow_transfer function for the same reasons as before.
    protocol.comment("\n\nEnough incubation, time to do s t u f f")
    slow_transfer(vol=beadsVol,
                  reagent=beads,
                  reagentName="Magnetic beads",
                  incubationTime=incubationBeadsNoMagnet,
                  columnID=columnID,
                  mixReagent=True,
                  magnetTime=False,
                  extraVol=10,
                  altura=SamplePlusProteinasePlusBeadsHeight)
    #INCUBATION 2: 5 min without magnet [Total: 15 min]
    protocol.comment(
        "Engaging magnet and keeping this incubation going for other %s minutes"
        % incubationBeadsMagnet)
    magneto.engage(height=magnetHeight)
    clock(time=incubationBeadsMagnet, stop=True)
    #INCUBATION 3: 5 min incubation with magnet [Total: 20 min]

    #STEP 3: Remove magnetic beads supernatant
    remove_supernatant(vol=initialSupernatant,
                       wasteID="A1",
                       reagentName="beads and proteinase",
                       columnID=columnID)
    protocol.comment("Disengaging magnet")
    magneto.disengage()

    #STEP 4: Add 280 ul of Wash WBE
    slow_transfer(vol=washVol,
                  reagent=WBE,
                  reagentName="WBE",
                  incubationTime=incubationWash,
                  columnID=columnID)
    #INCUBATION 4: 3 min incubaton with magnet [Total: 23 min]

    #STEP 5: Removing WBE Supernatant
    remove_supernatant(vol=washVol,
                       wasteID="A2",
                       reagentName="WBE",
                       columnID=columnID)
    protocol.comment("Disengaging magnet")
    magneto.disengage()

    #STEP 6: First wash with Eth, tips_transfer)anol
    slow_transfer(vol=washVol,
                  reagent=ethanol1,
                  reagentName="Ethanol 70% (First time)",
                  incubationTime=incubationWash,
                  columnID=columnID)
    #INCUBATION 5: 3 min incubaton with magnet [Total: 26 min]

    #STEP 7: Removing the supernatant of first wash with Ethanol
    remove_supernatant(vol=washVol,
                       wasteID="A3",
                       reagentName="Ethanol 70% (First time)",
                       columnID=columnID)
    protocol.comment("Disengaging magnet")
    magneto.disengage()

    #STEP 8: Second wash with Ethanol
    slow_transfer(vol=washVol,
                  reagent=ethanol2,
                  reagentName="Ethanol 70% (Second time)",
                  incubationTime=incubationWash,
                  columnID=columnID)
    #INCUBATION 6: 3 min incubaton with magnet [Total: 26 min]

    #STEP 9: Removing the supernatant of second wash with Ethanol
    remove_supernatant(vol=washVol,
                       wasteID="A4",
                       reagentName="Ethanol 70% (Second time)",
                       columnID=columnID)
    #INCUBATION 7: 5 min incubaton with magnet [Total: 31 min]
    protocol.comment(
        "This time, I do not disengage the magnet and let the beads dry for %s min"
        % incubationDry)
    clock(time=incubationDry)

    #STEP 10: Diluting samples in 80 ul of RNAse free water
    protocol.comment("Disengaging magnet")
    magneto.disengage()
    protocol.comment("Diluting samples in %s ul of RNAse free water" %
                     dilutionVol)
    slow_transfer(vol=dilutionVol,
                  reagent=water,
                  reagentName="RNAse-free water",
                  incubationTime=incubationWater,
                  columnID=columnID,
                  mixVol=waterMixing,
                  magnetTime=False,
                  repeats=waterMixRepeats)  #Moving tip on top of pellet
    #INCUBATION 8: 5 min incubaton WITHOUT magnet [Total: 36 min]
    protocol.comment("Engaging magnet now!")
    magneto.engage(height=magnetHeight)
    clock(time=incubationWaterMagnet)
    #INCUBATION 9: 3 min incubaton WITH magnet [Total: 39 min]

    #STEP 11: Transfering samples to output plate
    protocol.comment(
        "Transfering DNA to output plate while magnet is still engaged")
    p300.flow_rate.aspirate = 20
    for index, ID in enumerate(columnID):
        currentip = availableTips.pop()
        retrieve_tip(p300, currentip)
        src = deepPlate[ID]
        to = outplate[ID]

        p300.dispense(20, src.top())
        p300.transfer(dilutionVol,
                      src.bottom().move(types.Point(x=-1, y=0,
                                                    z=bottomHeight)),
                      to.bottom(5),
                      new_tip="never")
        protocol.delay(seconds=2)
        p300.dispense(20)
        remove_tip(p300, currentip)

    magneto.disengage()
    protocol.comment("\n\nFecho!")
Example #17
0
def run(protocol: protocol_api.ProtocolContext):
    plate = protocol.load_labware("corning_96_wellplate_360ul_flat", 2)
    tiprack_20 = protocol.load_labware("opentrons_96_tiprack_20ul", 1)
    tiprack_300 = protocol.load_labware("opentrons_96_tiprack_300ul", 4)
    tuberack_1 = protocol.load_labware(
        "opentrons_24_aluminumblock_nest_1.5ml_snapcap", 5)
    tuberack_2 = protocol.load_labware(
        "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical", 3)

    # Pipettes
    p300 = protocol.load_instrument("p300_single",
                                    "Left",
                                    tip_racks=[tiprack_300])
    p20 = protocol.load_instrument("p20_single_gen2",
                                   "Right",
                                   tip_racks=[tiprack_20])

    # Ensures liquids dispensed 10 mm above bottom of well to prevent contamination
    p300.well_bottom_clearance.dispense = 10
    p20.well_bottom_clearance.dispense = 10

    # buffers and enzyme volumes
    s30_buffer = [
        161, 165, 179, 183, 175, 166, 175, 179, 170, 175, 171, 165, 170, 170,
        165, 174, 156, 174, 179, 174, 170, 166, 161, 170, 166, 161, 170
    ]

    pal = [
        5, 10, 1, 1, 1, 10, 5, 5, 10, 5, 5, 1, 5, 1, 10, 1, 10, 10, 1, 1, 1, 5,
        10, 10, 5, 10, 5
    ]

    four_cl = [
        10, 10, 5, 1, 5, 5, 5, 1, 1, 1, 5, 10, 10, 5, 1, 1, 10, 1, 1, 10, 10,
        10, 5, 5, 5, 10, 1
    ]

    sts = [
        10, 1, 1, 1, 5, 5, 1, 1, 5, 5, 5, 10, 1, 10, 10, 10, 10, 1, 5, 1, 5, 5,
        10, 1, 10, 5, 10
    ]

    # S30 buffer
    p300.transfer(
        s30_buffer,
        tuberack_2['A1'],
        plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'A2', 'B2',
                    'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'A3', 'B3', 'C3', 'D3',
                    'E3', 'F3', 'G3', 'H3', 'A4', 'B4', 'C4'),
        touch_tip=True
    )  # Touches tip on side of the well to ensure no liquid on tip
    # ATP 2 ul each well
    p20.transfer(2,
                 tuberack_1['A1'],
                 plate.columns('1', '2', '3'),
                 touch_tip=True)
    p20.transfer(2,
                 tuberack_1['A1'],
                 plate.wells('A4', 'B4', 'C4'),
                 touch_tip=True)

    # CoA 2 ul each well
    p20.transfer(2,
                 tuberack_1['A2'],
                 plate.columns('1', '2', '3'),
                 touch_tip=True)
    p20.transfer(2,
                 tuberack_1['A2'],
                 plate.wells('A4', 'B4', 'C4'),
                 touch_tip=True)

    # Malonyl-CoA 8 ul each well
    p20.transfer(8,
                 tuberack_1['A3'],
                 plate.columns('1', '2', '3'),
                 touch_tip=True)
    p20.transfer(8,
                 tuberack_1['A3'],
                 plate.wells('A4', 'B4', 'C4'),
                 touch_tip=True)
    # PAL ul
    p20.transfer(pal,
                 tuberack_1['A5'],
                 plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1',
                             'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2',
                             'A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3',
                             'A4', 'B4', 'C4'),
                 touch_tip=True)
    # 4CL ul
    p20.transfer(four_cl,
                 tuberack_1['A6'],
                 plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1',
                             'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2',
                             'A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3',
                             'A4', 'B4', 'C4'),
                 touch_tip=True)
    # STS ul
    p20.transfer(sts,
                 tuberack_1['B1'],
                 plate.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1',
                             'A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2',
                             'A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3',
                             'A4', 'B4', 'C4'),
                 touch_tip=True)

    # L-tyrosine 2 ul each well
    p20.transfer(
        2,
        tuberack_1['A4'],
        plate.columns('1', '2', '3'),
        mix_after=(3, 20),
        new_tip='always'
    )  # no need for touch tip as new tip after mix. Mix_after means liquid in wells mixed

    p20.transfer(2,
                 tuberack_1['A4'],
                 plate.wells('A4', 'B4', 'C4'),
                 mix_after=(3, 20),
                 new_tip='always')

    protocol.comment('Protocol complete!')
Example #18
0
def run(ctx: protocol_api.ProtocolContext):
    [
        mag_engage_time, n_samples, left_pipette_lname, right_pipette_lname,
        left_pipette_tipracks_lname, right_pipette_tipracks_lname,
        reservoir_lname, destination_plate_lname, sample_plate_lname,
        mag_mod_lname, tube_rack_lname, dest_temp_mod_lname, do_vortex_pause,
        do_SDS_step, do_DNAse_step, use_NaCl, bead_volume, wash_buf_vol,
        elution_buf_vol, n_washes, n_wash_mixes, n_elution_mixes,
        incubation_time, n_bead_mixes, sds_buffer_vol, starting_volume
    ] = get_values(  # noqa: F821 (<--- DO NOT REMOVE!)
        "mag_engage_time", "n_samples", "left_pipette_lname",
        "right_pipette_lname", "left_pipette_tipracks_lname",
        "right_pipette_tipracks_lname", "reservoir_lname",
        "destination_plate_lname", "sample_plate_lname", "mag_mod_lname",
        "tube_rack_lname", "dest_temp_mod_lname", "do_vortex_pause",
        "do_SDS_step", "do_DNAse_step", "use_NaCl", "bead_volume",
        "wash_buf_vol", "elution_buf_vol", "n_washes", "n_wash_mixes",
        "n_elution_mixes", "incubation_time", "n_bead_mixes", "sds_buffer_vol",
        "starting_volume")

    # define all custom variables above here with descriptions:

    # Calculate reagent volumes
    # See docs for calculations
    # 10:1, v_lysis = v_s/9, e.g. for v_s = 1000 uL, v_lysis = 1000/9=111 uL
    lysis_buffer_vol = starting_volume / 9
    dnaseI_vol = starting_volume / 1000  # 1 uL per mL
    total_lysis_vol = starting_volume + lysis_buffer_vol + dnaseI_vol

    nacl_first_step_vol = total_lysis_vol / 7
    nacl_wash_buf_vol = wash_buf_vol / 7

    total_vol_after_bead_adddn = round(total_lysis_vol + bead_volume, 1)
    if use_NaCl:
        total_vol_after_bead_adddn = round(
            total_vol_after_bead_adddn + nacl_first_step_vol, 1)

    n_columns = math.ceil(n_samples / 8)

    # Error checking
    # If DNAse I is being added from a tube we need to make sure that
    # a) we have a single pipette loaded and b) that dnaseI_vol > pipette min.
    # volume
    if (("single" not in left_pipette_lname
         and "single" not in right_pipette_lname) and do_DNAse_step):
        raise Exception("No single channel pipette for DNAse I distribution " +
                        "selected")
    # More error checking on this in the pipette loading section

    # load modules
    '''

    Add your modules here with:

    module_name = ctx.load_module('{module_loadname}', '{slot number}')

    Note: if you are loading a thermocycler, you do not need to specify
    a slot number - thermocyclers will always occupy slots 7, 8, 10, and 11.

    For all other modules, you can load them on slots 1, 3, 4, 6, 7, 9, 10.

    '''
    # Load temperature modules (if any)
    dest_temp_mod = None
    if dest_temp_mod_lname:
        dest_temp_mod = ctx.load_module(dest_temp_mod_lname, '4')

    # Load the magnetic module
    mag_mod = ctx.load_module(mag_mod_lname, '1')

    # Load labware
    sample_plate = mag_mod.load_labware(sample_plate_lname)

    sample_well = sample_plate.wells()[0]
    if total_vol_after_bead_adddn > sample_well.max_volume:
        raise Exception("The sample wells are too small to handle the " +
                        "volumes of reagents")

    reservoir = ctx.load_labware(reservoir_lname, '7')

    dnaseI_tuberack = None
    if do_DNAse_step:
        dnaseI_tuberack = ctx.load_labware(tube_rack_lname, '10')

    dest_plate = None
    if dest_temp_mod_lname:
        dest_plate = dest_temp_mod.load_labware(destination_plate_lname)
    else:
        dest_plate = ctx.load_labware(destination_plate_lname, '4')

    # load tipracks
    tipracks_left, tipracks_right = [None] * 2
    if left_pipette_tipracks_lname:
        tipracks_left = [
            ctx.load_labware(left_pipette_tipracks_lname, slot)
            for slot in ['5', '8', '11']
        ]
    if right_pipette_tipracks_lname:
        tipracks_right = [
            ctx.load_labware(right_pipette_tipracks_lname, slot)
            for slot in ['6', '9']
        ]

    # load instrument
    '''
    Nomenclature for pipette:

    use 'p'  for single-channel, 'm' for multi-channel,
    followed by number of microliters.

    p20, p300, p1000 (single channel pipettes)
    m20, m300 (multi-channel pipettes)

    If loading pipette, load with:

    ctx.load_instrument(
                        '{pipette api load name}',
                        pipette_mount ("left", or "right"),
                        tip_racks=tiprack
                        )
    '''
    left_pip = None
    right_pip = None
    pipette = None  # Remains None if there are two pipettes loaded
    if left_pipette_lname:
        left_pip = ctx.load_instrument(left_pipette_lname,
                                       "left",
                                       tip_racks=tipracks_left)
    if right_pipette_lname:
        right_pip = ctx.load_instrument(right_pipette_lname,
                                        "right",
                                        tip_racks=tipracks_right)
    if not (right_pip and left_pip):
        if right_pip:
            pipette = right_pip
        else:
            pipette = left_pip

    # Rank the pipettes by minimum volume
    s_pip, l_pip = [None] * 2
    if pipette:
        s_pip, l_pip = [pipette, pipette]
    else:
        if left_pip.min_volume < right_pip.min_volume:
            s_pip, l_pip = [left_pip, right_pip]
        else:
            s_pip, l_pip = [right_pip, left_pip]

    # pipette functions   # INCLUDE ANY BINDING TO CLASS
    '''

    Define all pipette functions, and class extensions here.
    These may include but are not limited to:

    - Custom pickup functions
    - Custom drop tip functions
    - Custom Tip tracking functions
    - Custom Trash tracking functions
    - Slow tip withdrawal

    For any functions in your protocol, describe the function as well as
    describe the parameters which are to be passed in as a docstring below
    the function (see below).

    def pick_up(pipette):
        """`pick_up()` will pause the protocol when all tip boxes are out of
        tips, prompting the user to replace all tip racks. Once tipracks are
        reset, the protocol will start picking up tips from the first tip
        box as defined in the slot order when assigning the labware definition
        for that tip box. `pick_up()` will track tips for both pipettes if
        applicable.

        :param pipette: The pipette desired to pick up tip
        as definited earlier in the protocol (e.g. p300, m20).
        """
        try:
            pipette.pick_up_tip()
        except protocol_api.labware.OutOfTipsError:
            ctx.pause("Replace empty tip racks")
            pipette.reset_tipracks()
            pipette.pick_up_tip()

    '''
    def pick_up(pipette):
        """`pick_up()` will pause the protocol when all tip boxes are out of
        tips, prompting the user to replace all tip racks. Once tipracks are
        reset, the protocol will start picking up tips from the first tip
        box as defined in the slot order when assigning the labware definition
        for that tip box. `pick_up()` will track tips for both pipettes if
        applicable.

        :param pipette: The pipette desired to pick up tip
        as definited earlier in the protocol (e.g. p300, m20).
        """
        try:
            pipette.pick_up_tip()
        except protocol_api.labware.OutOfTipsError:
            ctx.pause("Replace empty tip racks")
            pipette.reset_tipracks()
            pipette.pick_up_tip()

    # helper functions
    '''
    Define any custom helper functions outside of the pipette scope here, using
    the convention seen above.

    e.g.

    def remove_supernatant(vol, index):
        """
        function description

        :param vol:

        :param index:
        """


    '''

    def choose_pipette(vol, multi_action=True):
        """
        Choose pipette selects an appropriate pipette based on
        the volume involved, and whether the pipette action involves all
        8 columns or a single well.

        :param vol: The volume to use this pipette with
        :param multi_action: True if the pipette action will be done with
        an 8-channel pipette, i.e. involving an 8-channel source and/or target

        """
        nonlocal s_pip, l_pip, pipette
        # Case when only one pipette has been loaded
        if pipette:
            if pipette.min_volume < vol:
                raise Exception(
                    ("\n\nThe loaded pipette has a minimum "
                     "volume of {} which is greater than {}. " +
                     "Consider adding a smaller volume pipette\n").format(
                         pipette.min_volume, vol))
            if pipette.channels == 8:
                return pipette, True
            else:
                return pipette, False

        s_pip_is_multi = True if s_pip.channels == 8 else False
        l_pip_is_multi = True if l_pip.channels == 8 else False

        if s_pip.min_volume > vol:
            raise Exception(("The volume ({} uL)is too small to handle for " +
                             "either pipette, please re-examine your" +
                             "parameters").format(vol))

        # The preference is for the multi-channel pipette with the largest
        # volume capability while the least preferred is the smallest volume
        # single channel pipette.
        if multi_action:
            # Prefer the multi-channel pipettes if available
            if l_pip_is_multi and l_pip.min_volume < vol:
                return l_pip, True
            elif s_pip_is_multi:
                return s_pip, True
            # If they are not, let a single channel pipette do the job of a
            # multi-channel pipette
            elif not l_pip_is_multi and l_pip.min_volume < vol:
                return l_pip, False
            else:
                return s_pip, False
        else:
            if not l_pip_is_multi and l_pip.min_volume < vol:
                return l_pip, False
            elif not s_pip_is_multi:
                return s_pip, False
            else:
                raise Exception(("No approriate single channel pipette is " +
                                 "loaded for the volume of {} uL".format(vol)))

    def transfer(pip, volume, source, dest, **kwargs):
        """
        A wrapper function for InstrumentContext.transfer that handles
        labware.OutOfTipsError by asking the user to refill empty tipracks.

        :param pip: The pipette to user for the transfer

        Other parameters: See class
        opentrons.protocol_api.contexts.InstrumentContext
        """
        try:
            pip.transfer(volume, source, dest, **kwargs)
        except protocol_api.labware.OutOfTipsError:
            ctx.pause("\n\nReplace empty tip racks for {}\n".format(pip.name))
            pip.reset_tipracks()
            pip.transfer(volume, source, dest, **kwargs)

    def reag_to_wells_or_col_transfer(vol,
                                      reag_source_well,
                                      dest_plate,
                                      multi_transfer=True,
                                      **transfer_kwargs):
        """
        function description

        :param vol:

        :param index:
        """
        nonlocal n_columns, n_samples
        pip, pip_is_multi = choose_pipette(vol, multi_transfer)

        if pip_is_multi:
            dest_columns = dest_plate.columns()[0:n_columns]
            for col in dest_columns:
                transfer(pip, vol, reag_source_well, col[0], **transfer_kwargs)
        else:  # Transfer to each well
            transfer(pip, vol, reag_source_well,
                     dest_plate.wells()[0:n_samples], **transfer_kwargs)

    def remove_supernatant(vol,
                           source_plate,
                           multi_transfer=True,
                           **transfer_kwargs):
        """
        Removes a volume from each sample well of the source plate
        (well 0 to well n_samples) and transfers it to the liquid trash
        well of the reservoir.

        :param vol (float): The volume to transfer to the trash.

        :param source_plate (labware plate): The plate to aspirate from.

        :param multi_transfer (Boolean): Use 8-channel pipette and transfer
        a whole column at a time if true, otherwise use a single channel
        pipette and transfer well by well.

        :param **transfer_kwargs: Any keyword arguments that you may want
        to pass into the transfer method calls
        """
        nonlocal n_columns, n_samples
        pip, pip_is_multi = choose_pipette(vol, multi_transfer)

        if pip_is_multi:
            source_columns = source_plate.columns()[0:n_columns]
            for col in source_columns:
                transfer(pip, vol, col[0], waste_well, **transfer_kwargs)
        else:  # Transfer from each well
            transfer(pip, vol,
                     source_plate.wells()[0:n_samples], waste_well,
                     **transfer_kwargs)

    def transfer_plate_to_plate(vol, source_plate, destination_plate):
        pip, is_multi = choose_pipette(vol, True)
        if is_multi:
            for s_col, d_col in zip(source_plate.columns(),
                                    destination_plate.columns()):
                transfer(pip, vol, s_col[0], d_col[0])
        else:
            transfer(pip, vol, sample_wells, dest_wells)

    # reagents
    '''
    Define where all reagents are on the deck using the labware defined above.

    e.g.

    water = reservoir12.wells()[-1]
    waste = reservoir.wells()[0]
    samples = plate.rows()[0][0]
    dnase = tuberack.wells_by_name()['A4']

    '''

    lysis_buffer_well = reservoir.wells_by_name()['A1']
    wash_buffer_well = reservoir.wells_by_name()['A2']
    elution_buffer_well = reservoir.wells_by_name()['A3']
    nacl_well = reservoir.wells_by_name()['A4']
    bead_well = reservoir.wells_by_name()['A5']
    waste_well = reservoir.wells()[-1].top(-2)
    dnaseI_tube = None
    if dnaseI_tuberack:
        dnaseI_tube = dnaseI_tuberack.wells_by_name()['A1']
    sds_page_buf_well = None
    if do_SDS_step:
        sds_page_buf_well = reservoir.wells_by_name()['A6']

    # plate, tube rack maps
    '''
    Define any plate or tube maps here.

    e.g.

    plate_wells_by_row = [well for row in plate.rows() for well in row]

    '''
    sample_wells = sample_plate.wells()[0:n_samples]

    dest_columns = dest_plate.columns()[0:n_columns]
    dest_wells = dest_plate.wells()[0:n_samples]

    # protocol
    '''

    Include header sections as follows for each "section" of your protocol.

    Section can be defined as a step in a bench protocol.

    e.g.

    ctx.comment('\n\nMOVING MASTERMIX TO SAMPLES IN COLUMNS 1-6\n')

    for .... in ...:
        ...
        ...

    ctx.comment('\n\nRUNNING THERMOCYCLER PROFILE\n')

    ...
    ...
    ...


    '''
    ctx.comment('\n\nAdding lysis buffer to samples\n')
    pip, pip_is_multi = choose_pipette(lysis_buffer_vol)

    reag_to_wells_or_col_transfer(lysis_buffer_vol, lysis_buffer_well,
                                  sample_plate, True)

    if do_DNAse_step:
        ctx.comment('\n\nAdding DNAse I to sample wells\n')
        # Must be single channel, protocol will raise exception otherwise
        reag_to_wells_or_col_transfer(dnaseI_vol, dnaseI_tube, sample_plate,
                                      False)

    ctx.pause("\n\nPlace sample plate on a shaker and shake for 10-20" +
              " minutes at room temperature for the lysis reaction" +
              " to complete\n")

    # Optional addition of NaCl for improved binding to beads
    if use_NaCl:
        ctx.comment("Adding NaCl to samples for a final conc. of 500 mM")
        reag_to_wells_or_col_transfer(nacl_first_step_vol, nacl_well,
                                      sample_plate, True)

    if do_vortex_pause:
        ctx.pause("Pause to vortex/resuspend your magnetic beads")

    # Mixing the beads in the bead well
    pick_up(l_pip)
    l_pip.mix(n_bead_mixes, l_pip.max_volume, bead_well)

    ctx.comment("\n\nTransferring bead solution to sample wells and mixing")
    pip, pip_is_multi = choose_pipette(bead_volume, True)
    if pip_is_multi:
        for col in dest_columns:
            if not pip.has_tip:
                pick_up(pip)
            pip.aspirate(bead_volume, bead_well)
            pip.dispense(bead_volume, col[0])
            mix_vol = (pip.max_volume if total_vol_after_bead_adddn /
                       2 > pip.max_volume else total_vol_after_bead_adddn / 2)
            pip.mix(5, mix_vol, col[0])
            pip.drop_tip()
    else:
        for well in sample_wells:
            if not pip.has_tip:
                pick_up(pip)
            pip.aspirate(bead_volume, bead_well)
            pip.dispense(bead_volume, well)
            mix_vol = (pip.max_volume if total_vol_after_bead_adddn /
                       2 > pip.max_volume else total_vol_after_bead_adddn / 2)
            pip.mix(5, mix_vol, well)
            pip.drop_tip()

    ctx.comment("\n\nIncubating beads\n")
    ctx.delay(0, incubation_time)
    ctx.comment("\n\nEngaging magnets\n")
    mag_mod.engage()
    ctx.delay(0, mag_engage_time)
    ctx.comment("\n\nRemoving supernatant\n")
    remove_supernatant(total_vol_after_bead_adddn, sample_plate)

    # Wash the beads n_washes times, the standard is 3 washes
    repetitions = 1 if do_SDS_step else n_washes
    for i in range(0, repetitions):
        ctx.comment("\n\nBead wash #{}".format(i + 1))
        mag_mod.disengage()
        # Transfer wash buffer to the sample wells
        supernatant_volume = (wash_buf_vol +
                              nacl_wash_buf_vol if use_NaCl else wash_buf_vol)
        half_supernatant_vol = supernatant_volume / 2
        mix_vol = (half_supernatant_vol
                   if half_supernatant_vol < l_pip.max_volume else
                   l_pip.max_volume)
        if use_NaCl:
            reag_to_wells_or_col_transfer(wash_buf_vol, wash_buffer_well,
                                          sample_plate, True)
            reag_to_wells_or_col_transfer(nacl_wash_buf_vol,
                                          nacl_well,
                                          sample_plate,
                                          True,
                                          mix_after=(n_wash_mixes, mix_vol))
        else:
            reag_to_wells_or_col_transfer(wash_buf_vol,
                                          wash_buffer_well,
                                          sample_plate,
                                          True,
                                          mix_after=(n_wash_mixes, mix_vol))
        mag_mod.engage()
        ctx.delay(0, mag_engage_time)
        # Remove supernatant
        remove_supernatant(supernatant_volume, sample_plate)

    mag_mod.disengage()

    if do_SDS_step:
        ctx.comment("\n\nAdding 1x SDS-PAGE buffer\n")
        reag_to_wells_or_col_transfer(sds_buffer_vol, sds_page_buf_well,
                                      sample_plate, True)
        mag_mod.engage()
        ctx.delay(0, mag_engage_time)
        # Transfer to target plate
        ctx.comment("\n\nTransferring protein in SDS-buffer to target plate\n")
        transfer_plate_to_plate(sds_buffer_vol, sample_plate, dest_plate)
    # Elute
    else:
        ctx.comment("\n\nAdding elution buffer and mixing\n")
        reag_to_wells_or_col_transfer(elution_buf_vol,
                                      elution_buffer_well,
                                      sample_plate,
                                      True,
                                      mix_after=(n_elution_mixes, mix_vol))
        ctx.comment("\n\nIncubating samples with elution buffer\n")
        ctx.delay(0, incubation_time)
        ctx.comment("\n\nAttracting beads to magnets\n")
        mag_mod.engage()
        ctx.delay(0, mag_engage_time)
        ctx.comment("\n\nTransferring protein elution supernatant to" +
                    "target plate\n")
        transfer_plate_to_plate(elution_buf_vol, sample_plate, dest_plate)
    ctx.comment("\n\n~~~~~ End of protocol ~~~~~\n")
Example #19
0
def run(protocol: protocol_api.ProtocolContext):

	# which steps should be run?
	AddBeads = True
	AddBindingBuffer = True
	CaptureBinding = True
	Wash1 = True
	Wash2 = True
	Wash2repeat = True
	Dry = True
	Elute = True

	# set tweakable variables
	elution_volume = 100 # ul of water to add to final beads
	elution_to_plate = 70 # ul to transfer to final elution plate
	capture_depth = -3 # depth below ideal bottom of plate to remove supernatants, this may be required as a function of a poor calibration or labware def
	capture_min = 2 # number of minutes to capture beads on magnets
	cols_to_extract = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] # which columns should be extracted?
	nmix = 5 # number of times to pipette to mix
	drying_min = 20 # number of minutes to evaporate residual ethanol !!!! SET TO 20
	trash_speed = 1 # the relative speed to discard of liquids into trash, this is an integer multiplier of normal speed, set higher to clear bubbles on outside of tip


	
	# define deck layout
	MagModule = protocol.load_module('magnetic module gen2', 10)
	BindingPlate = MagModule.load_labware('nest_96_wellplate_2ml_deep') # use the
	ElutionPlate = protocol.load_labware('biorad_96_wellplate_200ul_pcr', '3') # an empty biorad 96 well plate

	BeadsAndWater = protocol.load_labware('usascientific_12_reservoir_22ml', '2') # magbeads in A1 (4 mL), water in A2 (10mL), the end columns will be used for waste
	BindingBuffer = protocol.load_labware('agilent_1_reservoir_290ml', '7') # reservoir with 70 mL binding buffer
	MagWash1 = protocol.load_labware('agilent_1_reservoir_290ml', '4') # reservoir with 100 mL magwash1
	MagWash2 = protocol.load_labware('agilent_1_reservoir_290ml', '1') # reservoir with 200 mL magwash2

	tips_binding = protocol.load_labware('opentrons_96_filtertiprack_200ul', '11')
	tips_wash1 =  protocol.load_labware('opentrons_96_filtertiprack_200ul', '8')
	tips_wash2 =  protocol.load_labware('opentrons_96_filtertiprack_200ul', '5')
	tips_wash2_repeat =  protocol.load_labware('opentrons_96_filtertiprack_200ul', '9')
	tips_elution = protocol.load_labware('opentrons_96_filtertiprack_200ul', '6')

	fixed_trash = protocol.fixed_trash['A1']


	set_rail_lights = True

	# define pipettes
	multichannel = protocol.load_instrument('p300_multi_gen2', 'left', tip_racks=[tips_binding, tips_wash1, tips_wash2, tips_wash2_repeat, tips_elution])
	#P1000 = protocol.load_instrument('p1000_single_gen2', 'right', tip_racks=[tips_binding, tips_wash1, tips_wash2, tips_wash2rep])

	### Prerun setup ########################################
	MagModule.disengage()

	### MAG BINDING ######################################

	if AddBindingBuffer:
		protocol.comment('--------->Adding 600 ul binding buffer')
		multichannel.pick_up_tip(tips_binding['A1'])
		for i in cols_to_extract:
			multichannel.aspirate(200, BindingBuffer['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingBuffer['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingBuffer['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
		multichannel.return_tip()
		
	if AddBeads:
		protocol.comment('--------->Adding 25ul mag beads')
		multichannel.pick_up_tip(tips_binding['A2'])
		multichannel.mix(10, 100, BeadsAndWater['A1'].bottom(3)) # mix beads 20 x by pulling up 100ul
		for i in cols_to_extract:
			multichannel.aspirate(25, BeadsAndWater['A1'])
			multichannel.dispense(25, BindingPlate['A'+str(i)].top(4)
		multichannel.return_tip()
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_binding['A'+str(i)])
			multichannel.mix(10, 200, BeadsAndWater['A1'].bottom(3))
			multichannel.return_tip()
		
	if CaptureBinding:
		protocol.comment('--------->Removing Binding Buffer')
		MagModule.engage()
		protocol.delay(minutes=capture_min)
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_binding['A'+str(i)])
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.touch_tip()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.touch_tip()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2)
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.return_tip()
		
	if Wash1:
		protocol.comment('--------->Doing Wash 1')
		MagModule.disengage()
		multichannel.pick_up_tip(tips_wash1['A1'])
		for i in cols_to_extract:
			multichannel.aspirate(200, MagWash1['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash1['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash1['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash1['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
		multichannel.return_tip()	
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_wash1['A'+str(i)])
			multichannel.aspirate(100, MagWash1['A1'])
			multichannel.dispense(100, BindingPlate['A'+str(i)])
			multichannel.mix(nmix, 180, BindingPlate['A'+str(i)].bottom(2))
			multichannel.blow_out()
			multichannel.return_tip()
		MagModule.engage()
		protocol.delay(minutes=capture_min)
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_wash1['A'+str(i)])
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2)
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			multichannel.blow_out()
			multichannel.return_tip()
			
	if Wash2:
		protocol.comment('--------->Doing Wash 2')
		MagModule.disengage()
		multichannel.pick_up_tip(tips_wash2['A1'])
		for i in cols_to_extract:
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
		multichannel.return_tip()	
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_wash2['A'+str(i)])
			multichannel.aspirate(100, MagWash2['A1'])
			multichannel.dispense(100, BindingPlate['A'+str(i)])
			multichannel.mix(nmix, 180, BindingPlate['A'+str(i)].bottom(2))
			multichannel.blow_out()
			multichannel.return_tip()
		MagModule.engage()
		protocol.delay(minutes=capture_min)
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_wash2['A'+str(i)])
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)]) # is this a calibration issue?
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2)
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.return_tip()
			
	if Wash2repeat:
		protocol.comment('--------->Repeating Wash 2')
		MagModule.disengage()
		multichannel.pick_up_tip(tips_wash2_repeat['A1'])
		for i in cols_to_extract:
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
			multichannel.aspirate(200, MagWash2['A1'])
			multichannel.dispense(200, BindingPlate['A'+str(i)].top(4))
			#multichannel.blow_out()
		multichannel.return_tip()	
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_wash2_repeat['A'+str(i)])
			multichannel.aspirate(100, MagWash2['A1'])
			multichannel.dispense(100, BindingPlate['A'+str(i)])
			multichannel.mix(nmix, 180, BindingPlate['A'+str(i)].bottom(5))
			multichannel.blow_out()
			multichannel.return_tip()
		MagModule.engage()
		protocol.delay(minutes=capture_min)
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_wash2_repeat['A'+str(i)])
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)])
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)]) # is this a calibration issue?
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.aspirate(200, BindingPlate['A'+str(i)].bottom(capture_depth), rate=0.2)
			multichannel.dispense(200, fixed_trash, rate = trash_speed)
			#multichannel.blow_out()
			multichannel.return_tip()
	
	if Dry:
		protocol.comment('--------->Drying DNA')
		protocol.delay(minutes=drying_min)

	if Elute:
		protocol.comment('--------->Eluting DNA')
		MagModule.disengage()
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_elution['A'+str(i)])
			multichannel.aspirate(elution_volume, BeadsAndWater['A2'])
			multichannel.dispense(elution_volume, BindingPlate['A'+str(i)])
			multichannel.mix(nmix, elution_to_plate, BindingPlate['A'+str(i)].bottom(capture_depth))
			#multichannel.blow_out()
			multichannel.return_tip()
		MagModule.engage()
		protocol.delay(minutes=capture_min)
		for i in cols_to_extract:
			multichannel.pick_up_tip(tips_elution['A'+str(i)])
			multichannel.aspirate(elution_to_plate, BindingPlate['A'+str(i)].bottom(capture_depth+1), rate=0.2)
			multichannel.dispense(elution_to_plate, ElutionPlate['A'+str(i)])
			#multichannel.blow_out()
			multichannel.return_tip()

			
	
			
Example #20
0
def run(ctx: protocol_api.ProtocolContext):
    STEP = 0
    STEPS = {  # Dictionary with STEP activation, description and times
        1: {
            'Execute':
            True,
            'description':
            'Mezclar y dispensar muestras (' + str(VOLUME_SAMPLE) + 'ul)'
        }
    }
    for s in STEPS:  # Create an empty wait_time
        if 'wait_time' not in STEPS[s]:
            STEPS[s]['wait_time'] = 0

    #Folder and file_path for log time
    if not ctx.is_simulating():
        folder_path = '/var/lib/jupyter/notebooks/' + run_id
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        file_path = folder_path + '/StationA_time_log.txt'

    # Define Reagents as objects with their properties
    class Reagent:
        def __init__(self, name, flow_rate_aspirate, flow_rate_dispense, rinse,
                     delay):
            self.name = name
            self.flow_rate_aspirate = flow_rate_aspirate
            self.flow_rate_dispense = flow_rate_dispense
            self.rinse = bool(rinse)
            self.delay = delay

    # Reagents and their characteristics
    Samples = Reagent(name='Samples',
                      flow_rate_aspirate=25,
                      flow_rate_dispense=100,
                      rinse=False,
                      delay=0)

    ctx.comment(' ')
    ctx.comment('###############################################')
    ctx.comment('CONTROLES: ' + str(NUM_CONTROL_SPACES))
    ctx.comment('MUESTRAS: ' + str(NUM_REAL_SAMPLES))
    ctx.comment('###############################################')
    ctx.comment(' ')

    ##################
    # Custom functions
    def move_vol_multichannel(pipet,
                              reagent,
                              source,
                              dest,
                              vol,
                              air_gap_vol,
                              x_offset,
                              pickup_height,
                              rinse,
                              disp_height,
                              blow_out,
                              touch_tip,
                              shakes=0,
                              shake_v_offset=-45):
        '''
        x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1]
        pickup_height: height from bottom where volume
        rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer
        disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered
        blow_out, touch_tip: if True they will be done after dispensing
        '''
        # Rinse before aspirating
        if rinse == True:
            custom_mix(pipet,
                       reagent,
                       location=source,
                       vol=vol,
                       rounds=2,
                       blow_out=True,
                       mix_height=0,
                       x_offset=x_offset)

        # SOURCE
        s = source.bottom(pickup_height).move(Point(x=x_offset[0]))
        pipet.aspirate(vol, s,
                       rate=reagent.flow_rate_aspirate)  # aspirate liquid

        if shakes > 0:
            shake_pipet(pipet, v_offset=shake_v_offset)

        if air_gap_vol != 0:  # If there is air_gap_vol, switch pipette to slow speed
            pipet.aspirate(air_gap_vol,
                           source.top(z=-2),
                           rate=reagent.flow_rate_aspirate)  # air gap

        # GO TO DESTINATION
        drop = dest.top(z=disp_height).move(Point(x=x_offset[1]))
        pipet.dispense(vol + air_gap_vol,
                       drop,
                       rate=reagent.flow_rate_dispense)  # dispense all

        ctx.delay(
            seconds=reagent.delay)  # pause for x seconds depending on reagent

        if blow_out == True:
            pipet.blow_out(dest.top(z=disp_height))

        if touch_tip == True:
            pipet.touch_tip(speed=20, v_offset=-10)

        if air_gap_vol != 0:
            pipet.air_gap(air_gap_vol, height=disp_height)  #air gap

    def custom_mix(pipet,
                   reagent,
                   location,
                   vol,
                   rounds,
                   blow_out,
                   mix_height,
                   x_offset,
                   source_height=5):
        '''
        Function for mixing a given [vol] in the same [location] a x number of [rounds].
        blow_out: Blow out optional [True,False]
        x_offset = [source, destination]
        source_height: height from bottom to aspirate
        mix_height: height from bottom to dispense
        '''
        if mix_height <= 0:
            mix_height = 3

        pipet.aspirate(1,
                       location=location.bottom(z=source_height).move(
                           Point(x=x_offset[0])),
                       rate=reagent.flow_rate_aspirate)

        for _ in range(rounds):
            pipet.aspirate(vol,
                           location=location.bottom(z=source_height).move(
                               Point(x=x_offset[0])),
                           rate=reagent.flow_rate_aspirate)
            pipet.dispense(vol,
                           location=location.bottom(z=mix_height).move(
                               Point(x=x_offset[1])),
                           rate=reagent.flow_rate_dispense)

        pipet.dispense(1,
                       location=location.bottom(z=mix_height).move(
                           Point(x=x_offset[1])),
                       rate=reagent.flow_rate_dispense)

        if blow_out == True:
            pipet.blow_out(location.top(z=-2))  # Blow out

    def generate_source_table(source):
        '''
        Concatenate the wells frome the different origin racks
        '''
        num_cols = math.ceil(num_samples / 8)
        s = []
        for i in range(num_cols):
            if i < 6:
                s += source[0].columns()[i] + source[1].columns()[i]
            else:
                s += source[2].columns()[i - 6] + source[3].columns()[i - 6]
        return s

    def shake_pipet(pipet, rounds=2, speed=100, v_offset=0):
        ctx.comment("Shaking " + str(rounds) + " rounds.")
        for i in range(rounds):
            pipet.touch_tip(speed=speed, radius=0.1, v_offset=v_offset)

    ##########
    # pick up tip and if there is none left, prompt user for a new rack
    def pick_up(pip):
        nonlocal tip_track
        if not ctx.is_simulating():
            if tip_track['counts'][pip] == tip_track['maxes'][pip]:
                ctx.pause('Replace ' + str(pip.max_volume) +
                          'µl tipracks before \
                resuming.')
                pip.reset_tipracks()
                tip_track['counts'][pip] = 0
        pip.pick_up_tip()

    def run_quiet_process(command):
        subprocess.check_output('{} &> /dev/null'.format(command), shell=True)

    def play_sound(filename):
        print('Speaker')
        print('Next\t--> CTRL-C')
        try:
            run_quiet_process('mpg123 {}'.format(path_sounds + filename +
                                                 '.mp3'))
            run_quiet_process('mpg123 {}'.format(path_sounds + sonido_defecto))
            run_quiet_process('mpg123 {}'.format(path_sounds + filename +
                                                 '.mp3'))

        except KeyboardInterrupt:
            pass
            print()

    def start_run():
        ctx.comment(' ')
        ctx.comment('###############################################')
        ctx.comment('Empezando protocolo')
        if PHOTOSENSITIVE == False:
            ctx._hw_manager.hardware.set_lights(button=True, rails=True)
        else:
            ctx._hw_manager.hardware.set_lights(button=True, rails=False)
        now = datetime.now()

        # dd/mm/YY H:M:S
        start_time = now.strftime("%Y/%m/%d %H:%M:%S")
        return start_time

    def finish_run(switch_off_lights=False):
        ctx.comment('###############################################')
        ctx.comment('Protocolo finalizado')
        ctx.comment(' ')
        #Set light color to blue
        ctx._hw_manager.hardware.set_lights(button=True, rails=False)
        now = datetime.now()
        # dd/mm/YY H:M:S
        finish_time = now.strftime("%Y/%m/%d %H:%M:%S")
        if PHOTOSENSITIVE == False:
            for i in range(10):
                ctx._hw_manager.hardware.set_lights(button=False, rails=False)
                time.sleep(0.3)
                ctx._hw_manager.hardware.set_lights(button=True, rails=True)
                time.sleep(0.3)
        else:
            for i in range(10):
                ctx._hw_manager.hardware.set_lights(button=False, rails=False)
                time.sleep(0.3)
                ctx._hw_manager.hardware.set_lights(button=True, rails=False)
                time.sleep(0.3)
        if switch_off_lights:
            ctx._hw_manager.hardware.set_lights(button=True, rails=False)

        used_tips = tip_track['num_refills'][p1000] * 96 * len(
            p1000.tip_racks) + tip_track['counts'][p1000]
        ctx.comment('Puntas de 1000 ul utilizadas: ' + str(used_tips) + ' (' +
                    str(round(used_tips / 96, 2)) + ' caja(s))')
        ctx.comment('###############################################')

        if not ctx.is_simulating():
            for i in range(SOUND_NUM_PLAYS):
                if i > 0:
                    time.sleep(60)
                play_sound('finished_process_esp')

            return finish_time

    ####################################
    # load labware and modules

    ####################################
    # Load Sample racks
    if num_samples <= 48:
        rack_num = 2
        ctx.comment('Used source racks are ' + str(rack_num))
    else:
        rack_num = 4

    #source_racks = [ctx.load_labware(
    #    'pcr_24_wellplate_13200ul', slot,
    #    'source tuberack with snapcap' + str(i + 1)) for i, slot in enumerate(['4', '1', '5', '2'][:rack_num])
    #]

    source_racks = [
        ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_1', '4',
                         'samples grid 1'),
        ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_2', '1',
                         'samples grid 2'),
        ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_3', '5',
                         'samples grid 3'),
        ctx.load_labware('vitrobiocomma_24_tuberack_15000ul_4', '2',
                         'samples grid 4')
    ]

    ##################################
    # Destination plate
    dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '6',
                                  'NEST 96 Deepwell Plate 2mL')

    ####################################
    # Load tip_racks
    tips1000 = [
        ctx.load_labware(
            'opentrons_96_filtertiprack_1000ul' if OPENTRONS_TIPS else
            'geb_96_tiprack_1000ul', slot, '1000µl filter tiprack')
        for slot in ['8']
    ]

    ################################################################################
    # setup samples and destinations
    sample_sources_full = generate_source_table(source_racks)
    sample_sources = sample_sources_full[NUM_CONTROL_SPACES:num_samples]
    destinations = dest_plate.wells()[NUM_CONTROL_SPACES:num_samples]

    p1000 = ctx.load_instrument('p1000_single_gen2',
                                'left',
                                tip_racks=tips1000)  # load P1000 pipette

    # used tip counter and set maximum tips available
    tip_track = {
        'counts': {
            p1000: 0
        },
        'maxes': {
            p1000: 96 * len(p1000.tip_racks)
        },  #96 tips per tiprack * number or tipracks in the layout
        'num_refills': {
            p1000: 0
        },
        'tips': {
            p1000: [tip for rack in tips1000 for tip in rack.rows()[0]]
        }
    }

    start_run()

    ############################################################################
    # STEP 1: MIX AND MOVE SAMPLES
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')

        start = datetime.now()
        for s, d in zip(sample_sources, destinations):
            if not p1000.hw_pipette['has_tip']:
                pick_up(p1000)

            # Mix the sample BEFORE dispensing
            if NUM_MIXES > 0:
                custom_mix(p1000,
                           reagent=Samples,
                           location=s,
                           vol=volume_mix,
                           rounds=NUM_MIXES,
                           blow_out=True,
                           mix_height=5,
                           x_offset=x_offset)

            move_vol_multichannel(p1000,
                                  reagent=Samples,
                                  source=s,
                                  dest=d,
                                  vol=VOLUME_SAMPLE,
                                  air_gap_vol=air_gap_vol_sample,
                                  x_offset=x_offset,
                                  pickup_height=8,
                                  rinse=Samples.rinse,
                                  disp_height=-10,
                                  blow_out=True,
                                  touch_tip=False,
                                  shakes=SAMPLE_SAKES)

            p1000.drop_tip(home_after=False)
            tip_track['counts'][p1000] += 1

        # Time statistics
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    # Export the time log to a tsv file
    if not ctx.is_simulating():
        with open(file_path, 'w') as f:
            f.write(
                'STEP\texecution\tdescription\twait_time\texecution_time\n')
            for key in STEPS.keys():
                row = str(key)
                for key2 in STEPS[key].keys():
                    row += '\t' + format(STEPS[key][key2])
                f.write(row + '\n')
        f.close()

    ############################################################################
    # Light flash end of program
    # from opentrons.drivers.rpi_drivers import gpio

    finish_run(switch_off_lights)
Example #21
0
def run(protocol: protocol_api.ProtocolContext):
    """
    Aliquoting mastermix from a 5 mL tube to a 96 wells plate;
    using volume tracking so that the pipette starts 
    aspirating at the starting height of the liquid and goes down as the 
    volume decreases.
    Adding primers from PCR strips (with 10 uM primer F&R primer mix)
    the 96 wells plate.
    """
# =============================================================================


# ======================LOADING LABWARE AND PIPETTES===========================
# =============================================================================
    ## For available labware see "labware/list_of_available_labware".       ##
    # Pipette tips
    tips_200 = protocol.load_labware(
        'opentrons_96_filtertiprack_200ul', #labware definition
        2,                                  #deck position
        '200tips')                          #custom name
    tips_20_1 = protocol.load_labware(
        'opentrons_96_filtertiprack_20ul',  #labware definition
        7,                                  #deck position
        '20tips_1')                         #custom name       
    tips_20_2 = protocol.load_labware(
        'opentrons_96_filtertiprack_20ul',  #labware definition
        10,                                 #deck position
        '20tips_2')                         #custom name           
    
    # Tube_racks & plates
    plate_96 = protocol.load_labware(
        'biorad_96_wellplate_200ul_pcr',    #labware definition
        6,                                  #deck position
        'plate_96')                         #custom name     
   ##### !!! OPTION 1: ROBOT      
    mastermix_tube = protocol.load_labware(
        'eppendorfscrewcap_15_tuberack_5000ul',  #labware def
        3,                                     #deck position
        'mastermix_tube')                      #custom name          
    primer_strips_1 = protocol.load_labware(
        'pcrstrips_96_wellplate_200ul',    #labware definition
        4,                                 #deck position
        'primer_strips_1')                 #custom name
    primer_strips_2 = protocol.load_labware(
        'pcrstrips_96_wellplate_200ul',    #labware definition
        1,                                 #deck position
        'primer_strips_2')                 #custom name                  
   ##### !!! OPTION 2: SIMULATOR      
    # with open("labware/eppendorfscrewcap_15_tuberack_5000ul/"
    #             "eppendorfscrewcap_15_tuberack_5000ul.json") as labware_file:
    #           labware_def_5mL = json.load(labware_file)
    # mastermix_tube = protocol.load_labware_from_definition( 
    #     labware_def_5mL,   #variable derived from opening json
    #     3,                 #deck position
    #     'mastermix_tube')  #custom name 
    # with open("labware/pcrstrips_96_wellplate_200ul/"
    #           "pcrstrips_96_wellplate_200ul.json") as labware_file:
    #         labware_def_pcrstrips = json.load(labware_file)
    # primer_strips_1 = protocol.load_labware_from_definition( 
    #     labware_def_pcrstrips, #variable derived from opening json
    #     4,                     #deck position
    #     'primer_strips_1')     #custom name  
    # primer_strips_2 = protocol.load_labware_from_definition( 
    #     labware_def_pcrstrips, #variable derived from opening json
    #     1,                     #deck position
    #     'primer_strips_2')     #custom name                            
    
    # Pipettes
    p300 = protocol.load_instrument(
        'p300_single_gen2',                 #instrument definition
        'right',                            #mount position
        tip_racks=[tips_200])               #assigned tiprack
    p20 = protocol.load_instrument(
        'p20_single_gen2',                  #instrument definition
        'left',                             #mount position
        tip_racks=[tips_20_1, tips_20_2])   #assigned tiprack
# =============================================================================


# ==========================VARIABLES TO SET#!!!===============================
# =============================================================================
    start_vol = 2520 
      ## The start_vol is the volume (ul) that is in the source labware at  ##
      ## the start of the protocol.                                         ##
    dispension_vol = 42 
      ## The dispension_vol is the volume (ul) that needs to be aliquoted   ##
      ## into the destination wells/tubes.                                  ##
    primer_vol = 3 
      ## The primer_vol is the volume (ul) of primer added to the PCR       ##
      ## reaction.                                                          ##
    p300.starting_tip = tips_200.well('F2')
    p20.starting_tip = tips_20_1.well('H8')
      ## The starting_tip is the location of first pipette tip in the box   ##
    container = 'tube_5mL'
      ## The container variable is needed for the volume tracking module.   ##
      ## It tells the module which dimensions to use for the calculations   ##
      ## of the pipette height. It is the source labware from which liquid  ##
      ## is aliquoted.                                                      ##
      ## There are several options to choose from:                          ##
      ## 'tube_1.5ml', 'tube_2mL', 'tube_5mL', 'tube_15mL', 'tube_50mL'   	##
    mastermix_source = mastermix_tube['C1']
    std_dilution_primer = primer_strips_2['B11']
# Mastermix destination wells==================================================
    mastermix = []
      ## Create an empty list to append wells to                            ##
    mastermix_columns = (
        [plate_96.columns_by_name()[column_name] for column_name in
         ['1', '2', '3', '4', '5']]
        )
      ## Make a list of columns, this is a list of lists!                   ##
    for column in mastermix_columns:
        for well in column:
            mastermix.append(well)
      ## Separate the columns into wells and append them to list            ##
    mastermix_wells = (
        [plate_96.wells_by_name()[well_name] for well_name in
         ['A6', 'B6',
          'A11', 'B11', 'C11', 'D11', 'E11', 'F11',
          'A12', 'B12', 'C12', 'D12', 'E12', 'F12']]
        )
      ## Make a list of separate wells                                      ## 
    for well in mastermix_wells:
        mastermix.append(well)
      ## Append the wells to the list                                       ## 
# Primer source tubes==========================================================
    primers = []
      ## Create an empty list to append wells to                            ##
    primer_columns = (
        [primer_strips_1.columns_by_name()[column_name] for column_name in
         ['2', '7', '11']] + 
        [primer_strips_2.columns_by_name()[column_name] for column_name in
         ['2', '7']] 
        )
      ## Make a list of columns, this is a list of lists!                   ##
    for column in primer_columns:
        for well in column:
            primers.append(well)
      ## Separate the columns into wells and append them to list            ##                                       
    primer_wells = (
        [primer_strips_2.wells_by_name()[well_name] for well_name in
         ['A11', 'B11']]
        ) 
      ## Make a list of separate wells                                      ## 
    for well in primer_wells:
        primers.append(well)
      ## Append the wells to the list                                       ##
# Primer destinations - standard dilution series===============================
    std_primer_dests = []
      ## Create an empty list to append the destination wells to.           ##
    std_wells = (
        [plate_96.wells_by_name()[well_name] for well_name in
         ['A11', 'B11', 'C11', 'D11', 'E11', 'F11',
          'A12', 'B12', 'C12', 'D12', 'E12', 'F12']]
        )
      ## make a list of the primer wells                                     ##
    for well in std_wells:
        std_primer_dests.append(well)
# =============================================================================


# ==========================PREDIFINED VARIABLES===============================
# =============================================================================
    aspiration_vol = dispension_vol + (dispension_vol/100*2)
      ## The aspiration_vol is the volume (ul) that is aspirated from the   ##
      ## container.                                                         ##
    ##### Variables for volume tracking
    start_height = vt.cal_start_height(container, start_vol)
      ## Call start height calculation function from volume tracking module.##
    current_height = start_height
      ## Set the current height to start height at the beginning of the     ##
      ## protocol.                                                          ##
# =============================================================================


# ===============================ALIQUOTING MIX================================
# =============================================================================
    ## For each column in destination_wells, pick up a tip, than for each   ##
    ## well in these columns pipette mix, and after the+ column drop the tip##
    ## Repeat untill all columns in the list are done.                      ##      
    for i, well in enumerate(mastermix):
    ## Name all the wells in the plate 'well', for all these do:            ## 
        if i == 0:
            p300.pick_up_tip()
          ## If we are at the first well, start by picking up a tip.        ##
        elif i % 8 == 0:
            p300.drop_tip()
            p300.pick_up_tip() 
          ## Then, after every 8th well, drop tip and pick up a new one.    ##
        current_height, pip_height, bottom_reached = vt.volume_tracking(
            container, dispension_vol, current_height)  
          ## The volume_tracking function needs the arguments container,    ##
          ## dispension_vol, and the current_height which we have set in    ##
          ## this protocol. With those variables, the function updates      ##
          ## the current_height, the pip_height and calculates the          ##
          ## delta_height of the liquid after the next aspiration step.     ##
        if bottom_reached: 
            aspiration_location = mastermix_source.bottom(z=1) #!!!
            protocol.comment("You've reached the bottom!")
        else:
            aspiration_location = mastermix_source.bottom(pip_height) #!!!
          ## If the level of the liquid in the next run of the loop will    ## 
          ## be smaller than 1 we have reached the bottom of the tube.      ##
          ## To prevent the pipette from crashing into the bottom, we       ##
          ## tell it to go home and pause the protocol so that this can     ##
          ## never happen. Set the location of where to aspirate from.      ##
          ## Because we put this in the loop, the location will change      ##
          ## to the newly calculated height after each pipetting step.      ##
        p300.aspirate(aspiration_vol, aspiration_location)
          ## Aspirate the amount specified in aspiration_vol from the       ##
          ## location specified in aspiration_location.                     ##
        p300.dispense(dispension_vol, well)
          ## Dispense the amount specified in dispension_vol to the         ##
          ## location specified in well (so a new well every time the       ##
          ## loop restarts)                                                 ##
        p300.dispense(10, aspiration_location)
          ## Alternative for blow-out, make sure the tip doesn't fill       ##
          ## completely when using a disposal volume by dispensing some     ##
          ## of the volume after each pipetting step. (blow-out to many     ##
          ## bubbles)                                                       ##
    p300.drop_tip()      
# =============================================================================


# =========================ADDING PRIMERS TO SAMPLES===========================
# =============================================================================
    ## For the columns in both the source (primers) and the destination     ##
    ## (mix): loop trough the wells in those columns.                       #
    for primer_tube, mix_tube in zip(primers, mastermix):
        p20.pick_up_tip()
        p20.aspirate(primer_vol, primer_tube)
        p20.dispense(primer_vol, mix_tube)
        primer_mix_vol = primer_vol + 3
        ## primer_mix_vol = volume for pipetting up and down                ##
        p20.mix(3, primer_mix_vol, mix_tube)
        p20.dispense(10, mix_tube)
        p20.drop_tip()
# =============================================================================


# ===================ADDING PRIMERS TO STD DILUTION SERIES=====================
# =============================================================================
    ## For the columns in both the source (primers) and the destination     ##
    ## (mix): loop trough the wells in those columns.                       #
    for well in std_primer_dests:
        p20.pick_up_tip()
        p20.aspirate(primer_vol, std_dilution_primer)
        p20.dispense(primer_vol, well)
        primer_mix_vol = primer_vol + 3
        ## primer_mix_vol = volume for pipetting up and down                ##
        p20.mix(3, primer_mix_vol, well)
        p20.dispense(10, well)
        p20.drop_tip()
Example #22
0
def run(ctx: protocol_api.ProtocolContext):
    from opentrons.drivers.rpi_drivers import gpio
    ctx.comment('Actual used columns: ' + str(num_cols))

    # Define the STEPS of the protocol
    STEP = 0
    STEPS = {  # Dictionary with STEP activation, description, and times
        1: {
            'Execute': True,
            'description': 'Add MS2'
        },
        2: {
            'Execute': True,
            'description': 'Transfer beads'
        }
    }
    for s in STEPS:  # Create an empty wait_time
        if 'wait_time' not in STEPS[s]:
            STEPS[s]['wait_time'] = 0

    folder_path = '/var/lib/jupyter/notebooks'
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        file_path = folder_path + '/Station_KB_sample_prep_viral_path2_time_log.txt'

    # Define Reagents as objects with their properties
    class Reagent:
        def __init__(self,
                     name,
                     flow_rate_aspirate,
                     flow_rate_dispense,
                     rinse,
                     reagent_reservoir_volume,
                     delay,
                     num_wells,
                     h_cono,
                     v_fondo,
                     tip_recycling='none'):
            self.name = name
            self.flow_rate_aspirate = flow_rate_aspirate
            self.flow_rate_dispense = flow_rate_dispense
            self.rinse = bool(rinse)
            self.reagent_reservoir_volume = reagent_reservoir_volume
            self.delay = delay
            self.num_wells = num_wells
            self.col = 0
            self.vol_well = 0
            self.h_cono = h_cono
            self.v_cono = v_fondo
            self.unused = []
            self.tip_recycling = tip_recycling
            self.vol_well_original = reagent_reservoir_volume / num_wells

    # Reagents and their characteristics
    Sample = Reagent(name='Sample',
                     flow_rate_aspirate=1,
                     flow_rate_dispense=1,
                     rinse=True,
                     delay=0,
                     reagent_reservoir_volume=460 * 96,
                     num_wells=96,
                     h_cono=1.95,
                     v_fondo=35)

    Beads = Reagent(name='Magnetic beads and binding solution',
                    flow_rate_aspirate=0.75,
                    flow_rate_dispense=0.75,
                    rinse=True,
                    num_wells=math.ceil(NUM_SAMPLES / 32),
                    delay=2,
                    reagent_reservoir_volume=550 * 8 * num_cols * 1.1,
                    h_cono=1.95,
                    v_fondo=695)  # Prismatic

    MS = Reagent(
        name='MS2',
        flow_rate_aspirate=1,
        flow_rate_dispense=1,
        rinse=False,
        reagent_reservoir_volume=total_MS_volume,
        num_wells=8,
        delay=0,
        h_cono=h_cone,
        v_fondo=volume_cone  # V cono
    )  # Prismatic)

    Sample.vol_well = Sample.reagent_reservoir_volume
    Beads.vol_well = Beads.vol_well_original
    MS.vol_well = MS.reagent_reservoir_volume

    def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol,
                              x_offset, pickup_height, rinse, disp_height,
                              blow_out, touch_tip):
        '''
        x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1]
        pickup_height: height from bottom where volume
        rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer
        disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered
        blow_out, touch_tip: if True they will be done after dispensing
        '''
        # Rinse before aspirating
        if rinse == True:
            custom_mix(pipet,
                       reagent,
                       location=source,
                       vol=vol,
                       rounds=2,
                       blow_out=True,
                       mix_height=0,
                       x_offset=x_offset)
        # SOURCE
        s = source.bottom(pickup_height).move(Point(x=x_offset[0]))
        pipet.aspirate(vol, s,
                       rate=reagent.flow_rate_aspirate)  # aspirate liquid
        if air_gap_vol != 0:  # If there is air_gap_vol, switch pipette to slow speed
            pipet.aspirate(air_gap_vol,
                           source.top(z=-2),
                           rate=reagent.flow_rate_aspirate)  # air gap
        # GO TO DESTINATION
        drop = dest.top(z=disp_height).move(Point(x=x_offset[1]))
        pipet.dispense(vol + air_gap_vol,
                       drop,
                       rate=reagent.flow_rate_dispense)  # dispense all
        ctx.delay(
            seconds=reagent.delay)  # pause for x seconds depending on reagent
        if blow_out == True:
            pipet.blow_out(dest.top(z=-2))
        if touch_tip == True:
            pipet.touch_tip(speed=20, v_offset=-5)

    def custom_mix(pipet,
                   reagent,
                   location,
                   vol,
                   rounds,
                   blow_out,
                   mix_height,
                   x_offset,
                   source_height=3):
        '''
        Function for mixing a given [vol] in the same [location] a x number of [rounds].
        blow_out: Blow out optional [True,False]
        x_offset = [source, destination]
        source_height: height from bottom to aspirate
        mix_height: height from bottom to dispense
        '''
        if mix_height == 0:
            mix_height = 3
        pipet.aspirate(1,
                       location=location.bottom(z=source_height).move(
                           Point(x=x_offset[0])),
                       rate=reagent.flow_rate_aspirate)
        for _ in range(rounds):
            pipet.aspirate(vol,
                           location=location.bottom(z=source_height).move(
                               Point(x=x_offset[0])),
                           rate=reagent.flow_rate_aspirate)
            pipet.dispense(vol,
                           location=location.bottom(z=mix_height).move(
                               Point(x=x_offset[1])),
                           rate=reagent.flow_rate_dispense)
        pipet.dispense(1,
                       location=location.bottom(z=mix_height).move(
                           Point(x=x_offset[1])),
                       rate=reagent.flow_rate_dispense)
        if blow_out == True:
            pipet.blow_out(location.top(z=-2))  # Blow out

    def calc_height(reagent,
                    cross_section_area,
                    aspirate_volume,
                    min_height=0.5,
                    extra_volume=50):
        nonlocal ctx
        ctx.comment('Remaining volume ' + str(reagent.vol_well) +
                    '< needed volume ' + str(aspirate_volume) + '?')
        if reagent.vol_well < aspirate_volume + extra_volume:
            reagent.unused.append(reagent.vol_well)
            ctx.comment('Next column should be picked')
            ctx.comment('Previous to change: ' + str(reagent.col))
            # column selector position; intialize to required number
            reagent.col = reagent.col + 1
            ctx.comment(str('After change: ' + str(reagent.col)))
            reagent.vol_well = reagent.vol_well_original
            ctx.comment('New volume:' + str(reagent.vol_well))
            height = (reagent.vol_well - aspirate_volume -
                      reagent.v_cono) / cross_section_area
            #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Remaining volume:' + str(reagent.vol_well))
            if height < min_height:
                height = min_height
            col_change = True
        else:
            height = (reagent.vol_well - aspirate_volume -
                      reagent.v_cono) / cross_section_area  #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Calculated height is ' + str(height))
            if height < min_height:
                height = min_height
            ctx.comment('Used height is ' + str(height))
            col_change = False
        return height, col_change

    def divide_destinations(l, n):
        # Divide the list of destinations in size n lists.
        for i in range(0, len(l), n):
            yield l[i:i + n]

    ####################################
    # load labware and modules
    # 12 well rack
    reagent_res = ctx.load_labware('perkinelmer_12_reservoir_21000ul', '2',
                                   'Reagent deepwell plate')

    ##################################
    # Sample prep plate - final plate, goes to Kingfisher
    sample_plate = ctx.load_labware('kf_96_wellplate_2400ul', '1',
                                    'KF 96 Well 2400ul elution plate')

    ############################################
    # tempdeck
    tempdeck = ctx.load_module('tempdeck', '4')
    tempdeck.set_temperature(temperature)

    ##################################
    # MS plate -  plate with a column containing the internal control MS
    ms_plate = tempdeck.load_labware('vwr_96_wellplate_200ul_alum_opentrons',
                                     'pcr plate with MS control')

    ####################################
    # load labware and modules
    # 24 well rack aluminium opentrons
    #tuberack = ctx.load_labware(
    #    'opentrons_24_aluminumblock_generic_2ml_screwcap', '3',
    #    'Bloque Aluminio opentrons 24 Screwcaps')

    ##################################
    # Load Tipracks
    tips20 = [
        ctx.load_labware('opentrons_96_filtertiprack_20ul', slot)
        for slot in ['6']
    ]

    tips200 = [
        ctx.load_labware('opentrons_96_filtertiprack_200ul', slot)
        for slot in ['3']
    ]

    # pipettes. P1000 currently deactivated
    m300 = ctx.load_instrument('p300_multi_gen2', 'right',
                               tip_racks=tips200)  # Load multi pipette
    m20 = ctx.load_instrument('p20_multi_gen2', 'left', tip_racks=tips20)

    #p20 = ctx.load_instrument(
    #    'p20_single_gen2', 'left', tip_racks=tips20)  # load P1000 pipette

    tip_track = {
        'counts': {
            m300: 0,
            m20: 0
        },
        'maxes': {
            m300: len(tips200) * 96,
            m20: len(tips20) * 96
        }
    }

    # Divide destination wells in small groups for P300 pipette
    # Declare which reagents are in each reservoir as well as deepwell and elution plate
    #destinations = list(divide_destinations(sample_plate.wells()[:NUM_SAMPLES], size_transfer))
    Beads.reagent_reservoir = reagent_res.rows(
    )[0][:Beads.num_wells]  # 1 row, 4 columns (first ones)
    work_destinations = sample_plate.wells()[:NUM_SAMPLES]
    work_destinations_cols = sample_plate.rows()[0][:num_cols]
    ms_origins = ms_plate.rows()[0][0]  # 1 row, 1 columns

    ############################################################################
    # STEP 1: Transfer MS
    ############################################################################

    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        ctx.comment('ms_wells')
        #Loop over defined wells
        for d in work_destinations_cols:
            m20.pick_up_tip()
            #Source samples
            move_vol_multichannel(m20,
                                  reagent=MS,
                                  source=ms_origins,
                                  dest=d,
                                  vol=MS_vol,
                                  air_gap_vol=air_gap_vol_MS,
                                  x_offset=x_offset,
                                  pickup_height=0.2,
                                  disp_height=-35,
                                  rinse=False,
                                  blow_out=True,
                                  touch_tip=True)
            m20.drop_tip()
            tip_track['counts'][m20] += 8

        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    # Export the time log to a tsv file
    if not ctx.is_simulating():
        with open(file_path, 'w') as f:
            f.write(
                'STEP\texecution\tdescription\twait_time\texecution_time\n')
            for key in STEPS.keys():
                row = str(key)
                for key2 in STEPS[key].keys():
                    row += '\t' + format(STEPS[key][key2])
                f.write(row + '\n')
        f.close()

    ############################################################################
    # STEP 2: TRANSFER BEADS
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        # Transfer parameters
        start = datetime.now()
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')
        beads_transfer_vol = [150, 150, 150,
                              100]  # 4 rounds of different volumes
        rinse = True
        for i in range(num_cols):
            if not m300.hw_pipette['has_tip']:
                m300.pick_up_tip()
            for j, transfer_vol in enumerate(beads_transfer_vol):
                # Calculate pickup_height based on remaining volume and shape of container
                [pickup_height, change_col] = calc_height(Beads,
                                                          multi_well_rack_area,
                                                          transfer_vol * 8,
                                                          min_height=1)
                if change_col == True:  # If we switch column because there is not enough volume left in current reservoir column we mix new column
                    ctx.comment('Mixing new reservoir column: ' +
                                str(Beads.col))
                    custom_mix(m300,
                               Beads,
                               Beads.reagent_reservoir[Beads.col],
                               vol=170,
                               rounds=10,
                               blow_out=False,
                               mix_height=0,
                               x_offset=x_offset)
                ctx.comment('Aspirate from reservoir column: ' +
                            str(Beads.col))
                ctx.comment('Pickup height is ' + str(pickup_height))
                if j != 0:
                    rinse = False
                move_vol_multichannel(
                    m300,
                    reagent=Beads,
                    source=Beads.reagent_reservoir[Beads.col],
                    dest=work_destinations_cols[i],
                    vol=transfer_vol,
                    air_gap_vol=air_gap_vol,
                    x_offset=x_offset,
                    pickup_height=pickup_height,
                    disp_height=-2,
                    rinse=rinse,
                    blow_out=False,
                    touch_tip=False)

        m300.drop_tip(home_after=False)
        tip_track['counts'][m300] += 8
        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    # Export the time log to a tsv file
    if not ctx.is_simulating():
        with open(file_path, 'w') as f:
            f.write(
                'STEP\texecution\tdescription\twait_time\texecution_time\n')
            for key in STEPS.keys():
                row = str(key)
                for key2 in STEPS[key].keys():
                    row += '\t' + format(STEPS[key][key2])
                f.write(row + '\n')
        f.close()

    ############################################################################
    # Light flash end of program
    gpio.set_rail_lights(False)
    time.sleep(2)
    #os.system('mpg123 -f -8000 /var/lib/jupyter/notebooks/toreador.mp3 &')
    for i in range(3):
        gpio.set_rail_lights(False)
        gpio.set_button_light(1, 0, 0)
        time.sleep(0.3)
        gpio.set_rail_lights(True)
        gpio.set_button_light(0, 0, 1)
        time.sleep(0.3)
    gpio.set_button_light(0, 1, 0)
    ctx.comment('Finished! \nMove plate to KingFisher')
Example #23
0
def run(protocol: protocol_api.ProtocolContext):
    ###########################LABWARE SETUP#################################

    tiprack_300 = protocol.load_labware('opentrons_96_tiprack_300ul',
                                        labwarePositions.tiprack_300,
                                        'tiprack 300ul')

    pipette_300 = protocol.load_instrument(
        'p300_single_gen2' if pipette_300_GEN == 'GEN2' else 'p300_single',
        pipette_300_location,
        tip_racks=[tiprack_300])
    pipette_300.flow_rate.dispense = default_flow_rate
    pipette_300.flow_rate.aspirate = default_flow_rate
    pipette_300.starting_tip = tiprack_300.well(
        tiprack_starting_pos['tiprack_300'])

    par2 = protocol.load_labware(par2_type, labwarePositions.par2, 'PAR2')
    trough12 = protocol.load_labware('parhelia_12trough',
                                     labwarePositions.buffers_plate,
                                     '12-trough buffers reservoir')

    black_96 = protocol.load_labware('parhelia_black_96',
                                     labwarePositions.antibodies_plate,
                                     '96-well-plate')

    buffer_wells = trough12.wells_by_name()

    buffers = Object()
    buffers.Hydration_PFA_1pt6pct = buffer_wells['A1']
    buffers.Staining = buffer_wells['A2']
    buffers.Storage_PFA_4pct = buffer_wells['A3']
    buffers.MeOH = buffer_wells['A4']
    buffers.PBS = buffer_wells['A5']
    buffers.CODEX_buffer_1x = buffer_wells['A6']
    buffers.Screening_Buffer = buffer_wells['A7']
    buffers.Stripping_buffer = buffer_wells['A8']
    buffers.storage = buffer_wells['A9']

    preblock_wells = black_96.rows()[0]
    antibody_wells = black_96.rows()[1]
    reagent_F_wells = black_96.rows()[2]
    rendering_wells = black_96.rows()[3]

    sample_chambers = []

    for well in wellslist:
        sample_chambers.append(par2.wells_by_name()[well])

    #################PROTOCOL####################
    protocol.comment("Starting the CODEX staining protocol for samples:" +
                     str(sample_chambers))

    if not FFPE:
        #WASHING SAMPLES WITH PFA
        protocol.comment("first fix")
        washSamples(pipette_300, buffers.Hydration_PFA_1pt6pct,
                    buffers.Hydration_PFA_1pt6pct, 0, 1, extra_bottom_gap)
        washSamples(pipette_300, buffers.Hydration_PFA_1pt6pct,
                    sample_chambers, wash_volume, 1, extra_bottom_gap)
        #INCUBATE
        protocol.delay(minutes=10, msg="first fix incubation")

    #WASHING SAMPLES WITH S2
    protocol.comment("washing in S2")
    washSamples(pipette_300, buffers.Staining, buffers.Staining, 0, 1,
                extra_bottom_gap)
    washSamples(pipette_300, buffers.Staining, sample_chambers, wash_volume, 2,
                extra_bottom_gap)

    #WASHING SAMPLES WITH PREBLOCK
    protocol.comment("preblocking")
    for i in range(len(wellslist)):
        washSamples(pipette_300, preblock_wells[i], sample_chambers[i],
                    wash_volume, 1, extra_bottom_gap)
    #INCUBATE
    protocol.delay(minutes=15, msg="preblocking incubation")

    #APPLYING ANTIBODY COCKTAILS TO SAMPLES
    protocol.comment("applying antibodies")
    for i in range(len(wellslist)):
        washSamples(pipette_300, antibody_wells[i], sample_chambers[i],
                    ab_volume, 1, extra_bottom_gap)
    #INCUBATE
    protocol.delay(minutes=ab_incubation_time_minutes,
                   msg="staining incubation")

    for i in range(2):
        #WASHING SAMPLES WITH Staining buffer
        protocol.comment("first washing with Staining buffer")
        washSamples(pipette_300, buffers.Staining, sample_chambers,
                    wash_volume, 2, extra_bottom_gap)
        #INCUBATE
        protocol.delay(minutes=5, msg="first incubation in Staining Buffer")

    #POST STAINING FIXING SAMPLES WITH PFA
    protocol.comment("second fix")
    washSamples(pipette_300, buffers.Storage_PFA_4pct,
                buffers.Storage_PFA_4pct, 0, 1, extra_bottom_gap)
    washSamples(pipette_300, buffers.Storage_PFA_4pct, sample_chambers,
                wash_volume, 1, extra_bottom_gap)
    #INCUBATE
    protocol.delay(minutes=5, msg="incubation with fixative")

    #WASHING SAMPLES WITH PBS
    protocol.comment("PBS wash")
    washSamples(pipette_300, buffers.PBS, buffers.PBS, 0, 2, extra_bottom_gap)
    washSamples(pipette_300, buffers.PBS, sample_chambers, wash_volume, 2,
                extra_bottom_gap)

    # FIXING SAMPLES WITH Methanol
    washSamples(pipette_300, buffers.MeOH, buffers.MeOH, 0, 1,
                extra_bottom_gap)
    for i in range(2):
        protocol.comment("applying MeOH")
        washSamples(pipette_300, buffers.MeOH, sample_chambers, wash_volume, 1,
                    extra_bottom_gap)
        # INCUBATE
        protocol.delay(minutes=2.5, msg="First MeOH incubation")

    #WASHING SAMPLES WITH PBS
    protocol.comment("PBS wash")
    washSamples(pipette_300, buffers.PBS, sample_chambers, wash_volume, 2,
                extra_bottom_gap)

    #DILUTING AND APPLYING THE FIXATIVE
    for i in range(len(wellslist)):
        dilute_and_apply_fixative(pipette_300, reagent_F_wells[i], buffers.PBS,
                                  sample_chambers[i], 150)

    protocol.comment("third fix incubation")
    protocol.delay(minutes=10, msg="Reagent F incubation")

    #WASHING SAMPLES WITH PBS
    protocol.comment("PBS wash")
    washSamples(pipette_300, buffers.PBS, sample_chambers, wash_volume, 2,
                extra_bottom_gap)

    if Antibody_Screening:
        washSamples(pipette_300, buffers.Stripping_buffer,
                    buffers.Stripping_buffer, 0, 1, extra_bottom_gap)
        washSamples(pipette_300, buffers.Screening_Buffer,
                    buffers.Screening_Buffer, 0, 1, extra_bottom_gap)
        #PRE-CLEARING THE TISSUE
        for i in range(3):
            protocol.comment("tissue clearing round" + str(i + 1))
            washSamples(pipette_300, buffers.Stripping_buffer, sample_chambers,
                        wash_volume, 2, extra_bottom_gap)
            protocol.delay(seconds=30)
            washSamples(pipette_300, buffers.Screening_Buffer, sample_chambers,
                        wash_volume, 1, extra_bottom_gap)
            washSamples(pipette_300, buffers.CODEX_buffer_1x, sample_chambers,
                        wash_volume, 1, extra_bottom_gap)

        #Equilibration in rendering buffer
        protocol.comment("Equilibration in rendering buffer")
        washSamples(pipette_300, buffers.Screening_Buffer, sample_chambers,
                    wash_volume, 1, extra_bottom_gap)

        #RENDERING
        protocol.comment("Applying rendering solution to wells")
        for i in range(len(wellslist)):
            washSamples(pipette_300, rendering_wells[i], sample_chambers[i],
                        wash_volume, 1, extra_bottom_gap)
        #INCUBATE
        protocol.delay(minutes=10, msg="rendering hybridization")

        #WASH SAMPLES IN 1x CODEX buffer
        protocol.comment("Washing with rendering buffer")
        washSamples(pipette_300, buffers.Screening_Buffer, sample_chambers,
                    wash_volume, 2, extra_bottom_gap)

    #STORAGE, washing samples every hour for 100 hours
    washSamples(pipette_300, buffers.storage, buffers.storage, 0, 1,
                extra_bottom_gap)
    for i in range(10):
        washSamples(pipette_300,
                    buffers.storage,
                    sample_chambers,
                    wash_volume / 3,
                    1,
                    extra_bottom_gap,
                    keep_tip=True)
        protocol.delay(minutes=90, msg="storing samples in storage buffer")
Example #24
0
def run(ctx: protocol_api.ProtocolContext):
    # Init protocol run
    run = ProtocolRun(ctx)
    run.comment("You are about to run %s samples\n STEPS:%s" %
                (NUM_SAMPLES, steps),
                add_hash=True)
    run.pause(
        "Are you sure the set up is correct? Check the desk before continue")

    # Define stesp
    run.add_step(
        description="Transfer PK A6 - To AW_PLATE Single Slot1 -> Slot2")  # 1
    run.add_step(description="Transfer MS2 B6 - To AW_PLATE Single 1->2")  # 4
    run.add_step(description="Transfer Beats 3 - 2 Multi and mix")  # 3

    # execute avaliaible steps
    run.init_steps(steps)

    ##################################

    # Tube rack
    tube_rack = ctx.load_labware('opentrons_24_tuberack_nest_1.5ml_screwcap',
                                 4)

    # Destination plate SLOT 2
    if (ctx.is_simulating()):
        aw_slot = ctx.load_labware(
            'opentrons_96_aluminumblock_generic_pcr_strip_200ul', 5)
    else:
        aw_slot = ctx.load_labware('axygen_96_wellplate_2000ul', 5)

    aw_wells = aw_slot.wells()[:NUM_SAMPLES]
    aw_wells_multi = aw_slot.rows()[0][:num_cols]

    # Magnetic Beads Pool
    beads_slot = ctx.load_labware('nest_12_reservoir_15ml', 6)
    beads_wells_multi = beads_slot.rows()[0][:num_cols]

    # Mount pippets and set racks
    # Tipracks20_multi
    tips20 = ctx.load_labware('opentrons_96_tiprack_20ul', 7)
    tips300_1 = ctx.load_labware('opentrons_96_filtertiprack_200ul', 8)
    tips300_2 = ctx.load_labware('opentrons_96_filtertiprack_200ul', 9)

    run.mount_right_pip('p20_single_gen2', tip_racks=[tips20], capacity=20)
    run.mount_left_pip('p300_multi_gen2',
                       tip_racks=[tips300_1, tips300_2],
                       capacity=200,
                       multi=True)

    ############################################################################
    # STEP 1: Transfer A6 - To AW_PLATE
    ############################################################################
    if (run.next_step()):
        run.set_pip("right")  # single 20
        volumen_move = 5
        source = tube_rack.wells("A6")[0]
        liquid = Reagent(
            name='Proteinasa K',
            num_wells=1,  # change with num samples
            flow_rate_aspirate=0.75,  # Original 0.5
            flow_rate_dispense=3,  # Original 1
            reagent_reservoir_volume=528,
            h_cono=4,
            v_fondo=4 * math.pi * 4**3 / 3)

        run.pick_up()
        for dest in aw_wells:
            [pickup_height,
             col_change] = run.calc_height(liquid, 4.12 * 4.12 * math.pi,
                                           volumen_move)
            run.move_volume(reagent=liquid,
                            source=source,
                            dest=dest,
                            vol=volumen_move,
                            air_gap_vol=1,
                            pickup_height=pickup_height,
                            disp_height=-10,
                            blow_out=True,
                            post_dispense=True,
                            post_dispense_vol=5)

        run.drop_tip()
        run.finish_step()

    ############################################################################
    # STEP 2: Transfer B6 MS2 - To AW_PLATE
    ############################################################################
    if (run.next_step()):
        run.set_pip("right")  # single 20
        volumen_move = 5
        source = tube_rack.wells("B6")[0]
        liquid = Reagent(
            name='MS2',
            num_wells=1,  # change with num samples
            delay=0,
            flow_rate_aspirate=3,  # Original 0.5
            flow_rate_dispense=3,  # Original 1
            flow_rate_aspirate_mix=15,
            flow_rate_dispense_mix=25,
            reagent_reservoir_volume=528,
            h_cono=4,
            v_fondo=4 * math.pi * 4**3 / 3)
        run.pick_up()
        for dest in aw_wells:

            [pickup_height,
             col_change] = run.calc_height(liquid, 4.12 * 4.12 * math.pi,
                                           volumen_move)
            run.move_volume(reagent=liquid,
                            source=source,
                            dest=dest,
                            vol=volumen_move,
                            air_gap_vol=1,
                            pickup_height=pickup_height,
                            disp_height=-10,
                            blow_out=True,
                            post_dispense=True,
                            post_dispense_vol=5)

        run.drop_tip()

        run.finish_step()

    ############################################################################
    # STEP 3: Slot 3 -2 beats_PK AW
    ############################################################################
    if (run.next_step()):
        ############################################################################
        # Light flash end of program
        run.set_pip("left")  # p300 multi
        volume = 275
        beads = Reagent(name='Magnetic beads',
                        flow_rate_aspirate=0.5,
                        flow_rate_dispense=0.5,
                        flow_rate_dispense_mix=4,
                        flow_rate_aspirate_mix=4,
                        rinse=True,
                        delay=2,
                        reagent_reservoir_volume=30000,
                        num_wells=3,
                        h_cono=1.95,
                        v_fondo=695,
                        rinse_loops=3)

        air_gap_vol = 5
        disposal_height = -5
        pickup_height = 1
        beads.reagent_reservoir = beads_slot.rows()[0][0:3]
        pool_area = 8.3 * 71.1

        for destination in aw_wells_multi:
            run.pick_up()
            vol = 150
            vol_min = 1000
            [pickup_height, col_change] = run.calc_height(beads,
                                                          pool_area,
                                                          vol * 8,
                                                          extra_volume=vol_min)
            run.move_volume(reagent=beads,
                            source=beads.reagent_reservoir[beads.col],
                            dest=destination,
                            vol=vol,
                            air_gap_vol=air_gap_vol,
                            pickup_height=pickup_height,
                            disp_height=disposal_height,
                            rinse=True,
                            blow_out=True)
            run.change_tip()
            vol = 125
            [pickup_height, col_change] = run.calc_height(beads,
                                                          pool_area,
                                                          vol * 8,
                                                          extra_volume=vol_min)
            run.move_volume(reagent=beads,
                            source=beads.reagent_reservoir[beads.col],
                            dest=destination,
                            vol=vol,
                            air_gap_vol=air_gap_vol,
                            pickup_height=pickup_height,
                            disp_height=disposal_height,
                            rinse=True,
                            blow_out=True)

            run.custom_mix(beads,
                           location=destination,
                           vol=150,
                           rounds=3,
                           blow_out=True,
                           mix_height=0)

            run.drop_tip()

        run.finish_step()

    run.log_steps_time()
    run.blink()
    for c in robot.commands():
        ctx.comment(c)
    ctx.comment('Finished! \nMove plate to PCR')
def run(ctx: protocol_api.ProtocolContext):
    # Init protocol run
    run = ProtocolRun(ctx)
    
    # Define stesp
    run.add_step(
        description="Transfer PK+MS2 A6 - To AW_PLATE Single Slot1 -> Slot2")  # 1
    run.add_step(description="Transfer Beats 3 - 2 Multi and mix")  # 2

    # execute avaliaible steps
    run.init_steps(steps)

    ##################################

    # Tube rack
    tube_rack = ctx.load_labware(
        'opentrons_24_tuberack_nest_1.5ml_screwcap', 4)

    # Destination plate SLOT 2
    if(ctx.is_simulating()):
        aw_slot = ctx.load_labware(
            'opentrons_96_aluminumblock_generic_pcr_strip_200ul', 5)
    else:
        aw_slot = ctx.load_labware(
            'axygen_96_wellplate_2000ul', 5)

    aw_wells = aw_slot.wells()[:NUM_SAMPLES]

    # Mount pippets and set racks
    # Tipracks20_multi
    tips20_1 = ctx.load_labware('opentrons_96_tiprack_20ul', 7)
    tips20_2 = ctx.load_labware('opentrons_96_tiprack_20ul', 8)
    
    run.mount_right_pip('p20_single_gen2', tip_racks=[tips20_1,tips20_2], capacity=20)
    
    tips300 = ctx.load_labware('opentrons_96_filtertiprack_200ul', 7)
    
    run.mount_right_pip('p20_single_gen2', tip_racks=[tips20_1,tips20_2], capacity=20)

    ############################################################################
    # STEP 1: Transfer A6 - To AW_PLATE
    ############################################################################
    if (run.next_step()):
        run.set_pip("right")  # single 20
        volumen_move = 5
        source = tube_rack.wells("A6")[0]
        pkms2 = Reagent(
                         name='PK + MS2',
                         num_wells=1,  # change with num samples
                         flow_rate_aspirate=0.75,  # Original 0.5
                         flow_rate_dispense=3,  # Original 1
                         reagent_reservoir_volume=vol_pkms2*(NUM_SAMPLES+1),
                         h_cono=4,
                         v_fondo=4 * math.pi * 4 ** 3 / 3
                         )
        pkms2.set_positions([tube_rack.wells("A5"),tube_rack.wells("B5"),tube_rack.wells("B6")])
        run.comment(pkms2.get_volumes_fill_print(),add_hash=True)

        run.pick_up()
        for dest in aw_wells:
            pickup_height = run.calc_height(
                liquid, 4.12*4.12*math.pi, vol_pkms2)
            run.move_volume(reagent=liquid, source=source,
                            dest=pkms2.get_current_position(), vol=vol_pkms2, air_gap_vol=1,
                            pickup_height=pickup_height, disp_height=-10,
                            blow_out=True, post_dispense=True, post_dispense_vol=5)

        run.drop_tip()
        run.finish_step()

    ############################################################################
    # STEP 2: Slot 3 -2 beats_PK AW
    ############################################################################
    if (run.next_step()):
        ############################################################################
        # Light flash end of program
        run.set_pip("left")  # p300 multi
        volume = 275
        beads = Reagent(name='Magnetic beads',
                        flow_rate_aspirate=0.5,
                        flow_rate_dispense=0.5,
                        flow_rate_dispense_mix=4,
                        flow_rate_aspirate_mix=4,
                        rinse=True,
                        delay=2,
                        reagent_reservoir_volume=vol_beads*(NUM_SAMPLES+1),
                        h_cono=1.95,
                        v_fondo=695,
                        rinse_loops=3)

        beads.set_positions([tube_rack.wells("A6"),tube_rack.wells("B6")])
        air_gap_vol = 5
        disposal_height = -5
        
        for destination in aw_wells:
            
            volumes = beads.divide_volume(vol_beads,180)
            for vol in volumes:
                run.pick_up()
                pickup_height = beads.calc_height(
                    beads, pool_area, vol, extra_volume=vol_min)
                run.move_volume(reagent=beads, source=beads.get_current_position(),
                                dest=destination, vol=vol, air_gap_vol=air_gap_vol,
                                pickup_height=pickup_height, disp_height=disposal_height,
                                rinse=True, blow_out=True)
                
                run.custom_mix(beads, location=destination, vol=vol/2,
                            rounds=3, blow_out=True, mix_height=0)
                run.drop_tip()

        run.finish_step()

    run.log_steps_time()
    run.blink()
    for c in robot.commands():
        ctx.comment(c)
    ctx.comment('Finished! \nMove plate to PCR')
Example #26
0
def run(ctx: protocol_api.ProtocolContext):
    ctx.comment('Actual used columns: ' + str(num_cols))

    # Define the STEPS of the protocol
    STEP = 0
    STEPS = {  # Dictionary with STEP activation, description, and times
        1: {
            'Execute': True,
            'description': 'Transfer Mmix'
        },
        2: {
            'Execute': True,
            'description': 'Transfer samples'
        },
        3: {
            'Execute': True,
            'description': 'Transfer negative control'
        },
        4: {
            'Execute': True,
            'description': 'Transfer positive control'
        }
    }

    for s in STEPS:  # Create an empty wait_time
        if 'wait_time' not in STEPS[s]:
            STEPS[s]['wait_time'] = 0

    #Folder and file_path for log time
    folder_path = '/var/lib/jupyter/notebooks' + run_id
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        file_path = folder_path + '/Station_C_Vitro_time_log.txt'

    # Define Reagents as objects with their properties
    class Reagent:
        def __init__(self,
                     name,
                     flow_rate_aspirate,
                     flow_rate_dispense,
                     rinse,
                     reagent_reservoir_volume,
                     delay,
                     num_wells,
                     h_cono,
                     v_fondo,
                     tip_recycling='none'):
            self.name = name
            self.flow_rate_aspirate = flow_rate_aspirate
            self.flow_rate_dispense = flow_rate_dispense
            self.rinse = bool(rinse)
            self.reagent_reservoir_volume = reagent_reservoir_volume
            self.delay = delay
            self.num_wells = num_wells
            self.col = 0
            self.vol_well = 0
            self.h_cono = h_cono
            self.v_cono = v_fondo
            self.unused = []
            self.tip_recycling = tip_recycling
            self.vol_well_original = reagent_reservoir_volume / num_wells

    # Reagents and their characteristics
    Mmix = Reagent(
        name='Mmix',
        rinse=False,
        flow_rate_aspirate=3,
        flow_rate_dispense=3,
        reagent_reservoir_volume=1800,
        num_wells=1,  #change with num samples
        delay=0,
        h_cono=h_cone,
        v_fondo=volume_cone  # V cono
    )

    Samples = Reagent(
        name='Samples',
        rinse=False,
        flow_rate_aspirate=1,
        flow_rate_dispense=1,
        reagent_reservoir_volume=50,
        delay=0,
        num_wells=num_cols,  # num_cols comes from available columns
        h_cono=0,
        v_fondo=0)

    Mmix.vol_well = Mmix.vol_well_original
    Samples.vol_well = Samples.vol_well_original

    ##################
    # Custom functions
    def divide_volume(volume, max_vol):
        num_transfers = math.ceil(volume / max_vol)
        vol_roundup = math.ceil(volume / num_transfers)
        last_vol = volume - vol_roundup * (num_transfers - 1)
        vol_list = [vol_roundup for v in range(1, num_transfers)]
        vol_list.append(last_vol)
        return vol_list

    def divide_destinations(l, n):
        # Divide the list of destinations in size n lists.
        for i in range(0, len(l), n):
            yield l[i:i + n]

    def distribute_custom(pipette,
                          volume,
                          src,
                          dest,
                          waste_pool,
                          pickup_height,
                          extra_dispensal,
                          dest_x_offset,
                          disp_height=0):
        # Custom distribute function that allows for blow_out in different location and adjustement of touch_tip
        pipette.aspirate((len(dest) * volume) + extra_dispensal,
                         src.bottom(pickup_height))
        pipette.touch_tip(speed=20, v_offset=-5)
        pipette.move_to(src.top(z=5))
        pipette.aspirate(5)  # air gap
        for d in dest:
            pipette.dispense(5, d.top())
            drop = d.top(z=disp_height).move(Point(x=dest_x_offset))
            pipette.dispense(volume, drop)
            pipette.move_to(d.top(z=5))
            pipette.aspirate(5)  # air gap
        try:
            pipette.blow_out(waste_pool.wells()[0].bottom(pickup_height + 3))
        except:
            pipette.blow_out(waste_pool.bottom(pickup_height + 3))
        return (len(dest) * volume)

    def move_vol_multichannel(pipet, reagent, source, dest, vol, air_gap_vol,
                              x_offset, pickup_height, rinse, disp_height,
                              blow_out, touch_tip):
        '''
        x_offset: list with two values. x_offset in source and x_offset in destination i.e. [-1,1]
        pickup_height: height from bottom where volume
        rinse: if True it will do 2 rounds of aspirate and dispense before the tranfer
        disp_height: dispense height; by default it's close to the top (z=-2), but in case it is needed it can be lowered
        blow_out, touch_tip: if True they will be done after dispensing
        '''
        # Rinse before aspirating
        if rinse == True:
            custom_mix(pipet,
                       reagent,
                       location=source,
                       vol=vol,
                       rounds=2,
                       blow_out=True,
                       mix_height=0,
                       x_offset=x_offset)
        # SOURCE
        s = source.bottom(pickup_height).move(Point(x=x_offset[0]))
        pipet.aspirate(vol, s)  # aspirate liquid
        if air_gap_vol != 0:  # If there is air_gap_vol, switch pipette to slow speed
            pipet.aspirate(air_gap_vol,
                           source.top(z=-2),
                           rate=reagent.flow_rate_aspirate)  # air gap
        # GO TO DESTINATION
        drop = dest.top(z=disp_height).move(Point(x=x_offset[1]))
        pipet.dispense(vol + air_gap_vol,
                       drop,
                       rate=reagent.flow_rate_dispense)  # dispense all
        ctx.delay(
            seconds=reagent.delay)  # pause for x seconds depending on reagent
        if blow_out == True:
            pipet.blow_out(dest.top(z=-2))
        if touch_tip == True:
            pipet.touch_tip(speed=20, v_offset=-5, radius=0.5)

    def custom_mix(pipet,
                   reagent,
                   location,
                   vol,
                   rounds,
                   blow_out,
                   mix_height,
                   x_offset,
                   source_height=3):
        '''
        Function for mixing a given [vol] in the same [location] a x number of [rounds].
        blow_out: Blow out optional [True,False]
        x_offset = [source, destination]
        source_height: height from bottom to aspirate
        mix_height: height from bottom to dispense
        '''
        if mix_height <= 0:
            mix_height = 3
        pipet.aspirate(1,
                       location=location.bottom(z=source_height).move(
                           Point(x=x_offset[0])),
                       rate=reagent.flow_rate_aspirate)
        for _ in range(rounds):
            pipet.aspirate(vol,
                           location=location.bottom(z=source_height).move(
                               Point(x=x_offset[0])),
                           rate=reagent.flow_rate_aspirate)
            pipet.dispense(vol,
                           location=location.bottom(z=mix_height).move(
                               Point(x=x_offset[1])),
                           rate=reagent.flow_rate_dispense)
        pipet.dispense(1,
                       location=location.bottom(z=mix_height).move(
                           Point(x=x_offset[1])),
                       rate=reagent.flow_rate_dispense)
        if blow_out == True:
            pipet.blow_out(location.top(z=-2))  # Blow out

    def calc_height(reagent,
                    cross_section_area,
                    aspirate_volume,
                    min_height=0.5):
        nonlocal ctx
        ctx.comment('Remaining volume ' + str(reagent.vol_well) +
                    '< needed volume ' + str(aspirate_volume) + '?')
        if reagent.vol_well < aspirate_volume:
            reagent.unused.append(reagent.vol_well)
            ctx.comment('Next column should be picked')
            ctx.comment('Previous to change: ' + str(reagent.col))
            # column selector position; intialize to required number
            reagent.col = reagent.col + 1
            ctx.comment(str('After change: ' + str(reagent.col)))
            reagent.vol_well = reagent.vol_well_original
            ctx.comment('New volume:' + str(reagent.vol_well))
            height = (reagent.vol_well - aspirate_volume -
                      reagent.v_cono) / cross_section_area
            #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Remaining volume:' + str(reagent.vol_well))
            if height < min_height:
                height = min_height
            col_change = True
        else:
            height = (reagent.vol_well - aspirate_volume -
                      reagent.v_cono) / cross_section_area  #- reagent.h_cono
            reagent.vol_well = reagent.vol_well - aspirate_volume
            ctx.comment('Calculated height is ' + str(height))
            if height < min_height:
                height = min_height
            ctx.comment('Used height is ' + str(height))
            col_change = False
        return height, col_change

    ####################################
    # load labware and modules
    # 24 well rack
    tuberack = ctx.load_labware(
        'opentrons_24_aluminumblock_generic_2ml_screwcap', '2',
        'Opentrons 24 Well Aluminum Block with Generic 2 mL Screwcap')

    ############################################
    # tempdecks
    tempdeck_orig = ctx.load_module('Temperature Module Gen2', '4')
    tempdeck_dest = ctx.load_module('Temperature Module Gen2', '1')

    if SET_TEMP_ON_SLOT_4:
        tempdeck_orig.set_temperature(TEMPERATURE_SLOT_4)
    if SET_TEMP_ON_SLOT_1:
        tempdeck_dest.set_temperature(TEMPERATURE_SLOT_1)

    ##################################
    # Sample plate - comes from B
    source_plate = tempdeck_orig.load_labware(
        'kingfisher_96_aluminumblock_200ul',
        'Kingfisher 96 Aluminum Block 200 uL')
    samples = source_plate.wells()[:NUM_SAMPLES]

    ##################################
    # qPCR plate - final plate, goes to PCR
    qpcr_plate = tempdeck_dest.load_labware(
        'opentrons_96_aluminumblock_nest_wellplate_100ul',
        'Opentrons 96 Well Aluminum Block with NEST Well Plate 100 uL')

    ##################################
    # Load Tipracks
    tips20 = [
        ctx.load_labware('opentrons_96_filtertiprack_20ul', slot)
        for slot in ['5']
    ]

    tips200 = [
        ctx.load_labware('opentrons_96_filtertiprack_200ul', slot)
        for slot in ['3']
    ]

    ################################################################################
    # Declare which reagents are in each reservoir as well as deepwell and elution plate
    Mmix.reagent_reservoir = tuberack.rows()[0][0]  # A1

    # setup up sample sources and destinations
    samples = source_plate.wells()[2:NUM_SAMPLES]
    pcr_wells = qpcr_plate.wells()[:NUM_SAMPLES]
    pcr_wells_samples = qpcr_plate.wells()[2:NUM_SAMPLES]

    # Divide destination wells in small groups for P300 pipette
    dests = list(divide_destinations(pcr_wells, size_transfer))

    # pipettes
    p20 = ctx.load_instrument('p20_single_gen2',
                              mount='right',
                              tip_racks=tips20)
    p300 = ctx.load_instrument('p300_single_gen2',
                               mount='left',
                               tip_racks=tips200)

    # used tip counter and set maximum tips available
    tip_track = {
        'counts': {
            p300: 0,
            p20: 0
        },
        'maxes': {
            p300: 96 * len(p300.tip_racks),
            p20: 96 * len(p20.tip_racks)
        }
    }

    ##########
    # pick up tip and if there is none left, prompt user for a new rack
    def pick_up(pip):
        nonlocal tip_track
        if not ctx.is_simulating():
            if tip_track['counts'][pip] == tip_track['maxes'][pip]:
                ctx.pause('Replace ' + str(pip.max_volume) +
                          'µl tipracks before \
                resuming.')
                pip.reset_tipracks()
                tip_track['counts'][pip] = 0

        if not pip.hw_pipette['has_tip']:
            pip.pick_up_tip()

    ##########

    ############################################################################
    # STEP 1: TRANSFER MMIX
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        ctx.comment(' ')
        ctx.comment('###############################################')
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')
        ctx.comment(' ')

        pick_up(p300)
        used_vol = []

        for dest in dests:
            aspirate_volume = MMIX_VOL_PER_SAMPLE * len(dest) + extra_dispensal
            used_vol_temp = distribute_custom(
                p300,
                volume=MMIX_VOL_PER_SAMPLE,
                src=Mmix.reagent_reservoir,
                dest=dest,
                waste_pool=Mmix.reagent_reservoir,
                pickup_height=0.2,
                extra_dispensal=extra_dispensal,
                dest_x_offset=2,
                disp_height=-1)
            used_vol.append(used_vol_temp)
        p300.drop_tip(home_after=False)
        tip_track['counts'][p300] += 1

        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 2: TRANSFER SAMPLES
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        ctx.comment(' ')
        ctx.comment('###############################################')
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')
        ctx.comment(' ')

        for s, d in zip(samples, pcr_wells_samples):
            pick_up(p20)
            move_vol_multichannel(p20,
                                  reagent=Samples,
                                  source=s,
                                  dest=d,
                                  vol=VOLUME_SAMPLE,
                                  air_gap_vol=air_gap_sample,
                                  x_offset=x_offset,
                                  pickup_height=0.2,
                                  disp_height=0,
                                  rinse=False,
                                  blow_out=True,
                                  touch_tip=True)
            p20.drop_tip(home_after=False)
            tip_track['counts'][p20] += 1

        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 3: TRANSFER NEGATIVE CONTROL
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        ctx.comment(' ')
        ctx.comment('###############################################')
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')
        ctx.comment(' ')

        pick_up(p20)
        s = tuberack.rows()[0][1]  # A2
        d = qpcr_plate.wells()[1]  # B1
        move_vol_multichannel(p20,
                              reagent=Samples,
                              source=s,
                              dest=d,
                              vol=VOLUME_SAMPLE,
                              air_gap_vol=air_gap_sample,
                              x_offset=x_offset,
                              pickup_height=0.2,
                              disp_height=0,
                              rinse=False,
                              blow_out=True,
                              touch_tip=True)
        p20.drop_tip(home_after=False)
        tip_track['counts'][p20] += 1

        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    ############################################################################
    # STEP 4: TRANSFER POSITIVE CONTROL
    ############################################################################
    STEP += 1
    if STEPS[STEP]['Execute'] == True:
        start = datetime.now()
        ctx.comment(' ')
        ctx.comment('###############################################')
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'])
        ctx.comment('###############################################')
        ctx.comment(' ')

        pick_up(p20)
        s = tuberack.rows()[0][2]  # A3
        d = qpcr_plate.wells()[0]  # A1
        move_vol_multichannel(p20,
                              reagent=Samples,
                              source=s,
                              dest=d,
                              vol=VOLUME_SAMPLE,
                              air_gap_vol=air_gap_sample,
                              x_offset=x_offset,
                              pickup_height=0.2,
                              disp_height=0,
                              rinse=False,
                              blow_out=True,
                              touch_tip=True)
        p20.drop_tip(home_after=False)
        tip_track['counts'][p20] += 1

        end = datetime.now()
        time_taken = (end - start)
        ctx.comment('Step ' + str(STEP) + ': ' + STEPS[STEP]['description'] +
                    ' took ' + str(time_taken))
        STEPS[STEP]['Time:'] = str(time_taken)

    # Export the time log to a tsv file
    if not ctx.is_simulating():
        with open(file_path, 'w') as f:
            f.write(
                'STEP\texecution\tdescription\twait_time\texecution_time\n')
            for key in STEPS.keys():
                row = str(key)
                for key2 in STEPS[key].keys():
                    row += '\t' + format(STEPS[key][key2])
                f.write(row + '\n')
        f.close()

    ############################################################################
    # Light flash end of program
    for i in range(3):
        #ctx._hw_manager.hardware.set_lights(rails=False)
        ctx._hw_manager.hardware.set_lights(button=(1, 0, 0))
        time.sleep(0.3)
        #ctx._hw_manager.hardware.set_lights(rails=True)
        ctx._hw_manager.hardware.set_lights(button=(0, 0, 1))
        time.sleep(0.3)
    ctx._hw_manager.hardware.set_lights(button=(0, 1, 0))
    ctx.comment('Finished! \nMove plate to PCR')

    total_used_vol = np.sum(used_vol)
    total_needed_volume = total_used_vol
    ctx.comment('Total Mmix used volume is: ' + str(total_used_vol) +
                '\u03BCl.')
    ctx.comment('Needed Mmix volume is ' +
                str(total_needed_volume + extra_dispensal * len(dests)) +
                '\u03BCl')
    ctx.comment('Mmix remaining in tubes is: ' + format(
        np.sum(Mmix.unused) + extra_dispensal * len(dests) + Mmix.vol_well) +
                '\u03BCl.')
    ctx.comment('200 ul Used tips in total: ' + str(tip_track['counts'][p300]))
    ctx.comment('200 ul Used racks in total: ' +
                str(tip_track['counts'][p300] / 96))
    ctx.comment('20 ul Used tips in total: ' + str(tip_track['counts'][p20]))
    ctx.comment('20 ul Used racks in total: ' +
                str(tip_track['counts'][p20] / 96))
Example #27
0
def run(ctx: protocol_api.ProtocolContext):

    # load labware
    ic_pk = ctx.load_labware(
        'opentrons_24_aluminumblock_nest_2ml_snapcap', '9',
        'chilled tubeblock for internal control and proteinase K (strip 1)'
    ).wells()[0]
    source_racks = [
        ctx.load_labware(
            'opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap', slot,
            'source tuberack ' + str(i + 1))
        for i, slot in enumerate(['1', '2', '3', '4'])
    ]
    dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '8',
                                  '96-deepwell sample plate')
    binding_buffer = ctx.load_labware(
        'opentrons_6_tuberack_falcon_50ml_conical', '11',
        '50ml tuberack for binding buffer (tube B1)').wells('B1')
    # binding_buffer = ctx.load_labware(
    #     'biorad_96_wellplate_200ul_pcr', '7',
    #     '50ml tuberack for lysis buffer + PK (tube A1)').wells()[:1]
    tipracks1000 = [
        ctx.load_labware('opentrons_96_filtertiprack_1000ul', slot,
                         '1000µl filter tiprack') for slot in ['10', '7']
    ]
    tipracks20 = [
        ctx.load_labware('opentrons_96_filtertiprack_20ul', '6',
                         '20µl filter tiprack')
    ]

    # load pipette
    s20 = ctx.load_instrument('p20_single_gen2', 'left', tip_racks=tipracks20)
    p1000 = ctx.load_instrument('p1000_single_gen2',
                                'right',
                                tip_racks=tipracks1000)

    # setup samples
    sources = [well for rack in source_racks
               for well in rack.wells()][:NUM_SAMPLES]
    dests_single = dest_plate.wells()[:NUM_SAMPLES]
    num_cols = math.ceil(NUM_SAMPLES / 8)
    dests_multi = dest_plate.rows()[0][:num_cols]

    tip_log = {'count': {}}
    folder_path = '/data/A'
    tip_file_path = folder_path + '/tip_log.json'
    if TIP_TRACK and not ctx.is_simulating():
        if os.path.isfile(tip_file_path):
            with open(tip_file_path) as json_file:
                data = json.load(json_file)
                if 'tips1000' in data:
                    tip_log['count'][p1000] = data['tips1000']
                else:
                    tip_log['count'][p1000] = 0
                if 'tips20' in data:
                    tip_log['count'][s20] = data['tips20']
                else:
                    tip_log['count'][s20] = 0
    else:
        tip_log['count'] = {p1000: 0, s20: 0}

    tip_log['tips'] = {
        p1000: [tip for rack in tipracks1000 for tip in rack.wells()],
        #s20: [tip for rack in tipracks20 for tip in rack.rows()[0]]
        s20: [tip for rack in tipracks20 for tip in rack.wells()]
    }
    tip_log['max'] = {pip: len(tip_log['tips'][pip]) for pip in [p1000, s20]}

    def pick_up(pip):
        nonlocal tip_log
        if tip_log['count'][pip] == tip_log['max'][pip]:
            ctx.pause('Replace ' + str(pip.max_volume) + 'µl tipracks before \
resuming.')
            pip.reset_tipracks()
            tip_log['count'][pip] = 0
        pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]])
        tip_log['count'][pip] += 1

    heights = {tube: TUBE50_VOlUME * 1 for tube in binding_buffer}
    radius = (binding_buffer[0].diameter) / 2
    min_h = 5

    def h_track(vol, tube):
        nonlocal heights
        dh = vol / (math.pi * (radius**2))
        if heights[tube] - dh > min_h:
            heights[tube] = heights[tube] - dh
        else:
            heights[tube] = min_h  # stop 5mm short of the bottom
        return heights[tube]

    p1000.flow_rate.aspirate = 50
    p1000.flow_rate.dispense = 60
    p1000.flow_rate.blow_out = 100

    # transfer internal control + proteinase K
    pick_up(s20)
    for d in dests_single:
        s20.dispense(10, ic_pk.bottom(2))
        s20.transfer(ICPK_VOlUME,
                     ic_pk.bottom(2),
                     d.bottom(2),
                     air_gap=5,
                     new_tip='never')
        s20.air_gap(5)
    s20.drop_tip()

    # transfer binding buffer and mix
    pick_up(p1000)
    for i, (s, d) in enumerate(zip(sources, dests_single)):

        source = binding_buffer[
            i //
            96]  # 1 tube of binding buffer can accommodate all samples here
        h = h_track(275, source)
        # custom mix
        p1000.flow_rate.aspirate = 100
        p1000.flow_rate.dispense = 100
        p1000.dispense(500, source.bottom(h + 20))
        for _ in range(4):
            # p1000.air_gap(500)
            p1000.aspirate(500, source.bottom(h))
            p1000.dispense(500, source.bottom(h + 20))

    # p1000.transfer(BB_VOLUME, source.bottom(h), d.bottom(5), air_gap=100,
    #              new_tip='never')

        p1000.flow_rate.aspirate = 50
        p1000.flow_rate.dispense = 100
        p1000.aspirate(BB_VOLUME, source.bottom(h))
        p1000.air_gap(10)
        p1000.dispense(BB_VOLUME + 100, d.bottom(10))
        p1000.air_gap(10)
    p1000.drop_tip()

    ctx.comment('Move deepwell plate (slot 4) to Station B for RNA \
extraction.')

    # track final used tip
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        data = {
            'tips1000': tip_log['count'][p1000],
            'tips20': tip_log['count'][s20]
        }
        with open(tip_file_path, 'w') as outfile:
            json.dump(data, outfile)
Example #28
0
def run(protocol: protocol_api.ProtocolContext):
    """
    During the qPCR (project_related/Dina/21june_qPCRs_DINA_EVE.py) we take
    sample, dilute it 100x (pipetting up-and-down 3x with 4.5uL) and then 
    directly transfer the dilution to the PCR plate. With the sample_mix, which
    was added to the plate 8x I saw a lot of variation in Cq values. I'm under
    the impression mixing was not sufficient. In this protocol I want to 
    compare some different mixing techniques, to see if that can be improved.
    """
    # =============================================================================

    # LOADING LABWARE AND PIPETTES=================================================
    # =============================================================================
    ##### Loading labware
    tips_200 = protocol.load_labware(
        'opentrons_96_filtertiprack_200ul',  #labware definition
        3,  #deck position
        'tips_200')  #custom name
    tips_20_1 = protocol.load_labware(
        'opentrons_96_filtertiprack_20ul',  #labware definition
        10,  #deck position
        'tips_20')  #custom name
    tips_20_2 = protocol.load_labware(
        'opentrons_96_filtertiprack_20ul',  #labware definition
        7,  #deck position
        'tips_20')  #custom name
    plate_96_qPCR = protocol.load_labware(
        'biorad_96_wellplate_200ul_pcr',  #labware definition
        5,  #deck position
        'plate_96_qPCR')  #custom name

    # ##### !!! FOR ROBOT
    # sample_strips_1 = protocol.load_labware(
    #     'pcrstrips_96_wellplate_200ul',         #labware definition
    #     1,                                      #deck position
    #     'sample_strips_1')                      #custom name
    # dilution_strips = protocol.load_labware(
    #     'pcrstrips_96_wellplate_200ul',         #labware definition
    #     4,                                      #deck position
    #     'dilution_strips')                      #custom name
    # tubes_5mL = protocol.load_labware(
    #     'eppendorfscrewcap_15_tuberack_5000ul', #labware definition
    #     6,                                      #deck position
    #     'tubes_5mL')                            #custom name

    ####    !!! FOR SIMULATOR
    with open("labware/pcrstrips_96_wellplate_200ul/"
              "pcrstrips_96_wellplate_200ul.json") as labware_file:
        labware_def_pcrstrips = json.load(labware_file)
        sample_strips_1 = protocol.load_labware_from_definition(
            labware_def_pcrstrips,  #variable derived from opening json
            1,
            'sample_strips_1')
        dilution_strips = protocol.load_labware_from_definition(
            labware_def_pcrstrips,  #variable derived from opening json
            4,
            'dilution_strips')
    with open("labware/eppendorfscrewcap_15_tuberack_5000ul/"
              "eppendorfscrewcap_15_tuberack_5000ul.json") as labware_file:
        labware_def_5mL = json.load(labware_file)
        tubes_5mL = protocol.load_labware_from_definition(
            labware_def_5mL,  #variable derived from opening json
            6,
            '5mL_tubes')

    ##### Loading pipettes
    p300 = protocol.load_instrument(
        'p300_single_gen2',  #instrument definition
        'right',  #mount position
        tip_racks=[tips_200])  #assigned tiprack
    p20 = protocol.load_instrument(
        'p20_single_gen2',  #instrument definition
        'left',  #mount position
        tip_racks=[tips_20_1, tips_20_2])  #assigned tiprack

    # =============================================================================

    # VARIABLES TO SET#!!!=========================================================
    # =============================================================================
    start_vol_mix = 2178
    ## The start_vol_m is the volume (ul) of mix that is in the source    ##
    ## labware at the start of the protocol.                              ##
    start_vol_water = 5000
    ## The start_vol_w is the volume (ul) of water that is in the source  ##
    ## labware at the start of the protocol.                              ##
    ##!!! Fill up to above 5mL line                                       ##
    dispension_vol_mix = 22
    ## The dispension_vol_m is the volume (ul) of mastermix that needs to ##
    ## be aliquoted into the destination wells/tubes.                     ##
    dispension_vol_water = 148.5
    ## The dil_vol_w is the volume of water to be pipetted for the        ##
    ## dilution.                                                          ##
    sample_vol_dil = 1.5
    ## The dil_vol_s is the volume of sample to be pipetted for the       ##
    ## 100x dilution.                                                     ##
    sample_vol_pcr = 3
    ## The sample_vol is the volume (ul) of sample added to the PCR       ##
    ## reaction.                                                          ##
    p300.starting_tip = tips_200.well('A1')
    p20.starting_tip = tips_20_1.well('A1')
    ## The starting_tip is the location of first pipette tip in the box   ##

    #### Which wells/tubes are used
    mastermix = tubes_5mL['C1']
    H2O = ([
        tubes_5mL.wells_by_name()[well_name]
        for well_name in ['B1', 'B2', 'B3']
    ])
    sample = sample_strips_1['A1']

    ## where to put mastermix
    PCR_plate = []
    PCR_plate_columns = ([
        plate_96_qPCR.columns_by_name()[column_name] for column_name in
        ['1', '2', '3', '5', '6', '7', '8', '9', '10', '11']
    ])
    for column in PCR_plate_columns:
        for well in column:
            PCR_plate.append(well)

    for well in ([
            plate_96_qPCR.wells_by_name()[well_name]
            for well_name in ['G12', 'H12']
    ]):
        PCR_plate.append(well)
    ## where to put water for dilutions
    dilution_plate = dilution_strips.columns_by_name()['1']

    # =============================================================================

    # PREDIFINED VARIABLES=========================================================
    # =============================================================================
    container_mix = container_water = 'tube_5mL'

    ##### Variables for volume tracking
    start_height_mix = vt.cal_start_height(container_mix, start_vol_mix)
    start_height_water = vt.cal_start_height(container_water, start_vol_water)

    # =============================================================================

    # ALIQUOTING DILUTION WATER AND MASTERMIX======================================
    # =============================================================================

    aliquots = ['water', 'PCR_mix']
    ## what will be aliquoted in this protocol

    for aliquot in aliquots:
        if aliquot == 'PCR_mix':
            source = mastermix
            destination = PCR_plate
            current_height = start_height_mix
            container = container_mix
            dispension_vol = dispension_vol_mix

        elif aliquot == 'water':
            counter = 0  # how many tubes emptied
            source = H2O[counter]
            destination = dilution_plate
            current_height = start_height_water
            container = container_water
            dispension_vol = dispension_vol_water

        for i, well in enumerate(destination):
            ## aliquot mix in entire qPCR plate, for each well do the following:

            aspiration_vol = dispension_vol + (dispension_vol / 100 * 2)
            ## Set correct variables for volume_tracking

            if i == 0:
                p300.pick_up_tip()
                ## If we are at the first well, start by picking up a tip.##
            elif i % 8 == 0:
                p300.drop_tip()
                p300.pick_up_tip()
                ## Then, after every 8th well, drop tip and pick up new   ##

            current_height, pip_height, bottom_reached = vt.volume_tracking(
                container, dispension_vol, current_height)
            ## call volume_tracking function, obtain current_height,  ##
            ## pip_height and whether bottom_reached.                 ##

            if bottom_reached:
                if aliquot == 'water':
                    ## continue with next tube, reset vt
                    current_height = start_height_water
                    current_height, pip_height, bottom_reached = (
                        vt.volume_tracking(container, dispension_vol,
                                           current_height))
                    counter = counter + 1
                    source = H2O[counter]
                    aspiration_location = source.bottom(current_height)
                    protocol.comment("Continue with next tube of water")

                elif aliquot == 'PCR_mix':
                    aspiration_location = source.bottom(z=1)
                    protocol.comment("You've reached the bottom of the tube!")

            else:
                aspiration_location = source.bottom(pip_height)
                ## Set the location of where to aspirate from.            ##

            #### The actual aliquoting of mastermix
            p300.aspirate(aspiration_vol, aspiration_location)
            ## Aspirate the amount specified in aspiration_vol from the   ##
            ## location specified in aspiration_location.                 ##
            p300.dispense(dispension_vol, well)
            ## Dispense the amount specified in dispension_vol to the     ##
            ## location specified in well (looping through plate)         ##
            p300.dispense(10, aspiration_location)
            ## Alternative for blow-out, make sure the tip doesn't fill   ##
            ## completely when using a disposal volume by dispensing some ##
            ## of the volume after each pipetting step. (blow-out too many##
            ## bubbles)                                                   ##
        p300.drop_tip()
        ## when entire plate is full, drop tip                            ##
# =============================================================================

# DILUTING AND DISTRIBUTING SAMPLE MIX=========================================
# =============================================================================

    for mix_method in range(8):

        ## where to dilute
        dilution = dilution_plate[mix_method]

        ## set variables per mix_method
        if mix_method == 0:
            ## 1: Mixing 3x with sample_vol +3. Distribute in 24 wells
            mix_vol = sample_vol_dil + 3
            number_of_mixes = 3
            destination = PCR_plate[:24]
        elif mix_method == 1:
            ## 2: Mixing 5x with sample_vol + 3
            mix_vol = sample_vol_dil + 3
            number_of_mixes = 5
            destination = PCR_plate[24:32]
        elif mix_method == 2:
            ## 3: Mixing 7x with sample_vol + 3
            mix_vol = sample_vol_dil + 3
            number_of_mixes = 7
            destination = PCR_plate[32:40]
        elif mix_method == 3:
            ## 4: Mixing 10x with sample_vol + 3
            mix_vol = sample_vol_dil + 3
            number_of_mixes = 10
            destination = PCR_plate[40:48]
        elif mix_method == 4:
            ## 5: Mixing 3x with 20µL
            mix_vol = 20
            number_of_mixes = 3
            destination = PCR_plate[48:56]
        elif mix_method == 5:
            ## 6: Mixing 5x with 20µL
            mix_vol = 20
            number_of_mixes = 5
            destination = PCR_plate[56:64]
        elif mix_method == 6:
            ## 7: Mixing 7x with 20µL
            mix_vol = 20
            number_of_mixes = 7
            destination = PCR_plate[64:72]
        elif mix_method == 7:
            ## 8: Mixing 10x with 20µL
            mix_vol = 20
            number_of_mixes = 10
            destination = PCR_plate[72:80]

        #### diluting sample                                                ##
        p20.pick_up_tip()
        ## p20 picks up tip from location of specified starting_tip or    ##
        ## the following.                                                 ##
        p20.aspirate(sample_vol_dil, sample)
        ## aspirate sample_volume_dil = volume for dil. from sample_mix   ##
        p20.dispense(sample_vol_dil, dilution)
        ## dispense in dilution tube
        p20.mix(number_of_mixes, mix_vol, dilution)
        ## mix according to mix_method
        p20.dispense(10, dilution)
        ## instead of blow-out                                            ##
        p20.drop_tip()
        ## Drop tip in trashbin on 12.                                    ##

        #### Distribute from dilution to PCR plate                          ##
        for well in destination:
            p20.pick_up_tip()
            p20.aspirate(sample_vol_pcr, dilution)
            ## aspirate sample_vol_pcr from dilution                      ##
            p20.dispense(sample_vol_pcr, well)
            ## dispense into pcr_well                                     ##
            p20.mix(3, 20, well)
            ## Mix 3 times up and down with max pipette vol               ##
            p20.dispense(10, well)
            ## instead of blow-out.                                       ##
            p20.drop_tip()
Example #29
0
def run(ctx: protocol_api.ProtocolContext):

    # load labware
    ic_pk = ctx.load_labware(
        'opentrons_96_aluminumblock_nest_wellplate_100ul', '9',
        'chilled tubeblock for internal control and proteinase K (strip 1)'
    ).wells()[0]

    bb = ctx.load_labware('nest_12_reservoir_15ml', '8',
                          'reagent reservoir Binding Buffer')
    binding_buffer = bb.wells()[:2]
    dest_plate = ctx.load_labware('nest_96_wellplate_2ml_deep', '11',
                                  '96-deepwell sample plate')

    # load tips

    tips300 = [
        ctx.load_labware('opentrons_96_filtertiprack_200ul', '5',
                         '200µl filter tiprack')
    ]
    tips20 = [
        ctx.load_labware('opentrons_96_filtertiprack_20ul', '6',
                         '20µl filter tiprack')
    ]

    # load pipette

    m300 = ctx.load_instrument('p300_multi_gen2', 'right', tip_racks=tips300)
    s20 = ctx.load_instrument('p20_single_gen2', 'left', tip_racks=tips20)

    m300.flow_rate.aspirate = 50
    m300.flow_rate.dispense = 150
    m300.flow_rate.blow_out = 300

    s20.flow_rate.aspirate = 50
    s20.flow_rate.dispense = 100
    s20.flow_rate.blow_out = 300

    # setup samples
    num_cols = math.ceil(NUM_SAMPLES / 8)
    bbs = bb.wells()[:2]
    dests_single = dest_plate.wells()[:NUM_SAMPLES]
    dests_multi = dest_plate.rows()[0][:num_cols]

    tip_log = {'count': {}}
    folder_path = '/data/A'
    tip_file_path = folder_path + '/tip_log.json'
    if TIP_TRACK and not ctx.is_simulating():
        if os.path.isfile(tip_file_path):
            with open(tip_file_path) as json_file:
                data = json.load(json_file)
                if 'tips1000' in data:
                    tip_log['count'][m300] = data['tips1000']
                else:
                    tip_log['count'][m300] = 0
                if 'tips20' in data:
                    tip_log['count'][s20] = data['tips20']
                else:
                    tip_log['count'][s20] = 0
    else:
        tip_log['count'] = {m300: 0, s20: 0}

    tip_log['tips'] = {
        m300: [tip for rack in tips300 for tip in rack.wells()],
        s20: [tip for rack in tips20 for tip in rack.rows()[0]]
    }
    tip_log['max'] = {pip: len(tip_log['tips'][pip]) for pip in [m300, s20]}

    def pick_up(pip):
        nonlocal tip_log
        if tip_log['count'][pip] == tip_log['max'][pip]:
            ctx.pause('Replace ' + str(pip.max_volume) +
                      'µl tipracks before \ resuming.')
            pip.reset_tipracks()
            tip_log['count'][pip] = 0
        pip.pick_up_tip(tip_log['tips'][pip][tip_log['count'][pip]])
        tip_log['count'][pip] += 1

    heights = {tube: 20 for tube in binding_buffer}
    # radius = (binding_buffer[0].diameter)/2
    min_h = 5

    def h_track(vol, tube):
        nonlocal heights
        dh = vol / (math.pi * (radius**2))
        if heights[tube] - dh > min_h:
            heights[tube] = heights[tube] - dh
        else:
            heights[tube] = min_h  # stop 5mm short of the bottom
        return heights[tube]

        # transfer internal control + proteinase K

    for d in dests_single:
        pick_up(s20)
        s20.transfer(ICPK_VOlUME,
                     ic_pk.bottom(2),
                     d.bottom(2),
                     air_gap=5,
                     new_tip='never')
        s20.air_gap(5)
        s20.drop_tip()

        # transfer binding buffer
    for i, e in enumerate(dests_multi):
        pick_up(m300)
        m300.transfer(BB_VOLUME,
                      bbs[i // 6].bottom(2),
                      e.bottom(10),
                      air_gap=5,
                      mix_after=(5, 100),
                      new_tip='never')
        m300.air_gap(100)
        m300.drop_tip()

    ctx.comment('Terminado.')

    # track final used tip
    if not ctx.is_simulating():
        if not os.path.isdir(folder_path):
            os.mkdir(folder_path)
        data = {'tips300': tip_log['count'][m300]}
        with open(tip_file_path, 'w') as outfile:
            json.dump(data, outfile)
Example #30
0
def run(protocol: protocol_api.ProtocolContext):
    """
    Pick up 200µL filter tip. Aspirate 200µL from 50mL tube and transfer
    5x to 1x 1.5mL tube (1mL aliquots) - repeat for 24x 1.5mL tubes.
    Drop tip, pick up new tip and repeat.
    1x 50mL tube for 48x 1.5mL tubes.
    """
# =============================================================================


# =====================LOADING LABWARE AND PIPETTES============================
# =============================================================================
    # Pipette tips
    tips_200 = protocol.load_labware(
        'opentrons_96_filtertiprack_200ul',                      #labware def
        10,                                                      #deck position
        '200tips')                                               #custom name
    
    # Tube racks
    stock_tubes = protocol.load_labware(
        'opentrons_6_tuberack_falcon_50ml_conical',              #labware def
        7,                                                       #deck position
        '50mL_tubes')                                            #custom name   
    aliquot_tubes_1 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        4,                                                       #deck position
        'aliquot_tubes_1')                                       #custom name
    aliquot_tubes_2 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        1,                                                       #deck position
        'aliquot_tubes_2')                                       #custom name
    aliquot_tubes_3 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        11,                                                       #deck position
        'aliquot_tubes_3')                                       #custom name
    aliquot_tubes_4 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        8,                                                      #deck position
        'aliquot_tubes_4')                                       #custom name
    aliquot_tubes_5 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        5,                                                       #deck position
        'aliquot_tubes_5')                                       #custom name
    aliquot_tubes_6 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        2,                                                       #deck position
        'aliquot_tubes_6')                                       #custom name
    aliquot_tubes_7 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        6,                                                       #deck position
        'aliquot_tubes_7')                                       #custom name
    aliquot_tubes_8 = protocol.load_labware(
        'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap',#labware def
        3,                                                      #deck position
        'aliquot_tubes_8')                                       #custom name
    
    # Pipettes
    p300 = protocol.load_instrument(
        'p300_single_gen2',                              #instrument definition
        'right',                                         #mount position
        tip_racks=[tips_200])                            #assigned tiprack
# =============================================================================


# ==========================VARIABLES TO SET#!!!===============================
# =============================================================================
    start_vol = 50000 
      ## The start_vol is the volume (ul) that is in the source labware at  ##
      ## the start of the protocol.                                         ##
    dispension_vol = 200 
      ## The dispension_vol is the volume (ul) that needs to be aliquoted   ##
      ## into the destination wells/tubes.                                  ##
    p300.starting_tip = tips_200.well('E6')
      ## The starting_tip is the location of first pipette tip in the box   ##
    container = 'tube_50mL'
      ## The container variable is needed for the volume tracking module.   ##
      ## It tells the module which dimensions to use for the calculations   ##
      ## of the pipette height. It is the source labware from which liquid  ##
      ## is aliquoted.                                                      ##
      ## There are several options to choose from:                          ##
      ## 'tube_1.5ml', 'tube_2mL', 'tube_5mL', 'tube_15mL', 'tube_50mL'   	##
    number_of_source_wells = 3
# source and destination wells=================================================
    source_wells = []
    destination_wells = []
    if number_of_source_wells == 1:     
        source_well_1 = stock_tubes['A1']
        source_wells.append(
            source_well_1)
        destinations = (
            aliquot_tubes_1.wells() + aliquot_tubes_2.wells())
        for well in destinations:
            destination_wells.append(well)
    elif number_of_source_wells == 2:
        source_well_1 = stock_tubes['A1']
        source_well_2 = stock_tubes['A2']
        sources = (source_well_1, source_well_2)
        source_wells.append(sources)
        destinations = (
            aliquot_tubes_1.wells() + aliquot_tubes_2.wells() + 
            aliquot_tubes_3.wells() + aliquot_tubes_4.wells())
        for well in destinations:
            destination_wells.append(well)
    elif number_of_source_wells == 3:
        source_well_1 = stock_tubes['A1']
        source_well_2 = stock_tubes['A2']
        source_well_3 = stock_tubes['B1']
        sources = (source_well_1, source_well_2, source_well_3)
        source_wells.append(sources)
        destinations = (
            aliquot_tubes_1.wells() + aliquot_tubes_2.wells() +
            aliquot_tubes_3.wells() + aliquot_tubes_4.wells() + 
            aliquot_tubes_5.wells() + aliquot_tubes_6.wells())   
        for well in destinations:
            destination_wells.append(well)
    elif number_of_source_wells == 4: 
        source_well_1 = stock_tubes['A1']
        source_well_2 = stock_tubes['A2']
        source_well_3 = stock_tubes['B1']
        source_well_4 = stock_tubes['B2']  
        sources = (source_well_1, source_well_2, source_well_3, source_well_4)
        source_wells.append(sources)
        destinations = (
            aliquot_tubes_1.wells() + aliquot_tubes_2.wells() +
            aliquot_tubes_3.wells() + aliquot_tubes_4.wells() + 
            aliquot_tubes_5.wells() + aliquot_tubes_6.wells() +
            aliquot_tubes_7.wells() + aliquot_tubes_8.wells())   
        for well in destinations:
            destination_wells.append(well)
# =============================================================================


# ==========================PREDIFINED VARIABLES===============================
# =============================================================================
    aspiration_vol = dispension_vol
      ## The aspiration_vol is the volume (ul) that is aspirated from the   ##
      ## container.                                                         ##
    ##### Variables for volume tracking
    start_height = vt.cal_start_height(container, start_vol)
      ## Call start height calculation function from volume tracking module.##
    current_height = start_height
      ## Set the current height to start height at the beginning of the     ##
      ## protocol.                                                          ##
# =============================================================================


# =================================ALIQUOTING==================================
# =============================================================================
    ## For each column in destination_wells, pick up a tip, than for each   ##
    ## well in these columns pipette mix, and after the+ column drop the tip##
    ## Repeat untill all columns in the list are done.                      ##      
    for i, well in enumerate(destination_wells):
    ## Name all the wells in the plate 'well', for all these do:            ## 
        if i == 0:
            p300.pick_up_tip()
          ## If we are at the first well, start by picking up a tip.        ##
        elif i % 24 == 0:
            p300.drop_tip()
            p300.pick_up_tip() 
          ## Then, after every 8th well, drop tip and pick up a new one.    ##
        current_height, pip_height, bottom_reached = vt.volume_tracking(
            container, dispension_vol, current_height)  
          ## The volume_tracking function needs the arguments container,    ##
          ## dispension_vol, and the current_height which we have set in    ##
          ## this protocol. With those variables, the function updates      ##
          ## the current_height, the pip_height and calculates the          ##
          ## delta_height of the liquid after the next aspiration step.     ##
        if bottom_reached: 
            aspiration_location = source_wells.bottom(z=1) #!!!
            protocol.comment("You've reached the bottom!")
        else:
            aspiration_location = source_wells.bottom(pip_height) #!!!
          ## If the level of the liquid in the next run of the loop will    ## 
          ## be smaller than 1 we have reached the bottom of the tube.      ##
          ## To prevent the pipette from crashing into the bottom, we       ##
          ## tell it to go home and pause the protocol so that this can     ##
          ## never happen. Set the location of where to aspirate from.      ##
          ## Because we put this in the loop, the location will change      ##
          ## to the newly calculated height after each pipetting step.      ##
        p300.aspirate(aspiration_vol, aspiration_location)
          ## Aspirate the amount specified in aspiration_vol from the       ##
          ## location specified in aspiration_location.                     ##
        p300.dispense(dispension_vol, well)
          ## Dispense the amount specified in dispension_vol to the         ##
          ## location specified in well (so a new well every time the       ##
          ## loop restarts)                                                 ##
    p300.drop_tip()