def runQPCRDIL(self,src,vol,srcdil,tgt=None,dilPlate=False,pipMix=False,dilutant=decklayout.SSDDIL): [src,vol,srcdil]=listify([src,vol,srcdil]) vol=[float(v) for v in vol] if tgt is None: if dilPlate: tgt=[Sample(diluteName(src[i].name,srcdil[i]),decklayout.DILPLATE) for i in range(len(src))] else: tgt=[Sample(diluteName(src[i].name,srcdil[i]),decklayout.SAMPLEPLATE) for i in range(len(src))] srcvol=[vol[i]/srcdil[i] for i in range(len(vol))] watervol=[vol[i]-srcvol[i] for i in range(len(vol))] if len(watervol) > 4 and sum(watervol)>800: logging.notice("Could optimize distribution of "+str(len(watervol))+" moves of "+dilutant.name+": vol=["+str(["%.1f"%w for w in watervol])+"]") self.e.multitransfer(watervol,dilutant,tgt,(False,False)) self.e.shakeSamples(src,returnPlate=True) for i in range(len(src)): tgt[i].conc=None # Assume dilutant does not have a concentration of its own # Check if we can align the tips here if i<len(src)-3 and tgt[i].well+1==tgt[i+1].well and tgt[i].well+2==tgt[i+2].well and tgt[i].well+3==tgt[i+3].well and tgt[i].well%4==0 and self.e.cleanTips!=15: #print "Aligning tips" self.e.sanitize() self.e.transfer(srcvol[i],src[i],tgt[i],(not src[i].isMixed(),pipMix)) if tgt[i].conc != None: tgt[i].conc.final=None # Final conc are meaningless now return tgt
def __str__(self): s="" logging.notice(self.logentries) for e in self.logentries: le=self.logentries[e] s=s+str(le[0].sample)+":""\n" for ee in self.logentries[e]: s=s+" "+str(ee)+"\n" return s
def logmeasure(self,tip,height,submerge,zmax,zadd,time): # Time is the time in seconds of this measurement sample=self.lastSample[tip] if len(sample.extrainfo)>0: elapsed=time-sample.extrainfo[0] else: elapsed=timedelta(0) #print "%s: %f"%(sample.name,elapsed) sample.extrainfo=[time] # Keep track of last measurement time of this sample in the extrainfo list if sample.plate.location.zmax is not None: curzmax=2100-sample.plate.location.zmax-390+TIPOFFSETS[tip-1] if zmax!=curzmax: logging.warning("ZMax for plate %s, tip %d at time of run was %.0f, currently at %.0f"%(sample.plate.name, tip, zmax, curzmax)) zmax=curzmax prevol=sample.volume-sample.lastadd # Liquid height is measured before next op, whose volume effect has already been added to sample.volume if height==-1: vol=sample.plate.getliquidvolume((zadd+submerge)/10.0) if vol is not None: if prevol<vol and prevol!=0: # The liquid measure failed, but based on the previous volume estimate, it was guaranteed to fail since the submerge depth would've been below bottom # But if we don't know the volume (ie a prefilled tube -> prevol=0), then log this as a fail h=" @[DEEP <%.1fmm:<%.1ful#%d]"%((zadd+submerge)/10.0,vol,tip) #h="" else: h=" @[FAIL <%.1fmm:<%.1ful#%d]"%((zadd+submerge)/10,vol,tip) else: h=" @[FAIL <%.1f#%d]"%((zadd+submerge)/10.0,tip) else: vol=sample.plate.getliquidvolume((height+submerge-zmax)/10.0) if vol is None: h=" @[%.1fmm,%.1fmm#%d]"%((height-zmax)/10.0,submerge/10.0,tip) else: if prevol==0: logging.notice("Got a liquid height measurement for a well that should be empty -- assuming it was prefilled") sample.volume=vol+sample.lastadd prevol=vol expectHeight=sample.plate.getliquidheight(prevol) errorHeight=(height+submerge-zmax)-expectHeight*10 h=" @[%.1fmm,%.1fmm:%.1ful#%d]"%((height-zmax)/10.0,submerge/10.0,vol,tip) if abs(errorHeight)>1: if abs(errorHeight)>4: emphasize="*"*(min(10,int(abs(errorHeight))-3)) else: emphasize='' h=h+"{%sE=%d;%.1ful}"%(emphasize,errorHeight,vol-prevol) sample.volume=sample.volume+(vol-prevol) # Insert BEFORE last history entry since the liquid height is measured before aspirate/dispense hsplit=sample.history.split(' ') if elapsed.total_seconds()>=600: # Log elapsed time if more than 10 min sample.history=" ".join(hsplit[:-1]+["(T%.0f)"%(elapsed.total_seconds()/60)]+[h]+hsplit[-1:]) else: sample.history=" ".join(hsplit[:-1]+[h]+hsplit[-1:])
def __init__(self,op,tip,vol,wellx,welly,rack,grid,pos,lc,std,volset,isMulti): self.op=op self.tip=tip self.vol=vol self.lc=lc self.std=std self.volset=volset self.isMulti=isMulti self.sample=getSample(wellx,welly,rack,grid,pos) if lc=='Air' or lc[0:7]=='Blowout' or lc=='Dip': if op=='dispense': self.sample.addhistory(lc,vol,tip) elif op=='aspirate': self.sample.addhistory(lc,-vol,tip) else: logging.notice("LogEntry: bad op (%s) for LC %s"%(op,lc)) assert False self.sample.lastadd=0 elif op=='dispense': self.sample.addhistory("",vol,tip) self.sample.lastadd=vol elif op=='detect': self.sample.addhistory("detect",0,tip) self.sample.lastadd=0 elif op=='aspirate': self.sample.addhistory("",-vol,tip) self.sample.lastadd=-(vol+SURFACEREMOVE) # Extra for tip wetting elif op[0:3]=='mix': self.sample.addhistory(op,vol,tip) self.sample.lastadd=0 else: logging.warning("LogEntry: bad op: %s"%op) assert False if self.sample.volume+self.sample.lastadd<0 and self.sample.volume!=0: self.sample.history=self.sample.history + ("{Emptied%.2f}"%(self.sample.volume+self.sample.lastadd)) self.sample.volume=0 else: self.sample.volume+=self.sample.lastadd
def addSamples(self, src, needDil, primers, nreplicates=1, names=None, saveVol=None, saveDil=None, save=True): """Add sample(s) to list of qPCRs to do""" # print "addSamples(%s)"%src if not isinstance(src, list): src = [src] if save: # saveVol is total amount (after dilution) to be immediately saved if saveDil is None: saveDil = min(needDil, self.MAXDIL) if 1 < needDil / saveDil < 2: saveDil = math.sqrt(needDil) elif saveDil > needDil: logging.warning("addSamples: saveDil=" + saveDil + ", but needDil is only " + needDil) saveDil = needDil if saveVol is None: saveVol = max(self.MINDILVOL * 1.0 / saveDil, self.TGTINVOL) if names is None: tgt = [Sample(diluteName(src[i].name, saveDil), decklayout.DILPLATE) for i in range(len(src))] else: tgt = [Sample(diluteName(names[i], saveDil), decklayout.DILPLATE) for i in range(len(src))] sv = tgt for i in range(len(sv)): # print "Save ",src[i] svtmp = self.trp.runQPCRDIL(src=[src[i]], vol=saveVol * saveDil, srcdil=saveDil, tgt=[tgt[i]], dilPlate=True, dilutant=self.dilutant) sv[i] = svtmp[0] else: saveDil = 1 sv = src needDil = needDil / saveDil nstages = int(math.ceil(math.log(needDil) / math.log(self.MAXDIL))) ndil = len(src) * (nstages + (1 if save else 0)) logging.notice("QPCR: %dQ/%dD [%s], dilution:%.1fx, primers: [%s]" % ( len(src) * len(primers) * nreplicates, ndil, ",".join([s.name for s in src]) if names is None else ",".join(names), needDil, ",".join(primers))) for svi in range(len(sv)): s = sv[svi] if s.hasBeads: prereqs = [] else: j0 = self.jobq.addShake(sample=s, prereqs=[]) prereqs = [j0] intermed = s for i in range(nstages): dil = math.pow(needDil, 1.0 / nstages) # print "stage ",i,", needDil=",needDil,", dil=",dil if i > 0: vol = self.MAXDILVOL else: vol = min(self.MAXDILVOL * 1.0, max(self.MINDILVOL * 1.0, dil * self.TGTINVOL * 1.0)) if intermed.plate == decklayout.DILPLATE: firstWell = intermed.well + 4 # Skip by 4 wells at a time to optimize multi-tip movements else: firstWell = 0 if not save and i == 0 and names is not None: # Need to replace the name in this condition dest = Sample(diluteName(names[svi], dil), decklayout.DILPLATE, firstWell=firstWell) else: dest = Sample(diluteName(intermed.name, dil), decklayout.DILPLATE, firstWell=firstWell) # print "dest=",dest j1 = self.jobq.addMultiTransfer(volume=vol * (dil - 1) / dil, src=self.dilutant, dest=dest, prereqs=[]) prereqs.append(j1) j2 = self.jobq.addTransfer(volume=vol / dil, src=intermed, dest=dest, prereqs=prereqs) # print "Dilution of %s was %.2f instead of %.2f (error=%.0f%%)"%(dest.name,(dil/(1+dil))/(1/dil),dil,((dil/(1+dil))/(1/dil)/dil-1)*100) if dest.hasBeads: prereqs = [j2] else: j3 = self.jobq.addShake(sample=dest, prereqs=[j2]) prereqs = [j3] intermed = dest self.dilProds = self.dilProds + [intermed] self.primers = self.primers + [primers] self.nreplicates = self.nreplicates + [nreplicates]
def logspeed(self,platename,speed): logging.notice("logspeed(%s,%d)"%(platename,speed)) Sample.addallhistory("(S@%d)"%speed,onlyplate=platename)
def oneround(self, q, input, prefixOut, stop, prefixIn, keepCleaved, t7vol, rtvol, pcrdil, cycles, pcrvol, dolig): primerSet = [ set(["MX", "REF", "T7X", prefixIn[i] + "X", prefixOut[i] + "X"]) for i in range(len(prefixIn)) ] if keepCleaved: print "Starting new cleavage round, will add prefix: ", prefixOut assert (dolig) else: print "Starting new uncleaved round, will retain prefix: ", prefixIn print "stop=", stop, "prefixOut=", prefixOut, ", prefixIn=", prefixIn, ",t7vol=", t7vol, ",rtvol=", rtvol, ",pcrdil=", pcrdil, ",cycles=", cycles, ",dolig=", dolig if self.rtCarryForward: assert (dolig) names = [i.name for i in input] if self.rnaInput: rxs = input stopDil = 1 else: print "######## T7 ########### %.0f min" % (clock.elapsed() / 60) print "Inputs: (t7vol=%.2f)" % t7vol inconc = [inp.conc.final for inp in input] for inp in input: if inp.conc.units == 'nM': print " %s: %.1ful@%.1f %s, use %.1f ul (%.3f pmoles)" % ( inp.name, inp.volume, inp.conc.stock, inp.conc.units, t7vol / inp.conc.dilutionneeded(), t7vol * inp.conc.final / 1000) needDil = max([inp.conc.stock for inp in input]) * 1.0 / self.qConc else: print " %s: %.1ful@%.1f %s, use %.1f ul" % ( inp.name, inp.volume, inp.conc.stock, inp.conc.units, t7vol / inp.conc.dilutionneeded()) needDil = 100 / self.qConc # Assume 100nM # inp.conc.final=inp.conc.stock*self.templateDilution if self.directT7 and self.rndNum == 1: # Just add ligands and MT7 to each well if not keepCleaved: for i in range(len(input)): if self.inputs[i]['ligand'] is not None: ligand = reagents.getsample( self.inputs[i]['ligand']) self.e.transfer(t7vol / ligand.conc.dilutionneeded(), ligand, input[i], mix=(False, False)) names[i] += "+" mconc = reagents.getsample("MT7").conc.dilutionneeded() for i in range(len(input)): watervol = t7vol * (1 - 1 / mconc) - input[i].volume if watervol > 0.1: self.e.transfer(watervol, decklayout.WATER, input[i], mix=(False, False)) self.e.transfer(t7vol / mconc, reagents.getsample("MT7"), input[i], mix=(False, False)) assert (abs(input[i].volume - t7vol) < 0.1) rxs = input elif self.rndNum == len( self.rounds) and self.finalPlus and keepCleaved: rxs = self.runT7Setup( src=input, vol=t7vol, srcdil=[inp.conc.dilutionneeded() for inp in input]) for i in range(len(input)): inp = input[i] if self.inputs[i]['ligand'] is not None: rxs += self.runT7Setup( ligands=[ reagents.getsample(self.inputs[i]['ligand']) ], src=[inp], vol=t7vol, srcdil=[inp.conc.dilutionneeded()]) prefixIn += [prefixIn[i]] prefixOut += [prefixOut[i]] stop += [stop[i]] primerSet += [primerSet[i]] names += ["%s+" % names[i]] elif keepCleaved: rxs = self.runT7Setup( src=input, vol=t7vol, srcdil=[inp.conc.dilutionneeded() for inp in input]) else: rxs = self.runT7Setup( ligands=[ reagents.getsample(inp['ligand']) for inp in self.inputs ], src=input, vol=t7vol, srcdil=[inp.conc.dilutionneeded() for inp in input]) if self.rndNum == 1 and "template" in self.qpcrStages: # Initial input for i in range(len(rxs)): q.addSamples(src=rxs[i], needDil=needDil, primers=primerSet[i], names=["%s.T" % names[i]]) needDil = needDil * max( [inp.conc.dilutionneeded() for inp in input]) self.runT7Pgm(dur=self.t7dur, vol=t7vol) for i in range(len(rxs)): rxs[i].name = "%s.t7" % names[i] print "Estimate usable RNA concentration in T7 reaction at %.0f nM" % self.rnaConc print "######## Stop ########### %.0f min" % (clock.elapsed() / 60) self.e.lihahome() print "Have %.1f ul before stop" % rxs[0].volume preStopVolume = rxs[0].volume self.addEDTA(tgt=rxs, finalconc=2) # Stop to 2mM EDTA final stopDil = rxs[0].volume / preStopVolume if self.saveRNA: self.saveSamps( src=rxs, vol=5, dil=self.saveRNADilution, plate=decklayout.DILPLATE, dilutant=reagents.getsample("TE8"), mix=(False, False)) # Save to check [RNA] on Qubit, bioanalyzer needDil = self.rnaConc / self.qConc / stopDil if "stopped" in self.qpcrStages: for i in range(len(rxs)): q.addSamples(src=rxs[i:i + 1], needDil=needDil, primers=primerSet[i], names=["%s.stopped" % names[i]]) print "######## RT Setup ########### %.0f min" % (clock.elapsed() / 60) hiTemp = 95 stop = ["%s-Stop" % n for n in stop] rt = self.runRT(src=rxs, vol=rtvol, srcdil=self.rtDil, heatInactivate=self.rtHI, hiTemp=hiTemp, dur=self.rtdur, incTemp=50, stop=[reagents.getsample(s) for s in stop], stopConc=self.stopConc ) # Heat inactivate also allows splint to fold rxs = rt for i in range(len(rxs)): if dolig and not self.singlePrefix: rxs[i].name = names[i] + "." + prefixOut[i] + ".rt" else: rxs[i].name = names[i] + ".rt" print "RT volume= [", ",".join(["%.1f " % x.volume for x in rxs]), "]" needDil /= self.rtDil if self.rtpostdil[self.rndNum - 1] > 1: print "Dilution after RT: %.2f" % self.rtpostdil[self.rndNum - 1] self.diluteInPlace(tgt=rxs, dil=self.rtpostdil[self.rndNum - 1]) needDil = needDil / self.rtpostdil[self.rndNum - 1] if self.rtSave: rtsv = self.saveSamps( src=rxs, vol=self.rtSaveVol, dil=self.rtSaveDil, plate=decklayout.DILPLATE, dilutant=reagents.getsample("TE8"), mix=(False, False)) # Save to check RT product on gel (2x dil) if "rt" in self.qpcrStages: for i in range(len(rxs)): q.addSamples(src=rtsv[i:i + 1], needDil=needDil / 2, primers=self.rtprimers[self.rndNum - 1] if hasattr(self, 'rtprimers') else primerSet[i], names=["%s.rt" % names[i]]) else: if "rt" in self.qpcrStages: for i in range(len(rxs)): q.addSamples(src=rxs[i:i + 1], needDil=needDil, primers=self.rtprimers[self.rndNum - 1] if hasattr(self, 'rtprimers') else primerSet[i], names=["%s.rt" % names[i]]) rtCarryForwardDil = 10 rtCarryForwardVol = 3.5 if self.rtCarryForward and not keepCleaved: # Also include RT from a prior round from here on for r in self.lastSaved: newsamp = Sample("%s.samp" % r.name, decklayout.SAMPLEPLATE) self.e.transfer(rxs[0].volume, r, newsamp, (False, False)) rxs.append(newsamp) if dolig: print "######## Ligation setup ########### %.0f min" % ( clock.elapsed() / 60) extdil = 5.0 / 4 reagents.getsample("MLigase").conc = Concentration(5) if self.ligInPlace: rxs = self.runLig(rxs, inPlace=True, srcdil=extdil, incTime=self.ligdur) else: rxs = self.runLig(rxs, inPlace=False, srcdil=extdil, vol=20, incTime=self.ligdur) print "Ligation volume= ", [x.volume for x in rxs] needDil = needDil / extdil if self.extpostdil[self.rndNum - 1] > 1: print "Dilution after extension: %.2f" % self.extpostdil[ self.rndNum - 1] self.diluteInPlace(tgt=rxs, dil=self.extpostdil[self.rndNum - 1]) needDil = needDil / self.extpostdil[self.rndNum - 1] pcrdil = pcrdil * 1.0 / self.extpostdil[self.rndNum - 1] if self.saveDil is not None: ext = self.saveSamps( src=rxs, vol=3, dil=self.saveDil, dilutant=reagents.getsample("TE8"), tgt=[ Sample("%s.ext" % n, decklayout.DILPLATE) for n in names ], mix=(False, True)) # Save cDNA product for subsequent NGS if "ext" in self.qpcrStages: for i in range(len(ext)): # Make sure we don't take more than 2 more steps maxdil = q.MAXDIL * q.MAXDIL if needDil / self.saveDil > maxdil: logging.notice( "Diluting ext by %.0fx instead of needed %.0f to save steps" % (maxdil, needDil / self.saveDil)) q.addSamples(src=[ext[i]], needDil=min(maxdil, needDil / self.saveDil), primers=primerSet[i], names=["%s.ext" % names[i]], save=False) else: if "ext" in self.qpcrStages: print "needDil=", needDil for i in range(len(names)): q.addSamples(src=[rxs[i]], needDil=needDil, primers=primerSet[i], names=["%s.ext" % names[i]]) isave = i + len(names) if isave < len(rxs): # samples restored q.addSamples(src=[rxs[isave]], needDil=needDil / rtCarryForwardDil, primers=primerSet[isave]) else: extdil = 1 self.extpostdil[self.rndNum - 1] = 1 if self.rtpostdil[self.rndNum - 1] > 1: pcrdil = pcrdil * 1.0 / self.rtpostdil[self.rndNum - 1] totalDil = stopDil * self.rtDil * self.rtpostdil[ self.rndNum - 1] * extdil * self.extpostdil[self.rndNum - 1] fracRetained = rxs[0].volume / (t7vol * totalDil) print "Total dilution from T7 to Pre-pcr Product = %.2f*%.2f*%.2f*%.2f*%.2f = %.2f, fraction retained=%.0f%%" % ( stopDil, self.rtDil, self.rtpostdil[self.rndNum - 1], extdil, self.extpostdil[self.rndNum - 1], totalDil, fracRetained * 100) if self.rtCarryForward and not keepCleaved: # Remove the extra samples assert (len(self.lastSaved) > 0) rxs = rxs[:len(rxs) - len(self.lastSaved)] self.lastSaved = [] if len(rxs) > len(input): # Have extra samples due when self.finalPlus is True rxs = rxs[0:len(input)] # Only keep -target products prefixOut = prefixOut[0:len(input)] prefixIn = prefixIn[0:len(input)] stop = stop[0:len(input)] if self.dopcr and not (keepCleaved and self.noPCRCleave): print "######### PCR ############# %.0f min" % (clock.elapsed() / 60) maxvol = max([r.volume for r in rxs]) print "PCR Volume: %.1f, Dilution: %.1f, volumes available for PCR: [%s]" % ( pcrvol, pcrdil, ",".join(["%.1f" % r.volume for r in rxs])) initConc = needDil * self.qConc / pcrdil if keepCleaved: initConc = initConc * self.cleavage # Only use cleaved as input conc else: initConc = initConc * (1 - self.cleavage) gain = pcrgain(initConc, 400, cycles) finalConc = min(200, initConc * gain) print "Estimated starting concentration in PCR = %.1f nM, running %d cycles -> %.0f nM\n" % ( needDil * self.qConc / pcrdil, cycles, finalConc) nsplit = int(math.ceil(pcrvol * 1.0 / self.maxPCRVolume)) print "Split each PCR into %d reactions" % nsplit minsrcdil = 1 / (1 - 1.0 / 3 - 1.0 / 4) sampNeeded = pcrvol / pcrdil if self.rtCarryForward and keepCleaved: sampNeeded += rtCarryForwardVol maxvol = max([r.volume for r in rxs]) minvol = min([r.volume for r in rxs]) if keepCleaved and self.rtCarryForward: assert (len(rxs) == len(rtCarryForward)) print "Saving %.1f ul of each pre-PCR sample" % ( rtCarryForwardVol) self.lastSaved = [ Sample("%s.sv" % x.name, decklayout.DILPLATE) for x in rxs ] for i in range(len(rxs)): # Save with rtCarryForwardDil dilution to reduce amount of RT consumed (will have Ct's 2-3 lower than others) self.e.transfer(rtCarryForwardVol, rxs[i], self.lastSaved[i], (False, False)) self.e.transfer( rtCarryForwardVol * (rtCarryForwardDil - 1), decklayout.WATER, self.lastSaved[i], (False, True) ) # Use pipette mixing -- shaker mixing will be too slow #print "NSplit=",nsplit,", PCR vol=",pcrvol/nsplit,", srcdil=",pcrdil,", input vol=",pcrvol/nsplit/pcrdil minvol = min([r.volume for r in rxs]) maxpcrvol = (minvol - 15 - 1.4 * nsplit) * pcrdil if maxpcrvol < pcrvol: print "Reducing PCR volume from %.1ful to %.1ful due to limited input" % ( pcrvol, maxpcrvol) pcrvol = maxpcrvol if keepCleaved: master = "MTaqC" else: master = "MTaqU" if self.barcoding: primers = self.bcprimers[self.rndNum - 1] if primers is not None and nsplit > 1: primers = primers * nsplit else: primers = None if primers is None: primers = [("T7%sX" % x).replace("T7T7", "T7") for x in prefixOut] * nsplit print "Running PCR with master=", master, ", primers=", primers pcr = self.runPCR(src=rxs * nsplit, vol=pcrvol / nsplit, srcdil=pcrdil, ncycles=cycles, primers=primers, usertime=self.usertime if keepCleaved else None, fastCycling=False, inPlace=False, master=master, lowhi=self.lowhi, annealTemp=57) if keepCleaved and self.regenPCRCycles is not None: # Regenerate prefix pcr2 = self.runPCR(src=pcr, vol=self.regenPCRVolume, srcdil=self.regenPCRDilution, ncycles=self.regenPCRCycles, primers=None, usertime=None, fastCycling=False, inPlace=False, master="MTaqR", lowhi=self.lowhi, annealTemp=55) # Add BT575p for 1 more cycle for p in pcr2: self.e.transfer(p.volume * 0.5 / 10, reagents.getsample("Unclvd-Stop"), p, (False, False)) # One more cycle cycling = ' TEMP@95,30 TEMP@55,30 TEMP@68,30 TEMP@25,2' worklist.pyrun('PTC\\ptcsetpgm.py rfin %s' % (cycling)) self.e.runpgm("rfin", 5.0, False, max([p.volume for p in pcr2]), hotlidmode="CONSTANT", hotlidtemp=100) pcr = pcr2 # Use 2nd PCR as actual output if len(pcr) <= len(names): # Don't relabel if we've split for i in range(len(pcr)): pcr[i].name = names[i] + ".pcr" #print "Volume remaining in PCR input source: [",",".join(["%.1f"%r.volume for r in rxs]),"]" needDil = finalConc / self.qConc print "Projected final concentration = %.0f nM" % (needDil * self.qConc) for i in range(len(pcr)): pcr[i].conc = Concentration(stock=finalConc, final=None, units='nM') if self.pcrSave: # Save samples at 1x (move all contents -- can ignore warnings) maxSaveVol = (100 if self.savedilplate else 1500) * 1.0 / nsplit if self.finalRound and nsplit == 1 and self.savedilplate: print "Skipping save of final PCR" sv = pcr else: sv = self.saveSamps( src=pcr[:len(rxs)], vol=[ min([maxSaveVol, x.volume]) for x in pcr[:len(rxs)] ], dil=1, plate=(decklayout.DILPLATE if self.savedilplate else decklayout.EPPENDORFS), atEnd=self.savePCRAtEnd) if nsplit > 1: # Combine split for i in range(len(rxs), len(rxs) * nsplit): self.e.transfer(min([maxSaveVol, pcr[i].volume]), pcr[i], sv[i % len(sv)], mix=(False, i >= len(rxs) * (nsplit - 1))) # Correct concentration (above would've assumed it was diluted) for i in range(len(sv)): sv[i].conc = pcr[i].conc if "pcr" in self.qpcrStages: for i in range(len(sv)): q.addSamples(sv[i], needDil, primers=primerSet[i], names=["%s.pcr" % names[i]]) processEff = 0.5 # Estimate of overall efficiency of process print "Have %.2f pmoles of product (%.0f ul @ %.1f nM)" % ( sv[0].volume * sv[0].conc.stock / 1000, sv[0].volume, sv[0].conc.stock) return sv else: assert "pcr" not in self.qpcrStages ## Not implemented return pcr[:len(rxs)] elif self.noPCRCleave: print "Dilution instead of PCR: %.2f" % self.nopcrdil # Need to add enough t7prefix to compensate for all of the Stop primer currently present, regardless of whether it is for cleaved or uncleaved # Will result in some short transcripts corresponding to the stop primers that are not used for cleaved product, producing just GGG_W_GTCTGC in the next round. These would be reverse-trancribed, but may compete for T7 yield t7prefix = reagents.getsample("BT88") dil = self.extpostdil[self.rndNum - 1] * userDil stopconc = 1000.0 / dil bt88conc = t7prefix.conc.stock relbt88 = stopconc / bt88conc print "Using EXT with %.0fnM of stop oligo as input to next T7, need %.2ful of BT88@%.0fnM per ul of sample" % ( stopconc, relbt88, bt88conc) for r in rxs: vol = r.volume * relbt88 t7prefix.conc.final = t7prefix.conc.stock * vol / (r.volume + vol) r.conc.final = r.conc.stock * r.volume / (r.volume + vol) self.e.transfer(vol, t7prefix, r, mix=(False, False)) if self.nopcrdil > (1 + relbt88): self.diluteInPlace(tgt=rxs, dil=self.nopcrdil / (1.0 + relbt88)) needDil = needDil / self.nopcrdil print "Dilution of EXT product: %.2fx * %.2fx = %2.fx\n" % ( 1 + relbt88, self.nopcrdil / (1 + relbt88), self.nopcrdil) else: print "Dilution of EXT product: %.2fx\n" % (1 + relbt88) return rxs else: return rxs
def addSamples(self, src, needDil, primers, nreplicates=1, names=None, saveVol=None, saveDil=None, save=True): 'Add sample(s) to list of qPCRs to do' #print "addSamples(%s)"%src if not isinstance(src, list): src = [src] if save: # saveVol is total amount (after dilution) to be immediately saved if saveDil is None: saveDil = min(needDil, self.MAXDIL) if needDil / saveDil > 1 and needDil / saveDil < 2: saveDil = math.sqrt(needDil) elif saveDil > needDil: logging.warning("addSamples: saveDil=", saveDil, ", but needDil is only ", needDil) saveDil = needDil if saveVol is None: saveVol = max(self.MINDILVOL * 1.0 / saveDil, self.TGTINVOL) if names is None: tgt = [ Sample(diluteName(src[i].name, saveDil), decklayout.DILPLATE) for i in range(len(src)) ] else: tgt = [ Sample(diluteName(names[i], saveDil), decklayout.DILPLATE) for i in range(len(src)) ] sv = tgt for i in range(len(sv)): #print "Save ",src[i] svtmp = self.trp.runQPCRDIL(src=[src[i]], vol=saveVol * saveDil, srcdil=saveDil, tgt=[tgt[i]], dilPlate=True, dilutant=self.dilutant) sv[i] = svtmp[0] else: saveDil = 1 sv = src needDil = needDil / saveDil nstages = int(math.ceil(math.log(needDil) / math.log(self.MAXDIL))) ndil = len(src) * (nstages + (1 if save else 0)) logging.notice( "QPCR: %dQ/%dD [%s], dilution:%.1fx, primers: [%s]" % (len(src) * len(primers) * nreplicates, ndil, ",".join([s.name for s in src]) if names is None else ",".join(names), needDil, ",".join(primers))) for svi in range(len(sv)): s = sv[svi] if s.hasBeads: prereqs = [] else: j0 = self.jobq.addShake(sample=s, prereqs=[]) prereqs = [j0] intermed = s for i in range(nstages): dil = math.pow(needDil, 1.0 / nstages) #print "stage ",i,", needDil=",needDil,", dil=",dil if i > 0: vol = self.MAXDILVOL else: vol = min(self.MAXDILVOL, max(self.MINDILVOL, dil * self.TGTINVOL)) if intermed.plate == decklayout.DILPLATE: firstWell = intermed.well + 4 # Skip by 4 wells at a time to optimize multi-tip movements else: firstWell = 0 if not save and i == 0 and names is not None: # Need to replace the name in this condition dest = Sample(diluteName(names[svi], dil), decklayout.DILPLATE, firstWell=firstWell) else: dest = Sample(diluteName(intermed.name, dil), decklayout.DILPLATE, firstWell=firstWell) #print "dest=",dest j1 = self.jobq.addMultiTransfer(volume=vol * (dil - 1) / dil, src=self.dilutant, dest=dest, prereqs=[]) prereqs.append(j1) j2 = self.jobq.addTransfer(volume=vol / dil, src=intermed, dest=dest, prereqs=prereqs) #print "Dilution of %s was %.2f instead of %.2f (error=%.0f%%)"%(dest.name,(dil/(1+dil))/(1/dil),dil,((dil/(1+dil))/(1/dil)/dil-1)*100) if dest.hasBeads: prereqs = [j2] else: j3 = self.jobq.addShake(sample=dest, prereqs=[j2]) prereqs = [j3] intermed = dest self.dilProds = self.dilProds + [intermed] self.primers = self.primers + [primers] self.nreplicates = self.nreplicates + [nreplicates]
def idbarcoding(self, rsrc, left, right): """Perform barcoding of the given inputs; rsrsc,left,right should all be equal length""" pcrcycles = [4] # Don't need 2nd PCR since this will go directly into constriction #pcr1inputconc = 0.05 # PCR1 concentration final in reaction pcr1inputdil = 10 pcr1vol = 30 pcr1postdil = 100.0 / pcr1vol pcr2dil = 50 pcr2minvol = 50.0 samps = [s.getsample() for s in rsrc] print("Inputs:") for i in range(len(samps)): print("%2s %-10s %8s-%-8s %.1f%s" % ( samps[i].plate.wellname(samps[i].well), self.inputs[i]['name'], left[i], right[i], samps[i].conc.stock,samps[i].conc.units)) # Compute pcr1inputconc such that lowest concentration input ends up with at least 30ul after dilution pcr1inputconc=min([s.conc.stock*s.volume/30.0/pcr1inputdil for s in samps]) print("Diluting inputs so PCR1 final template conc = %.0f pM"%(pcr1inputconc*1000)) wellnum = 5 for s in left + right: primer = "P-" + s if not reagents.isReagent(primer): reagents.add(primer, conc=Concentration(2.67, 0.4, 'uM'), extraVol=30, plate=decklayout.REAGENTPLATE, well=decklayout.REAGENTPLATE.wellname(wellnum)) wellnum += 1 # Run first pass dilution where needed for i in range(len(samps)): # Dilute down to desired conc dil = samps[i].conc.stock / pcr1inputconc / pcr1inputdil dilvol = samps[i].volume * dil if dilvol > 100.0: logging.notice("Dilution of input %s (%.1f ul) by %.2f would require %.1f ul" % ( samps[i].name, samps[i].volume, dil, dilvol)) # Do a first pass dilution into 150ul, then remove enough so second dilution can go into 100ul dil1 = 100.0 / samps[i].volume self.diluteInPlace(tgt=[samps[i]], dil=dil1) print("First pass dilution of %s by %.1f/%.1f (conc now %.3f nM)" % (samps[i].name, dil1, dil, samps[i].conc.stock)) dil /= dil1 # Make sure they are all mixed self.e.shakeSamples(samps) # Final dilution for s in samps: # Dilute down to desired conc dil = s.conc.stock / pcr1inputconc / pcr1inputdil if dil < 1.0: logging.error("Input %s requires dilution of %.2f" % (s.name, dil)) elif dil > 1.0: dilvol = s.volume * dil if dilvol>100: toremove=s.volume-100.0/dil print("Removing %.1f ul from %s to allow enough room for dilution"%(toremove,s.name)) self.e.dispose(toremove, s) self.diluteInPlace(tgt=[s], dil=dil) print("Diluting %s by %.1f" % (s.name, dil)) pcr1 = self.runPCR(src=samps, srcdil=pcr1inputdil, ncycles=pcrcycles[0], vol=pcr1vol, primers=[[left[i], right[i]] for i in range(len(left))], usertime=0, fastCycling=False, inPlace=False, master="MKapa", kapa=True) pcr1finalconc = pcr1inputconc * self.pcreff ** pcrcycles[0] print("PCR1 output concentration = %.3f nM" % pcr1finalconc) if pcr1postdil > 1: pcr1finalconc /= pcr1postdil print("Post dilute PCR1 by %.2fx to %.3f nM " % (pcr1postdil, pcr1finalconc)) self.diluteInPlace(tgt=pcr1, dil=pcr1postdil) for x in pcr1: x.conc = Concentration(stock=pcr1finalconc, units='nM') self.q.addSamples(src=pcr1, needDil=pcr1finalconc / self.qconc, primers=self.qprimers, save=True, nreplicates=1) if len(pcrcycles) > 1: # Second PCR with 235p/236p on mixture (use at least 4ul of prior) pcr2 = self.runPCR(src=pcr1, srcdil=pcr2dil / pcr1postdil, vol=max(pcr2minvol, pcr2dil / pcr1postdil * 4), ncycles=pcrcycles[1], primers="End", fastCycling=False, master="MKapa", kapa=True) pcr2finalconc = min(200, pcr1finalconc / (pcr2dil / pcr1postdil) * self.pcreff ** pcrcycles[1]) print("PCR2 final conc = %.1f nM" % pcr2finalconc) d2 = min(4.0, 150.0 / max([p.volume for p in pcr2])) if d2 > 1: pcr2finalconc /= d2 print("Post-dilute PCR2 by %.1fx to %.3fnM" % (d2, pcr2finalconc)) self.diluteInPlace(tgt=pcr2, dil=d2) self.e.shakeSamples(pcr2) for x in pcr2: x.conc = Concentration(stock=pcr2finalconc, units='nM') self.q.addSamples(src=pcr2, needDil=pcr2finalconc / self.qconc, primers=self.qprimers, save=True, nreplicates=2) res = pcr2 else: res = pcr1 return res
def runPCR(self,primers,src,srcdil,vol=None,tgt=None,ncycles=20,usertime=None,fastCycling=False,inPlace=False,master="MTaq",annealTemp=None,kapa=False): ## PCR if inPlace: if vol!=None: print "runPCR: cannot specify volume when using inPlace=True, srcdil and input volume determine reaction volume" assert(False) if tgt!=None: print "runPCR: cannot specify tgt when using inPlace=True" assert(False) [primers,src,vol,srcdil]=listify([primers,src,vol,srcdil]) vol=[src[i].volume*srcdil[i] for i in range(len(src))] tgt=src else: [primers,src,tgt,vol,srcdil]=listify([primers,src,tgt,vol,srcdil]) for i in range(len(tgt)): if tgt[i] is None: if isinstance(primers[i],list): tgt[i]=Sample("%s.P%s"%(src[i].name,"+".join(primers[i])),src[i].plate) else: tgt[i]=Sample("%s.P%s"%(src[i].name,primers[i]),src[i].plate) # Adjust source dilution for i in range(len(src)): src[i].conc=Concentration(srcdil[i],1) logging.notice( "primer="+str(primers)) # Add reagent entries for any missing primers if isinstance(primers[0],list): allprimers=[x for y in primers for x in y] else: allprimers=primers for up in set(allprimers): s="P-%s"%up if not reagents.isReagent(s): reagents.add(name=s,conc=4,extraVol=30) if isinstance(primers[0],list): # Multiple primers if inPlace: assert len(primers[0])==2 self.runRxInPlace(src,vol,reagents.getsample(master),master2=[reagents.getsample("P-%s"%p[0]) for p in primers],master3=[reagents.getsample("P-%s"%p[1]) for p in primers],returnPlate=False) else: for i in range(len(primers)): self.e.stage('PCR%d'%i,[reagents.getsample(master)]+[reagents.getsample("P-%s"%s) for s in primers[i]],src[i:i+1] ,tgt[i:i+1],vol[i:i+1],destMix=False) #self.e.shakeSamples(tgt,returnPlate=False) else: # Single primer if inPlace: self.runRxInPlace(src,vol,reagents.getsample(master),master2=[reagents.getsample("P-%s"%p) for p in primers],returnPlate=False) else: for up in set(primers): self.e.stage('PCR%s'%up,[reagents.getsample(master),reagents.getsample("P-%s"%up)],[src[i] for i in range(len(src)) if primers[i]==up],[tgt[i] for i in range(len(tgt)) if primers[i]==up],[vol[i] for i in range(len(vol)) if primers[i]==up],destMix=False) #self.e.shakeSamples(tgt,returnPlate=False) pgm="PCR%d"%ncycles if usertime is None: runTime=0 else: runTime=usertime if annealTemp is None: annealTemp=60 if kapa else 57 meltTemp=98 if kapa else 95 hotTime=180 if kapa else 30 extTemp=72 if kapa else 68 if fastCycling: cycling='TEMP@37,%d TEMP@95,%d TEMP@%.1f,10 TEMP@%.1f,10 TEMP @%.1f,1 GOTO@3,%d TEMP@%.1f,60 TEMP@25,2'%(1 if usertime is None else usertime*60,hotTime,meltTemp,annealTemp,extTemp,ncycles-1,extTemp) runTime+=hotTime/60+2.8+1.65*ncycles else: cycling='TEMP@37,%d TEMP@95,%d TEMP@%.1f,30 TEMP@%.1f,30 TEMP@%.1f,30 GOTO@3,%d TEMP@%.1f,60 TEMP@25,2'%(1 if usertime is None else usertime*60,hotTime,meltTemp,annealTemp,extTemp,ncycles-1,extTemp) runTime+=hotTime/60+2.8+3.0*ncycles print "PCR volume=[",",".join(["%.1f"%t.volume for t in tgt]), "], srcdil=[",",".join(["%.1fx"%s for s in srcdil]),"], program: %s"%cycling worklist.pyrun('PTC\\ptcsetpgm.py %s %s'%(pgm,cycling)) self.e.runpgm(pgm,runTime,False,max(vol),hotlidmode="CONSTANT",hotlidtemp=100) # Mark samples as mixed (by thermal convection) print "Marking samples as mixed (by thermal convection)" for t in tgt: t.wellMixed=True t.lastMixed=clock.elapsed() #self.e.shakeSamples(tgt,returnPlate=True) return tgt
def beadWash(self,src,washTgt=None,sepTime=None,residualVolume=0.1,keepWash=False,numWashes=2,wash=None,washVol=50,keepFinal=False,finalTgt=None,keepVol=4.2,keepDil=5,shakeWashes=False): # Perform washes # If keepWash is true, retain all washes (combined) # If keepFinal is true, take a sample of the final wash (diluted by keepDil) if wash is None: wash=decklayout.WATER [src,wash]=listify([src,wash]) # Do all washes while on magnet assert(len(set([s.plate for s in src]))==1) # All on same plate if keepWash: if washTgt is None: washTgt=[] for i in range(len(src)): if s[i].volume-residualVolume+numWashes*(washVol-residualVolume) > decklayout.DILPLATE.maxVolume-20: logging.notice("Saving %.1f ul of wash in eppendorfs"%(numWashes*washVol)) washTgt.append(Sample("%s.Wash"%src[i].name,decklayout.EPPENDORFS)) else: washTgt.append(Sample("%s.Wash"%src[i].name,decklayout.DILPLATE)) if keepFinal: if finalTgt is None: finalTgt=[] for i in range(len(src)): finalTgt.append(Sample("%s.Final"%src[i].name,decklayout.DILPLATE)) if any([s.volume>residualVolume for s in src]): # Separate and remove supernatant self.e.moveplate(src[0].plate,"Magnet") # Move to magnet self.sepWait(src,sepTime) # Remove the supernatant for i in range(len(src)): if src[i].volume > residualVolume: amt=src[i].amountToRemove(residualVolume) if keepWash: self.e.transfer(amt,src[i],washTgt[i]) # Keep supernatants washTgt[i].conc=None # Allow it to be reused else: self.e.dispose(amt,src[i]) # Discard supernatant # Wash for washnum in range(numWashes): if src[0].plate.curloc!="Home" and src[0].plate.curloc!="Magnet": self.e.moveplate(src[0].plate,"Home") if keepFinal and washnum==numWashes-1: 'Retain sample of final' for i in range(len(src)): src[i].conc=None self.e.transfer(washVol-src[i].volume,wash[i],src[i],mix=(False,True)) # Add wash self.e.shakeSamples(src,returnPlate=True) self.saveSamps(src=src,tgt=finalTgt,vol=keepVol,dil=keepDil,plate=decklayout.DILPLATE) else: for i in range(len(src)): src[i].conc=None self.e.transfer(washVol-src[i].volume,wash[i],src[i],mix=(False,False)) # Add wash, no need to pipette mix since some heterogenity won't hurt here if shakeWashes: self.e.shakeSamples(src,returnPlate=False) self.e.moveplate(src[0].plate,"Magnet") # Move to magnet self.sepWait(src,sepTime) for i in range(len(src)): amt=src[i].amountToRemove(residualVolume) if keepWash: self.e.transfer(amt,src[i],washTgt[i],mix=(False,False)) # Remove wash washTgt[i].conc=None # Allow it to be reused else: self.e.dispose(amt,src[i]) # Remove wash #self.e.moveplate(src[0].plate,"Home") # Should only be residualVolume left with beads now result=[] if keepWash: result=result+washTgt if keepFinal: result=result+finalTgt return result
def oneround(self, q, inputs, prefixOut, stop, prefixIn, keepCleaved, t7vol, rtvol, pcrdil, cycles, pcrvol, dolig,pcrtgt=None): primerSet=[set(["REF","T7X",prefixIn[i]+"X",prefixOut[i]+"X"]+(["MX"] if self.useMX else [])) for i in range(len(prefixIn))] if self.extraQPCRPrimers is not None: primerSet=[set(list(p) + self.extraQPCRPrimers) for p in primerSet] print("primerSet=",primerSet) if keepCleaved: print("Starting new cleavage round, will add prefix: ",prefixOut) assert dolig else: print("Starting new uncleaved round, will retain prefix: ",prefixIn) print("stop=",stop,"prefixOut=",prefixOut,", prefixIn=",prefixIn,",t7vol=",round(t7vol,ndigits=2),",rtvol=",rtvol,",pcrdil=",pcrdil,",cycles=",cycles,",dolig=",dolig) if self.rtCarryForward: assert dolig names=[i.name for i in inputs] if self.rnaInput: rxs=inputs stopDil=1 else: print("######## T7 ########### %.0f min"%(clock.elapsed()/60)) db.pushStatus("T7") print("Inputs: (t7vol=%.2f)"%t7vol) for inp in inputs: if inp.conc.units=='nM': print(" %s: %.1ful@%.1f %s, use %.1f ul (%.3f pmoles)"%(inp.name,inp.volume,inp.conc.stock,inp.conc.units,t7vol/inp.conc.dilutionneeded(), t7vol*inp.conc.final/1000)) else: print(" %s: %.1ful@%.1f %s, use %.1f ul"%(inp.name,inp.volume,inp.conc.stock,inp.conc.units,t7vol/inp.conc.dilutionneeded())) # inp.conc.final=inp.conc.stock*self.templateDilution units=list(set([inp.conc.units for inp in inputs])) if len(units)>1: print("Inputs have inconsistent concentration units: ",units) assert False if units[0]=='nM': needDil = max([inp.conc.stock for inp in inputs]) * 1.0 / self.qConc else: needDil = 100 / self.qConc # Assume 100nM if self.directT7 and self.rndNum==1: # Just add ligands and MT7 to each well mconc=reagents.getsample("MT7").conc.dilutionneeded() for i in range(len(inputs)): watervol=t7vol*(1-1/mconc) - inputs[i].volume if watervol<-0.1: print("Negative amount of water (%.1f ul) needed for T7 setup"%watervol) assert False elif watervol>0.1: self.e.transfer(watervol, decklayout.WATER, inputs[i], mix=(False, False)) self.e.transfer(t7vol / mconc, reagents.getsample("MT7"), inputs[i], mix=(False, False)) assert(abs(inputs[i].volume - t7vol) < 0.1) # Add ligands last in case they crash out when they hit aqueous; this way, they'll be as dilute as possible if keepCleaved: for i in range(len(inputs)): if self.inputs[i]['negligand'] is not None: negligand=reagents.getsample(self.inputs[i]['negligand']) self.e.transfer(t7vol / negligand.conc.dilutionneeded(), negligand, inputs[i], mix=(False, False)) names[i]+="+" else: for i in range(len(inputs)): if self.inputs[i]['ligand'] is not None: ligand=reagents.getsample(self.inputs[i]['ligand']) self.e.transfer(t7vol / ligand.conc.dilutionneeded(), ligand, inputs[i], mix=(False, False)) names[i]+="+" rxs=inputs self.e.shakeSamples(inputs,returnPlate=True) elif self.rndNum==len(self.rounds) and self.finalPlus and keepCleaved: rxs = self.runT7Setup(ligands=[reagents.getsample(inp['ligand']) for inp in self.inputs],src=inputs, vol=t7vol, srcdil=[inp.conc.dilutionneeded() for inp in inputs]) for i in range(len(inputs)): inp=inputs[i] if self.inputs[i]['ligand'] is not None: rxs += self.runT7Setup(ligands=[reagents.getsample(self.inputs[i]['ligand'])],src=[inp],vol=t7vol,srcdil=[inp.conc.dilutionneeded()]) prefixIn+=[prefixIn[i]] prefixOut+=[prefixOut[i]] stop+=[stop[i]] primerSet+=[primerSet[i]] names+=["%s+"%names[i]] elif keepCleaved: rxs = self.runT7Setup(ligands=[reagents.getsample(inp['negligand']) for inp in self.inputs], src=inputs, vol=t7vol, srcdil=[inp.conc.dilutionneeded() for inp in inputs]) else: rxs = self.runT7Setup(ligands=[reagents.getsample(inp['ligand']) for inp in self.inputs], src=inputs, vol=t7vol, srcdil=[inp.conc.dilutionneeded() for inp in inputs]) if self.rndNum==1 and "template" in self.qpcrStages: # Initial input for i in range(len(rxs)): q.addSamples(src=rxs[i],needDil=needDil,primers=primerSet[i],names=["%s.T"%names[i]]) self.runT7Pgm(dur=self.t7dur,vol=t7vol) for i in range(len(rxs)): rxs[i].name="%s.t7"%names[i] self.e.lihahome() print("Estimate usable RNA concentration in T7 reaction at %.0f nM"%self.rnaConc) if self.rndNum==1: worklist.userprompt("T7 Incubation Started",120) self.e.waitpgm() # So elapsed time will be updated db.popStatus() if self.edtastop: print("######## Stop ########### %.0f min"%(clock.elapsed()/60)) db.pushStatus("Stop") print("Have %.1f ul before stop"%rxs[0].volume) preStopVolume=rxs[0].volume self.addEDTA(tgt=rxs,finalconc=2) # Stop to 2mM EDTA final db.popStatus("Stop") stopDil=rxs[0].volume/preStopVolume else: stopDil=1 if self.pauseAfterStop: worklist.userprompt("Post EDTA pause") if self.saveRNA: self.saveSamps(src=rxs,vol=self.saveRNAVolume,dil=self.saveRNADilution,plate=self.savePlate,dilutant=reagents.getsample("TE8"),mix=(False,False)) # Save to check [RNA] on Qubit, bioanalyzer needDil = self.rnaConc/self.qConc/stopDil if "stopped" in self.qpcrStages: for i in range(len(rxs)): q.addSamples(src=rxs[i:i+1],needDil=needDil,primers=primerSet[i],names=["%s.stopped"%names[i]]) print("######## RT Setup ########### %.0f min"%(clock.elapsed()/60)) db.pushStatus("RT") hiTemp=95 stop=["%s-Stop"%n for n in stop] rt=self.runRT(src=rxs,vol=rtvol,srcdil=self.rtDil,heatInactivate=self.rtHI,hiTemp=hiTemp,dur=self.rtdur,incTemp=50,stop=[reagents.getsample(s) for s in stop],stopConc=self.stopConc) # Heat inactivate also allows splint to fold rxs=rt for i in range(len(rxs)): if dolig and not self.singlePrefix: rxs[i].name=names[i]+"."+prefixOut[i]+".rt" else: rxs[i].name=names[i]+".rt" print("RT volume= [",",".join(["%.1f "%x.volume for x in rxs]),"]") needDil /=self.rtDil if self.rtpostdil[self.rndNum-1]>1: print("Dilution after RT: %.2f"%self.rtpostdil[self.rndNum-1]) self.diluteInPlace(tgt=rxs,dil=self.rtpostdil[self.rndNum-1]) needDil=needDil/self.rtpostdil[self.rndNum-1] # Discard extra volume of any sample that has more than current rt volume so that we can shake at high speed for r in Sample.getAllOnPlate(rxs[0].plate): if r not in rxs and r.volume>max(15+1.4,rxs[0].volume)+4: remove=r.volume-(15+1.4) oldvol=r.volume if r.lastMixed is None: r.lastMixed=clock.elapsed # Override since we don't care about mixing for disposal self.e.dispose(remove,r) print("Discarding some of %s to reduce volume from %.1f to %.1f to allow faster shaking"%(r.name,oldvol,r.volume)) print("RT volume= ",[x.volume for x in rxs]) self.e.shakeSamples(rxs) if self.rtSave: rtsv=self.saveSamps(src=rxs,vol=self.rtSaveVol,dil=self.rtSaveDil,plate=self.savePlate,dilutant=reagents.getsample("TE8"),mix=(False,False)) # Save to check RT product on gel (2x dil) if "rt" in self.qpcrStages: for i in range(len(rxs)): q.addSamples(src=rtsv[i:i+1],needDil=needDil/2,primers=self.rtprimers[self.rndNum-1] if hasattr(self,'rtprimers') else primerSet[i],names=["%s.rt"%names[i]]) else: if "rt" in self.qpcrStages: for i in range(len(rxs)): q.addSamples(src=rxs[i:i+1],needDil=needDil,primers=self.rtprimers[self.rndNum-1] if hasattr(self,'rtprimers') else primerSet[i],names=["%s.rt"%names[i]]) rtCarryForwardDil=10 rtCarryForwardVol=3.5 if self.rtCarryForward and not keepCleaved: # Also include RT from a prior round from here on for r in self.lastSaved: newsamp=Sample("%s.samp"%r.name,decklayout.SAMPLEPLATE) self.e.transfer(rxs[0].volume,r,newsamp,(False,False)) rxs.append(newsamp) db.popStatus() if dolig: print("######## Ligation setup ########### %.0f min"%(clock.elapsed()/60)) db.pushStatus("Ligation") extdil=5.0/4 reagents.getsample("MLigase").conc=Concentration(5) if self.ligInPlace: rxs=self.runLig(rxs,inPlace=True,srcdil=extdil,incTime=self.ligdur) else: rxs=self.runLig(rxs,inPlace=False,srcdil=extdil,vol=20,incTime=self.ligdur) print("Ligation volume= ",[x.volume for x in rxs]) needDil=needDil/extdil if self.extpostdil[self.rndNum-1]>1: print("Dilution after extension: %.2f"%self.extpostdil[self.rndNum-1]) self.diluteInPlace(tgt=rxs,dil=self.extpostdil[self.rndNum-1]) needDil=needDil/self.extpostdil[self.rndNum-1] pcrdil=pcrdil*1.0/self.extpostdil[self.rndNum-1] if self.saveDil is not None: ext=self.saveSamps(src=rxs,vol=3,dil=self.saveDil,dilutant=reagents.getsample("TE8"),tgt=[Sample("%s.ext"%n,self.savePlate) for n in names],mix=(False,True)) # Save cDNA product for subsequent NGS if "ext" in self.qpcrStages: for i in range(len(ext)): # Make sure we don't take more than 2 more steps maxdil=q.MAXDIL*q.MAXDIL if needDil/self.saveDil>maxdil: logging.notice( "Diluting ext by %.0fx instead of needed %.0f to save steps"%(maxdil,needDil/self.saveDil)) pset=primerSet[i] if "extraQPCR" in self.inputs[i]: pset.udpate(self.inputs[i]["extraQPCR"]) q.addSamples(src=[ext[i]],needDil=min(maxdil,needDil/self.saveDil),primers=pset,names=["%s.ext"%names[i]],save=False) else: if "ext" in self.qpcrStages: print("needDil=",needDil) for i in range(len(names)): pset=primerSet[i] if "extraQPCR" in self.inputs[i]: pset.update(self.inputs[i]["extraQPCR"]) q.addSamples(src=[rxs[i]],needDil=needDil,primers=pset,names=["%s.ext"%names[i]]) isave=i+len(names) if isave<len(rxs): # samples restored q.addSamples(src=[rxs[isave]],needDil=needDil/rtCarryForwardDil,primers=primerSet[isave]) db.popStatus() else: extdil=1 self.extpostdil[self.rndNum-1]=1 if self.rtpostdil[self.rndNum-1]>1: pcrdil=pcrdil*1.0/self.rtpostdil[self.rndNum-1] totalDil=stopDil*self.rtDil*self.rtpostdil[self.rndNum-1]*extdil*self.extpostdil[self.rndNum-1] fracRetained=rxs[0].volume/(t7vol*totalDil) print("Total dilution from T7 to Pre-pcr Product = %.2f*%.2f*%.2f*%.2f*%.2f = %.2f, fraction retained=%.0f%%"%(stopDil,self.rtDil,self.rtpostdil[self.rndNum-1],extdil,self.extpostdil[self.rndNum-1],totalDil,fracRetained*100)) if self.rtCarryForward and not keepCleaved: # Remove the extra samples assert(len(self.lastSaved)>0) rxs=rxs[:len(rxs)-len(self.lastSaved)] self.lastSaved=[] if len(rxs)>len(inputs): # Have extra samples due when self.finalPlus is True rxs= rxs[0:len(inputs)] # Only keep -target products prefixOut= prefixOut[0:len(inputs)] prefixIn= prefixIn[0:len(inputs)] stop= stop[0:len(inputs)] if self.dopcr and not (keepCleaved and self.noPCRCleave): print("######### PCR ############# %.0f min"%(clock.elapsed()/60)) db.pushStatus("PCR") print("PCR Volume: %.1f, Dilution: %.1f, volumes available for PCR: [%s]"%(pcrvol, pcrdil,",".join(["%.1f"%r.volume for r in rxs]))) initConc=needDil*self.qConc/pcrdil if keepCleaved: initConc=initConc*self.cleavage # Only use cleaved as input conc else: initConc=initConc*(1-self.cleavage) gain=pcrgain(initConc,400,cycles) finalConc=min(200,initConc*gain) print("Estimated starting concentration in PCR = %.1f nM, running %d cycles -> %.0f nM\n"%(needDil*self.qConc/pcrdil,cycles,finalConc)) nsplit=int(math.ceil(pcrvol*1.0/self.maxPCRVolume)) print("Split each PCR into %d reactions"%nsplit) sampNeeded=pcrvol/pcrdil if self.rtCarryForward and keepCleaved: sampNeeded+=rtCarryForwardVol if keepCleaved and self.rtCarryForward: print("Saving %.1f ul of each pre-PCR sample" % rtCarryForwardVol) self.lastSaved=[Sample("%s.sv"%x.name,self.savePlate) for x in rxs] for i in range(len(rxs)): # Save with rtCarryForwardDil dilution to reduce amount of RT consumed (will have Ct's 2-3 lower than others) self.e.transfer(rtCarryForwardVol,rxs[i],self.lastSaved[i],(False,False)) self.e.transfer(rtCarryForwardVol*(rtCarryForwardDil-1),decklayout.WATER,self.lastSaved[i],(False,True)) # Use pipette mixing -- shaker mixing will be too slow #print "NSplit=",nsplit,", PCR vol=",pcrvol/nsplit,", srcdil=",pcrdil,", input vol=",pcrvol/nsplit/pcrdil minvol=min([r.volume for r in rxs]) maxpcrvol=(minvol-15-1.4*nsplit)*pcrdil if maxpcrvol<pcrvol: print("Reducing PCR volume from %.1ful to %.1ful due to limited input"%(pcrvol, maxpcrvol)) pcrvol=maxpcrvol if keepCleaved: master="MTaqC" else: master="MTaqU" reagents.getsample(master) # Allocate for this before primers if self.barcoding: primers=self.bcprimers[self.rndNum-1] if primers is not None and nsplit>1: primers=primers*nsplit else: primers=None if primers is None: primers=[("T7%sX"%x).replace("T7T7","T7") for x in prefixOut]*nsplit rnddef = self.rnddef[self.rndNum-1] bcout=[] if 'barcode' in rnddef: # Add barcoding primers assert len(rnddef['barcode'])==len(rxs) dil=self.saveSamps(rxs,dil=50,vol=2,plate=decklayout.SAMPLEPLATE) for i in range(len(rxs)): dil[i].conc=Concentration(25,1) for bc in rnddef['barcode'][i]: tgt=Sample("%s.%s"%(rxs[i].name,bc),decklayout.SAMPLEPLATE) bparts=bc.split("/") for b in bparts: if not reagents.isReagent("P-%s"%b): reagents.add(name="P-%s"%b,conc=Concentration(2.67,0.4,'uM'),extraVol=30) print("PCR-%s"%bc) self.e.stage("PCR-%s"%bc,reagents=[reagents.getsample("MTaqBar"),reagents.getsample("P-%s"%bparts[0]),reagents.getsample("P-%s"%bparts[1])],samples=[tgt],sources=[dil[i] ],volume=50,destMix=False) bcout.append(tgt) print(tgt.name,"wellMixed=",tgt.wellMixed) print("Running PCR with master=",master,", primers=",primers) pcr=self.runPCR(src=rxs*nsplit,vol=pcrvol/nsplit,srcdil=pcrdil,ncycles=cycles,primers=primers,usertime=self.usertime if keepCleaved else None,fastCycling=False,inPlace=False,master=master,lowhi=self.lowhi,annealTemp=57) if keepCleaved and self.regenPCRCycles is not None: # Regenerate prefix pcr2=self.runPCR(src=pcr,vol=self.regenPCRVolume,srcdil=self.regenPCRDilution,ncycles=self.regenPCRCycles,primers=None,usertime=None,fastCycling=False,inPlace=False,master="MTaqR",lowhi=self.lowhi,annealTemp=55) # Add BT575p for 1 more cycle for p in pcr2: self.e.transfer(p.volume*0.5/10,reagents.getsample("Unclvd-Stop"),p,(False,False)) # One more cycle cycling=' TEMP@95,30 TEMP@55,30 TEMP@68,30 TEMP@25,2' thermocycler.setpgm('rfin',100,cycling) self.e.runpgm("rfin",5.0,False,max([p.volume for p in pcr2])) pcr=pcr2 # Use 2nd PCR as actual output if len(pcr)<=len(names): # Don't relabel if we've split for i in range(len(pcr)): pcr[i].name=names[i]+".pcr" #print "Volume remaining in PCR input source: [",",".join(["%.1f"%r.volume for r in rxs]),"]" needDil=finalConc/self.qConc print("Projected final concentration = %.0f nM"%(needDil*self.qConc)) for i in range(len(pcr)): pcr[i].conc=Concentration(stock=finalConc,final=None,units='nM') db.popStatus() if self.pcrSave: # Save samples at 1x (move all contents -- can ignore warnings) maxSaveVol=(100 if self.savedilplate else 1500)*1.0/nsplit if self.finalRound and nsplit==1 and self.savedilplate and pcrtgt is None: print("Skipping save of final PCR") sv=pcr else: residual=2.4 # Amount to leave behind to avoid aspirating air sv=self.saveSamps(src=pcr[:len(rxs)],vol=[min([maxSaveVol,x.volume-residual]) for x in pcr[:len(rxs)]],dil=1,plate=(self.savePlate if self.savedilplate else decklayout.EPPENDORFS),tgt=pcrtgt) if nsplit>1: # Combine split for i in range(len(rxs),len(rxs)*nsplit): self.e.transfer(min([maxSaveVol,pcr[i].volume-residual]),pcr[i],sv[i%len(sv)],mix=(False,False)) # Correct concentration (above would've assumed it was diluted) for i in range(len(sv)): sv[i].conc=pcr[i].conc # Shake self.e.shakeSamples(sv) if "pcr" in self.qpcrStages: for i in range(len(sv)): q.addSamples(sv[i],needDil,primers=primerSet[i],names=["%s.pcr"%names[i]]) print("Have %.2f pmoles of product (%.0f ul @ %.1f nM)"%(sv[0].volume*sv[0].conc.stock/1000,sv[0].volume,sv[0].conc.stock)) # Save barcoded products too if len(bcout)>0: print("bcout=",",".join(str(b) for b in bcout)) print("mixed=",bcout[0].isMixed(),", wellMixed=",bcout[0].wellMixed) bcsave=self.saveSamps(src=bcout,vol=[b.volume for b in bcout],dil=1,plate=self.savePlate,mix=(False,False)) if "bc" in self.qpcrStages: print("Doing qPCR of barcoding: ",bcsave) for i in range(len(bcsave)): needDil=640 q.addSamples(src=bcsave[i],needDil=needDil,primers=["T7X","WX","ZX"]+(["MX"] if self.useMX else []),save=False) else: bcsave=[] return sv, bcsave else: assert "pcr" not in self.qpcrStages ## Not implemented return pcr[:len(rxs)], bcout elif self.noPCRCleave: print("Dilution instead of PCR: %.2f"%self.nopcrdil) # Need to add enough t7prefix to compensate for all of the Stop primer currently present, regardless of whether it is for cleaved or uncleaved # Will result in some short transcripts corresponding to the stop primers that are not used for cleaved product, producing just GGG_W_GTCTGC in the next round. These would be reverse-trancribed, but may compete for T7 yield t7prefix=reagents.getsample("BT88") dil=self.extpostdil[self.rndNum-1] # FIXME: Is this correct? Used to have a 'userDil' term stopconc=1000.0/dil bt88conc=t7prefix.conc.stock relbt88=stopconc/bt88conc print("Using EXT with %.0fnM of stop oligo as input to next T7, need %.2ful of BT88@%.0fnM per ul of sample"%(stopconc,relbt88,bt88conc)) for r in rxs: vol=r.volume*relbt88 t7prefix.conc.final=t7prefix.conc.stock*vol/(r.volume+vol) r.conc.final=r.conc.stock*r.volume/(r.volume+vol) self.e.transfer(vol,t7prefix,r,mix=(False,False)) if self.nopcrdil>(1+relbt88): self.diluteInPlace(tgt=rxs,dil=self.nopcrdil/(1.0+relbt88)) #needDil=needDil/self.nopcrdil # needDil not used subsequently print("Dilution of EXT product: %.2fx * %.2fx = %2.fx\n"%(1+relbt88,self.nopcrdil/(1+relbt88),self.nopcrdil)) else: print("Dilution of EXT product: %.2fx\n"%(1+relbt88)) return rxs, [] else: return rxs, []
def oneround(self,q,input,prefixOut,prefixIn,keepCleaved,t7vol,rtvol,pcrdil,cycles,pcrvol,dolig): if keepCleaved: print "Starting new cleavage round, will add prefix: ",prefixOut assert(dolig) else: print "Starting new uncleaved round, will retain prefix: ",prefixIn if self.rtSave: assert(dolig) names=[i.name for i in input] print "######## T7 ###########" print "Inputs: (t7vol=%.2f)"%t7vol inconc=[inp.conc.final for inp in input] for inp in input: print " %s: %.1ful@%.1f nM, use %.1f ul (%.3f pmoles)"%(inp.name,inp.volume,inp.conc.stock,t7vol/inp.conc.dilutionneeded(), t7vol*inp.conc.final/1000) # inp.conc.final=inp.conc.stock*self.templateDilution needDil = max([inp.conc.stock for inp in input])*1.0/self.qConc if self.directT7 and self.rndNum==1: # Just add ligands and MT7 to each well for i in range(len(input)): ligand=reagents.getsample(self.inputs[i]['ligand']) self.e.transfer(t7vol/ligand.conc.dilutionneeded(),ligand,input[i],mix=(False,False)) mconc=reagents.getsample("MT7").conc.dilutionneeded() for i in range(len(input)): watervol=t7vol*(1-1/mconc)-input[i].volume if watervol>0.1: self.e.transfer(watervol,decklayout.WATER,input[i],mix=(False,False)) self.e.transfer(t7vol/mconc,reagents.getsample("MT7"),input[i],mix=(False,False)) assert(abs(input[i].volume-t7vol)<0.1) rxs=input elif self.rndNum==self.nrounds and self.finalPlus: rxs = self.runT7Setup(src=input,vol=t7vol,srcdil=[inp.conc.dilutionneeded() for inp in input]) rxs += self.runT7Setup(ligands=[reagents.getsample(inp['ligand']) for inp in self.inputs],src=input,vol=t7vol,srcdil=[inp.conc.dilutionneeded() for inp in input]) prefixIn+=prefixIn prefixOut+=prefixOut names+=["%s+"%n for n in names] elif keepCleaved: rxs = self.runT7Setup(src=input,vol=t7vol,srcdil=[inp.conc.dilutionneeded() for inp in input]) else: rxs = self.runT7Setup(ligands=[reagents.getsample(inp['ligand']) for inp in self.inputs],src=input,vol=t7vol,srcdil=[inp.conc.dilutionneeded() for inp in input]) for i in range(len(rxs)): rxs[i].name="%s.rx"%names[i] if self.rndNum==1 and "template" in self.qpcrStages: # Initial input for i in range(len(rxs)): q.addSamples(src=rxs[i],needDil=needDil,primers=["T7X","REF","T7"+prefixIn[i]+"X"],names=["%s.T-"%names[i]]) needDil = needDil*max([inp.conc.dilutionneeded() for inp in input]) t7dur=30 self.runT7Pgm(dur=t7dur,vol=t7vol) print "Estimate RNA concentration in T7 reaction at %.0f nM"%self.rnaConc self.rnaConc=min(40,inconc)*t7dur*65/30 print "######## Stop ###########" #self.saveSamps(src=rxs,vol=5,dil=10,plate=decklayout.EPPENDORFS,dilutant=reagents.getsample("TE8"),mix=(False,False)) # Save to check [RNA] on Qubit, bioanalyzer self.e.lihahome() print "Have %.1f ul before stop"%rxs[0].volume preStopVolume=rxs[0].volume self.addEDTA(tgt=rxs,finalconc=2) # Stop to 2mM EDTA final stop=["Unclvd-Stop" if (not dolig) else "A-Stop" if n=="A" else "B-Stop" if n=="B" else "W-Stop" if n=="W" else "BADPREFIX" for n in prefixOut] stopDil=rxs[0].volume/preStopVolume needDil = self.rnaConc/self.qConc/stopDil if "stopped" in self.qpcrStages: q.addSamples(src=rxs,needDil=needDil,primers=["T7AX","MX","T7X","REF"],names=["%s.stopped"%r.name for r in rxs]) print "######## RT Setup ###########" rtDil=4 hiTemp=95 rtDur=20 rxs=self.runRT(src=rxs,vol=rtvol,srcdil=rtDil,heatInactivate=True,hiTemp=hiTemp,dur=rtDur,incTemp=50,stop=[reagents.getsample(s) for s in stop]) # Heat inactivate also allows splint to fold print "RT volume= ",[x.volume for x in rxs] needDil /= rtDil if "rt" in self.qpcrStages: q.addSamples(src=rxs,needDil=needDil,primers=["T7AX","MX","REF"],names=["%s.rt"%r.name for r in rxs]) rtSaveDil=10 rtSaveVol=3.5 if self.rtSave and not keepCleaved: # Also include RT from a prior round from here on for r in self.lastSaved: newsamp=Sample("%s.samp"%r.name,decklayout.SAMPLEPLATE) self.e.transfer(rxs[0].volume,r,newsamp,(False,False)) rxs.append(newsamp) if dolig: print "######## Ligation setup ###########" extdil=5.0/4 reagents.getsample("MLigase").conc=Concentration(5) rxs=self.runLig(rxs,inPlace=True) print "Ligation volume= ",[x.volume for x in rxs] needDil=needDil/extdil extpostdil=2 if extpostdil>1: print "Dilution after extension: %.2f"%extpostdil self.diluteInPlace(tgt=rxs,dil=extpostdil) needDil=needDil/extpostdil if not self.doexo: pcrdil=pcrdil/extpostdil if self.saveDil is not None: ext=self.saveSamps(src=rxs,vol=3,dil=self.saveDil,dilutant=reagents.getsample("TE8"),tgt=[Sample("%s.ext"%n,decklayout.DILPLATE) for n in names],mix=(False,True)) # Save cDNA product for subsequent NGS if "ext" in self.qpcrStages: for i in range(len(ext)): # Make sure we don't take more than 2 more steps maxdil=q.MAXDIL*q.MAXDIL if needDil/self.saveDil>maxdil: logging.notice( "Diluting ext by %.0fx instead of needed %.0f to save steps"%(maxdil,needDil/self.saveDil)) q.addSamples(src=[ext[i]],needDil=min(maxdil,needDil/self.saveDil),primers=["T7"+prefixIn[i]+"X","T7"+prefixOut[i]+"X","MX","T7X","REF"],names=["%s.ext"%names[i]],save=False) else: if "ext" in self.qpcrStages: for i in range(len(input)): q.addSamples(src=[rxs[i]],needDil=needDil,primers=["T7"+prefixIn[i]+"X","T7"+prefixOut[i]+"X","MX","T7X","REF"],names=["%s.ext"%names[i]]) isave=i+len(input) if isave<len(rxs): # samples restored q.addSamples(src=[rxs[isave]],needDil=needDil/rtSaveDil,primers=["T7"+rxs[isave].name[0]+"X","T7"+("B" if rxs[isave].name[0]=="A" else "W" if rxs[isave].name[0]=="B" else "A")+"X","MX","T7X","REF"]) if self.doexo: print "######## Exo ###########" prevvol=rxs[0].volume rxs=self.runExo(rxs,incTime=30,inPlace=True) exoDil=rxs[0].volume/prevvol needDil/=exoDil needDil/=7 # Anecdotal based on Ct's -- large components (MX) reduced by exo digestion if "exo" in self.qpcrStages: q.addSamples(src=[rxs[i] for i in self.trackIndices],needDil=needDil,primers=["T7AX","T7BX","MX","T7X","REF"],names=["%s.exo"%names[i] for i in self.trackIndices]) exo=self.saveSamps(src=rxs,vol=10*exoDil,dil=2/exoDil,dilutant=reagents.getsample("TE8"),tgt=[Sample("%s.exo"%n,decklayout.SAMPLEPLATE) for n in names]) # Save cDNA product else: exoDil=1 exo=[] else: extdil=1 extpostdil=1 exoDil=1 if self.doampure: print "######## Ampure Cleanup ###########" ratio=1.5 elutionVol=30 cleanIn=ext+exo+user needDil=needDil*cleanIn[0].volume/elutionVol clean=self.runAmpure(src=cleanIn,ratio=ratio,elutionVol=elutionVol) if "ampure" in self.qpcrStages: q.addSamples(src=[clean[i] for i in self.trackIndices],needDil=needDil,primers=["T7AX","MX","T7X","REF"]) rxs=rxs+clean # Use the cleaned products for PCR totalDil=stopDil*rtDil*extdil*extpostdil*exoDil fracRetained=rxs[0].volume/(t7vol*totalDil) print "Total dilution from T7 to Pre-pcr Product = %.2f*%.2f*%.2f*%.2f*%.2f = %.2f, fraction retained=%.0f%%"%(stopDil,rtDil,extdil,extpostdil,exoDil,totalDil,fracRetained*100) if self.rtSave and not keepCleaved: # Remove the extra samples assert(len(self.lastSaved)>0) rxs=rxs[:len(rxs)-len(self.lastSaved)] self.lastSaved=[] if len(rxs)>len(input): rxs=rxs[0:len(input)] # Only keep -target products prefixOut=prefixOut[0:len(input)] prefixIn=prefixIn[0:len(input)] if self.dopcr: print "######### PCR #############" print "PCR Volume: %.1f, Dilution: %.1f, volumes available for PCR: [%s]"%(pcrvol, pcrdil,",".join(["%.1f"%r.volume for r in rxs])) maxSampleVolume=100 # Maximum sample volume of each PCR reaction (thermocycler limit, and mixing limit) initConc=needDil*self.qConc/pcrdil if keepCleaved: if self.doexo: initConc=initConc*7*self.cleavage # Back out 7x dilution in exo step, but only use cleaved as input conc else: initConc=initConc*self.cleavage # Only use cleaved as input conc else: initConc=initConc*(1-self.cleavage) gain=pcrgain(initConc,400,cycles) finalConc=initConc*gain print "Estimated starting concentration in PCR = %.1f nM, running %d cycles -> %.0f nM\n"%(needDil*self.qConc,cycles,finalConc) nsplit=int(math.ceil(pcrvol*1.0/maxSampleVolume)) print "Split each PCR into %d reactions"%nsplit srcdil=(1-1.0/3-1.0/4) sampNeeded=pcrvol/pcrdil if self.rtSave and keepCleaved: sampNeeded+=rtSaveVol maxvol=max([r.volume for r in rxs]); minvol=min([r.volume for r in rxs]); predil=min(75/maxvol,(40+1.4*nsplit)/(minvol-sampNeeded)) # Dilute to have 40ul left -- keeps enough sample to allow good mixing if keepCleaved and self.rtSave and predil>rtSaveDil: print "Reducing predil from %.1f to %.1f (rtSaveDil)"%(predil, rtSaveDil) predil=rtSaveDil if predil>1: self.diluteInPlace(rxs,predil) self.e.shakeSamples(rxs) print "Pre-diluting by %.1fx into [%s] ul"%(predil,",".join(["%.1f"%r.volume for r in rxs])) if keepCleaved and self.rtSave: assert(len(rxs)==len(rtSave)) print "Saving %.1f ul of each pre-PCR sample (@%.1f*%.1f dilution)"%(rtSaveVol ,predil, rtSaveDil/predil) self.lastSaved=[Sample("%s.sv"%x.name,decklayout.DILPLATE) for x in rxs] for i in range(len(rxs)): # Save with rtSaveDil dilution to reduce amount of RT consumed (will have Ct's 2-3 lower than others) self.e.transfer(rtSaveVol*predil,rxs[i],self.lastSaved[i],(False,False)) self.e.transfer(rtSaveVol*(rtSaveDil/predil-1),decklayout.WATER,self.lastSaved[i],(False,True)) # Use pipette mixing -- shaker mixing will be too slow pcr=self.runPCR(src=rxs*nsplit,vol=pcrvol/nsplit,srcdil=pcrdil*1.0/predil,ncycles=cycles,primers=["T7%sX"%x for x in (prefixOut if keepCleaved else prefixIn)]*nsplit,usertime=self.usertime if keepCleaved else None,fastCycling=True,inPlace=False) needDil=finalConc/self.qConc print "Projected final concentration = %.0f nM"%(needDil*self.qConc) for i in range(len(pcr)): pcr[i].conc=Concentration(stock=finalConc,final=None,units='nM') if self.pcrSave: # Save samples at 1x (move all contents -- can ignore warnings) if self.savedilplate: sv=self.saveSamps(src=pcr[:len(rxs)],vol=[x.volume for x in pcr[:len(rxs)]],dil=1,plate=decklayout.DILPLATE,atEnd=True) else: sv=self.saveSamps(src=pcr[:len(rxs)],vol=[x.volume for x in pcr[:len(rxs)]],dil=1,plate=decklayout.EPPENDORFS) if nsplit>1: # Combine split for i in range(len(rxs),len(rxs)*nsplit): self.e.transfer(pcr[i].volume-16.4,pcr[i],sv[i%len(sv)],mix=(False,i>=len(rxs)*(nsplit-1))) # Correct concentration (above would've assumed it was diluted) for i in range(len(sv)): sv[i].conc=pcr[i].conc if "pcr" in self.qpcrStages: for i in range(len(pcr)): q.addSamples(pcr,needDil,["T7%sX"%prefixOut[i]]) processEff=0.5 # Estimate of overall efficiency of process print "Saved %.2f pmoles of product (%.0f ul @ %.1f nM)"%(sv[0].volume*sv[0].conc.stock/1000,sv[0].volume,sv[0].conc.stock) return sv else: return pcr[:len(rxs)] else: return rxs
def parselog(filename: str, outfile:str=None, follow=False): global logdb, lnum if filename is not None: fd=open(filename,'rb') else: fd=sys.stdin if outfile is not None: outfd=codecs.open(outfile,'w','latin-1') else: outfd=sys.stdout csum=fd.readline() hdr=fd.readline() version=fd.readline() prevcode='?' prevtime='?' send={} error=False lastgeminicmd=None geminicmdtimes={} geminicmdcnt={} tipcmd="" lasttime=datetime.datetime.strptime(hdr[:15].decode('latin-1'),'%Y%m%d_%H%M%S') print("Header time: %s"%str(lasttime),file=outfd) shakePlate=None # Plate on shaker logdb=LogDB(filename) # Handle high-bit characters in stdout (since .log contains 0xb5 (\micro) charactures sleeping=False while True: bline=fd.readline() if not bline: if follow: if not sleeping: logdb.flush() print("sleeping at line %d of %s..."%(lnum,filename),end='',flush=True) sleeping=True time.sleep(10) continue else: break if sleeping: print("done") sleeping=False while len(bline)>0 and (bline[-1]==13 or bline[-1]==10): bline=bline[:-1] #line=line.rstrip('\r\n') if len(bline)==0: continue line=bline.decode('latin-1') code=line[0] gtime=line[2:10] cmd=line[11:] if debug: print("\tcode=%s(%d),time=%s,cmd=%s"%(code,ord(code[0]),gtime,cmd),file=outfd) if code[0]==' ': if prevcode[0]!='D': # print "Copying previous code: %c"%prevcode code=prevcode else: print("Blank code, previous=D, assuming new one is F",file=outfd) code='F' if len(gtime)<1 or gtime[0]==' ': gtime=prevtime prevcode=code prevtime=gtime if code[0]=='F': spcmd=cmd[1:].split(',') dev=spcmd[0][1:] spcmd=spcmd[1:] if len(cmd)<1: print("Empty cmd",file=outfd) elif cmd[0]=='>': if dev in send: print("Double cmd to %s: %s AND %s"%(dev,send[dev],str(spcmd)),file=outfd) send[dev]=[spcmd[0][0:3],spcmd[0][3:]]+spcmd[1:] elif cmd[0]=='-' or cmd[0]=='*': if dev not in send: print("Missing cmd when received reply from %s: %s"%(dev,str(spcmd)),file=outfd) continue error=cmd[0]=='*' fwparse(dev,send[dev],spcmd,error,lasttime,outfd) send.pop(dev) else: print("Bad cmd: %s"%cmd) else: if cmd.find('detected_volume_')==-1 or cmd.find('= -1')==-1: print("Gemini %s %s"%(gtime,cmd),file=outfd) if cmd[0:3]=='tip': tipcmd=cmd else: if len(tipcmd)>0 and cmd[0:3]==' ': gemtip(tipcmd,cmd,outfd) tipcmd="" if cmd.find("setShakeTargetSpeed")!=-1: pos=cmd.find("setShakeTargetSpeed") speed=int(cmd[pos+19:]) print("SPEED %d"%speed,file=outfd) dl.logspeed(shakePlate,speed) if cmd.startswith("moveplate") and cmd.find("Shaker")!=-1: pos=cmd.find("Shaker") shakePlate=cmd[10:pos-1] print("SHAKEPLATE %s"%shakePlate,file=outfd) if cmd.startswith("Starting program"): logdb.logfileStart(cmd,lasttime) if cmd.startswith('Line'): colon=cmd.find(':') cname=cmd[(colon+2):] lnum=int(cmd[4:(colon-1)]) logdb.setline(lnum) #print "cname=",cname t=datetime.datetime.combine(lasttime.date(),datetime.datetime.strptime(gtime,'%H:%M:%S').time()) if (t-lasttime).total_seconds()<0: t=t+datetime.timedelta(1) # Wrapped around logging.notice("Gemini time wrapped from %s to %s"%(lasttime,t)) if lastgeminicmd is not None: if (t-lasttime).total_seconds() > 30: print("Skipping long pause of %s for %s"%(str(t-lasttime),lastgeminicmd),file=outfd) elif t<lasttime: assert False # Shouldn't happen print("Skipping negative elapsed time of %d seconds for %s"%((t-lasttime).total_seconds(),lastgeminicmd),file=outfd) elif lastgeminicmd in list(geminicmdtimes.keys()): geminicmdtimes[lastgeminicmd]+=(t-lasttime).total_seconds() geminicmdcnt[lastgeminicmd]+=1 else: geminicmdtimes[lastgeminicmd]=(t-lasttime).total_seconds() geminicmdcnt[lastgeminicmd]=1 lastgeminicmd=cname lasttime=t if cmd.startswith('@log_'): print("PYTHON: %s" % cmd[1:],file=outfd) eval("logdb." + cmd[1:]) if cmd.find('closing log-file') != -1: # End of log (in case we're in -f mode) print("Found closing log-file message; exiting") logdb.logfileDone(lnum,lasttime) break logdb.flush() #print "log=",dl dl.printallsamples(fd=outfd) # This 'sys.stdout' (modified above) seems different from the default one that Samples.print* would use for cmd in list(geminicmdtimes.keys()): print("%s: %.0f seconds for %.0f occurrences: %.2f second/call"%(cmd,geminicmdtimes[cmd],geminicmdcnt[cmd], geminicmdtimes[cmd]*1.0/geminicmdcnt[cmd]),file=outfd)