def evapcheck(self, op, thresh=0.20): 'Update amount of evaporation and check for issues' if self.plate.name == "Samples": dt = clock.pipetting - self.lastevapupdate # Assume no evaporation while in PTC if dt < -0.1: # This may happen during thermocycler operation since pipetting while thermocycling is moved to pipthermotime after waitpgm() is called logging.notice( "%s: clock went backwards: pipetting=%f, lastevapupdate=%f, dt=%f -- probably OK due to counting pipetting time during PTC operation" % (self.name, clock.pipetting, self.lastevapupdate, dt)) else: dt = clock.elapsed() - self.lastevapupdate if dt < -0.1: logging.error( "%s: clock went backwards: elapsed=%f, lastevapupdate=%f, dt=%f" % (self.name, clock.elapsed(), self.lastevapupdate, dt)) if dt <= 0.1: return for i in range( 10): # Break it into smaller steps since volume affects rate evaprate = self.plate.getevaprate(max(0, self.volume - self.evap)) self.evap += evaprate * dt / 3600 / 10 if op == 'aspirate' and self.evap > thresh * self.volume and self.evap > 2.0 and self.volume > 0: pctevap = self.evap / self.volume * 100 logging.warning( " %s (%s.%s, vol=%.1f ul) may have %.1f ul of evaporation (%.0f%%)" % (self.name, str(self.plate), self.plate.wellname( self.well), self.volume, self.evap, pctevap)) if "evap" in __historyOptions: self.history = self.history + (' [Evap: %0.1f ul]' % (self.evap)) self.lastevapupdate += dt
def savesummary(self, filename, settings=None): # Print amount of samples needed fd = open(filename, "w") # print >>fd,"Deck layout:" # print >>fd,decklayout.REAGENTPLATE # print >>fd,decklayout.SAMPLEPLATE # print >>fd,decklayout.QPCRPLATE # print >>fd,decklayout.WATERLOC # print >>fd,decklayout.WASTE # print >>fd,decklayout.BLEACHLOC # print >>fd,decklayout.WASHLOC # print >>fd #print >>fd,"DiTi usage:",worklist.getDITIcnt() #print >>fd print >> fd, "Generated %s (%s-%s pyTecan-%s)" % ( datetime.now().ctime(), sys.argv[0], self.checksum, self.gitlabel) rtime = "Run time: %d (pipetting only) + %d (thermocycling only) + %d (both) = %d minutes (%.1f hours)\n" % ( clock.pipetting / 60.0, clock.thermotime / 60, clock.pipandthermotime / 60, clock.elapsed() / 60, clock.elapsed() / 3600.0) print rtime print >> fd, rtime reagents.printprep(fd) Sample.printallsamples("All Samples:", fd, w=worklist) liquidclass.LC.printalllc(fd) if settings is not None: pprint(settings, stream=fd) fd.close()
def shaken(plate,speed): 'Called after shaking to mark all samples as mixed' for s in __allsamples: if plate==s.plate.name and s.volume>0: [minx,maxx]=s.plate.getmixspeeds(s.volume,s.volume) s.wellMixed=s.wellMixed or speed>=minx s.lastMixed=clock.elapsed()
def shaken(plate, speed): 'Called after shaking to mark all samples as mixed' for s in __allsamples: if plate == s.plate.name and s.volume > 0: [minx, maxx] = s.plate.getmixspeeds(s.volume, s.volume) s.wellMixed = s.wellMixed or speed >= minx s.lastMixed = clock.elapsed()
def shaken(plate, speed): 'Called after shaking to mark all samples as mixed' for s in __allsamples: if plate == s.plate.name and s.volume > 0: if not s.wellMixed: (minx, maxx) = s.getmixspeeds() s.wellMixed = speed >= minx - 1 s.lastMixed = clock.elapsed()
def isMixed(self): 'Check if sample is currently mixed' if self.lastMixed is None: return False elif not self.hasBeads: return True else: return clock.elapsed() - self.lastMixed < BEADSETTLINGTIME
def isMixed(self): 'Check if sample is currently mixed' if self.lastMixed is None: return False elif not self.hasBeads: return True else: return clock.elapsed()-self.lastMixed < BEADSETTLINGTIME
def shaken(plate,speed): 'Called after shaking to mark all samples as mixed' for s in __allsamples: if plate==s.plate.name and s.volume>0: if not s.wellMixed: (minx,maxx)=s.getmixspeeds() s.wellMixed=speed>=minx-1 s.lastMixed=clock.elapsed()
def evapcheck(self,op,thresh=0.20): 'Update amount of evaporation and check for issues' if self.plate.name=="Samples": dt=clock.pipetting-self.lastevapupdate # Assume no evaporation while in PTC else: dt=clock.elapsed()-self.lastevapupdate if dt<-0.1: print "***ERROR*** -- clock went backwards: elapsed=",clock.elapsed(),", lastevapupdate=",self.lastevapupdate,", dt=",dt assert False if dt<=0.1: return for i in range(10): # Break it into smaller steps since volume affects rate evaprate=self.plate.getevaprate(max(0,self.volume-self.evap)) self.evap+=evaprate*dt/3600/10 if op=='aspirate' and self.evap>thresh*self.volume and self.evap>2.0 and self.volume>0: pctevap=self.evap/self.volume*100 print "WARNING: %s (%s.%s, vol=%.1f ul) may have %.1f ul of evaporation (%.0f%%)"%(self.name,str(self.plate),self.plate.wellname(self.well),self.volume,self.evap,pctevap) self.history= self.history + (' [Evap: %0.1f ul]'%(self.evap)) self.lastevapupdate+=dt
def evapcheck(self, op, thresh=0.20): 'Update amount of evaporation and check for issues' if self.plate.name == "Samples": dt = clock.pipetting - self.lastevapupdate # Assume no evaporation while in PTC else: dt = clock.elapsed() - self.lastevapupdate if dt < -0.1: print "***ERROR*** -- clock went backwards: elapsed=", clock.elapsed( ), ", lastevapupdate=", self.lastevapupdate, ", dt=", dt assert False if dt <= 0.1: return for i in range( 10): # Break it into smaller steps since volume affects rate evaprate = self.plate.getevaprate(max(0, self.volume - self.evap)) self.evap += evaprate * dt / 3600 / 10 if op == 'aspirate' and self.evap > thresh * self.volume and self.evap > 2.0 and self.volume > 0: pctevap = self.evap / self.volume * 100 print "WARNING: %s (%s.%s, vol=%.1f ul) may have %.1f ul of evaporation (%.0f%%)" % ( self.name, str(self.plate), self.plate.wellname( self.well), self.volume, self.evap, pctevap) self.history = self.history + (' [Evap: %0.1f ul]' % (self.evap)) self.lastevapupdate += dt
def savesummary(self, filename): # Print amount of samples needed fd = open(filename, "w") # print >>fd,"Deck layout:" # print >>fd,decklayout.REAGENTPLATE # print >>fd,decklayout.SAMPLEPLATE # print >>fd,decklayout.QPCRPLATE # print >>fd,decklayout.WATERLOC # print >>fd,decklayout.WASTE # print >>fd,decklayout.BLEACHLOC # print >>fd,decklayout.WASHLOC # print >>fd #print >>fd,"DiTi usage:",worklist.getDITIcnt() #print >>fd rtime = "Run time: %d (pipetting only) + %d (thermocycling only) + %d (both) = %d minutes\n" % ( clock.pipetting / 60.0, clock.thermotime / 60, clock.pipandthermotime / 60, clock.elapsed() / 60) print rtime print >> fd, rtime reagents.printprep(fd) Sample.printallsamples("All Samples:", fd, w=worklist) liquidclass.LC.printalllc("All LC:", fd) fd.close()
def __init__(self, name, plate, well=None, conc=None, volume=0, hasBeads=False, extraVol=50, mixLC=liquidclass.LCMixBottom, firstWell=None, extrainfo=[], ingredients=None, atEnd=False): while True: # wrap with a loop to allow use of backupPlate # If firstWell is not None, then it is a hint of the first well position that should be used if well != None and well != -1: if not isinstance(well, int): well = plate.wellnumber(well) if well not in plate.wells: logging.warning( "Attempt to assign sample %s to well %d (%s) which is not legal on plate %s" % (name, well, plate.wellname(well), plate.name)) for s in __allsamples: if s.well == well and s.plate == plate: logging.warning( "Attempt to assign sample %s to plate %s, well %s that already contains %s" % (name, str(plate), plate.wellname(well), s.name)) if firstWell is None: firstWell = well well = None break if well is None: # Find first unused well found = False if firstWell is not None: # First check only wells>=firstWell for well in plate.wells: if well < firstWell: continue found = True for s in __allsamples: if s.plate == plate and s.well == well: well = well + 1 found = False break if found: break if not found: well = max(plate.wells) if atEnd else min(plate.wells) while (well >= 0) if atEnd else (well <= max(plate.wells)): found = True for s in __allsamples: if s.plate == plate and s.well == well: well = well + (-1 if atEnd else 1) found = False break if found: break elif well == -1: well = None if well >= plate.nx * plate.ny: # Overflow if plate.backupPlate is not None: # Overflow onto backup plate logging.warning( "Overflow of %s plate, moving %s to %s plate -- verify carefully!" % (plate.name, name, plate.backupPlate.name)) plate = plate.backupPlate well = None continue else: logging.error("Overflow of plate %s while adding %s" % (str(plate), name)) break for s in __allsamples: if s.plate == plate and s.well == well: logging.error( "Attempt to assign sample %s to plate %s, well %s that already contains %s" % (name, str(plate), plate.wellname(well), s.name)) if name in [s.name for s in __allsamples]: while name in [s.name for s in __allsamples]: name = name + "#" logging.notice("renaming sample to %s" % name) self.name = name self.plate = plate self.well = well if isinstance(conc, Concentration) or conc is None: self.conc = conc else: self.conc = Concentration(conc) self.volume = volume self.initVol = volume if volume > 0: if ingredients is None: self.ingredients = {name: volume} else: self.ingredients = ingredients.copy() total = sum([v for v in ingredients.values()]) for k in self.ingredients: self.ingredients[k] = self.ingredients[k] * volume / total self.lastvolcheck = None else: self.ingredients = {} self.lastvolcheck = 0 # Assume that it has already been checked for 0 (since it can't be any less...) self.checkingredients() if plate.pierce: self.bottomLC = liquidclass.LCWaterPierce self.bottomSideLC = self.bottomLC # Can't use side with piercing self.inliquidLC = self.bottomLC # Can't use liquid detection when piercing else: self.bottomLC = liquidclass.LCWaterBottom self.bottomSideLC = liquidclass.LCWaterBottomSide self.inliquidLC = liquidclass.LCWaterInLiquid self.beadsLC = liquidclass.LCWaterBottomBeads self.mixLC = mixLC self.airLC = liquidclass.LCAir # Same as bottom for now self.emptyLC = self.bottomLC self.history = "" __allsamples.append(self) if hasBeads: self.lastMixed = None else: self.lastMixed = clock.elapsed( ) - 20 * 60 # Assume it was last mixed an 20 min before start of run self.wellMixed = True self.initHasBeads = hasBeads self.hasBeads = hasBeads # Setting this to true overrides the manual conditioning self.extraVol = extraVol # Extra volume to provide self.evap = 0 # Amount that has evaporated if self.plate.name == "Samples": self.lastevapupdate = clock.pipetting else: self.lastevapupdate = clock.elapsed() self.extrainfo = extrainfo self.emptied = False
def mix(self,tipMask,preaspirateAir=False,nmix=4): if self.isMixed() and self.wellMixed: logging.notice( "mix() called for sample %s, which is already mixed"%self.name) return False logging.mixwarning("Pipette mixing of %s may introduce bubbles"%self.name) self.volcheck(tipMask,[self.well],0) blowvol=5 mstr="" extraspace=blowvol+0.1 if preaspirateAir: extraspace+=5 mixvol=self.volume # -self.plate.unusableVolume; # Can mix entire volume, if air is aspirated, it will just be dispensed first without making a bubble if self.volume>MAXVOLUME-extraspace: mixvol=MAXVOLUME-extraspace logging.mixwarning("Mix of %s limited to %.0f ul instead of full volume of %.0ful"%(self.name,mixvol,self.volume)) well=[self.well if self.well!=None else 2**(tipMask-1)-1 ] mixprefillvol=5 if mixvol<self.plate.unusableVolume-mixprefillvol: logging.notice("Not enough volume in sample %s (%.1f) to mix"%(self.name,self.volume)) self.history+="(UNMIXED)" return False else: if preaspirateAir: # Aspirate some air to avoid mixing with excess volume aspirated into pipette from source in previous transfer self.aspirateAir(tipMask,5) if False: # this results in losing mixprefillvol of sample which was not mixed; remainder has different concentration than planned worklist.aspirateNC(tipMask,well,self.inliquidLC,mixprefillvol,self.plate) self.volume-=mixprefillvol self.addhistory("(PRE)",-mixprefillvol,tipMask) worklist.mix(tipMask,well,self.mixLC,mixvol,self.plate,nmix) mstr="(MB)" elif False: # self.volume>=MINLIQUIDDETECTVOLUME: # Another short-lived strategy worklist.mix(tipMask,well,self.inliquidLC,mixvol,self.plate,nmix) self.history+="(MLD)" else: height=self.plate.getliquidheight(self.volume) if height is None: worklist.mix(tipMask,well,self.mixLC,mixvol,self.plate,nmix) mstr="(MB)" else: mixheight=math.floor(height-1) # At least 1mm below liquid height if mixheight<2: mixheight=2 # print 'Vol=%.1f ul, height=%.1f mm, mix=%d, blow=%d'%(self.volume,height,mixheight,blowheight) mixLC=liquidclass.LCMix[min(12,mixheight)] if blowvol>0: blowoutLC=liquidclass.LCBlowoutLD worklist.aspirateNC(tipMask,well,self.airLC,(blowvol+0.1),self.plate) if self.volume<30: worklist.mix(tipMask,well,self.mixLC,mixvol,self.plate,nmix) mstr="(MB)" else: for _ in range(nmix): worklist.aspirateNC(tipMask,well,mixLC,mixvol,self.plate) worklist.dispense(tipMask,well,mixLC,mixvol,self.plate) mstr="(M@%d)"%(mixheight) if blowvol>0: worklist.dispense(tipMask,well,blowoutLC,blowvol,self.plate) worklist.dispense(tipMask,well,liquidclass.LCDip,0.1,self.plate) self.volume-=MIXLOSS self.addhistory(mstr,-MIXLOSS,tipMask) self.lastMixed=clock.elapsed() self.wellMixed=True return True
def __init__(self,name,plate,well=None,conc=None,volume=0,hasBeads=False,extraVol=50,mixLC=liquidclass.LCMixBottom,firstWell=None,extrainfo=[],ingredients=None,atEnd=False): # If firstWell is not None, then it is a hint of the first well position that should be used if well!=None and well!=-1: if not isinstance(well,int): well=plate.wellnumber(well) if well not in plate.wells: logging.warning("Attempt to assign sample %s to well %d (%s) which is not legal on plate %s"%(name,well,plate.wellname(well),plate.name)) for s in __allsamples: if s.well==well and s.plate==plate: logging.warning("Attempt to assign sample %s to plate %s, well %s that already contains %s"%(name,str(plate),plate.wellname(well),s.name)) well=None break if well is None: # Find first unused well found=False if firstWell is not None: # First check only wells>=firstWell for well in plate.wells: if well<firstWell: continue found=True for s in __allsamples: if s.plate==plate and s.well==well: well=well+1 found=False break if found: break if not found: well=max(plate.wells) if atEnd else min(plate.wells) while (well>=0) if atEnd else (well<=max(plate.wells)): found=True for s in __allsamples: if s.plate==plate and s.well==well: well=well+(-1 if atEnd else 1) found=False break if found: break elif well==-1: well=None for s in __allsamples: if s.plate==plate and s.well==well: logging.error("Attempt to assign sample %s to plate %s, well %s that already contains %s"%(name,str(plate),plate.wellname(well),s.name)) if name in [s.name for s in __allsamples]: while name in [s.name for s in __allsamples]: name=name+"#" logging.notice("renaming sample to %s"%name) self.name=name self.plate=plate if well>=plate.nx*plate.ny: logging.error("Overflow of plate %s while adding %s"%(str(plate),name)) self.well=well if isinstance(conc,Concentration) or conc is None: self.conc=conc else: self.conc=Concentration(conc) self.volume=volume self.initVol=volume if volume>0: if ingredients is None: self.ingredients={name:volume} else: self.ingredients=ingredients total=sum([v for v in ingredients.values()]) for k in self.ingredients: self.ingredients[k]=self.ingredients[k]*volume/total self.lastvolcheck=None else: self.ingredients={} self.lastvolcheck=0 # Assume that it has already been checked for 0 (since it can't be any less...) if plate.pierce: self.bottomLC=liquidclass.LCWaterPierce self.bottomSideLC=self.bottomLC # Can't use side with piercing self.inliquidLC=self.bottomLC # Can't use liquid detection when piercing else: self.bottomLC=liquidclass.LCWaterBottom self.bottomSideLC=liquidclass.LCWaterBottomSide self.inliquidLC=liquidclass.LCWaterInLiquid self.beadsLC=liquidclass.LCWaterBottomBeads self.mixLC=mixLC self.airLC=liquidclass.LCAir # Same as bottom for now self.emptyLC=self.bottomLC self.history="" __allsamples.append(self) if hasBeads: self.lastMixed=None else: self.lastMixed=clock.elapsed()-20*60 # Assume it was last mixed an 20 min before start of run self.wellMixed=True self.initHasBeads=hasBeads self.hasBeads=hasBeads # Setting this to true overrides the manual conditioning self.extraVol=extraVol # Extra volume to provide self.evap=0 # Amount that has evaporated if self.plate.name=="Samples": self.lastevapupdate=clock.pipetting else: self.lastevapupdate=clock.elapsed() self.extrainfo=extrainfo self.emptied=False
def aspirate(self,tipMask,volume,multi=False): self.evapcheck('aspirate') if self.plate.curloc=='PTC': logging.error( "Aspirate from PTC!, loc=%d,%d"%(self.plate.grid,self.plate.pos)) removeAll=volume==self.volume if removeAll: logging.notice("Removing all contents (%.1ful) from %s"%(volume,self.name)) if volume<0.1: logging.warning("attempt to aspirate only %.1f ul from %s ignored"%(volume,self.name)) return if volume<2 and not multi and self.name!="Water": logging.warning("Inaccurate for < 2ul: attempting to aspirate %.1f ul from %s"%(volume,self.name)) if volume>self.volume and self.volume>0: logging.error("Attempt to aspirate %.1f ul from %s that contains only %.1f ul"%(volume, self.name, self.volume)) if not self.isMixed() and self.plate.curloc!="Magnet": if self.hasBeads and self.lastMixed is not None: logging.mixwarning("Aspirate %.1f ul from sample %s that has beads and has not been mixed for %.0f sec. "%(volume,self.name,clock.elapsed()-self.lastMixed)) else: logging.mixwarning("Aspirate %.1f ul from unmixed sample %s. "%(volume,self.name)) if not self.wellMixed and self.plate.curloc!="Magnet": logging.mixwarning("Aspirate %.1f ul from poorly mixed sample %s (shake speed was too low). "%(volume,self.name)) if self.well is None: well=[] for i in range(4): if (tipMask & (1<<i)) != 0: well.append(i) else: well=[self.well] lc=self.chooseLC(volume) self.volcheck(tipMask,well,volume) if (self.hasBeads and self.plate.curloc=="Magnet") or removeAll: # With beads don't do any manual conditioning and don't remove extra (since we usually want to control exact amounts left behind, if any) worklist.aspirateNC(tipMask,well,lc,volume,self.plate) remove=lc.volRemoved(volume,multi=False) if self.volume==volume: # Removing all, ignore excess remove remove=self.volume-0.1 # Leave behind enough to be able to keep track of ingredients self.emptied=True else: worklist.aspirate(tipMask,well,lc,volume,self.plate) # Manual conditioning handled in worklist remove=lc.volRemoved(volume,multi=True) if self.volume<remove+0.1 and self.volume>0: logging.warning("Removing all contents (%.1f from %.1ful) from %s"%(remove,self.volume,self.name)) remove=self.volume-0.1 # Leave residual for k in self.ingredients: self.ingredients[k] *= (self.volume-remove)/self.volume self.volume=self.volume-remove if self.volume+.001<self.plate.unusableVolume and self.volume+remove>0 and not (self.hasBeads and self.plate.curloc=='Magnet') and not removeAll: logging.warning("Aspiration of %.1ful from %s brings volume down to %.1ful which is less than its unusable volume of %.1f ul"%(remove,self.name,self.volume,self.plate.unusableVolume)) self.addhistory("",-remove,tipMask)
def aspirate(self, tipMask, volume, multi=False): self.evapcheck('aspirate') if self.plate.curloc == 'PTC': print "Aspirate from PTC!, loc=", self.plate.grid, ",", self.plate.pos assert False if volume < 0.1: print "WARNING: attempt to aspirate only %.1f ul from %s ignored" % ( volume, self.name) return if volume < 2 and not multi and self.name != "Water": print "WARNING: Inaccurate for < 2ul: attempting to aspirate %.1f ul from %s" % ( volume, self.name) if volume > self.volume and self.volume > 0: print "ERROR:Attempt to aspirate %.1f ul from %s that contains only %.1f ul" % ( volume, self.name, self.volume) if not self.isMixed() and self.plate.curloc != "Magnet": if self.hasBeads and self.lastMixed is not None: print "WARNING: Aspirate %.1f ul from sample %s that has beads and has not been mixed for %.0f sec. " % ( volume, self.name, clock.elapsed() - self.lastMixed) else: print "WARNING: Aspirate %.1f ul from unmixed sample %s. " % ( volume, self.name) if not self.wellMixed: print "WARNING: Aspirate %.1f ul from poorly mixed sample %s (shake speed was too low). " % ( volume, self.name) if self.well is None: well = [] for i in range(4): if (tipMask & (1 << i)) != 0: well.append(i) else: well = [self.well] lc = self.chooseLC(volume) if self.hasBeads and self.plate.curloc == "Magnet": # With beads don't do any manual conditioning and don't remove extra (since we usually want to control exact amounts left behind, if any) worklist.aspirateNC(tipMask, well, lc, volume, self.plate) remove = lc.volRemoved(volume, multi=False) if self.volume == volume: # Removing all, ignore excess remove remove = self.volume self.ingredients = {} else: worklist.aspirate(tipMask, well, lc, volume, self.plate) # Manual conditioning handled in worklist remove = lc.volRemoved(volume, multi=True) if self.volume < remove and self.volume > 0: print "WARNING: Removing all contents (%.1f from %.1ful) from %s" % ( remove, self.volume, self.name) remove = self.volume self.ingredients = {} for k in self.ingredients: if self.plate.curloc == "Magnet" and k == 'BIND': pass else: self.ingredients[k] *= (self.volume - remove) / self.volume self.volume = self.volume - remove if self.volume + .001 < self.plate.unusableVolume and self.volume + remove > 0 and not ( self.hasBeads and self.plate.curloc == 'Magnet'): print "WARNING: Aspiration of %.1ful from %s brings volume down to %.1ful which is less than its unusable volume of %.1f ul" % ( remove, self.name, self.volume, self.plate.unusableVolume) self.addhistory("", -remove, tipMask)
def __init__(self,name,plate,well=None,conc=None,volume=0,hasBeads=False,extraVol=50,mixLC=liquidclass.LCMixBottom): if well!=None and well!=-1: if not isinstance(well,int): well=plate.wellnumber(well) if well not in plate.wells: print "Attempt to assign sample %s to well %d (%s) which is not legal on plate %s"%(name,well,plate.wellname(well),plate.name) for s in __allsamples: if s.well==well and s.plate==plate: print "Attempt to assign sample %s to plate %s, well %s that already contains %s"%(name,str(plate),plate.wellname(well),s.name) well=None break if well is None: # Find first unused well found=False for well in plate.wells: found=True for s in __allsamples: if s.plate==plate and s.well==well: well=well+1 found=False break if found: break elif well==-1: well=None for s in __allsamples: if s.plate==plate and s.well==well: print "Attempt to assign sample %s to plate %s, well %s that already contains %s"%(name,str(plate),plate.wellname(well),s.name) # print "Aliasing %s as %s"%(s.name,name) assert False if name in [s.name for s in __allsamples]: while name in [s.name for s in __allsamples]: name=name+"#" print "NOTICE: renaming sample to %s"%name self.name=name self.plate=plate if well>=plate.nx*plate.ny: print "Overflow of plate %s"%str(plate) for s in __allsamples: if s.plate==plate: print s assert False self.well=well if isinstance(conc,Concentration) or conc is None: self.conc=conc else: self.conc=Concentration(conc) self.volume=volume if volume>0: self.ingredients={name:volume} else: self.ingredients={} if plate.pierce: self.bottomLC=liquidclass.LCWaterPierce self.bottomSideLC=self.bottomLC # Can't use side with piercing self.inliquidLC=self.bottomLC # Can't use liquid detection when piercing else: self.bottomLC=liquidclass.LCWaterBottom self.bottomSideLC=liquidclass.LCWaterBottomSide self.inliquidLC=liquidclass.LCWaterInLiquid self.beadsLC=liquidclass.LCWaterBottomBeads self.mixLC=mixLC self.airLC=liquidclass.LCAir # Same as bottom for now self.emptyLC=self.bottomLC self.history="" __allsamples.append(self) if hasBeads: self.lastMixed=None else: self.lastMixed=clock.elapsed()-20*60 # Assume it was last mixed an 20 min before start of run self.wellMixed=True self.initHasBeads=hasBeads self.hasBeads=hasBeads # Setting this to true overrides the manual conditioning self.extraVol=extraVol # Extra volume to provide self.evap=0 # Amount that has evaporated if self.plate.name=="Samples": self.lastevapupdate=clock.pipetting else: self.lastevapupdate=clock.elapsed()
def __init__(self, name, plate, well=None, conc=None, volume=0, hasBeads=False, extraVol=50, mixLC=liquidclass.LCMixBottom): if well != None and well != -1: if not isinstance(well, int): well = plate.wellnumber(well) if well not in plate.wells: print "Attempt to assign sample %s to well %d (%s) which is not legal on plate %s" % ( name, well, plate.wellname(well), plate.name) for s in __allsamples: if s.well == well and s.plate == plate: print "Attempt to assign sample %s to plate %s, well %s that already contains %s" % ( name, str(plate), plate.wellname(well), s.name) well = None break if well is None: # Find first unused well found = False for well in plate.wells: found = True for s in __allsamples: if s.plate == plate and s.well == well: well = well + 1 found = False break if found: break elif well == -1: well = None for s in __allsamples: if s.plate == plate and s.well == well: print "Attempt to assign sample %s to plate %s, well %s that already contains %s" % ( name, str(plate), plate.wellname(well), s.name) # print "Aliasing %s as %s"%(s.name,name) assert False if name in [s.name for s in __allsamples]: while name in [s.name for s in __allsamples]: name = name + "#" print "NOTICE: renaming sample to %s" % name self.name = name self.plate = plate if well >= plate.nx * plate.ny: print "Overflow of plate %s" % str(plate) for s in __allsamples: if s.plate == plate: print s assert False self.well = well if isinstance(conc, Concentration) or conc is None: self.conc = conc else: self.conc = Concentration(conc) self.volume = volume if volume > 0: self.ingredients = {name: volume} else: self.ingredients = {} if plate.pierce: self.bottomLC = liquidclass.LCWaterPierce self.bottomSideLC = self.bottomLC # Can't use side with piercing self.inliquidLC = self.bottomLC # Can't use liquid detection when piercing else: self.bottomLC = liquidclass.LCWaterBottom self.bottomSideLC = liquidclass.LCWaterBottomSide self.inliquidLC = liquidclass.LCWaterInLiquid self.beadsLC = liquidclass.LCWaterBottomBeads self.mixLC = mixLC self.airLC = liquidclass.LCAir # Same as bottom for now self.emptyLC = self.bottomLC self.history = "" __allsamples.append(self) if hasBeads: self.lastMixed = None else: self.lastMixed = clock.elapsed( ) - 20 * 60 # Assume it was last mixed an 20 min before start of run self.wellMixed = True self.initHasBeads = hasBeads self.hasBeads = hasBeads # Setting this to true overrides the manual conditioning self.extraVol = extraVol # Extra volume to provide self.evap = 0 # Amount that has evaporated if self.plate.name == "Samples": self.lastevapupdate = clock.pipetting else: self.lastevapupdate = clock.elapsed()
def savesummary(self,filename): # Print amount of samples needed fd=open(filename,"w") # print >>fd,"Deck layout:" # print >>fd,decklayout.REAGENTPLATE # print >>fd,decklayout.SAMPLEPLATE # print >>fd,decklayout.QPCRPLATE # print >>fd,decklayout.WATERLOC # print >>fd,decklayout.WASTE # print >>fd,decklayout.BLEACHLOC # print >>fd,decklayout.WASHLOC # print >>fd #print >>fd,"DiTi usage:",worklist.getDITIcnt() #print >>fd rtime="Run time: %d (pipetting only) + %d (thermocycling only) + %d (both) = %d minutes\n"%(clock.pipetting/60.0,clock.thermotime/60, clock.pipandthermotime/60, clock.elapsed()/60) print rtime print >>fd,rtime reagents.printprep(fd) Sample.printallsamples("All Samples:",fd,w=worklist) liquidclass.LC.printalllc("All LC:",fd) fd.close()
def aspirate(self,tipMask,volume,multi=False): self.evapcheck('aspirate') if self.plate.curloc=='PTC': print "Aspirate from PTC!, loc=",self.plate.grid,",",self.plate.pos assert False if volume<0.1: print "WARNING: attempt to aspirate only %.1f ul from %s ignored"%(volume,self.name) return if volume<2 and not multi and self.name!="Water": print "WARNING: Inaccurate for < 2ul: attempting to aspirate %.1f ul from %s"%(volume,self.name) if volume>self.volume and self.volume>0: print "ERROR:Attempt to aspirate %.1f ul from %s that contains only %.1f ul"%(volume, self.name, self.volume) if not self.isMixed() and self.plate.curloc!="Magnet": if self.hasBeads and self.lastMixed is not None: print "WARNING: Aspirate %.1f ul from sample %s that has beads and has not been mixed for %.0f sec. "%(volume,self.name,clock.elapsed()-self.lastMixed) else: print "WARNING: Aspirate %.1f ul from unmixed sample %s. "%(volume,self.name) if not self.wellMixed: print "WARNING: Aspirate %.1f ul from poorly mixed sample %s (shake speed was too low). "%(volume,self.name) if self.well is None: well=[] for i in range(4): if (tipMask & (1<<i)) != 0: well.append(i) else: well=[self.well] lc=self.chooseLC(volume) if self.hasBeads and self.plate.curloc=="Magnet": # With beads don't do any manual conditioning and don't remove extra (since we usually want to control exact amounts left behind, if any) worklist.aspirateNC(tipMask,well,lc,volume,self.plate) remove=lc.volRemoved(volume,multi=False) if self.volume==volume: # Removing all, ignore excess remove remove=self.volume self.ingredients={} else: worklist.aspirate(tipMask,well,lc,volume,self.plate) # Manual conditioning handled in worklist remove=lc.volRemoved(volume,multi=True) if self.volume<remove and self.volume>0: print "WARNING: Removing all contents (%.1f from %.1ful) from %s"%(remove,self.volume,self.name) remove=self.volume self.ingredients={} for k in self.ingredients: if self.plate.curloc=="Magnet" and k=='BIND': pass else: self.ingredients[k] *= (self.volume-remove)/self.volume self.volume=self.volume-remove if self.volume+.001<self.plate.unusableVolume and self.volume+remove>0 and not (self.hasBeads and self.plate.curloc=='Magnet'): print "WARNING: Aspiration of %.1ful from %s brings volume down to %.1ful which is less than its unusable volume of %.1f ul"%(remove,self.name,self.volume,self.plate.unusableVolume) self.addhistory("",-remove,tipMask)
def aspirate(self, tipMask, volume, multi=False): self.evapcheck('aspirate') if self.plate.curloc == 'PTC': logging.error("Aspirate from PTC!, loc=%d,%d" % (self.plate.grid, self.plate.pos)) removeAll = volume == self.volume if removeAll: logging.notice("Removing all contents (%.1ful) from %s" % (volume, self.name)) if volume < 0.1: logging.warning( "attempt to aspirate only %.1f ul from %s ignored" % (volume, self.name)) return if volume < 2 and not multi and self.name != "Water": logging.warning( "Inaccurate for < 2ul: attempting to aspirate %.1f ul from %s" % (volume, self.name)) if volume > self.volume and self.volume > 0: logging.error( "Attempt to aspirate %.1f ul from %s that contains only %.1f ul" % (volume, self.name, self.volume)) if not self.isMixed() and self.plate.curloc != "Magnet": if self.hasBeads and self.lastMixed is not None: logging.mixwarning( "Aspirate %.1f ul from sample %s that has beads and has not been mixed for %.0f sec. " % (volume, self.name, clock.elapsed() - self.lastMixed)) else: logging.mixwarning( "Aspirate %.1f ul from unmixed sample %s. " % (volume, self.name)) if not self.wellMixed and self.plate.curloc != "Magnet": logging.mixwarning( "Aspirate %.1f ul from poorly mixed sample %s (shake speed was too low). " % (volume, self.name)) if self.well is None: well = [] for i in range(4): if (tipMask & (1 << i)) != 0: well.append(i) else: well = [self.well] lc = self.chooseLC(volume) self.volcheck(tipMask, well, volume) if (self.hasBeads and self.plate.curloc == "Magnet") or removeAll: # With beads don't do any manual conditioning and don't remove extra (since we usually want to control exact amounts left behind, if any) worklist.aspirateNC(tipMask, well, lc, volume, self.plate) remove = lc.volRemoved(volume, multi=False) if self.volume == volume: # Removing all, ignore excess remove remove = self.volume - 0.1 # Leave behind enough to be able to keep track of ingredients self.emptied = True else: worklist.aspirate(tipMask, well, lc, volume, self.plate) # Manual conditioning handled in worklist remove = lc.volRemoved(volume, multi=True) if self.volume < remove + 0.1 and self.volume > 0: logging.warning( "Removing all contents (%.1f from %.1ful) from %s" % (remove, self.volume, self.name)) remove = self.volume - 0.1 # Leave residual self.removeVolume(remove) if self.volume + .001 < self.plate.unusableVolume and self.volume + remove > 0 and not ( self.hasBeads and self.plate.curloc == 'Magnet') and not removeAll: logging.warning( "Aspiration of %.1ful from %s brings volume down to %.1ful which is less than its unusable volume of %.1f ul" % (remove, self.name, self.volume, self.plate.unusableVolume)) self.addhistory("", -remove, tipMask)
def savesummary(self,filename): # Print amount of samples needed fd=open(filename,"w") # print >>fd,"Deck layout:" # print >>fd,decklayout.REAGENTPLATE # print >>fd,decklayout.SAMPLEPLATE # print >>fd,decklayout.QPCRPLATE # print >>fd,decklayout.WATERLOC # print >>fd,decklayout.WASTE # print >>fd,decklayout.BLEACHLOC # print >>fd,decklayout.WASHLOC # print >>fd #print >>fd,"DiTi usage:",worklist.getDITIcnt() #print >>fd print >>fd,"Generated %s (%s-%s pyTecan-%s)"%(datetime.now().ctime(),sys.argv[0],self.checksum,self.gitlabel) rtime="Run time: %d (pipetting only) + %d (thermocycling only) + %d (both) = %d minutes (%.1f hours)\n"%(clock.pipetting/60.0,clock.thermotime/60, clock.pipandthermotime/60, clock.elapsed()/60, clock.elapsed()/3600.0) print rtime print >>fd,rtime reagents.printprep(fd) Sample.printallsamples("All Samples:",fd,w=worklist) liquidclass.LC.printalllc(fd) fd.close()
def mix(self, tipMask, preaspirateAir=False, nmix=4): if self.isMixed() and self.wellMixed: logging.notice( "mix() called for sample %s, which is already mixed" % self.name) return False logging.mixwarning("Pipette mixing of %s may introduce bubbles" % self.name) self.volcheck(tipMask, [self.well], 0) blowvol = 5 mstr = "" extraspace = blowvol + 0.1 if preaspirateAir: extraspace += 5 mixvol = self.volume # -self.plate.unusableVolume; # Can mix entire volume, if air is aspirated, it will just be dispensed first without making a bubble if self.volume > MAXVOLUME - extraspace: mixvol = MAXVOLUME - extraspace logging.mixwarning( "Mix of %s limited to %.0f ul instead of full volume of %.0ful" % (self.name, mixvol, self.volume)) well = [self.well if self.well != None else 2**(tipMask - 1) - 1] mixprefillvol = 5 if mixvol < self.plate.unusableVolume - mixprefillvol: logging.notice("Not enough volume in sample %s (%.1f) to mix" % (self.name, self.volume)) self.history += "(UNMIXED)" return False else: if preaspirateAir: # Aspirate some air to avoid mixing with excess volume aspirated into pipette from source in previous transfer self.aspirateAir(tipMask, 5) if False: # this results in losing mixprefillvol of sample which was not mixed; remainder has different concentration than planned worklist.aspirateNC(tipMask, well, self.inliquidLC, mixprefillvol, self.plate) self.removeVolume(mixprefillvol) self.addhistory("(PRE)", -mixprefillvol, tipMask) worklist.mix(tipMask, well, self.mixLC, mixvol, self.plate, nmix) mstr = "(MB)" elif False: # self.volume>=MINLIQUIDDETECTVOLUME: # Another short-lived strategy worklist.mix(tipMask, well, self.inliquidLC, mixvol, self.plate, nmix) self.history += "(MLD)" else: height = self.plate.getliquidheight(self.volume) if height is None: worklist.mix(tipMask, well, self.mixLC, mixvol, self.plate, nmix) mstr = "(MB)" else: mixheight = math.floor( height - 1) # At least 1mm below liquid height if mixheight < 2: mixheight = 2 # print 'Vol=%.1f ul, height=%.1f mm, mix=%d, blow=%d'%(self.volume,height,mixheight,blowheight) mixLC = liquidclass.LCMix[min(12, mixheight)] if blowvol > 0: blowoutLC = liquidclass.LCBlowoutLD worklist.aspirateNC(tipMask, well, self.airLC, (blowvol + 0.1), self.plate) if self.volume < 30: worklist.mix(tipMask, well, self.mixLC, mixvol, self.plate, nmix) mstr = "(MB)" else: for _ in range(nmix): worklist.aspirateNC(tipMask, well, mixLC, mixvol, self.plate) worklist.dispense(tipMask, well, mixLC, mixvol, self.plate) mstr = "(M@%d)" % (mixheight) if blowvol > 0: worklist.dispense(tipMask, well, blowoutLC, blowvol, self.plate) worklist.dispense(tipMask, well, liquidclass.LCDip, 0.1, self.plate) self.removeVolume(MIXLOSS) self.addhistory(mstr, -MIXLOSS, tipMask) self.lastMixed = clock.elapsed() self.wellMixed = True return True