Exemplo n.º 1
0
    def __init__(self, *elements):

        # Test elements are nodal
        if not all(e.is_nodal() for e in elements):
            raise ValueError("Not all elements given for construction "
                             "of NodalEnrichedElement are nodal")

        # Extract common data
        ref_el = elements[0].get_reference_element()
        expansion_set = elements[0].get_nodal_basis().get_expansion_set()
        degree = min(e.get_nodal_basis().get_degree() for e in elements)
        embedded_degree = max(e.get_nodal_basis().get_embedded_degree()
                              for e in elements)
        order = max(e.get_order() for e in elements)
        mapping = elements[0].mapping()[0]
        formdegree = None if any(e.get_formdegree() is None for e in elements) \
            else max(e.get_formdegree() for e in elements)
        value_shape = elements[0].value_shape()

        # Sanity check
        assert all(e.get_nodal_basis().get_reference_element() ==
                   ref_el for e in elements)
        assert all(type(e.get_nodal_basis().get_expansion_set()) ==
                   type(expansion_set) for e in elements)
        assert all(e_mapping == mapping for e in elements
                   for e_mapping in e.mapping())
        assert all(e.value_shape() == value_shape for e in elements)

        # Merge polynomial sets
        coeffs = _merge_coeffs([e.get_coeffs() for e in elements])
        dmats = _merge_dmats([e.dmats() for e in elements])
        poly_set = PolynomialSet(ref_el,
                                 degree,
                                 embedded_degree,
                                 expansion_set,
                                 coeffs,
                                 dmats)

        # Renumber dof numbers
        offsets = np.cumsum([0] + [e.space_dimension() for e in elements[:-1]])
        entity_ids = _merge_entity_ids((e.entity_dofs() for e in elements),
                                       offsets)

        # Merge dual bases
        nodes = [node for e in elements for node in e.dual_basis()]
        dual_set = DualSet(nodes, ref_el, entity_ids)

        # CiarletElement constructor adjusts poly_set coefficients s.t.
        # dual_set is really dual to poly_set
        super().__init__(poly_set, dual_set, order,
                         formdegree=formdegree, mapping=mapping)
Exemplo n.º 2
0
    def __init__(self, element, indices=None, restriction_domain=None):
        '''For sake of argument, indices overrides restriction_domain'''

        if not (indices or restriction_domain):
            raise RuntimeError("Either indices or restriction_domain must be passed in")

        if not indices:
            indices = _get_indices(element, restriction_domain)

        if isinstance(indices, str):
            raise RuntimeError("variable 'indices' was a string; did you forget to use a keyword?")

        if len(indices) == 0:
            raise ValueError("No point in creating empty RestrictedElement.")

        self._element = element
        self._indices = indices

        # Fetch reference element
        ref_el = element.get_reference_element()

        # Restrict primal set
        poly_set = element.get_nodal_basis().take(indices)

        # Restrict dual set
        dof_counter = 0
        entity_ids = {}
        nodes = []
        nodes_old = element.dual_basis()
        for d, entities in element.entity_dofs().items():
            entity_ids[d] = {}
            for entity, dofs in entities.items():
                entity_ids[d][entity] = []
                for dof in dofs:
                    if dof not in indices:
                        continue
                    entity_ids[d][entity].append(dof_counter)
                    dof_counter += 1
                    nodes.append(nodes_old[dof])
        assert dof_counter == len(indices)
        dual = DualSet(nodes, ref_el, entity_ids)

        # Restrict mapping
        mapping_old = element.mapping()
        mapping_new = [mapping_old[dof] for dof in indices]
        assert all(e_mapping == mapping_new[0] for e_mapping in mapping_new)

        # Call constructor of CiarletElement
        super().__init__(poly_set, dual, 0, element.get_formdegree(), mapping_new[0])
Exemplo n.º 3
0
    def __init__(self, ref_el, points, weights=None):
        # Create entity dofs.
        entity_dofs = {dim: {entity: [] for entity in entities}
                       for dim, entities in ref_el.get_topology().items()}
        entity_dofs[ref_el.get_dimension()] = {0: list(range(len(points)))}

        # The dual nodes are PointEvaluations at the quadrature points.
        # FIXME: KBO: Check if this gives expected results for code like evaluate_dof.
        nodes = [PointEvaluation(ref_el, tuple(point)) for point in points]

        # Construct the dual set
        dual = DualSet(nodes, ref_el, entity_dofs)

        super().__init__(ref_el, dual, order=None)
        self._points = points  # save the quadrature points & weights
        self._weights = weights
Exemplo n.º 4
0
    def __init__(self, element):
        self._element = element
        new_entity_ids = {}
        topology = element.get_reference_element().get_topology()
        for dim in sorted(topology):
            new_entity_ids[dim] = {}
            for ent in sorted(topology[dim]):
                new_entity_ids[dim][ent] = []

        new_entity_ids[dim][0] = list(range(element.space_dimension()))
        # re-initialise the dual, so entity_closure_dofs is recalculated
        self.dual = DualSet(element.dual_basis(),
                            element.get_reference_element(), new_entity_ids)

        # fully discontinuous
        self.formdegree = element.get_reference_element(
        ).get_spatial_dimension()
Exemplo n.º 5
0
    def __init__(self, elements, ref_el=None):
        elements = tuple(elements)

        cells = set(e.get_reference_element() for e in elements)
        if ref_el is not None:
            cells.add(ref_el)
        ref_el, = cells

        # These functionals are absolutely wrong, they all map from
        # functions of the wrong shape, and potentially of different
        # shapes.  However, they are wrong precisely as FFC hacks
        # expect them to be. :(
        nodes = [L for e in elements for L in e.dual_basis()]

        entity_dofs = concatenate_entity_dofs(ref_el, elements)

        dual = DualSet(nodes, ref_el, entity_dofs)
        super().__init__(ref_el, dual, None, mapping=None)
        self._elements = elements
Exemplo n.º 6
0
    def __init__(self, *elements):
        # Firstly, check it makes sense to enrich.  Elements must have:
        # - same reference element
        # - same mapping
        # - same value shape
        if len(set(e.get_reference_element() for e in elements)) > 1:
            raise ValueError(
                "Elements must be defined on the same reference element")
        if len(set(m for e in elements for m in e.mapping())) > 1:
            raise ValueError("Elements must have same mapping")
        if len(set(e.value_shape() for e in elements)) > 1:
            raise ValueError("Elements must have the same value shape")

        # order is at least max, possibly more, though getting this
        # right isn't important AFAIK
        order = max(e.get_order() for e in elements)
        # form degree is essentially max (not true for Hdiv/Hcurl,
        # but this will raise an error above anyway).
        # E.g. an H^1 function enriched with an L^2 is now just L^2.
        if any(e.get_formdegree() is None for e in elements):
            formdegree = None
        else:
            formdegree = max(e.get_formdegree() for e in elements)

        # set up reference element and mapping, following checks above
        ref_el, = set(e.get_reference_element() for e in elements)
        mapping, = set(m for e in elements for m in e.mapping())

        # set up entity_ids - for each geometric entity, just concatenate
        # the entities of the constituent elements
        entity_ids = concatenate_entity_dofs(ref_el, elements)

        # set up dual basis - just concatenation
        nodes = list(chain.from_iterable(e.dual_basis() for e in elements))
        dual = DualSet(nodes, ref_el, entity_ids)

        super().__init__(ref_el, dual, order, formdegree, mapping)

        # required degree (for quadrature) is definitely max
        self.polydegree = max(e.degree() for e in elements)

        # Store subelements
        self._elements = elements
Exemplo n.º 7
0
    def __init__(self, element):

        nodes = element.dual.nodes
        dim = element.ref_el.get_spatial_dimension()

        if dim == 2:
            ref_el = UFCQuadrilateral()
        elif dim == 3:
            ref_el = UFCHexahedron()
        else:
            raise ValueError("Illegal element dimension %s" % dim)

        entity_ids = element.dual.entity_ids

        flat_entity_ids = flatten_entities(entity_ids)
        dual = DualSet(nodes, ref_el, flat_entity_ids)
        super().__init__(ref_el, dual, element.get_order(),
                         element.get_formdegree(), element._mapping)
        self.element = element

        # Construct unflattening map for passing correct values to tabulate()
        self.unflattening_map = compute_unflattening_map(
            self.element.ref_el.get_topology())
Exemplo n.º 8
0
    def __init__(self, A, B):
        # set up simple things
        order = min(A.get_order(), B.get_order())
        if A.get_formdegree() is None or B.get_formdegree() is None:
            formdegree = None
        else:
            formdegree = A.get_formdegree() + B.get_formdegree()

        # set up reference element
        ref_el = TensorProductCell(A.get_reference_element(),
                                   B.get_reference_element())

        if A.mapping()[0] != "affine" and B.mapping()[0] == "affine":
            mapping = A.mapping()[0]
        elif B.mapping()[0] != "affine" and A.mapping()[0] == "affine":
            mapping = B.mapping()[0]
        elif A.mapping()[0] == "affine" and B.mapping()[0] == "affine":
            mapping = "affine"
        else:
            raise ValueError(
                "check tensor product mappings - at least one must be affine")

        # set up entity_ids
        Adofs = A.entity_dofs()
        Bdofs = B.entity_dofs()
        Bsdim = B.space_dimension()
        entity_ids = {}

        for curAdim in Adofs:
            for curBdim in Bdofs:
                entity_ids[(curAdim, curBdim)] = {}
                dim_cur = 0
                for entityA in Adofs[curAdim]:
                    for entityB in Bdofs[curBdim]:
                        entity_ids[(curAdim, curBdim)][dim_cur] = \
                            [x*Bsdim + y for x in Adofs[curAdim][entityA]
                                for y in Bdofs[curBdim][entityB]]
                        dim_cur += 1

        # set up dual basis
        Anodes = A.dual_basis()
        Bnodes = B.dual_basis()

        # build the dual set by inspecting the current dual
        # sets item by item.
        # Currently supported cases:
        # PointEval x PointEval = PointEval [scalar x scalar = scalar]
        # PointScaledNormalEval x PointEval = PointScaledNormalEval [vector x scalar = vector]
        # ComponentPointEvaluation x PointEval [vector x scalar = vector]
        nodes = []
        for Anode in Anodes:
            if isinstance(Anode, functional.PointEvaluation):
                for Bnode in Bnodes:
                    if isinstance(Bnode, functional.PointEvaluation):
                        # case: PointEval x PointEval
                        # the PointEval functional just requires the
                        # coordinates. these are currently stored as
                        # the key of a one-item dictionary. we retrieve
                        # these by calling get_point_dict(), and
                        # use the concatenation to make a new PointEval
                        nodes.append(
                            functional.PointEvaluation(
                                ref_el,
                                _first_point(Anode) + _first_point(Bnode)))
                    elif isinstance(Bnode, functional.IntegralMoment):
                        # dummy functional for product with integral moments
                        nodes.append(
                            functional.Functional(None, None, None, {},
                                                  "Undefined"))
                    elif isinstance(Bnode, functional.PointDerivative):
                        # dummy functional for product with point derivative
                        nodes.append(
                            functional.Functional(None, None, None, {},
                                                  "Undefined"))
                    else:
                        raise NotImplementedError(
                            "unsupported functional type")

            elif isinstance(Anode, functional.PointScaledNormalEvaluation):
                for Bnode in Bnodes:
                    if isinstance(Bnode, functional.PointEvaluation):
                        # case: PointScaledNormalEval x PointEval
                        # this could be wrong if the second shape
                        # has spatial dimension >1, since we are not
                        # explicitly scaling by facet size
                        if len(_first_point(Bnode)) > 1:
                            # TODO: support this case one day
                            raise NotImplementedError(
                                "PointScaledNormalEval x PointEval is not yet supported if the second shape has dimension > 1"
                            )
                        # We cannot make a new functional.PSNEval in
                        # the natural way, since it tries to compute
                        # the normal vector by itself.
                        # Instead, we create things manually, and
                        # call Functional() with these arguments
                        sd = ref_el.get_spatial_dimension()
                        # The pt_dict is a one-item dictionary containing
                        # the details of the functional.
                        # The key is the spatial coordinate, which
                        # is just a concatenation of the two parts.
                        # The value is a list of tuples, representing
                        # the normal vector (scaled by the volume of
                        # the facet) at that point.
                        # Each tuple looks like (foo, (i,)); the i'th
                        # component of the scaled normal is foo.

                        # The following line is only valid when the second
                        # shape has spatial dimension 1 (enforced above)
                        Apoint, Avalue = _first_point_pair(Anode)
                        pt_dict = {
                            Apoint + _first_point(Bnode):
                            Avalue + [(0.0, (len(Apoint), ))]
                        }

                        # The following line should be used in the
                        # general case
                        # pt_dict = {Anode.get_point_dict().keys()[0] + Bnode.get_point_dict().keys()[0]: Anode.get_point_dict().values()[0] + [(0.0, (ii,)) for ii in range(len(Anode.get_point_dict().keys()[0]), len(Anode.get_point_dict().keys()[0]) + len(Bnode.get_point_dict().keys()[0]))]}

                        # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED
                        shp = (sd, )
                        nodes.append(
                            functional.Functional(ref_el, shp, pt_dict, {},
                                                  "PointScaledNormalEval"))
                    else:
                        raise NotImplementedError(
                            "unsupported functional type")

            elif isinstance(Anode, functional.PointEdgeTangentEvaluation):
                for Bnode in Bnodes:
                    if isinstance(Bnode, functional.PointEvaluation):
                        # case: PointEdgeTangentEval x PointEval
                        # this is very similar to the case above, so comments omitted
                        if len(_first_point(Bnode)) > 1:
                            raise NotImplementedError(
                                "PointEdgeTangentEval x PointEval is not yet supported if the second shape has dimension > 1"
                            )
                        sd = ref_el.get_spatial_dimension()
                        Apoint, Avalue = _first_point_pair(Anode)
                        pt_dict = {
                            Apoint + _first_point(Bnode):
                            Avalue + [(0.0, (len(Apoint), ))]
                        }

                        # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED
                        shp = (sd, )
                        nodes.append(
                            functional.Functional(ref_el, shp, pt_dict, {},
                                                  "PointEdgeTangent"))
                    else:
                        raise NotImplementedError(
                            "unsupported functional type")

            elif isinstance(Anode, functional.ComponentPointEvaluation):
                for Bnode in Bnodes:
                    if isinstance(Bnode, functional.PointEvaluation):
                        # case: ComponentPointEval x PointEval
                        # the CptPointEval functional requires the component
                        # and the coordinates. very similar to PE x PE case.
                        sd = ref_el.get_spatial_dimension()
                        nodes.append(
                            functional.ComponentPointEvaluation(
                                ref_el, Anode.comp, (sd, ),
                                _first_point(Anode) + _first_point(Bnode)))
                    else:
                        raise NotImplementedError(
                            "unsupported functional type")

            elif isinstance(Anode, functional.FrobeniusIntegralMoment):
                for Bnode in Bnodes:
                    if isinstance(Bnode, functional.PointEvaluation):
                        # case: FroIntMom x PointEval
                        sd = ref_el.get_spatial_dimension()
                        pt_dict = {}
                        pt_old = Anode.get_point_dict()
                        for pt in pt_old:
                            pt_dict[pt + _first_point(Bnode)] = pt_old[pt] + [
                                (0.0, sd - 1)
                            ]
                        # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED
                        shp = (sd, )
                        nodes.append(
                            functional.Functional(ref_el, shp, pt_dict, {},
                                                  "FrobeniusIntegralMoment"))
                    else:
                        raise NotImplementedError(
                            "unsupported functional type")

            elif isinstance(Anode, functional.IntegralMoment):
                for Bnode in Bnodes:
                    if isinstance(Bnode, functional.PointEvaluation):
                        # case: IntMom x PointEval
                        sd = ref_el.get_spatial_dimension()
                        pt_dict = {}
                        pt_old = Anode.get_point_dict()
                        for pt in pt_old:
                            pt_dict[pt + _first_point(Bnode)] = pt_old[pt]
                        # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED
                        shp = (sd, )
                        nodes.append(
                            functional.Functional(ref_el, shp, pt_dict, {},
                                                  "IntegralMoment"))
                    else:
                        raise NotImplementedError(
                            "unsupported functional type")

            elif isinstance(Anode, functional.Functional):
                # this should catch everything else
                for Bnode in Bnodes:
                    nodes.append(
                        functional.Functional(None, None, None, {},
                                              "Undefined"))
            else:
                raise NotImplementedError("unsupported functional type")

        dual = DualSet(nodes, ref_el, entity_ids)

        super().__init__(ref_el, dual, order, formdegree, mapping)
        # Set up constituent elements
        self.A = A
        self.B = B

        # degree for quadrature rule
        self.polydegree = max(A.degree(), B.degree())
Exemplo n.º 9
0
    def __init__(self, ref_el, degree):
        """Constructor for the HDivTrace element.

        :arg ref_el: A reference element, which may be a tensor product
                     cell.
        :arg degree: The degree of approximation. If on a tensor product
                     cell, then provide a tuple of degrees if you want
                     varying degrees.
        """
        sd = ref_el.get_spatial_dimension()
        if sd in (0, 1):
            raise ValueError("Cannot take the trace of a %d-dim cell." % sd)

        # Store the degrees if on a tensor product cell
        if ref_el.get_shape() == "TENSORPRODUCT":
            try:
                degree = tuple(degree)
            except TypeError:
                degree = (degree,) * len(ref_el.cells)

            assert len(ref_el.cells) == len(degree), (
                "Number of specified degrees must be equal to the number of cells."
            )
        else:
            if ref_el.get_shape() not in ["TRIANGLE", "TETRAHEDRON", "QUADRILATERAL"]:
                raise NotImplementedError(
                    "Trace element on a %s not implemented" % type(ref_el)
                )
            # Cannot have varying degrees for these reference cells
            if isinstance(degree, tuple):
                raise ValueError("Must have a tensor product cell if providing multiple degrees")

        # Initialize entity dofs and construct the DG elements
        # for the facets
        facet_sd = sd - 1
        dg_elements = {}
        entity_dofs = {}
        topology = ref_el.get_topology()
        for top_dim, entities in topology.items():
            cell = ref_el.construct_subelement(top_dim)
            entity_dofs[top_dim] = {}

            # We have a facet entity!
            if cell.get_spatial_dimension() == facet_sd:
                dg_elements[top_dim] = construct_dg_element(cell, degree)
            # Initialize
            for entity in entities:
                entity_dofs[top_dim][entity] = []

        # Compute the dof numbering for all facet entities
        # and extract nodes
        offset = 0
        pts = []
        for facet_dim in sorted(dg_elements):
            element = dg_elements[facet_dim]
            nf = element.space_dimension()
            num_facets = len(topology[facet_dim])

            for i in range(num_facets):
                entity_dofs[facet_dim][i] = list(range(offset, offset + nf))
                offset += nf

                # Run over nodes and collect the points for point evaluations
                for dof in element.dual_basis():
                    facet_pt, = dof.get_point_dict()
                    transform = ref_el.get_entity_transform(facet_dim, i)
                    pts.append(tuple(transform(facet_pt)))

        # Setting up dual basis - only point evaluations
        nodes = [PointEvaluation(ref_el, pt) for pt in pts]
        dual = DualSet(nodes, ref_el, entity_dofs)

        # Degree of the element
        deg = max([e.degree() for e in dg_elements.values()])

        super().__init__(ref_el, dual, order=deg,
                         formdegree=facet_sd, mapping="affine")

        # Set up facet elements
        self.dg_elements = dg_elements

        # Degree for quadrature rule
        self.polydegree = deg