Пример #1
0
def _generate_physical_offsets(ufl_element, offset=0):
    """Generate offsets.

    I.e., value offset for each basis function relative to a physical
    element representation.

    """
    cell = ufl_element.cell()
    gdim = cell.geometric_dimension()
    tdim = cell.topological_dimension()

    # Refer to reference if gdim == tdim. This is a hack to support more
    # stuff (in particular restricted elements)
    if gdim == tdim:
        return _generate_reference_offsets(create_element(ufl_element))

    if isinstance(ufl_element, ufl.MixedElement):
        offsets = []
        for e in ufl_element.sub_elements():
            offsets += _generate_physical_offsets(e, offset)
            # e is a ufl element, so value_size means the physical value size
            offset += e.value_size()
        return offsets
    elif isinstance(ufl_element, ufl.EnrichedElement):
        offsets = []
        for e in ufl_element._elements:  # TODO: Avoid private member access
            offsets += _generate_physical_offsets(e, offset)
        return offsets
    elif isinstance(ufl_element, ufl.FiniteElement):
        fiat_element = create_element(ufl_element)
        return [offset] * fiat_element.space_dimension()
    else:
        raise NotImplementedError(
            "This element combination is not implemented")
Пример #2
0
def _compute_element_ir(ufl_element, element_numbers, finite_element_names,
                        epsilon):
    """Compute intermediate representation of element."""

    logger.info("Computing IR for element {}".format(ufl_element))

    # Create FIAT element
    fiat_element = create_element(ufl_element)
    cell = ufl_element.cell()
    cellname = cell.cellname()

    # Store id
    ir = {"id": element_numbers[ufl_element]}
    ir["name"] = finite_element_names[ufl_element]

    # Compute data for each function
    ir["signature"] = repr(ufl_element)
    ir["cell_shape"] = cellname
    ir["topological_dimension"] = cell.topological_dimension()
    ir["geometric_dimension"] = cell.geometric_dimension()
    ir["space_dimension"] = fiat_element.space_dimension()
    ir["value_shape"] = ufl_element.value_shape()
    ir["reference_value_shape"] = ufl_element.reference_value_shape()

    ir["degree"] = ufl_element.degree()
    ir["family"] = ufl_element.family()

    ir["evaluate_basis"] = _evaluate_basis(ufl_element, fiat_element, epsilon)
    ir["evaluate_dof"] = _evaluate_dof(ufl_element, fiat_element)
    ir["tabulate_dof_coordinates"] = _tabulate_dof_coordinates(
        ufl_element, fiat_element)
    ir["num_sub_elements"] = ufl_element.num_sub_elements()
    ir["create_sub_element"] = [
        finite_element_names[e] for e in ufl_element.sub_elements()
    ]

    if isinstance(ufl_element, ufl.VectorElement) or isinstance(
            ufl_element, ufl.TensorElement):
        ir["block_size"] = ufl_element.num_sub_elements()
        ufl_element = ufl_element.sub_elements()[0]
        fiat_element = create_element(ufl_element)
    else:
        ir["block_size"] = 1

    ir["base_permutations"] = dof_permutations.base_permutations(ufl_element)
    ir["dof_reflection_entities"] = dof_permutations.reflection_entities(
        ufl_element)
    ir["dof_face_tangents"] = dof_permutations.face_tangents(ufl_element)

    ir["dof_types"] = [i.functional_type for i in fiat_element.dual_basis()]
    ir["entity_dofs"] = fiat_element.entity_dofs()

    return ir_element(**ir)
Пример #3
0
def _generate_offsets(ufl_element, reference_offset=0, physical_offset=0):
    """Generate offsets.

    I.e., value offset for each basis function relative to a physical
    element representation.

    """
    if isinstance(ufl_element, ufl.MixedElement):
        offsets = []
        for e in ufl_element.sub_elements():
            offsets += _generate_offsets(e, reference_offset, physical_offset)
            # e is a ufl element, so value_size means the physical value size
            reference_offset += e.reference_value_size()
            physical_offset += e.value_size()
        return offsets
    elif isinstance(ufl_element, ufl.EnrichedElement):
        offsets = []
        for e in ufl_element._elements:  # TODO: Avoid private member access
            offsets += _generate_offsets(e, reference_offset, physical_offset)
        return offsets
    elif isinstance(ufl_element, ufl.FiniteElement):
        fiat_element = create_element(ufl_element)
        return [(reference_offset, physical_offset)
                ] * fiat_element.space_dimension()
    else:
        # TODO: Support RestrictedElement, QuadratureElement,
        #       TensorProductElement, etc.!  and replace
        #       _generate_{physical|reference}_offsets with this
        #       function.
        raise NotImplementedError(
            "This element combination is not implemented")
Пример #4
0
def face_tangents_from_subdofmap(ufl_element):
    """Calculate permutations and reflection entites for a root element.
    Calculates the base permutations and the entities that the direction of vector-valued
    functions depend on for an element with no sub elements."""
    fiat_element = create_element(ufl_element)

    cname = ufl_element.cell().cellname()

    dof_types = [e.functional_type for e in fiat_element.dual_basis()]
    entity_dofs = fiat_element.entity_dofs()

    rotations = []
    if cname == "tetrahedron":
        # Iterate through faces
        for entity_n in range(len(entity_dofs[2])):
            dofs = entity_dofs[2][entity_n]
            types = [dof_types[i] for i in dofs]

            # PointFaceTangent dofs
            if "PointFaceTangent" in types:
                type_dofs = [i for i, t in zip(dofs, types) if t == "PointFaceTangent"]
                for dof_pair in zip(type_dofs[::2], type_dofs[1::2]):
                    # (entity_dim, entity_number), dofs
                    rotations.append(((2, entity_n), dof_pair))
            # FrobeniusIntegralMoment dofs
            if "FrobeniusIntegralMoment" in types:
                type_dofs = [i for i, t in zip(dofs, types) if t == "FrobeniusIntegralMoment"]
                s = get_frobenius_side_length(len(type_dofs))
                for dof_pair in zip(type_dofs[3 * s::2], type_dofs[3 * s + 1::2]):
                    # (entity_dim, entity_number), dofs
                    rotations.append(((2, entity_n), dof_pair))

    return rotations
Пример #5
0
def interpolate_vertex_values(dofmap, x):

    """NB: This is the path working when the element
    has vertex dofs. If it does not one needs to take whole
    expansion and evaluate basis members at vertices.

    FIXME: Add assertion?! Or add proper fix?"""

    # Fetch data from mesh
    tdim = dofmap.mesh.reference_cell.get_dimension()
    num_vertices = dofmap.mesh.num_entities(0)

    # Fetch data from dofmap
    cell_dofs = dofmap.cell_dofs
    cell_vertex_conn = dofmap.mesh.get_connectivity(tdim, 0)

    # Build local vertex-dof connectivity
    fiat_element = fiatinterface.create_element(dofmap.element)
    vertex_dofs = fiat_element.entity_dofs()[0]
    dofs_per_vertex = len(vertex_dofs[0])
    assert dofs_per_vertex == 1, "just have to agree on ordering"
    k = 0
    vertex_dofs = [dofs[k] for entity, dofs in sorted(vertex_dofs.items())]

    # Build vertex values
    vertex_values = numpy.ndarray((num_vertices,), dtype=x.dtype)
    vertex_values[cell_vertex_conn[:]] = \
        x[numpy.ascontiguousarray(cell_dofs[:, vertex_dofs])]

    return vertex_values
Пример #6
0
def _compute_dofmap_ir(ufl_element, element_numbers, dofmap_names):
    """Compute intermediate representation of dofmap."""

    logger.info("Computing IR for dofmap of {}".format(ufl_element))

    # Create FIAT element
    fiat_element = create_element(ufl_element)

    # Precompute repeatedly used items
    num_dofs_per_entity = _num_dofs_per_entity(fiat_element)
    entity_dofs = fiat_element.entity_dofs()

    # Store id
    ir = {"id": element_numbers[ufl_element]}
    ir["name"] = dofmap_names[ufl_element]

    # Compute data for each function
    ir["signature"] = "FFCX dofmap for " + repr(ufl_element)
    ir["num_global_support_dofs"] = _num_global_support_dofs(fiat_element)
    ir["num_element_support_dofs"] = fiat_element.space_dimension(
    ) - ir["num_global_support_dofs"]
    ir["num_entity_dofs"] = num_dofs_per_entity
    ir["tabulate_entity_dofs"] = (entity_dofs, num_dofs_per_entity)
    ir["num_sub_dofmaps"] = ufl_element.num_sub_elements()
    ir["create_sub_dofmap"] = [
        dofmap_names[e] for e in ufl_element.sub_elements()
    ]
    ir["dof_types"] = [i.functional_type for i in fiat_element.dual_basis()]
    ir["base_permutations"] = dof_permutations.base_permutations(ufl_element)
    ir["dof_reflection_entities"] = dof_permutations.reflection_entities(
        ufl_element)

    return ir_dofmap(**ir)
Пример #7
0
def build_dofmap(element, mesh):
    fiat_element = fiatinterface.create_element(element)

    assert mesh.reference_cell == fiat_element.get_reference_element()
    tdim = mesh.reference_cell.get_dimension()

    # Build cell dofs - mapping of cells to global dofs.
    # cell_dofs(i, j) is global dof number for cell i and local dof
    # index j.
    cell_dofs = numpy.ndarray((mesh.num_entities(tdim),
                               fiat_element.space_dimension()),
                              dtype=numpy.int32)
    offset = 0

    for dim, local_dofs in fiat_element.entity_dofs().items():
        dofs_per_entity = len(local_dofs[0])
        connectivity = mesh.get_connectivity(tdim, dim)

        # FIXME: Dofs on single entity are not consecutive?
        for k in range(dofs_per_entity):
            entity_dofs = [dofs[k] for entity, dofs
                           in sorted(local_dofs.items())]
            cell_dofs[:, entity_dofs] = \
                dofs_per_entity * connectivity + (offset + k)

        offset += dofs_per_entity*mesh.num_entities(dim)

    # Build dofmap structure and store what it depends on
    return DofMap(cell_dofs=cell_dofs, dim=offset, mesh=mesh, element=element)
Пример #8
0
def base_permutations_from_subdofmap(ufl_element):
    """Calculate permutations and reflection entites for a root element.
    Calculates the base permutations and the entities that the direction of vector-valued
    functions depend on for an element with no sub elements."""
    fiat_element = create_element(ufl_element)
    num_dofs = len(fiat_element.dual_basis())

    tdim = ufl_element.cell().topological_dimension()

    # Get the entity counts and shape of each entity for the cell type
    entity_counts = get_entity_counts(fiat_element)
    entity_functions = get_entity_functions(ufl_element)

    # There is 1 permutation for a 1D entity, 2 for a 2D entity and 4 for a 3D entity
    num_perms = sum([0, 1, 2, 4][i] * j for i, j in enumerate(entity_counts[:tdim]))

    dof_types = [e.functional_type for e in fiat_element.dual_basis()]
    entity_dofs = fiat_element.entity_dofs()

    perms = identity_permutations(num_perms, num_dofs)
    perm_n = 0
    # Iterate through the entities of the reference element
    for dim in range(1, tdim):
        for entity_n in range(entity_counts[dim]):
            dofs = entity_dofs[dim][entity_n]
            types = [dof_types[i] for i in dofs]
            # Find the unique dof types
            unique_types = []
            for t in types:
                if t not in unique_types:
                    unique_types.append(t)
            # Permute the dofs of each entity type separately
            for t in unique_types:
                permuted = None
                type_dofs = [i for i, j in zip(dofs, types) if j == t]
                if t in ["PointEval", "PointNormalDeriv", "PointEdgeTangent",
                         "PointDeriv", "PointNormalEval", "PointScaledNormalEval"]:
                    # Dof is a point evaluation, use blocksize 1
                    permuted = entity_functions[dim](type_dofs, 1)
                elif t in ["ComponentPointEval", "IntegralMoment"]:
                    # Dof blocksize is equal to entity dimension
                    permuted = entity_functions[dim](type_dofs, dim)
                elif t == "PointFaceTangent":
                    # Dof blocksize is 2
                    permuted = entity_functions[dim](type_dofs, 2)
                elif t in ["FrobeniusIntegralMoment"] and dim == 2:
                    permuted = permute_frobenius_face(type_dofs, 1)
                else:
                    if dim < tdim:
                        # TODO: What to do with other dof types
                        warnings.warn("Permutations of " + t + " dofs not yet "
                                      "implemented. Results on unordered meshes may be incorrect")
                    continue

                # Apply these permutations
                for n, p in enumerate(permuted):
                    for i, j in zip(type_dofs, p):
                        perms[perm_n + n][i] = j
            perm_n += 2 ** (dim - 1)
    return perms
Пример #9
0
def form_needs_oriented_jacobian(form_data):
    # Check whether this form needs an oriented jacobian (only forms
    # involving contravariant piola mappings seem to need it)
    for ufl_element in form_data.unique_elements:
        element = create_element(ufl_element)
        if "contravariant piola" in element.mapping():
            return True
    return False
Пример #10
0
def num_coordinate_component_dofs(coordinate_element):
    """Get the number of dofs for a coordinate component for this degree.

    """
    fiat_elements = create_element(coordinate_element).elements()
    # Extracting only first component degrees of freedom from FIAT
    fiat_element = fiat_elements[0]
    assert (all(
        isinstance(element, type(fiat_element)) for element in fiat_elements))
    return fiat_element.space_dimension()
Пример #11
0
def reflection_entities_from_subdofmap(ufl_element):
    """Calculate permutations and reflection entites for a root element.
    Calculates the base permutations and the entities that the direction of vector-valued
    functions depend on for an element with no sub elements."""
    fiat_element = create_element(ufl_element)
    dual = fiat_element.dual_basis()
    num_dofs = len(dual)

    cname = ufl_element.cell().cellname()
    tdim = ufl_element.cell().topological_dimension()

    # Get the entity counts for the cell type
    entity_counts = get_entity_counts(fiat_element)

    dof_types = [e.functional_type for e in dual]
    entity_dofs = fiat_element.entity_dofs()

    reflections = [None for i in range(num_dofs)]
    # Iterate through the entities of the reference element
    for dim in range(1, tdim):
        for entity_n in range(entity_counts[dim]):
            dofs = entity_dofs[dim][entity_n]
            types = [dof_types[i] for i in dofs]
            # Find the unique dof types
            unique_types = []
            for t in types:
                if t not in unique_types:
                    unique_types.append(t)
            # Permute the dofs of each entity type separately
            for t in unique_types:
                type_dofs = [i for i, j in zip(dofs, types) if j == t]
                if t in [
                        "PointScaledNormalEval", "ComponentPointEval",
                        "PointEdgeTangent", "PointScaledNormalEval",
                        "PointNormalEval", "IntegralMoment"
                ]:
                    for i in type_dofs:
                        reflections[i] = [(dim, entity_n)]
                elif t == "FrobeniusIntegralMoment" and cname in [
                        "triangle", "tetrahedron"
                ]:
                    if dim == 2:
                        s = get_frobenius_side_length(len(type_dofs))
                        for i, b in enumerate(
                                fiat_element.ref_el.connectivity[dim, dim -
                                                                 1][entity_n]):
                            for a in type_dofs[i * s:(i + 1) * s]:
                                reflections[a] = [(dim, entity_n),
                                                  (dim - 1, b)]
    return reflections
Пример #12
0
    def test_values(self, family, cell, degree, reference):
        # Create element
        element = create_element(FiniteElement(family, cell, degree))

        # Get some points and check basis function values at points
        points = [random_point(element_coords(cell)) for i in range(5)]
        for x in points:
            table = element.tabulate(0, (x, ))
            basis = table[list(table.keys())[0]]
            for i in range(len(basis)):
                if not element.value_shape():
                    assert round(float(basis[i]) - reference[i](x), 10) == 0.0
                else:
                    for k in range(element.value_shape()[0]):
                        assert round(basis[i][k][0] - reference[i](x)[k],
                                     10) == 0.0
Пример #13
0
    def facet_edge_vectors(self, e, mt, tabledata, num_points):
        L = self.language

        # Get properties of domain
        domain = mt.terminal.ufl_domain()
        cellname = domain.ufl_cell().cellname()
        gdim = domain.geometric_dimension()
        coordinate_element = domain.ufl_coordinate_element()

        if cellname in ("tetrahedron", "hexahedron"):
            pass
        elif cellname in ("interval", "triangle", "quadrilateral"):
            raise RuntimeError(
                "The physical facet edge vectors doesn't make sense for {0} cell."
                .format(cellname))
        else:
            raise RuntimeError("Unhandled cell types {0}.".format(cellname))

        # Get dimension and dofmap of scalar element
        assert isinstance(coordinate_element, MixedElement)
        assert coordinate_element.value_shape() == (gdim, )
        ufl_scalar_element, = set(coordinate_element.sub_elements())
        assert ufl_scalar_element.family() in ("Lagrange", "Q", "S")
        fiat_scalar_element = create_element(ufl_scalar_element)
        num_scalar_dofs = fiat_scalar_element.space_dimension()

        # Get edge vertices
        facet = self.symbols.entity("facet", mt.restriction)
        facet_edge = mt.component[0]
        facet_edge_vertices = L.Symbol(
            "{0}_facet_edge_vertices".format(cellname))
        vertex0 = facet_edge_vertices[facet][facet_edge][0]
        vertex1 = facet_edge_vertices[facet][facet_edge][1]

        # Get dofs and component
        component = mt.component[1]
        assert coordinate_element.degree() == 1, "Assuming degree 1 element"
        dof0 = vertex0
        dof1 = vertex1
        expr = (self.symbols.domain_dof_access(
            dof0, component, gdim, num_scalar_dofs, mt.restriction) -
                self.symbols.domain_dof_access(
                    dof1, component, gdim, num_scalar_dofs, mt.restriction))

        return expr
Пример #14
0
    def cell_edge_vectors(self, e, mt, tabledata, num_points):
        # Get properties of domain
        domain = mt.terminal.ufl_domain()
        cellname = domain.ufl_cell().cellname()
        gdim = domain.geometric_dimension()
        coordinate_element = domain.ufl_coordinate_element()

        if cellname in ("triangle", "tetrahedron", "quadrilateral",
                        "hexahedron"):
            pass
        elif cellname == "interval":
            raise RuntimeError(
                "The physical cell edge vectors doesn't make sense for interval cell."
            )
        else:
            raise RuntimeError("Unhandled cell types {0}.".format(cellname))

        # Get dimension and dofmap of scalar element
        assert isinstance(coordinate_element, MixedElement)
        assert coordinate_element.value_shape() == (gdim, )
        ufl_scalar_element, = set(coordinate_element.sub_elements())
        assert ufl_scalar_element.family() in ("Lagrange", "Q", "S")
        fiat_scalar_element = create_element(ufl_scalar_element)
        vertex_scalar_dofs = fiat_scalar_element.entity_dofs()[0]
        num_scalar_dofs = fiat_scalar_element.space_dimension()

        # Get edge vertices
        edge = mt.component[0]
        edge_vertices = fiat_scalar_element.get_reference_element(
        ).get_topology()[1][edge]
        vertex0, vertex1 = edge_vertices

        # Get dofs and component
        dof0, = vertex_scalar_dofs[vertex0]
        dof1, = vertex_scalar_dofs[vertex1]
        component = mt.component[1]

        expr = (self.symbols.domain_dof_access(
            dof0, component, gdim, num_scalar_dofs, mt.restriction) -
                self.symbols.domain_dof_access(
                    dof1, component, gdim, num_scalar_dofs, mt.restriction))
        return expr
Пример #15
0
    def cell_vertices(self, e, mt, tabledata, num_points):
        # Get properties of domain
        domain = mt.terminal.ufl_domain()
        gdim = domain.geometric_dimension()
        coordinate_element = domain.ufl_coordinate_element()

        # Get dimension and dofmap of scalar element
        assert isinstance(coordinate_element, MixedElement)
        assert coordinate_element.value_shape() == (gdim, )
        ufl_scalar_element, = set(coordinate_element.sub_elements())
        assert ufl_scalar_element.family() in ("Lagrange", "Q", "S")
        fiat_scalar_element = create_element(ufl_scalar_element)
        vertex_scalar_dofs = fiat_scalar_element.entity_dofs()[0]
        num_scalar_dofs = fiat_scalar_element.space_dimension()

        # Get dof and component
        dof, = vertex_scalar_dofs[mt.component[0]]
        component = mt.component[1]

        expr = self.symbols.domain_dof_access(dof, component, gdim, num_scalar_dofs, mt.restriction)
        return expr
Пример #16
0
def _tabulate_coordinate_mapping_basis(ufl_element):
    # TODO: Move this function to a table generation module?

    # Get scalar element, assuming coordinates are represented
    # with a VectorElement of scalar subelements
    selement = ufl_element.sub_elements()[0]

    fiat_element = create_element(selement)
    cell = selement.cell()
    tdim = cell.topological_dimension()

    tables = {}

    # Get points
    origo = (0.0, ) * tdim
    midpoint = cell_midpoint(cell)

    # Tabulate basis
    t0 = fiat_element.tabulate(1, [origo])
    tm = fiat_element.tabulate(1, [midpoint])

    # Get basis values at cell origo
    tables["x0"] = t0[(0, ) * tdim][:, 0]

    # Get basis values at cell midpoint
    tables["xm"] = tm[(0, ) * tdim][:, 0]

    # Single direction derivatives, e.g. [(1,0), (0,1)] in 2d
    derivatives = [(0, ) * i + (1, ) + (0, ) * (tdim - 1 - i)
                   for i in range(tdim)]

    # Get basis derivative values at cell origo
    tables["J0"] = numpy.asarray([t0[d][:, 0] for d in derivatives])

    # Get basis derivative values at cell midpoint
    tables["Jm"] = numpy.asarray([tm[d][:, 0] for d in derivatives])

    return tables
Пример #17
0
def _compute_expression_ir(expression, index, prefix, analysis, parameters,
                           visualise):

    logger.info("Computing IR for expression {}".format(index))

    # Compute representation
    ir = {}

    original_expression = (expression[2], expression[1])
    sig = naming.compute_signature([original_expression], "", parameters)
    ir["name"] = "expression_{!s}".format(sig)

    original_expression = expression[2]
    points = expression[1]
    expression = expression[0]

    try:
        cell = expression.ufl_domain().ufl_cell()
    except AttributeError:
        # This case corresponds to a spatially constant expression without any dependencies
        cell = None

    # Prepare dimensions of all unique element in expression, including
    # elements for arguments, coefficients and coordinate mappings
    ir["element_dimensions"] = {
        ufl_element: create_element(ufl_element).space_dimension()
        for ufl_element in analysis.unique_elements
    }

    # Extract dimensions for elements of arguments only
    arguments = ufl.algorithms.extract_arguments(expression)
    argument_elements = tuple(f.ufl_element() for f in arguments)
    argument_dimensions = [
        ir["element_dimensions"][ufl_element]
        for ufl_element in argument_elements
    ]

    tensor_shape = argument_dimensions
    ir["tensor_shape"] = tensor_shape

    ir["expression_shape"] = list(expression.ufl_shape)

    coefficients = ufl.algorithms.extract_coefficients(expression)
    coefficient_numbering = {}
    for i, coeff in enumerate(coefficients):
        coefficient_numbering[coeff] = i

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

    original_coefficient_positions = []
    original_coefficients = ufl.algorithms.extract_coefficients(
        original_expression)
    for coeff in coefficients:
        original_coefficient_positions.append(
            original_coefficients.index(coeff))

    ir["original_coefficient_positions"] = original_coefficient_positions

    coefficient_elements = tuple(f.ufl_element() for f in coefficients)

    offsets = {}
    _offset = 0
    for i, el in enumerate(coefficient_elements):
        offsets[coefficients[i]] = _offset
        _offset += ir["element_dimensions"][el]

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

    ir["integral_type"] = "expression"
    ir["entitytype"] = "cell"

    # Build offsets for Constants
    original_constant_offsets = {}
    _offset = 0
    for constant in ufl.algorithms.analysis.extract_constants(expression):
        original_constant_offsets[constant] = _offset
        _offset += numpy.product(constant.ufl_shape, dtype=numpy.int)

    ir["original_constant_offsets"] = original_constant_offsets

    ir["points"] = points

    weights = numpy.array([1.0] * points.shape[0])
    rule = QuadratureRule(points, weights)
    integrands = {rule: expression}

    if cell is None:
        assert len(ir["original_coefficient_positions"]) == 0 and len(
            ir["original_constant_offsets"]) == 0

    expression_ir = compute_integral_ir(cell, ir["integral_type"],
                                        ir["entitytype"], integrands,
                                        tensor_shape, parameters, visualise)

    ir.update(expression_ir)

    return ir_expression(**ir)
Пример #18
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
Пример #19
0
def build_dirichlet_dofs(dofmap, value, dtype=numpy.double):

    # Fetch mesh data
    tdim = dofmap.mesh.reference_cell.get_dimension()
    cell_vertex_conn = dofmap.mesh.get_connectivity(tdim, 0)
    cell_facet_conn = dofmap.mesh.get_connectivity(tdim, tdim-1)
    vertices = dofmap.mesh.vertices
    boundary_facets = dofmap.mesh.boundary_facets
    num_facets_per_cell = cell_facet_conn.shape[1]

    # Fetch dofmap data
    cell_dofs = dofmap.cell_dofs

    # Fetch data from reference element
    fiat_element = fiatinterface.create_element(dofmap.element)
    facet_dofs = fiat_element.entity_closure_dofs()[tdim-1]
    mapping, = set(fiat_element.mapping())

    # Define appropriate pull-back
    if mapping == "affine":
        def f_hat(B, b):
            def _f_hat(xhat):
                return value(B.dot(xhat) + b)
            return _f_hat
    elif mapping == "covariant piola":
        def f_hat(B, b):
            def _f_hat(xhat):
                return value(B.dot(xhat) + b).dot(B)
            return _f_hat
    elif mapping == "contravariant piola":
        def f_hat(B, b):
            Binv = numpy.linalg.inv(B)

            def _f_hat(xhat):
                return Binv.dot(value(B.dot(xhat) + b))
            return _f_hat
    else:
        raise NotImplementedError

    # Turn dual basis nodes into interpolation operator
    dim = fiat_element.space_dimension()

    def interpolation_operator(f):
        return numpy.fromiter(
            (phi(f) for phi in fiat_element.get_dual_set().get_nodes()),
            dtype=dtype, count=dim)

    # Temporary
    bc_map = {}

    # Iterate over cells
    for c in range(cell_facet_conn.shape[0]):

        # Check which facets are on boundary
        is_boundary = [cell_facet_conn[c][f] in boundary_facets
                       for f in range(num_facets_per_cell)]

        if any(is_boundary):

            # NB: This is affine transformation resulting from UFC
            #     simplex definition in FIAT
            b = vertices[cell_vertex_conn[c, 0]]
            B = (vertices[cell_vertex_conn[c, 1:]] - b).T

            # Interpolate Dirichlet datum
            dof_vals = interpolation_operator(f_hat(B, b))
            dof_indices = cell_dofs[c]

            # Figure out which facets and dofs are on boundary
            local_boundary_facets, = numpy.where(is_boundary)
            local_boundary_dofs = numpy.fromiter(
                set(d for f in local_boundary_facets for d in facet_dofs[f]),
                dof_indices.dtype)

            # Store dof-value pair into temporary
            for d in local_boundary_dofs:
                bc_map[dof_indices[d]] = dof_vals[d]

    # Convert to arrays
    # FIXME: Is deterministic?
    dofs = numpy.fromiter(bc_map.keys(), numpy.int32, count=len(bc_map))
    vals = numpy.fromiter(bc_map.values(), dtype, count=len(bc_map))

    return dofs, vals
Пример #20
0
def face_tangents_from_subdofmap(ufl_element):
    """Calculate the effect of permuting a face on the DOFs tangential to that face.

    This function returns a dictionary with the format:
        rotations[(entity_dim, entity_number)][face_permuation][dof] = [(d_i, a_i), ...]
    This entry tells us that on the entity number entity_number of dimension entity_dim,
    if the face permutation is equal to face_permutation, then the following should be
    applied to the values for the DOFs on that face:
        value[dof] = sum(d_i * a_i for i in (...))
    """
    fiat_element = create_element(ufl_element)
    dual = fiat_element.dual_basis()
    cname = ufl_element.cell().cellname()

    dof_types = [e.functional_type for e in dual]
    entity_dofs = fiat_element.entity_dofs()

    face_tangents = {}
    if cname == "tetrahedron" or cname == "hexahedron":
        # Iterate through faces
        for entity_n in range(len(entity_dofs[2])):
            dofs = entity_dofs[2][entity_n]
            types = [dof_types[i] for i in dofs]
            if cname == "tetrahedron":
                tangent_data = {i: {} for i in range(6)}
            elif cname == "hexahedron":
                tangent_data = {i: {} for i in range(8)}

            # PointFaceTangent dofs
            if cname == "tetrahedron" and "PointFaceTangent" in types:
                type_dofs = [i for i, t in zip(dofs, types) if t == "PointFaceTangent"]
                for dof_pair in zip(type_dofs[::2], type_dofs[1::2]):
                    tangent_data[0][dof_pair[0]] = [(dof_pair[0], 1)]
                    tangent_data[1][dof_pair[0]] = [(dof_pair[1], 1)]
                    tangent_data[2][dof_pair[0]] = [(dof_pair[0], -1), (dof_pair[1], -1)]
                    tangent_data[3][dof_pair[0]] = [(dof_pair[0], -1), (dof_pair[1], -1)]
                    tangent_data[4][dof_pair[0]] = [(dof_pair[1], 1)]
                    tangent_data[5][dof_pair[0]] = [(dof_pair[0], 1)]

                    tangent_data[0][dof_pair[1]] = [(dof_pair[1], 1)]
                    tangent_data[1][dof_pair[1]] = [(dof_pair[0], 1)]
                    tangent_data[2][dof_pair[1]] = [(dof_pair[0], 1)]
                    tangent_data[3][dof_pair[1]] = [(dof_pair[1], 1)]
                    tangent_data[4][dof_pair[1]] = [(dof_pair[0], -1), (dof_pair[1], -1)]
                    tangent_data[5][dof_pair[1]] = [(dof_pair[0], -1), (dof_pair[1], -1)]

            # FrobeniusIntegralMoment dofs
            if cname == "tetrahedron" and "FrobeniusIntegralMoment" in types:
                type_dofs = [i for i, t in zip(dofs, types) if t == "FrobeniusIntegralMoment"]
                s = get_frobenius_side_length(len(type_dofs))
                for dof_pair in zip(type_dofs[3 * s::2], type_dofs[3 * s + 1::2]):
                    tangent_data[0][dof_pair[0]] = [(dof_pair[0], 1)]
                    tangent_data[1][dof_pair[0]] = [(dof_pair[1], 1)]
                    tangent_data[2][dof_pair[0]] = [(dof_pair[0], -1), (dof_pair[1], -1)]
                    tangent_data[3][dof_pair[0]] = [(dof_pair[0], -1), (dof_pair[1], -1)]
                    tangent_data[4][dof_pair[0]] = [(dof_pair[1], 1)]
                    tangent_data[5][dof_pair[0]] = [(dof_pair[0], 1)]

                    tangent_data[0][dof_pair[1]] = [(dof_pair[1], 1)]
                    tangent_data[1][dof_pair[1]] = [(dof_pair[0], 1)]
                    tangent_data[2][dof_pair[1]] = [(dof_pair[0], 1)]
                    tangent_data[3][dof_pair[1]] = [(dof_pair[1], 1)]
                    tangent_data[4][dof_pair[1]] = [(dof_pair[0], -1), (dof_pair[1], -1)]
                    tangent_data[5][dof_pair[1]] = [(dof_pair[0], -1), (dof_pair[1], -1)]

            # PointFaceTangent dofs on a hex
            if cname == "hexahedron" and "PointFaceTangent" in types:
                type_dofs = [i for i, t in zip(dofs, types) if t == "PointFaceTangent"]
                for dof in type_dofs[:len(type_dofs) // 2]:
                    tangent_data[0][dof] = [(dof, 1)]
                    tangent_data[1][dof] = [(dof, 1)]
                    tangent_data[2][dof] = [(dof, 1)]
                    tangent_data[3][dof] = [(dof, 1)]
                    tangent_data[4][dof] = [(dof, -1)]
                    tangent_data[5][dof] = [(dof, -1)]
                    tangent_data[6][dof] = [(dof, -1)]
                    tangent_data[7][dof] = [(dof, -1)]
                for dof in type_dofs[len(type_dofs) // 2:]:
                    tangent_data[0][dof] = [(dof, 1)]
                    tangent_data[1][dof] = [(dof, 1)]
                    tangent_data[2][dof] = [(dof, -1)]
                    tangent_data[3][dof] = [(dof, -1)]
                    tangent_data[4][dof] = [(dof, -1)]
                    tangent_data[5][dof] = [(dof, -1)]
                    tangent_data[6][dof] = [(dof, 1)]
                    tangent_data[7][dof] = [(dof, 1)]

            if max(len(a) for a in tangent_data.values()) > 0:
                face_tangents[(2, entity_n)] = tangent_data

    return face_tangents
Пример #21
0
def test_hhj(degree, expected_dim):
    "Test space dimensions of Hellan-Herrmann-Johnson element."
    P = create_element(FiniteElement("HHJ", "triangle", degree))
    assert P.space_dimension() == expected_dim
Пример #22
0
def test_continuous_lagrange_quadrilateral(degree, expected_dim):
    "Test space dimensions of continuous TensorProduct elements (quadrilateral)."
    P = create_element(FiniteElement("Lagrange", "quadrilateral", degree))
    assert P.space_dimension() == expected_dim
Пример #23
0
def test_regge(degree, expected_dim):
    "Test space dimensions of generalized Regge element."
    P = create_element(FiniteElement("Regge", "triangle", degree))
    assert P.space_dimension() == expected_dim
Пример #24
0
def get_ffcx_table_values(points, cell, integral_type, ufl_element, avg,
                          entitytype, derivative_counts, flat_component):
    """Extract values from ffcx element table.

    Returns a 3D numpy array with axes
    (entity number, quadrature point number, dof number)
    """
    deriv_order = sum(derivative_counts)

    if integral_type in ufl.custom_integral_types:
        # Use quadrature points on cell for analysis in custom integral types
        integral_type = "cell"
        assert not avg

    if integral_type == "expression":
        # FFCX tables for expression are generated as interior cell points
        integral_type = "cell"

    if avg in ("cell", "facet"):
        # Redefine points to compute average tables

        # Make sure this is not called with points, that doesn't make sense
        # assert points is None

        # Not expecting derivatives of averages
        assert not any(derivative_counts)
        assert deriv_order == 0

        # Doesn't matter if it's exterior or interior facet integral,
        # just need a valid integral type to create quadrature rule
        if avg == "cell":
            integral_type = "cell"
        elif avg == "facet":
            integral_type = "exterior_facet"

        # Make quadrature rule and get points and weights
        points, weights = create_quadrature_points_and_weights(
            integral_type, cell, ufl_element.degree(), "default")

    # Tabulate table of basis functions and derivatives in points for each entity
    tdim = cell.topological_dimension()
    entity_dim = integral_type_to_entity_dim(integral_type, tdim)
    num_entities = ufl.cell.num_cell_entities[cell.cellname()][entity_dim]

    fiat_element = create_element(ufl_element)

    # Extract arrays for the right scalar component
    component_tables = []
    sh = ufl_element.value_shape()
    if sh == ():
        # Scalar valued element
        for entity in range(num_entities):
            entity_points = map_integral_points(points, integral_type, cell,
                                                entity)
            tbl = fiat_element.tabulate(deriv_order,
                                        entity_points)[derivative_counts]
            component_tables.append(tbl)
    elif len(sh) > 0 and ufl_element.num_sub_elements() == 0:
        # 2-tensor-valued elements, not a tensor product
        # mapping flat_component back to tensor component
        (_, f2t) = ufl.permutation.build_component_numbering(
            sh, ufl_element.symmetry())
        t_comp = f2t[flat_component]

        for entity in range(num_entities):
            entity_points = map_integral_points(points, integral_type, cell,
                                                entity)
            tbl = fiat_element.tabulate(deriv_order,
                                        entity_points)[derivative_counts]
            if len(sh) == 1:
                component_tables.append(tbl[:, t_comp[0], :])
            elif len(sh) == 2:
                component_tables.append(tbl[:, t_comp[0], t_comp[1], :])
            else:
                raise RuntimeError(
                    "Cannot tabulate tensor valued element with rank > 2")
    else:
        # Vector-valued or mixed element
        sub_dims = [0] + list(e.space_dimension()
                              for e in fiat_element.elements())
        sub_cmps = [0] + list(
            numpy.prod(e.value_shape(), dtype=int)
            for e in fiat_element.elements())
        irange = numpy.cumsum(sub_dims)
        crange = numpy.cumsum(sub_cmps)

        # Find index of sub element which corresponds to the current flat component
        component_element_index = numpy.where(
            crange <= flat_component)[0].shape[0] - 1

        ir = irange[component_element_index:component_element_index + 2]
        cr = crange[component_element_index:component_element_index + 2]

        component_element = fiat_element.elements()[component_element_index]

        # Get the block size to switch XXYYZZ ordering to XYZXYZ
        if isinstance(ufl_element, ufl.VectorElement) or isinstance(
                ufl_element, ufl.TensorElement):
            block_size = fiat_element.num_sub_elements()
            ir = [ir[0] * block_size // irange[-1], irange[-1], block_size]

        def slice_size(r):
            if len(r) == 1:
                return r[0]
            if len(r) == 2:
                return r[1] - r[0]
            if len(r) == 3:
                return 1 + (r[1] - r[0] - 1) // r[2]

        # Follows from FIAT's MixedElement tabulation
        # Tabulating MixedElement in FIAT would result in tabulated subelements
        # padded with zeros
        for entity in range(num_entities):
            entity_points = map_integral_points(points, integral_type, cell,
                                                entity)

            # Tabulate subelement, this is dense nonzero table, [a, b, c]
            tbl = component_element.tabulate(deriv_order,
                                             entity_points)[derivative_counts]

            # Prepare a padded table with zeros
            padded_shape = (fiat_element.space_dimension(
            ), ) + fiat_element.value_shape() + (len(entity_points), )
            padded_tbl = numpy.zeros(padded_shape, dtype=tbl.dtype)

            tab = tbl.reshape(slice_size(ir), slice_size(cr), -1)
            padded_tbl[slice(*ir), slice(*cr)] = tab

            component_tables.append(padded_tbl[:, flat_component, :])

    if avg in ("cell", "facet"):
        # Compute numeric integral of the each component table
        wsum = sum(weights)
        for entity, tbl in enumerate(component_tables):
            num_dofs = tbl.shape[0]
            tbl = numpy.dot(tbl, weights) / wsum
            tbl = numpy.reshape(tbl, (num_dofs, 1))
            component_tables[entity] = tbl

    # Loop over entities and fill table blockwise (each block = points x dofs)
    # Reorder axes as (points, dofs) instead of (dofs, points)
    assert len(component_tables) == num_entities
    num_dofs, num_points = component_tables[0].shape
    shape = (num_entities, num_points, num_dofs)
    res = numpy.zeros(shape)
    for entity in range(num_entities):
        res[entity, :, :] = numpy.transpose(component_tables[entity])
    return res
Пример #25
0
def test_discontinuous_lagrange(degree, expected_dim):
    "Test space dimensions of discontinuous Lagrange elements."
    P = create_element(FiniteElement("DG", "triangle", degree))
    assert P.space_dimension() == expected_dim