Beispiel #1
0
    def __call__(self, weights, weight_label):
        """
        evaluation function for approximated control law
        :param weights: 1d ndarray of approximation weights
        :param weight_label: string, label of functions the weights correspond to.
        :return: control output u
        """
        output = 0 + 0j

        # add dynamic part
        for lbl, law in self._cfs.get_dynamic_terms().iteritems():
            dst_weights = [0]
            if law[0] is not None:
                # build eval vector
                if lbl not in self._eval_vectors.keys():
                    self._eval_vectors[lbl] = self._build_eval_vector(law)

                # collect information
                info = TransformationInfo()
                info.src_lbl = weight_label
                info.dst_lbl = lbl
                info.src_base = get_base(weight_label, 0)
                info.dst_base = get_base(lbl, 0)
                info.src_order = int(weights.size / info.src_base.size) - 1
                info.dst_order = int(self._eval_vectors[lbl].size / info.dst_base.size) - 1

                if info not in self._transformations.keys():
                    # fetch handle
                    handle = get_weight_transformation(info)
                    self._transformations[info] = handle

                dst_weights = self._transformations[info](weights)
                output += np.dot(self._eval_vectors[lbl], dst_weights)

            if self._storage is not None:
                entry = self._storage.get(info.dst_lbl, [])
                entry.append(dst_weights)
                self._storage[info.dst_lbl] = entry

        # add constant term
        static_terms = self._cfs.get_static_terms()
        if static_terms[1] is not None:
            output += static_terms[1][0]

        # TODO: replace with the one from utils
        if abs(np.imag(output)) > np.finfo(np.complex128).eps * 100:
            print("Warning: Imaginary part of output is nonzero! out = {0}".format(output))

        out = np.real_if_close(output, tol=10000000)
        if np.imag(out) != 0:
            raise sim.SimulationException(
                "calculated complex control output u={0}," " check for errors in control law!".format(out)
            )

        return out
Beispiel #2
0
def evaluate_approximation(base_label, weights, temp_domain, spat_domain, spat_order=0, name=""):
    """
    evaluate an approximation given by weights and functions at the points given in spatial and temporal steps

    :param weights: 2d np.ndarray where axis 1 is the weight index and axis 0 the temporal index
    :param base_label: functions to use for back-projection
    :param temp_domain: steps to evaluate at
    :param spat_domain: sim.Domain to evaluate at (or in)
    :param spat_order: spatial derivative order to use
    :param name: name to use
    :return: EvalData
    """
    funcs = get_base(base_label, spat_order)
    if weights.shape[1] != funcs.shape[0]:
        raise ValueError("weights (len={0}) have to fit provided functions (len={1})!".format(weights.shape[1],
                                                                                              funcs.size))

    # evaluate shape functions at given points
    if isinstance(base_label[0], LagrangeFirstOrder):
        # shortcut for fem approximations
        shape_vals = [func.top for func in funcs]
    else:
        shape_vals = [func(spat_domain) for func in funcs]
    shape_vals = np.atleast_2d(shape_vals)

    def eval_spatially(weight_vector):
        return np.real_if_close(np.dot(weight_vector, shape_vals), 1000)

    data = np.apply_along_axis(eval_spatially, 1, weights)
    return vis.EvalData([temp_domain, spat_domain], data, name=name)
Beispiel #3
0
def process_sim_data(weight_lbl, q, temp_domain, spat_domain, temp_order, spat_order, name=""):
    """
    create handles and evaluate at given points
    :param weight_lbl: label of Basis for reconstruction
    :param temp_order: order or temporal derivatives to evaluate additionally
    :param spat_order: order or spatial derivatives to evaluate additionally
    :param q: weights
    :param spat_domain: sim.Domain object providing values for spatial evaluation
    :param temp_domain: timesteps on which rows of q are given
    :param name: name of the WeakForm, used to generate the dataset
    """
    data = []

    # temporal
    ini_funcs = get_base(weight_lbl, 0)
    for der_idx in range(temp_order+1):
        name = "{0}{1}".format(name, "_" + "".join(["d" for x in range(der_idx)] + ["t"]) if der_idx > 0 else "")
        data.append(evaluate_approximation(weight_lbl, q[:, der_idx * ini_funcs.size:(der_idx + 1) * ini_funcs.size],
                                           temp_domain, spat_domain, name=name))

    # spatial (0th derivative is skipped since this is already handled above)
    for der_idx in range(1, spat_order+1):
        name = "{0}{1}".format(name, "_" + "".join(["d" for x in range(der_idx)] + ["z"]) if der_idx > 0 else "")
        data.append(
            evaluate_approximation(weight_lbl, q[:, :ini_funcs.size], temp_domain, spat_domain, der_idx, name=name))

    return data
Beispiel #4
0
def _parse_control_law(law):
    """
    parses the given control law by approximating given terms
    :param law:  list of equation terms
    :return: evaluation handle
    """
    # check terms
    for term in law.terms:
        if not isinstance(term, EquationTerm):
            raise TypeError("only EquationTerm(s) accepted.")

    cfs = sim.CanonicalForms(law.name)

    for term in law.terms:
        placeholders = dict(
            [
                ("field_variables", term.arg.get_arg_by_class(FieldVariable)),
                ("scalars", term.arg.get_arg_by_class(Scalars)),
            ]
        )
        if placeholders["field_variables"]:
            field_var = placeholders["field_variables"][0]
            temp_order = field_var.order[0]
            func_lbl = field_var.data["func_lbl"]
            weight_lbl = field_var.data["weight_lbl"]
            init_funcs = get_base(func_lbl, field_var.order[1])

            factors = np.atleast_2d(
                [integrate_function(func, domain_intersection(term.limits, func.nonzero))[0] for func in init_funcs]
            )

            if placeholders["scalars"]:
                scales = placeholders["scalars"][0]
                res = np.prod(np.array([factors, scales]), axis=0)
            else:
                res = factors

            cfs.add_to(weight_lbl, ("E", temp_order), res * term.scale)

        elif placeholders["scalars"]:
            # TODO make sure that all have the same target form!
            scalars = placeholders["scalars"]
            if len(scalars) > 1:
                # TODO if one of 'em is just a scalar and no array an error occurs
                res = np.prod(np.array([scalars[0].data, scalars[1].data]), axis=0)
            else:
                res = scalars[0].data

            cfs.add_to(scalars[0].target_form, get_scalar_target(scalars), res * term.scale)

        else:
            raise NotImplementedError

    return cfs
Beispiel #5
0
def get_weight_transformation(info):
    """
    somehow calculates a handle that will transform weights from src into weights for dst with the given derivative
    orders.

    :param info: transformation info
    :return: handle
    """
    # trivial case
    if info.src_lbl == info.dst_lbl:
        mat = calculate_expanded_base_transformation_matrix(info.src_base, None, info.src_order, info.dst_order, True)

        def identity(weights):
            return np.dot(mat, weights)

        return identity

    # try to get help from the destination base
    handle, hint = info.dst_base[0].transformation_hint(info, True)
    # if handle is None:
    #     # try source instead
    #     handle, hint = info.src_base[0].transformation_hint(info, False)

    if handle is None:
        raise TypeError("no transformation between given bases possible!")

    # check termination criterion
    if hint is None:
        # direct transformation possible
        return handle

    kwargs = {}
    new_handle = None
    if hasattr(hint, "extras"):
        # try to gain transformations that will satisfy the extra terms
        for dep_lbl, dep_order in hint.extras.iteritems():
            new_info = copy(info)
            new_info.dst_lbl = dep_lbl
            new_info.dst_base = get_base(dep_lbl, 0)
            new_info.dst_order = dep_order
            dep_handle = get_weight_transformation(new_info)
            kwargs[dep_lbl] = dep_handle

    if hint.src_lbl is not None:
        # transformation to assistant system required
        new_handle = get_weight_transformation(hint)

    def last_handle(weights):
        if new_handle:
            return handle(new_handle(weights), **kwargs)
        else:
            return handle(weights, **kwargs)

    return last_handle
Beispiel #6
0
def evaluate_placeholder_function(placeholder, input_values):
    """
    evaluate a given placeholder object, that contains functions

    :param placeholder: instance of ref:py:class: FieldVariable or ref:py:class TestFunction ref:py:class ScalarFunction
    :return: results as np.ndarray
    """
    if not isinstance(placeholder, (FieldVariable, TestFunction)):
        raise TypeError("Input Object not supported!")

    funcs = get_base(placeholder.data["func_lbl"], placeholder.order[1])
    return np.array([func(input_values) for func in funcs])
Beispiel #7
0
    def _simplify_product(a, b):
        # try to simplify expression containing ScalarFunctions
        scalar_func = None
        other_func = None
        for obj1, obj2 in [(a, b), (b, a)]:
            if isinstance(obj1, ScalarFunction):
                scalar_func = obj1
                if isinstance(obj2, (FieldVariable, TestFunction, ScalarFunction)):
                    other_func = obj2
                    break

        if scalar_func and other_func:
            s_func = get_base(scalar_func.data["func_lbl"], scalar_func.order[1])
            o_func = get_base(other_func.data["func_lbl"], other_func.order[1])

            if s_func.shape != o_func.shape:
                if s_func.shape[0] == 1:
                    # only one function provided, use it for all others
                    s_func = s_func[[0 for i in range(o_func.shape[0])]]
                else:
                    raise ValueError("Cannot simplify Product due to dimension mismatch!")

            new_func = np.asarray([func.scale(scale_func) for func, scale_func in zip(o_func, s_func)])
            new_name = new_func.tostring()
            register_base(new_name, new_func)

            if isinstance(other_func, (ScalarFunction, TestFunction)):
                a = other_func.__class__(function_label=new_name, order=other_func.order[1],
                                         location=other_func.location)
            elif isinstance(other_func, FieldVariable):
                # overwrite spatial derivative order, since derivation has been performed
                a = FieldVariable(function_label=new_name, weight_label=other_func.data["weight_lbl"],
                                  order=(other_func.order[0], 0), location=other_func.location)
            b = None

        return a, b
Beispiel #8
0
def simulate_system(weak_form, initial_states, temporal_domain, spatial_domain, der_orders=(0, 0)):
    """
    convenience wrapper that encapsulates the whole simulation process

    :param weak_form:
    :param initial_states: np.array of core.Functions for :math:`x(t=0, z), \\dot{x}(t=0, z), \\dotsc, x^{(n)}(t=0, z)`
    :param temporal_domain: sim.Domain object holding information for time evaluation
    :param spatial_domain: sim.Domain object holding information for spatial evaluation
    :param der_orders: tuple of derivative orders (time, spat) that shall be evaluated additionally

    :return: list of EvalData object, holding the results for the FieldVariable and asked derivatives
    """
    print("simulating system: {0}".format(weak_form.name))
    if not isinstance(weak_form, WeakFormulation):
        raise TypeError("only WeakFormulation accepted.")

    initial_states = np.atleast_1d(initial_states)
    if not isinstance(initial_states[0], Function):
        raise TypeError("only core.Function accepted as initial state")

    if not isinstance(temporal_domain, Domain) or not isinstance(spatial_domain, Domain):
        raise TypeError("domains must be given as Domain object")

    # parse input and create state space system
    print(">>> parsing formulation")
    canonical_form = parse_weak_formulation(weak_form)
    print(">>> creating state space system")
    state_space_form = canonical_form.convert_to_state_space()

    # calculate initial state
    print(">>> deriving initial conditions")
    q0 = np.array([project_on_base(initial_state, get_base(
        canonical_form.weights, 0)) for initial_state in
                   initial_states]).flatten()

    # simulate
    print(">>> performing time step integration")
    sim_domain, q = simulate_state_space(state_space_form, canonical_form.input_function, q0, temporal_domain)

    # evaluate
    print(">>> performing postprocessing")
    temporal_order = min(initial_states.size-1, der_orders[0])
    data = process_sim_data(canonical_form.weights, q, sim_domain, spatial_domain, temporal_order, der_orders[1],
                            name=canonical_form.name)

    print("finished simulation.")
    return data
Beispiel #9
0
def _evaluate_placeholder(placeholder):
    """
    evaluates a placeholder object and returns a Scalars object

    :param placeholder:
    :return:
    """
    if not isinstance(placeholder, Placeholder):
        raise TypeError("only placeholders supported")
    if isinstance(placeholder, (Scalars, Input)):
        raise TypeError("provided type cannot be evaluated")

    functions = get_base(placeholder.data['func_lbl'], placeholder.order[1])
    location = placeholder.location
    values = np.atleast_2d([func(location) for func in functions])

    if isinstance(placeholder, FieldVariable):
        return Scalars(values, target_term=("E", placeholder.order[0]), target_form=placeholder.data["weight_lbl"])
    elif isinstance(placeholder, TestFunction):
        # target form does not matter, since the f vector is added independently
        return Scalars(values.T, target_term=("f", 0))
    else:
        raise NotImplementedError
Beispiel #10
0
def parse_weak_formulation(weak_form):
        """
        creates an ode system for the weights x_i based on the weak formulation.

        :return: simulation.ODESystem
        """
        if not isinstance(weak_form, WeakFormulation):
            raise TypeError("only able to parse WeakFormulation")

        cf = CanonicalForm(weak_form.name)

        # handle each term
        for term in weak_form.terms:
            # extract Placeholders
            placeholders = dict(scalars=term.arg.get_arg_by_class(Scalars),
                                functions=term.arg.get_arg_by_class(TestFunction),
                                field_variables=term.arg.get_arg_by_class(FieldVariable),
                                inputs=term.arg.get_arg_by_class(Input))

            # field variable terms, sort into E_n, E_n-1, ..., E_0
            if placeholders["field_variables"]:
                if len(placeholders["field_variables"]) != 1:
                    raise NotImplementedError
                field_var = placeholders["field_variables"][0]
                temp_order = field_var.order[0]
                init_funcs = get_base(field_var.data["func_lbl"], field_var.order[1])

                if placeholders["inputs"]:
                    # TODO think about this case, is it relevant?
                    raise NotImplementedError

                # is the integrand a product?
                if placeholders["functions"]:
                    if len(placeholders["functions"]) != 1:
                        raise NotImplementedError
                    func = placeholders["functions"][0]
                    test_funcs = get_base(func.data["func_lbl"], func.order[1])
                    result = calculate_scalar_product_matrix(dot_product_l2, test_funcs, init_funcs)
                else:
                    # pull constant term out and compute integral
                    a = Scalars(np.atleast_2d([integrate_function(func, func.nonzero)[0] for func in init_funcs]))

                    if placeholders["scalars"]:
                        b = placeholders["scalars"][0]
                    else:
                        b = Scalars(np.ones_like(a.data.T))

                    result = _compute_product_of_scalars([a, b])

                cf.weights = field_var.data["weight_lbl"]
                cf.add_to(("E", temp_order), result*term.scale)
                continue

            # TestFunction Terms, those will end up in f
            if placeholders["functions"]:
                if not 1 <= len(placeholders["functions"]) <= 2:
                    raise NotImplementedError
                func = placeholders["functions"][0]
                test_funcs = get_base(func.data["func_lbl"], func.order[1])

                if len(placeholders["functions"]) == 2:
                    # TODO this computation is nonesense. Result must be a vektor conataining int of (tf1*tf2)
                    raise NotImplementedError

                    func2 = placeholders["functions"][1]
                    test_funcs2 = get_base(func2.data["func_lbl"], func2.order[2])
                    result = calculate_scalar_product_matrix(dot_product_l2, test_funcs, test_funcs2)
                    cf.add_to(("f", 0), result*term.scale)
                    continue

                if placeholders["scalars"]:
                    a = placeholders["scalars"][0]
                    b = Scalars(np.vstack([integrate_function(func, func.nonzero)[0]
                                           for func in test_funcs]))
                    result = _compute_product_of_scalars([a, b])
                    cf.add_to(get_scalar_target(placeholders["scalars"]), result*term.scale)
                    continue

                if placeholders["inputs"]:
                    if len(placeholders["inputs"]) != 1:
                        raise NotImplementedError
                    input_var = placeholders["inputs"][0]
                    input_func = input_var.data
                    input_order = input_var.order

                    result = np.array([integrate_function(func, func.nonzero)[0] for func in init_funcs])
                    cf.add_to(("g", 0), result*term.scale)
                    cf.input_function = input_func
                    continue

            # pure scalar terms, sort into corresponding matrices
            if placeholders["scalars"]:
                result = _compute_product_of_scalars(placeholders["scalars"])
                target = get_scalar_target(placeholders["scalars"])

                if placeholders["inputs"]:
                    input_var = placeholders["inputs"][0]
                    input_func = input_var.data
                    input_order = input_var.order[0]

                    # this would mean that the input term should appear in a matrix like E1 or E2
                    if target[0] == "E":
                        raise NotImplementedError

                    cf.add_to(("g", 0), result*term.scale)
                    # TODO think of a more modular concept for input handling
                    cf.input_function = input_func
                    continue

                cf.add_to(target, result*term.scale)
                continue

        return cf