예제 #1
0
 def test_solve_mul(self):
     L = Dimension({"length":1})
     solution = dim_solve(
         Mul(L, Symbol("x")),
         L.mul(L)
     )
     self.assertEqual(solution["x"], L)
예제 #2
0
 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))
예제 #3
0
 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({}))
예제 #4
0
 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)
예제 #5
0
 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)
예제 #6
0
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
예제 #7
0
    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"))
예제 #8
0
 def test_solve_add(self):
     L = Dimension({"length":1})
     solution = dim_solve(
         Add(L, Symbol("x")),
         L
     )
     self.assertEqual(solution["x"], L)
예제 #9
0
 def test_solve_mul2(self):
     L = Dimension({"length":1})
     solution = dim_solve(
         Mul(Symbol("y"), Symbol("x")),
         L
     )
     self.assertEqual(solution, {})
예제 #10
0
    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))
예제 #11
0
    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)
예제 #12
0
    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)
예제 #13
0
    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)
예제 #14
0
    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)
예제 #15
0
    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))
예제 #16
0
    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))
예제 #17
0
 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)
예제 #18
0
    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
예제 #19
0
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)
예제 #20
0
 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"])
예제 #21
0
 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")
예제 #22
0
 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")
예제 #23
0
 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")
예제 #24
0
 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")
예제 #25
0
 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)
예제 #26
0
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
예제 #27
0
    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)
예제 #28
0
    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)
예제 #29
0
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
예제 #30
0
 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}))