def test_compound_units(self):
     u1 = Unit(20, 'microliter/second')
     u2 = Unit(30, 'microliter/second')
     assert (u1 < u2)
     assert (u2 > u1)
     assert (u1 + u2 == Unit(50, 'microliter/second'))
     assert (u1 - u2 == Unit(-10, 'microliter/second'))
 def test_compound_units(self):
     u1 = Unit(20, "microliter/second")
     u2 = Unit(30, "microliter/second")
     assert u1 < u2
     assert u2 > u1
     assert u1 + u2 == Unit(50, "microliter/second")
     assert u1 - u2 == Unit(-10, "microliter/second")
Esempio n. 3
0
 def test_compound_units(self):
     u1 = Unit(20, 'microliter/second')
     u2 = Unit(30, 'microliter/second')
     self.assertTrue(u1 < u2)
     self.assertTrue(u2 > u1)
     self.assertEqual(u1 + u2, Unit(50, 'microliter/second'))
     self.assertEqual(u1 - u2, Unit(-10, 'microliter/second'))
 def test_set_volume_unit_conv(self):
     self.c.well(0).set_volume("200:nanoliter")
     assert (self.c.well(0).volume == Unit(0.2, "microliter"))
     self.c.well(1).set_volume(".1:milliliter")
     assert (self.c.well(1).volume == Unit(100, "microliter"))
     with pytest.raises(ValueError):
         self.c.well(2).set_volume("1:milliliter")
 def test_input_types(self):
     u1 = Unit("20.1:microliter")
     u2 = Unit(20.1, "microliter")
     u3 = Unit(Decimal("20.1"), "microliter")
     assert u1 == u2 == u3
     assert isinstance(u1.magnitude, Decimal)
     with pytest.raises(UnitValueError):
         Unit(1j, "microliter")
 def test_string_rounding(self):
     # Ensure that string representation is rounded
     assert (
         str(Unit(0.1, "microliter") + Unit(0.2, "microliter")) == "0.3:microliter"
     )
     # Currently we round to 12 decimal places by default
     assert str(Unit(0.1234567890123, "microliter")) == "0.123456789012:microliter"
     assert str(Unit(0.1234567890126, "microliter")) == "0.123456789013:microliter"
Esempio n. 7
0
 def test_generates_liquid_handle_with_density(self):
     self.p.transfer(self.flat.well(0),
                     self.flat.well(0),
                     "1:uL",
                     density=Unit(1.1, "mg/ml"))
     inst = self.p.instructions[-1]
     assert inst.op == "liquid_handle"
     assert inst.data["locations"][0]["transports"][4]["density"] == Unit(
         1.1, "mg/ml")
Esempio n. 8
0
    def test_updates_well_volume(self):
        source = self.flat.well(0)
        destination = self.flat.well(1)
        source_volume = Unit(30, "uL")
        volume = Unit(20, "uL")

        self.p.transfer(source.set_volume(source_volume), destination, volume)
        assert source.volume == source_volume - volume
        assert destination.volume == volume
Esempio n. 9
0
    def test_split_volume_transfer(self):
        self.p.transfer(self.deep.well(0), self.deep.well(1), "2000:uL")
        assert len(self.p.instructions) == 3
        volumes = [
            _.locations[1]["transports"][4]["volume"]
            for _ in self.p.instructions
        ]
        expected_volumes = [Unit(895, "uL"), Unit(895, "uL"), Unit(210, "uL")]

        assert volumes == expected_volumes
Esempio n. 10
0
 def test_comparison(self):
     u1 = Unit(20, 'microliter')
     u2 = Unit(30, 'microliter')
     self.assertFalse(u1 == u2)
     self.assertTrue(u1 == Unit(20, 'microliter'))
     self.assertTrue(u1 != u2)
     self.assertTrue(u1 < u2)
     self.assertTrue(u1 <= u2)
     self.assertFalse(u1 > u2)
     self.assertTrue(u2 > u1)
Esempio n. 11
0
 def test_arithmetic(self):
     u1 = Unit(20, "microliter")
     u2 = Unit(30, "microliter")
     assert Unit(50, "microliter") == u1 + u2
     assert Unit(50, "microliter") == u2 + u1
     assert Unit(-10, "microliter") == u1 - u2
     assert Unit(10, "microliter") == u2 - u1
     assert Unit(600, "microliter") == u2 * u1.magnitude
     assert Unit(600, "microliter") == u2.magnitude * u1
     assert Unit(1.5, "microliter") == u2 / u1.magnitude
     assert Unit(1, "dimensionless") == u2 // u1
Esempio n. 12
0
 def test_dimensional_arithmetic(self):
     u1 = Unit(200, "microliter")
     u2 = Unit(3, "milliliter")
     assert Unit(3200, "microliter") == u1 + u2
     assert Unit(3.2, "milliliter") == u2 + u1
     assert Unit(-2800, "microliter") == u1 - u2
     assert Unit(2.8, "milliliter") == u2 - u1
     assert Unit(0.6, "milliliter**2") == u2 * u1
     assert Unit(600000, "microliter**2") == u1 * u2
     assert Unit(15, "dimensionless") == u2 / u1
     assert Unit(0.066666667, "dimensionless") == (u1 / u2).round(9)
 def test_arithmetic(self):
     u1 = Unit(20, 'microliter')
     u2 = Unit(30, 'microliter')
     assert (Unit(50, 'microliter') == u1 + u2)
     assert (Unit(50, 'microliter') == u2 + u1)
     assert (Unit(-10, 'microliter') == u1 - u2)
     assert (Unit(10, 'microliter') == u2 - u1)
     assert (Unit(600, 'microliter') == u2 * u1._magnitude)
     assert (Unit(600, 'microliter') == u2._magnitude * u1)
     assert (Unit(1.5, 'microliter') == u2 / u1._magnitude)
     assert (Unit(1, 'dimensionless') == u2 // u1)
 def test_dimensional_arithmetic(self):
     u1 = Unit(200, 'microliter')
     u2 = Unit(3, 'milliliter')
     assert Unit(3200, 'microliter') == u1 + u2
     assert Unit(3.2, 'milliliter') == u2 + u1
     assert Unit(-2800, 'microliter') == u1 - u2
     assert Unit(2.8, 'milliliter') == u2 - u1
     assert Unit(0.6, 'milliliter**2') == u2 * u1
     assert Unit(600000, 'microliter**2') == u1 * u2
     assert Unit(15, 'dimensionless') == u2 / u1
     assert Unit(0.066666667, 'dimensionless') == (u1 / u2).round(9)
Esempio n. 15
0
 def test_arithmetic(self):
     u1 = Unit(20, 'microliter')
     u2 = Unit(30, 'microliter')
     self.assertEqual(Unit(50, 'microliter'), u1 + u2)
     self.assertEqual(Unit(50, 'microliter'), u2 + u1)
     self.assertEqual(Unit(-10, 'microliter'), u1 - u2)
     self.assertEqual(Unit(10, 'microliter'), u2 - u1)
     self.assertEqual(Unit(600, 'microliter'), u2 * u1.value)
     self.assertEqual(Unit(1.5, 'microliter'), u2 / u1.value)
     self.assertEqual(Unit(1, 'microliter'), u2 // u1)
Esempio n. 16
0
 def __init__(self, shortname, description):
     """
     Parameters
     ----------
     shortname : str
         A short name for the solution. (e.g. 'buffer')
     description:
         A descriptive name for the solution (e.g. '20 mM Tris 50 mM NaCl')
     """
     self.shortname = shortname
     self.description = description
     self.species = None
     self.concentration = Unit(0.0, 'moles/liter')
     self.uncertainty = Unit(0.0, 'moles/liter')
Esempio n. 17
0
 def test_comparison(self):
     u1 = Unit(20, "microliter")
     u2 = Unit(30, "microliter")
     u3 = Unit(1, "milliliter")
     assert u1 != u2
     assert u1 == Unit(20, "microliter")
     assert u1 != u2
     assert u1 < u2
     assert u1 <= u2
     assert u1 <= u2
     assert u2 > u1
     assert u1 < u2
     assert u2 >= u1
     assert u3 > u1
     assert u2 < u3
 def test_comparison(self):
     u1 = Unit(20, 'microliter')
     u2 = Unit(30, 'microliter')
     u3 = Unit(1, 'milliliter')
     assert (u1 != u2)
     assert (u1 == Unit(20, 'microliter'))
     assert (u1 != u2)
     assert (u1 < u2)
     assert (u1 <= u2)
     assert (u1 <= u2)
     assert (u2 > u1)
     assert (u1 < u2)
     assert (u2 >= u1)
     assert (u3 > u1)
     assert (u2 < u3)
Esempio n. 19
0
 def __init__(self, dmso_stock):
     """
     Parameters
     ----------
     dmso_stock : dict
         The dictionary containing 'id', 'compound_name', 'compound mass (mg)', 'molecular weight', 'purity', 'solvent_mass'
     """
     self.shortname = dmso_stock['id']
     self.species = dmso_stock['compound name']
     self.description = '10 mM ' + dmso_stock['compound name'] + ' DMSO stock'
     dmso_density = Unit(1.1004, 'grams/milliliter')
     mass_uncertainty = 0.01 # TODO: Calculate from balance precision
     concentration = Unit(dmso_stock['compound mass (mg)'], 'milligrams') * dmso_stock['purity'] / Unit(dmso_stock['molecular weight'], 'grams/mole') / (Unit(dmso_stock['solvent mass (g)'], 'grams') / dmso_density) # mol/liter
     self.concentration = concentration.to('moles/liter')
     self.uncertainty = mass_uncertainty * self.concentration
     self.buffer = DMSO
Esempio n. 20
0
def sample_protocol(protocol, params):
    dest_plate = params["destination_plate"]
    wells_to_measure = []

    for location in params["dye_locations"]:
        protocol.transfer(params["dye"],
                          dest_plate.well(location["well_index"]),
                          location["volume"])
        if location["volume"] != Unit(100, "microliter"):
            protocol.transfer(params["water"],
                              dest_plate.well(location["well_index"]),
                              Unit(100, "microliter") - location["volume"],
                              mix_after=True)
        wells_to_measure.append(location["well_index"])

    protocol.absorbance(dest_plate, wells_to_measure, "475:nanometer", "test")
Esempio n. 21
0
def provision_assay_plate(name, plate_type='4titude 4ti-0223', id=None):
    """
    Provision a new assay plate.

    Parameters
    ----------
    name : str
       The name of the container
    plate_type : str, optional, default='4titude 4ti-0223'
       The name of the plate type used to retrieve the `container_type` from library
    id : str, optional, default=None
       Unless `id` is specified, a unique container ID will be autogenerated.
    """
    if id == None:
        id = generate_uuid

    # Define the container
    container_type = container_types[plate_type]
    container = Container(name="assay-plate",
                          id=id,
                          container_type=container_type)

    # Initialize well properties for this container
    for well in container.all_wells():
        well.set_volume(Unit(0.0, 'microliters'))  # well starts empty

    return container
    def measure_absorbance(self, record: paml.ActivityNodeExecution):
        results = {}
        call = record.call.lookup()
        parameter_value_map = call.parameter_value_map()

        wl = parameter_value_map["wavelength"]["value"]
        wl_units = tyto.OM.get_term_by_uri(wl.unit)
        samples = parameter_value_map["samples"]["value"]
        wells = self.var_to_entity[samples]
        measurements = parameter_value_map["measurements"]["value"]

        # HACK extract contrainer from well group since we do not have it as input
        container = wells[0].container

        l.debug(f"measure_absorbance:")
        l.debug(f"  container: {container}")
        l.debug(f"  samples: {samples}")
        l.debug(f"  wavelength: {wl.value} {wl_units}")

        self.protocol.spectrophotometry(
            dataref=measurements,
            obj=container,
            groups=Spectrophotometry.builders.groups([
                Spectrophotometry.builders.group(
                    "absorbance",
                    Spectrophotometry.builders.absorbance_mode_params(
                        wells=wells,
                        wavelength=Unit(wl.value, wl_units),
                        num_flashes=None,
                        settle_time=None,
                        read_position=None,
                        position_z=None))
            ]))
        return results
 def test_single_transfer(self):
     p = Protocol()
     c = p.ref("test", None, "96-flat", discard=True)
     p.transfer(c.well(0), c.well(1), "20:microliter")
     self.assertEqual(Unit(20, "microliter"), c.well(1).volume)
     self.assertEqual(None, c.well(0).volume)
     self.assertTrue("transfer" in p.instructions[-1].groups[-1])
Esempio n. 24
0
    def test_one_source(self):
        p = Protocol()
        c = p.ref("test", None, "96-flat", discard=True)
        with self.assertRaises(RuntimeError):
            p.transfer(c.wells_from(0, 2),
                   c.wells_from(2, 2), "40:microliter", one_source=True)
        with self.assertRaises(RuntimeError):
            p.transfer(c.wells_from(0, 2).set_volume("1:microliter"),
                       c.wells_from(1, 5), "10:microliter", one_source=True)
        p.transfer(c.wells_from(0, 2).set_volume("50:microliter"),
                   c.wells_from(2, 2), "40:microliter", one_source=True)
        self.assertEqual(2, len(p.instructions[0].groups))
        self.assertFalse(p.instructions[0].groups[0]["transfer"][0]["from"] == p.instructions[0].groups[1]["transfer"][0]["from"])
        p.transfer(c.wells_from(0, 2).set_volume("100:microliter"),
                   c.wells_from(2, 4), "40:microliter", one_source=True)
        self.assertEqual(7, len(p.instructions[0].groups))
        self.assertTrue(p.instructions[0].groups[2]["transfer"][0]["from"] == p.instructions[0].groups[4]["transfer"][0]["from"])
        self.assertTrue(p.instructions[0].groups[4]["transfer"][0]["volume"] == Unit.fromstring("20:microliter"))
        p.transfer(c.wells_from(0, 2).set_volume("100:microliter"),
                   c.wells_from(2, 4), ["20:microliter", "40:microliter", "60:microliter", "80:microliter"], one_source=True)
        self.assertEqual(12, len(p.instructions[0].groups))
        self.assertTrue(p.instructions[0].groups[7]["transfer"][0]["from"] == p.instructions[0].groups[9]["transfer"][0]["from"])
        self.assertFalse(p.instructions[0].groups[9]["transfer"][0]["from"] == p.instructions[0].groups[10]["transfer"][0]["from"])
        self.assertEqual(Unit.fromstring("20:microliter"), p.instructions[0].groups[10]["transfer"][0]["volume"])
        p.transfer(c.wells_from(0, 2).set_volume("50:microliter"), c.wells(2), "100:microliter", one_source=True)
        c.well(0).set_volume("50:microliter")
        c.well(1).set_volume("200:microliter")
        p.transfer(c.wells_from(0, 2), c.well(1), "100:microliter", one_source=True)
        self.assertFalse(p.instructions[0].groups[14]["transfer"][0]["from"] == p.instructions[0].groups[15]["transfer"][0]["from"])
        c.well(0).set_volume("100:microliter")
        c.well(1).set_volume("0:microliter")
        c.well(2).set_volume("100:microliter")
        p.transfer(c.wells_from(0, 3), c.wells_from(3, 2), "100:microliter", one_source=True)

        p = Protocol()
        c = p.ref("test", None, "96-flat", discard=True)
        c.well(0).set_volume("100.0000000000005:microliter")
        c.well(1).set_volume("100:microliter")
        p.transfer(c.wells_from(0, 2), c.wells_from(3, 3), "50:microliter", one_source=True)
        self.assertEqual(3, len(p.instructions[0].groups))

        p = Protocol()
        c = p.ref("test", None, "96-flat", discard=True)
        c.well(0).set_volume("50:microliter")
        c.well(1).set_volume("101:microliter")
        p.transfer(c.wells_from(0, 2), c.wells_from(3, 3), "50.0000000000005:microliter", one_source=True)
        self.assertEqual(3, len(p.instructions[0].groups))
 def test_echo_max_vol(self):
     from autoprotocol.protocol import Protocol
     p = Protocol()
     echo_w = p.ref("echo", None, "384-echo", discard=True).well(0)
     echo_w.set_volume("135:microliter")
     assert echo_w.volume == Unit(135, "microliter")
     with pytest.raises(ValueError):
         echo_w.set_volume("136:microliter")
Esempio n. 26
0
def dummy_type():
    return ContainerType(
        name="dummy",
        well_count=15,
        well_depth_mm=None,
        well_volume_ul=Unit(200, "microliter"),
        well_coating=None,
        sterile=False,
        is_tube=False,
        cover_types=[],
        seal_types=None,
        capabilities=[],
        shortname="dummy",
        col_count=5,
        dead_volume_ul=Unit(15, "microliter"),
        safe_min_volume_ul=Unit(30, "microliter")
    )
Esempio n. 27
0
    def _create_extinction_coefficients_model(self):
        """
        Determine all spectroscopic wavelengths in use and create model of extinction coefficients

        Populates the following fields:
        * parameter_names['extinction coefficients'] : all extinction coefficients
        * all_wavelengths : list of all wavelengths in use
        * absorbance : True if absorbance in use
        * fluorescence : True if fluorescence in use

        """
        #
        # Spectroscopic measurements
        #

        # TODO: Switch to log extinction coefficients and uniform prior in log extinction coefficients
        MIN_EXTINCTION_COEFFICIENT = Unit(0.1, '1/(moles/liter)/centimeter') # maximum physically reasonable extinction coefficient
        MAX_EXTINCTION_COEFFICIENT = Unit(100e3, '1/(moles/liter)/centimeter') # maximum physically reasonable extinction coefficient
        EXTINCTION_COEFFICIENT_GUESS = Unit(1.0, '1/(moles/liter)/centimeter') # maximum physically reasonable extinction coefficient

        # Determine all wavelengths and detection technologies in use
        self.all_wavelengths = set()
        for well in self.wells:
            measurements = well.properties['measurements']
            if 'absorbance' in measurements:
                for wavelength in measurements['absorbance'].keys():
                    self.all_wavelengths.add(wavelength)
            if 'fluorescence' in measurements:
                for (excitation_wavelength, emission_wavelength, geometry) in measurements['fluorescence'].keys():
                    self.all_wavelengths.add(excitation_wavelength)
                    self.all_wavelengths.add(emission_wavelength)
        print("all wavelengths in use:")
        print(self.all_wavelengths)

        if (len(self.all_wavelengths) > 0):
            # Priors for spectroscopic parameters of each component
            self.parameter_names['extinction coefficients'] = list()
            for species in self.all_species:
                for wavelength in self.all_wavelengths:
                    name = 'log extinction coefficient of %s at wavelength %s' % (species, wavelength)
                    log_extinction_coefficient = pymc.Uniform(name, lower=np.log(MIN_EXTINCTION_COEFFICIENT.to_base_units().m), upper=np.log(MAX_EXTINCTION_COEFFICIENT.to_base_units().m), value=np.log(EXTINCTION_COEFFICIENT_GUESS.to_base_units().m)) # extinction coefficient or molar absorptivity for ligand, units of 1/M/cm
                    self.model[name] = log_extinction_coefficient
                    self.parameter_names['extinction coefficients'].append(name)
 def test_conversion(self):
     assert (Unit(200, 'centimeter').to('meter') == Unit(2, 'meter'))
     assert (Unit(20, 'microliter/second').to('liter/hour') == Unit(
         0.072, 'liter / hour'))
     # Due to floating point imprecision, use AlmostEqual as the test
     # condition
     assert almost_equal(
         Unit(1000, 'microliter').to('milliliter'), Unit(1, 'milliliter'),
         Unit(10**-12, 'milliliter'))
 def test_multiple_sources(self):
     p = Protocol()
     c = p.ref("test", None, "96-flat", discard=True)
     with self.assertRaises(AssertionError):
         p.consolidate(c.wells_from(0, 3), c.wells_from(2, 3),
                       "10:microliter")
         p.consolidate(c.wells_from(0, 3), c.well(4), ["10:microliter"])
     p.consolidate(c.wells_from(0, 3), c.well(4), "10:microliter")
     self.assertEqual(Unit(30, "microliter"), c.well(4).volume)
     self.assertEqual(
         3, len(p.instructions[0].groups[0]["consolidate"]["from"]))
Esempio n. 30
0
    def test_updates_well_volume(self):
        source_volume = Unit(30, "uL")
        volume = Unit(20, "uL")

        row_count = self.flat.container_type.row_count()
        source_origin = self.flat.well(0)
        source_wells = self.flat.wells_from(
            source_origin, row_count,
            columnwise=True).set_volume(source_volume)
        destination_origin = self.flat.well(1)
        destination_wells = self.flat.wells_from(destination_origin,
                                                 row_count,
                                                 columnwise=True)

        self.p.transfer(source_origin,
                        destination_origin,
                        volume,
                        rows=row_count)
        assert all(_.volume == source_volume - volume for _ in source_wells)
        assert all(_.volume == volume for _ in destination_wells)
Esempio n. 31
0
def dummy_echo():
    return Container(
        None,
        ContainerType(
            name="dummy",
            well_count=384,
            well_depth_mm=None,
            well_volume_ul=Unit(65, "microliter"),
            well_coating=None,
            sterile=False,
            is_tube=False,
            cover_types=[],
            seal_types=None,
            capabilities=[],
            shortname="dummy",
            col_count=96,
            dead_volume_ul=Unit(15, "microliter"),
            safe_min_volume_ul=Unit(15, "microliter"),
            true_max_vol_ul=Unit(135, "microliter"),
            vendor="Labcyte"
        )
    )
Esempio n. 32
0
def define_container_types():
    #
    # Define assay plate container
    #

    container_types = dict()

    # Define the container type for 4titude 4ti-0223.
    # info: http://4ti.co.uk/microplates/black-clear-bottom/96-well/
    # drawing: http://4ti.co.uk/files/1614/0542/7662/4ti-0223_Marketing_Drawing.pdf
    # All arguments to ContainerType are required!
    capabilities = [
        'pipette', 'spin', 'absorbance', 'fluorescence', 'luminescence',
        'incubate', 'gel_separate', 'cover', 'seal', 'stamp', 'dispense'
    ]
    container_type = ContainerType(name='4titude 4ti-0223',
                                   is_tube=False,
                                   well_count=96,
                                   well_depth_mm=Unit(11.15, 'millimeter'),
                                   well_volume_ul=Unit(300, 'microliter'),
                                   well_coating='polystyrene',
                                   sterile=False,
                                   cover_types=[],
                                   seal_types=[],
                                   capabilities=capabilities,
                                   shortname='4ti-0223',
                                   col_count=12,
                                   dead_volume_ul=Unit(20, 'microliter'),
                                   safe_min_volume_ul=Unit(50, 'microliter'))
    # Attach well area.
    well_diameter = Unit(6.30, "millimeters")
    well_area = np.pi * (well_diameter / 2)**2
    setattr(container_type, 'well_area', well_area)

    container_types[container_type.name] = container_type

    return container_types
    def test_units_match(self):
        with pytest.raises(ValueError):
            Unit(20, 'microliter') + Unit(30, 'second')

        with pytest.raises(ValueError):
            Unit(20, 'microliter') - Unit(30, 'second')

        with pytest.raises(ValueError):
            Unit(20, 'microliter') < Unit(30, 'second')
Esempio n. 34
0
def thermocycle_ramp(start_temp, end_temp, total_duration, step_duration):
    '''
        Create a ramp instruction for the thermocyler. Used in annealing protocols.
    '''
    assert Unit.fromstring(total_duration).unit == Unit.fromstring(step_duration).unit, ("Thermocycle_ramp durations"
                                                                                         " must be specified using the"
                                                                                         " same unit of time.")
    thermocycle_steps = []
    start_temp = Unit.fromstring(start_temp).value
    num_steps = int(Unit.fromstring(total_duration).value // Unit.fromstring(step_duration).value)
    step_size = (Unit.fromstring(end_temp).value - start_temp) // num_steps
    for i in xrange(0, num_steps):
        thermocycle_steps.append({
            "temperature": "%d:celsius" % (start_temp + i * step_size), "duration": step_duration
            })
    return thermocycle_steps
Esempio n. 35
0
 def test_fromstring(self):
     self.assertEqual(Unit.fromstring("20:microliter"), Unit(20,'microliter'))
def volume_check(well, usage_volume=0, use_safe_vol=False,
                 use_safe_dead_diff=False):
    """Basic Volume check

    Checks to see if the designated well has usage_volume above the well's
    dead volume. In other words, this method checks if usage_volume can be
    pipetted out of well.

    Example Usage:

    .. code-block:: python

            from autoprotocol import Protocol
            from autoprotocol_utilities.container_helpers import volume_check

            p = Protocol()
            example_container = p.ref(name="exampleplate", id=None,
                                      cont_type="96-pcr", storage="warm_37")
            p.dispense(ref=example_container, reagent="water",
                       columns=[{"column": 0, "volume": "10:microliters"}])

            #Checks if there are 5 microliters above the dead volume
            #available in well 0
            assert (volume_check(well=example_container.well(0),
                    usage_volume=5)) is None
            #Checks if the volume in well 0 is at least the safe minimum volume
            assert (volume_check(well=example_container.well(0),
                    usage_volume=0, use_safe_vol=True) is None

    Parameters
    ----------
    well : Well, WellGroup, list
        Well(s) to test
    usage_volume : Unit, str, int, float, optional
        Volume to test for. If 0 the aliquot will be tested against the
        container dead volume. If int or float is used, microliter will be
        assumed.
    use_safe_vol : bool, optional
        Use safe minimum volume instead of dead volume
    use_safe_dead_diff : bool, optional
        Use the safe_minimum_volume - dead_volume as the required amount.
        Useful if `set_pipettable_volume()` was used before to correct the
        well_volume to not include the dead_volume anymore

    Returns
    -------
    str
        string of errors if volume check failed OR
    None
        If no errors are detected

    Raises
    ------
    ValueError
        If well is not of type Well, list or WellGroup
    ValueError
        If elements of well are not of type Well

    """

    assert isinstance(well, (Well, WellGroup, list))
    if isinstance(well, Well):
        well = [well]

    error_message = []
    # noinspection PyTypeChecker
    for aliquot in well:
        assert isinstance(aliquot, Well)
        if isinstance(usage_volume, (int, float)):
            usage_volume = Unit(usage_volume, "microliter")
        if isinstance(usage_volume, string_type):
            usage_volume = int(usage_volume.split(":microliter")[0])
        if not aliquot.volume:
            error_message.append(
                "Your aliquot does not have a volume. (%s) We assume 0 uL "
                "for this test." % aliquot)

        correction_vol = aliquot.container.container_type.dead_volume_ul
        message_string = "dead volume"
        volume = Unit(0, "microliter")
        if aliquot.volume:
            volume = aliquot.volume
        if use_safe_vol:
            correction_vol = \
                aliquot.container.container_type.safe_min_volume_ul
            message_string = "safe minimum volume"
        elif use_safe_dead_diff:
            correction_vol = \
                aliquot.container.container_type.safe_min_volume_ul - \
                aliquot.container.container_type.dead_volume_ul
            message_string = "safe minimum volume"
            volume = volume + aliquot.container.container_type.dead_volume_ul
        test_vol = correction_vol + usage_volume

        if test_vol > volume:
            if usage_volume == 0:
                error_message.append(
                    "You want to pipette from a container with {:~P} {!s}. "
                    "However, your aliquot: {!s}, only has {:~P}.".format(
                        correction_vol, message_string,
                        well_name(aliquot), volume))
            else:
                error_message.append(
                    "You want to pipette {:~P} from a container with {:~P} "
                    "{!s} ({:~P} total). However, your aliquot: {!s}, only has"
                    " {:~P}.".format(
                        usage_volume, correction_vol, message_string,
                        usage_volume + correction_vol,
                        well_name(aliquot), volume))
    if error_message:
        error_message = str(len(error_message)) + " volume errors: " + \
            ", ".join(error_message)
    else:
        error_message = None
    return error_message
def thermocycle_ramp(start_temp, end_temp, total_duration, step_duration):
    """Create a ramp instruction for the thermocyler.

    Create a multi-temperature thermocycling program commonly used in
    annealing protocols. Based on total time and the step duration this
    function computes the temperature increment required for each step within
    the start and the end temperature.

    Parameters
    ----------
    start_temp: string, int, float, Unit
        Start of the thermocycle protocol, in the format "37:celsius"
    end_temp: string, int, float, Unit
        End of the thermocycle protocol, in the format "37:celsius"
    total_duration: string, Unit
        Total duration of the thermocycle protocol, in the format "1:hour"
    step_duration: string, Unit
        Time that each temperature should be held, in the format "1:minute"


    Example
    -------

    .. code-block:: python

        therm = thermocycle_ramp(65, 95, "30:minute", "1:minute")
        protocol.thermocycle(dest_plate,
                             therm,
                             volume="15:microliter")


    Returns
    -------
    dict
        containing thermocycling steps that can be used in the
        thermocycle instruction

    Raises
    ------
    ValueError
        If either temperature is not of type `int`, `float`, `string` or
        `Unit` and if either duration is not of type `string` or `Unit`

    """
    assert isinstance(start_temp, (int, float, string_type, Unit))
    assert isinstance(end_temp, (int, float, string_type, Unit))
    assert isinstance(total_duration, (string_type, Unit))
    assert isinstance(step_duration, (string_type, Unit))

    if isinstance(start_temp, string_type):
        start_temp = Unit.fromstring(start_temp)
    elif isinstance(start_temp, (int, float)):
        start_temp = Unit(start_temp, 'degC')
    if isinstance(end_temp, string_type):
        end_temp = Unit.fromstring(end_temp)
    elif isinstance(end_temp, (int, float)):
        end_temp = Unit(end_temp, 'degC')
    if isinstance(total_duration, string_type):
        total_duration = Unit.fromstring(total_duration)
    if isinstance(step_duration, string_type):
        step_duration = Unit.fromstring(step_duration)

    start_temp.ito('degC')
    end_temp.ito('degC')
    total_duration.ito_base_units()
    step_duration.ito_base_units()

    num_steps = int(total_duration // step_duration)
    step_size = (end_temp - start_temp).magnitude // num_steps

    thermocycle_steps = []
    for i in range(num_steps + 1):
        thermocycle_steps.append({
            "temperature": "%s:celsius" % (
                start_temp.magnitude + i * step_size),
            "duration": str(step_duration)
        })
    return thermocycle_steps
def molar_to_mass_conc(length, molar, ds=True):
    """
    For the DNA molarity given, return the mass concentration of DNA

    Example Usage:

    .. code-block:: python

        from autoprotocol_utilities import molar_to_mass_conc
        from autoprotocol_utilities import dna_mole_to_mass
        from autoprotocol.unit import Unit

        dna_length = 5000
        dna_molarity = Unit(10, 'uM')
        molar_to_mass_conc(dna_length, dna_molarity)

    Returns:

    .. code-block:: python

        Unit(33000.0, 'nanogram / microliter')

    Parameters
    ----------
    length: int
        Length of DNA in bp
    molar: str, Unit
        Molarity of DNA in prefix-M
    ds: bool, optional
        True for dsDNA, False for ssDNA

    Returns
    -------
    mass_conc: Unit
        Mass concentration of DNA in ng/uL

    Raises
    ------
    ValueError
        If inputs are not of specified types

    """
    if not isinstance(length, int):
        raise ValueError(
            "Length of DNA is of type %s, must be of type "
            "integer" % type(length))

    if isinstance(molar, str):
        molar = Unit.fromstring(molar)

    if not (isinstance(molar, Unit) and
            str(molar.dimensionality) == '[substance] / [length] ** 3'):
        raise ValueError(
            "Molar concentration of DNA must be of type string or Unit")

    if not isinstance(ds, bool):
        raise ValueError(
            "ds is of type %s, must be of type bool: True for dsDNA, "
            "False for ssDNA" % type(ds))

    dna_umole = Unit((molar / Unit(1, "M")).magnitude, "umol")
    dna_ug = dna_mole_to_mass(length, dna_umole, ds)
    mass_conc = Unit(dna_ug.magnitude * 1000, "ng/uL")

    return mass_conc
def mass_conc_to_molar(length, mass_conc, ds=True):
    """
    For the DNA mass concentration given, return the molarity of DNA

    Example Usage:

    .. code-block:: python

        from autoprotocol_utilities import mass_conc_to_molar
        from autoprotocol_utilities import dna_mass_to_mole
        from autoprotocol.unit import Unit

        dna_length = 5000
        dna_mass_conc = Unit(33, 'ng/uL')
        mass_conc_to_molar(dna_length, dna_mass_conc)

    Returns:

    .. code-block:: python

        Unit(0.01, 'micromolar')

    Parameters
    ----------
    length: int
        Length of DNA in bp
    mass_conc: str, Unit
        Mass concentration of DNA
    ds: bool, optional
        True for dsDNA, False for ssDNA

    Returns
    -------
    molar: Unit
        Molarity of DNA in uM

    Raises
    ------
    ValueError
        If inputs are not of specified types

    """
    if not isinstance(length, int):
        raise ValueError(
            "Length of DNA is of type %s, must be of type "
            "integer" % type(length))

    if isinstance(mass_conc, str):
        mass_conc = Unit.fromstring(mass_conc)

    if not isinstance(mass_conc, Unit) or \
            str(mass_conc.dimensionality) != '[mass] / [length] ** 3':
        raise ValueError("Mass concentration of DNA must be of type Unit")

    if not isinstance(ds, bool):
        raise ValueError(
            "ds is of type %s, must be of type bool: True for dsDNA, "
            "False for ssDNA" % type(ds))

    dna_ng = Unit((mass_conc / Unit(1, "ng/uL")).magnitude, "ng")
    dna_pmol = dna_mass_to_mole(length, dna_ng, ds)
    dna_molar = Unit(round(dna_pmol.magnitude, 9), "uM")

    return dna_molar
def ligation_insert_ng(plasmid_size,  plasmid_mass,
                       insert_size, molar_ratio=1):
    """
    For the plasmid size, plasmid amount, insert size, and molar ratio given,
    return the mass of insert needed for ligation

    Different from ligation_insert_volume: no insert concentration is
    given -> returns mass of insert needed

    Example Usage:

    .. code-block:: python

        from autoprotocol_utilities import ligation_insert_ng
        from autoprotocol.unit import Unit

        plasmid_size = 3000
        plasmid_mass = Unit(100, 'ng')
        insert_size = 48
        ligation_insert_ng(plasmid_size, plasmid_mass, insert_size)

    Returns:

    .. code-block:: python

        Unit(1.6, 'nanogram')

    Parameters
    ----------
    plasmid_size : int
        Length of plasmid in bp.
    insert_size: int
        Length of insert in bp
    plasmid_mass : str, Unit
        Mass of plasmid in prefix-g
    molar_ratio : int, float, string, optional
        Ligation molar ratio of insert : vector.  By default it is 1 : 1.
        Generally ligations are tested at 1:3, 1:1, and 3:1

    Returns
    -------
    insert_amount: Unit
        Amount of insert solution needed in ng

    Raises
    ------
    ValueError
        If wells are not of type list, WellGroup or Container

    """

    # Check input types
    if not isinstance(plasmid_size, int):
        raise ValueError("Plasmid_size: must be an integer")

    if not isinstance(insert_size, int):
        raise ValueError("insert_size: must be an integer")

    if type(molar_ratio) == str:
        molar_ratio = float(
            molar_ratio.split(":")[0]) / float(molar_ratio.split(":")[1])

    if type(molar_ratio) not in (int, float):
        raise ValueError(
            "molar_ratio: must be an int, float, or string in the form "
            "of int:int")

    if isinstance(plasmid_mass, str):
        plasmid_mass = Unit.fromstring(plasmid_mass)

    if not (isinstance(plasmid_mass, Unit) and
            str(plasmid_mass.dimensionality) == "[mass]"):
        raise ValueError(
            "Plasmid amount must be of type str or Unit in prefix-g")

    length_ratio = float(insert_size) / float(plasmid_size)
    plasmid_ng = plasmid_mass.to("ng")
    insert_ng = plasmid_ng * length_ratio * molar_ratio

    return insert_ng
def dna_mass_to_mole(length, mass, ds=True):
    """
    For the DNA Length and mass given, return the mole amount of DNA

    Example Usage:

    .. code-block:: python

        from autoprotocol_utilities import dna_mass_to_mole
        from autoprotocol.unit import Unit

        dna_length = 100
        dna_mass = Unit(33, 'ng')
        dna_mass_to_mole(dna_length, dna_mass)

    Returns:

    .. code-block:: python

        Unit(0.5, 'picomole')

    Parameters
    ----------
    length: int
        Length of DNA in bp
    mass: str, Unit
        Weight of DNA in prefix-g
    ds: bool, optional
        True for dsDNA, False for ssDNA

    Returns
    -------
    pmole_dna: Unit
        Mole amount of DNA in pmol

    Raises
    ------
    ValueError
        If inputs are not of specified types

    """
    if isinstance(mass, str):
        mass = Unit.fromstring(mass)

    if not isinstance(mass, Unit) or str(mass.dimensionality) != "[mass]":
        raise ValueError("Mass of DNA must be of type Unit in prefix-gram")

    if not isinstance(length, int):
        raise ValueError(
            "Length of DNA is of type %s, must be of type "
            "integer" % type(length))

    if not isinstance(ds, bool):
        raise ValueError(
            "ds is of type %s, must be of type bool: True for dsDNA, "
            "False for ssDNA" % type(ds))

    dna_pg = mass.to("pg")

    if ds:
        dna_pmol = dna_pg / (Unit(660, "pg/pmol") * length)
    else:
        dna_pmol = dna_pg / (Unit(330, "pg/pmol") * length)

    return dna_pmol
def ligation_insert_volume(plasmid_size,  plasmid_mass, insert_size,
                           insert_conc, ds=True, molar_ratio=1):
    """
    For the plasmid size, plasmid amount, insert size, insert concentration,
    and molar ratio given, return the volume of insert solution needed for
    ligation

    Different from ligation_insert_ng: insert concentration is given -> returns
    volume of insert solution needed

    Example Usage:

    .. code-block:: python

        from autoprotocol_utilities import ligation_insert_volume
        from autoprotocol_utilities import molar_to_mass_conc
        from autoprotocol.unit import Unit

        plasmid_size = 3000
        plasmid_mass = Unit(100, 'ng')
        insert_size = 48
        insert_conc = Unit(25, 'ng/uL')
        ligation_insert_volume(plasmid_size, plasmid_mass, insert_size,
                               insert_conc)

    Returns:

    .. code-block:: python

        Unit(0.064, 'microliter')

    Parameters
    ----------
    plasmid_size : int
        Length of plasmid in bp.
    plasmid_mass : str, Unit
        Mass of plasmid in prefix-g
    insert_size: int
        Length of insert in bp
    insert_conc: str, Unit
        Molar or mass concentration of insert
    ds: bool, optional
        True for dsDNA, False for ssDNA
    molar_ratio : int, float, string, optional
        Ligation molar ratio of insert : vector.
        Common ratios are 1:3, 1:1, and 3:1. 1:1 by default

    Returns
    -------
    insert_amount: Unit
        Volume of insert solution needed in uL

    Raises
    ------
    ValueError
        If wells are not of type list, WellGroup or Container

    """

    conc_dimension = ["[substance] / [length] ** 3", '[mass] / [length] ** 3']

    # Check input types
    if not isinstance(plasmid_size, int):
        raise ValueError("Plasmid_size: must be an integer")

    if isinstance(plasmid_mass, str):
        plasmid_mass = Unit.fromstring(plasmid_mass)

    if not isinstance(plasmid_mass, Unit) and \
            str(plasmid_mass.dimensionality) == "[mass]":
        raise ValueError(
            "Plasmid mass must be of type str or Unit in prefix-g")

    if not isinstance(insert_size, int):
        raise ValueError("insert_size: must be an integer")

    if isinstance(insert_conc, str):
        insert_conc = Unit.fromstring(insert_conc)

    if not (isinstance(insert_conc, Unit) and
            str(insert_conc.dimensionality) in conc_dimension):
        raise ValueError(
            "Plasmid concentration must be of type Unit in prefix-M or "
            "prefix-g / prefix-L ")

    if not isinstance(ds, bool):
        raise ValueError(
            "ds is of type %s, must be of type bool: True for dsDNA, "
            "False for ssDNA" % type(ds))

    if type(molar_ratio) == str:
        molar_ratio = float(
            molar_ratio.split(":")[0]) / float(molar_ratio.split(":")[1])

    if type(molar_ratio) not in (int, float):
        raise ValueError(
            "molar_ratio: must be an int, float, or string in the "
            "form of int:int")

    len_ratio = float(insert_size) / float(plasmid_size)
    plasmid_ng = plasmid_mass.to("ng")
    insert_ng = plasmid_ng * len_ratio * molar_ratio

    # Convert concentration to ng/uL
    if str(insert_conc.dimensionality) == conc_dimension[0]:
        insert_conc = molar_to_mass_conc(insert_size, insert_conc, ds)

    else:
        insert_conc = insert_conc.to("ng/uL")

    insert_vol = insert_ng / insert_conc

    return insert_vol
def ligation_insert_amount(plasmid_size, plasmid_conc, plasmid_volume,
                           insert_size, insert_conc, ds=True, molar_ratio=1):
    """
    For the plasmid size, plasmid concentration, insert size, insert
    concentration, and molar ratio given,
    return the volume of insert solution needed for ligation

    Different form ligation_insert_volume: plasmid concentration and volume
    are given instead of plasmid mass

    Example Usage:

    .. code-block:: python

        from autoprotocol_utilities import ligation_insert_amount
        from autoprotocol_utilities import molar_to_mass_conc
        from autoprotocol.unit import Unit

        plasmid_size = 2000
        plasmid_conc = '1.5:uM'
        plasmid_volume = Unit(10, 'uL')
        insert_size = 25
        insert_conc = Unit(10, 'ng/uL')
        ligation_insert_amount(plasmid_size, plasmid_conc, plasmid_volume,
                               insert_size, insert_conc)
    Returns:

    .. code-block:: python

        Unit(24.75, 'microliter')

    Parameters
    ----------
    plasmid_size : int
        Length of plasmid in bp.
    plasmid_conc : str, Unit
        Molar or mass concentration of plasmid solution
    plasmid_volume: str, Unit
        Volume of plasmid solution in prefix-L
    insert_size: int
        Length of insert in bp
    insert_conc : str, Unit
        Molar or mass concentration of insert solution
    ds: bool, optional
        True for dsDNA, False for ssDNA
    molar_ratio : int, float, string, optional
        Ligation molar ratio of insert : vector.
        Common ratios are 1:3, 1:1, and 3:1. 1:1 by default

    Returns
    -------
    insert_amount: Unit
        Volume of insert solution in uL

    Raises
    ------
    ValueError
        If wells are not of type list, WellGroup or Container

    """
    # Check input types
    if not isinstance(plasmid_size, int):
        raise ValueError("Plasmid_size: must be an integer")

    if not isinstance(insert_size, int):
        raise ValueError("insert_size: must be an integer")

    if isinstance(plasmid_volume, str):
        plasmid_volume = Unit.fromstring(plasmid_volume)
    if not isinstance(plasmid_volume, Unit) or \
            str(plasmid_volume.dimensionality) != "[length] ** 3":
        raise ValueError(
            "Volume of plasmid solution must be of type str or Unit")

    conc_dimension = ["[substance] / [length] ** 3", '[mass] / [length] ** 3']
    conc = [plasmid_conc, insert_conc]
    size = [plasmid_size, insert_size]
    for i in range(0, 2):
        if isinstance(conc[i], str):
            conc[i] = Unit.fromstring(conc[i])
        if (isinstance(conc[i], Unit) and
                str(conc[i].dimensionality) in conc_dimension):
            # Convert all concentrations to ng/uL
            if str(conc[i].dimensionality) == conc_dimension[0]:
                conc[i] = molar_to_mass_conc(size[i], conc[i], ds)
            else:
                conc[i] = conc[i].to("ng/uL")
        else:
            raise ValueError(
                "Concentration must be of type string or Unit ")

    if not isinstance(ds, bool):
        raise ValueError(
            "ds is of type %s, must be of type bool: True for dsDNA, "
            "False for ssDNA" % type(ds))

    if type(molar_ratio) == str:
        molar_ratio = float(
            molar_ratio.split(":")[0]) / float(molar_ratio.split(":")[1])

    if type(molar_ratio) not in (int, float):
        raise ValueError(
            "molar_ratio: must be an int, float, or string in the "
            "form of int:int")

    plasmid_conc = conc[0]
    insert_conc = conc[1]
    # Convert input volume to uL
    plasmid_uL = Unit((plasmid_volume / Unit(1, "uL")).magnitude, "uL")
    len_ratio = float(insert_size) / float(plasmid_size)
    plasmid_ng = plasmid_conc * plasmid_uL
    insert_ng = plasmid_ng * len_ratio * molar_ratio
    insert_amount = insert_ng / insert_conc

    return insert_amount
def dna_mole_to_mass(length, mole, ds=True):
    """
    For the DNA Length and mole amount given, return the mass of DNA

    Example Usage:

    .. code-block:: python

        from autoprotocol_utilities import dna_mole_to_mass
        from autoprotocol.unit import Unit

        dna_length = 5000
        dna_mole = "10:pmol"
        dna_mole_to_mass(dna_length, dna_mole)

    Returns:

    .. code-block:: python

        Unit(33.0, 'microgram')

    Parameters
    ----------
    length: int
        Length of DNA in bp
    mole: str, Unit
        Mole amount of DNA in prefix-mol
    ds: bool, optional
        True for dsDNA, False for ssDNA

    Returns
    -------
    dna_ug: Unit
        Weight of DNA in ug

    Raises
    ------
    ValueError
        If inputs are not of specified types

    """
    if isinstance(mole, str):
        mole = Unit.fromstring(mole)

    if not isinstance(mole, Unit) or str(mole.dimensionality) != "[substance]":
        raise ValueError(
            "Mole amount of DNA must be of type Unit in prefix-mol")

    if not isinstance(length, int):
        raise ValueError(
            "Length of DNA is of type %s, must be of type "
            "integer" % type(length))

    if not isinstance(ds, bool):
        raise ValueError(
            "ds is of type %s, must be of type bool: True for dsDNA, "
            "False for ssDNA" % type(ds))

    dna_pmol = mole.to("pmol")

    if ds:
        dna_ug = (
            Unit(660, "pg/pmol") * dna_pmol * Unit(10**(-6), "ug/pg") * length)
    else:
        dna_ug = (
            Unit(330, "pg/pmol") * dna_pmol * Unit(10**(-6), "ug/pg") * length)

    return dna_ug