def pgm(self): self.q = QSetup(self, maxdil=16, debug=False, mindilvol=60) # Don't start idler (to minimize tip cross-contamination); last PCR allows plenty of time for doing dilutions without any effect on run time # Will start after first constriction PCR is running #self.q.debug = True # self.e.addIdleProgram(self.q.idler) self.q.addReferences(dstep=10, primers=self.qprimers, ref=reagents.getsample("BT5310"),nreplicates=2) samps=[r.getsample() for r in self.rsrc] for s in samps: self.q.addSamples([s],needDil=max(10,s.conc.stock*1e-9/self.qconc),primers=self.qprimers) print("### Mixdown #### (%.0f min)" % (clock.elapsed() / 60.0)) if len(samps)>1: mixdown = self.mix(samps, [x['weight'] for x in self.inputs]) else: mixdown=samps[0] self.q.addSamples(mixdown, needDil=max(1.0,mixdown.conc.stock * 1e-9 / self.qconc), primers=self.qprimers) print("Mixdown final concentration = %.0f pM" % (mixdown.conc.stock * 1000)) print("### Constriction #### (%.1f min)" % (clock.elapsed() / 60.0)) constricted = self.constrict(mixdown, mixdown.conc.stock * 1e-9) print("### Regeneration #### (%.0f min)" % (clock.elapsed() / 60.0)) prefixes = set([x['left'][0] for x in self.inputs]) self.regenerate(constricted * len(prefixes), [p for p in prefixes for _ in constricted]) print("### qPCR #### (%.0f min)" % (clock.elapsed() / 60.0)) self.q.run(confirm=False, enzName='EvaGreen', waitForPTC=True) print("### qPCR Done #### (%.0f min)" % (clock.elapsed() / 60.0)) worklist.userprompt("qPCR done -- only need to complete final PCR", 300) self.e.waitpgm() print("### Final PCR Done #### (%.0f min)" % (clock.elapsed() / 60.0))
def pgm(self): self.q = QSetup(self, maxdil=16, debug=False, mindilvol=60) self.q.debug = True self.q.addReferences(dstep=10, primers=self.qprimers, ref=reagents.getsample("BT5310"),nreplicates=2) print("### Barcoding #### (%.0f min)" % (clock.elapsed() / 60.0)) self.idbarcoding(self.rsrc, left=[x['left'] for x in self.inputs], right=[x['right'] for x in self.inputs]) print("### qPCR #### (%.0f min)" % (clock.elapsed() / 60.0)) self.q.run(confirm=False, enzName='EvaGreen')
def pgm(self): q = QSetup(self,maxdil=16,debug=False,mindilvol=60) self.e.addIdleProgram(q.idler) t7in = [s.getsample() for s in self.srcs] if "negative" in self.qpcrStages: qpcrPrimers=["REF","MX","T7X","T7AX","T7BX","T7WX"] q.addSamples(decklayout.SSDDIL,1,qpcrPrimers,save=False) # Negative controls # Save RT product from first (uncleaved) round and then use it during 2nd (cleaved) round for ligation and qPCR measurements self.rndNum=0 self.nextID=self.firstID curPrefix=[inp['prefix'] for inp in self.inputs] while self.rndNum<self.nrounds: self.rndNum=self.rndNum+1 prefixOut=["A" if p=="W" else "B" if p=="A" else "W" if p=="B" else "BADPREFIX" for p in curPrefix] if self.rndNum==1: self.t7vol1=self.t7vol1a else: self.t7vol1=max(20,self.pmolesIn*1000/min([inp.conc.final for inp in t7in])) # New input volueme r1=self.oneround(q,t7in,prefixOut,prefixIn=curPrefix,keepCleaved=False,rtvol=self.rtvol1,t7vol=self.t7vol1,cycles=self.pcrcycles1,pcrdil=self.pcrdil1,pcrvol=self.pcrvol1,dolig=self.allLig) # pcrvol is set to have same diversity as input for i in range(len(r1)): r1[i].name="%s_%d_R%dU_%s"%(curPrefix[i],self.nextID,self.inputs[i]['round']+self.rndNum,self.inputs[i]['ligand']) self.nextID+=1 r1[i].conc.final=r1[i].conc.stock*self.templateDilution if self.rndNum>=self.nrounds: logging.warning("Warning: ending on an uncleaved round") break self.rndNum=self.rndNum+1 print "prefixIn=",curPrefix print "prefixOut=",prefixOut self.t7vol2=max(20,self.pmolesIn*1000/min([inp.conc.final for inp in r1])) r2=self.oneround(q,r1,prefixOut,prefixIn=curPrefix,keepCleaved=True,rtvol=self.rtvol2,t7vol=self.t7vol2,cycles=self.pcrcycles2,pcrdil=self.pcrdil2,pcrvol=self.pcrvol2,dolig=True) # pcrvol is set to have same diversity as input = (self.t7vol2*self.templateDilution/rnagain*stopdil*rtdil*extdil*exodil*pcrdil) for i in range(len(self.inputs)): r2[i].name="%s_%d_R%dC_%s"%(prefixOut[i],self.nextID,self.inputs[i]['round']+self.rndNum,self.inputs[i]['ligand']) self.nextID+=1 r2[i].conc.final=r2[i].conc.stock*self.templateDilution t7in=r2 curPrefix=prefixOut if "finalpcr" in self.qpcrStages: for i in range(len(r2)): q.addSamples(src=r2[i],needDil=r2[i].conc.stock/self.qConc,primers=["T7X","T7"+prefixOut[i]+"X"]) print "######### qPCR ###########" #q.addReferences(mindil=4,nsteps=6,primers=["T7X","MX","T7AX"]) if self.qpcrWait: worklist.userprompt('Continue to setup qPCR') q.run()
def pgm(self): self.q = QSetup(self, maxdil=16, debug=False, mindilvol=100) self.e.addIdleProgram(self.q.idler) if self.doqpcr: self.q.addReferences(dstep=10, primers=self.qprimers, ref=reagents.getsample("BT5310")) print("### Barcoding #### (%.0f min)" % (clock.elapsed() / 60.0)) bcout = self.barcoding(names=[x['name'] for x in self.inputs], left=[x['left'] for x in self.inputs], right=[x['right'] for x in self.inputs]) for i in range(len(self.inputs)): x = self.inputs[i] if 'bconc' in x and x['bconc'] is not None: print("Resetting concentration of %s from expected %.1f to bconc setting of %.1f nM" % \ (x['name'], bcout[i].conc.stock, x['bconc'])) bcout[i].conc.stock = x['bconc'] print("### qPCR #### (%.0f min)" % (clock.elapsed() / 60.0)) self.q.run(confirm=False, enzName='EvaGreen') print("### qPCR Done #### (%.0f min)" % (clock.elapsed() / 60.0)) print("### Final PCR Done #### (%.0f min)" % (clock.elapsed() / 60.0)) worklist.flushQueue() if all(['bconc' in x and x['bconc'] is not None for x in self.inputs]): print("### Mixdown #### (%.0f min)" % (clock.elapsed() / 60.0)) worklist.flushQueue() worklist.comment('Start mixdown only at this point') self.e.sanitize(force=True) # mixdown = self.mix(bcout, [x['weight'] for x in self.inputs]) # Set default mix number to 1 for x in self.inputs: if 'mix' not in x: x['mix']=1 mixes=set([x['mix'] for x in self.inputs]) for m in mixes: sel=[ i for i in range(len(self.inputs)) if self.inputs[i]['mix']==m] print("sel=",sel) mixdown = self.mix([bcout[i] for i in sel], [self.inputs[i]['weight'] for i in sel],prefix="Mix%d_"%m) mixdown.name="Mix%d_Final"%m # self.q.addSamples(mixdown, needDil=mixdown.conc.stock * 1e-9 / self.qconc, primers=self.qprimers,nreplicates=3) else: print("### Not doing mixdown as bconc not set in all inputs")
def pgm(self): q = QSetup(self, maxdil=16, debug=False, mindilvol=60) self.e.addIdleProgram(q.idler) input = [s.getsample() for s in self.srcs] # Save RT product from first (uncleaved) round and then use it during 2nd (cleaved) round for ligation and qPCR measurements prefixIn = [inp['prefix'] for inp in self.inputs] prefixOut = [ "A" if p == "W" else "B" if p == "A" else "W" if p == "B" else "BADPREFIX" for p in prefixIn ] qpcrPrimers = ["REF", "MX", "T7X"] if "W" in prefixIn + prefixOut: qpcrPrimers += ["T7WX"] if "A" in prefixIn + prefixOut: qpcrPrimers += ["T7AX"] if "B" in prefixIn + prefixOut: qpcrPrimers += ["T7BX"] q.addSamples(decklayout.SSDDIL, 1, qpcrPrimers, save=False) # Negative controls print "Starting new cleavage round, will add prefix: ", prefixOut names = [i.name for i in input] print "######## T7 ###########" print "Inputs: (t7vol=%.2f)" % self.t7vol for inp in input: print " %s: %.1ful@%.1f nM, use %.1f ul (%.3f pmoles)" % ( inp.name, inp.volume, inp.conc.stock, self.t7vol / inp.conc.dilutionneeded(), self.t7vol * inp.conc.final / 1000) print "input[0]=", input[0] needDil = max([inp.conc.final for inp in input]) * 1.0 / self.qConc if self.directT7: # Just add MT7 and possibly water to each well mconc = reagents.getsample("MT7").conc.dilutionneeded() for i in range(len(input)): watervol = self.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(self.t7vol / mconc, reagents.getsample("MT7"), input[i], mix=(False, False)) assert (input[i].volume == self.t7vol) rxs = input else: rxs = self.runT7Setup( src=input, vol=self.t7vol, srcdil=[inp.conc.dilutionneeded() for inp in input]) print "input[0]=", input[0] #for i in range(len(rxs)): # q.addSamples(src=rxs],needDil=needDil,primers=["T7"+prefixIn[i]+"X","MX","T7X","REF"],names=["%s.T-"%names[i]]) self.runT7Pgm(dur=self.t7dur, vol=self.t7vol) print "Template conc=%.1f nM, estimated RNA concentration in T7 reaction at %.0f nM" % ( self.tmplFinalConc, self.rnaConc) print "######## Stop ###########" 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=2, plate=decklayout.DILPLATE, dilutant=reagents.getsample("TE8"), mix=(False, False)) # Save to check [RNA] on Qubit, bioanalyzer stop = [ "A-Stop" if n == "A" else "B-Stop" if n == "B" else "W-Stop" if n == "W" else "BADPREFIX" for n in prefixOut ] for i in range(len(rxs)): rxs[i].name = rxs[i].name + "." + stop[i] needDil = self.rnaConc / self.qConc / stopDil #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 rxin = rxs rxs = self.runRT(src=rxs, vol=self.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] for r in rxin: if r.volume > 20: print "Have more T7 reaction remaining than needed: %s has %.1f ul" % ( r.name, r.volume) needDil /= rtDil rtPostDil = 5 if rtPostDil != 1: self.diluteInPlace(tgt=rxs, dil=rtPostDil) needDil /= rtPostDil #q.addSamples(src=rxs,needDil=needDil,primers=["T7AX","MX","REF"],names=["%s.rt"%r.name for r in rxs]) print "######## Ligation setup ###########" extdil = 5.0 / 4 reagents.getsample("MLigase").conc = Concentration(5) extvol = 20 print "Extension volume=", extvol rxs = self.runLig(rxs, vol=extvol) print "Ligation volume= ", [x.volume for x in rxs] needDil = needDil / extdil extpostdil = 4 if extpostdil > 1: print "Dilution after extension: %.2f" % extpostdil self.diluteInPlace(tgt=rxs, dil=extpostdil) needDil = needDil / extpostdil if not self.doexo: self.pcrdil = self.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 for i in range(len(rxs)): q.addSamples(src=[ext[i]], needDil=needDil / self.saveDil, primers=[ "T7" + prefixIn[i] + "X", "T7" + prefixOut[i] + "X", "MX", "T7X", "REF" ], names=["%s.ext" % names[i]]) else: for i in range(len(rxs)): q.addSamples(src=[rxs[i]], needDil=needDil, primers=[ "T7" + prefixIn[i] + "X", "T7" + prefixOut[i] + "X", "MX", "T7X", "REF" ], names=["%s.ext" % names[i]]) 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 q.addSamples(src=rxs, needDil=needDil, primers=["T7AX", "T7BX", "MX", "T7X", "REF"], names=["%s.exo" % names[i] for i in range(len(rxs))]) #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 = [] 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) q.addSamples(src=[clean[i] for i in range(len(rxs))], needDil=needDil, primers=["T7AX", "MX", "T7X", "REF"]) rxs = rxs + clean # Use the cleaned products for PCR totalDil = stopDil * rtDil * rtPostDil * extdil * extpostdil * exoDil fracRetained = rxs[0].volume / (self.t7vol * totalDil) print "Total dilution from T7 to Pre-pcr Product = %.2f*%.2f*%.2f*%.2f*%.2f*%.2f = %.2f, fraction retained=%.0f%%" % ( stopDil, rtDil, rtPostDil, extdil, extpostdil, exoDil, totalDil, fracRetained * 100) if self.dopcr: print "######### PCR #############" print "PCR Volume: %.1f, Dilution: %.1f, volumes available for PCR: [%s]" % ( self.pcrvol, self.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 / self.pcrdil 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 gain = pcrgain(initConc, 400, self.pcrcycles) finalConc = initConc * gain print "Estimated starting concentration in PCR = %.1f nM, running %d cycles -> %.0f nM\n" % ( needDil * self.qConc, self.pcrcycles, finalConc) pcr = self.runPCR(src=rxs, vol=self.pcrvol, srcdil=self.pcrdil, ncycles=self.pcrcycles, primers=["T7%sX" % x for x in prefixOut], usertime=self.usertime, fastCycling=True) needDil = finalConc / self.qConc pcrpostdil = 2 if pcrpostdil > 1: self.diluteInPlace(pcr, pcrpostdil) needDil = needDil / pcrpostdil print "Projected final concentration = %.0f nM (after %.1fx dilution)" % ( needDil * self.qConc, pcrpostdil) for i in range(len(pcr)): pcr[i].conc = Concentration(stock=finalConc / pcrpostdil, final=None, units='nM') if self.pcrSave: # Save samples at 1x if self.savedilplate: sv = self.saveSamps( src=pcr[:len(rxs)], vol=[x.volume - 16.4 for x in pcr[:len(rxs)]], dil=1, plate=decklayout.DILPLATE) else: sv = self.saveSamps( src=pcr[:len(rxs)], vol=[x.volume - 16.4 for x in pcr[:len(rxs)]], dil=1, plate=decklayout.EPPENDORFS) # 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) print "######### qPCR ###########" #q.addReferences(mindil=4,nsteps=6,primers=["T7X","MX","T7AX"]) q.run(confirm=False)
def pgm(self): q = QSetup(self, maxdil=self.maxdilstep, debug=False, mindilvol=60) self.e.addIdleProgram(q.idler) if self.barcoding: # Setup barcode primers for cleaved rounds only self.bcprimers = [[ "BC-%s-R%d_T7" % (inp['ligand'], r + 1) for inp in self.inputs ] if self.rounds[r] == 'C' else None for r in range(len(self.rounds))] for bcp in self.bcprimers: if bcp is not None: for p in ["P-%s" % pp for pp in bcp]: if not reagents.isReagent(p): reagents.add(name=p, conc=4, extraVol=30, plate=decklayout.REAGENTPLATE, well="B2") s = reagents.getsample(p) # Force allocation of a well print "Adding %s to reagents at well %s" % ( p, s.plate.wellname(s.well)) print "BC primers=", self.bcprimers # Add any missing fields to inputs for i in range(len(self.inputs)): if 'ligand' not in self.inputs[i]: self.inputs[i]['ligand'] = None if 'round' not in self.inputs[i]: self.inputs[i]['round'] = None if 'name' not in self.inputs[i]: if self.inputs[i]['ligand'] is None: self.inputs[i]['name'] = '%s_%d_R%d' % ( self.inputs[i]['prefix'], self.inputs[i]['ID'], self.inputs[i]['round']) else: self.inputs[i]['name'] = '%s_%d_R%d_%s' % ( self.inputs[i]['prefix'], self.inputs[i]['ID'], self.inputs[i]['round'], self.inputs[i]['ligand']) # Add templates if self.directT7: self.srcs = self.addTemplates( [inp['name'] for inp in self.inputs], stockconc=self.tmplFinalConc / self.templateDilution, finalconc=self.tmplFinalConc, plate=decklayout.SAMPLEPLATE, looplengths=[inp['looplength'] for inp in self.inputs], initVol=self.t7vol[0] * self.templateDilution, extraVol=0) else: self.srcs = self.addTemplates( [inp['name'] for inp in self.inputs], stockconc=self.tmplFinalConc / self.templateDilution, finalconc=self.tmplFinalConc, plate=decklayout.DILPLATE, looplengths=[inp['looplength'] for inp in self.inputs], extraVol=15) t7in = [s.getsample() for s in self.srcs] if "negative" in self.qpcrStages: q.addSamples(decklayout.SSDDIL, 1, self.allprimers, save=False) # Negative controls if "reference" in self.qpcrStages: q.addReferences(dstep=10, nsteps=5, primers=["T7WX", "MX", "T7X"], ref=reagents.getsample("BT5310"), nreplicates=1) q.addReferences(dstep=10, nsteps=5, primers=["T7WX", "MX", "T7X"], ref=reagents.getsample("BT5310"), nreplicates=1) # Save RT product from first (uncleaved) round and then use it during 2nd (cleaved) round for ligation and qPCR measurements self.rndNum = 0 self.nextID = self.firstID curPrefix = [inp['prefix'] for inp in self.inputs] r1 = t7in for roundType in self.rounds: # Run a single round of roundType with r1 as input # roundType is either "U" for uncleaved, or a new prefix for a cleaved round (with "T" being a T7 prepend) # Set r1 to new output at end # Computed output prefix if roundType == 'U': prefixOut = curPrefix stop = ["Unclvd" for p in curPrefix] else: if roundType == 'T': stop = ['T7%s' % p for p in curPrefix] prefixOut = curPrefix elif any([p == roundType for p in curPrefix]): logging.error( "Round %d is a cleaved round but goes to %s without changing prefix" % (self.rndNum, roundType)) assert (False) else: prefixOut = [roundType for p in curPrefix] stop = prefixOut # May be explicitly overridden for i in range(len(self.inputs)): if 'stop' in self.inputs[i]: if isinstance(self.inputs[i]['stop'], list): assert (len(self.inputs[i]['stop']) == len( self.rounds)) t = self.inputs[i]['stop'][self.rndNum] else: t = self.inputs[i]['stop'] if (roundType == 'U') != (t == 'U'): print "Attempt to override round %d (type %s) with a input-specific round type of %s" % ( self.rndNum, roundType, t) assert (False) if roundType != 'U': if t == 'T': stop[i] = 'T7%s' % curPrefix[i] prefixOut[i] = curPrefix[i] else: stop[i] = t prefixOut[i] = t self.rndNum = self.rndNum + 1 self.finalRound = self.rndNum == len(self.rounds) r1 = self.oneround(q, r1, prefixOut=prefixOut, stop=stop, prefixIn=curPrefix, keepCleaved=(roundType != 'U'), rtvol=self.rtvol[self.rndNum - 1], t7vol=self.t7vol[self.rndNum - 1], cycles=self.pcrcycles[self.rndNum - 1], pcrdil=self.pcrdil[self.rndNum - 1], pcrvol=self.pcrvol[self.rndNum - 1], dolig=self.allLig or (roundType != 'U')) for i in range(len(r1)): r1[i].name = "%s_%d" % (prefixOut[i], self.nextID) if self.inputs[i]['round'] is not None: r1[i].name = "%s_R%d%c" % (r1[i].name, self.inputs[i]['round'] + self.rndNum, roundType) if self.inputs[i]['ligand'] is not None: r1[i].name = "%s_%s" % (r1[i].name, self.inputs[i]['ligand']) print "Used ID ", self.nextID, " for ", r1[i].name, ": ", r1[i] self.nextID += 1 r1[i].conc.final = r1[i].conc.stock * self.templateDilution curPrefix = prefixOut if "finalpcr" in self.qpcrStages: for i in range(len(r1)): if self.singlePrefix: q.addSamples(src=r1[i], needDil=r1[i].conc.stock / self.qConc, primers=["T7X", "MX"]) else: q.addSamples(src=r1[i], needDil=r1[i].conc.stock / self.qConc, primers=["T7X", prefixOut[i] + "X", "MX"]) print "######### qPCR ########### %.0f min" % (clock.elapsed() / 60) self.allprimers = q.allprimers() q.run(confirm=self.qpcrWait)
class Constrict(TRP): # Mix constriction inputs, constrict, PCR, remove barcodes pcreff = 1.98 def __init__(self, inputs, nmolecules, nconstrict, vol): super(Constrict, self).__init__() self.inputs = inputs self.nmolecules = nmolecules self.nconstrict = nconstrict self.qconc = 20e-12 # Target qPCR concentration self.qprimers = ["End"] self.mix_conc = 100e-9 # Concentration of mixdown self.con_dilvol = 100 # Volume to use for constriction dilutions self.con_maxdilperstage = 100 / 3.0 # Maximum dilution/stage self.con_pcr1vol = 100 self.con_pcr1inputvol = 2 self.con_pcr1tgtconc = self.qconc * 4 # Enough to take qPCR without dilutiojn self.con_pcr2dil = 4 self.con_pcr2vol = 50 self.con_pcr2tgtconc = 10e-9 self.regen_predilvol = 100 self.regen_predil = 25 self.regen_dil = 25 self.regen_vol = 100 self.regen_cycles = 10 self.rsrc = [reagents.add("%s-%s-%s" % (inputs[i]['name'], inputs[i]['left'], inputs[i]['right']), decklayout.SAMPLEPLATE, well=inputs[i]['well'] if 'well' in inputs[i] else None, conc=Concentration(stock=inputs[i]['bconc'], units="nM"), initVol=vol, extraVol=0) for i in range(len(inputs))] self.q = None # Defined in pgm() def pgm(self): self.q = QSetup(self, maxdil=16, debug=False, mindilvol=60) # Don't start idler (to minimize tip cross-contamination); last PCR allows plenty of time for doing dilutions without any effect on run time # Will start after first constriction PCR is running #self.q.debug = True # self.e.addIdleProgram(self.q.idler) self.q.addReferences(dstep=10, primers=self.qprimers, ref=reagents.getsample("BT5310"),nreplicates=2) samps=[r.getsample() for r in self.rsrc] for s in samps: self.q.addSamples([s],needDil=max(10,s.conc.stock*1e-9/self.qconc),primers=self.qprimers) print("### Mixdown #### (%.0f min)" % (clock.elapsed() / 60.0)) if len(samps)>1: mixdown = self.mix(samps, [x['weight'] for x in self.inputs]) else: mixdown=samps[0] self.q.addSamples(mixdown, needDil=max(1.0,mixdown.conc.stock * 1e-9 / self.qconc), primers=self.qprimers) print("Mixdown final concentration = %.0f pM" % (mixdown.conc.stock * 1000)) print("### Constriction #### (%.1f min)" % (clock.elapsed() / 60.0)) constricted = self.constrict(mixdown, mixdown.conc.stock * 1e-9) print("### Regeneration #### (%.0f min)" % (clock.elapsed() / 60.0)) prefixes = set([x['left'][0] for x in self.inputs]) self.regenerate(constricted * len(prefixes), [p for p in prefixes for _ in constricted]) print("### qPCR #### (%.0f min)" % (clock.elapsed() / 60.0)) self.q.run(confirm=False, enzName='EvaGreen', waitForPTC=True) print("### qPCR Done #### (%.0f min)" % (clock.elapsed() / 60.0)) worklist.userprompt("qPCR done -- only need to complete final PCR", 300) self.e.waitpgm() print("### Final PCR Done #### (%.0f min)" % (clock.elapsed() / 60.0)) 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 constrict(self, constrictin, conc): """Constrict sample with concentration given by conc (in M)""" # noinspection PyPep8Naming AN = 6.022e23 dil = conc * (self.con_pcr1inputvol * 1e-6) * AN / self.nmolecules nstages = int(math.ceil(math.log(dil) / math.log(self.con_maxdilperstage))) dilperstage = math.pow(dil, 1.0 / nstages) print("Diluting by %.0fx in %.0f stages of %.1f" % (dil, nstages, dilperstage)) s = [decklayout.WATER] + [constrictin] * self.nconstrict + [decklayout.SSDDIL] self.e.sanitize(3, 50) # Heavy sanitize for j in range(nstages): print("Stage ", j, ", conc=", conc) if conc <= self.qconc * 1e-9: self.q.addSamples(s, needDil=1.0, primers=self.qprimers, save=False) s = self.runQPCRDIL(s, self.con_dilvol, dilperstage, dilPlate=True) conc /= dilperstage cycles = int( math.log(self.con_pcr1tgtconc / conc * self.con_pcr1vol / self.con_pcr1inputvol) / math.log(self.pcreff) + 0.5) pcr1finalconc = conc * self.con_pcr1inputvol / self.con_pcr1vol * self.pcreff ** cycles print("Running %d cycle PCR1 -> %.1f pM" % (cycles, pcr1finalconc * 1e12)) s = s + [decklayout.WATER] # Extra control of just water added to PCR mix pcr = self.runPCR(primers=None, src=s, vol=self.con_pcr1vol, srcdil=self.con_pcr1vol * 1.0 / self.con_pcr1inputvol, ncycles=cycles, master="MConstrict", kapa=True) for p in pcr: p.conc = Concentration(stock=pcr1finalconc * 1e9, final=pcr1finalconc / self.con_pcr2dil, units='nM') self.e.addIdleProgram(self.q.idler) # Now that constriction is done, can start on qPCR setup needDil = max(4, pcr1finalconc / self.qconc) print("Running qPCR of PCR1 products using %.1fx dilution" % needDil) self.q.addSamples(pcr, needDil=needDil, primers=self.qprimers, save=True) pcr = pcr[1:-2] # Remove negative controls cycles2 = int(math.log(self.con_pcr2tgtconc / pcr1finalconc * self.con_pcr2dil) / math.log(self.pcreff) + 0.5) pcr2finalconc = pcr1finalconc / self.con_pcr2dil * self.pcreff ** cycles2 if cycles2 > 0: print("Running %d cycle PCR2 -> %.1f nM" % (cycles2, pcr2finalconc * 1e9)) pcr2 = self.runPCR(primers="End", src=pcr, vol=self.con_pcr2vol, srcdil=self.con_pcr2dil, ncycles=cycles2, master="MKapa", kapa=True) self.q.addSamples(pcr2, needDil=pcr2finalconc / self.qconc, primers=self.qprimers, save=True) for p in pcr2: p.conc = Concentration(stock=pcr2finalconc * 1e9, units='nM') self.e.waitpgm() return pcr2 else: return pcr def regenerate(self, inp, prefix): """Regenerate T7 templates without barcodes with each of the given prefixes""" print("Regen Predilute: %.1f nM by %.1fx to %.2f nM" % ( inp[0].conc.stock, self.regen_predil, inp[0].conc.stock / self.regen_predil)) d1 = self.runQPCRDIL(inp, self.regen_predilvol, self.regen_predil, dilPlate=True) inconc = inp[0].conc.stock / self.regen_predil / self.regen_dil print("Regen PCR: %.3f nM with %d cycles -> %.1f nM" % ( inconc, self.regen_cycles, inconc * self.pcreff ** self.regen_cycles)) res = self.runPCR(src=d1, srcdil=self.regen_dil, vol=self.regen_vol, ncycles=self.regen_cycles, primers=["T7%sX" % p for p in prefix], fastCycling=False, master="MKapa", kapa=True) return res
def pgm(self): q = QSetup(self,maxdil=16,debug=False,mindilvol=60) self.e.addIdleProgram(q.idler) input = [s.getsample() for s in self.srcs] qpcrPrimers=["REF","MX","T7X","T7AX","T7BX","T7WX"] q.addSamples(decklayout.SSDDIL,1,qpcrPrimers,save=False) # Negative controls # Save RT product from first (uncleaved) round and then use it during 2nd (cleaved) round for ligation and qPCR measurements prefixIn=[inp['prefix'] for inp in self.inputs] prefixOut=["A" if p=="W" else "B" if p=="A" else "W" if p=="B" else "BADPREFIX" for p in prefixIn] print "Starting new cleavage round, will add prefix: ",prefixOut names=[i.name for i in input] print "######## T7 ###########" print "Inputs: (t7vol=%.2f)"%self.t7vol for inp in input: print " %s: %.1ful@%.1f nM, use %.1f ul (%.3f pmoles)"%(inp.name,inp.volume,inp.conc.stock,self.t7vol/inp.conc.dilutionneeded(), self.t7vol*inp.conc.final/1000) print "input[0]=",input[0] needDil = max([inp.conc.final for inp in input])*1.0/self.qConc if self.directT7: # Just add MT7 and possibly water to each well mconc=reagents.getsample("MT7").conc.dilutionneeded() for i in range(len(input)): watervol=self.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(self.t7vol/mconc,reagents.getsample("MT7"),input[i],mix=(False,False)) assert(input[i].volume==self.t7vol) rxs=input else: rxs = self.runT7Setup(src=input,vol=self.t7vol,srcdil=[inp.conc.dilutionneeded() for inp in input]) print "input[0]=",input[0] #for i in range(len(rxs)): # q.addSamples(src=rxs],needDil=needDil,primers=["T7"+prefixIn[i]+"X","MX","T7X","REF"],names=["%s.T-"%names[i]]) self.runT7Pgm(dur=self.t7dur,vol=self.t7vol) print "Template conc=%.1f nM, estimated RNA concentration in T7 reaction at %.0f nM"%(self.tmplFinalConc,self.rnaConc) 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=[ "A-Stop" if n=="A" else "B-Stop" if n=="B" else "W-Stop" if n=="W" else "BADPREFIX" for n in prefixOut] for i in range(len(rxs)): rxs[i].name=rxs[i].name+"."+stop[i] stopDil=rxs[0].volume/preStopVolume needDil = self.rnaConc/self.qConc/stopDil #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 rxin=rxs rxs=self.runRT(src=rxs,vol=self.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] for r in rxin: if r.volume>20: print "Have more T7 reaction remaining than needed: %s has %.1f ul"%(r.name,r.volume) needDil /= rtDil rtPostDil=5 if rtPostDil!=1: self.diluteInPlace(tgt=rxs,dil=rtPostDil) needDil /= rtPostDil #q.addSamples(src=rxs,needDil=needDil,primers=["T7AX","MX","REF"],names=["%s.rt"%r.name for r in rxs]) print "######## Ligation setup ###########" extdil=5.0/4 reagents.getsample("MLigase").conc=Concentration(5) extvol=20; print "Extension volume=",extvol rxs=self.runLig(rxs,vol=extvol) print "Ligation volume= ",[x.volume for x in rxs] needDil=needDil/extdil extpostdil=4 if extpostdil>1: print "Dilution after extension: %.2f"%extpostdil self.diluteInPlace(tgt=rxs,dil=extpostdil) needDil=needDil/extpostdil if not self.doexo: self.pcrdil=self.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 for i in range(len(rxs)): q.addSamples(src=[ext[i]],needDil=needDil/self.saveDil,primers=["T7"+prefixIn[i]+"X","T7"+prefixOut[i]+"X","MX","T7X","REF"],names=["%s.ext"%names[i]]) else: for i in range(len(rxs)): q.addSamples(src=[rxs[i]],needDil=needDil,primers=["T7"+prefixIn[i]+"X","T7"+prefixOut[i]+"X","MX","T7X","REF"],names=["%s.ext"%names[i]]) 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 q.addSamples(src=rxs,needDil=needDil,primers=["T7AX","T7BX","MX","T7X","REF"],names=["%s.exo"%names[i] for i in range(len(rxs))]) #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=[] 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) q.addSamples(src=[clean[i] for i in range(len(rxs))],needDil=needDil,primers=["T7AX","MX","T7X","REF"]) rxs=rxs+clean # Use the cleaned products for PCR totalDil=stopDil*rtDil*rtPostDil*extdil*extpostdil*exoDil fracRetained=rxs[0].volume/(self.t7vol*totalDil) print "Total dilution from T7 to Pre-pcr Product = %.2f*%.2f*%.2f*%.2f*%.2f*%.2f = %.2f, fraction retained=%.0f%%"%(stopDil,rtDil,rtPostDil,extdil,extpostdil,exoDil,totalDil,fracRetained*100) if self.dopcr: print "######### PCR #############" print "PCR Volume: %.1f, Dilution: %.1f, volumes available for PCR: [%s]"%(self.pcrvol, self.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/self.pcrdil 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 gain=pcrgain(initConc,400,self.pcrcycles) finalConc=initConc*gain print "Estimated starting concentration in PCR = %.1f nM, running %d cycles -> %.0f nM\n"%(needDil*self.qConc,self.pcrcycles,finalConc) pcr=self.runPCR(src=rxs,vol=self.pcrvol,srcdil=self.pcrdil,ncycles=self.pcrcycles,primers=["T7%sX"%x for x in prefixOut],usertime=self.usertime,fastCycling=True) needDil=finalConc/self.qConc pcrpostdil=2 if pcrpostdil>1: self.diluteInPlace(pcr,pcrpostdil) needDil=needDil/pcrpostdil print "Projected final concentration = %.0f nM (after %.1fx dilution)"%(needDil*self.qConc,pcrpostdil) for i in range(len(pcr)): pcr[i].conc=Concentration(stock=finalConc/pcrpostdil,final=None,units='nM') if self.pcrSave: # Save samples at 1x if self.savedilplate: sv=self.saveSamps(src=pcr[:len(rxs)],vol=[x.volume-16.4 for x in pcr[:len(rxs)]],dil=1,plate=decklayout.DILPLATE) else: sv=self.saveSamps(src=pcr[:len(rxs)],vol=[x.volume-16.4 for x in pcr[:len(rxs)]],dil=1,plate=decklayout.EPPENDORFS) # 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) print "######### qPCR ###########" #q.addReferences(mindil=4,nsteps=6,primers=["T7X","MX","T7AX"]) #worklist.userprompt('Continue to setup qPCR') q.run()
class Barcoding(TRP): """Barcode multiple samples, mix them""" def __init__(self, inputs, pcr1inputconc=0.05, used=None,doqpcr=True,inputPlate=decklayout.SAMPLEPLATE): super(Barcoding, self).__init__() if used is None: used = [] self.inputs = inputs self.qconc = 50e-12 # Target qPCR concentration self.qprimers = ["End"] self.doqpcr=doqpcr self.bc1_inputvol = 4 # ul of input samples self.mix_conc = 100e-9 # Concentration of mixdown self.pcr1inputconc = pcr1inputconc for inp in inputs: bc = "%s-%s" % (inp['left'], inp['right']) if bc in used: logging.error("Barcode %s is being reused for %s" % (bc, inp['name'])) used.append(bc) for x in inputs: if not reagents.isReagent(x['name']): reagents.add(x['name'], inputPlate, well=x['well'] if 'well' in x else None, conc=Concentration(stock=x['conc'], units="nM"), initVol=self.bc1_inputvol, extraVol=0) else: r = reagents.getsample(x['name']) if r.conc.stock != x['conc']: logging.error('Input %s has conflicting concentrations set: %f and %f', x['name'], r.conc.stock, x['conc']) assert False self.q = None # Defined in pgm() def pgm(self): self.q = QSetup(self, maxdil=16, debug=False, mindilvol=100) self.e.addIdleProgram(self.q.idler) if self.doqpcr: self.q.addReferences(dstep=10, primers=self.qprimers, ref=reagents.getsample("BT5310")) print("### Barcoding #### (%.0f min)" % (clock.elapsed() / 60.0)) bcout = self.barcoding(names=[x['name'] for x in self.inputs], left=[x['left'] for x in self.inputs], right=[x['right'] for x in self.inputs]) for i in range(len(self.inputs)): x = self.inputs[i] if 'bconc' in x and x['bconc'] is not None: print("Resetting concentration of %s from expected %.1f to bconc setting of %.1f nM" % \ (x['name'], bcout[i].conc.stock, x['bconc'])) bcout[i].conc.stock = x['bconc'] print("### qPCR #### (%.0f min)" % (clock.elapsed() / 60.0)) self.q.run(confirm=False, enzName='EvaGreen') print("### qPCR Done #### (%.0f min)" % (clock.elapsed() / 60.0)) print("### Final PCR Done #### (%.0f min)" % (clock.elapsed() / 60.0)) worklist.flushQueue() if all(['bconc' in x and x['bconc'] is not None for x in self.inputs]): print("### Mixdown #### (%.0f min)" % (clock.elapsed() / 60.0)) worklist.flushQueue() worklist.comment('Start mixdown only at this point') self.e.sanitize(force=True) # mixdown = self.mix(bcout, [x['weight'] for x in self.inputs]) # Set default mix number to 1 for x in self.inputs: if 'mix' not in x: x['mix']=1 mixes=set([x['mix'] for x in self.inputs]) for m in mixes: sel=[ i for i in range(len(self.inputs)) if self.inputs[i]['mix']==m] print("sel=",sel) mixdown = self.mix([bcout[i] for i in sel], [self.inputs[i]['weight'] for i in sel],prefix="Mix%d_"%m) mixdown.name="Mix%d_Final"%m # self.q.addSamples(mixdown, needDil=mixdown.conc.stock * 1e-9 / self.qconc, primers=self.qprimers,nreplicates=3) else: print("### Not doing mixdown as bconc not set in all inputs") def mix(self, inp, weights, tgtdil=1.0, maxusefrac=0.75, prefix="Mix"): """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))])) relvol = [weights[i] * 1.0 / inp[i].conc.stock for i in range(len(inp))] stages=mixsplit(vols=relvol,samps=inp,avail=[(i.volume-15.0)*maxusefrac-1.4 for i in inp],minvol=4.0,minmix=100.0,maxmix=100.0,plate=decklayout.SAMPLEPLATE,debug=False,prefix=prefix) for s in stages: dest=s[0] moles=0 for i in range(len(s[1])-1,-1,-1): src=s[1][i] vol=s[2][i] self.e.transfer(vol,src,dest,(True,False)) if src.conc is not None: moles+=src.conc.stock*vol newconc=moles/sum(s[2]) print("Adjusting concentration of %s from %s to %.2f nM"%(dest.name,str(dest.conc) if dest.conc is not None else -1,newconc)) dest.conc=Concentration(newconc,newconc,units='nM') print(s[0].name,'\n =',"\n + ".join(['%5.1fuL %5.2f %s %s'%(s[2][i],s[1][i].conc.stock if s[1][i].conc is not None else 0,s[1][i].conc.units if s[1][i].conc is not None else " ",s[1][i].name) for i in range(len(s[1]))]),"\n-> %5.1ful %5.2f %s %s"%(dest.volume,dest.conc.stock,dest.conc.units,dest.name)) print() return stages[0][0] 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 def barcoding(self, names, left, right): """Perform barcoding of the given inputs; rsrsc,left,right should all be equal length""" pcrcycles = [5, 10] pcr1inputdil = 10 pcr1vol = 30 pcr1postdil = 100.0 / pcr1vol pcr2dil = 10*pcr1postdil pcr2vol = 40.0 samps = [reagents.getsample(s) for s in names] print("Inputs:") for i in range(len(samps)): print("%2s %-10s %8s-%-8s %s" % ( samps[i].plate.wellname(samps[i].well), self.inputs[i]['name'], left[i], right[i], str(samps[i].conc))) 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 for s in samps: # Dilute down to desired conc dil = s.conc.stock / self.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 > 150.0: maxdil = 150.0 / s.volume logging.info( "Dilution of input %s (%.1f ul) by %.2f would require %.1f ul -- only diluting by %.1fx" % ( s.name, s.volume, dil, dilvol, maxdil)) dil = maxdil self.diluteInPlace(tgt=[s], dil=dil) print("Diluting %s by %.1f" % (s.name, dil)) print("### PCR1 #### (%.0f min)" % (clock.elapsed() / 60.0)) pcr1 = self.runPCR(src=samps, srcdil=[s.conc.stock / self.pcr1inputconc for s in samps], ncycles=pcrcycles[0], vol=pcr1vol, primers=[[left[i], right[i]] for i in range(len(left))], usertime=30, fastCycling=False, inPlace=False, master="MPCR1", kapa=False, annealTemp=57) pcr1finalconc = self.pcr1inputconc * 2 ** pcrcycles[0] print("PCR1 output concentration = %.1f 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') if len(pcrcycles) > 1: # Second PCR with 235p/236p on mixture (use at least 4ul of prior) print("### PCR2 #### (%.0f min)" % (clock.elapsed() / 60.0)) pcr2 = self.runPCR(src=pcr1, srcdil=pcr2dil / pcr1postdil, vol=pcr2vol, ncycles=pcrcycles[1], primers=None, fastCycling=False, master="MPCR2", kapa=True, annealTemp=64) pcr2finalconc = pcr1finalconc / (pcr2dil / pcr1postdil) * 2 ** pcrcycles[1] print("PCR2 final conc = %.1f nM" % pcr2finalconc) if pcr2finalconc > 200: print("Capping at 200nM") pcr2finalconc = 200 for x in pcr2: x.conc = Concentration(stock=pcr2finalconc, units='nM') if self.doqpcr: self.q.addSamples(src=pcr2, needDil=pcr2finalconc * 1e-9 / self.qconc, primers=self.qprimers) res = pcr2 else: self.q.addSamples(src=pcr1, needDil=pcr1finalconc / (self.qconc * 1e9), primers=self.qprimers, save=True, nreplicates=1) res = pcr1 return res
class IDPrep(TRP): # Barcode multiple samples pcreff = 1.98 def __init__(self, inputs): super(IDPrep, self).__init__() self.inputs = inputs self.qconc = 0.020 # Target qPCR concentration in nM self.qprimers = ["End"] self.bc1_inputvol = 2 # ul into PCR1 used = [] for inp in inputs: bc = "%s-%s" % (inp['left'], inp['right']) if bc in used: logging.error("Barcode %s is being reused for %s" % (bc, inp['name'])) used.append(bc) print("used=",used) self.rsrc = [reagents.add("%s-%s-%s" % (inputs[i]['name'], inputs[i]['left'], inputs[i]['right']), decklayout.SAMPLEPLATE, well=inputs[i]['well'] if 'well' in inputs[i] else None, conc=Concentration(stock=inputs[i]['conc'], units="nM"), initVol=self.bc1_inputvol, extraVol=0) for i in range(len(inputs))] self.q = None # Defined in pgm() def pgm(self): self.q = QSetup(self, maxdil=16, debug=False, mindilvol=60) self.q.debug = True self.q.addReferences(dstep=10, primers=self.qprimers, ref=reagents.getsample("BT5310"),nreplicates=2) print("### Barcoding #### (%.0f min)" % (clock.elapsed() / 60.0)) self.idbarcoding(self.rsrc, left=[x['left'] for x in self.inputs], right=[x['right'] for x in self.inputs]) print("### qPCR #### (%.0f min)" % (clock.elapsed() / 60.0)) self.q.run(confirm=False, enzName='EvaGreen') 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 pgm(self): q = QSetup(self,maxdil=self.maxdilstep,debug=False,mindilvol=60) self.e.addIdleProgram(q.idler) if self.barcoding: # Setup barcode primers for cleaved rounds only self.bcprimers=[["BC-%s-R%d_T7"%(inp['ligand'],r+1) for inp in self.inputs] if self.rounds[r]=='C' else None for r in range(len(self.rounds))] for bcp in self.bcprimers: if bcp is not None: for p in ["P-%s"%pp for pp in bcp]: if not reagents.isReagent(p): reagents.add(name=p,conc=4,extraVol=30,plate=decklayout.REAGENTPLATE,well="B2") s=reagents.getsample(p) # Force allocation of a well print("Adding %s to reagents at well %s"%(p,s.plate.wellname(s.well))) print("BC primers=", self.bcprimers) # Add any missing fields to inputs for i in range(len(self.inputs)): if 'ligand' not in self.inputs[i]: self.inputs[i]['ligand']=None if 'negligand' not in self.inputs[i]: self.inputs[i]['negligand']=None if 'round' not in self.inputs[i]: self.inputs[i]['round']=None if 'name' not in self.inputs[i]: if self.inputs[i]['ligand'] is None: self.inputs[i]['name']='%s_%d_R%d'%(self.inputs[i]['prefix'],self.inputs[i]['ID'],self.inputs[i]['round']) else: self.inputs[i]['name']='%s_%d_R%d_%s'%(self.inputs[i]['prefix'],self.inputs[i]['ID'],self.inputs[i]['round'],self.inputs[i]['ligand']) # Add templates if self.directT7: self.srcs = self.addTemplates([inp['name'] for inp in self.inputs],stockconc=self.tmplFinalConc/self.templateDilution,finalconc=self.tmplFinalConc,plate=decklayout.SAMPLEPLATE,looplengths=[inp['looplength'] for inp in self.inputs],initVol=self.t7vol[0]*self.templateDilution,extraVol=0) else: self.srcs = self.addTemplates([inp['name'] for inp in self.inputs],stockconc=self.tmplFinalConc/self.templateDilution,finalconc=self.tmplFinalConc,plate=decklayout.DILPLATE,looplengths=[inp['looplength'] for inp in self.inputs],extraVol=15) if self.dopcr: # Reserve space for PCR products pcrprods=[ [Sample("R%d-T%s"%(r,inp['ligand']),self.savePlate) for inp in self.inputs] for r in range(len(self.rounds))] else: pcrprods=None t7in = [s.getsample() for s in self.srcs] if "negative" in self.qpcrStages: q.addSamples(decklayout.SSDDIL,1,self.allprimers,save=False) # Negative controls if "reference" in self.qpcrStages: q.addReferences(dstep=10,nsteps=5,primers=["WX","MX","T7X"] if self.useMX else ["WX","T7X"],ref=reagents.getsample("BT5310"),nreplicates=1) # Save RT product from first (uncleaved) round and then use it during 2nd (cleaved) round for ligation and qPCR measurements self.rndNum=0 self.nextID=self.firstID curPrefix=[inp['prefix'] for inp in self.inputs] r1=t7in for roundType in self.rounds: # Run a single round of roundType with r1 as input # roundType is either "U" for uncleaved, or a new prefix for a cleaved round (with "T" being a T7 prepend) # Set r1 to new output at end if self.roundCallback is not None: self.roundCallback(self,self.rndNum,roundType) # Computed output prefix if roundType=='U': prefixOut=curPrefix stop=["Unclvd" for _ in curPrefix] else: if roundType=='T': stop=['T7%s'%p for p in curPrefix] prefixOut=curPrefix elif any([p==roundType for p in curPrefix]): logging.error( "Round %d is a cleaved round but goes to %s without changing prefix"%(self.rndNum, roundType)) assert False else: prefixOut=[roundType for _ in curPrefix] stop=prefixOut # May be explicitly overridden for i in range(len(self.inputs)): if 'stop' in self.inputs[i]: if isinstance(self.inputs[i]['stop'],list): assert(len(self.inputs[i]['stop'])==len(self.rounds)) t=self.inputs[i]['stop'][self.rndNum] else: t=self.inputs[i]['stop'] if (roundType=='U') != (t=='U'): print("Attempt to override round %d (type %s) with a input-specific round type of %s"%(self.rndNum, roundType, t)) assert False if roundType!='U': if t=='T': stop[i]='T7%s'%curPrefix[i] prefixOut[i]=curPrefix[i] else: stop[i]=t prefixOut[i]=t self.rndNum=self.rndNum+1 self.finalRound=self.rndNum==len(self.rounds) db.pushStatus("%s%d"%(roundType,self.rndNum)) [r1,bc1]=self.oneround(q,r1,prefixOut=prefixOut,stop=stop,prefixIn=curPrefix,keepCleaved=(roundType!='U'),rtvol=self.rtvol[self.rndNum-1],t7vol=self.t7vol[self.rndNum-1],cycles=self.pcrcycles[self.rndNum-1],pcrdil=self.pcrdil[self.rndNum-1],pcrvol=self.pcrvol[self.rndNum-1],dolig=self.allLig or (roundType!='U'),pcrtgt=None if pcrprods is None else pcrprods[self.rndNum-1]) db.popStatus() # Add TRefs specified in rnddefs if 'tref' in self.rnddef[self.rndNum-1]: tref=self.rnddef[self.rndNum-1]['tref'] assert len(tref)==len(r1) for i in range(len(r1)): if tref[i] is not None: trefname='TRef%d'%tref[i] print("Adding %s to %s"%(trefname,r1[i].name)) if not reagents.isReagent(trefname): reagents.add(name=trefname,conc=10,extraVol=30,well="E4") trefSamp=reagents.getsample(trefname) oldConc=r1[i].conc.stock oldUnits=r1[i].conc.units oldVol=r1[i].volume self.e.transfer(r1[i].volume/(trefSamp.conc.dilutionneeded()-1),trefSamp,r1[i],mix=(False,False)) # TODO: Check that these end up mixed r1[i].conc=Concentration(stock=oldConc*oldVol/r1[i].volume, units=oldUnits) # Treat TRef as straight dilution print("New conc=",r1[i].conc) for i in range(len(r1)): if self.inputs[i]['round'] is None: r1[i].name="%s_%d"%(prefixOut[i],self.nextID) else: r1[i].name="%d_%s_R%d%c"%(self.nextID,prefixOut[i],self.inputs[i]['round']+self.rndNum,roundType) if self.inputs[i]['ligand'] is not None: r1[i].name="%s_%s"%(r1[i].name,self.inputs[i]['ligand']) print("Used ID ", self.nextID," for ", r1[i].name,": ",r1[i]) self.nextID+=1 r1[i].conc.final=r1[i].conc.stock*self.templateDilution for i in range(len(bc1)): #print("Renaming",bc1[i].name) pts=bc1[i].name.split(".") bc1[i].name="%d_BC_R%d%c"%(self.nextID,self.inputs[i//2]['round']+self.rndNum,roundType) if self.inputs[i//2]['ligand'] is not None: bc1[i].name="%s_%s"%(bc1[i].name,self.inputs[i//2]['ligand']) bc1[i].name+="_"+pts[-2] print("Used ID ", self.nextID," for ", bc1[i].name,":",bc1[i]) self.nextID+=1 curPrefix=prefixOut if "finalpcr" in self.qpcrStages: for i in range(len(r1)): if self.singlePrefix: q.addSamples(src=r1[i],needDil=r1[i].conc.stock/self.qConc,primers=["T7X","MX"] if self.useMX else ["T7X"]) else: # noinspection PyUnboundLocalVariable q.addSamples(src=r1[i],needDil=r1[i].conc.stock/self.qConc,primers=["T7X",prefixOut[i]+"X"]+(["MX"] if self.useMX else [])) # Add TRefs if needed for i in range(len(r1)): if 'tref' in self.inputs[i]: trefname='TRef%d'%self.inputs[i]['tref'] if not reagents.isReagent(trefname): reagents.add(name=trefname,conc=10,extraVol=30) tref=reagents.getsample(trefname) self.e.transfer(r1[i].volume/(tref.conc.dilutionneeded()-1),tref,r1[i],mix=(False,False)) db.pushStatus('qPCR') print("######### qPCR ########### %.0f min"%(clock.elapsed()/60)) self.allprimers=q.allprimers() q.run(confirm=self.qpcrWait) db.popStatus()