Example #1
0
def accumulate_integrals(itg_data, quadrature_rule_sizes):
    """Group and accumulate integrals according to the number of quadrature points in their rules."""
    if not itg_data.integrals:
        return {}

    # Group integrands by quadrature rule
    sorted_integrands = collections.defaultdict(list)
    if itg_data.integral_type in custom_integral_types:
        # Should only be one size here, ignoring irrelevant metadata and parameters
        num_points, = quadrature_rule_sizes.values()
        for integral in itg_data.integrals:
            sorted_integrands[num_points].append(integral.integrand())
    else:
        default_scheme = itg_data.metadata["quadrature_degree"]
        default_degree = itg_data.metadata["quadrature_rule"]
        for integral in itg_data.integrals:
            md = integral.metadata() or {}
            scheme = md.get("quadrature_rule", default_scheme)
            degree = md.get("quadrature_degree", default_degree)
            rule = (scheme, degree)
            num_points = quadrature_rule_sizes[rule]
            sorted_integrands[num_points].append(integral.integrand())

    # Accumulate integrands in a canonical ordering defined by UFL
    sorted_integrals = {
        num_points:
        Integral(sorted_expr_sum(integrands), itg_data.integral_type,
                 itg_data.domain, itg_data.subdomain_id, {}, None)
        for num_points, integrands in list(sorted_integrands.items())
    }
    return sorted_integrals
Example #2
0
def _sort_integrals(integrals, metadata, form_data):
    """Sort integrals according to the number of quadrature points needed per axis.
    Only consider those integrals defined on the given domain."""

    # Get domain type and id
    if integrals:
        domain_type = integrals[0].domain_type()
        domain_id = integrals[0].domain_id()
        ffc_assert(all(domain_type == itg.domain_type() for itg in integrals),
                   "Expecting only integrals on the same subdomain.")
        ffc_assert(all(domain_id == itg.domain_id() for itg in integrals),
                   "Expecting only integrals on the same subdomain.")

    sorted_integrands = {}
    # TODO: We might want to take into account that a form like
    # a = f*g*h*v*u*dx(0, quadrature_order=4) + f*v*u*dx(0, quadrature_order=2),
    # although it involves two integrals of different order, will most
    # likely be integrated faster if one does
    # a = (f*g*h + f)*v*u*dx(0, quadrature_order=4)
    # It will of course only work for integrals defined on the same
    # subdomain and representation.
    for integral in integrals:
        # Get default degree and rule.
        degree = metadata["quadrature_degree"]
        rule = metadata["quadrature_rule"]

        # Override if specified in integral metadata
        integral_compiler_data = integral.compiler_data()
        if not integral_compiler_data is None:
            if "quadrature_degree" in integral_compiler_data:
                degree = integral_compiler_data["quadrature_degree"]
            if "quadrature_rule" in integral_compiler_data:
                rule = integral_compiler_data["quadrature_rule"]

        # Add integrand to dictionary according to degree and rule.
        if not (degree, rule) in sorted_integrands:
            sorted_integrands[(degree, rule)] = [integral.integrand()]
        else:
            sorted_integrands[(degree, rule)] += [integral.integrand()]

    # Create integrals from accumulated integrands.
    sorted_integrals = {}
    for key, integrands in sorted_integrands.items():
        # Summing integrands in a canonical ordering defined by UFL
        integrand = sorted_expr_sum(integrands)
        sorted_integrals[key] = Integral(integrand, domain_type, domain_id, {},
                                         None)
    return sorted_integrals
def _sort_integrals(integrals, metadata, form_data):
    """Sort integrals according to the number of quadrature points needed per axis.
    Only consider those integrals defined on the given domain."""

    # Get domain type and id
    if integrals:
        domain_type = integrals[0].domain_type()
        domain_id = integrals[0].domain_id()
        ffc_assert(all(domain_type == itg.domain_type() for itg in integrals),
                   "Expecting only integrals on the same subdomain.")
        ffc_assert(all(domain_id == itg.domain_id() for itg in integrals),
                   "Expecting only integrals on the same subdomain.")

    sorted_integrands = {}
    # TODO: We might want to take into account that a form like
    # a = f*g*h*v*u*dx(0, quadrature_order=4) + f*v*u*dx(0, quadrature_order=2),
    # although it involves two integrals of different order, will most
    # likely be integrated faster if one does
    # a = (f*g*h + f)*v*u*dx(0, quadrature_order=4)
    # It will of course only work for integrals defined on the same
    # subdomain and representation.
    for integral in integrals:
        # Get default degree and rule.
        degree = metadata["quadrature_degree"]
        rule  = metadata["quadrature_rule"]

        # Override if specified in integral metadata
        integral_compiler_data = integral.compiler_data()
        if not integral_compiler_data is None:
            if "quadrature_degree" in integral_compiler_data:
                degree = integral_compiler_data["quadrature_degree"]
            if "quadrature_rule" in integral_compiler_data:
                rule = integral_compiler_data["quadrature_rule"]

        # Add integrand to dictionary according to degree and rule.
        if not (degree, rule) in sorted_integrands:
            sorted_integrands[(degree, rule)] = [integral.integrand()]
        else:
            sorted_integrands[(degree, rule)] += [integral.integrand()]

    # Create integrals from accumulated integrands.
    sorted_integrals = {}
    for key, integrands in sorted_integrands.items():
        # Summing integrands in a canonical ordering defined by UFL
        integrand = sorted_expr_sum(integrands)
        sorted_integrals[key] = Integral(integrand, domain_type, domain_id, {}, None)
    return sorted_integrals
def sort_integrals(integrals, default_quadrature_degree,
                   default_quadrature_rule):
    """Sort and accumulate integrals according to the number of quadrature points needed per axis.

    All integrals should be over the same (sub)domain.
    """

    if not integrals:
        return {}

    # Get domain properties from first integral, assuming all are the same
    integral_type = integrals[0].integral_type()
    subdomain_id = integrals[0].subdomain_id()
    domain_label = integrals[0].domain().label()
    domain = integrals[0].domain()  # FIXME: Is this safe? Get as input?
    ffc_assert(all(integral_type == itg.integral_type() for itg in integrals),
               "Expecting only integrals of the same type.")
    ffc_assert(all(domain_label == itg.domain().label() for itg in integrals),
               "Expecting only integrals on the same domain.")
    ffc_assert(all(subdomain_id == itg.subdomain_id() for itg in integrals),
               "Expecting only integrals on the same subdomain.")

    sorted_integrands = collections.defaultdict(list)
    for integral in integrals:
        # Override default degree and rule if specified in integral metadata
        integral_metadata = integral.metadata() or {}
        degree = integral_metadata.get("quadrature_degree",
                                       default_quadrature_degree)
        rule = integral_metadata.get("quadrature_rule",
                                     default_quadrature_rule)
        assert isinstance(degree, int)
        # Add integrand to dictionary according to degree and rule.
        key = (degree, rule)
        sorted_integrands[key].append(integral.integrand())

    # Create integrals from accumulated integrands.
    sorted_integrals = {}
    for key, integrands in list(sorted_integrands.items()):
        # Summing integrands in a canonical ordering defined by UFL
        integrand = sorted_expr_sum(integrands)
        sorted_integrals[key] = Integral(integrand, integral_type, domain,
                                         subdomain_id, {}, None)
    return sorted_integrals
Example #5
0
def sort_integrals(integrals, default_scheme, default_degree):
    """Sort and accumulate integrals according to the number of quadrature
    points needed per axis.

    All integrals should be over the same (sub)domain.
    """

    if not integrals:
        return {}

    # Get domain properties from first integral, assuming all are the
    # same
    integral_type = integrals[0].integral_type()
    subdomain_id = integrals[0].subdomain_id()
    domain = integrals[0].ufl_domain()
    ffc_assert(all(integral_type == itg.integral_type() for itg in integrals),
               "Expecting only integrals of the same type.")
    ffc_assert(all(domain == itg.ufl_domain() for itg in integrals),
               "Expecting only integrals on the same domain.")
    ffc_assert(all(subdomain_id == itg.subdomain_id() for itg in integrals),
               "Expecting only integrals on the same subdomain.")

    sorted_integrands = collections.defaultdict(list)
    for integral in integrals:
        # Override default degree and rule if specified in integral
        # metadata
        integral_metadata = integral.metadata() or {}
        degree = integral_metadata.get("quadrature_degree", default_degree)
        scheme = integral_metadata.get("quadrature_rule", default_scheme)
        assert isinstance(degree, int)
        # Add integrand to dictionary according to degree and scheme.
        rule = (scheme, degree)
        sorted_integrands[rule].append(integral.integrand())

    # Create integrals from accumulated integrands.
    sorted_integrals = {}
    for rule, integrands in list(sorted_integrands.items()):
        # Summing integrands in a canonical ordering defined by UFL
        integrand = sorted_expr_sum(integrands)
        sorted_integrals[rule] = Integral(integrand, integral_type, domain,
                                          subdomain_id, {}, None)
    return sorted_integrals
Example #6
0
def _compute_integral_ir(form_data, form_index, prefix, element_numbers,
                         integral_names, parameters, visualise):
    """Compute intermediate represention for form integrals."""

    _entity_types = {
        "cell": "cell",
        "exterior_facet": "facet",
        "interior_facet": "facet",
        "vertex": "vertex",
        "custom": "cell"
    }

    # Iterate over groups of integrals
    irs = []
    for itg_data_index, itg_data in enumerate(form_data.integral_data):

        logger.info("Computing IR for integral in integral group {}".format(
            itg_data_index))

        # Compute representation
        entitytype = _entity_types[itg_data.integral_type]
        cell = itg_data.domain.ufl_cell()
        cellname = cell.cellname()
        tdim = cell.topological_dimension()
        assert all(tdim == itg.ufl_domain().topological_dimension()
                   for itg in itg_data.integrals)

        ir = {
            "integral_type": itg_data.integral_type,
            "subdomain_id": itg_data.subdomain_id,
            "rank": form_data.rank,
            "geometric_dimension": form_data.geometric_dimension,
            "topological_dimension": tdim,
            "entitytype": entitytype,
            "num_facets": cell.num_facets(),
            "num_vertices": cell.num_vertices(),
            "needs_oriented": form_needs_oriented_jacobian(form_data),
            "enabled_coefficients": itg_data.enabled_coefficients,
            "cell_shape": cellname
        }

        # Get element space dimensions
        unique_elements = element_numbers.keys()
        ir["element_dimensions"] = {
            ufl_element: create_element(ufl_element).space_dimension()
            for ufl_element in unique_elements
        }

        ir["element_ids"] = {
            ufl_element: i
            for i, ufl_element in enumerate(unique_elements)
        }

        # Create dimensions of primary indices, needed to reset the argument
        # 'A' given to tabulate_tensor() by the assembler.
        argument_dimensions = [
            ir["element_dimensions"][ufl_element]
            for ufl_element in form_data.argument_elements
        ]

        # Compute shape of element tensor
        if ir["integral_type"] == "interior_facet":
            ir["tensor_shape"] = [2 * dim for dim in argument_dimensions]
        else:
            ir["tensor_shape"] = argument_dimensions

        integral_type = itg_data.integral_type
        cell = itg_data.domain.ufl_cell()

        # Group integrands with the same quadrature rule
        grouped_integrands = {}
        for integral in itg_data.integrals:
            md = integral.metadata() or {}
            scheme = md["quadrature_rule"]
            degree = md["quadrature_degree"]

            if scheme == "custom":
                points = md["quadrature_points"]
                weights = md["quadrature_weights"]
            elif scheme == "vertex":
                # FIXME: Could this come from FIAT?
                #
                # The vertex scheme, i.e., averaging the function value in the
                # vertices and multiplying with the simplex volume, is only of
                # order 1 and inferior to other generic schemes in terms of
                # error reduction. Equation systems generated with the vertex
                # scheme have some properties that other schemes lack, e.g., the
                # mass matrix is a simple diagonal matrix. This may be
                # prescribed in certain cases.
                if degree > 1:
                    warnings.warn(
                        "Explicitly selected vertex quadrature (degree 1), but requested degree is {}."
                        .format(degree))
                if cellname == "tetrahedron":
                    points, weights = (numpy.array([[0.0, 0.0, 0.0],
                                                    [1.0, 0.0, 0.0],
                                                    [0.0, 1.0, 0.0],
                                                    [0.0, 0.0, 1.0]]),
                                       numpy.array([
                                           1.0 / 24.0, 1.0 / 24.0, 1.0 / 24.0,
                                           1.0 / 24.0
                                       ]))
                elif cellname == "triangle":
                    points, weights = (numpy.array([[0.0, 0.0], [1.0, 0.0],
                                                    [0.0, 1.0]]),
                                       numpy.array(
                                           [1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0]))
                elif cellname == "interval":
                    # Trapezoidal rule
                    return (numpy.array([[0.0], [1.0]]),
                            numpy.array([1.0 / 2.0, 1.0 / 2.0]))
            else:
                (points, weights) = create_quadrature_points_and_weights(
                    integral_type, cell, degree, scheme)

            points = numpy.asarray(points)
            weights = numpy.asarray(weights)

            rule = QuadratureRule(points, weights)

            if rule not in grouped_integrands:
                grouped_integrands[rule] = []

            grouped_integrands[rule].append(integral.integrand())

        sorted_integrals = {}
        for rule, integrands in grouped_integrands.items():
            integrands_summed = sorted_expr_sum(integrands)

            integral_new = Integral(integrands_summed, itg_data.integral_type,
                                    itg_data.domain, itg_data.subdomain_id, {},
                                    None)
            sorted_integrals[rule] = integral_new

        # TODO: See if coefficient_numbering can be removed
        # Build coefficient numbering for UFC interface here, to avoid
        # renumbering in UFL and application of replace mapping
        coefficient_numbering = {}
        for i, f in enumerate(form_data.reduced_coefficients):
            coefficient_numbering[f] = i

        # Add coefficient numbering to IR
        ir["coefficient_numbering"] = coefficient_numbering

        index_to_coeff = sorted([(v, k)
                                 for k, v in coefficient_numbering.items()])
        offsets = {}
        width = 2 if integral_type in ("interior_facet") else 1
        _offset = 0
        for k, el in zip(index_to_coeff, form_data.coefficient_elements):
            offsets[k[1]] = _offset
            _offset += width * ir["element_dimensions"][el]

        # Copy offsets also into IR
        ir["coefficient_offsets"] = offsets

        # Build offsets for Constants
        original_constant_offsets = {}
        _offset = 0
        for constant in form_data.original_form.constants():
            original_constant_offsets[constant] = _offset
            _offset += numpy.product(constant.ufl_shape, dtype=numpy.int)

        ir["original_constant_offsets"] = original_constant_offsets

        ir["precision"] = itg_data.metadata["precision"]

        # Create map from number of quadrature points -> integrand
        integrands = {
            rule: integral.integrand()
            for rule, integral in sorted_integrals.items()
        }

        # Build more specific intermediate representation
        integral_ir = compute_integral_ir(itg_data.domain.ufl_cell(),
                                          itg_data.integral_type,
                                          ir["entitytype"], integrands,
                                          ir["tensor_shape"], parameters,
                                          visualise)

        ir.update(integral_ir)

        # Fetch name
        ir["name"] = integral_names[(form_index, itg_data_index)]

        irs.append(ir_integral(**ir))

    return irs