def __convert_other(self, other): su = getattr(self, 'units', dimensionless) ou = getattr(other, 'units', dimensionless) if su == None and ou == None: u = None else: if isinstance(other, units.unit.unit): # Handles 5 * liters ou = units.unit.unit(1, other.derivation) other = units.convert(other.value, ou, su) elif isinstance(other, UnitArray): # Handles UnitArray or UnitScalar other = units.convert(numpy.array(other), ou, su) elif isinstance(other, numpy.ndarray): if len(other.shape) > 0 and hasattr(other.item(0), 'derivation'): # Handles array([1,2,3] * liters) ou = units.unit.unit(1, other.item(0).derivation) other = units.convert(other / ou, ou, su) else: #Everything else other = units.convert(numpy.array(other), ou, su) u = su return other, u
def setUp(self): unittest.TestCase.setUp(self) # Put unit adapters on either side of a masking adapter to see if they # cooperate. Store meters in the raw context, push fathoms through the # mask, and expose feet to the outside world. self.units = units = { 'in':meters, 'mid':fathom, 'out':feet } # Set up data for the contexts depth = Log(linspace(0.0, 100.0, 11), units=units['in']) lith = array(['sand']*len(depth), dtype=object) # Create the contexts self.context = PassThruContext( ReductionContext( NumericContext() ) ) self.raw_context = self.context.context_base # Add data (before creating the adapters) self.context.update(depth=depth, lith=lith) # (This simplifies creating UnitConversionAdapters) def always(value): class C(dict): def get(self, key, default=None): return value def __repr__(self): return '{*:%r}' % value return C() # Layer multiple adapters self.mask = (15.0 < depth) & (depth < 55.0) self.convert_out = lambda x: convert(x, units['in'], units['out']) self.convert_in = lambda x: convert(x, units['out'], units['in']) self.context.get_reduction_context(OpenContext).context_filter = \ MaskFilter( mask = self.mask )
def test_convert(self): t1 = units.convert(60, angle.minutes, angle.degree) self.assertAlmostEqual(t1, 1) t1 = units.convert(3600, angle.seconds, angle.degree) self.assertAlmostEqual(t1, 1) t1 = units.convert(1, angle.degree, angle.radian) self.assertAlmostEqual(t1, numpy.pi / 180.)
def _compute_force(self, mass, acc, mass_units=kg, acc_units=ms2, **unused_units): """ """ # algorithm units are mass_u = kg acc_u = ms2 # convert into algorithm units mass = units.convert(mass, mass_units, mass_u) acc = units.convert(acc, acc_units, acc_u) # do the math :-) return mass * acc
def test_temperature(self): k = 200 c = -73.15 f = -99.67 k2c = units.convert(k, kelvin, celsius) self.assertAlmostEqual(k2c, c) k2f = units.convert(k, kelvin, fahrenheit) self.assertAlmostEqual(k2f, f) f2c = units.convert(f, fahrenheit, celsius) self.assertAlmostEqual(f2c, c) t1 = units.convert(-40.0, temperature.fahrenheit, temperature.celsius) self.assertAlmostEqual(t1, -40.0, 6) t2 = units.convert(0.0, temperature.celsius, temperature.fahrenheit) self.assertAlmostEqual(t2, 32.0, 6) return
def test_convert(self): t1 = units.convert(1, time.year, time.second) self.assertAlmostEqual(t1, (365.25 * 24 * 60 * 60), 6) t1 = units.convert(1, time.week, time.second) self.assertAlmostEqual(t1, (7 * 24 * 60 * 60)) t1 = units.convert(1, time.day, time.second) self.assertAlmostEqual(t1, (24 * 60 * 60)) t1 = units.convert(1, time.hour, time.second) self.assertAlmostEqual(t1, 60 * 60) t1 = units.convert(1, time.minute, time.second) self.assertAlmostEqual(t1, 60)
def as_units(self, new_units): """ Convert UnitArray from its current units to a new set of units. """ result = self.__class__(units.convert(self.view(numpy.ndarray), self.units, new_units)) result.units = new_units return result
def as_units(self, new_units): """ Convert UnitArray from its current units to a new set of units. """ result = self.__class__( units.convert(self.view(numpy.ndarray), self.units, new_units)) result.units = new_units return result
def setUp(self): unittest.TestCase.setUp(self) # Put unit adapters on either side of a masking adapter to see if they # cooperate. Store meters in the raw context, push fathoms through the # mask, and expose feet to the outside world. self.units = units = {'in': meters, 'mid': fathom, 'out': feet} # Set up data for the contexts depth = UnitArray(linspace(0.0, 100.0, 11), units=units['in']) lith = array(['sand'] * len(depth), dtype=object) # Create the contexts self.context = AdaptedDataContext(subcontext=DataContext()) self.raw_context = self.context.subcontext # Add data (before creating the adapters) self.context.update(depth=depth, lith=lith) # (This simplifies creating UnitConversionAdapters) def always(value): class C(dict): def get(self, key, default=None): return value def __repr__(self): return '{*:%r}' % value return C() # Layer multiple adapters d = depth.view(ndarray) self.mask = (15.0 < d) & (d < 55.0) self.convert_out = lambda x: convert(x, units['in'], units['out']) self.convert_in = lambda x: convert(x, units['out'], units['in']) #self.context.push_adapter( # UnitConversionAdapter(setitem_units=always(units['in']), # getitem_units=always(units['mid']))) self.context.push_adapter(MaskingAdapter(mask=self.mask)) self.context.push_adapter( # UnitConversionAdapter(setitem_units=always(units['mid']), UnitConversionAdapter(setitem_units=always(units['in']), getitem_units=always(units['out'])))
def unit_array_units_converter(unit_array, new_units): """ Convert a UnitArray from one set of units to another. """ if unit_array.units != new_units: # Need conversion. if isinstance(unit_array, ndarray) and unit_array.shape != (): # this is an array result = UnitArray(units.convert(unit_array.view(ndarray), unit_array.units, new_units)) else: # this is a scalar result = UnitScalar(units.convert(unit_array.view(ndarray), unit_array.units, new_units)) result.units = new_units else: # No conversion needed. Just return the unit_array. result = unit_array return result
def setUp(self): unittest.TestCase.setUp(self) # Put unit adapters on either side of a masking adapter to see if they # cooperate. Store meters in the raw context, push fathoms through the # mask, and expose feet to the outside world. self.units = units = { 'in':meters, 'mid':fathom, 'out':feet } # Set up data for the contexts depth = UnitArray(linspace(0.0, 100.0, 11), units=units['in']) lith = array(['sand']*len(depth), dtype=object) # Create the contexts self.context = AdaptedDataContext(subcontext=DataContext()) self.raw_context = self.context.subcontext # Add data (before creating the adapters) self.context.update(depth=depth, lith=lith) # (This simplifies creating UnitConversionAdapters) def always(value): class C(dict): def get(self, key, default=None): return value def __repr__(self): return '{*:%r}' % value return C() # Layer multiple adapters d = depth.view(ndarray) self.mask = (15.0 < d) & (d < 55.0) self.convert_out = lambda x: convert(x, units['in'], units['out']) self.convert_in = lambda x: convert(x, units['out'], units['in']) #self.context.push_adapter( # UnitConversionAdapter(setitem_units=always(units['in']), # getitem_units=always(units['mid']))) self.context.push_adapter(MaskingAdapter(mask=self.mask)) self.context.push_adapter( # UnitConversionAdapter(setitem_units=always(units['mid']), UnitConversionAdapter(setitem_units=always(units['in']), getitem_units=always(units['out'])))
def __convert_other(self, other): su = getattr(self, 'units', dimensionless) ou = getattr(other, 'units', dimensionless) if su == None and ou == None: u = None else: if isinstance(other, unit): # Handles 5 * liters ou = unit(1, other.derivation) other = convert(other.value, ou, su) elif isinstance(other, UnitArray): # Handles UnitArray or UnitScalar other = convert(numpy.array(other), ou, su) elif isinstance(other, numpy.ndarray): if len(other.shape) > 0 and hasattr(other.item(0), 'derivation'): # Handles array([1,2,3] * liters) ou = unit(1, other.item(0).derivation) other = convert(other/ou, ou, su) u = su return other, u
def test_as_units(self): """ Conversion from one unit system to another. """ ary = numpy.array((1,2,3)) unit_ary = UnitArray(ary, units=meters) new_unit_ary = unit_ary.as_units(feet) # Test the values are correct desired_array = units.convert(ary, meters, feet) self.assert_(numpy.all(desired_array==new_unit_ary)) # Also, make sure the units are correctly assigned. self.assertEqual(new_unit_ary.units, feet)
def test_as_units(self): """ Conversion from one unit system to another. """ ary = numpy.array((1, 2, 3)) unit_ary = UnitArray(ary, units=meters) new_unit_ary = unit_ary.as_units(feet) # Test the values are correct desired_array = units.convert(ary, meters, feet) self.assertTrue(numpy.all(desired_array == new_unit_ary)) # Also, make sure the units are correctly assigned. self.assertEqual(new_unit_ary.units, feet)
def unit_array_units_converter(unit_array, new_units): """ Convert a UnitArray from one set of units to another. """ if unit_array.units != new_units: # A conversion is needed. Must pass in a real ndarray instead of # a UnitArray since operations on it will also try to conversions that # we don't want it to do. result = units.convert(unit_array.view(numpy.ndarray), unit_array.units, new_units).view(UnitArray) result.units = new_units else: # No conversion needed. Just return the unit_array. result = unit_array return result
def setUp(self): unittest.TestCase.setUp(self) # Put unit adapters on either side of a masking adapter to see if they # cooperate. Store meters in the raw context, push fathoms through the # mask, and expose feet to the outside world. self.units = units = {'in': meters, 'mid': fathom, 'out': feet} # Set up data for the contexts depth = Log(linspace(0.0, 100.0, 11), units=units['in']) lith = array(['sand'] * len(depth), dtype=object) # Create the contexts self.context = PassThruContext(ReductionContext(NumericContext())) self.raw_context = self.context.context_base # Add data (before creating the adapters) self.context.update(depth=depth, lith=lith) # (This simplifies creating UnitConversionAdapters) def always(value): class C(dict): def get(self, key, default=None): return value def __repr__(self): return '{*:%r}' % value return C() # Layer multiple adapters self.mask = (15.0 < depth) & (depth < 55.0) self.convert_out = lambda x: convert(x, units['in'], units['out']) self.convert_in = lambda x: convert(x, units['out'], units['in']) self.context.get_reduction_context(OpenContext).context_filter = \ MaskFilter( mask = self.mask )
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 add_processor(self, func, *args, **kwargs): """ Add a new processor and bind it to a set of parameters. - func should be a function implementing the numpy ufunc class. If not, then the signature and types keyword arguments will be necessary. - args is a list of names and constants. The names link to internal numpy array variables that will be bound to the function. Names can be given in the form: "varname(length, type)[range]" The optional (length, type) field is used to initialize a new variable if necessary. The optional [range] field copies binds only a subrange of the array to the function. Non-string constant values will be converted to the right type and bound to the function. Constants with scimath time units will be converted to the internal clock unit. - keyword arguments include: - signature: broadcasting signature for a ufunc. By default, use func.signature - types: a list of strings defining the types of arrays needed for the func. By default, use func.types """ # Get the signature and list of valid types for the function signature = kwargs.get("signature", None) if (signature == None): signature = func.signature if (signature == None): signature = '->' for i in range(func.nin): signature = ',()' + signature for i in range(func.nout): signature = signature + '(),' signature = signature.strip(',') types = kwargs.get("types", None) if (types == None): types = func.types.copy() if (types == None): raise TypeError("Could not find a type signature list for " + func.__name__ + ". Please supply a valid list of types.") for i, typestr in enumerate(types): types[i] = typestr.replace('->', '') # make a list of parameters from *args. Replace any strings in the list # with numpy objects from vars_dict, where able params = [] for i, param in enumerate(args): if (isinstance(param, str)): param_val = self.get_variable(param) if param_val is not None: param = param_val params.append(param) # Make sure arrays obey the broadcasting rules, and make a dictionary # of the correct dimensions dims_list = re.findall("\((.*?)\)", signature) dims_dict = {} outerdims = [] # print('\nsetting param:', params[-1]) # print('dims list is:', dims_list) for ipar, dims in enumerate(dims_list): # print(ipar, dims, params[ipar]) if not isinstance(params[ipar], np.ndarray): continue fun_dims = outerdims + [d.strip() for d in dims.split(',') if d] arr_dims = list(params[ipar].shape) #check if arr_dims can be broadcast to match fun_dims for i in range(max(len(fun_dims), len(arr_dims))): fd = fun_dims[-i - 1] if i < len(fun_dims) else None ad = arr_dims[-i - 1] if i < len(arr_dims) else None if (isinstance(fd, str)): # Define the dimension or make sure it is consistent if not ad or dims_dict.setdefault(fd, ad) != ad: raise ValueError( "Failed to broadcast array dimensions for " + func.__name__ + ". Could not find consistent value for dimension " + fd) elif not fd: # if we ran out of function dimensions, add a new outer dim outerdims.insert(0, ad) elif not ad: continue elif fd != ad: # If dimensions disagree, either insert a broadcasted array dimension or raise an exception if len(fun_dims) > len(arr_dims): arr_dims.insert(len(arr_dims) - i, 1) elif len(fun_dims) < len(arr_dims): outerdims.insert(len(fun_dims) - i, ad) fun_dims.insert(len(fun_dims) - i, ad) else: raise ValueError( "Failed to broadcast array dimensions for " + func.__name__ + ". Input arrays do not have consistent outer dimensions." ) # find type signatures that match type of array arr_type = params[ipar].dtype.char types = [ type_sig for type_sig in types if arr_type == type_sig[ipar] ] # Get the type signature we are using if (not types): raise TypeError( "Could not find a type signature matching the types of the variables given for " + func.__name__) elif (len(types) > 1): self.__print( 1, "Found multiple compatible type signatures for this function:", types, "Using signature " + types[0] + ".") types = [np.dtype(t) for t in types[0]] # Reshape variable arrays to add broadcast dimensions and allocate new arrays as needed for i, param, dims, dtype in zip(range(len(params)), params, dims_list, types): shape = outerdims + [ dims_dict[d.strip()] for d in dims.split(',') if d ] if isinstance(param, str): params[i] = self.__add_var(param, dtype, shape) elif isinstance(param, np.ndarray): arshape = list(param.shape) for idim in range(-1, -1 - len(shape), -1): if arshape[idim] != shape[idim]: arshape.insert(len(arshape) + idim + 1, 1) params[i] = param.reshape(tuple(arshape)) else: if isinstance(param, unit): param = convert(1, param, self._clk) if np.issubdtype(dtype, np.integer): params[i] = dtype.type(round(param)) else: params[i] = dtype.type(param) # Make strings of input variables/constants for later printing proc_strs = [] for i, arg in enumerate(args): if isinstance(arg, str): proc_strs.append(arg) else: proc_strs.append(str(params[i])) proc_strs = tuple(proc_strs) self.__print( 2, 'Added processor: ' + func.__name__ + str(proc_strs).replace("'", "")) # Add the function and bound parameters to the list of processors self.__proc_list.append((func, tuple(params))) self.__proc_strs.append(proc_strs) return None
def test_time(self): t1 = units.convert(1, time.year, time.second) self.assertAlmostEqual(t1, (365.25 * 24 * 60 * 60), 6)
def test_density(self): d1 = units.convert(1, density.lb_per_gal, density.grams_per_cubic_centimeter) self.assertAlmostEqual(d1, 0.1198284429, 6) return
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__))
def test_frequency(self): f1 = units.convert(1, frequency.hertz, frequency.khz) self.assertAlmostEqual(f1, 0.001, 6) return
def test_speed(self): s1 = units.convert(55, speed.miles_per_hour, speed.meters_per_second) self.assertAlmostEqual(s1, 24.5872, 6)
def __add_io_buffer(self, buff, varname, input, dtype, buffer_len, scale): """ append a tuple with the buffer and variable to either the input buffer list (if input=true) or output buffer list (if input=false), making sure that buffer shapes are compatible """ var = self.get_variable(varname) if buff is not None and not isinstance(buff, np.ndarray): raise ValueError("Buffers must be ndarrays.") # if buffer length is not defined, figure out what it should be if buffer_len is not None: if self._buffer_len is None: self._buffer_len = buffer_len elif self._buffer_len != buffer_len: raise ValueError( "Buffer length was already set to a number different than the one provided. To change the buffer length, you must reset the buffers." ) if not self._buffer_len: if buff is not None: self._buffer_len = buff.shape[0] else: self._buffer_len = self._block_width self.__print( 1, "Setting i/o buffer length to " + str(self._buffer_len)) # if a unit is given, convert it to a scaling factor if isinstance(scale, unit): scale = convert(1, scale, self._clk) # if no buffer was provided, make one returnbuffer = False if buff is None: if var is None: raise ValueError( "Cannot make buffer for non-existent variable " + varname) # deduce dtype. If scale is used, force float type if not dtype: if not scale: dtype = var.dtype else: dtype = np.dtype('float' + str(var.dtype.itemsize * 8)) buff = np.zeros((self._buffer_len, ) + var.shape[1:], dtype) returnbuffer = True # Check that the buffer length is correct. For 1D buffer, reshape # it if possible, assuming array of structure ordering if not buff.shape[0] == self._buffer_len: if buff.ndim == 1 and len(buff) % self._buffer_len == 0: buff = buff.reshape(self._buffer_len, len(buff) // self._buffer_len) else: raise ValueError("Buffer provided for " + varname + " is the wrong length.") # Check that shape of buffer is compatible with shape of variable. # If variable does not yet exist, add it here if var is None: if dtype is None: if not scale: dtype = buff.dtype else: dtype = np.dtype('float' + str(buff.dtype.itemsize * 8)) var = self.__add_var(varname, dtype, (self._block_width, ) + buff.shape[1:]) elif var.shape[1:] != buff.shape[1:]: raise ValueError("Provided buffer has shape " + str(buff.shape) + " which is not compatible with " + str(varname) + " shape " + str(var.shape)) varname = re.search('(\w+)', varname).group(0) if input: self.__input_buffers[varname] = (buff, var, scale) self.__print( 2, 'Binding input buffer of shape ' + str(buff.shape) + ' and type ' + str(buff.dtype) + ' to variable ' + varname + ' with shape ' + str(var.shape) + ' and type ' + str(var.dtype)) else: self.__output_buffers[varname] = (buff, var, scale) self.__print( 2, 'Binding output buffer of shape ' + str(buff.shape) + ' and type ' + str(buff.dtype) + ' to variable ' + varname + ' with shape ' + str(var.shape) + ' and type ' + str(var.dtype)) if returnbuffer: return buff
def length_label(meters): convert = unitmap[parser.args.unit] value = units.convert(meters, units.length.m, convert) return "%i %s" % (value, convert.label)