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")
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)
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")
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
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
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)
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)
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
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
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()
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
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
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
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
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
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
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)
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
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
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
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
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
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
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
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