def shake(self,plate,dur=60,speed=None,accel=5,returnPlate=True,samps=None,force=False): if self.ptcrunning and plate==decklayout.SAMPLEPLATE: self.waitpgm() # Move the plate to the shaker, run for the given time, and bring plate back allsamps=Sample.getAllOnPlate(plate) if samps is None: samps=allsamps if all([x.isMixed() for x in samps]) and not force: logging.notice( "No need to shake "+plate.name+", but doing so anyway.") minspeed=0 maxspeed=2000 for x in samps: (a,b)=x.getmixspeeds() minspeed=max([a,minspeed]) maxspeed=min([b,maxspeed]) if speed is None: if minspeed<maxspeed: speed=max((maxspeed+minspeed)/2,maxspeed-50) # Mix as fast as safely possible (but always above minspeed) else: speed=maxspeed if speed<minspeed-2 or speed>maxspeed+2: others="" for x in allsamps: (a,b)=x.getmixspeeds() if b<minspeed or a>maxspeed: if a is not None and a>0: others+=" {%s: %.1ful,G=%.2f%%,min=%.0f,max=%.0f}"%(x.name,x.volume,x.glycerolfrac()*100,a,b) else: others+=" {%s: %.1ful,G=%.2f%%,max=%.0f}"%(x.name,x.volume,x.glycerolfrac()*100,b) logging.mixwarning("Mixing %s at %.0f RPM; minspeed(%.0f) > maxspeed(%.0f), limits=[%s]"%(plate.name,speed,minspeed,maxspeed,others)) else: logging.notice("Mixing %s at %.0f RPM ( min RPM=%.0f, max RPM=%.f)"%(plate.name, speed, minspeed, maxspeed)) oldloc=plate.curloc self.moveplate(plate,"Shaker",returnHome=False) global __shakerActive __shakerActive=True worklist.pyrun("BioShake\\bioexec.py setElmLockPos") worklist.pyrun("BioShake\\bioexec.py setShakeTargetSpeed%d"%speed) worklist.pyrun("BioShake\\bioexec.py setShakeAcceleration%d"%accel) worklist.pyrun("BioShake\\bioexec.py shakeOn") self.starttimer() Sample.shaken(plate.name,speed) Sample.addallhistory("(S%d@%.0f)"%(dur,speed),onlyplate=plate.name) self.waittimer(dur) worklist.pyrun("BioShake\\bioexec.py shakeOff") self.starttimer() self.waittimer(accel+4) worklist.pyrun("BioShake\\bioexec.py setElmUnlockPos") __shakerActive=False if returnPlate: self.moveplate(plate,oldloc)
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 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 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
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 shake(self, plate, dur=60, speed=None, accel=5, returnPlate=True, samps=None, force=False): if self.ptcrunning and plate == decklayout.SAMPLEPLATE: self.waitpgm() # Move the plate to the shaker, run for the given time, and bring plate back allsamps = Sample.getAllOnPlate(plate) if samps is None: samps = allsamps if all([x.isMixed() for x in samps]) and not force: logging.notice("No need to shake " + plate.name + ", but doing so anyway.") minspeed = 0 maxspeed = 2000 for x in samps: (a, b) = x.getmixspeeds() minspeed = max([a, minspeed]) for x in allsamps: (a, b) = x.getmixspeeds() maxspeed = min([b, maxspeed]) if speed is None: if minspeed < maxspeed: speed = max( (maxspeed + minspeed) / 2, maxspeed - 50 ) # Mix as fast as safely possible (but always above minspeed) else: speed = maxspeed if speed < minspeed - 2 or speed > maxspeed + 2: others = "" for x in allsamps: (a, b) = x.getmixspeeds() if b < minspeed or a > maxspeed: if a is not None and a > 0: others += " {%s: %.1ful,G=%.2f%%,min=%.0f,max=%.0f}" % ( x.name, x.volume, x.glycerolfrac() * 100, a, b) else: others += " {%s: %.1ful,G=%.2f%%,max=%.0f}" % ( x.name, x.volume, x.glycerolfrac() * 100, b) logging.mixwarning( "Mixing %s at %.0f RPM; minspeed(%.0f) > maxspeed(%.0f), limits=[%s]" % (plate.name, speed, minspeed, maxspeed, others)) else: logging.notice( "Mixing %s at %.0f RPM ( min RPM=%.0f, max RPM=%.f)" % (plate.name, speed, minspeed, maxspeed)) oldloc = plate.curloc self.moveplate(plate, "Shaker", returnHome=False) global __shakerActive __shakerActive = True worklist.pyrun("BioShake\\bioexec.py setElmLockPos") worklist.pyrun("BioShake\\bioexec.py setShakeTargetSpeed%d" % speed) worklist.pyrun("BioShake\\bioexec.py setShakeAcceleration%d" % accel) worklist.pyrun("BioShake\\bioexec.py shakeOn") self.starttimer() Sample.shaken(plate.name, speed) Sample.addallhistory("(S%d@%.0f)" % (dur, speed), onlyplate=plate.name, type="shake") self.waittimer(dur) worklist.pyrun("BioShake\\bioexec.py shakeOff") self.starttimer() self.waittimer(accel + 4) worklist.pyrun("BioShake\\bioexec.py setElmUnlockPos") __shakerActive = False if returnPlate: self.moveplate(plate, oldloc)