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 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 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] numpy.set_printoptions(suppress=True, precision=2) basix_element = create_element(ufl_element) # Extract arrays for the right scalar component component_tables = [] sh = tuple(basix_element.value_shape) assert len(sh) > 0 component_element, offset, stride = basix_element.get_component_element(flat_component) for entity in range(num_entities): entity_points = map_integral_points(points, integral_type, cell, entity) tbl = component_element.tabulate(deriv_order, entity_points) tbl = tbl[basix_index(*derivative_counts)] component_tables.append(tbl) 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[1] tbl = numpy.dot(tbl, weights) / wsum tbl = numpy.reshape(tbl, (1, num_dofs)) 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_points, num_dofs = component_tables[0].shape shape = (1, num_entities, num_points, num_dofs) res = numpy.zeros(shape) for entity in range(num_entities): res[:, entity, :, :] = component_tables[entity] return {'array': res, 'offset': offset, 'stride': stride}