Source code for science_jubilee.tools.PumpDispenser

import json
import logging
import os
from typing import Tuple, Union

from science_jubilee.labware.Labware import Labware, Location, Well
from science_jubilee.tools.Tool import Tool


[docs] class PumpDispenser(Tool): """ Class to manage dispenser tool, for example for a color mixing demo :param index: The tool index of the dispenser head on the machine :type indes: int :param name: The tool name :type name: str :param pump_group: science_jubilee.tools.PeristalticPump.PeristalticPumps object to assign to dispenser head :type pump_group: science_jubilee.tools.PeristalticPump.PeristalticPumps :param dispense_tip_offset: Offsets from tool reference point for every dispense tip on the tool :type dispense_tip_offset: list :param line_volume: Volume in mL of tubing line between pump and dispense head for 1 line :type line_volume: int, float :param waste: Location of waste basin :type waste: class 'Well' """ def __init__( self, index, name, pump_group, dispense_tip_offsets, line_volume, waste=None ): """ Dispenser tool head for use with peristaltic pumps. Requires science_jubilee.tools.PeristalticPump.PeristalticPumps object to control pumps :param index: The tool index of the dispenser head on the machine :type indes: int :param name: The tool name :type name: str :param pump_group: science_jubilee.tools.PeristalticPump.PeristalticPumps object to assign to dispenser head :type pump_group: science_jubilee.tools.PeristalticPump.PeristalticPumps :param dispense_tip_offset: Offsets from tool reference point for every dispense tip on the tool :type dispense_tip_offset: list :param line_volume: Volume in mL of tubing line between pump and dispense head for 1 line :type line_volume: int, float :param waste: Location of waste basin :type waste: class 'Well' """ super().__init__( index, name, pump_group=pump_group, dispense_tip_offsets=dispense_tip_offsets, )
[docs] self.n_dispense_heads = len(self.dispense_tip_offsets)
[docs] self.waste = waste
[docs] self.line_volume = line_volume
assert ( self.pump_group.n_pumps == self.n_dispense_heads ), "Number of pumps must match number of dispenser tips" @classmethod
[docs] def from_config( cls, index, pump_group, config_file: str, path: str = os.path.join(os.path.dirname(__file__), "configs"), ): """Initialize the pipette object from a config file :param index: The tool index of the dispenser_head on the machine :type index: int :param config_file: The name of the config file containign the dispenser tool parameters :type config_file: str :returns: A :class:`PumpDispenser` object :rtype: :class:`PumpDispenser` """ config = os.path.join(path, config_file) with open(config) as f: kwargs = json.load(f) print(kwargs) kwargs["pump_group"] = pump_group kwargs["index"] = index return cls(**kwargs)
[docs] def add_waste(self, location: Union[Well, Tuple, Location]): """ Specify a waste collection container to dump extra liquid into. This should be large enough to catch liquid from all dispenser tips simultaneously. :param location: location of container :type location: class 'Well' """ assert ( isinstance(location, Well) or isinstance(location, Tuple) or isinstance(location, Location) ), "location must be a well, tuple, or location" self.waste = location
[docs] def dispense( self, vol: Union[float, int, list], location: Union[Well, Tuple, Location], dispense_head_index=None, ): """ Dispense volume vol from dispense head dispense_head_index into location :param vol: Volume to dispense, in mL. Specify a single number (float, int) and a dispense head index (int) to dispense from a single specific dispense head, the single volume with no dispense head to dispense this amount from all dispense tips, or a list of volumes to dispense unique amount from each dispense tip :type vol: float, int list :param location: Location to dispense :type location: class 'Well' :param dispense_head_index: index of dispense tip to dispense from, if passing single number for vol :type dispense_head_index: int """ # get dispense volumes for each dispense head if isinstance(vol, list): assert len(vol) == self.n_dispense_heads dispense_volumes = vol elif isinstance(vol, float) or isinstance(vol, int): if dispense_head_index is None: # dispense same volume from every head dispense_volumes = [vol] * self.n_dispense_heads elif isinstance(dispense_head_index, int): dispense_volumes = [0] * self.n_dispense_heads dispense_volumes[dispense_head_index] = vol else: raise AssertionError("dispense_head_index must be an integer") else: raise AssertionError("vol must be a float, int, or list") # calculate XY location for each dispense head x, y, z = Labware._getxyz(location) if type(location) == Well: if z == location.z: z = z + 10 else: pass else: pass # iterate over each dispense head, skip if 0. Move to location and dispense for i, vol in enumerate(dispense_volumes): if vol == 0: continue else: # get volume for this specific dispense head iter_vol = [0] * self.n_dispense_heads iter_vol[i] = vol tip_offsets = self.dispense_tip_offsets[i] x_tip = x + tip_offsets[0] y_tip = y + tip_offsets[1] self._machine.safe_z_movement() self._machine.move_to(x=x_tip, y=y_tip) self._machine.move_to(z=z) self.pump_group.pump(iter_vol)
[docs] def prime_lines( self, volume: Union[int, float] = None, location: Union[Well, Tuple, Location] = None, ): """ Fill all lines with liquid from all pumps simultaneously. :param volume: volume to prime with, if not specified in config file :type volue: int, float :param location: location of waste container, if not already specified :type location: class 'Well' """ if volume is None: try: volume = self.line_volume except AttributeError: raise AssertionError( "Line prime volume not specified in configuration. Please specify a volume" ) if location is None and self.waste is None: raise AssertionError("Please specify a waste location") if location is None: location = self.waste # calculate XY location for each dispense head x, y, z = Labware._getxyz(location) if type(location) == Well: if z == location.z: z = z + 10 else: pass else: pass self._machine.safe_z_movement() self._machine.move_to(x=x, y=y) self._machine.move_to(z=z) self.pump_group.pump(volume)
[docs] def empty_lines(self, volume: Union[int, float] = None): """ return line contents to stock wells. :param volume: line volume to return, if not set in instance already :type volume: int, float """ if volume is None: try: volume = self.line_volume except AttributeError: raise AssertionError( "Line prime volume not specified in configuration. Please specify a volume" ) self.pump_group.pump(-volume)