예제 #1
0
def apply_geometry_lowering(form, preserve_types=()):
    """Change GeometricQuantity objects in expression to the lowest level GeometricQuantity objects.

    Assumes the expression is preprocessed or at least that derivatives have been expanded.

    @param form:
        An Expr or Form.
    """
    if isinstance(form, Form):
        newintegrals = [
            apply_geometry_lowering(integral, preserve_types)
            for integral in form.integrals()
        ]
        return Form(newintegrals)

    elif isinstance(form, Integral):
        integral = form
        if integral.integral_type() in (custom_integral_types +
                                        point_integral_types):
            automatic_preserve_types = [SpatialCoordinate, Jacobian]
        else:
            automatic_preserve_types = [CellCoordinate]
        preserve_types = set(preserve_types) | set(automatic_preserve_types)

        mf = GeometryLoweringApplier(preserve_types)
        newintegrand = map_expr_dag(mf, integral.integrand())
        return integral.reconstruct(integrand=newintegrand)

    elif isinstance(form, Expr):
        expr = form
        mf = GeometryLoweringApplier(preserve_types)
        return map_expr_dag(mf, expr)

    else:
        error("Invalid type %s" % (form.__class__.__name__, ))
예제 #2
0
def apply_integral_scaling(form):
    "Multiply integrands by a factor to scale the integral to reference frame."
    # TODO: Consider adding an in_reference_frame property to Integral
    #       and checking it here and setting it in the returned form
    if isinstance(form, Form):
        newintegrals = [
            apply_integral_scaling(integral) for integral in form.integrals()
        ]
        return Form(newintegrals)

    elif isinstance(form, Integral):
        integral = form
        integrand = integral.integrand()
        # Compute and apply integration scaling factor since we want to compute
        # coordinate derivatives at the end, the scaling factor has to be moved
        # inside those
        scale = compute_integrand_scaling_factor(integral)

        def scale_coordinate_derivative(o, scale):
            o_ = o.ufl_operands
            if isinstance(o, CoordinateDerivative):
                return CoordinateDerivative(
                    scale_coordinate_derivative(o_[0], scale), o_[1], o_[2],
                    o_[3])
            else:
                return scale * o

        newintegrand = scale_coordinate_derivative(integrand, scale)
        return integral.reconstruct(integrand=newintegrand)

    else:
        error("Invalid type %s" % (form.__class__.__name__, ))
예제 #3
0
def attach_estimated_degrees(form):
    """Attach estimated polynomial degree to a form's integrals.

    :arg form: The :class:`~.Form` to inspect.
    :returns: A new Form with estimate degrees attached.
    """
    integrals = form.integrals()

    new_integrals = []
    for integral in integrals:
        md = {}
        md.update(integral.metadata())
        degree = estimate_total_polynomial_degree(integral.integrand())
        md["estimated_polynomial_degree"] = degree
        new_integrals.append(integral.reconstruct(metadata=md))
    return Form(new_integrals)
예제 #4
0
def apply_integral_scaling(form):
    "Multiply integrands by a factor to scale the integral to reference frame."
    # TODO: Consider adding an in_reference_frame property to Integral
    #       and checking it here and setting it in the returned form
    if isinstance(form, Form):
        newintegrals = [
            apply_integral_scaling(integral) for integral in form.integrals()
        ]
        return Form(newintegrals)

    elif isinstance(form, Integral):
        integral = form
        integrand = integral.integrand()
        # Compute and apply integration scaling factor since we want to compute
        # coordinate derivatives at the end, the scaling factor has to be moved
        # inside those
        scale, degree = compute_integrand_scaling_factor(integral)
        md = {}
        md.update(integral.metadata())
        new_degree = degree
        cur_degree = md.get("estimated_polynomial_degree")
        if cur_degree is not None:
            if isinstance(cur_degree, tuple) and isinstance(degree, tuple):
                new_degree = tuple(d[0] + d[1]
                                   for d in zip(cur_degree, degree))
            elif isinstance(cur_degree, tuple):
                new_degree = tuple(d + degree for d in cur_degree)
            elif isinstance(degree, tuple):
                new_degree = tuple(cur_degree + d for d in degree)
            else:
                new_degree = cur_degree + degree
        md["estimated_polynomial_degree"] = new_degree

        def scale_coordinate_derivative(o, scale):
            o_ = o.ufl_operands
            if isinstance(o, CoordinateDerivative):
                return CoordinateDerivative(
                    scale_coordinate_derivative(o_[0], scale), o_[1], o_[2],
                    o_[3])
            else:
                return scale * o

        newintegrand = scale_coordinate_derivative(integrand, scale)
        return integral.reconstruct(integrand=newintegrand, metadata=md)

    else:
        error("Invalid type %s" % (form.__class__.__name__, ))
예제 #5
0
def attach_coordinate_derivatives(form, coordinate_derivatives):
    if coordinate_derivatives is None:
        return form

    if isinstance(form, Form):
        cds = coordinate_derivatives
        new_integrals = [attach_coordinate_derivatives(integ, cds) for integ in form.integrals()]
        return Form(new_integrals)

    elif isinstance(form, Integral):
        integral = form
        integrand = integral.integrand()
        # apply the stored coordinate derivatives back onto the integrand
        for tup in reversed(coordinate_derivatives):
            integrand = CoordinateDerivative(integrand, tup[0], tup[1], tup[2])
        return integral.reconstruct(integrand=integrand)
    else:
        error("Invalid type %s" % (form.__class__.__name__,))
예제 #6
0
def replace_terminal_data(o, mapping):
    """Return a new form where the terminals have been replaced using the
    provided mapping.

    :arg o: The object to have its terminals replaced. This must either be a
        :class:`~.Form` or :class:`~.Integral`.
    :arg mapping: A mapping suitable for reconstructing the form such as the one
        returned by :func:`strip_terminal_data`.
    :returns: The new form.
    """
    if isinstance(o, Form):
        return Form(
            [replace_terminal_data(itg, mapping) for itg in o.integrals()])
    elif isinstance(o, Integral):
        expr_map, domain_map = mapping
        integrand = replace(o.integrand(), expr_map)
        return o.reconstruct(integrand, domain=domain_map[o.ufl_domain()])
    else:
        raise ValueError("Only Form or Integral inputs expected")
예제 #7
0
def apply_integral_scaling(form):
    "Multiply integrands by a factor to scale the integral to reference frame."
    # TODO: Consider adding an in_reference_frame property to Integral
    #       and checking it here and setting it in the returned form
    if isinstance(form, Form):
        newintegrals = [
            apply_integral_scaling(integral) for integral in form.integrals()
        ]
        return Form(newintegrals)

    elif isinstance(form, Integral):
        integral = form
        # Compute and apply integration scaling factor
        scale = compute_integrand_scaling_factor(integral)
        newintegrand = integral.integrand() * scale
        return integral.reconstruct(integrand=newintegrand)

    else:
        error("Invalid type %s" % (form.__class__.__name__, ))
예제 #8
0
def strip_coordinate_derivatives(form):

    if isinstance(form, Form):
        if len(form.integrals()) == 0:
            return form, None
        stripped_integrals = []
        coordinate_derivatives = []
        for integral in form.integrals():
            (si, cd) = strip_coordinate_derivatives(integral)
            stripped_integrals.append(si)
            coordinate_derivatives.append(cd)
        assert_that_coordinate_derivatives_are_the_same(coordinate_derivatives)
        # now that we have checked that the coordinate derivative that we apply is
        # the same for all integrals, we only have to return them for the first integral
        return (Form(stripped_integrals), coordinate_derivatives[0])

    elif isinstance(form, Integral):
        integral = form
        integrand = integral.integrand()
        checker = CoordinateDerivativeIsOutermostChecker()
        map_expr_dags(checker, [integrand])
        coordinate_derivatives = []

        # grab all coordinate derivatives and store them, so that we can apply
        # them later again
        def take_top_coordinate_derivatives(o):
            o_ = o.ufl_operands
            if isinstance(o, CoordinateDerivative):
                coordinate_derivatives.append((o_[1], o_[2], o_[3]))
                return take_top_coordinate_derivatives(o_[0])
            else:
                return o

        newintegrand = take_top_coordinate_derivatives(integrand)
        return (integral.reconstruct(integrand=newintegrand), coordinate_derivatives)

    else:
        error("Invalid type %s" % (form.__class__.__name__,))
예제 #9
0
def strip_terminal_data(o):
    """Return a new form where all terminals have been replaced by UFL-only
    equivalents.

    :arg o: The object to be stripped. This must either be a :class:`~.Form`
        or :class:`~.Integral`.
    :returns: A 2-tuple containing an equivalent UFL-only object and a mapping
        allowing the original form to be reconstructed using
        :func:`replace_terminal_data`.

    This function is useful for forms containing augmented UFL objects that
    hold references to large data structures. These objects are be extracted
    into the mapping allowing the form to be cached without leaking memory.
    """
    # We need to keep track of two maps because integrals store references to the
    # domain and ``replace`` expects only a mapping containing ``Expr`` objects.
    if isinstance(o, Form):
        integrals = []
        expr_map = {}
        domain_map = {}
        for integral in o.integrals():
            itg, (emap, dmap) = strip_terminal_data(integral)
            integrals.append(itg)
            expr_map.update(emap)
            domain_map.update(dmap)
        return Form(integrals), (expr_map, domain_map)
    elif isinstance(o, Integral):
        handler = TerminalStripper()
        integrand = map_expr_dag(handler, o.integrand())
        domain = strip_domain(o.ufl_domain())
        # invert the mapping so it can be passed straight into replace_terminal_data
        expr_map = {v: k for k, v in handler.mapping.items()}
        domain_map = {domain: o.ufl_domain()}
        return o.reconstruct(integrand, domain=domain), (expr_map, domain_map)
    else:
        raise ValueError("Only Form or Integral inputs expected")