Esempio n. 1
0
    def test_extend_from_module(self):
        # Custom unit is not known initially
        with self.assertRaises(UnableToParseUnits):
            unit_parser.parse_unit("custom_unit", suppress_unknown=False)

        unit_parser.parser.extend(sample_units)

        # Once parser is extended, the unit can be parsed
        expected_unit = sample_units.custom_unit
        self.assertEqual(unit_parser.parse_unit("custom_unit"), expected_unit)
        self.assertEqual(unit_parser.parse_unit("cuwl"), expected_unit)
Esempio n. 2
0
def volumetric_CV_flow_rate_to_volumetric_flow_rate(vol_cv_flow_rate,
                                                    column,
                                                    to_unit=None):
    """ Convert a volumetric flow rate using the CV unit to a physical unit.

    Parameters
    ----------
    vol_cv_flow_rate : UnitScalar
        Fow rate in column volume unit CV per unit time.

    column : Column
        Column object the flow happens in.

    to_unit : str or Unit
        Unit of the result flow rate.

    Returns
    -------
    float
        Flow rate in SI compatible unit.
    """
    if to_unit is None:
        to_unit = m**3 / second
    elif isinstance(to_unit, basestring):
        to_unit = unit_parser.parse_unit(to_unit)

    si_flow_rate = vol_cv_flow_rate * column.volume
    return chr_units.convert_units(si_flow_rate, tgt_unit=to_unit)
Esempio n. 3
0
    def test_strict_units_trait(self):
        obj = UnitsStrict(units='km')
        self.assertFalse(obj is None)
        self.assertEqual(obj.units.label, 'km')

        obj.units = 'm/sec**2'
        self.assertFalse(obj is None)
        self.assertEqual(obj.units.label, 'm/sec**2')
        self.assertEqual(obj.units.derivation, (1, 0, -2, 0, 0, 0, 0))

        self.assertRaises(TraitError, setattr, obj, 'units', 'invalid')

        self.assertFalse(obj is None)
        self.assertEqual(obj.units.label, 'm/sec**2')
        self.assertEqual(obj.units.derivation, (1, 0, -2, 0, 0, 0, 0))

        units = unit_parser.parse_unit('g/cc')
        self.assertFalse(units is None)
        self.assertEqual(units.label, 'g/cc')
        self.assertNotEqual(units.derivation, dimensionless.derivation)

        obj.units = units
        self.assertTrue(obj.units is units)

        return
Esempio n. 4
0
    def test_strict_units_trait(self):
        obj = UnitsStrict(units='km')
        self.failIf(obj is None)
        self.failUnlessEqual(obj.units.label, 'km')

        obj.units = 'm/sec**2'
        self.failIf(obj is None)
        self.failUnlessEqual(obj.units.label, 'm/sec**2')
        self.failUnlessEqual(obj.units.derivation, (1, 0, -2, 0, 0, 0, 0))

        self.failUnlessRaises(TraitError, setattr, obj, 'units', 'invalid')

        self.failIf(obj is None)
        self.failUnlessEqual(obj.units.label, 'm/sec**2')
        self.failUnlessEqual(obj.units.derivation, (1, 0, -2, 0, 0, 0, 0))

        units = unit_parser.parse_unit('g/cc')
        self.failIf(units is None)
        self.failUnlessEqual(units.label, 'g/cc')
        self.failIfEqual(units.derivation, dimensionless.derivation)

        obj.units = units
        self.failUnless(obj.units is units)

        return
Esempio n. 5
0
    def test_parse_absorption_units(self):
        # add custom units to the parser.
        initialize_unit_parser()

        # check we can parse based on the unit name
        u1 = unit_parser.parse_unit('absorption_unit', suppress_unknown=False)
        self.assertEqual(u1.label, 'absorption_unit')

        # check we can parse based on the unit label
        u2 = unit_parser.parse_unit('AU', suppress_unknown=False)
        self.assertEqual(u2.label, 'AU')

        # check the two units mean the same thing (except labels)
        self.assertEqual(u1.derivation, u2.derivation)
        self.assertEqual(u1.offset, u2.offset)
        self.assertEqual(u1.value, u2.value)
 def test_calculate_component_concentrations(self):
     comp_absorb_data = {
         "Acidic_1_Sim": self.data,
         "Acidic_2_Sim": self.data,
         "Native_Sim": self.data
     }
     comps = [Prod001_comp1, Prod001_comp2, Prod001_comp3]
     product = Product(product_components=comps, **PRODUCT_DATA)
     start_collect_idx, stop_collect_idx = 1, 3
     comp_conc = calculate_component_concentrations(product,
                                                    comp_absorb_data,
                                                    start_collect_idx,
                                                    stop_collect_idx)
     self.assertIsInstance(comp_conc, UnitArray)
     self.assertEqual(comp_conc.units, unit_parser.parse_unit("g/liter"))
     # All the same value since all the same XYdata
     for concentration in comp_conc:
         self.assertEqual(concentration, comp_conc[0])
def convert_units(unitted_data, tgt_unit, **kwargs):
    """ Convert unitted data to the units specified by `tgt_unit`.

    Parameters
    ----------
    unitted_data : UnitScalar or UnitArray
        The data to be converted to `tgt_unit`

    tgt_unit : scimath.unit or str
        The target units for `unitted_data`.

    kwargs : dict
        Additional arguments that may be needed by custom converters for
        special units.

    Returns
    -------
    UnitScalar or UnitArray
        The converted data.
    """
    if isinstance(tgt_unit, basestring):
        tgt_unit = unit_parser.parse_unit(tgt_unit)

    if isinstance(unitted_data, UnitScalar):
        unit_klass = UnitScalar
    elif isinstance(unitted_data, UnitArray):
        unit_klass = UnitArray
    else:
        msg = "The `unitted_data` argument must be an instance of either " \
              "scimath's UnitScalar or UnitArray but got {}."
        msg = msg.format(unitted_data.__class__.__name__)
        logger.exception(msg)
        raise ValueError(msg)

    src_unit = unitted_data.units
    if (src_unit.label, tgt_unit.label) in CUSTOM_UNITS_CONVERTERS:
        converter = CUSTOM_UNITS_CONVERTERS[(src_unit.label, tgt_unit.label)]
        unitted_data = converter(unitted_data, **kwargs)
        return unit_klass(unitted_data, units=tgt_unit)

    else:
        data = np.array(unitted_data.tolist())
        data = convert(data, src_unit, tgt_unit)
        return unit_klass(data, units=tgt_unit)
def build_flow_rate_array(times, experiment, to_unit="liter/minute"):
    """ Build array of flow rates in liter/min at each time of 'times' array.

    Parameters
    ----------
    times : numpy.array
        Array of chromatogram times at which to extract the flow rates.

    experiment : Experiment
        Experiment to extract the flow rates from.

    to_unit : str
        Unit of the output.

    Returns
    -------
    UnitArray
        Array of flow rates at the times of the times array.
    """
    method_steps = experiment.method.method_steps
    step_boundary_times = experiment.method_step_boundary_times

    flow_rates = ones(times.shape) * nan
    for i, step in enumerate(method_steps):
        step_start_time = step_boundary_times[i]
        step_stop_time = step_boundary_times[i+1]
        mask = (times >= step_start_time) & (times < step_stop_time)
        if is_linear_flow_rate(step.flow_rate):
            diam = experiment.column.column_type.diameter
            flow_rate = linear_flow_rate_to_volumetric(step.flow_rate, diam,
                                                       to_unit=to_unit)
        elif is_volumetric_flow_rate(step.flow_rate):
            flow_rate = convert(step.flow_rate, from_unit=step.flow_rate.units,
                                to_unit=unit_parser.parse_unit(to_unit))
        else:
            flow_rate = volumetric_CV_flow_rate_to_volumetric_flow_rate(
                step.flow_rate, experiment.column, to_unit=to_unit
            )

        flow_rates[mask] = float(flow_rate)

    return UnitArray(flow_rates, units=to_unit)
Esempio n. 9
0
    def test_units_trait(self):
        obj = UnitsNonStrict(units='km')
        self.assertFalse(obj is None)
        self.assertEqual(obj.units.label, 'km')

        obj.units = 'm/sec**2'
        self.assertFalse(obj is None)
        self.assertEqual(obj.units.label, 'm/sec**2')

        obj.units = 'invalid'
        self.assertFalse(obj is None)
        self.assertEqual(obj.units.label, 'invalid')
        self.assertEqual(obj.units.derivation, dimensionless.derivation)

        units = unit_parser.parse_unit('g/cc')
        self.assertFalse(units is None)
        self.assertEqual(units.label, 'g/cc')
        self.assertNotEqual(units.derivation, dimensionless.derivation)

        obj.units = units
        self.assertTrue(obj.units is units)

        return
Esempio n. 10
0
    def test_units_trait(self):
        obj = UnitsNonStrict(units='km')
        self.failIf(obj is None)
        self.failUnlessEqual(obj.units.label, 'km')

        obj.units = 'm/sec**2'
        self.failIf(obj is None)
        self.failUnlessEqual(obj.units.label, 'm/sec**2')

        obj.units = 'invalid'
        self.failIf(obj is None)
        self.failUnlessEqual(obj.units.label, 'invalid')
        self.failUnlessEqual(obj.units.derivation, dimensionless.derivation)

        units = unit_parser.parse_unit('g/cc')
        self.failIf(units is None)
        self.failUnlessEqual(units.label, 'g/cc')
        self.failIfEqual(units.derivation, dimensionless.derivation)

        obj.units = units
        self.failUnless(obj.units is units)

        return
def compute_mass_from_abs_data(absorb_data, ext_coeff, experim, t_start=None,
                               t_stop=None, t_start_idx=None, t_stop_idx=None):
    """ Compute total mass of a product component between start and stop times.

    The total mass is computed by integrating the specified chromatogram,
    between t_start and t_stop and using the specified extinction coefficient
    and flow rate at each time.

    Parameters
    ----------
    absorb_data : XYData
        Data (fraction or continuous) to integrate to compute the contained
        mass.

    ext_coeff : UnitScalar
        Extinction coefficient to use to convert the absorbance to a product
        concentration.

    experim : Experiment
        Experiment from which to extract the method (and therefore flow rate)
        information and the system's path length.

    t_start : UnitScalar
        Time at which to start integrating, in minutes. Leave as None to use
        the t_start_idx to specify the time range to integrate.

    t_stop : UnitScalar
        Time at which to stop integrating, in minutes. Leave as None to use
        the t_stop_idx to specify the time range to integrate.

    t_start_idx : Int or None
        Index in the x_data to start integrating at (inclusive).

    t_stop_idx : Int or None
        Index in the x_data to stop integrating at (exclusive). Leave as None
        to go all the way to the end.

    Returns
    -------
    UnitScalar
        Product mass, in grams, estimated to elute between t_start and t_stop.
    """
    all_x_data = absorb_data.x_data
    all_y_data = absorb_data.y_data

    # Convert time inputs into minutes:
    data_time_unit = unit_parser.parse_unit(absorb_data.x_metadata["units"])
    all_x_data = convert(all_x_data, from_unit=data_time_unit, to_unit=minute)

    if t_start is not None and t_stop is not None:
        t_start = convert_units(t_start, tgt_unit=minute)
        t_stop = convert_units(t_stop, tgt_unit=minute)

        t_start_idx = searchsorted(all_x_data, t_start)
        t_stop_idx = searchsorted(all_x_data, t_stop)
        if t_start_idx == t_stop_idx:
            msg = "Unable to compute the integral of the provided because" \
                  "t_start too close to t_stop."
            logger.warning(msg)
            return UnitScalar(0., units="gram")

    collect_idx = slice(t_start_idx, t_stop_idx)
    times = all_x_data[collect_idx]
    absorbances = all_y_data[collect_idx]

    #: Extract the flow rate from the experiment method:
    flow_rates = build_flow_rate_array(times, experim, to_unit="liter/minute")
    missing_flow_rates = where(isnan(flow_rates))[0]
    if len(missing_flow_rates) > 0:
        msg = "The time range requested to integrate results goes beyond the "\
              "known method steps, and will need to be cropped by {} values." \
              " Cropped values are {}.".format(len(missing_flow_rates),
                                               missing_flow_rates)
        logger.warning(msg)
        t_stop_idx = missing_flow_rates[0]
        collect_idx = slice(t_start_idx, t_stop_idx)
        times = all_x_data[collect_idx]
        absorbances = all_y_data[collect_idx]
        flow_rates = flow_rates[collect_idx]

    # Turn absorbances into AU/cm
    path_length = convert_units(experim.system.abs_path_length, "cm")[()]
    data_absorb_unit = unit_parser.parse_unit(absorb_data.y_metadata["units"])
    absorbances_au = convert(absorbances, from_unit=data_absorb_unit,
                             to_unit=absorption_unit)
    # Compute masses in grams
    masses = (absorbances_au*array(flow_rates)) / (path_length*ext_coeff[()])
    total_mass = trapz(masses, times)
    return UnitScalar(total_mass, units="gram")
Esempio n. 12
0
def units_compatible(u0, u1):
    return (unit_parser.parse_unit(u0).derivation ==
            unit_parser.parse_unit(u1).derivation)
Esempio n. 13
0
def unit_string_is_valid(units):
    return unit_parser.parse_unit(units).valid
Esempio n. 14
0
    def __parse_expr(self, node):
        """
        helper function for get_variable that recursively evaluates the AST tree
        based on: https://stackoverflow.com/a/9558001.
        """
        if node is None:
            return None

        elif isinstance(node, ast.Num):
            return node.n

        elif isinstance(node, ast.Str):
            return node.s

        elif isinstance(node, ast.Constant):
            return node.val

        # look for name in variable dictionary
        elif isinstance(node, ast.Name):
            # check if it is a unit
            val = unit_parser.parse_unit(node.id)
            if val.is_valid():
                return convert(1, val, self._clk)

            #check if it is a variable
            val = self.__vars_dict.get(node.id, None)
            return val

        # define binary operators (+,-,*,/)
        elif isinstance(node, ast.BinOp):
            lhs = self.__parse_expr(node.left)
            rhs = self.__parse_expr(node.right)
            op = ast_ops_dict[type(node.op)]
            if isinstance(lhs, np.ndarray) or isinstance(rhs, np.ndarray):
                if not isinstance(lhs, np.ndarray):
                    if np.issubdtype(rhs.dtype, np.integer):
                        lhs = rhs.dtype.type(round(lhs))
                    else:
                        lhs = rhs.dtype.type(lhs)
                if not isinstance(rhs, np.ndarray):
                    if np.issubdtype(lhs.dtype, np.integer):
                        rhs = lhs.dtype.type(round(rhs))
                    else:
                        rhs = lhs.dtype.type(rhs)
                out = op(lhs, rhs)
                self.__proc_list.append((op, (lhs, rhs, out)))
                self.__proc_strs.append("Binary operator: " + op.__name__)
                return out
            return op(lhs, rhs)

        # define unary operators (-)
        elif isinstance(node, ast.UnaryOp):
            operand = self.__parse_expr(node.operand)
            op = ast_ops_dict[type(node.op)]
            out = op(operand)
            # if we have a np array, add to processor list
            if isinstance(out, np.ndarray):
                self.__proc_list.append((op, (operand, out)))
                self.__proc_strs.append("Unary operator: " + op.__name__)
            return out

        elif isinstance(node, ast.Subscript):
            # print(ast.dump(node))
            val = self.__parse_expr(node.value)
            if isinstance(node.slice, ast.Index):
                if isinstance(val, np.ndarray):
                    return val[..., self.__parse_expr(node.slice.value)]
                else:
                    return val[self.__parse_expr(node.slice.value)]
            elif isinstance(node.slice, ast.Slice):
                if isinstance(val, np.ndarray):
                    return val[...,
                               slice(self.__parse_expr(node.slice.lower),
                                     self.__parse_expr(node.slice.upper),
                                     self.__parse_expr(node.slice.step))]
                else:
                    print(self.__parse_expr(node.slice.upper))
                    return val[slice(self.__parse_expr(node.slice.upper),
                                     self.__parse_expr(node.slice.lower),
                                     self.__parse_expr(node.slice.step))]
            elif isinstance(node.slice, ast.ExtSlice):
                slices = tuple(node.slice.dims)
                for i, sl in enumerate(slices):
                    if isinstance(sl, ast.index):
                        slices[i] = self.__parse_expr(sl.value)
                    else:
                        slices[i] = slice(self.__parse_expr(sl.upper),
                                          self.__parse_expr(sl.lower),
                                          self.__parse_expr(sl.step))
                return val[..., slices]

        # for name.attribute
        elif isinstance(node, ast.Attribute):
            val = self.__parse_expr(node.value)
            # get shape with buffer_len dimension removed
            if node.attr == 'shape' and isinstance(val, np.ndarray):
                return val.shape[1:]

        # for func([args])
        elif isinstance(node, ast.Call):
            func = node.func.id
            # get length of 1D array variable
            if func == "len" and len(node.args) == 1 and isinstance(
                    node.args[0], ast.Name):
                var = self.__parse_expr(node.args[0])
                if isinstance(var, np.ndarray) and len(var.shape) == 2:
                    return var.shape[1]
                else:
                    raise ValueError("len(): " + node.args[0].id +
                                     "has wrong number of dims")
            elif func == "round" and len(node.args) == 1:
                var = self.__parse_expr(node.args[0])
                return int(round(var))
            # if this is a valid call to construct a new array, do so; otherwise raise an exception
            else:
                if len(node.args) == 2:
                    shape = self.__parse_expr(node.args[0])
                    if isinstance(shape, (int, np.int32, np.int64)):
                        shape = (self._block_width, shape)
                    elif isinstance(shape, tuple):
                        shape = (self._block_width, ) + shape
                    else:
                        raise ValueError(
                            "Do not recognize call to " + func +
                            " with arguments of types " +
                            str([arg.__dict__ for arg in node.args]))
                    try:
                        dtype = np.dtype(node.args[1].id)
                    except:
                        raise ValueError(
                            "Do not recognize call to " + func +
                            " with arguments of types " +
                            str([arg.__dict__ for arg in node.args]))

                    if func in self.__vars_dict:
                        var = self.__vars_dict[func]
                        if not var.shape == shape and var.dtype == dtype:
                            raise ValueError("Requested shape and type for " +
                                             func +
                                             " do not match existing values")
                        return var
                    else:
                        var = np.zeros(shape, dtype, 'F')
                        self.__vars_dict[func] = var
                        self.__print(
                            2, 'Added variable ' + func + ' with shape ' +
                            str(tuple(shape)) + ' and type ' + str(dtype))

                        return var
                else:
                    raise ValueError(
                        "Do not recognize call to " + func +
                        " with arguments " +
                        str([str(arg.__dict__) for arg in node.args]))

        raise ValueError("Cannot parse AST nodes of type " +
                         str(node.__dict__))
Esempio n. 15
0
def units_compatible(u0, u1):
    return (unit_parser.parse_unit(u0).derivation == unit_parser.parse_unit(
        u1).derivation)
Esempio n. 16
0
def unit_string_is_valid(units):
    return unit_parser.parse_unit(units).valid