def accumulate_integrals(itg_data, quadrature_rule_sizes): """Group and accumulate integrals according to the number of quadrature points in their rules.""" if not itg_data.integrals: return {} # Group integrands by quadrature rule sorted_integrands = collections.defaultdict(list) if itg_data.integral_type in custom_integral_types: # Should only be one size here, ignoring irrelevant metadata and parameters num_points, = quadrature_rule_sizes.values() for integral in itg_data.integrals: sorted_integrands[num_points].append(integral.integrand()) else: default_scheme = itg_data.metadata["quadrature_degree"] default_degree = itg_data.metadata["quadrature_rule"] for integral in itg_data.integrals: md = integral.metadata() or {} scheme = md.get("quadrature_rule", default_scheme) degree = md.get("quadrature_degree", default_degree) rule = (scheme, degree) num_points = quadrature_rule_sizes[rule] sorted_integrands[num_points].append(integral.integrand()) # Accumulate integrands in a canonical ordering defined by UFL sorted_integrals = { num_points: Integral(sorted_expr_sum(integrands), itg_data.integral_type, itg_data.domain, itg_data.subdomain_id, {}, None) for num_points, integrands in list(sorted_integrands.items()) } return sorted_integrals
def _sort_integrals(integrals, metadata, form_data): """Sort integrals according to the number of quadrature points needed per axis. Only consider those integrals defined on the given domain.""" # Get domain type and id if integrals: domain_type = integrals[0].domain_type() domain_id = integrals[0].domain_id() ffc_assert(all(domain_type == itg.domain_type() for itg in integrals), "Expecting only integrals on the same subdomain.") ffc_assert(all(domain_id == itg.domain_id() for itg in integrals), "Expecting only integrals on the same subdomain.") sorted_integrands = {} # TODO: We might want to take into account that a form like # a = f*g*h*v*u*dx(0, quadrature_order=4) + f*v*u*dx(0, quadrature_order=2), # although it involves two integrals of different order, will most # likely be integrated faster if one does # a = (f*g*h + f)*v*u*dx(0, quadrature_order=4) # It will of course only work for integrals defined on the same # subdomain and representation. for integral in integrals: # Get default degree and rule. degree = metadata["quadrature_degree"] rule = metadata["quadrature_rule"] # Override if specified in integral metadata integral_compiler_data = integral.compiler_data() if not integral_compiler_data is None: if "quadrature_degree" in integral_compiler_data: degree = integral_compiler_data["quadrature_degree"] if "quadrature_rule" in integral_compiler_data: rule = integral_compiler_data["quadrature_rule"] # Add integrand to dictionary according to degree and rule. if not (degree, rule) in sorted_integrands: sorted_integrands[(degree, rule)] = [integral.integrand()] else: sorted_integrands[(degree, rule)] += [integral.integrand()] # Create integrals from accumulated integrands. sorted_integrals = {} for key, integrands in sorted_integrands.items(): # Summing integrands in a canonical ordering defined by UFL integrand = sorted_expr_sum(integrands) sorted_integrals[key] = Integral(integrand, domain_type, domain_id, {}, None) return sorted_integrals
def sort_integrals(integrals, default_quadrature_degree, default_quadrature_rule): """Sort and accumulate integrals according to the number of quadrature points needed per axis. All integrals should be over the same (sub)domain. """ if not integrals: return {} # Get domain properties from first integral, assuming all are the same integral_type = integrals[0].integral_type() subdomain_id = integrals[0].subdomain_id() domain_label = integrals[0].domain().label() domain = integrals[0].domain() # FIXME: Is this safe? Get as input? ffc_assert(all(integral_type == itg.integral_type() for itg in integrals), "Expecting only integrals of the same type.") ffc_assert(all(domain_label == itg.domain().label() for itg in integrals), "Expecting only integrals on the same domain.") ffc_assert(all(subdomain_id == itg.subdomain_id() for itg in integrals), "Expecting only integrals on the same subdomain.") sorted_integrands = collections.defaultdict(list) for integral in integrals: # Override default degree and rule if specified in integral metadata integral_metadata = integral.metadata() or {} degree = integral_metadata.get("quadrature_degree", default_quadrature_degree) rule = integral_metadata.get("quadrature_rule", default_quadrature_rule) assert isinstance(degree, int) # Add integrand to dictionary according to degree and rule. key = (degree, rule) sorted_integrands[key].append(integral.integrand()) # Create integrals from accumulated integrands. sorted_integrals = {} for key, integrands in list(sorted_integrands.items()): # Summing integrands in a canonical ordering defined by UFL integrand = sorted_expr_sum(integrands) sorted_integrals[key] = Integral(integrand, integral_type, domain, subdomain_id, {}, None) return sorted_integrals
def sort_integrals(integrals, default_scheme, default_degree): """Sort and accumulate integrals according to the number of quadrature points needed per axis. All integrals should be over the same (sub)domain. """ if not integrals: return {} # Get domain properties from first integral, assuming all are the # same integral_type = integrals[0].integral_type() subdomain_id = integrals[0].subdomain_id() domain = integrals[0].ufl_domain() ffc_assert(all(integral_type == itg.integral_type() for itg in integrals), "Expecting only integrals of the same type.") ffc_assert(all(domain == itg.ufl_domain() for itg in integrals), "Expecting only integrals on the same domain.") ffc_assert(all(subdomain_id == itg.subdomain_id() for itg in integrals), "Expecting only integrals on the same subdomain.") sorted_integrands = collections.defaultdict(list) for integral in integrals: # Override default degree and rule if specified in integral # metadata integral_metadata = integral.metadata() or {} degree = integral_metadata.get("quadrature_degree", default_degree) scheme = integral_metadata.get("quadrature_rule", default_scheme) assert isinstance(degree, int) # Add integrand to dictionary according to degree and scheme. rule = (scheme, degree) sorted_integrands[rule].append(integral.integrand()) # Create integrals from accumulated integrands. sorted_integrals = {} for rule, integrands in list(sorted_integrands.items()): # Summing integrands in a canonical ordering defined by UFL integrand = sorted_expr_sum(integrands) sorted_integrals[rule] = Integral(integrand, integral_type, domain, subdomain_id, {}, None) return sorted_integrals
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