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(FlattenedDimensions, self).__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())
def compute_pointwise_dual(el, pts): """Constructs a dual basis to the basis for el as a linear combination of a set of pointwise evaluations. This is useful when the prescribed finite element isn't Ciarlet (e.g. the basis functions are provided explicitly as formulae). Alternately, the element's given dual basis may involve differentiation, making run-time interpolation difficult in FIAT clients. The pointwise dual, consisting only of pointwise evaluations, will effectively replace these derivatives with (automatically determined) finite differences. This is exact on the polynomial space, but is an approximation if applied to functions outside the space. :param el: a :class:`FiniteElement`. :param pts: an iterable of points with the same length as el's dimension. These points must be unisolvent for the polynomial space :returns: a :class `DualSet` """ nbf = el.space_dimension() T = el.ref_el sd = T.get_spatial_dimension() assert np.asarray(pts).shape == (int(nbf / np.prod(el.value_shape())), sd) z = tuple([0] * sd) nds = [] V = el.tabulate(0, pts)[z] # Make a square system, invert, and then put it back in the right # shape so we have (nbf, ..., npts) with more dimensions # for vector or tensor-valued elements. alphas = np.linalg.inv(V.reshape((nbf, -1)).T).reshape(V.shape) # Each row of alphas gives the coefficients of a functional, # represented, as elsewhere in FIAT, as a summation of # components of the input at particular points. # This logic picks out the points and components for which the # weights are actually nonzero to construct the functional. pts = np.asarray(pts) for coeffs in alphas: pt_dict = defaultdict(list) nonzero = np.where(np.abs(coeffs) > 1.e-12) *comp, pt_index = nonzero for pt, coeff_comp in zip(pts[pt_index], zip_longest(coeffs[nonzero], zip(*comp), fillvalue=())): pt_dict[tuple(pt)].append(coeff_comp) nds.append(Functional(T, el.value_shape(), dict(pt_dict), {}, "node")) return DualSet(nds, T, el.entity_dofs())
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(RestrictedElement, self).__init__(poly_set, dual, 0, element.get_formdegree(), mapping_new[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(NodalEnrichedElement, self).__init__(poly_set, dual_set, order, formdegree=formdegree, mapping=mapping)
def __init__(self, ref_el, points): # 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(QuadratureElement, self).__init__(ref_el, dual, order=None) self._points = points # save the quadrature points
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()
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(EnrichedElement, self).__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
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(MixedElement, self).__init__(ref_el, dual, None, mapping=None) self._elements = elements
def __init__(self, element): """ Constructs a FlattenToQuad element. :arg element: a fiat element """ nodes = element.dual.nodes ref_el = FiredrakeQuadrilateral() entity_ids = element.dual.entity_ids flat_entity_ids = {} flat_entity_ids[0] = entity_ids[(0, 0)] flat_entity_ids[1] = dict( enumerate([v for k, v in sorted(entity_ids[(0, 1)].items())] + [v for k, v in sorted(entity_ids[(1, 0)].items())])) flat_entity_ids[2] = entity_ids[(1, 1)] dual = DualSet(nodes, ref_el, flat_entity_ids) super(FlattenToQuad, self).__init__(ref_el, dual, element.get_order(), element.get_formdegree(), element._mapping) self.element = element
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(HDivTrace, self).__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
def create_actual_fiat_element(ufl_element): fiat_element = None # Check if finite element family is supported by FIAT family = ufl_element.family() if not family in FIAT.supported_elements: # We support RTCE and RTCF elements on quadrilaterals, # even though they are not supported by FIAT. if ufl_element.cell().cellname() == "quadrilateral": fiat_element = create_actual_fiat_element( reconstruct_element(ufl_element, ufl_element.family(), _quad_opc, ufl_element.degree())) else: if family in ("FacetElement", "InteriorElement"): # rescue these pass else: error( "Sorry, finite element of type \"%s\" are not supported by FIAT.", family) # Skip all cases if FIAT element is ready already if fiat_element is not None: pass # HDiv and HCurl elements have family "OuterProductElement", # so get matching FIAT element directly rather than via lookup elif isinstance(ufl_element, ufl.HDivElement): fiat_element = FIAT.Hdiv(create_element(ufl_element._element)) elif isinstance(ufl_element, ufl.HCurlElement): fiat_element = FIAT.Hcurl(create_element(ufl_element._element)) elif isinstance(ufl_element, ufl.FacetElement): fiat_element = FIAT.RestrictedElement(create_element( ufl_element._element), restriction_domain="facet") elif isinstance(ufl_element, ufl.InteriorElement): fiat_element = FIAT.RestrictedElement(create_element( ufl_element._element), restriction_domain="interior") else: # Look up FIAT element ElementClass = FIAT.supported_elements[family] if isinstance(ufl_element, ufl.EnrichedElement): A = create_element(ufl_element._elements[0]) B = create_element(ufl_element._elements[1]) fiat_element = ElementClass(A, B) # OPVE is only here to satisfy calls from Firedrake elif isinstance( ufl_element, (ufl.OuterProductElement, ufl.OuterProductVectorElement, ufl.OuterProductTensorElement)): cell = ufl_element.cell() if not isinstance(cell, ufl.OuterProductCell): error( "An OuterProductElement must have an OuterProductCell as domain, sorry." ) A = create_element(ufl_element._A) B = create_element(ufl_element._B) fiat_element = ElementClass(A, B) elif isinstance(ufl_element, (ufl.BrokenElement, ufl.TraceElement)): fiat_element = ElementClass(create_element(ufl_element._element)) elif ufl_element.cell().cellname() == "quadrilateral": fiat_element = create_actual_fiat_element( ufl_element.reconstruct(domain=_quad_opc)) else: # "Normal element" case cell = ufl_element.cell() degree = ufl_element.degree() fiat_cell = reference_cell(cell) fiat_element = ElementClass(fiat_cell, degree) if fiat_element is None: raise Exception( "Something strange happened: reached end of function without returning an element" ) if ufl_element.cell().cellname() == "quadrilateral" and \ isinstance(fiat_element.get_reference_element(), FIAT.reference_element.two_product_cell): # Flatten tensor product element from FIAT.reference_element import FiredrakeQuadrilateral from FIAT.dual_set import DualSet nodes = fiat_element.dual.nodes ref_el = FiredrakeQuadrilateral() entity_ids = fiat_element.dual.entity_ids flat_entity_ids = {} flat_entity_ids[0] = entity_ids[(0, 0)] flat_entity_ids[1] = dict( enumerate(entity_ids[(0, 1)].values() + entity_ids[(1, 0)].values())) flat_entity_ids[2] = entity_ids[(1, 1)] fiat_element.dual = DualSet(nodes, ref_el, flat_entity_ids) fiat_element.ref_el = ref_el return fiat_element
def __init__(self, *elements): assert len( elements ) == 2, "EnrichedElement only implemented for two subelements" A, B = elements # Firstly, check it makes sense to enrich. Elements must have: # - same reference element # - same mapping # - same value shape if not A.get_reference_element() == B.get_reference_element(): raise ValueError( "Elements must be defined on the same reference element") if not A.mapping()[0] == B.mapping()[0]: raise ValueError("Elements must have same mapping") if not A.value_shape() == B.value_shape(): 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(A.get_order(), B.get_order()) # 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 A.get_formdegree() is None or B.get_formdegree() is None: formdegree = None else: formdegree = max(A.get_formdegree(), B.get_formdegree()) # set up reference element and mapping, following checks above ref_el = A.get_reference_element() mapping = A.mapping()[0] # set up entity_ids - for each geometric entity, just concatenate # the entities of the constituent elements Adofs = A.entity_dofs() Bdofs = B.entity_dofs() offset = A.space_dimension() # number of entities belonging to A entity_ids = {} for ent_dim in Adofs: entity_ids[ent_dim] = {} for ent_dim_index in Adofs[ent_dim]: entlist = copy(Adofs[ent_dim][ent_dim_index]) entlist += [c + offset for c in Bdofs[ent_dim][ent_dim_index]] entity_ids[ent_dim][ent_dim_index] = entlist # set up dual basis - just concatenation nodes = A.dual_basis() + B.dual_basis() dual = DualSet(nodes, ref_el, entity_ids) super(EnrichedElement, self).__init__(ref_el, dual, order, formdegree, mapping) # Set up constituent elements self.A = A self.B = B # required degree (for quadrature) is definitely max self.polydegree = max(A.degree(), B.degree()) # Store subelements self._elements = elements