def is_linear_flow_rate(flow_rate): """ Test whether a flow rate (UnitScalar) has a unit of a volume per time. """ try: convert(flow_rate, from_unit=flow_rate.units, to_unit=m / second) return True except InvalidConversion: return False
def volume_to_depth(volume, column, to_unit="meters"): """ Translate a volume of fluid into a depth in infinitely long column. """ if volume.units != chr_units.column_volumes: volume = convert(volume, from_unit=volume.units, to_unit=column.volume.units) volume *= column.volume volume = float(volume) depth = volume * column.bed_height_actual if to_unit: depth = convert(depth, from_unit=depth.units, to_unit=parse_unit(to_unit)) return float(depth)
def unit_scalar_almost_equal(x1, x2, eps=1e-9): """ Returns whether 2 UnitScalars are almost equal. Parameters ---------- x1 : UnitScalar First unit scalar to compare. x2 : UnitScalar Second unit scalar to compare. eps : float Absolute precision of the comparison. """ if not isinstance(x1, UnitScalar): msg = "x1 is supposed to be a UnitScalar but a {} was passed." msg = msg.format(type(x1)) logger.exception(msg) raise ValueError(msg) if not isinstance(x2, UnitScalar): msg = "x2 is supposed to be a UnitScalar but a {} was passed." msg = msg.format(type(x2)) logger.exception(msg) raise ValueError(msg) a1 = float(x1) try: a2 = convert(float(x2), from_unit=x2.units, to_unit=x1.units) except InvalidConversion: return False return np.abs(a1 - a2) < eps
def convertUnit(self, value, unit_from, unit_to): if (self.delete == True): return exec('from scimath.units.SI import *') exec('from scimath.units.pressure import *') exec('from scimath.units.acceleration import *') exec('from scimath.units.angle import *') exec('from scimath.units.area import *') exec('from scimath.units.density import *') exec('from scimath.units.electromagnetism import *') exec('from scimath.units.energy import *') exec('from scimath.units.force import *') exec('from scimath.units.frequency import *') exec('from scimath.units.length import *') exec('from scimath.units.mass import *') exec('from scimath.units.power import *') exec('from scimath.units.pressure import *') exec('from scimath.units.speed import *') exec('from scimath.units.substance import *') exec('from scimath.units.temperature import *') exec('from scimath.units.time import *') exec('from scimath.units.volume import *') from scimath.units.api import convert try: value = convert(value, eval(unit_from), eval(unit_to)) except: print( 'Einheit "' + unit_from + '" oder "' + unit_to + '" wird nicht unterstuetzt von scimath. Funktion wird beendet...' ) return (False) return (value)
def get_step_velocity(step, column=None): """ Extract the step linear superficial velocity (flow_rate), in m/s. """ if is_volumetric_flow_rate(step.flow_rate): step_velocity = volumetric_flow_rate_to_linear( step.flow_rate, column.column_type.diameter, to_unit="m/s") else: step_velocity = convert(step.flow_rate, from_unit=step.flow_rate.units, to_unit=meter / second) return float(step_velocity)
def linear_flow_rate_to_volumetric(linear_flow_rate, diam, to_unit=""): """ Convert a linear flow rate in a column of diameter diam to a volumetric flow rate. """ vol_flow_rate = (np.pi * diam**2 / 4.) * linear_flow_rate if to_unit: parse_unit = unit_parser.parse_unit from_unit = vol_flow_rate.units to_unit = parse_unit(to_unit) vol_flow_rate_val = convert(float(vol_flow_rate), from_unit=from_unit, to_unit=to_unit) vol_flow_rate = UnitScalar(vol_flow_rate_val, units=to_unit) return vol_flow_rate
def time_to_volume(time, flow_rate, column=None, to_unit="CV"): """ Convert a flow time to a volume, assuming a specified flow rate. Parameters ---------- time : UnitScalar Time of flow. flow_rate : UnitScalar Flow rate. column : Column [OPTIONAL] Column in which the flow occur. Only needed if providing linear flow rate, or if the output unit must be CV. to_unit : str String representation of the output unit. Returns ------- UnitScalar Volume that flowed during the specified time. """ if not isinstance(time, UnitScalar): msg = "The time provided should be a UnitScalar." logger.exception(msg) raise ValueError(msg) if not is_volumetric_flow_rate(flow_rate): diam = column.column_type.diameter flow_rate = linear_flow_rate_to_volumetric(flow_rate, diam) volume = time * flow_rate if to_unit: if to_unit == "CV": volume = UnitScalar(float(volume / column.volume), units="CV") else: # Convert to target unit parse_unit = unit_parser.parse_unit volume = convert(volume, from_unit=volume.units, to_unit=parse_unit(to_unit)) return volume
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)
def create_cadet_external_and_validate(self, sim=None): if sim is None: sim = self.sim cadet_ext = CADETPhExternalProfile.from_simulation(sim) # lengths of arrays: self.assertEqual(len(cadet_ext.ext_prof_delta), len(cadet_ext.ext_profile)) one_col_length = float( convert_str(self.column.bed_height_actual, "cm", "m")) # Initial values: depths start at 0, and includes 1 column of initial # buffer: self.assertEqual(cadet_ext.ext_prof_delta[0], 0.0) self.assertEqual(cadet_ext.ext_prof_delta[1], one_col_length) init_solution = self.sim.method.initial_buffer first_step = self.sim.method.method_steps[0] self.assertEqual(cadet_ext.ext_profile[0], float(init_solution.pH)) self.assertEqual(cadet_ext.ext_profile[1], first_step.solutions[0].pH) flow_rate0 = first_step.flow_rate first_velocity = convert(float(flow_rate0), from_unit=flow_rate0.units, to_unit=meter / second) self.assertEqual(cadet_ext.ext_velocity, first_velocity) # Step lengths, in column, all add up to the profile_deltas: col_lengths = [one_col_length] step_velocity0 = get_step_velocity(first_step) for step in self.method.method_steps: step_velocity = float(get_step_velocity(step)) step_volume = float(step.volume) # Normalize the duration based on the velocity of the step col_length = step_volume * step_velocity0 / step_velocity col_length *= one_col_length col_lengths.append(float(col_length)) self.assertAlmostEqual(cadet_ext.ext_prof_delta.sum(), sum(col_lengths)) return cadet_ext
def vol_to_time(volume, flow_rate, column=None, to_unit="minute"): """ Convert a volume to a time, using a flow rate. Parameters ---------- volume : UnitScalar Volume to convert to a time. flow_rate : UnitScalar Flow rate used to convert a volume to a time. column : Column [OPTIONAL] Column the volume is flowing through. From that object are read the diameter and colume of the column for unit conversion if needed. to_unit : str [OPTIONAL, default: minutes] Target time unit. Must be parsable by scimath's unit parser. Returns ------- float Value of the time needed for the provided volume to flow through the column. """ if not isinstance(volume, UnitScalar): raise ValueError("The volume provided should be a UnitScalar.") elif volume.units == chr_units.column_volumes: volume = float(volume) * column.volume # Convert a linear flow rate into a volumetric flow rate if not is_volumetric_flow_rate(flow_rate): diam = column.column_type.diameter flow_rate = linear_flow_rate_to_volumetric(flow_rate, diam) # Compute the time time = volume / flow_rate if to_unit: # Convert to target unit time = convert(time, from_unit=time.units, to_unit=parse_unit(to_unit)) return float(time)
def _set_volume(self, value): """ Back-calculate the column's bed height H to have requested volume. To make sure that the volume follows:: vol = pi * D^2 * H / 4 the bed height is set to:: H = 4 * vol / (pi * D^2) Note that this may raise an exception if the resulting bed height doesn't fall in the range set by the column type. """ #: Do nothing if value requested is already the value of the volume: if unit_scalar_almost_equal(value, self.volume): return if not isinstance(value, UnitScalar): msg = "Setting the column volume must be done providing a " \ "UnitScalar to specify the unit." logger.exception(msg) raise ValueError(msg) if not has_volume_units(value): msg = "Setting the column volume must be done providing a " \ "UnitScalar to specify the unit." logger.exception(msg) raise ValueError(msg) diam = self.column_type.diameter bed_height_actual = 4. * value / (diam**2 * pi) val = convert( float(bed_height_actual), from_unit=bed_height_actual.units, to_unit=centimeter ) self.bed_height_actual = UnitScalar(val, units=centimeter)
def from_simulation(cls, sim): """ Compute the profiles and instantiate a CADETExternal. """ column = sim.column # Initial array values step0 = sim.method.method_steps[0] step0_ph = float(step0.solutions[0].pH) step0_velocity = get_step_velocity(step0, column) # Initialization of the profiles to build init_ph = sim.method.initial_buffer.pH column_length = convert(column.bed_height_actual, to_unit=meter, from_unit=column.bed_height_actual.units) ph_values = [init_ph, step0_ph] depth_deltas = [0., float(column_length)] for step in sim.method.method_steps: step_depth = volume_to_depth(step.volume, column=column, to_unit="meter") step_velocity = get_step_velocity(step, column) # If a step flows N times faster, we model this by making its # duration N times smaller. step_depth *= step0_velocity / step_velocity if len(step.solutions) == 0: msg = "Step with no solution not supported" raise NotImplementedError(msg) # Deal with the transition from the previous step start_step_ph = float(step.solutions[0].pH) transition_needed = ph_values[-1] != start_step_ph if transition_needed: # Create a fake gradient of pH since CADET requires pH profile # to be differentiable depth_deltas.append(EPS) ph_values.append(start_step_ph) step_depth -= EPS # Deal with the contributions from this step: num_solutions = len(step.solutions) if num_solutions == 1: end_step_ph = start_step_ph elif num_solutions == 2: # 2 solutions during gradient elution when the buffer # composition goes from 1 solution to the other: end_step_ph = float(step.solutions[1].pH) else: msg = "Found a step with more than 2 solutions. Don't know " \ "how to compute the pH profile in that case." logger.exception(msg) raise NotImplementedError(msg) ph_values.append(end_step_ph) depth_deltas.append(step_depth) ext_profile = np.array(ph_values, dtype="float64") ext_prof_delta = np.array(depth_deltas, dtype="float64") return cls(ext_profile=ext_profile, ext_prof_delta=ext_prof_delta, ext_velocity=step0_velocity)
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")
def has_volume_units(vol): try: convert(vol, from_unit=vol.units, to_unit=m**3) return True except InvalidConversion: return False
#! /usr/bin/env python from __future__ import print_function import os import math from scimath.units.length import km, cm from scimath.units.api import convert def deflection_tangent(tangent_length, radius): return (tangent_length**2 + radius**2)**(1.0/2.0) - radius # cannot use math.hypot or math.sqrt: # scimath.units.unit.InvalidConversion: dimensional quantities ('m') cannot be converted to scalars # cannot use numpy.hypot: # AttributeError: 'unit' object has no attribute 'hypot' def deflection_arc(arc_length, radius): return radius * (math.cos(arc_length/radius)**-1 - 1) radius_earth = 6371.01 * km length = 1.0 * km deflection_1 = convert(deflection_tangent(length, radius_earth).value, km, cm) deflection_2 = convert(deflection_arc(length, radius_earth).value, km, cm) print(deflection_1, cm.label) print(deflection_2, cm.label)
def get_step_duration(self, step): step_duration = self.column.bed_height_actual / step.flow_rate step_duration = convert(float(step_duration), from_unit=step_duration.units, to_unit=second) return step_duration