def runQPCR(self,src,vol,primers,nreplicates=1,enzName="EvaUSER"): ## QPCR setup worklist.comment("runQPCR: primers=%s, source=%s"%([p for p in primers],[s.name for s in src])) [src,vol,nreplicates]=listify([src,vol,nreplicates]) self.e.shakeSamples(src,returnPlate=True) # Build a list of sets to be run torun=[] for repl in range(max(nreplicates)): for p in primers: for i in range(len(src)): if nreplicates[i]<=repl: continue if repl==0: sampname="%s.Q%s"%(src[i].name,p) else: sampname="%s.Q%s.%d"%(src[i].name,p,repl+1) s=Sample(sampname,decklayout.QPCRPLATE) torun=torun+[(src[i],s,p,vol[i])] # Add enzyme e=reagents.getsample(enzName) v=[a[3]/e.conc.dilutionneeded() for a in torun] t=[a[1] for a in torun] self.e.multitransfer(v,e,t) # Make the target have 'none' concentration so we can multiadd to it again for s in t: s.conc=None # Fill the master mixes dil={} for p in primers: mname="P-%s"%p if not reagents.isReagent(mname): reagents.add(name=mname,conc=4,extraVol=30) mq=reagents.getsample(mname) t=[a[1] for a in torun if a[2]==p] v=[a[3]/mq.conc.dilutionneeded() for a in torun if a[2]==p] assert(v>0) self.e.multitransfer(v,mq,t,(False,False)) dil[p]=1.0/(1-1/e.conc.dilutionneeded()-1/mq.conc.dilutionneeded()) # Add the samples self.e.sanitize() # In case we are aligned for a in torun: s=a[0] t=a[1] p=a[2] v=a[3]/dil[p] t.conc=None # Concentration of master mix is irrelevant now self.e.transfer(v,s,t) return [a[1] for a in torun]
def mix(self, inp, weights,mixvol=100,tgtconc=None,maxinpvol=20): """Mix given inputs according to weights (by moles -- use conc.stock of each input)""" vol = [weights[i] *1.0 / inp[i].conc.stock for i in range(len(inp))] scale = mixvol / sum(vol) conc=sum([inp[i].conc.stock * scale * vol[i] for i in range(len(inp))]) / mixvol if tgtconc is not None and conc>tgtconc: scale*=tgtconc*1.0/conc if max(vol)*scale<4.0: scale=4.1/max(vol) # At least one input with 4ul input vol = [x * scale for x in vol] # Mix to make planned total without water for i in range(len(vol)): # Check if this would require more than available of any input newscale= min(maxinpvol,inp[i].volume-inp[i].plate.unusableVolume()-2)/vol[i] if newscale<1: vol = [x * 1.0 * newscale for x in vol] if tgtconc is not None: mixvol *= newscale # Maintain same target concentration by reducing total volume if min(vol) < 4.0: # Some components are too small; split mixing lowvol=[i for i in range(len(inp)) if vol[i]<4.0] highvol=[i for i in range(len(inp)) if i not in lowvol] assert len(highvol)>0 assert len(lowvol)>0 lowtgtconc=sum([inp[i].conc.stock *1.0/ weights[i] for i in highvol])/len(highvol)*sum([weights[i] for i in lowvol]) print("Running premix of samples "+",".join(["%d"%ind for ind in lowvol])+" with target concentration of %.4f"%lowtgtconc) mix1=self.mix([inp[i] for i in lowvol],[weights[i] for i in lowvol],tgtconc=lowtgtconc,mixvol=mixvol,maxinpvol=maxinpvol) wt1=sum([weights[i] for i in lowvol]) mix2=self.mix([inp[i] for i in highvol]+[mix1],[weights[i] for i in highvol]+[wt1],tgtconc=tgtconc,mixvol=mixvol,maxinpvol=maxinpvol) return mix2 print("Mixing into %.0ful with tgtconc of %s, dil=%.2f"%(mixvol,"None" if tgtconc is None else "%.4f"%tgtconc,mixvol/sum(vol))) for i in range(len(inp)): print("%-30.30s %6.3fnM wt=%5.2f v=%5.2ful"%(inp[i].name,inp[i].conc.stock,weights[i],vol[i])) watervol = mixvol - sum(vol) #print "Mixdown: vols=[", ",".join(["%.2f " % v for v in vol]), "], water=", watervol, ", total=", mixvol, " ul" mixdown = Sample('mixdown', plate=decklayout.SAMPLEPLATE) if watervol < -0.1: print("Total mixdown is %.1f ul, more than planned %.0f ul" % (sum(vol), mixvol)) assert False elif watervol >= 4.0: # Omit if too small self.e.transfer(watervol, decklayout.WATER, mixdown, (False, False)) else: pass ordering=sorted(list(range(len(inp))),key=lambda i: vol[i],reverse=True) for i in ordering: inp[i].conc.final = inp[i].conc.stock * vol[i] / mixvol # Avoid warnings about concentrations not adding up self.e.transfer(vol[i], inp[i], mixdown, (False, False)) self.e.shakeSamples([mixdown]) if not mixdown.wellMixed: self.e.mix(mixdown) mixdown.conc = Concentration(stock=sum([inp[i].conc.stock * vol[i] for i in range(len(inp))]) / mixvol, final=None, units='nM') print("Mix product, %s, is in well %s with %.1ful @ %.2f nM"%(mixdown.name,mixdown.plate.wellname(mixdown.well),mixdown.volume,mixdown.conc.stock)) print("----------") return mixdown
def oldmix(self, inp, weights, tgtdil=1.0): """Mix given inputs according to weights (by moles -- use conc.stock of each input)""" print("Mix: tgtdil=%.2f, inp=" % tgtdil, ",".join( ["%s@%.2f" % (inp[i].name, weights[i]) for i in range(len(inp))])) mixvol = 100.0 if len(inp) == 1: if tgtdil > 1.0: vol = [mixvol / tgtdil] if vol[0] < 4.0: vol[0] = 4.0 else: # Special case, just dilute 10x vol = [mixvol / 10] else: relvol = [weights[i] * 1.0 / inp[i].conc.stock for i in range(len(inp))] scale = mixvol / sum(relvol) for i in range(len(inp)): if relvol[i] * scale > inp[i].volume - 16.4: scale = (inp[i].volume - 16.4) / relvol[i] vol = [x * scale for x in relvol] if min(vol) > 4.0 and tgtdil > 1.0: scale = max(1.0 / tgtdil, 4.0 / min(vol)) print("Rescale volumes by %.2f to get closer to target dilution of %.2f" % (scale, tgtdil)) vol = [x * scale for x in vol] print("Mix1: vols=[", ",".join(["%.3f" % v for v in vol]), "]") if min(vol) < 4.0: logging.info("Minimum volume into mixing would be only %.2f ul - staging..." % min(vol)) if max(vol) < 4.0: # All volumes are low, just too much # Split into 2 stages sel = list(range(int(len(inp) / 2))) nsel = list(range(int(len(inp) / 2), len(inp))) else: # Choose a splitting threshold mindilution = 4.0 / min(vol) thresh = np.median(vol) while mixvol / sum([v for v in vol if v < thresh]) < mindilution: thresh = thresh * 0.8 print("Using %.2f as threshold to split mixdown" % thresh) sel = [i for i in range(len(inp)) if vol[i] < thresh] nsel = [i for i in range(len(inp)) if vol[i] >= thresh] print("Mixing ", ",".join([inp[i].name for i in sel]), " with vols [", ",".join( ["%.2f" % vol[i] for i in sel]), "] in separate stage.") tgtdil = float(np.median([vol[i] for i in nsel]) / sum([vol[i] for i in sel])) print("tgtdil=%.2f" % tgtdil) mix1 = self.mix([inp[i] for i in sel], [weights[i] for i in sel], tgtdil) mix2 = self.mix([inp[i] for i in nsel] + [mix1], [weights[i] for i in nsel] + [sum([weights[i] for i in sel])]) return mix2 watervol = mixvol - sum(vol) print("Mixdown: vols=[", ",".join(["%.2f " % v for v in vol]), "], water=", watervol, ", total=", mixvol, " ul") mixdown = Sample('mixdown', plate=decklayout.SAMPLEPLATE) if watervol < -0.1: print("Total mixdown is %.1f ul, more than planned %.0f ul" % (sum(vol), mixvol)) assert False elif watervol > 0.0: self.e.transfer(watervol, decklayout.WATER, mixdown, (False, False)) else: pass for i in range(len(inp)): inp[i].conc.final = inp[i].conc.stock * vol[i] / mixvol # Avoid warnings about concentrations not adding up self.e.transfer(vol[i], inp[i], mixdown, (False, i == len(inp) - 1)) self.e.shakeSamples([mixdown]) mixdown.conc = Concentration(stock=sum([inp[i].conc.stock * vol[i] for i in range(len(inp))]) / mixvol, final=None, units='nM') print("Mixdown final concentration = %.1f nM" % mixdown.conc.stock) return mixdown