예제 #1
0
    def test_scalar_convert_units(self):
        scalar_data = UnitScalar(1, units=chr_units.length.m)
        conv_data = convert_units(scalar_data, chr_units.length.cm)
        self.assertEqual(conv_data, UnitScalar(100, units=chr_units.length.cm))

        # convert back to original units and check
        conv_data_2 = convert_units(conv_data, scalar_data.units)
        self.assertEqual(conv_data_2, scalar_data)
예제 #2
0
    def test_invalid_arguments(self):
        scalar_data = UnitScalar(1, units=chr_units.length.m)

        # raise error if the units are not consistent
        with self.assertRaises(InvalidConversion):
            convert_units(scalar_data, chr_units.volume.liter)

        # raise error if the data does not have units
        with self.assertRaises(ValueError):
            convert_units(1, chr_units.volume.liter)
예제 #3
0
    def test_array_convert_units(self):
        array_data = UnitArray([1, 2, 3], units=chr_units.length.m)
        conv_data = convert_units(array_data, chr_units.length.cm)
        assert_allclose(conv_data.tolist(), (100 * array_data).tolist())
        self.assertEqual(conv_data.units, chr_units.length.cm)

        # convert back to original units and check
        conv_data_2 = convert_units(conv_data, array_data.units)
        assert_allclose(conv_data_2.tolist(), array_data.tolist())
        self.assertEqual(conv_data_2.units, array_data.units)
예제 #4
0
    def test_g_per_liter_resin_to_cv(self):
        concentration = UnitScalar(8.03, units="g/L")
        vol = UnitScalar(0.0635, units=chr_units.g_per_liter_resin)
        # without a concentration, the conversion is impossible:
        with self.assertRaises(ValueError):
            convert_units(vol, tgt_unit="CV")

        new_vol = convert_units(vol,
                                tgt_unit="CV",
                                concentration=concentration)
        expected = UnitScalar(0.0635 / 8.03, units="CV")
        assert_unit_scalar_almost_equal(new_vol, expected)
예제 #5
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)
def calculate_step_yield(pool_concentration, pool_volume, load_step):
    """ Calculates protein yield, in percent of the mass of protein
    loaded (at load step).

    Parameters
    ----------
    pool_concentration : UnitScalar
        Concentration of the pool extracted.

    pool_volume : UnitScalar
        Volume of the pool.

    load_step : MethodStep
        Step describing the load of the protein.
    """
    # Make sure that both volumes are in the same unit
    load_mass = load_step.volume * load_step.solutions[0].product_concentration
    pool_mass = pool_concentration * pool_volume
    step_yield = pool_mass / load_mass
    if step_yield.units != fraction:
        msg = "Computation of step_yield: the formula doesn't provide a " \
              "fraction (step_yield={!r}). Check the units.".format(step_yield)
        logger.error(msg)
        return np.nan

    step_yield = convert_units(step_yield, tgt_unit=percent)
    return step_yield
예제 #7
0
 def test_g_per_liter_resin_to_cv_conc_units(self):
     """ g/L_resin -> CV, passing concentration in different unit.
     """
     concentration = UnitScalar(8030, units="g/m**3")
     vol = UnitScalar(0.0635, units=chr_units.g_per_liter_resin)
     expected = UnitScalar(0.0635 / 8.03, units="CV")
     new_vol = convert_units(vol,
                             tgt_unit="CV",
                             concentration=concentration)
     assert_unit_scalar_almost_equal(new_vol, expected)
예제 #8
0
 def _build_volume_strategy_msg(self):
     conc = self.mass_bal_analyzer.current_concentration
     vol = convert_units(self.mass_bal_analyzer.current_volume,
                         tgt_unit=g_per_liter_resin, concentration=conc)
     target = self.mass_bal_analyzer.compute_loaded_vol(
         tgt_units=g_per_liter_resin)
     msg = "Clicking 'OK' will automatically adjust the Load step volume " \
           "so the load rate changes from {0:.4g} {1} to {2:.4g} {1}."
     msg = msg.format(float(vol), vol.units.label, float(target))
     self.mass_bal_strategy_msg = msg
예제 #9
0
    def _get_product_component_purities(self):
        from kromatography.utils.chromatography_units import convert_units
        if self.solution_type == 'Pool':
            concentrations = self._product_component_concentrations
        else:
            concentrations = self.product_component_concentrations

        if concentrations is None or self.product_concentration is None:
            return

        return convert_units(concentrations / self.product_concentration,
                             tgt_unit="%")
def calculate_pool_concentration(comp_concentrations):
    """Calculates total pool concentration in g/liter by summing individual
    component concentrations.
    """
    if not isinstance(comp_concentrations, UnitArray):
        msg = "The component concentrations are expected to be passed as a " \
              "UnitArray."
        logger.exception(msg)
        raise ValueError(msg)

    pool_concentration = UnitScalar(float(np.sum(comp_concentrations)),
                                    units=comp_concentrations.units)
    pool_concentration = convert_units(pool_concentration, tgt_unit="g/L")
    return pool_concentration
    def loaded_mass_from_method(self):
        """ Returns loaded product mass in grams, as compute from method data.
        """
        vol = self.current_volume
        if units_almost_equal(vol, column_volumes):
            vol = float(vol) * self.target_experiment.column.volume
        elif has_volume_units(vol):
            pass
        else:
            msg = "Unexpected unit for the load step volume: {}"
            msg = msg.format(vol.units.label)
            logger.exception(msg)
            raise NotImplementedError(msg)

        mass = vol * self.current_concentration
        return convert_units(mass, tgt_unit="gram")
    def compute_concentration(self):
        """ Compute the load solution concentration that would match the UV
        data at constant load step volume.

        Returns
        -------
        UnitScalar
            Load solution concentration, in g/L, that would be needed to match
            the UV data.
        """
        target_mass = self.mass_from_uv
        vol = self.current_volume
        if units_almost_equal(vol, column_volumes):
            vol = float(vol) * self.target_experiment.column.volume

        concentration = target_mass / vol
        return convert_units(concentration, tgt_unit="g/L")
    def loaded_mass_from_uv(self):
        """ Returns loaded product mass in grams, as compute from UV data.
        """
        if not self.continuous_data:
            return

        data = self.continuous_data
        product = self.target_experiment.product
        ext_coeff = product.product_components[0].extinction_coefficient
        method_step_times = self.target_experiment.method_step_boundary_times
        t_stop = UnitScalar(method_step_times[-1],
                            units=method_step_times.units)
        mass = compute_mass_from_abs_data(data,
                                          ext_coeff=ext_coeff,
                                          experim=self.target_experiment,
                                          t_start=self.time_of_origin,
                                          t_stop=t_stop)
        return convert_units(mass, tgt_unit="gram")
예제 #14
0
def build_cadet_input(simulation):
    """ Builds a valid CADETModel and CADETInput from the given Simulation.

    Note: sections and steps mean the same thing in the CADET and the
    experimental language respectively.

    Parameters
    ----------
    simulation : Simulation
        Simulation for which to build the CADET input file.

    Returns
    -------
    CADETInput
        Valid CADET input object that can be serialized to HDF5 and run by
        CADET.
    """
    # FIXME: modularize this function!

    # Collect simulation components -------------------------------------------

    column = simulation.column
    method = simulation.method
    simulated_steps = method.method_steps
    binding_model = simulation.binding_model
    transport_model = simulation.transport_model
    binding_type = binding_model.model_type
    lig_density = column.resin.ligand_density
    col_porosity = transport_model.column_porosity
    bead_porosity = transport_model.bead_porosity

    # Input validation
    if column.resin.resin_type not in ['CEX', 'AEX']:
        msg = 'The simulation is only supported for CEX and AEX resins.'
        logger.exception(msg)
        raise NotImplementedError(msg)

    num_sections = len(simulated_steps)
    section_times = simulation.section_times

    # Build CadetInput instance -----------------------------------------------

    velocities = [step.flow_rate for step in simulated_steps]
    velocities = convert_units(unitted_list_to_array(velocities),
                               SI.meter / SI.second)

    num_components = len(simulation.product.product_component_names)
    sma_binding_type = binding_type in [
        STERIC_BINDING_MODEL, PH_STERIC_BINDING_MODEL
    ]

    if sma_binding_type:
        # +1 for the cation component (salt) if using the SMA binding model:
        num_components += 1

    # configure CADETModel
    model = CADETModel(num_components, num_sections)

    # interstitial velocity for each step
    model.velocity[:] = velocities / transport_model.column_porosity

    # Initialize boundary conditions ------------------------------------------

    inlet = model.inlet
    inlet.section_times[:] = section_times

    # Initial condition for component concentration (bulk mobile and bead
    # mobile phases):
    # FIXME: this assumes that first step isn't
    if sma_binding_type:
        model.init_c[0] = method.initial_buffer.cation_concentration[()]
        model.init_cp[0] = model.init_c[0]
        # This in theory should be equal to sma_lambda: if it is not, CADET
        # will enforce it.
        model.init_q[0] = float(lig_density / (1 - col_porosity) /
                                (1 - bead_porosity))
    # Component concentration evolution parameters ----------------------------

    for ii, section in enumerate(inlet.section):
        step = simulated_steps[ii]
        # Computing concentrations for the step. By default,
        # concentration = const_coeff + lin_coeff t + quad_coeff t**2

        if step.step_type == LOAD_STEP_TYPE:
            # This is a constant
            const_coeff = section.const_coeff
            sol = step.solutions[0]
            scalars = [
                comp.molecular_weight
                for comp in sol.product.product_components
            ]
            molecular_weights = unitted_list_to_array(scalars)
            prod_comp_conc = (sol.product_component_concentrations /
                              molecular_weights)
            if sma_binding_type:
                const_coeff[0] = sol.cation_concentration
                prod_comp_idx = slice(1, None)
            else:
                prod_comp_idx = slice(None, None)

            const_coeff[prod_comp_idx] = prod_comp_conc[:]

        elif step.step_type == GRADIENT_ELUT_STEP_TYPE and sma_binding_type:
            # This step has two solutions and implements sol -> sol2, so
            # cation concentration will see a gradient:
            sol_1, sol_2 = step.solutions
            const_coeff = section.const_coeff
            const_coeff[0] = sol_1.cation_concentration

            lin_coeff = section.lin_coeff
            conc_change = (sol_2.cation_concentration -
                           sol_1.cation_concentration)
            section_duration = np.diff(section_times[ii:][:2])
            lin_coeff[0] = conc_change / section_duration
        elif sma_binding_type:
            # There are no products at the inlet, so just buffer concentrations
            const_coeff = section.const_coeff
            const_coeff[0] = step.solutions[0].cation_concentration

    # Configure column/resin properties and set to SI units to make CADET-ready
    model.col_length = float(convert_units(column.bed_height_actual, SI.meter))
    bead_radius = column.resin.average_bead_diameter / 2.
    model.par_radius = float(convert_units(bead_radius, SI.meter))

    # No unit conversion is needed here as the transport model is assumed to be
    # in the right units
    model.col_porosity = transport_model.column_porosity
    model.col_dispersion = transport_model.axial_dispersion
    model.par_porosity = transport_model.bead_porosity
    # Vector quantities: remove the cation component for langmuir models:
    if sma_binding_type:
        transport_idx = slice(None, None)
    else:
        transport_idx = slice(1, None)

    model.film_diffusion = transport_model.film_mass_transfer[transport_idx]
    model.par_diffusion = transport_model.pore_diffusion[transport_idx]
    model.par_surfdiffusion = transport_model.surface_diffusion[transport_idx]

    # Attached binding model to CADETModel
    model.adsorption_type = ALL_CADET_TYPES[binding_type]
    model.adsorption = binding_model

    profile_models = [PH_STERIC_BINDING_MODEL, PH_LANGMUIR_BINDING_MODEL]
    if binding_type in profile_models:
        model.external = CADETPhExternalProfile.from_simulation(simulation)

    # finally, create the CADETInput model and return.
    cadet_input = CADETInput(
        chromatography_type=transport_model.model_type,
        model=model,
        discretization=simulation.discretization,
        solver=simulation.solver,
    )

    return cadet_input
def calculate_exp_component_concentrations(exp, fraction_data, flow_rate,
                                           pool_volume, start_collect,
                                           stop_collect):
    """ Calculate the concentration of each component in the pool in g/L from
    experimentally measured fraction data.
    The mass of a component in the pool is its fraction at various times, times
    the product concentration integrated over duration of the pooling process.
    The pool component concentration is the
    """
    from kromatography.utils.units_utils import is_volumetric_flow_rate, \
        linear_flow_rate_to_volumetric
    from kromatography.utils.chromatography_units import column_volumes, \
        convert_units
    from kromatography.utils.units_utils import unitted_list_to_array

    if is_volumetric_flow_rate(flow_rate):
        pooling_flow_rate = flow_rate
    else:
        d = exp.column.column_type.diameter
        pooling_flow_rate = linear_flow_rate_to_volumetric(
            flow_rate, diam=d, to_unit="liter/minute"
        )

    comp_concentrations = []

    for i, comp in enumerate(exp.product.product_components):
        # Collect component attributes:
        comp_name = comp.name
        comp_fraction_data = fraction_data[comp_name]
        avail_x = comp_fraction_data.x_data

        start_collect_idx = searchsorted(avail_x, start_collect)
        stop_collect_idx = searchsorted(avail_x, stop_collect)
        collect_idx = range(start_collect_idx, stop_collect_idx)

        ext_coeff = comp.extinction_coefficient.tolist()
        fraction_comp_conc = comp_fraction_data.y_data[collect_idx] / ext_coeff
        fraction_comp_conc = UnitArray(fraction_comp_conc, units='g/L')

        # Integration of these concentrations over time to get the total mass
        # of the component:
        if stop_collect_idx+1 > len(comp_fraction_data.x_data):
            msg = "Computing the total mass of component {} cannot complete " \
                  "because there are no fractions specified after the " \
                  "pooling step. Please add at least 1 fraction to your " \
                  "fractions for experiment {}."
            msg = msg.format(comp.name, exp.name)
            logger.exception(msg)
            raise ValueError(msg)

        collect_idx_times = range(start_collect_idx, stop_collect_idx+1)
        times = comp_fraction_data.x_data[collect_idx_times]
        time_units = comp_fraction_data.x_metadata["units"]
        delta_times = UnitArray(list(diff(times)), units=time_units)
        frac_comp_masses = fraction_comp_conc * delta_times * pooling_flow_rate
        frac_mass = float(frac_comp_masses.sum())
        comp_mass = UnitScalar(frac_mass, units=frac_comp_masses.units)

        if pool_volume.units == column_volumes:
            pool_volume = float(pool_volume) * exp.column.volume

        comp_conc = comp_mass / pool_volume
        comp_concentrations.append(convert_units(comp_conc, 'g/L'))

    # Read off the units from the fraction tab in the Excel spreadsheet!
    product_component_concentrations = \
        unitted_list_to_array(comp_concentrations)

    return product_component_concentrations
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")
예제 #17
0
 def test_milli_au_to_au(self):
     x = UnitArray([1000, 2000, 3000],
                   units=chr_units.milli_absorption_unit)
     conv_data = convert_units(x, chr_units.absorption_unit)
     expected = UnitArray([1, 2, 3], units=chr_units.absorption_unit)
     assert_unit_array_almost_equal(conv_data, expected)