Beispiel #1
0
    def plot(self, *expr_pairs, save=None, xunit=None, yunit=None, xrange=None, yrange=None, ignore_dim=False):
        """ Plots data or functions

        Args:
            expr_pairs: one or more pair of quantity on x-axis and on y-axis. e.g. ["p","V"]
                        y-axis can also be a function. e.g. ["t", "7*exp(t/t0)"]
            save: string of file name without extension. if specified, plot will be saved to '<save>.png'
            xunit: unit on x-axis. if not given, will find unit on its own
            yunit: unit on y-axis. if not given, will find unit on its own
            xrange: pair of x-axis range, e.g. [-5,10]
            yrange: pair of y-axis range
            ignore_dim: if True, will skip dimension check
        """

        if len(expr_pairs) == 0:#
            raise ValueError("nothing to plot specified.")

        expr_pairs_obj = []

        for expr_pair in expr_pairs:
            # parse expressions
            expr_pairs_obj.append( (quantities.parse_expr(expr_pair[0], self.data), quantities.parse_expr(expr_pair[1], self.data)) )

        if not xunit is None:
            xunit = units.parse_unit(xunit)[2]
        if not yunit is None:
            yunit = units.parse_unit(yunit)[2]
        if not xrange is None:
            xrange = [quantities.get_value(quantities.parse_expr(xrange[0], self.data)),
                      quantities.get_value(quantities.parse_expr(xrange[1], self.data))]
        if not yrange is None:
            yrange = [quantities.get_value(quantities.parse_expr(yrange[0], self.data)),
                      quantities.get_value(quantities.parse_expr(yrange[1], self.data))]
        return plotting.plot(expr_pairs_obj, self.config, save=save, xunit=xunit, yunit=yunit, xrange=xrange, yrange=yrange, ignore_dim=ignore_dim)
Beispiel #2
0
    def plot(self,
             *expr_pairs,
             save=None,
             xunit=None,
             yunit=None,
             xrange=None,
             yrange=None,
             ignore_dim=False):
        """ Plots data or functions

        Args:
            expr_pairs: one or more pair of quantity on x-axis and on y-axis. e.g. ["p","V"]
                        y-axis can also be a function. e.g. ["t", "7*exp(t/t0)"]
            save: string of file name without extension. if specified, plot will be saved to '<save>.png'
            xunit: unit on x-axis. if not given, will find unit on its own
            yunit: unit on y-axis. if not given, will find unit on its own
            xrange: pair of x-axis range, e.g. [-5,10]
            yrange: pair of y-axis range
            ignore_dim: if True, will skip dimension check
        """

        if len(expr_pairs) == 0:  #
            raise ValueError("nothing to plot specified.")

        expr_pairs_obj = []

        for expr_pair in expr_pairs:
            # parse expressions
            expr_pairs_obj.append(
                (quantities.parse_expr(expr_pair[0], self.data),
                 quantities.parse_expr(expr_pair[1], self.data)))

        if not xunit is None:
            xunit = units.parse_unit(xunit)[2]
        if not yunit is None:
            yunit = units.parse_unit(yunit)[2]
        if not xrange is None:
            xrange = [
                quantities.get_value(
                    quantities.parse_expr(xrange[0], self.data)),
                quantities.get_value(
                    quantities.parse_expr(xrange[1], self.data))
            ]
        if not yrange is None:
            yrange = [
                quantities.get_value(
                    quantities.parse_expr(yrange[0], self.data)),
                quantities.get_value(
                    quantities.parse_expr(yrange[1], self.data))
            ]
        return plotting.plot(expr_pairs_obj,
                             self.config,
                             save=save,
                             xunit=xunit,
                             yunit=yunit,
                             xrange=xrange,
                             yrange=yrange,
                             ignore_dim=ignore_dim)
Beispiel #3
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"])
Beispiel #4
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)
Beispiel #5
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
Beispiel #6
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
Beispiel #7
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"])
Beispiel #8
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)
Beispiel #9
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