def __init__(self, integral_type, subdomain_id, domain_number): """Initialise a kernel builder.""" super(KernelBuilder, self).__init__(integral_type.startswith("interior_facet")) self.kernel = Kernel(integral_type=integral_type, subdomain_id=subdomain_id, domain_number=domain_number) self.local_tensor = None self.coordinates_arg = None self.coefficient_args = [] self.coefficient_split = {} # Facet number if integral_type in ['exterior_facet', 'exterior_facet_vert']: facet = gem.Variable('facet', (1, )) self._facet_number = { None: gem.VariableIndex(gem.Indexed(facet, (0, ))) } elif integral_type in ['interior_facet', 'interior_facet_vert']: facet = gem.Variable('facet', (2, )) self._facet_number = { '+': gem.VariableIndex(gem.Indexed(facet, (0, ))), '-': gem.VariableIndex(gem.Indexed(facet, (1, ))) } elif integral_type == 'interior_facet_horiz': self._facet_number = {'+': 1, '-': 0}
def translate_coefficient(terminal, mt, ctx): vec = ctx.coefficient(terminal, mt.restriction) if terminal.ufl_element().family() == 'Real': assert mt.local_derivatives == 0 return vec element = ctx.create_element(terminal.ufl_element()) # Collect FInAT tabulation for all entities per_derivative = collections.defaultdict(list) for entity_id in ctx.entity_ids: finat_dict = ctx.basis_evaluation(element, mt.local_derivatives, entity_id) for alpha, table in finat_dict.items(): # Filter out irrelevant derivatives if sum(alpha) == mt.local_derivatives: # A numerical hack that FFC used to apply on FIAT # tables still lives on after ditching FFC and # switching to FInAT. table = ffc_rounding(table, ctx.epsilon) per_derivative[alpha].append(table) # Merge entity tabulations for each derivative if len(ctx.entity_ids) == 1: def take_singleton(xs): x, = xs # asserts singleton return x per_derivative = {alpha: take_singleton(tables) for alpha, tables in per_derivative.items()} else: f = ctx.entity_number(mt.restriction) per_derivative = {alpha: gem.select_expression(tables, f) for alpha, tables in per_derivative.items()} # Coefficient evaluation ctx.index_cache.setdefault(terminal.ufl_element(), element.get_indices()) beta = ctx.index_cache[terminal.ufl_element()] zeta = element.get_value_indices() vec_beta, = gem.optimise.remove_componenttensors([gem.Indexed(vec, beta)]) value_dict = {} for alpha, table in per_derivative.items(): table_qi = gem.Indexed(table, beta + zeta) summands = [] for var, expr in unconcatenate([(vec_beta, table_qi)], ctx.index_cache): value = gem.IndexSum(gem.Product(expr, var), var.index_ordering()) summands.append(gem.optimise.contraction(value)) optimised_value = gem.optimise.make_sum(summands) value_dict[alpha] = gem.ComponentTensor(optimised_value, zeta) # Change from FIAT to UFL arrangement result = fiat_to_ufl(value_dict, mt.local_derivatives) assert result.shape == mt.expr.ufl_shape assert set(result.free_indices) <= set(ctx.point_indices) # Detect Jacobian of affine cells if not result.free_indices and all(numpy.count_nonzero(node.array) <= 2 for node in traversal((result,)) if isinstance(node, gem.Literal)): result = gem.optimise.aggressive_unroll(result) return result
def test_expressions(): x = gem.Variable("x", (3, 4)) y = gem.Variable("y", (4, )) i, j = gem.indices(2) xij = x[i, j] yj = y[j] assert xij == gem.Indexed(x, (i, j)) assert yj == gem.Indexed(y, (j, )) assert xij + yj == gem.Sum(xij, yj) assert xij * yj == gem.Product(xij, yj) assert xij - yj == gem.Sum(xij, gem.Product(gem.Literal(-1), yj)) assert xij / yj == gem.Division(xij, yj) assert xij + 1 == gem.Sum(xij, gem.Literal(1)) assert 1 + xij == gem.Sum(gem.Literal(1), xij) assert (xij + y).shape == (4, ) assert (x @ y).shape == (3, ) assert x.T.shape == (4, 3) with pytest.raises(ValueError): xij.T @ y with pytest.raises(ValueError): xij + "foo"
def _entity_support_dofs(self): esd = {} for entity_dim in self.cell.sub_entities.keys(): beta = self.get_indices() zeta = self.get_value_indices() entity_cell = self.cell.construct_subelement(entity_dim) quad = make_quadrature(entity_cell, (2 * numpy.array(self.degree)).tolist()) eps = 1.e-8 # Is this a safe value? result = {} for f in self.entity_dofs()[entity_dim].keys(): # Tabulate basis functions on the facet vals, = self.basis_evaluation(0, quad.point_set, entity=(entity_dim, f)).values() # Integrate the square of the basis functions on the facet. ints = gem.IndexSum( gem.Product( gem.IndexSum( gem.Product(gem.Indexed(vals, beta + zeta), gem.Indexed(vals, beta + zeta)), zeta), quad.weight_expression), quad.point_set.indices) evaluation, = evaluate([gem.ComponentTensor(ints, beta)]) ints = evaluation.arr.flatten() assert evaluation.fids == () result[f] = [dof for dof, i in enumerate(ints) if i > eps] esd[entity_dim] = result return esd
def conditional(self, o, condition, then, else_): assert condition.shape == () shape, = set([then.shape, else_.shape]) indices = gem.indices(len(shape)) return gem.ComponentTensor( gem.Conditional(condition, gem.Indexed(then, indices), gem.Indexed(else_, indices)), indices)
def gem_to_loopy(gem_expr, var2terminal, scalar_type): """ Method encapsulating stage 2. Converts the gem expression dag into imperoc first, and then further into loopy. :return slate_loopy: 2-tuple of loopy kernel for slate operations and loopy GlobalArg for the output variable. """ # Creation of return variables for outer loopy shape = gem_expr.shape if len(gem_expr.shape) != 0 else (1, ) idx = make_indices(len(shape)) indexed_gem_expr = gem.Indexed(gem_expr, idx) args = ([ loopy.GlobalArg("output", shape=shape, dtype=scalar_type, is_output=True, is_input=True) ] + [ loopy.GlobalArg(var.name, shape=var.shape, dtype=scalar_type) for var in var2terminal.keys() ]) ret_vars = [gem.Indexed(gem.Variable("output", shape), idx)] preprocessed_gem_expr = impero_utils.preprocess_gem([indexed_gem_expr]) # glue assignments to return variable assignments = list(zip(ret_vars, preprocessed_gem_expr)) # Part A: slate to impero_c impero_c = impero_utils.compile_gem(assignments, (), remove_zeros=False) # Part B: impero_c to loopy return generate_loopy(impero_c, args, scalar_type, "slate_loopy", []), args[0].copy()
def point_evaluation_ciarlet(fiat_element, order, refcoords, entity): # Coordinates on the reference entity (SymPy) esd, = refcoords.shape Xi = sp.symbols('X Y Z')[:esd] # Coordinates on the reference cell cell = fiat_element.get_reference_element() X = cell.get_entity_transform(*entity)(Xi) # Evaluate expansion set at SymPy point poly_set = fiat_element.get_nodal_basis() degree = poly_set.get_embedded_degree() base_values = poly_set.get_expansion_set().tabulate(degree, [X]) m = len(base_values) assert base_values.shape == (m, 1) base_values_sympy = np.array(list(base_values.flat)) # Find constant polynomials def is_const(expr): try: float(expr) return True except TypeError: return False const_mask = np.array(list(map(is_const, base_values_sympy))) # Convert SymPy expression to GEM mapper = gem.node.Memoizer(sympy2gem) mapper.bindings = { s: gem.Indexed(refcoords, (i, )) for i, s in enumerate(Xi) } base_values = gem.ListTensor(list(map(mapper, base_values.flat))) # Populate result dict, creating precomputed coefficient # matrices for each derivative tuple. result = {} for i in range(order + 1): for alpha in mis(cell.get_spatial_dimension(), i): D = form_matrix_product(poly_set.get_dmats(), alpha) table = np.dot(poly_set.get_coeffs(), np.transpose(D)) assert table.shape[-1] == m zerocols = np.isclose( abs(table).max(axis=tuple(range(table.ndim - 1))), 0.0) if all(np.logical_or(const_mask, zerocols)): # Casting is safe by assertion of is_const vals = base_values_sympy[const_mask].astype(np.float64) result[alpha] = gem.Literal(table[..., const_mask].dot(vals)) else: beta = tuple(gem.Index() for s in table.shape[:-1]) k = gem.Index() result[alpha] = gem.ComponentTensor( gem.IndexSum( gem.Product( gem.Indexed(gem.Literal(table), beta + (k, )), gem.Indexed(base_values, (k, ))), (k, )), beta) return result
def compile_to_gem(expr, translator): """Compile a single pointwise expression to GEM. :arg expr: The expression to compile. :arg translator: a :class:`Translator` instance. :returns: A (lvalue, rvalue) pair of preprocessed GEM.""" if not isinstance(expr, Assign): raise ValueError( f"Don't know how to assign expression of type {type(expr)}") spaces = tuple(c.function_space() for c in expr.coefficients) if any( type(s.ufl_element()) is ufl.MixedElement for s in spaces if s is not None): raise ValueError("Not expecting a mixed space at this point, " "did you forget to index a function with .sub(...)?") if len(set(s.ufl_element() for s in spaces if s is not None)) != 1: raise ValueError("All coefficients must be defined on the same space") lvalue = expr.lvalue rvalue = expr.rvalue broadcast = all( isinstance(c, firedrake.Constant) for c in expr.rcoefficients) and rvalue.ufl_shape == () if not broadcast and lvalue.ufl_shape != rvalue.ufl_shape: try: rvalue = reshape(rvalue, lvalue.ufl_shape) except ValueError: raise ValueError( "Mismatching shapes between lvalue and rvalue in pointwise assignment" ) rvalue, = map_expr_dags(LowerCompoundAlgebra(), [rvalue]) try: lvalue, rvalue = map_expr_dags(translator, [lvalue, rvalue]) except (AssertionError, ValueError): raise ValueError("Mismatching shapes in pointwise assignment. " "For intrinsically vector-/tensor-valued spaces make " "sure you're not using shaped Constants or literals.") indices = gem.indices(len(lvalue.shape)) if not broadcast: if rvalue.shape != lvalue.shape: raise ValueError( "Mismatching shapes in pointwise assignment. " "For intrinsically vector-/tensor-valued spaces make " "sure you're not using shaped Constants or literals.") rvalue = gem.Indexed(rvalue, indices) lvalue = gem.Indexed(lvalue, indices) if isinstance(expr, IAdd): rvalue = gem.Sum(lvalue, rvalue) elif isinstance(expr, ISub): rvalue = gem.Sum(lvalue, gem.Product(gem.Literal(-1), rvalue)) elif isinstance(expr, IMul): rvalue = gem.Product(lvalue, rvalue) elif isinstance(expr, IDiv): rvalue = gem.Division(lvalue, rvalue) return preprocess_gem([lvalue, rvalue])
def basis_evaluation(self, order, ps, entity=None): '''Return code for evaluating the element at known points on the reference element. :param order: return derivatives up to this order. :param ps: the point set. :param entity: the cell entity on which to tabulate. ''' space_dimension = self._element.space_dimension() value_size = np.prod(self._element.value_shape(), dtype=int) fiat_result = self._element.tabulate(order, ps.points, entity) result = {} for alpha, fiat_table in iteritems(fiat_result): if isinstance(fiat_table, Exception): result[alpha] = gem.Failure( self.index_shape + self.value_shape, fiat_table) continue derivative = sum(alpha) table_roll = fiat_table.reshape(space_dimension, value_size, len(ps.points)).transpose(1, 2, 0) exprs = [] for table in table_roll: if derivative < self.degree: point_indices = ps.indices point_shape = tuple(index.extent for index in point_indices) exprs.append( gem.partial_indexed( gem.Literal( table.reshape(point_shape + self.index_shape)), point_indices)) elif derivative == self.degree: # Make sure numerics satisfies theory assert np.allclose(table, table.mean(axis=0, keepdims=True)) exprs.append(gem.Literal(table[0])) else: # Make sure numerics satisfies theory assert np.allclose(table, 0.0) exprs.append(gem.Zero(self.index_shape)) if self.value_shape: beta = self.get_indices() zeta = self.get_value_indices() result[alpha] = gem.ComponentTensor( gem.Indexed( gem.ListTensor( np.array([ gem.Indexed(expr, beta) for expr in exprs ]).reshape(self.value_shape)), zeta), beta + zeta) else: expr, = exprs result[alpha] = expr return result
def to_reference_coordinates(ufl_coordinate_element): # Set up UFL form cell = ufl_coordinate_element.cell() domain = ufl.Mesh(ufl_coordinate_element) K = ufl.JacobianInverse(domain) x = ufl.SpatialCoordinate(domain) x0_element = ufl.VectorElement("Real", cell, 0) x0 = ufl.Coefficient(ufl.FunctionSpace(domain, x0_element)) expr = ufl.dot(K, x - x0) # Translation to GEM C = ufl_utils.coordinate_coefficient(domain) expr = ufl_utils.preprocess_expression(expr) expr = ufl_utils.replace_coordinates(expr, C) expr = ufl_utils.simplify_abs(expr) builder = firedrake_interface.KernelBuilderBase() builder._coefficient(C, "C") builder._coefficient(x0, "x0") dim = cell.topological_dimension() point = gem.Variable('X', (dim, )) context = tsfc.fem.GemPointContext( interface=builder, ufl_cell=cell, precision=parameters["precision"], point_indices=(), point_expr=point, ) translator = tsfc.fem.Translator(context) ir = map_expr_dag(translator, expr) # Unroll result ir = [gem.Indexed(ir, alpha) for alpha in numpy.ndindex(ir.shape)] # Unroll IndexSums max_extent = parameters["unroll_indexsum"] if max_extent: def predicate(index): return index.extent <= max_extent ir = gem.optimise.unroll_indexsum(ir, predicate=predicate) # Translate to COFFEE ir = impero_utils.preprocess_gem(ir) return_variable = gem.Variable('dX', (dim, )) assignments = [(gem.Indexed(return_variable, (i, )), e) for i, e in enumerate(ir)] impero_c = impero_utils.compile_gem(assignments, ()) body = tsfc.coffee.generate(impero_c, {}, parameters["precision"]) body.open_scope = False return body
def test_pickle_gem(protocol): f = gem.VariableIndex(gem.Indexed(gem.Variable('facet', (2, )), (1, ))) q = gem.Index() r = gem.Index() _1 = gem.Indexed(gem.Literal(numpy.random.rand(3, 6, 8)), (f, q, r)) _2 = gem.Indexed( gem.view(gem.Variable('w', (None, None)), slice(8), slice(1)), (r, 0)) expr = gem.ComponentTensor(gem.IndexSum(gem.Product(_1, _2), (r, )), (q, )) unpickled = pickle.loads(pickle.dumps(expr, protocol)) assert repr(expr) == repr(unpickled)
def merge(tables): tables = tuple(tables) zeta = self.get_value_indices() tensors = [] for elem, table in zip(self.elements, tables): beta_i = elem.get_indices() tensors.append( gem.ComponentTensor(gem.Indexed(table, beta_i + zeta), beta_i)) beta = self.get_indices() return gem.ComponentTensor( gem.Indexed(gem.Concatenate(*tensors), beta), beta + zeta)
def test_refactorise(): f = gem.Variable('f', (3,)) u = gem.Variable('u', (3,)) v = gem.Variable('v', ()) i = gem.Index() f_i = gem.Indexed(f, (i,)) u_i = gem.Indexed(u, (i,)) def classify(atomics_set, expression): if expression in atomics_set: return ATOMIC for node in traversal([expression]): if node in atomics_set: return COMPOUND return OTHER classifier = partial(classify, {u_i, v}) # \sum_i 5*(2*u_i + -1*v)*(u_i + v*f) expr = gem.IndexSum( gem.Product( gem.Literal(5), gem.Product( gem.Sum(gem.Product(gem.Literal(2), u_i), gem.Product(gem.Literal(-1), v)), gem.Sum(u_i, gem.Product(v, f_i)) ) ), (i,) ) expected = [ Monomial((i,), (u_i, u_i), gem.Literal(10)), Monomial((i,), (u_i, v), gem.Product(gem.Literal(5), gem.Sum(gem.Product(f_i, gem.Literal(2)), gem.Literal(-1)))), Monomial((), (v, v), gem.Product(gem.Literal(5), gem.IndexSum(gem.Product(f_i, gem.Literal(-1)), (i,)))), ] actual, = collect_monomials([expr], classifier) assert expected == list(actual)
def __init__(self, interior_facet=False): """Initialise a kernel builder. :arg interior_facet: kernel accesses two cells """ super(KernelBuilderBase, self).__init__(interior_facet=interior_facet) # Cell orientation if self.interior_facet: cell_orientations = gem.Variable("cell_orientations", (2, )) self._cell_orientations = (gem.Indexed(cell_orientations, (0, )), gem.Indexed(cell_orientations, (1, ))) else: cell_orientations = gem.Variable("cell_orientations", (1, )) self._cell_orientations = (gem.Indexed(cell_orientations, (0, )), )
def translate_cell_edge_vectors(terminal, mt, ctx): # WARNING: Assumes straight edges! coords = CellVertices(terminal.ufl_domain()) ufl_expr = construct_modified_terminal(mt, coords) cell_vertices = ctx.translator(ufl_expr) e = gem.Index() c = gem.Index() expr = gem.ListTensor([ gem.Sum( gem.Indexed(cell_vertices, (u, c)), gem.Product(gem.Literal(-1), gem.Indexed(cell_vertices, (v, c)))) for _, (u, v) in sorted(ctx.fiat_cell.get_topology()[1].items()) ]) return gem.ComponentTensor(gem.Indexed(expr, (e, )), (e, c))
def point_evaluation(self, order, point, entity=None): entities = self._factor_entity(entity) entity_dim, _ = zip(*entities) # Split point expression assert len(self.cell.cells) == len(entity_dim) point_dims = [ cell.construct_subelement(dim).get_spatial_dimension() for cell, dim in zip(self.cell.cells, entity_dim) ] assert isinstance(point, gem.Node) and point.shape == (sum(point_dims), ) slices = TensorProductCell._split_slices(point_dims) point_factors = [] for s in slices: point_factors.append( gem.ListTensor([ gem.Indexed(point, (i, )) for i in range(s.start, s.stop) ])) # Subelement results factor_results = [ fe.point_evaluation(order, p_, e) for fe, p_, e in zip(self.factors, point_factors, entities) ] return self._merge_evaluations(factor_results)
def basis_evaluation(self, order, ps, entity=None): r"""Produce the recipe for basis function evaluation at a set of points :math:`q`: .. math:: \boldsymbol\phi_{(\gamma \epsilon) (i \alpha \beta) q} = \delta_{\alpha \gamma}\delta{\beta \epsilon}\phi_{i q} \nabla\boldsymbol\phi_{(\epsilon \gamma \zeta) (i \alpha \beta) q} = \delta_{\alpha \epsilon} \deta{\beta \gamma}\nabla\phi_{\zeta i q} """ # Old basis function and value indices scalar_i = self._base_element.get_indices() scalar_vi = self._base_element.get_value_indices() # New basis function and value indices tensor_i = tuple(gem.Index(extent=d) for d in self._shape) tensor_vi = tuple(gem.Index(extent=d) for d in self._shape) # Couple new basis function and value indices deltas = reduce(gem.Product, (gem.Delta(j, k) for j, k in zip(tensor_i, tensor_vi))) scalar_result = self._base_element.basis_evaluation(order, ps, entity) result = {} for alpha, expr in iteritems(scalar_result): result[alpha] = gem.ComponentTensor( gem.Product(deltas, gem.Indexed(expr, scalar_i + scalar_vi)), scalar_i + tensor_i + scalar_vi + tensor_vi) return result
def _tensorise(self, scalar_evaluation): # Old basis function and value indices scalar_i = self._base_element.get_indices() scalar_vi = self._base_element.get_value_indices() # New basis function and value indices tensor_i = tuple(gem.Index(extent=d) for d in self._shape) tensor_vi = tuple(gem.Index(extent=d) for d in self._shape) # Couple new basis function and value indices deltas = reduce(gem.Product, (gem.Delta(j, k) for j, k in zip(tensor_i, tensor_vi))) if self._transpose: index_ordering = tensor_i + scalar_i + tensor_vi + scalar_vi else: index_ordering = scalar_i + tensor_i + tensor_vi + scalar_vi result = {} for alpha, expr in scalar_evaluation.items(): result[alpha] = gem.ComponentTensor( gem.Product(deltas, gem.Indexed(expr, scalar_i + scalar_vi)), index_ordering ) return result
def physical_points(self, point_set, entity=None): """Converts point_set from reference to physical space""" expr = SpatialCoordinate(self.mt.terminal.ufl_domain()) point_shape, = point_set.expression.shape if entity is not None: e, _ = entity assert point_shape == e else: assert point_shape == expr.ufl_domain().topological_dimension() if self.mt.restriction == '+': expr = PositiveRestricted(expr) elif self.mt.restriction == '-': expr = NegativeRestricted(expr) config = {"point_set": point_set} config.update(self.config) if entity is not None: config.update({ name: getattr(self.interface, name) for name in ["integration_dim", "entity_ids"] }) context = PointSetContext(**config) expr = self.preprocess(expr, context) mapped = map_expr_dag(context.translator, expr) indices = tuple(gem.Index() for _ in mapped.shape) return gem.ComponentTensor(gem.Indexed(mapped, indices), point_set.indices + indices)
def einsum(factors, sum_indices): """Evaluates a tensor product at compile time. :arg factors: iterable of indexed GEM literals :arg sum_indices: indices to sum over :returns: a single indexed GEM literal """ # Maps the problem onto numpy.einsum index2letter = defaultdict(partial(lambda c: chr(ord('i') + next(c)), count())) operands = [] subscript_parts = [] for factor in factors: literal, = factor.children selectors = [] letters = [] for index in factor.multiindex: if isinstance(index, int): selectors.append(index) else: selectors.append(slice(None)) letters.append(index2letter[index]) operands.append(literal.array.__getitem__(tuple(selectors))) subscript_parts.append(''.join(letters)) result_pairs = sorted((letter, index) for index, letter in index2letter.items() if index not in sum_indices) subscripts = ','.join(subscript_parts) + '->' + ''.join(l for l, i in result_pairs) tensor = numpy.einsum(subscripts, *operands) return gem.Indexed(gem.Literal(tensor), tuple(i for l, i in result_pairs))
def translate_argument(terminal, mt, ctx): argument_multiindex = ctx.argument_multiindices[terminal.number()] sigma = tuple(gem.Index(extent=d) for d in mt.expr.ufl_shape) element = ctx.create_element(terminal.ufl_element(), restriction=mt.restriction) def callback(entity_id): finat_dict = ctx.basis_evaluation(element, mt, entity_id) # Filter out irrelevant derivatives filtered_dict = { alpha: table for alpha, table in finat_dict.items() if sum(alpha) == mt.local_derivatives } # Change from FIAT to UFL arrangement square = fiat_to_ufl(filtered_dict, mt.local_derivatives) # A numerical hack that FFC used to apply on FIAT tables still # lives on after ditching FFC and switching to FInAT. return ffc_rounding(square, ctx.epsilon) table = ctx.entity_selector(callback, mt.restriction) return gem.ComponentTensor(gem.Indexed(table, argument_multiindex + sigma), sigma)
def matvec(table): i, j = gem.indices(2) value_indices = self.get_value_indices() table = gem.Indexed(table, (j, ) + value_indices) val = gem.ComponentTensor(gem.IndexSum(M[i, j] * table, (j, )), (i, ) + value_indices) # Eliminate zeros return gem.optimise.aggressive_unroll(val)
def prepare_arguments(arguments, multiindices, scalar_type, interior_facet=False, diagonal=False): """Bridges the kernel interface and the GEM abstraction for Arguments. Vector Arguments are rearranged here for interior facet integrals. :arg arguments: UFL Arguments :arg multiindices: Argument multiindices :arg interior_facet: interior facet integral? :arg diagonal: Are we assembling the diagonal of a rank-2 element tensor? :returns: (funarg, expression) funarg - :class:`loopy.GlobalArg` function argument expressions - GEM expressions referring to the argument tensor """ assert isinstance(interior_facet, bool) if len(arguments) == 0: # No arguments funarg = lp.GlobalArg("A", dtype=scalar_type, shape=(1,)) expression = gem.Indexed(gem.Variable("A", (1,)), (0,)) return funarg, [expression] elements = tuple(create_element(arg.ufl_element()) for arg in arguments) shapes = tuple(element.index_shape for element in elements) if diagonal: if len(arguments) != 2: raise ValueError("Diagonal only for 2-forms") try: element, = set(elements) except ValueError: raise ValueError("Diagonal only for diagonal blocks (test and trial spaces the same)") elements = (element, ) shapes = tuple(element.index_shape for element in elements) multiindices = multiindices[:1] def expression(restricted): return gem.Indexed(gem.reshape(restricted, *shapes), tuple(chain(*multiindices))) u_shape = numpy.array([numpy.prod(shape, dtype=int) for shape in shapes]) if interior_facet: c_shape = tuple(2 * u_shape) slicez = [[slice(r * s, (r + 1) * s) for r, s in zip(restrictions, u_shape)] for restrictions in product((0, 1), repeat=len(arguments))] else: c_shape = tuple(u_shape) slicez = [[slice(s) for s in u_shape]] funarg = lp.GlobalArg("A", dtype=scalar_type, shape=c_shape) varexp = gem.Variable("A", c_shape) expressions = [expression(gem.view(varexp, *slices)) for slices in slicez] return funarg, prune(expressions)
def __init__(self, scalar_type=None, interior_facet=False): """Initialise a kernel builder. :arg interior_facet: kernel accesses two cells """ if scalar_type is None: from tsfc.parameters import SCALAR_TYPE scalar_type = SCALAR_TYPE super(KernelBuilderBase, self).__init__(scalar_type=scalar_type, interior_facet=interior_facet) # Cell orientation if self.interior_facet: cell_orientations = gem.Variable("cell_orientations", (2, )) self._cell_orientations = (gem.Indexed(cell_orientations, (0, )), gem.Indexed(cell_orientations, (1, ))) else: cell_orientations = gem.Variable("cell_orientations", (1, )) self._cell_orientations = (gem.Indexed(cell_orientations, (0, )), )
def __init__(self, scalar_type, interior_facet=False): """Initialise a kernel builder. :arg interior_facet: kernel accesses two cells """ super(KernelBuilderBase, self).__init__(scalar_type=scalar_type, interior_facet=interior_facet) # Cell orientation if self.interior_facet: shape = (2,) cell_orientations = gem.Variable("cell_orientations", shape) self._cell_orientations = (gem.Indexed(cell_orientations, (0,)), gem.Indexed(cell_orientations, (1,))) else: shape = (1,) cell_orientations = gem.Variable("cell_orientations", shape) self._cell_orientations = (gem.Indexed(cell_orientations, (0,)),) self.cell_orientations_loopy_arg = lp.GlobalArg("cell_orientations", dtype=numpy.int32, shape=shape)
def transpose(expr, rank): assert not expr.free_indices assert 0 <= rank < len(expr.shape) if rank == 0: return expr else: indices = tuple(gem.Index(extent=extent) for extent in expr.shape) transposed_indices = indices[rank:] + indices[:rank] return gem.ComponentTensor(gem.Indexed(expr, indices), transposed_indices)
def select_hdiv_transformer(element): # Assume: something x interval assert len(element.factors) == 2 assert element.factors[1].cell.get_shape() == LINE # Globally consistent edge orientations of the reference # quadrilateral: rightward horizontally, upward vertically. # Their rotation by 90 degrees anticlockwise is interpreted as the # positive direction for normal vectors. ks = tuple(fe.formdegree for fe in element.factors) if ks == (0, 1): # Make the scalar value the leftward-pointing normal on the # y-aligned edges. return lambda v: [gem.Product(gem.Literal(-1), v), gem.Zero()] elif ks == (1, 0): # Make the scalar value the upward-pointing normal on the # x-aligned edges. return lambda v: [gem.Zero(), v] elif ks == (2, 0): # Same for 3D, so z-plane. return lambda v: [gem.Zero(), gem.Zero(), v] elif ks == (1, 1): if element.mapping == "contravariant piola": # Pad the 2-vector normal on the "base" cell into a # 3-vector, maintaining direction. return lambda v: [ gem.Indexed(v, (0, )), gem.Indexed(v, (1, )), gem.Zero() ] elif element.mapping == "covariant piola": # Rotate the 2-vector tangential component on the "base" # cell 90 degrees anticlockwise into a 3-vector and pad. return lambda v: [ gem.Indexed(v, (1, )), gem.Product(gem.Literal(-1), gem.Indexed(v, (0, ))), gem.Zero() ] else: assert False, "Unexpected original mapping!" else: assert False, "Unexpected form degree combination!"
def entity_support_dofs(elem, entity_dim): """Return the map of entity id to the degrees of freedom for which the corresponding basis functions take non-zero values. :arg elem: FInAT finite element :arg entity_dim: Dimension of the cell subentity. """ if not hasattr(elem, "_entity_support_dofs"): elem._entity_support_dofs = {} cache = elem._entity_support_dofs try: return cache[entity_dim] except KeyError: pass beta = elem.get_indices() zeta = elem.get_value_indices() entity_cell = elem.cell.construct_subelement(entity_dim) quad = make_quadrature(entity_cell, (2 * numpy.array(elem.degree)).tolist()) eps = 1.e-8 # Is this a safe value? result = {} for f in elem.entity_dofs()[entity_dim].keys(): # Tabulate basis functions on the facet vals, = itervalues( elem.basis_evaluation(0, quad.point_set, entity=(entity_dim, f))) # Integrate the square of the basis functions on the facet. ints = gem.IndexSum( gem.Product( gem.IndexSum( gem.Product(gem.Indexed(vals, beta + zeta), gem.Indexed(vals, beta + zeta)), zeta), quad.weight_expression), quad.point_set.indices) ints = aggressive_unroll(gem.ComponentTensor(ints, beta)).array.flatten() result[f] = [dof for dof, i in enumerate(ints) if i > eps] cache[entity_dim] = result return result
def point_evaluation_generic(fiat_element, order, refcoords, entity): # Coordinates on the reference entity (SymPy) esd, = refcoords.shape Xi = sp.symbols('X Y Z')[:esd] space_dimension = fiat_element.space_dimension() value_size = np.prod(fiat_element.value_shape(), dtype=int) fiat_result = fiat_element.tabulate(order, [Xi], entity) result = {} for alpha, fiat_table in fiat_result.items(): if isinstance(fiat_table, Exception): result[alpha] = gem.Failure( (space_dimension, ) + fiat_element.value_shape(), fiat_table) continue # Convert SymPy expression to GEM mapper = gem.node.Memoizer(sympy2gem) mapper.bindings = { s: gem.Indexed(refcoords, (i, )) for i, s in enumerate(Xi) } gem_table = np.vectorize(mapper)(fiat_table) table_roll = gem_table.reshape(space_dimension, value_size).transpose() exprs = [] for table in table_roll: exprs.append(gem.ListTensor(table.reshape(space_dimension))) if fiat_element.value_shape(): beta = (gem.Index(extent=space_dimension), ) zeta = tuple( gem.Index(extent=d) for d in fiat_element.value_shape()) result[alpha] = gem.ComponentTensor( gem.Indexed( gem.ListTensor( np.array([gem.Indexed(expr, beta) for expr in exprs ]).reshape(fiat_element.value_shape())), zeta), beta + zeta) else: expr, = exprs result[alpha] = expr return result
def fiat_to_ufl(fiat_dict, order): # All derivative multiindices must be of the same dimension. dimension, = set(len(alpha) for alpha in fiat_dict.keys()) # All derivative tables must have the same shape. shape, = set(table.shape for table in fiat_dict.values()) sigma = tuple(gem.Index(extent=extent) for extent in shape) # Convert from FIAT to UFL format eye = numpy.eye(dimension, dtype=int) tensor = numpy.empty((dimension, ) * order, dtype=object) for multiindex in numpy.ndindex(tensor.shape): alpha = tuple(eye[multiindex, :].sum(axis=0)) tensor[multiindex] = gem.Indexed(fiat_dict[alpha], sigma) delta = tuple(gem.Index(extent=dimension) for _ in range(order)) if order > 0: tensor = gem.Indexed(gem.ListTensor(tensor), delta) else: tensor = tensor[()] return gem.ComponentTensor(tensor, sigma + delta)