Source code for science_jubilee.tools.Loop

import json
import os
import random
import warnings
from typing import Tuple

import numpy as np

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


[docs] class Loop(Tool): """A class representation of an inoculation loop. :param Tool: The base tool class :type Tool: :class:`Tool` """ def __init__(self, index, name): """Constructor method""" super().__init__(index, name) @requires_active_tool
[docs] def transfer( self, source: Well = None, destination: Well = None, s: int = 2000, sweep_x: float = 5, sweep_y: float = 5, sweep_z: float = 10, sweep_speed: float = 100, up_speed: float = 800, randomize_pickup: bool = False, ): """Dip into a number of (source, destination) well pairs. Used for transferring items from source to destination. :param source: Source well, defaults to None :type source: :class:`Well`, optional :param destination: Destination well, defaults to None :type destination: :class:`Well`, optional :param s: Movement speed in mm/min, defaults to 2000 :type s: int, optional :param sweep_x: Distance (in mm) to sweep in the x direction to pick up material, defaults to 5 :type sweep_x: float, optional :param sweep_y: Distance (in mm) to sweep in the y direction to pick up material, defaults to 5 :type sweep_y: float, optional :param sweep_z: Distance (in mm) to move in the z direction to pick up material, defaults to 10 :type sweep_z: float, optional :param sweep_speed: Speed (in mm/min) at which to sweep to pick up material, defaults to 100 :type sweep_speed: float, optional :param up_speed: Speed to move out of well (in mm/min), defaults to 800 :type up_speed: float, optional :param randomize_pickup: Move to a random position in the source well to pick up material, defaults to False :type randomize_pickup: bool, optional """ if type(source) != list: source = [source] if type(destination) != list: destination = [destination] # Assemble tuples of (source, destination) num_source_wells = len(source) num_destination_wells = len(destination) if num_source_wells == num_destination_wells: # n to n transfers pass elif ( num_source_wells == 1 and num_destination_wells > 1 ): # one to many transfers source = list(np.repeat(source, num_destination_wells)) elif ( num_source_wells > 1 and num_destination_wells == 1 ): # many to one transfers destination = list(np.repeat(destination, num_source_wells)) elif num_source_wells > 1 and num_destination_wells > 1: # uneven transfers # for uneven transfers, find least common multiple to pair off wells # raise a warning, as this might be a mistake # this mimics OT-2 behavior least_common_multiple = np.lcm(num_source_wells, num_destination_wells) source_repeat = least_common_multiple / num_source_wells destination_repeat = least_common_multiple / num_destination_wells source = list(np.repeat(source, source_repeat)) destination = list(np.repeat(destination, destination_repeat)) warnings.warn("Warning: Uneven source & destination wells specified.") source_destination_pairs = list(zip(source, destination)) for source_well, destination_well in source_destination_pairs: xs, ys, zs = self._get_xyz(well=source_well) if ( randomize_pickup ): # to make sure we don't try to pickup from an empty region r = 20 rx = random.randint(-r, r) ry = random.randint(-r, r) xs += rx ys += ry xd, yd, zd = self._get_xyz(well=destination_well) self._machine.safe_z_movement() self._machine.move_to(x=xs, y=ys) self._machine.move_to(z=zs + 5) # slowly sweep in the reservoir to pick up duckweed # can tune these default values self._machine.move(dx=sweep_x, s=sweep_speed) self._machine.move(dy=sweep_y, s=sweep_speed) self._machine.move(dz=sweep_z, s=up_speed) self.current_well = source_well # self._aspirate(vol, s=s) self._machine.safe_z_movement() self._machine.move_to(x=xd, y=yd) self._machine.move_to(z=zd + 5) # sweep again to drop off duckweed # make smaller movements and move opposite direction self._machine.move(dx=sweep_x / 2, s=sweep_speed) self._machine.move(dy=-sweep_y, s=sweep_speed) self._machine.dwell(250) # give the duckweed time to release self.current_well = destination_well
# self._dispense(vol, s=s) @staticmethod
[docs] def _get_xyz(well: Well = None, location: Tuple[float] = None): """Get the (x,y,z) position of a well. :param well: The well to fetch position of, defaults to None :type well: :class:`Well`, optional :param location: Directly specify an (x,y,z) location, defaults to None :type location: Tuple[float], optional :raises ValueError: Must specify either a well or a location :return: The well location :rtype: Tuple[float, float, float] """ if well is not None and location is not None: raise ValueError("Specify only one of Well or x,y,z location") elif well is not None: x, y, z = well.x, well.y, well.z else: x, y, z = location return x, y, z
@staticmethod
[docs] def _get_top_bottom(well: Well = None): """Get the top and bottom heights of a well. :param well: The well to fetch position of, defaults to None :type well: Well, optional :return: The z-height of the top and bottom of the well :rtype: Tuple[float, float] """ top = well.top bottom = well.bottom return top, bottom