def plateStrandBAndMix(): # Plate strand B and mix # Mixing always needs the p50, but plating may need either; optimize tip usage log('Plating Strand B') mixed_wells = set() for row in range(num_rows): for col in range(num_columns): volume = strand_b_plate[row][col] if volume == 0: continue p: EnhancedPipetteV1 = p10 if usesP10(volume, allow_zero=True) else p50 well = plate.rows(row).wells(col) log("Plating Strand B: well='%s' vol=%d pipette=%s" % (well.get_name(), volume, p.name)) if not p.tip_attached: p.pick_up_tip() p.transfer(volume, diluted_strand_b, well, new_tip='never', trash=config.trash_control, full_dispense=True) if p is p50: mix_plate_well(well, keep_last_tip=True) mixed_wells.add(well) p.done_tip() for well in plate.wells(): if well not in mixed_wells: mix_plate_well(well)
def plateStrandBAndMix(): # Plate strand B and mix # Mixing always needs the p50, but plating may need either; optimize tip usage log('Plating Strand B') mixed_wells = set() for iVolume in range(0, len(strand_volumes)): dest_wells = calculateStrandBWells(iVolume) volume = strand_volumes[iVolume] if usesP10( volume, len(dest_wells), allow_zero=True ): # nb: len(dest_wells) is technically incorrect, since we pipette each individually. but we leave for historical reasons p = p10 else: p = p50 # We can't use distribute here as we need to avoid cross contamination from plate well to plate well for well in dest_wells: if volume != 0: log("Plating Strand B: well='%s' vol=%d pipette=%s" % (well.get_name(), volume, p.name)) p.pick_up_tip() p.transfer(volume, diluted_strand_b, well, new_tip='never', full_dispense=True) if p is p50: mix_plate_well(well, keep_last_tip=True) mixed_wells.add(well) p.done_tip() for well in plate.wells(): if well not in mixed_wells: mix_plate_well(well)
def plate_dilutions(): log('plating dilutions') tubes_to_plate = WellSeries([initial_stock]) + dilutions volumes = [50, 25, 10, 5] num_replicates = 3 for row, source_tube in enumerate(tubes_to_plate): log(f'plating {source_tube.get_name()}') if row < 8: plate = plateA row_delta = 0 else: plate = plateB row_delta = 8 for iVolume, volume in enumerate(volumes): col_first = iVolume * num_replicates destination_wells = plate.rows(row - row_delta)[col_first:col_first + num_replicates] p = p10 if volume <= 10 else p50 if not p.has_tip: # make sure we have a tip. can reuse so long as we keep the same source tube p.pick_up_tip() p.transfer(volume, source_tube, destination_wells, new_tip='never', trash=config.trash_control) # We're going on to another source tube. Any tip we have is now junk p10.done_tip() p50.done_tip()
def mix_master_mix(): log('Mixing Master Mix') p50.layered_mix( [master_mix], incr=2, initial_turnover=master_mix_evagreen_vol * 1.2, max_tip_cycles=config.layered_mix.max_tip_cycles_large)
def plateStrandA(): # Plate strand A # All plate wells at this point only have water and master mix, so we can't get cross-plate-well # contamination. We only need to worry about contaminating the Strand A source, which we accomplish # by using new_tip='always'. Update: we don't worry about that pollution, that source is disposable. # So we can minimize tip usage. log('Plating Strand A') p10.pick_up_tip() p50.pick_up_tip() for iVolume in range(0, len(strand_volumes)): dest_wells = calculateStrandAWells(iVolume) volume = strand_volumes[iVolume] if volume == 0: continue if usesP10(volume, len(dest_wells), allow_zero=False): p = p10 else: p = p50 log('Plating Strand A: volume %d with %s' % (volume, p.name)) volumes = [volume] * len(dest_wells) p.transfer(volumes, diluted_strand_a, dest_wells, new_tip='never', trash=config.trash_control, full_dispense=True) p10.done_tip() p50.done_tip()
def plateMasterMix(): log('Plating Master Mix') master_mix_per_well = 28 p50.transfer( master_mix_per_well, master_mix, usedWells(), new_tip='once', trash=config.trash_control, full_dispense=True #, # aspirate_top_clearance=-5.0 # large to help avoid soap bubbles. remove when we no longer use detergent in buffer )
def make_dilution(water, source, dilution, dilution_volume, dilution_factor, manual): if manual: name = f'Dilution of {source.get_name()} by {dilution_factor}x' note_liquid(dilution, name, initially=dilution_volume) log(f'diluting from {source.get_name()} to {dilution.get_name()}') dilution_source_volume = dilution_volume / dilution_factor dilution_water_volume = dilution_volume - dilution_source_volume if manual: info(f'water vol={dilution_water_volume}') info(f'source vol={dilution_source_volume}') else: p50.transfer(dilution_water_volume, water, dilution) p50.transfer(dilution_source_volume, source, dilution, new_tip='once', trash=config.trash_control, keep_last_tip=False)
def plate_dilution(source, dx): # We have two dilutions, three replicates each, so get 16 volumes per volumes = [1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 50, 75, 100, 125, 150, 200] num_reps = 3 for i, vol in enumerate(volumes): log(f'plating source={source.get_name()} vol={vol}') row = i % 8 col_first = int(i / 8) p = p10 if vol <= 10 else p50 for j in range(num_reps): col = col_first * num_reps + j + dx dest = plate.rows(row).wells(col) # info(f'dest={row},{col}') p.transfer(vol, source, dest, trash=config.trash_control)
def plateMasterMix(): log('Plating Master Mix') for row in range(num_rows): for col in range(num_columns): volume = master_mix_plate[row][col] if volume == 0: continue p: EnhancedPipetteV1 = p10 if usesP10(volume) else p50 if not p.tip_attached: p.pick_up_tip() well = plate.rows(row).wells(col) p.transfer(volume, master_mix, well, new_tip='never', trash=config.trash_control, full_dispense=True) p10.done_tip() p50.done_tip()
def platePerWellWater(): log('Plating per-well water') # Plate per-well water. We save tips by being happy to pollute our water trough with a bit of master mix. for row in range(num_rows): for col in range(num_columns): volume = per_well_water_plate[row][col] if volume == 0: continue p: EnhancedPipetteV1 = p10 if usesP10(volume) else p50 if not p.tip_attached: p.pick_up_tip() well = plate.rows(row).wells(col) p.transfer(volume, waterB, well, new_tip='never', trash=config.trash_control, full_dispense=True) p10.done_tip() p50.done_tip()
def platePerWellWater(): log('Plating per-well water') # Plate per-well water. We save tips by being happy to pollute our water trough with a bit of master mix. # We begin by flattening per_well_water_volumes into a column-major array water_volumes = [0] * (columns_per_plate * rows_per_plate) for iRow in range(rows_per_plate): for iCol in range(len(per_well_water_volumes[iRow])): volume = per_well_water_volumes[iRow][iCol] for iReplicate in range(num_replicates): index = (iCol * num_replicates + iReplicate) * rows_per_plate + iRow water_volumes[index] = volume p50.transfer(water_volumes, waterB, plate.wells(), new_tip='once', trash=config.trash_control, full_dispense=True)
def make_dilutions(): log('transferring water for dilutions') p50.transfer(dilution_water_volume, water, dilutions, new_tip='once', trash=config.trash_control) sources = WellSeries([initial_stock]) + dilutions[0:-1] destinations = dilutions for source, destination in zip(sources, destinations): log(f'diluting from {source.get_name()} to {destination.get_name()}') p50.transfer( dilution_source_volume, source, destination, new_tip='once', trash=config.trash_control, keep_last_tip=True) # keep tip cause we can use it for mixing p50.layered_mix([destination])
def transfer_multiple(msg, xfer_vol_remaining, tubes, dest, new_tip, *args, **kwargs): tube_index = 0 cur_well = None cur_vol = 0 min_vol = 0 while xfer_vol_remaining > 0: if xfer_vol_remaining < p50_min_vol: warn( "remaining transfer volume of %f too small; ignored" % xfer_vol_remaining) return # advance to next tube if there's not enough in this tube while cur_well is None or cur_vol <= min_vol: if tube_index >= len(tubes): fatal('%s: more reagent needed' % msg) cur_well = tubes[tube_index][0] cur_vol = tubes[tube_index][1] min_vol = max( p50_min_vol, cur_vol / config. min_aspirate_factor_hack, # tolerance is proportional to specification of volume. can probably make better guess cur_well.geometry.min_aspiratable_volume) tube_index = tube_index + 1 this_vol = min(xfer_vol_remaining, cur_vol - min_vol) assert this_vol >= p50_min_vol # TODO: is this always the case? log('%s: xfer %f from %s in %s to %s in %s' % (msg, this_vol, cur_well, cur_well.parent, dest, dest.parent)) p50.transfer(this_vol, cur_well, dest, trash=config.trash_control, new_tip=new_tip, **kwargs) xfer_vol_remaining -= this_vol cur_vol -= this_vol
def make_dilution(water, source, dilution, dilution_volume, dilution_factor, manual): if manual: name = f'Dilution of {source.get_name()} by {dilution_factor}x' note_liquid(dilution, name, initially=dilution_volume) log(f'{"Manually" if manual else "Automatically"} diluting from {source.get_name()} to {dilution.get_name()}' ) dilution_source_volume = dilution_volume / dilution_factor dilution_water_volume = dilution_volume - dilution_source_volume if manual: info(f'water vol={dilution_water_volume}') info(f'source vol={dilution_source_volume}') user_prompt('') else: p50.transfer(dilution_water_volume, water, dilution, new_tip='once', trash=config.trash_control) p50.transfer(dilution_source_volume, source, dilution, new_tip='once', trash=config.trash_control)
def plate_dilution(source, stride, parity): # We have two dilutions, three replicates each, so get 16 volumes per volumes = [ 5, 10, 15, 20, 25, 30, 35, 50, 60, 70, 80, 90, 100, 125, 150, 175 ] num_reps = 3 for i, vol in enumerate(reversed(volumes)): log(f'Plating: source={source.get_name()} vol={vol}') row = i % 8 col_first = int(i / 8) offset = abs(parity - (i % 2)) p = p10 if vol <= 10 else p50 for j in range(num_reps): col = (col_first * num_reps + j) * stride + offset dest = plate.rows(row).wells(col) if not p.tip_attached: p.pick_up_tip() p.transfer(vol, source, dest, trash=config.trash_control, new_tip='never') if p10.tip_attached: p10.done_tip() if p50.tip_attached: p50.done_tip()
def plateStrandA(): # Plate strand A # All plate wells at this point only have water and master mix, so we can't get cross-plate-well # contamination. We only need to worry about contaminating the Strand A source, which we accomplish # by using new_tip='always'. Update: we don't worry about that pollution, that source is disposable. # So we can minimize tip usage. log('Plating Strand A') for row in range(num_rows): for col in range(num_columns): volume = strand_a_plate[row][col] if volume == 0: continue p: EnhancedPipetteV1 = p10 if usesP10(volume) else p50 if not p.tip_attached: p.pick_up_tip() well = plate.rows(row).wells(col) log('Plating Strand A: volume %d with %s' % (volume, p.name)) p.transfer(volume, diluted_strand_a, well, new_tip='never', trash=config.trash_control, full_dispense=True) p10.done_tip() p50.done_tip()
# Control tip usage p10.start_at_tip(tips10[p10_start_tip]) p50.start_at_tip(tips300a[p50_start_tip]) # All the labware containers eppendorf_1_5_rack = labware_manager.load('opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap', slot=2, label='eppendorf_1_5_rack') eppendorf_5_0_rack = labware_manager.load('Atkinson_15_tuberack_5ml_eppendorf', slot=5, label='eppendorf_5_0_rack') plate = labware_manager.load('biorad_96_wellplate_200ul_pcr', slot=6, label='plateA') # Name specific places in the labware containers water = eppendorf_5_0_rack['C5'] initial_stock = eppendorf_1_5_rack['A1'] dilutions = eppendorf_5_0_rack.rows['A']['1':2] # Remember initial liquid names and volumes log('Liquid Names') note_liquid(location=water, name='Water', initially=water_volume) note_liquid(location=initial_stock, name='AlluraRed', concentration="20.1442 mM", initially=stock_volume) ######################################################################################################################## # Dilutions ######################################################################################################################## def make_dilution(water, source, dilution, dilution_volume, dilution_factor, manual): if manual: name = f'Dilution of {source.get_name()} by {dilution_factor}x' note_liquid(dilution, name, initially=dilution_volume) log(f'diluting from {source.get_name()} to {dilution.get_name()}') dilution_source_volume = dilution_volume / dilution_factor dilution_water_volume = dilution_volume - dilution_source_volume if manual:
def diluteStrands(): if manually_dilute_strands: note_liquid(location=diluted_strand_a, name='Diluted StrandA', initially=strand_dilution_vol) note_liquid(location=diluted_strand_b, name='Diluted StrandB', initially=strand_dilution_vol) log('Diluting Strands') info( pretty.format( 'Diluted Strand A recipe: water={0:n} strandA={1:n} vol={2:n}', strand_dilution_water_vol, strand_dilution_source_vol, strand_dilution_vol)) info( pretty.format( 'Diluted Strand B recipe: water={0:n} strandB={1:n} vol={2:n}', strand_dilution_water_vol, strand_dilution_source_vol, strand_dilution_vol)) user_prompt('Ensure diluted strands manually present and mixed') else: strand_a = eppendorf_1_5_rack['A1'] strand_b = eppendorf_1_5_rack['B1'] assert strand_a_min_vol >= strand_dilution_source_vol + strand_a.geometry.min_aspiratable_volume assert strand_b_min_vol >= strand_dilution_source_vol + strand_b.geometry.min_aspiratable_volume note_liquid(location=strand_a, name='StrandA', concentration=strand_a_conc, initially_at_least=strand_a_min_vol ) # i.e.: we have enough, just not specified how much note_liquid(location=strand_b, name='StrandB', concentration=strand_b_conc, initially_at_least=strand_b_min_vol) # ditto note_liquid(location=diluted_strand_a, name='Diluted StrandA') note_liquid(location=diluted_strand_b, name='Diluted StrandB') # We used to auto-mix, but now, even when auto-diluting, we rely on user to have mixed on the vortexer # p50.layered_mix([strand_a]) # p50.layered_mix([strand_b]) # Create dilutions of strands log('Moving water for diluting Strands A and B') p50.transfer( strand_dilution_water_vol, waterA, [diluted_strand_a, diluted_strand_b], new_tip= 'once', # can reuse for all diluent dispensing since dest tubes are initially empty trash=config.trash_control) log('Diluting Strand A') p50.transfer(strand_dilution_source_vol, strand_a, diluted_strand_a, trash=config.trash_control, keep_last_tip=True) p50.layered_mix([diluted_strand_a]) log('Diluting Strand B') p50.transfer(strand_dilution_source_vol, strand_b, diluted_strand_b, trash=config.trash_control, keep_last_tip=True) p50.layered_mix([diluted_strand_b])
label='plateA') plateB = labware_manager.load('biorad_96_wellplate_200ul_pcr', 3, label='plateB') trough = labware_manager.load('usascientific_12_reservoir_22ml', 9, label='trough') # Name specific places in the labware containers water = trough['A1'] initial_stock = eppendorf_1_5_rack['A1'] dilutions = eppendorf_1_5_rack.rows(1) + eppendorf_1_5_rack.rows( 2) # 12 in all # Remember initial liquid names and volumes log('Liquid Names') note_liquid(location=water, name='Water', initially_at_least=7000) # volume is rough guess note_liquid(location=initial_stock, name='AlluraRed', concentration="20.1442 mM", initially=stock_volume) ######################################################################################################################## # Dilutions ######################################################################################################################## dilution_volume = 600 dilution_factor = math.sqrt(5) # yes, that's correct dilution_source_volume = dilution_volume / dilution_factor
label='eppendorf_1_5_rack') eppendorf_5_0_rack = labware_manager.load('Atkinson_15_tuberack_5ml_eppendorf', slot=5, label='eppendorf_5_0_rack') plate = labware_manager.load('biorad_96_wellplate_200ul_pcr', slot=6, label='plate') # Name specific places in the labware containers waterA = eppendorf_5_0_rack['C4'] waterB = eppendorf_5_0_rack['C5'] initial_stock = eppendorf_1_5_rack['A1'] dilutions = eppendorf_5_0_rack.rows['A']['1':2] # Remember initial liquid names and volumes log('Liquid Names') note_liquid(location=waterA, name='Water', initially=waterA_initial_volume) note_liquid(location=waterB, name='Water', initially=waterB_initial_volume) note_liquid(location=initial_stock, name='AlluraRed', concentration="20.1442 mM", initially=stock_volume) ######################################################################################################################## # Dilutions ######################################################################################################################## def make_dilution(water, source, dilution, dilution_volume, dilution_factor, manual): if manual:
def createMasterMix(): if manually_make_master_mix: note_liquid(location=master_mix, name='Master Mix', initially=master_mix_vol) log('Creating Master Mix') info( pretty.format( 'Master Mix recipe: water={0:n} buffer={1:n} EvaGreen={2:n} total={3:n} (extra={4}%)', master_mix_common_water_vol, master_mix_buffer_vol, master_mix_evagreen_vol, master_mix_vol, 100.0 * (mm_overhead_factor - 1))) user_prompt('Ensure master mix manually present and mixed') else: # Mostly just for fun, we put the ingredients for the master mix in a nice warm place to help them melt temp_slot = 11 temp_module = modules_manager.load('tempdeck', slot=temp_slot) screwcap_rack = labware_manager.load( 'opentrons_24_aluminumblock_generic_2ml_screwcap', slot=temp_slot, label='screwcap_rack', share=True, well_geometry=IdtTubeWellGeometry) buffers = list(zip(screwcap_rack.rows(0), buffer_volumes)) evagreens = list(zip(screwcap_rack.rows(1), evagreen_volumes)) for buffer in buffers: note_liquid(location=buffer[0], name='Buffer', initially=buffer[1], concentration=buffer_source_concentration) for evagreen in evagreens: note_liquid(location=evagreen[0], name='Evagreen', initially=evagreen[1], concentration=evagreen_source_concentration) note_liquid(location=master_mix, name='Master Mix') # Buffer was just unfrozen. Mix to ensure uniformity. EvaGreen doesn't freeze, no need to mix p50.layered_mix([buffer for buffer, __ in buffers], incr=2) # transfer from multiple source wells, each with a current defined volume def transfer_multiple(msg, xfer_vol_remaining, tubes, dest, new_tip, *args, **kwargs): tube_index = 0 cur_well = None cur_vol = 0 min_vol = 0 while xfer_vol_remaining > 0: if xfer_vol_remaining < p50_min_vol: warn( "remaining transfer volume of %f too small; ignored" % xfer_vol_remaining) return # advance to next tube if there's not enough in this tube while cur_well is None or cur_vol <= min_vol: if tube_index >= len(tubes): fatal('%s: more reagent needed' % msg) cur_well = tubes[tube_index][0] cur_vol = tubes[tube_index][1] min_vol = max( p50_min_vol, cur_vol / config. min_aspirate_factor_hack, # tolerance is proportional to specification of volume. can probably make better guess cur_well.geometry.min_aspiratable_volume) tube_index = tube_index + 1 this_vol = min(xfer_vol_remaining, cur_vol - min_vol) assert this_vol >= p50_min_vol # TODO: is this always the case? log('%s: xfer %f from %s in %s to %s in %s' % (msg, this_vol, cur_well, cur_well.parent, dest, dest.parent)) p50.transfer(this_vol, cur_well, dest, trash=config.trash_control, new_tip=new_tip, **kwargs) xfer_vol_remaining -= this_vol cur_vol -= this_vol def mix_master_mix(): log('Mixing Master Mix') p50.layered_mix( [master_mix], incr=2, initial_turnover=master_mix_evagreen_vol * 1.2, max_tip_cycles=config.layered_mix.max_tip_cycles_large) log('Creating Master Mix: Water') p50.transfer(master_mix_common_water_vol, waterB, master_mix, trash=config.trash_control) log('Creating Master Mix: Buffer') transfer_multiple( 'Creating Master Mix: Buffer', master_mix_buffer_vol, buffers, master_mix, new_tip='once', keep_last_tip=True ) # 'once' because we've only got water & buffer in context p50.done_tip() # EvaGreen needs a new tip log('Creating Master Mix: EvaGreen') transfer_multiple( 'Creating Master Mix: EvaGreen', master_mix_evagreen_vol, evagreens, master_mix, new_tip='always', keep_last_tip=True ) # 'always' to avoid contaminating the Evagreen source w/ buffer mix_master_mix()
def plate_dilutions(): log('Plating') plate_dilution(dilutions[0], 0) plate_dilution(dilutions[1], 6)