Source code for AFL.automation.loading.RSoXSSolutionSampleCell

from AFL.automation.loading.SampleCell import SampleCell
from AFL.automation.loading.Tubing import Tubing
from AFL.automation.APIServer.Driver import Driver
from collections import defaultdict
import time

import math

[docs] class RSoXSSolutionSampleCell(Driver,SampleCell): ''' Class for a sample cell consisting of a pump and a one-to-many flow selector for a flowrate-limited measurement cell (e.g., a liquid TEM sample holder for RSoXS). The pump draws from any of the sample ports and can quickly purge/rinse itself and the tubing between the loader and the cell. When the pump is connected to the cell, its max rate is limited to very slow (5 ul/min for RSoXS). ''' defaults={} defaults['ncells'] = 1 defaults['thickness'] = None defaults['catch_to_selector_vol'] = Tubing(1517,112).volume() defaults['cell_to_selector_vol'] = Tubing(1517,170).volume()+0.6 defaults['syringe_to_selector_vol'] = Tubing(1530,49.27+10.4).volume() defaults['selector_internal_vol'] = Tubing(1529,1).volume() defaults['calibrated_catch_to_syringe_vol'] = 1.1 defaults['calibrated_syringe_to_cell_vol'] = 3.2 defaults['rinse_speed'] = 50.0 defaults['load_speed'] = 10.0 defaults['rinse_flow_delay'] = 3.0 defaults['load_flow_delay'] = 10.0 defaults['catch_empty_ffvol'] = 2 defaults['to_waste_vol'] = 1 defaults['rinse_prime_vol'] = 3 defaults['rinse_vol_ml'] = 3 defaults['rinse_vol_catch_ml'] = 2 defaults['dry_vol_ml'] = 5 defaults['blow_out_vol'] = 6 defaults['nrinses_cell_flood'] = 2 defaults['nrinses_syringe'] = 2 defaults['nrinses_cell'] = 1 defaults['nrinses_catch'] = 2
[docs] def __init__(self,pump, selector, rinse_tank_level=950, waste_tank_level=0, cell_waste_tank_level=0, overrides=None, ): ''' ncells = number of connected cells (up to 6 cells with a 10-position flow selector, with four positions taken by load port, rinse, waste, and air) Name = the cell name, array with length = ncells thickness = cell path length, to be incorporated into metadata, array with length = ncells cell state if not 'clean', array with length = ncells pump: a pump object supporting withdraw() and dispense() methods e.g. pump = NE1KSyringePump(port,syringe_id_mm,syringe_volume) selector: a selector object supporting string-based selectPort() method with options 'catch','cell','rinse','waste','air' e.g. selector = ViciMultiposSelector(port,portlabels={'catch':1,'cell':2,'rinse':3,'waste':4,'air':5}) ''' self._app = None Driver.__init__(self,name='RSoXSSolutionSampleCell',defaults=self.gather_defaults(),overrides=overrides) self.pump = pump self.selector = selector self.syringe_dirty = False self.rinse_tank_level = rinse_tank_level self.waste_tank_level = waste_tank_level self.cell_waste_tank_level = cell_waste_tank_level
[docs] def reset_tank_levels(self,rinse=950,waste=0,cell_waste=0): self.rinse_tank_level = rinse self.waste_tank_level = waste self.cell_waste_tank_level = cell_waste
@property def app(self): return self._app @app.setter def app(self,app): if app is None: self._app = app else: self._app = app self.pump.app = app self.selector.app = app
[docs] def status(self): status = [] status.append(f'CellState: {dict(self.cell_state)}') status.append(f'SelectorState: {self.selector.portString}') status.append(f'Rinse tank: {self.rinse_tank_level} mL') status.append(f'Waste tank: {self.waste_tank_level} mL') status.append(f'Cell Waste: {self.cell_waste_tank_level} mL') status.append(f'Pump: {self.pump.name}') status.append(f'Selector: {self.selector.name}') status.append(f'Cell: {self.name}') for k,v in self.selector.portlabels.items(): status.append(f'Port {v}: {k}') return status
[docs] def transfer(self,source,dest,vol_source,vol_dest=None): if vol_dest is None: vol_dest = vol_source self.app.logger.debug(f'Transferring {vol_source} mL from {source} and {vol_dest}mL to {dest}') if vol_dest>vol_source: self.app.logger.debug(f'Withrawing {vol_dest-vol_source} mL from air') self.selector.selectPort('air') self.pump.withdraw(vol_dest-vol_source,delay=False) self.selector.selectPort(source) self.pump.withdraw(vol_source) self.selector.selectPort(dest) self.pump.dispense(vol_dest) if vol_dest<vol_source: self.app.logger.debug(f'Dumping {vol_source-vol_dest} mL excess to waste') self.selector.selectPort('waste') self.pump.dispense(vol_source-vol_dest) if source == 'rinse': self.rinse_tank_level -= vol_source if dest == 'waste': self.waste_tank_level += vol_dest if dest == 'cell': self.cell_waste_tank_level += min(vol_source,vol_dest)
[docs] def catchToSyringe(self,sampleVolume=0): self.pump.setRate(self.config['load_speed']) self.pump.flow_delay = self.config['load_flow_delay'] vol_source = self.config['catch_to_selector_vol']+self.config['syringe_to_selector_vol']+self.config['catch_empty_ffvol'] + sampleVolume self.selector.selectPort('catch') self.pump.withdraw(vol_source)
[docs] def loadSample(self,cellname='cell',sampleVolume=0): if self.syringe_dirty: self.rinseSyringe() if not self.cell_state[cellname] =='clean': self.rinseCell(cellname=cellname) self.drySyringe() self.pump.setRate(self.config['load_speed']) self.pump.flow_delay = self.config['load_flow_delay'] if (self.config['calibrated_catch_to_syringe_vol'] is None) or (self.config['calibrated_catch_to_syringe_vol']=='None'): vol_source = self.config['catch_to_selector_vol'] vol_source += self.config['syringe_to_selector_vol'] vol_source +=self.config['catch_empty_ffvol'] else: vol_source = self.config['calibrated_catch_to_syringe_vol'] if (self.config['calibrated_syringe_to_cell_vol'] is None) or (self.config['calibrated_syringe_to_cell_vol']=='None'): vol_dest = self.config['syringe_to_selector_vol'] vol_dest += self.config['cell_to_selector_vol'] else: vol_dest = self.config['calibrated_syringe_to_cell_vol'] vol_dest += sampleVolume vol_source += sampleVolume self.transfer('catch',cellname,vol_source,vol_dest) # self.selector.selectPort('catch') # self.pump.withdraw(self.config['catch_to_selector_vol']+self.config['syringe_to_selector_vol']+self.config['catch_empty_ffvol']) # self.selector.selectPort(cellname) # self.pump.dispense(self.config['syringe_to_selector_vol'] + self.config['cell_to_selector_vol']) self.cell_state[cellname] = 'loaded' self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay']
def _firstCleanCell(self,rinse_if_none=False): # find the first clean cell and use that. for (cn,ct,cs) in zip(self.name,self.config['thickness'],self.state): if cs is 'clean': return (cn,ct,cs) if clean_if_none: for (cn,ct,cs) in zip(self.name,self.config['thickness'],self.state): if cs is 'dirty': self.rinseCell(cellname=cn) return (cn,ct,cs)
[docs] def catchToWaste(self,sampleVolume=0.0): if (self.config['calibrated_catch_to_syringe_vol'] is None) or (self.config['calibrated_catch_to_syringe_vol']=='None'): vol_source = self.config['syringe_to_selector_vol'] vol_source +=self.config['catch_empty_ffvol'] vol_source += sampleVolume else: vol_source = self.config['calibrated_catch_to_syringe_vol'] vol_source += sampleVolume vol_dest = self.config['syringe_to_selector_vol'] + self.config['to_waste_vol'] + sampleVolume self.transfer('catch','waste',vol_source,vol_dest) self.syringe_dirty = True
[docs] def cellToWaste(self,cellname='cell'): self.transfer(cellname,'waste',self.config['cell_to_selector_vol'] + self.config['syringe_to_selector_vol'],vol_dest = self.config['syringe_to_selector_vol'] + self.config['to_waste_vol']) self.syringe_dirty = True
[docs] def rinseSyringe(self): self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] for i in range(self.config['nrinses_syringe']): self.transfer('rinse','waste',self.config['rinse_vol_ml']) self.syringe_dirty = False
[docs] def drySyringe(self,blow=True,waittime=1): ''' transfer from air to waste, to push out any residual liquid. if blow is True, additionally use a 1 s pulse of nitrogen to clear the syringe transfer line. ''' self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] self.transfer('air','waste',self.config['dry_vol_ml']) if blow: self.selector.selectPort('waste') self.blowselector.selectPort('blow') time.sleep(waittime) self.blowselector.selectPort('pump')
[docs] def rinseCell(self,cellname='cell'): self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] self.rinseCellFlood(cellname)
[docs] def rinseCellPull(self,cellname = 'cell'): self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] #rinse the cell for i in range(self.config['nrinses_cell']): self.selector.selectPort('rinse') self.pump.withdraw(self.config['rinse_vol_ml']) self.selector.selectPort(cellname) for i in range(3): #swish the fluid around in the cell self.pump.dispense(self.config['rinse_vol_ml']) self.pump.withdraw(self.config['rinse_vol_ml']) self.selector.selectPort('waste') self.pump.dispense(self.config['rinse_vol_ml'] + self.config['to_waste_vol']) self.syringe_dirty = True
[docs] def rinseCellFlood(self,cellname='cell'): self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] if self.syringe_dirty: self.rinseSyringe() # self.blowOutCell(cellname) for i in range(self.config['nrinses_cell_flood']): self.transfer('rinse',cellname,self.config['rinse_vol_ml']) self.blowOutCell(cellname) self.cell_state[cellname] = 'clean'
[docs] def swish(self,vol): self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] self.pump.withdraw(vol) self.pump.dispense(vol)
[docs] def blowOutCellLegacy(self,cellname='cell'): self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] self.selector.selectPort('air') self.pump.withdraw(self.config['blow_out_vol'],delay=False) self.selector.selectPort(cellname) self.pump.dispense(self.config['blow_out_vol'])
[docs] def blowOutCell(self,cellname='cell',waittime=20): self.selector.selectPort('cell') self.blowselector.selectPort('blow') time.sleep(waittime) self.blowselector.selectPort('pump')
[docs] def rinseCatch(self): self.pump.setRate(self.config['rinse_speed']) self.pump.flow_delay = self.config['rinse_flow_delay'] for i in range(self.config['nrinses_catch']): from_vol = self.config['rinse_vol_catch_ml'] + self.config['syringe_to_selector_vol'] if (self.config['calibrated_catch_to_syringe_vol'] is None) or (self.config['calibrated_catch_to_syringe_vol']=='None'): to_vol = self.config['rinse_vol_catch_ml'] + self.config['catch_to_selector_vol'] else: to_vol = self.config['calibrated_catch_to_syringe_vol'] + self.config['rinse_vol_catch_ml'] self.transfer('rinse','catch',from_vol,to_vol) for i in range(1): self.selector.selectPort('catch') self.swish(self.config['rinse_vol_catch_ml']) if (self.config['calibrated_catch_to_syringe_vol'] is None) or (self.config['calibrated_catch_to_syringe_vol']=='None'): from_vol = self.config['rinse_vol_catch_ml'] + self.config['catch_to_selector_vol'] + self.config['catch_empty_ffvol'] else: from_vol = self.config['calibrated_catch_to_syringe_vol'] + self.config['catch_empty_ffvol'] + self.config['rinse_vol_catch_ml'] to_vol = self.config['rinse_vol_catch_ml'] + self.config['syringe_to_selector_vol'] self.transfer('catch','waste',from_vol,to_vol) #clear out any remaining volume in the syringe self.selector.selectPort('waste') self.pump.emptySyringe()
[docs] def rinseAll(self,cellname='cell'): # if self.state is 'loaded': # self.cellToWaste() self.rinseCell(cellname=cellname) self.rinseCatch() self.rinseSyringe()
[docs] def setRinseLevel(self,vol): self.rinse_tank_level = vol
[docs] def setWasteLevel(self,vol): self.waste_tank_level = vol