def test_solve_mul(self): L = Dimension({"length":1}) solution = dim_solve( Mul(L, Symbol("x")), L.mul(L) ) self.assertEqual(solution["x"], L)
def test_get_dimension(self): q1 = Quantity("q1") q2 = Quantity("q2") q1.dim = Dimension(length=1) q2.dim = Dimension(time=1) self.assertEqual(get_dimension(q1 * q2**2), Dimension(length=1, time=2))
def test_solve_mul_add(self): L = Dimension({"length":1}) solution = dim_solve( Mul(Symbol("y"), Add(Symbol("x"), L)), L ) self.assertEqual(solution["x"], L) self.assertEqual(solution["y"], Dimension({}))
def test_sin(self): L = Dimension({"length":1}) solution = dim_solve( sin(Symbol("x"))*Symbol("y"), L ) self.assertEqual(solution["x"], Dimension({})) self.assertEqual(solution["y"], L)
def test_solve_pow(self): L = Dimension({"length":1}) solution = dim_solve( Pow(Add(Symbol("x"),Symbol("y")), 2), L.pow(2) ) self.assertEqual(solution["x"], L) self.assertEqual(solution["y"], L)
def dim_simplify(expr): """ Simplify expression by recursively evaluating the dimension arguments. This function proceeds to a very rough dimensional analysis. It tries to simplify expression with dimensions, and it deletes all what multiplies a dimension without being a dimension. This is necessary to avoid strange behavior when Add(L, L) be transformed into Mul(2, L). """ if isinstance(expr, Dimension): return expr if isinstance(expr, Symbol): return None args = [] for arg in expr.args: if isinstance(arg, (Mul, Pow, Add, Symbol, Function)): arg = dim_simplify(arg) args.append(arg) if all([arg!=None and (arg.is_number or (isinstance(arg, Dimension) and arg.is_dimensionless)) for arg in args]): return Dimension({}) if isinstance(expr, Function): raise ValueError("Arguments of this function cannot have a dimension: %s" % expr) elif isinstance(expr, Pow): if isinstance(args[0], Dimension): return args[0].pow(args[1]) else: return None elif isinstance(expr, Add): dimargs = [arg for arg in args if isinstance(arg, Dimension)] if (all(isinstance(arg, Dimension) or arg==None for arg in args) or all(arg.is_dimensionless for arg in dimargs)): if len(dimargs)>0: return reduce(lambda x, y: x.add(y), dimargs) else: return Dimension({}) else: raise ValueError("Dimensions cannot be added: %s" % expr) elif isinstance(expr, Mul): result = Dimension({}) for arg in args: if isinstance(arg, Dimension): result = result.mul(arg) elif arg == None: return None return result return None
def test_plot(self): p = Project() p.data["x"]=Quantity("x") p.data["y"]=Quantity("y") p.data["y"].value_prefUnit = si["ms"] p.data["x"].value = np.float_([0,1,2,3,4,5]) p.data["x"].error = np.float_([0.1,0.1,0.2,0.3,0.4,0.3]) p.data["x"].dim = Dimension(time=1) p.data["y"].value = np.float_([1,2.3,1.8,3.4,4.5,8]) p.data["y"].dim = Dimension(time=1) p.plot(("x","y"))
def test_solve_add(self): L = Dimension({"length":1}) solution = dim_solve( Add(L, Symbol("x")), L ) self.assertEqual(solution["x"], L)
def test_solve_mul2(self): L = Dimension({"length":1}) solution = dim_solve( Mul(Symbol("y"), Symbol("x")), L ) self.assertEqual(solution, {})
def test_ignore_dim(self): a = assign(3, 1, "m") q = assign(a**2, None, "s", ignore_dim=True) self.assertEqual(q.prefer_unit, si["s"]) self.assertEqual(q.dim, Dimension(time=1))
def test_no_error(self): q = assign(3, None, "s") self.assertAlmostEqual(q.value, 3) self.assertEqual(q.prefer_unit, si["s"]) self.assertEqual(q.value_formula, None) self.assertEqual(q.dim, Dimension(time=1)) self.assertEqual(q.error, None) self.assertEqual(q.error_formula, None)
def test_different_units(self): q = assign(14, 23, None, "r", "Radius", "dm", "cm") self.assertEqual(q.name, "r") self.assertEqual(q.longname, "Radius") self.assertAlmostEqual(q.value, 1.4) self.assertEqual(q.prefer_unit, si["dm"]) self.assertEqual(q.value_formula, None) self.assertEqual(q.dim, Dimension(length=1)) self.assertAlmostEqual(q.error, 0.23) self.assertEqual(q.error_formula, None)
def test_simple_assign(self): q = assign(12, 1 / 1000, "mm", "r", "Radius") self.assertEqual(q.name, "r") self.assertEqual(q.longname, "Radius") self.assertAlmostEqual(q.value, 0.012) self.assertEqual(q.prefer_unit, si["mm"]) self.assertEqual(q.value_formula, None) self.assertEqual(q.dim, Dimension(length=1)) self.assertAlmostEqual(q.error, 1e-6) self.assertEqual(q.error_formula, None)
def test_simple_calculation(self): r = assign(4, 1, "m", "r") q = assign(2 * pi * r**3, None, None, "V", "Volumen") self.assertEqual(q.name, "V") self.assertEqual(q.longname, "Volumen") self.assertAlmostEqual(q.value, 2 * np.pi * 4**3) self.assertEqual(q.prefer_unit, None) self.assertEqual(q.value_formula, 2 * pi * r**3) self.assertEqual(q.dim, Dimension(length=3)) self.assertAlmostEqual(q.error, 2 * np.pi * 3 * 4**2 * 1)
def test_calculataion_two_variables(self): r = assign(4, 1, "m", "r") h = assign(3, 2, "m", "h") q = assign(2 * pi * r**2 * h, None, None, "V", "Volumen") self.assertEqual(q.name, "V") self.assertEqual(q.longname, "Volumen") self.assertAlmostEqual(q.value, 2 * np.pi * 4**2 * 3) self.assertEqual(q.prefer_unit, None) self.assertEqual(q.value_formula, 2 * pi * r**2 * h) self.assertEqual(q.dim, Dimension(length=3)) self.assertAlmostEqual( q.error, 2 * np.pi * np.sqrt((2 * 4 * 3 * 1)**2 + (4**2 * 2)**2))
def test_MeanValue(self): p = Project() # test unweighted mean value p.data["a"] = Quantity("a") p.data["a"].value = np.float_([3, 4, 5, 6, 7, 8]) p.data["a"].dim = Dimension() p.mean_value("m", "a", longname="Mittelwert") self.assertEqual(p.data["m"].name, "m") self.assertEqual(p.data["m"].longname, "Mittelwert") self.assertTrue(np.fabs(p.data["m"].value - 5.5) < 0.000001) self.assertTrue(np.fabs(p.data["m"].error - 0.84876886033) < 0.000001) self.assertEqual(p.data["m"].value_formula, "standard mean value") self.assertEqual(p.data["m"].dim, Dimension()) # test weighted mean value p = Project() p.data["a"] = Quantity("a") p.data["b"] = Quantity("b") p.data["a"].value = np.float_([3, 4]) p.data["a"].error = np.float_([0.3, 0.4]) p.data["a"].dim = Dimension(length=1) p.data["b"].value = np.float_(7) p.data["b"].error = np.float_(2) p.data["b"].dim = Dimension(length=1) p.mean_value("n", "a", "b", longname="Mittel") self.assertEqual(p.data["n"].name, "n") self.assertEqual(p.data["n"].longname, "Mittel") self.assertTrue(np.fabs(p.data["n"].value - 3.41167192429) < 0.000001) self.assertTrue(np.fabs(p.data["n"].error - 0.23829044123) < 0.000001) self.assertEqual(p.data["n"].value_formula, "standard weighted mean value") self.assertEqual(p.data["n"].dim, Dimension(length=1))
def test_solve_pow(self): L = Dimension({"length":1}) T = Dimension({"time":1}) solution = dim_solve( sympify("a*(t-b)**2+c"), L, {'t': T} ) self.assertEqual(solution["b"], T) self.assertEqual(solution["a"], L.mul(T.pow(-2))) self.assertEqual(solution["t"], T) self.assertEqual(solution["c"], L)
def assign(self, name, value=None, error=None, unit=None, longname=None, value_unit=None, error_unit=None, replace=False, ignore_dim=False): """ Assigns value and/or error to quantity Args: name: quantity name longname: description of quantity value: value to assign, can be expression, string, list or number error: error to assign, can be expression, string, list or number, but mustn't depend on other quantities unit: unit of both value and error, replaces 'value_unit' and 'error_unit' if given value_unit: value unit expression or string error_unit: error unit expression or string replace: if True, will replace quantity instead of trying to keep data ignore_dim: if True, will ignore calculated dimension and use given unit instead """ if not unit is None: value_unit = unit error_unit = unit if value is None and error is None: raise ValueError( "At least either value or error must be specified.") value_len = None value_dim = None value_formula = None error_len = None error_dim = None error_formula = None # if value is given if not value is None: # parse unit if given if not value_unit is None: factor, value_dim, value_unit = units.parse_unit(value_unit) # parse value if isinstance(value, list) or isinstance(value, tuple): # if it's a list, parse each element parsed_list = [] for v in value: parsed_list.append(quantities.parse_expr(v, self.data)) elif isinstance(value, str) or isinstance(value, Expr): # if it's not a list, parse once value = quantities.parse_expr(value, self.data) # if it's a calculation if isinstance(value, Expr) and not value.is_number: # calculate value from dependency value_formula = value value = quantities.get_value(value_formula) # calculate dimension from dependency if not ignore_dim: calculated_dim = quantities.get_dimension(value_formula) if not value_dim is None and not calculated_dim == value_dim: raise RuntimeError( "dimension mismatch for '%s'\n%s != %s" % (name, value_dim, calculated_dim)) elif value_dim is None: value_dim = calculated_dim else: # if ignore_dim is True and there's no unit given -> dimensionless if value_dim is None: factor = 1 value_dim = Dimension() value_unit = S.One # calculated value must be converted to given unit (ignore_dim=True) value = np.float_(factor) * value # if it's a number else: # if no unit given, set dimensionless if value_unit is None: factor = 1 value_dim = Dimension() value_unit = S.One value = np.float_(factor) * np.float_(value) # calculate value length if isinstance(value, np.ndarray): value_len = len(value) else: value_len = 1 # if error is given if not error is None: # parse unit if given if not error_unit is None: factor, error_dim, error_unit = units.parse_unit(error_unit) # parse value if isinstance(error, list) or isinstance(error, tuple): # if it's a list, parse each element parsed_list = [] for u in error: parsed_list.append(quantities.parse_expr(u, self.data)) elif isinstance(error, str) or isinstance(error, Expr): # if it's not a list, parse once error = quantities.parse_expr(error, self.data) # make sure error is a number if isinstance(error, Expr) and not error.is_number: raise RuntimeError("error '%s' is not a number" % error) # if no unit given, set dimensionless if error_unit is None: factor = 1 error_dim = Dimension() error_unit = S.One error = np.float_(factor) * np.float_(error) # calculate error length, ignore len(error)==1 because it can be duplicated to fit any value length if isinstance(error, np.ndarray): error_len = len(error) # if error can be calculated elif not value_formula is None: error, error_formula = quantities.get_error(value_formula) # merge dimensions dim = value_dim if not dim is None and not error_dim is None and not dim == error_dim: raise RuntimeError( "value dimension and error dimension are not the same\n%s != %s" % (dim, error_dim)) if not error_dim is None: dim = error_dim # merge lengths new_len = value_len if not new_len is None and not error_len is None and not new_len == error_len: raise RuntimeError( "value length doesn't fit error length for '%s':\n%s != %s" % (name, new_len, error_len)) if not error_len is None: new_len = error_len # if quantity didn't exist if not name in self.data or replace: self.data[name] = quantities.Quantity(name) # if it did exist else: # get old length, len(error)=1 is not a length, because it can be duplicated to fit any value length old_len = None if not self.data[name].value is None: if isinstance(self.data[name].value, np.ndarray): old_len = len(self.data[name].value) else: old_len = 1 if not self.data[name].error is None and isinstance( self.data[name].error, np.ndarray): old_len = len(self.data[name].error) # if new dimension or new length, create new quantity if (not self.data[name].dim == dim or (not old_len is None and not new_len is None and not old_len == new_len)): self.data[name] = quantities.Quantity(name) # save stuff if not longname is None: self.data[name].longname = longname if not value is None: self.data[name].value = value self.data[name].value_formula = value_formula if not value_unit is None: self.data[name].prefer_unit = value_unit elif not error_unit is None: self.data[name].prefer_unit = error_unit if not error is None: self.data[name].error = error self.data[name].error_formula = error_formula self.data[name].dim = dim # check if error must be duplicated to adjust to value length if isinstance(self.data[name].value, np.ndarray) and isinstance( self.data[name].error, np.float_): error_arr = np.full(len(self.data[name].value), self.data[name].error) self.data[name].error = error_arr
import errorpro.plot_mat as matplot import numpy as np from errorpro.dimensions.dimensions import Dimension from errorpro.si import system as si from errorpro.quantities import Quantity # not suitable for automated testing, so must be tested manually if __name__ == '__main__': # single data set data = { "t": Quantity("t", "Zeit"), "v": Quantity("v", "Geschwindigkeit"), "t0": Quantity("t0", "Startzeit"), "e": Quantity("e", "Bremsdings"), "s": Quantity("s", "Startgeschwindigkeit") } data["t"].value = np.float_([0, 1, 2, 3, 4, 5]) data["t"].dim = Dimension(time=1) data["v"].value = np.float_([33, 23, 14, 10, 7, 3]) data["v"].error = np.float_([1, 2, 1.3, 2.1, 0.5, 0.6]) data["v"].value_prefUnit = si["km"] / si["h"] data["v"].dim = Dimension(length=1, time=-1) matplot.plot([(data["t"], data["v"])], [], si)
def test_parse_unit(self): factor, dim, unit = units.parse_unit("13e4*W*s") self.assertEqual(factor, 130000) self.assertEqual(dim, Dimension(mass=1, length=2, time=-2)) self.assertEqual(unit, 13e4 * si["W"] * si["s"])
def test_convert_to_unit_dimension_mismatch(self): dim = Dimension(mass=1, length=1, time=-2) self.assertRaises(RuntimeError, units.convert_to_unit, dim, output_unit="J")
def test_convert_to_unit_with_output_unit(self): dim = Dimension(mass=1, length=1, time=-2) factor, unit = units.convert_to_unit(dim, output_unit="g*m/s**2") self.assertEqual(factor, 1e-3) self.assertEqual(str(unit), "g*m/s**2")
def test_convert_to_unit_with_only_base(self): dim = Dimension(mass=1, length=1, time=-2) factor, unit = units.convert_to_unit(dim, only_base=True) self.assertEqual(factor, 1) self.assertEqual(str(unit), "kg*m/s**2")
def test_convert_to_unit(self): dim = Dimension(mass=1, length=1, time=-2) factor, unit = units.convert_to_unit(dim) self.assertEqual(factor, 1) self.assertEqual(str(unit), "N")
def test_parse_empty_unit(self): factor, dim, unit = units.parse_unit("") self.assertEqual(factor, 1) self.assertEqual(dim, Dimension()) self.assertEqual(unit, S.One)
def dim_solve(expr, dim = None, resolved=None): if resolved is None: resolved = {} if dim == None: if isinstance(expr,Function): for arg in expr.args: resolved = dim_solve(arg, Dimension({}), resolved) for arg in expr.args: resolved = dim_solve(arg, None, resolved) if isinstance(expr, Add): for arg in expr.args: argdim = dim_solve_global(arg, resolved) if argdim != None: return dim_solve(expr, argdim, resolved) return resolved else: if isinstance(expr,Function): if dim.is_dimensionless: for arg in expr.args: resolved = dim_solve(arg, Dimension({}), resolved) return resolved else: raise ValueError("Expression %s is dimensionless and not of calculated Dimension %s"%(expr, dim)) if isinstance(expr, Dimension): if expr == dim: return resolved else: raise ValueError("Dimensions do not match: expected %s, found %s"%(expr, dim)) if isinstance(expr, Symbol): if expr.name in resolved: if resolved[expr.name] != dim: raise ValueError("Dimension of %s cannot be resolved."%expr.name) else: resolved[expr.name] = dim return resolved if isinstance(expr, Add): for arg in expr.args: resolved = dim_solve(arg, dim, resolved) return resolved if isinstance(expr, Mul): for arg in expr.args: resolved = dim_solve(arg, None, resolved) # we can only conclude sth else if only one arg is of unknown dimension unknown = None unknowndim = dim for arg in expr.args: argdim = dim_solve_global(arg, resolved) if argdim == None: if unknown != None: # second unknown factor found, give up return resolved unknown = arg else: unknowndim = unknowndim.div(argdim) return dim_solve(unknown, unknowndim, resolved) if isinstance(expr, Pow): if expr.args[1].is_number: return dim_solve(expr.args[0], dim.pow(1/expr.args[1]) if dim!=None else None, resolved) else: return dim_solve(expr.args[0], Dimension({}), resolved) return resolved
def test_assignment(self): p = Project() small=0.0000001 # test single Assignments a = commands.Assignment("r","Radius") a.value = "12" a.value_unit = "mm" a.uncert = "1/1000" a.uncert_unit = "m" a.execute(p) self.assertEqual(p.data["r"].name, "r") self.assertEqual(p.data["r"].longname, "Radius") self.assertTrue(abs(p.data["r"].value - 0.012) < small) self.assertEqual(p.data["r"].value_prefUnit, si["mm"]) self.assertEqual(p.data["r"].value_depend, None) self.assertEqual(p.data["r"].dim, Dimension(length=1)) self.assertTrue(abs(p.data["r"].uncert - 0.001) < small) self.assertEqual(p.data["r"].uncert_prefUnit, si["m"]) self.assertEqual(p.data["r"].uncert_depend, None) b = commands.Assignment("h","Höhe") b.value = "3" b.value_unit = "dm" b.uncert = "4" b.uncert_unit = "cm" b.execute(p) self.assertEqual(p.data["h"].name, "h") self.assertEqual(p.data["h"].longname, "Höhe") self.assertTrue(abs(p.data["h"].value - 0.3) < small) self.assertEqual(p.data["h"].value_prefUnit, si["dm"]) self.assertEqual(p.data["h"].value_depend, None) self.assertEqual(p.data["h"].dim, Dimension(length=1)) self.assertTrue(abs(p.data["h"].uncert - 0.04) < small) self.assertEqual(p.data["h"].uncert_prefUnit, si["cm"]) self.assertEqual(p.data["h"].uncert_depend, None) # test calculation c = commands.Assignment("V","Volumen") c.value = "pi*r**2*h" c.execute(p) self.assertEqual(p.data["V"].name, "V") self.assertEqual(p.data["V"].longname, "Volumen") self.assertTrue(abs(p.data["V"].value - 0.0001357168) < small) self.assertEqual(p.data["V"].value_prefUnit, None) self.assertEqual(p.data["V"].value_depend, sympy.pi*p.data["r"]**2*p.data["h"]) self.assertEqual(p.data["V"].dim, Dimension(length=3)) self.assertTrue(abs(p.data["V"].uncert - 0.00002896705) < small) self.assertEqual(p.data["V"].uncert_prefUnit, None) self.assertEqual(p.data["V"].uncert_depend, sympy.sqrt((Symbol("r_err",positive=True)*sympy.pi*2*p.data["r"]*p.data["h"])**2 + (sympy.pi*Symbol("h_err",positive=True)*p.data["r"]**2)**2)) # test dimension mismatch d = commands.Assignment("V","Vol2") d.value = "50" d.value_unit = "s" d.uncert = "50" d.uncert_unit = "A*s" self.assertRaises(DimensionError, d.execute, p) # test replacing quantity e = commands.Assignment("V","Vol3") e.value = "50" e.value_unit = "s" e.execute(p) self.assertEqual(p.data["V"].name, "V") self.assertEqual(p.data["V"].longname, "Vol3") self.assertTrue(abs(p.data["V"].value - 50) < small) self.assertEqual(p.data["V"].value_prefUnit, si["s"]) self.assertEqual(p.data["V"].value_depend, None) self.assertEqual(p.data["V"].dim, Dimension(time=1)) self.assertTrue(p.data["V"].uncert is None) self.assertTrue(p.data["V"].uncert_prefUnit is None) self.assertTrue(p.data["V"].uncert_depend is None) # test p.data set f = commands.Assignment("y","Werte") f.value = ["12","13","14"] f.value_unit = "C" f.uncert = ["0.5","0.4","1e-1"] f.uncert_unit = "C" f.execute(p) self.assertEqual(p.data["y"].name, "y") self.assertEqual(p.data["y"].longname, "Werte") self.assertTrue((np.fabs(p.data["y"].value - np.float_([12,13,14])).all() < small).all()) self.assertEqual(p.data["y"].value_prefUnit, si["C"]) self.assertEqual(p.data["y"].value_depend, None) self.assertEqual(p.data["y"].dim, Dimension(current=1,time=1)) self.assertTrue((np.fabs(p.data["y"].uncert - np.float_([0.5,0.4,0.1])) < small).all()) self.assertEqual(p.data["y"].uncert_prefUnit, si["C"]) self.assertEqual(p.data["y"].uncert_depend, None)
def fit(self, fit_function, xydata, parameters, weighted=None, plot=False, ignore_dim=False): """ fits function to data Args: fit_function: function to fit, e.g. "n*t**2 + m*t + b" xydata: pair of x-quantity and y-quantity of data to fit to, e.g. ["t","U"] parameters: list of parameters in fit function, e.g. ["n","m","b"] weighted: if True, will weight fit by errors (returns error if not possible) if False, will not weight fit by errors if None, will try to weight fit, but if at least one error is not given, will not weight it plot: Bool, if data and fit function should be plotted ignore_dim: if True, will ignore dimensions and just calculate in base units instead """ if self.config["fit_module"] == "scipy": import errorpro.fit_scipy as fit_module else: raise ValueError("no fit module called '%s'." % self.config["fit_module"]) # get parameter quantities parameters_obj = [] for p in parameters: if isinstance(p, str): if not p in self.data: self.data[p] = quantities.Quantity(p) self.data[p].dim = Dimension() parameters_obj.append(self.data[p]) elif isinstance(p, quantities.Quantity): parameters_obj.append(p) else: raise TypeError( "parameters can only be strings or Quantity objects") # parse fit function fit_function = quantities.parse_expr(fit_function, self.data) # get data quantities x_data = quantities.parse_expr(xydata[0], self.data) # if x-data is an expression if not isinstance(x_data, quantities.Quantity): dummy = quantities.Quantity() fit_function = fit_function.subs(x_data, dummy) dummy.value = quantities.get_value(x_data) dummy.error = quantities.get_error(x_data)[0] dummy.dim = quantities.get_dimension(x_data) x_data = dummy y_data = quantities.parse_expr(xydata[1], self.data) # if y-data is an expression if not isinstance(y_data, quantities.Quantity): dummy = quantities.Quantity() dummy.value = quantities.get_value(y_data) dummy.error = quantities.get_error(y_data)[0] dummy.dim = quantities.get_dimension(y_data) y_data = dummy # check if dimension fits if not ignore_dim: try: dim_func = quantities.get_dimension(fit_function) except ValueError: dim_func = None if not dim_func == y_data.dim: # try to solve for dimensionless parameters known_dimensions = {x_data.name: x_data.dim} known_dimensions = dim_solve(fit_function, y_data.dim, known_dimensions) for q_name in known_dimensions: if q_name in self.data: if not self.data[q_name].dim == known_dimensions[ q_name]: self.data[q_name].dim = known_dimensions[q_name] self.data[q_name].prefer_unit = None dim_func = quantities.get_dimension(fit_function) # if it still doesn't work, raise error if not dim_func == y_data.dim: raise RuntimeError("Finding dimensions of fit parameters was not sucessful.\n"\ "Check fit function or specify parameter units manually.\n"\ "This error will occur until dimensions are right.") # fit values, errors = fit_module.fit(x_data, y_data, fit_function, parameters_obj, weighted) # save results i = 0 for p in parameters_obj: p.value = values[i] p.value_formula = "fit" p.error = errors[i] p.error_formula = "fit" i += 1 # plot if plot: return plotting.plot([(x_data, y_data), (x_data, fit_function)], self.config, ignore_dim=ignore_dim) else: return self.table(*parameters_obj)
def assign(value, error=None, unit=None, name=None, longname=None, value_unit=None, error_unit=None, ignore_dim=False): """ function to create a new quantity Args: value: number or string that can be parsed by numpy, or sympy expression. If it's a sympy expression containing quantities, it will perform the calculation, otherwise it just saves the value. error: number that is saved as the value's uncertainty. this will replace any error coming from a calculation. unit: sympy expression of Unit objects. This is used to convert and save value and error in base units. Replaces value_unit and error_unit if specified. name: short name of the quantity (usually one letter). If not specified, quantity will get a dummy name. longname: optional additional description of the quantity value_unit: unit of value. Use this if value and error have different units. error_unit: unit of error. ignore_dim: bool. Keeps function from raising an error even if calculated and given unit don't match. Then given unit is used instead. """ value_formula = None value_factor = 1 value_dim = Dimension() error_formula = None error_factor = 1 error_dim = Dimension() # parse units if unit is not None: # if one general unit is given value_factor, value_dim, value_unit = parse_unit(unit) error_factor = value_factor error_dim = value_dim error_unit = value_unit else: # if value unit is given if value_unit is not None: value_factor, value_dim, value_unit = parse_unit(value_unit) # if error unit is given if error_unit is not None: error_factor, error_dim, error_unit = parse_unit(error_unit) # check dimension consistency between value_dim and error_dim if value_unit is not None and not value_dim == error_dim: raise RuntimeError("dimension mismatch\n%s != %s" % (value_dim, error_dim)) # process value # if it's a calculation if isinstance(value, Expr) and not value.is_number: value_formula = value value = get_value(value_formula) if ignore_dim: # with ignore_dim=True, calculated value is converted to given unit value = np.float_(value_factor)*np.float_(value) else: # calculate dimension from dependency calculated_dim = get_dimension(value_formula) if value_unit is None: value_dim = calculated_dim else: if not calculated_dim == value_dim: raise RuntimeError("dimension mismatch \n%s != %s" % (value_dim, calculated_dim)) # if it's a number else: value=np.float_(value_factor)*np.float_(value) # process error if error is not None: error=np.float_(error_factor)*np.float_(error) # check value and error shapes and duplicate error in case if error.shape == () or value.shape[-len(error.shape):] == error.shape: error = np.resize(error, value.shape) else: raise RuntimeError("length of value and error don't match and "\ "can't be adjusted by duplicating.\n"\ "%s and %s" % (value.shape, error.shape)) # if error can be calculated elif value_formula is not None: error, error_formula = get_error(value_formula) if ignore_dim: # with ignore_dim=True, calculated error is converted to given unit error = np.float_(error_factor)*np.float_(error) q = Quantity(name, longname) q.value = value q.value_formula = value_formula q.error = error q.error_formula = error_formula if value_unit is not None: q.prefer_unit = value_unit else: q.prefer_unit = error_unit q.dim = value_dim return q
def test_dim_solve_global(self): self.assertEqual(dim_solve_global(Symbol('x'), {'x':Dimension({})}),Dimension({})) self.assertEqual(dim_solve_global(Symbol('x'), {}), None) self.assertEqual(dim_solve_global(sympify('t-b'), {'t':Dimension({'time':1})}), Dimension({'time':1})) self.assertEqual(dim_solve_global(sympify('t-b'), {'t':Dimension({'time':1}), 'b':Dimension({'time':1})}), Dimension({'time':1}))