def test_triangle_variant_spectral_fail_l2(): ufl_element = ufl.FiniteElement('DP L2', ufl.triangle, 2, variant='spectral') with pytest.raises(ValueError): create_element(ufl_element)
def test_cache_hit_vector(ufl_vector_element): A = f.create_element(ufl_vector_element) B = f.create_element(ufl_vector_element) assert A is B assert all(a == A.elements()[0] for a in A.elements())
def test_cache_miss_vector(ufl_vector_element): A = f.create_element(ufl_vector_element) B = f.create_element(ufl_vector_element, vector_is_mixed=False) assert A is not B assert A.elements()[0] is not B
def test_tensor_prod_simple(ufl_A, ufl_B): tensor_ufl = ufl.TensorProductElement(ufl_A, ufl_B) tensor = f.create_element(tensor_ufl) A = f.create_element(ufl_A) B = f.create_element(ufl_B) assert isinstance(tensor, f.supported_elements[tensor_ufl.family()]) assert tensor.A is A assert tensor.B is B
def test_tensor_prod_simple(ufl_A, ufl_B): tensor_ufl = ufl.TensorProductElement(ufl_A, ufl_B) tensor = create_element(tensor_ufl) A = create_element(ufl_A) B = create_element(ufl_B) assert isinstance(tensor, supported_elements[tensor_ufl.family()]) assert tensor.A is A assert tensor.B is B
def test_tensor_prod_simple(ufl_A, ufl_B): tensor_ufl = ufl.TensorProductElement(ufl_A, ufl_B) tensor = create_element(tensor_ufl) A = create_element(ufl_A) B = create_element(ufl_B) assert isinstance(tensor, FIAT.TensorProductElement) assert tensor.A is A assert tensor.B is B
def test_triangle_vector(mixed, ufl_element, ufl_vector_element): scalar = f.create_element(ufl_element) vector = f.create_element(ufl_vector_element, vector_is_mixed=mixed) if not mixed: assert isinstance(scalar, f.supported_elements[ufl_element.family()]) assert isinstance(vector, f.supported_elements[ufl_element.family()]) else: assert isinstance(vector, f.MixedElement) assert isinstance(vector.elements()[0], f.supported_elements[ufl_element.family()]) assert len(vector.elements()) == ufl_vector_element.num_sub_elements()
def test_quadrilateral_variant_spectral_rtcf(): element = create_element( ufl.FiniteElement('RTCF', ufl.quadrilateral, 2, variant='spectral')) assert isinstance(element.element.A.A, FIAT.GaussLobattoLegendre) assert isinstance(element.element.A.B, FIAT.GaussLegendre) assert isinstance(element.element.B.A, FIAT.GaussLegendre) assert isinstance(element.element.B.B, FIAT.GaussLobattoLegendre)
def compile_python_kernel(expression, to_pts, to_element, fs, coords): """Produce a :class:`PyOP2.Kernel` wrapping the eval method on the function provided.""" coords_space = coords.function_space() coords_element = create_element(coords_space.ufl_element(), vector_is_mixed=False) X_remap = list(coords_element.tabulate(0, to_pts).values())[0] # The par_loop will just pass us arguments, since it doesn't # know about keyword args at all so unpack into a dict that we # can pass to the user's eval method. def kernel(output, x, *args): kwargs = {} for (slot, _), arg in zip(expression._user_args, args): kwargs[slot] = arg X = numpy.dot(X_remap.T, x) for i in range(len(output)): # Pass a slice for the scalar case but just the # current vector in the VFS case. This ensures the # eval method has a Dolfin compatible API. expression.eval(output[i:i+1, ...] if numpy.ndim(output) == 1 else output[i, ...], X[i:i+1, ...] if numpy.ndim(X) == 1 else X[i, ...], **kwargs) coefficients = [coords] for _, arg in expression._user_args: coefficients.append(GlobalWrapper(arg)) return kernel, False, False, tuple(coefficients)
def _calculate_values(function, points, dimension, cell_mask=None): """Calculate function values at given reference points :arg function: function to be sampled :arg points: points to be sampled in reference space :arg cell_mask: Masks for cell node list """ import numpy.ma as ma function_space = function.function_space() keys = {1: (0,), 2: (0, 0)} fiat_element = create_element(function_space.ufl_element(), vector_is_mixed=False) elem = fiat_element.tabulate(0, points)[keys[dimension]] cell_node_list = function_space.cell_node_list if cell_mask is not None: cell_mask = np.tile(cell_mask.reshape(-1, 1), cell_node_list.shape[1]) cell_node_list = ma.compress_rows(ma.masked_array(cell_node_list, mask=cell_mask)) data = function.dat.data_ro[cell_node_list] if function.ufl_shape == (): vec_length = 1 else: vec_length = function.ufl_shape[0] if vec_length == 1: data = np.reshape(data, data.shape+(1, )) return np.einsum("ijk,jl->ilk", data, elem)
def get_transfer_kernel(coarse, fine, typ=None): ch, level = get_level(coarse.mesh()) mesh = ch[0] assert hasattr(mesh, "_shared_data_cache") key = entity_dofs_key(coarse.finat_element.entity_dofs()) + (typ, coarse.dim) cache = mesh._shared_data_cache["hierarchy_transfer_kernel"] try: return cache[key] except KeyError: if not (coarse.ufl_element() == fine.ufl_element()): raise ValueError("Can't transfer between different spaces") ch, level = get_level(coarse.mesh()) fh, fine_level = get_level(fine.mesh()) refinements_per_level = ch.refinements_per_level assert ch is fh assert refinements_per_level*level + 1 == refinements_per_level*fine_level dim = coarse.dim element = create_element(coarse.ufl_element(), vector_is_mixed=False) omap = fine.cell_node_map().values c2f, vperm = ch._cells_vperm[int(level*refinements_per_level)] indices, _ = get_unique_indices(element, omap[c2f[0, :], ...].reshape(-1), vperm[0, :], offset=None) if typ == "prolong": kernel = get_prolongation_kernel(element, indices, dim) elif typ == "inject": kernel = get_injection_kernel(element, indices, dim) elif typ == "restrict": discontinuous = element.entity_dofs() == element.entity_closure_dofs() kernel = get_restriction_kernel(element, indices, dim, no_weights=discontinuous) else: raise ValueError("Unknown transfer type '%s'" % typ) return cache.setdefault(key, kernel)
def convert_finiteelement(element): cell = as_fiat_cell(element.cell()) if element.family() not in supported_elements: return fiat_compat(element) lmbda = supported_elements.get(element.family()) if lmbda is None: if element.cell().cellname() != "quadrilateral": raise ValueError("%s is supported, but handled incorrectly" % element.family()) # Handle quadrilateral short names like RTCF and RTCE. element = element.reconstruct(cell=quad_tpc) return finat.QuadrilateralElement(create_element(element)) kind = element.variant() if kind is None: kind = 'equispaced' # default variant if element.family() == "Lagrange": if kind == 'equispaced': lmbda = finat.Lagrange elif kind == 'spectral' and element.cell().cellname() == 'interval': lmbda = finat.GaussLobattoLegendre else: raise ValueError("Variant %r not supported on %s" % (kind, element.cell())) elif element.family() == "Discontinuous Lagrange": kind = element.variant() or 'equispaced' if kind == 'equispaced': lmbda = finat.DiscontinuousLagrange elif kind == 'spectral' and element.cell().cellname() == 'interval': lmbda = finat.GaussLegendre else: raise ValueError("Variant %r not supported on %s" % (kind, element.cell())) return lmbda(cell, element.degree())
def fix_coarse_boundaries(cV, fV): # With lots of help from Lawrence Mitchell c2fmap = coarse_to_fine_node_map(cV, fV) cmesh = cV.mesh() ch, level = get_level(cmesh) refinements_per_level = ch.refinements_per_level element = create_element(cV.ufl_element(), vector_is_mixed=False) omap = fV.cell_node_map().values c2f, vperm = ch._cells_vperm[int(level * refinements_per_level)] indices, _ = get_unique_indices(element, omap[c2f[0, :], ...].reshape(-1), vperm[0, :], offset=None) weights = restriction_weights(element)[indices] sums = (weights > 0).sum(axis=1) indices_to_fix = where(sums <= cmesh.topological_dimension())[0] nodelist = unique(c2fmap.values[:, indices_to_fix]) class FixedDirichletBC(DirichletBC): def __init__(self, V, g, nodelist): self.nodelist = nodelist DirichletBC.__init__(self, V, g, "on_boundary") @utils.cached_property def nodes(self): return self.nodelist bc = FixedDirichletBC(fV, (0, 0), nodelist) return bc
def _interpolator(V, dat, expr, subset): to_element = create_element(V.ufl_element(), vector_is_mixed=False) to_pts = [] if V.ufl_element().mapping() != "identity": raise NotImplementedError("Can only interpolate onto elements " "with affine mapping. Try projecting instead") for dual in to_element.dual_basis(): if not isinstance(dual, FIAT.functional.PointEvaluation): raise NotImplementedError("Can only interpolate onto point " "evaluation operators. Try projecting instead") to_pts.append(list(iterkeys(dual.pt_dict))[0]) if len(expr.ufl_shape) != len(V.ufl_element().value_shape()): raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d' % (len(expr.ufl_shape), len(V.ufl_element().value_shape()))) if expr.ufl_shape != V.ufl_element().value_shape(): raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r' % (expr.ufl_shape, V.ufl_element().value_shape())) mesh = V.ufl_domain() coords = mesh.coordinates if not isinstance(expr, (firedrake.Expression, SubExpression)): if expr.ufl_domain() and expr.ufl_domain() != V.mesh(): raise NotImplementedError("Interpolation onto another mesh not supported.") if expr.ufl_shape != V.shape: raise ValueError("UFL expression has incorrect shape for interpolation.") ast, oriented, coefficients = compile_ufl_kernel(expr, to_pts, coords) kernel = op2.Kernel(ast, ast.name) indexed = True elif hasattr(expr, "eval"): kernel, oriented, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords) indexed = False elif expr.code is not None: kernel, oriented, coefficients = compile_c_kernel(expr, to_pts, to_element, V, coords) indexed = True else: raise RuntimeError("Attempting to evaluate an Expression which has no value.") cell_set = coords.cell_set if subset is not None: assert subset.superset == cell_set cell_set = subset args = [kernel, cell_set] if indexed: args.append(dat(op2.WRITE, V.cell_node_map()[op2.i[0]])) else: args.append(dat(op2.WRITE, V.cell_node_map())) if oriented: co = mesh.cell_orientations() args.append(co.dat(op2.READ, co.cell_node_map())) for coefficient in coefficients: args.append(coefficient.dat(op2.READ, coefficient.cell_node_map())) return partial(op2.par_loop, *args)
def prolongation_transfer_kernel_action(Vf, expr): from tsfc import compile_expression_dual_evaluation from tsfc.fiatinterface import create_element coords = Vf.ufl_domain().coordinates to_element = create_element(Vf.ufl_element(), vector_is_mixed=False) ast, oriented, needs_cell_sizes, coefficients, _ = compile_expression_dual_evaluation( expr, to_element, coords, coffee=False) return op2.Kernel(ast, ast.name)
def firedrake_local_to_cart(element): r"""Gets the list of nodes for an element (provided they exist.) :arg element: a ufl element. :returns: a list of arrays of floats where each array is a node. """ fiat_element = create_element(element, vector_is_mixed=False) # TODO: Surely there is an easier way that I've forgotten? return [np.array(list(phi.get_point_dict().keys())[0]) for phi in fiat_element.dual_basis()]
def prolongation_transfer_kernel_aij(Pk, P1): # Works for Pk, Pm; I just retain the notation # P1 to remind you that P1 is of lower degree # than Pk from tsfc import compile_expression_dual_evaluation from tsfc.fiatinterface import create_element from firedrake import TestFunction expr = TestFunction(P1) coords = Pk.ufl_domain().coordinates to_element = create_element(Pk.ufl_element(), vector_is_mixed=False) ast, oriented, needs_cell_sizes, coefficients, _ = compile_expression_dual_evaluation(expr, to_element, coords, coffee=False) kernel = op2.Kernel(ast, ast.name) return kernel
def _bezier_calculate_points(function): """Calculate points values for a function used for bezier plotting :arg function: 1D Function with 1 < deg < 4 """ deg = function.function_space().ufl_element().degree() M = np.empty([deg + 1, deg + 1], dtype=float) fiat_element = create_element(function.function_space().ufl_element(), vector_is_mixed=False) basis = fiat_element.dual_basis() for i in range(deg + 1): for j in range(deg + 1): M[i, j] = _bernstein(list(basis[j].get_point_dict().keys())[0][0], i, deg) M_inv = np.linalg.inv(M) cell_node_list = function.function_space().cell_node_list return np.dot(function.dat.data_ro[cell_node_list], M_inv)
def _bezier_calculate_points(function): """Calculate points values for a function used for bezier plotting :arg function: 1D Function with 1 < deg < 4 """ deg = function.function_space().ufl_element().degree() M = np.empty([deg + 1, deg + 1], dtype=float) fiat_element = create_element(function.function_space().ufl_element(), vector_is_mixed=False) basis = fiat_element.dual_basis() for i in range(deg + 1): for j in range(deg + 1): M[i, j] = _bernstein(basis[j].get_point_dict().keys()[0][0], i, deg) M_inv = np.linalg.inv(M) cell_node_list = function.function_space().cell_node_list return np.dot(function.dat.data_ro[cell_node_list], M_inv)
def __init__(self, mesh, element, name=None): super(FunctionSpace, self).__init__() if type(element) is ufl.MixedElement: raise ValueError("Can't create FunctionSpace for MixedElement") fiat_element = create_element(element, vector_is_mixed=False) sdata = get_shared_data(mesh, fiat_element) # The function space shape is the number of dofs per node, # hence it is not always the value_shape. Vector and Tensor # element modifiers *must* live on the outside! if type(element) is ufl.TensorElement: # UFL enforces value_shape of the subelement to be empty # on a TensorElement. self.shape = element.value_shape() elif type(element) is ufl.VectorElement: # First dimension of the value_shape is the VectorElement # shape. self.shape = element.value_shape()[:1] else: self.shape = () self._ufl_element = element self._shared_data = sdata self._mesh = mesh self.rank = len(self.shape) """The rank of this :class:`FunctionSpace`. Spaces where the element is scalar-valued (or intrinsically vector-valued) have rank zero. Spaces built on :class:`~ufl.classes.VectorElement` or :class:`~ufl.classes.TensorElement` instances have rank equivalent to the number of components of their :meth:`~ufl.classes.FiniteElementBase.value_shape`.""" self.dim = numpy.prod(self.shape, dtype=int) """The total number of degrees of freedom at each function space node.""" self.name = name """The (optional) descriptive name for this space.""" self.node_set = sdata.node_set """A :class:`pyop2.Set` representing the function space nodes.""" self.dof_dset = op2.DataSet(self.node_set, self.shape or 1, name="%s_nodes_dset" % self.name) """A :class:`pyop2.DataSet` representing the function space degrees of freedom.""" self.comm = self.node_set.comm self.fiat_element = fiat_element self.extruded = sdata.extruded self.offset = sdata.offset self.bt_masks = sdata.bt_masks
def prepare_arguments(arguments, indices, interior_facet=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 indices: Argument indices :arg interior_facet: interior facet integral? :returns: (funarg, prepare, expressions) funarg - :class:`coffee.Decl` function argument prepare - list of COFFEE nodes to be prepended to the kernel body expressions - GEM expressions referring to the argument tensor """ funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A"), pointers=[()]) varexp = gem.Variable("A", (None,)) elements = tuple(create_element(arg.ufl_element()) for arg in arguments) space_dimensions = tuple(element.space_dimension() for element in elements) for i, sd in zip(indices, space_dimensions): i.set_extent(sd) if arguments and interior_facet: shape = tuple(2 * element.space_dimension() for element in elements) strides = cumulative_strides(shape) expressions = [] for restrictions in product((0, 1), repeat=len(arguments)): offset = sum(r * sd * stride for r, sd, stride in zip(restrictions, space_dimensions, strides)) expressions.append(gem.FlexiblyIndexed( varexp, ((offset, tuple((i, s) for i, s in zip(indices, shape))),) )) else: shape = space_dimensions expressions = [gem.FlexiblyIndexed(varexp, ((0, tuple(zip(indices, space_dimensions))),))] zero = coffee.FlatBlock( str.format("memset({name}, 0, {size} * sizeof(*{name}));\n", name=funarg.sym.gencode(), size=numpy.product(shape, dtype=int)) ) return funarg, [zero], expressions
def prepare_coordinates(coefficient, name, interior_facet=False): """Bridges the kernel interface and the GEM abstraction for coordinates. :arg coefficient: UFL Coefficient :arg name: unique name to refer to the Coefficient in the kernel :arg interior_facet: interior facet integral? :returns: (funarg, expression) funarg - :class:`coffee.Decl` function argument expression - GEM expression referring to the Coefficient values """ if not interior_facet: funargs = [coffee.Decl(SCALAR_TYPE, coffee.Symbol(name), pointers=[("",)], qualifiers=["const"])] else: funargs = [coffee.Decl(SCALAR_TYPE, coffee.Symbol(name+"_0"), pointers=[("",)], qualifiers=["const"]), coffee.Decl(SCALAR_TYPE, coffee.Symbol(name+"_1"), pointers=[("",)], qualifiers=["const"])] fiat_element = create_element(coefficient.ufl_element()) shape = (fiat_element.space_dimension(),) gdim = coefficient.ufl_element().cell().geometric_dimension() assert len(shape) == 1 and shape[0] % gdim == 0 num_nodes = shape[0] // gdim # Translate coords from XYZXYZXYZXYZ into XXXXYYYYZZZZ # NOTE: See dolfin/mesh/Cell.h:get_coordinate_dofs for ordering scheme indices = list(map(int, numpy.arange(num_nodes * gdim).reshape(num_nodes, gdim).transpose().flat)) if not interior_facet: variable = gem.Variable(name, shape) expression = gem.ListTensor([gem.Indexed(variable, (i,)) for i in indices]) else: variable0 = gem.Variable(name+"_0", shape) variable1 = gem.Variable(name+"_1", shape) expression = gem.ListTensor([[gem.Indexed(variable0, (i,)) for i in indices], [gem.Indexed(variable1, (i,)) for i in indices]]) return funargs, expression
def prepare_coefficients(coefficients, coefficient_numbers, name, interior_facet=False): """Bridges the kernel interface and the GEM abstraction for Coefficients. Mixed element Coefficients are rearranged here for interior facet integrals. :arg coefficient: iterable of UFL Coefficients :arg coefficient_numbers: iterable of coefficient indices in the original form :arg name: unique name to refer to the Coefficient in the kernel :arg interior_facet: interior facet integral? :returns: (funarg, expressions) funarg - :class:`coffee.Decl` function argument expressions- GEM expressions referring to the Coefficient values """ assert len(coefficients) == len(coefficient_numbers) funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name), pointers=[("const",), ()], qualifiers=["const"]) varexp = gem.Variable(name, (None, None)) expressions = [] for coefficient_number, coefficient in zip(coefficient_numbers, coefficients): if coefficient.ufl_element().family() == 'Real': shape = coefficient.ufl_shape else: fiat_element = create_element(coefficient.ufl_element()) shape = (fiat_element.space_dimension(),) if interior_facet: shape = (2,) + shape alpha = tuple(gem.Index() for s in shape) expressions.append(gem.ComponentTensor( gem.FlexiblyIndexed( varexp, ((coefficient_number, ()), (0, tuple(zip(alpha, shape)))) ), alpha )) return funarg, expressions
def tabulate(ufl_element, order, points, entity, epsilon): """Ask FIAT to tabulate ``points`` up to order ``order``, then rearranges the result into a series of ``(c, D, table)`` tuples, where: c: component index (for vector-valued and tensor-valued elements) D: derivative tuple (e.g. (1, 2) means d/dx d^2/dy^2) table: tabulation matrix for the given component and derivative. shape: len(points) x space_dimension :arg ufl_element: element to tabulate :arg order: FIAT gives all derivatives up to this order :arg points: points to tabulate the element on :arg entity: particular entity to tabulate on """ element = create_element(ufl_element) phi = element.space_dimension() C = ufl_element.reference_value_size() q = len(points) for D, fiat_table in iteritems(element.tabulate(order, points, entity)): if isinstance(fiat_table, Exception): # In the case an exception is found in the fiat table, do not # perform any rounding gem_fail = gem.Failure((q, phi), fiat_table) for c in range(C): yield c, D, gem_fail else: reordered_table = fiat_table.reshape(phi, C, q).transpose(1, 2, 0) # (C, q, phi) for c, table in enumerate(reordered_table): # Copied from FFC (ffc/quadrature/quadratureutils.py) table[abs(table) < epsilon] = 0 table[abs(table - 1.0) < epsilon] = 1.0 table[abs(table + 1.0) < epsilon] = -1.0 table[abs(table - 0.5) < epsilon] = 0.5 table[abs(table + 0.5) < epsilon] = -0.5 if spanning_degree(ufl_element) <= sum(D) and ufl_element.family() != "HDiv Trace": assert compat.allclose(table, table.mean(axis=0, keepdims=True), equal_nan=True) table = table[0] yield c, D, gem.Literal(table)
def compile_element(ufl_element, cdim): """Generates C code for point evaluations. :arg ufl_element: UFL element of the function space :arg cdim: ``cdim`` of the function space :returns: C code as string """ from tsfc.constants import PRECISION from firedrake.pointquery_utils import set_float_formatting, format from tsfc.fiatinterface import create_element from FIAT.reference_element import TensorProductCell as two_product_cell import sympy as sp import numpy as np # Set code generation parameters set_float_formatting(PRECISION) def calculate_basisvalues(ufl_cell, fiat_element): f_component = format["component"] f_decl = format["declaration"] f_float_decl = format["float declaration"] f_tensor = format["tabulate tensor"] f_new_line = format["new line"] tdim = ufl_cell.topological_dimension() gdim = ufl_cell.geometric_dimension() code = [] # Symbolic tabulation tabs = fiat_element.tabulate(0, np.array([[sp.Symbol("reference_coords.X[%d]" % i) for i in xrange(tdim)]])) tabs = tabs[(0,) * tdim] tabs = tabs.reshape(tabs.shape[:-1]) # Generate code for intermediate values s_code, (theta,) = ssa_arrays([tabs]) for name, value in s_code: code += [f_decl(f_float_decl, name, c_print(value))] # Prepare Jacobian, Jacobian inverse and determinant s_detJ = sp.Symbol('detJ') s_J = np.array([[sp.Symbol("J[{i}*{tdim} + {j}]".format(i=i, j=j, tdim=tdim)) for j in range(tdim)] for i in range(gdim)]) s_Jinv = np.array([[sp.Symbol("K[{i}*{gdim} + {j}]".format(i=i, j=j, gdim=gdim)) for j in range(gdim)] for i in range(tdim)]) # Apply transformations phi = [] for i, val in enumerate(theta): mapping = fiat_element.mapping()[i] if mapping == "affine": phi.append(val) elif mapping == "contravariant piola": phi.append(s_J.dot(val) / s_detJ) elif mapping == "covariant piola": phi.append(s_Jinv.transpose().dot(val)) else: raise ValueError("Unknown mapping: %s" % mapping) phi = np.asarray(phi, dtype=object) # Dump tables of basis values code += ["", "\t// Values of basis functions"] code += [f_decl("double", f_component("phi", phi.shape), f_new_line + f_tensor(phi))] shape = phi.shape if len(shape) <= 1: vdim = 1 elif len(shape) == 2: vdim = shape[1] return "\n".join(code), vdim # Create FIAT element element = create_element(ufl_element, vector_is_mixed=False) cell = ufl_element.cell() calculate_basisvalues, vdim = calculate_basisvalues(cell, element) extruded = isinstance(element.get_reference_element(), two_product_cell) code = { "cdim": cdim, "vdim": vdim, "geometric_dimension": cell.geometric_dimension(), "ndofs": element.space_dimension(), "calculate_basisvalues": calculate_basisvalues, "extruded_arg": ", int nlayers" if extruded else "", "nlayers": ", f->n_layers" if extruded else "", } evaluate_template_c = """static inline void evaluate_kernel(double *result, double *phi_, double **F) { const int ndofs = %(ndofs)d; const int cdim = %(cdim)d; const int vdim = %(vdim)d; double (*phi)[vdim] = (double (*)[vdim]) phi_; // F: ndofs x cdim // phi: ndofs x vdim // result = F' * phi: cdim x vdim // // Usually cdim == 1 or vdim == 1. for (int q = 0; q < cdim * vdim; q++) { result[q] = 0.0; } for (int i = 0; i < ndofs; i++) { for (int c = 0; c < cdim; c++) { for (int v = 0; v < vdim; v++) { result[c*vdim + v] += F[i][c] * phi[i][v]; } } } } static inline void wrap_evaluate(double *result, double *phi, double *data, int *map%(extruded_arg)s, int cell); int evaluate(struct Function *f, double *x, double *result) { struct ReferenceCoords reference_coords; int cell = locate_cell(f, x, %(geometric_dimension)d, &to_reference_coords, &reference_coords); if (cell == -1) { return -1; } if (!result) { return 0; } double *J = reference_coords.J; double *K = reference_coords.K; double detJ = reference_coords.detJ; %(calculate_basisvalues)s wrap_evaluate(result, (double *)phi, f->f, f->f_map%(nlayers)s, cell); return 0; } """ return evaluate_template_c % code
def convert_vectorelement(element): scalar_element = create_element(element.sub_elements()[0]) return finat.TensorFiniteElement(scalar_element, (element.num_sub_elements(), ))
def compile_c_kernel(expression, to_pts, to_element, fs, coords): """Produce a :class:`PyOP2.Kernel` from the c expression provided.""" coords_space = coords.function_space() coords_element = create_element(coords_space.ufl_element(), vector_is_mixed=False) names = {v[0] for v in expression._user_args} X = list(coords_element.tabulate(0, to_pts).values())[0] # Produce C array notation of X. X_str = "{{" + "},\n{".join([",".join(map(str, x)) for x in X.T]) + "}}" A = utils.unique_name("A", names) X = utils.unique_name("X", names) x_ = utils.unique_name("x_", names) k = utils.unique_name("k", names) d = utils.unique_name("d", names) i_ = utils.unique_name("i", names) # x is a reserved name. x = "x" if "x" in names: raise ValueError( "cannot use 'x' as a user-defined Expression variable") ass_exp = [ ast.Assign(ast.Symbol(A, (k, ), ((len(expression.code), i), )), ast.FlatBlock("%s" % code)) for i, code in enumerate(expression.code) ] dim = coords_space.value_size ndof = to_element.space_dimension() xndof = coords_element.space_dimension() nfdof = to_element.space_dimension() * numpy.prod(fs.value_size, dtype=int) init_X = ast.Decl(typ="double", sym=ast.Symbol(X, rank=(ndof, xndof)), qualifiers=["const"], init=X_str) init_x = ast.Decl(typ="double", sym=ast.Symbol(x, rank=(coords_space.value_size, ))) init_pi = ast.Decl(typ="double", sym="pi", qualifiers=["const"], init="3.141592653589793") init = ast.Block([init_X, init_x, init_pi]) incr_x = ast.Incr( ast.Symbol(x, rank=(d, )), ast.Prod(ast.Symbol(X, rank=(k, i_)), ast.Symbol(x_, rank=(ast.Sum(ast.Prod(i_, dim), d), )))) assign_x = ast.Assign(ast.Symbol(x, rank=(d, )), 0) loop_x = ast.For(init=ast.Decl("unsigned int", i_, 0), cond=ast.Less(i_, xndof), incr=ast.Incr(i_, 1), body=[incr_x]) block = ast.For(init=ast.Decl("unsigned int", d, 0), cond=ast.Less(d, dim), incr=ast.Incr(d, 1), body=[assign_x, loop_x]) loop = ast.c_for(k, ndof, ast.Block([block] + ass_exp, open_scope=True)) user_args = [] user_init = [] for _, arg in expression._user_args: if arg.shape == (1, ): user_args.append(ast.Decl("double *", "%s_" % arg.name)) user_init.append( ast.FlatBlock("const double %s = *%s_;" % (arg.name, arg.name))) else: user_args.append(ast.Decl("double *", arg.name)) kernel_code = ast.FunDecl( "void", "expression_kernel", [ ast.Decl("double", ast.Symbol(A, (nfdof, ))), ast.Decl("double*", x_) ] + user_args, ast.Block(user_init + [init, loop], open_scope=False)) coefficients = [coords] for _, arg in expression._user_args: coefficients.append(GlobalWrapper(arg)) return op2.Kernel(kernel_code, kernel_code.name), False, tuple(coefficients)
def _interpolator(V, tensor, expr, subset, arguments, access): try: to_element = create_element(V.ufl_element(), vector_is_mixed=False) except KeyError: # FInAT only elements raise NotImplementedError("Don't know how to create FIAT element for %s" % V.ufl_element()) if access is op2.READ: raise ValueError("Can't have READ access for output function") if len(expr.ufl_shape) != len(V.ufl_element().value_shape()): raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d' % (len(expr.ufl_shape), len(V.ufl_element().value_shape()))) if expr.ufl_shape != V.ufl_element().value_shape(): raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r' % (expr.ufl_shape, V.ufl_element().value_shape())) mesh = V.ufl_domain() coords = mesh.coordinates if not isinstance(expr, firedrake.Expression): if expr.ufl_domain() and expr.ufl_domain() != V.mesh(): raise NotImplementedError("Interpolation onto another mesh not supported.") ast, oriented, needs_cell_sizes, coefficients, _ = compile_expression_dual_evaluation(expr, to_element, coords, coffee=False) kernel = op2.Kernel(ast, ast.name) elif hasattr(expr, "eval"): to_pts = [] for dual in to_element.dual_basis(): if not isinstance(dual, FIAT.functional.PointEvaluation): raise NotImplementedError("Can only interpolate Python kernels with Lagrange elements") pts, = dual.pt_dict.keys() to_pts.append(pts) kernel, oriented, needs_cell_sizes, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords) else: raise RuntimeError("Attempting to evaluate an Expression which has no value.") cell_set = coords.cell_set if subset is not None: assert subset.superset == cell_set cell_set = subset parloop_args = [kernel, cell_set] if tensor in set((c.dat for c in coefficients)): output = tensor tensor = op2.Dat(tensor.dataset) if access is not op2.WRITE: copyin = (partial(output.copy, tensor), ) else: copyin = () copyout = (partial(tensor.copy, output), ) else: copyin = () copyout = () if isinstance(tensor, op2.Global): parloop_args.append(tensor(access)) elif isinstance(tensor, op2.Dat): parloop_args.append(tensor(access, V.cell_node_map())) else: assert access == op2.WRITE # Other access descriptors not done for Matrices. parloop_args.append(tensor(op2.WRITE, (V.cell_node_map(), arguments[0].function_space().cell_node_map()))) if oriented: co = mesh.cell_orientations() parloop_args.append(co.dat(op2.READ, co.cell_node_map())) if needs_cell_sizes: cs = mesh.cell_sizes parloop_args.append(cs.dat(op2.READ, cs.cell_node_map())) for coefficient in coefficients: m_ = coefficient.cell_node_map() parloop_args.append(coefficient.dat(op2.READ, m_)) for o in coefficients: domain = o.ufl_domain() if domain is not None and domain.topology != mesh.topology: raise NotImplementedError("Interpolation onto another mesh not supported.") parloop = op2.ParLoop(*parloop_args).compute if isinstance(tensor, op2.Mat): return parloop, tensor.assemble else: return copyin + (parloop, ) + copyout
def fiat_compat(element): from tsfc.fiatinterface import create_element from finat.fiat_elements import FiatElement assert element.cell().is_simplex() return FiatElement(create_element(element))
def compile_coordinate_element(ufl_coordinate_element): """Generates C code for changing to reference coordinates. :arg ufl_coordinate_element: UFL element of the coordinates :returns: C code as string """ from tsfc.constants import PRECISION from tsfc.fiatinterface import create_element from firedrake.pointeval_utils import ssa_arrays, c_print from FIAT.reference_element import TensorProductCell as two_product_cell import sympy as sp import numpy as np # Set code generation parameters set_float_formatting(PRECISION) def dX_norm_square(topological_dimension): return " + ".join("dX[{0}]*dX[{0}]".format(i) for i in xrange(topological_dimension)) def X_isub_dX(topological_dimension): return "\n".join("\tX[{0}] -= dX[{0}];".format(i) for i in xrange(topological_dimension)) def is_affine(ufl_element): return ufl_element.cell().is_simplex() and ufl_element.degree() <= 1 and ufl_element.family() in ["Discontinuous Lagrange", "Lagrange"] def inside_check(ufl_cell, fiat_cell): dim = ufl_cell.topological_dimension() point = tuple(sp.Symbol("X[%d]" % i) for i in xrange(dim)) return " && ".join("(%s)" % arg for arg in fiat_cell.contains_point(point, epsilon=1e-14).args) def init_X(fiat_element): f_float = format["floating point"] f_assign = format["assign"] fiat_cell = fiat_element.get_reference_element() vertices = np.array(fiat_cell.get_vertices()) X = np.average(vertices, axis=0) return "\n".join(f_assign("X[%d]" % i, f_float(v)) for i, v in enumerate(X)) def to_reference_coordinates(ufl_cell, fiat_element): f_decl = format["declaration"] f_float_decl = format["float declaration"] # Get the element cell name and geometric dimension. cell = ufl_cell gdim = cell.geometric_dimension() tdim = cell.topological_dimension() code = [] # Symbolic tabulation tabs = fiat_element.tabulate(1, np.array([[sp.Symbol("X[%d]" % i) for i in xrange(tdim)]])) tabs = sorted((d, value.reshape(value.shape[:-1])) for d, value in tabs.iteritems()) # Generate code for intermediate values s_code, d_phis = ssa_arrays(map(lambda (k, v): v, tabs), prefix="t") phi = d_phis.pop(0) for name, value in s_code: code += [f_decl(f_float_decl, name, c_print(value))] # Cell coordinate data C = np.array([[sp.Symbol("C[%d][%d]" % (i, j)) for j in range(gdim)] for i in range(fiat_element.space_dimension())]) # Generate physical coordinates x = phi.dot(C) for i, e in enumerate(x): code += ["\tx[%d] = %s;" % (i, e)] # Generate Jacobian grad_phi = np.vstack(reversed(d_phis)) J = np.transpose(grad_phi.dot(C)) for i, row in enumerate(J): for j, e in enumerate(row): code += ["\tJ[%d * %d + %d] = %s;" % (i, tdim, j, e)] # Get code snippets for Jacobian, inverse of Jacobian and mapping of # coordinates from physical element to the FIAT reference element. code += ["compute_jacobian_inverse_%s(K, detJ, J);" % cellname[cell]] # FIXME: use cell orientations! # if needs_orientation: # code_ += [format["orientation"]["ufc"](tdim, gdim)] x = np.array([sp.Symbol("x[%d]" % i) for i in xrange(gdim)]) x0 = np.array([sp.Symbol("x0[%d]" % i) for i in xrange(gdim)]) K = np.array([[sp.Symbol("K[%d]" % (i*gdim + j)) for j in range(gdim)] for i in range(tdim)]) dX = K.dot(x - x0) for i, e in enumerate(dX): code += ["\tdX[%d] = %s;" % (i, e)] return "\n".join(code) # Create FIAT element element = create_element(ufl_coordinate_element, vector_is_mixed=False) cell = ufl_coordinate_element.cell() # calculate_basisvalues, vdim = calculate_basisvalues(cell, element) extruded = isinstance(element.get_reference_element(), two_product_cell) code = { "geometric_dimension": cell.geometric_dimension(), "topological_dimension": cell.topological_dimension(), "inside_predicate": inside_check(cell, element.get_reference_element()), "to_reference_coords": to_reference_coordinates(cell, element), "init_X": init_X(element), "max_iteration_count": 1 if is_affine(ufl_coordinate_element) else 16, "convergence_epsilon": 1e-12, "dX_norm_square": dX_norm_square(cell.topological_dimension()), "X_isub_dX": X_isub_dX(cell.topological_dimension()), "extruded_arg": ", int nlayers" if extruded else "", "nlayers": ", f->n_layers" if extruded else "", } evaluate_template_c = """#include <math.h> #include <firedrake_geometry.h> struct ReferenceCoords { double X[%(geometric_dimension)d]; double J[%(geometric_dimension)d * %(topological_dimension)d]; double K[%(topological_dimension)d * %(geometric_dimension)d]; double detJ; }; static inline void to_reference_coords_kernel(void *result_, double *x0, int *return_value, double **C) { struct ReferenceCoords *result = (struct ReferenceCoords *) result_; const int space_dim = %(geometric_dimension)d; /* * Mapping coordinates from physical to reference space */ double *X = result->X; %(init_X)s double x[space_dim]; double *J = result->J; double *K = result->K; double detJ; double dX[%(topological_dimension)d]; int converged = 0; for (int it = 0; !converged && it < %(max_iteration_count)d; it++) { %(to_reference_coords)s if (%(dX_norm_square)s < %(convergence_epsilon)g * %(convergence_epsilon)g) { converged = 1; } %(X_isub_dX)s } result->detJ = detJ; // Are we inside the reference element? *return_value = %(inside_predicate)s; } static inline void wrap_to_reference_coords(void *result_, double *x, int *return_value, double *coords, int *coords_map%(extruded_arg)s, int cell); int to_reference_coords(void *result_, struct Function *f, int cell, double *x) { int return_value; wrap_to_reference_coords(result_, x, &return_value, f->coords, f->coords_map%(nlayers)s, cell); return return_value; } """ return evaluate_template_c % code
def _interpolator(V, dat, expr, subset, access): to_element = create_element(V.ufl_element(), vector_is_mixed=False) to_pts = [] if access is op2.READ: raise ValueError("Can't have READ access for output function") if V.ufl_element().mapping() != "identity": raise NotImplementedError("Can only interpolate onto elements " "with affine mapping. Try projecting instead") for dual in to_element.dual_basis(): if not isinstance(dual, FIAT.functional.PointEvaluation): raise NotImplementedError("Can only interpolate onto point " "evaluation operators. Try projecting instead") pts, = dual.pt_dict.keys() to_pts.append(pts) if len(expr.ufl_shape) != len(V.ufl_element().value_shape()): raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d' % (len(expr.ufl_shape), len(V.ufl_element().value_shape()))) if expr.ufl_shape != V.ufl_element().value_shape(): raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r' % (expr.ufl_shape, V.ufl_element().value_shape())) mesh = V.ufl_domain() coords = mesh.coordinates if not isinstance(expr, firedrake.Expression): if expr.ufl_domain() and expr.ufl_domain() != V.mesh(): raise NotImplementedError("Interpolation onto another mesh not supported.") if expr.ufl_shape != V.shape: raise ValueError("UFL expression has incorrect shape for interpolation.") ast, oriented, needs_cell_sizes, coefficients, _ = compile_ufl_kernel(expr, to_pts, coords, coffee=False) kernel = op2.Kernel(ast, ast.name) elif hasattr(expr, "eval"): kernel, oriented, needs_cell_sizes, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords) else: raise RuntimeError("Attempting to evaluate an Expression which has no value.") cell_set = coords.cell_set if subset is not None: assert subset.superset == cell_set cell_set = subset args = [kernel, cell_set] if dat in set((c.dat for c in coefficients)): output = dat dat = op2.Dat(dat.dataset) if access is not op2.WRITE: copyin = (partial(output.copy, dat), ) else: copyin = () copyout = (partial(dat.copy, output), ) else: copyin = () copyout = () args.append(dat(access, V.cell_node_map())) if oriented: co = mesh.cell_orientations() args.append(co.dat(op2.READ, co.cell_node_map())) if needs_cell_sizes: cs = mesh.cell_sizes args.append(cs.dat(op2.READ, cs.cell_node_map())) for coefficient in coefficients: m_ = coefficient.cell_node_map() args.append(coefficient.dat(op2.READ, m_)) for o in coefficients: domain = o.ufl_domain() if domain is not None and domain.topology != mesh.topology: raise NotImplementedError("Interpolation onto another mesh not supported.") return copyin + (op2.ParLoop(*args).compute, ) + copyout
def test_triangle_basic(ufl_element): element = create_element(ufl_element) assert isinstance(element, supported_elements[ufl_element.family()])
def test_interval_variant(family, variant, expected_cls): ufl_element = ufl.FiniteElement(family, ufl.interval, 3, variant=variant) assert isinstance(create_element(ufl_element), expected_cls)
def compile_element(ufl_element, cdim): """Generates C code for point evaluations. :arg ufl_element: UFL element of the function space :arg cdim: ``cdim`` of the function space :returns: C code as string """ from tsfc import default_parameters from firedrake.pointquery_utils import set_float_formatting, format from tsfc.fiatinterface import create_element from FIAT.reference_element import TensorProductCell as two_product_cell import sympy as sp import numpy as np # Set code generation parameters set_float_formatting(default_parameters()["precision"]) def calculate_basisvalues(ufl_cell, fiat_element): f_component = format["component"] f_decl = format["declaration"] f_float_decl = format["float declaration"] f_tensor = format["tabulate tensor"] f_new_line = format["new line"] tdim = ufl_cell.topological_dimension() gdim = ufl_cell.geometric_dimension() code = [] # Symbolic tabulation tabs = fiat_element.tabulate( 0, np.array([[ sp.Symbol("reference_coords.X[%d]" % i) for i in range(tdim) ]])) tabs = tabs[(0, ) * tdim] tabs = tabs.reshape(tabs.shape[:-1]) # Generate code for intermediate values s_code, (theta, ) = ssa_arrays([tabs]) for name, value in s_code: code += [f_decl(f_float_decl, name, c_print(value))] # Prepare Jacobian, Jacobian inverse and determinant s_detJ = sp.Symbol('detJ') s_J = np.array([[ sp.Symbol("J[{i}*{tdim} + {j}]".format(i=i, j=j, tdim=tdim)) for j in range(tdim) ] for i in range(gdim)]) s_Jinv = np.array([[ sp.Symbol("K[{i}*{gdim} + {j}]".format(i=i, j=j, gdim=gdim)) for j in range(gdim) ] for i in range(tdim)]) # Apply transformations phi = [] for i, val in enumerate(theta): mapping = fiat_element.mapping()[i] if mapping == "affine": phi.append(val) elif mapping == "contravariant piola": phi.append(s_J.dot(val) / s_detJ) elif mapping == "covariant piola": phi.append(s_Jinv.transpose().dot(val)) else: raise ValueError("Unknown mapping: %s" % mapping) phi = np.asarray(phi, dtype=object) # Dump tables of basis values code += ["", "\t// Values of basis functions"] code += [ f_decl("double", f_component("phi", phi.shape), f_new_line + f_tensor(phi)) ] shape = phi.shape if len(shape) <= 1: vdim = 1 elif len(shape) == 2: vdim = shape[1] return "\n".join(code), vdim # Create FIAT element element = create_element(ufl_element, vector_is_mixed=False) cell = ufl_element.cell() calculate_basisvalues, vdim = calculate_basisvalues(cell, element) extruded = isinstance(element.get_reference_element(), two_product_cell) code = { "cdim": cdim, "vdim": vdim, "geometric_dimension": cell.geometric_dimension(), "ndofs": element.space_dimension(), "calculate_basisvalues": calculate_basisvalues, "extruded_arg": ", %s nlayers" % as_cstr(IntType) if extruded else "", "nlayers": ", f->n_layers" if extruded else "", "IntType": as_cstr(IntType), } evaluate_template_c = """static inline void evaluate_kernel(double *result, double *phi_, double **F) { const int ndofs = %(ndofs)d; const int cdim = %(cdim)d; const int vdim = %(vdim)d; double (*phi)[vdim] = (double (*)[vdim]) phi_; // F: ndofs x cdim // phi: ndofs x vdim // result = F' * phi: cdim x vdim // // Usually cdim == 1 or vdim == 1. for (int q = 0; q < cdim * vdim; q++) { result[q] = 0.0; } for (int i = 0; i < ndofs; i++) { for (int c = 0; c < cdim; c++) { for (int v = 0; v < vdim; v++) { result[c*vdim + v] += F[i][c] * phi[i][v]; } } } } static inline void wrap_evaluate(double *result, double *phi, double *data, %(IntType)s *map%(extruded_arg)s, %(IntType)s cell); int evaluate(struct Function *f, double *x, double *result) { struct ReferenceCoords reference_coords; int cell = locate_cell(f, x, %(geometric_dimension)d, &to_reference_coords, &reference_coords); if (cell == -1) { return -1; } if (!result) { return 0; } double *J = reference_coords.J; double *K = reference_coords.K; double detJ = reference_coords.detJ; %(calculate_basisvalues)s wrap_evaluate(result, (double *)phi, f->f, f->f_map%(nlayers)s, cell); return 0; } """ return evaluate_template_c % code
def test_triangle_basic(ufl_element): element = f.create_element(ufl_element) assert isinstance(element, f.supported_elements[ufl_element.family()])
def test_quadrilateral_variant_spectral_rtcf(): element = create_element(ufl.FiniteElement('RTCF', ufl.quadrilateral, 2, variant='spectral')) assert isinstance(element.element._elements[0].A, FIAT.GaussLobattoLegendre) assert isinstance(element.element._elements[0].B, FIAT.GaussLegendre) assert isinstance(element.element._elements[1].A, FIAT.GaussLegendre) assert isinstance(element.element._elements[1].B, FIAT.GaussLobattoLegendre)
def test_quadrilateral_variant_spectral_dq(): element = create_element(ufl.FiniteElement('DQ', ufl.quadrilateral, 1, variant='spectral')) assert isinstance(element.element.A, FIAT.GaussLegendre) assert isinstance(element.element.B, FIAT.GaussLegendre)
def test_triangle_variant_spectral_fail(): ufl_element = ufl.FiniteElement('DP', ufl.triangle, 2, variant='spectral') with pytest.raises(ValueError): create_element(ufl_element)
def test_cache_hit(ufl_element): A = create_element(ufl_element) B = create_element(ufl_element) assert A is B
def _interpolator(V, dat, expr, subset, access): to_element = create_element(V.ufl_element(), vector_is_mixed=False) to_pts = [] if access is op2.READ: raise ValueError("Can't have READ access for output function") if V.ufl_element().mapping() != "identity": raise NotImplementedError("Can only interpolate onto elements " "with affine mapping. Try projecting instead") for dual in to_element.dual_basis(): if not isinstance(dual, FIAT.functional.PointEvaluation): raise NotImplementedError("Can only interpolate onto point " "evaluation operators. Try projecting instead") pts, = dual.pt_dict.keys() to_pts.append(pts) if len(expr.ufl_shape) != len(V.ufl_element().value_shape()): raise RuntimeError('Rank mismatch: Expression rank %d, FunctionSpace rank %d' % (len(expr.ufl_shape), len(V.ufl_element().value_shape()))) if expr.ufl_shape != V.ufl_element().value_shape(): raise RuntimeError('Shape mismatch: Expression shape %r, FunctionSpace shape %r' % (expr.ufl_shape, V.ufl_element().value_shape())) mesh = V.ufl_domain() coords = mesh.coordinates if not isinstance(expr, firedrake.Expression): if expr.ufl_domain() and expr.ufl_domain() != V.mesh(): raise NotImplementedError("Interpolation onto another mesh not supported.") if expr.ufl_shape != V.shape: raise ValueError("UFL expression has incorrect shape for interpolation.") ast, oriented, needs_cell_sizes, coefficients, _ = compile_ufl_kernel(expr, to_pts, coords, coffee=False) kernel = op2.Kernel(ast, ast.name) elif hasattr(expr, "eval"): kernel, oriented, needs_cell_sizes, coefficients = compile_python_kernel(expr, to_pts, to_element, V, coords) else: raise RuntimeError("Attempting to evaluate an Expression which has no value.") cell_set = coords.cell_set if subset is not None: assert subset.superset == cell_set cell_set = subset args = [kernel, cell_set] if dat in set((c.dat for c in coefficients)): output = dat dat = op2.Dat(dat.dataset) if access is not op2.WRITE: copyin = (partial(output.copy, dat), ) else: copyin = () copyout = (partial(dat.copy, output), ) else: copyin = () copyout = () args.append(dat(access, V.cell_node_map())) if oriented: co = mesh.cell_orientations() args.append(co.dat(op2.READ, co.cell_node_map())) if needs_cell_sizes: cs = mesh.cell_sizes args.append(cs.dat(op2.READ, cs.cell_node_map())) for coefficient in coefficients: m_ = coefficient.cell_node_map() args.append(coefficient.dat(op2.READ, m_)) for o in coefficients: domain = o.ufl_domain() if domain is not None and domain.topology != mesh.topology: raise NotImplementedError("Interpolation onto another mesh not supported.") return copyin + (partial(op2.par_loop, *args), ) + copyout
def prepare_arguments(arguments, indices, interior_facet=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 indices: Argument indices :arg interior_facet: interior facet integral? :returns: (funarg, prepare, expression, finalise) funarg - :class:`coffee.Decl` function argument prepare - list of COFFEE nodes to be prepended to the kernel body expressions - GEM expressions referring to the argument tensor finalise - list of COFFEE nodes to be appended to the kernel body """ assert isinstance(interior_facet, bool) if len(arguments) == 0: # No arguments funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=(1,))) expression = gem.Indexed(gem.Variable("A", (1,)), (0,)) return funarg, [], [expression], [] elements = tuple(create_element(arg.ufl_element()) for arg in arguments) if not interior_facet: # Not an interior facet integral shape = tuple(element.space_dimension() for element in elements) funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=shape)) expression = gem.Indexed(gem.Variable("A", shape), indices) return funarg, [], [expression], [] if not any(isinstance(element, MixedElement) for element in elements): # Interior facet integral, but no vector (mixed) arguments shape = tuple(2 * element.space_dimension() for element in elements) funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=shape)) varexp = gem.Variable("A", shape) expressions = [] for restrictions in product((0, 1), repeat=len(arguments)): expressions.append(gem.FlexiblyIndexed( varexp, tuple((r * e.space_dimension(), ((i, e.space_dimension()),)) for e, i, r in zip(elements, indices, restrictions)) )) return funarg, [], expressions, [] # Interior facet integral + vector (mixed) argument(s) shape = tuple(element.space_dimension() for element in elements) funarg_shape = tuple(s * 2 for s in shape) funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol("A", rank=funarg_shape)) prepare = [] expressions = [] references = [] for restrictions in product((0, 1), repeat=len(arguments)): name = "A" + "".join(map(str, restrictions)) prepare.append(coffee.Decl(SCALAR_TYPE, coffee.Symbol(name, rank=shape), init=coffee.ArrayInit(numpy.zeros(1)))) expressions.append(gem.Indexed(gem.Variable(name, shape), indices)) for multiindex in numpy.ndindex(shape): references.append(coffee.Symbol(name, multiindex)) restriction_shape = [] for e in elements: if isinstance(e, MixedElement): restriction_shape += [len(e.elements()), e.elements()[0].space_dimension()] else: restriction_shape += [1, e.space_dimension()] restriction_shape = tuple(restriction_shape) references = numpy.array(references) if len(arguments) == 1: references = references.reshape((2,) + restriction_shape) references = references.transpose(1, 0, 2) elif len(arguments) == 2: references = references.reshape((2, 2) + restriction_shape) references = references.transpose(2, 0, 3, 4, 1, 5) references = references.reshape(funarg_shape) finalise = [] for multiindex in numpy.ndindex(funarg_shape): finalise.append(coffee.Assign(coffee.Symbol("A", rank=multiindex), references[multiindex])) return funarg, prepare, expressions, finalise
def test_cache_hit(ufl_element): A = f.create_element(ufl_element) B = f.create_element(ufl_element) assert A is B
def test_quadrilateral_variant_spectral_dq(): element = create_element( ufl.FiniteElement('DQ', ufl.quadrilateral, 1, variant='spectral')) assert isinstance(element.element.A, FIAT.GaussLegendre) assert isinstance(element.element.B, FIAT.GaussLegendre)
def fiat_compat(element): from tsfc.fiatinterface import create_element return FiatElementWrapper(create_element(element), degree=spanning_degree(element))
def convert_tensorproductelement(element): cell = element.cell() if type(cell) is not ufl.TensorProductCell: raise ValueError("TensorProductElement not on TensorProductCell?") return finat.TensorProductElement( [create_element(elem) for elem in element.sub_elements()])
def prepare_coefficient(coefficient, name, mode=None, interior_facet=False): """Bridges the kernel interface and the GEM abstraction for Coefficients. Mixed element Coefficients are rearranged here for interior facet integrals. :arg coefficient: UFL Coefficient :arg name: unique name to refer to the Coefficient in the kernel :arg mode: 'manual_loop' or 'list_tensor'; two ways to deal with interior facet integrals on mixed elements :arg interior_facet: interior facet integral? :returns: (funarg, prepare, expression) funarg - :class:`coffee.Decl` function argument prepare - list of COFFEE nodes to be prepended to the kernel body expression - GEM expression referring to the Coefficient values """ if mode is None: mode = 'manual_loop' assert mode in ['manual_loop', 'list_tensor'] assert isinstance(interior_facet, bool) if coefficient.ufl_element().family() == 'Real': # Constant funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name), pointers=[("restrict",)], qualifiers=["const"]) expression = gem.reshape(gem.Variable(name, (None,)), coefficient.ufl_shape) return funarg, [], expression fiat_element = create_element(coefficient.ufl_element()) size = fiat_element.space_dimension() if not interior_facet: # Simple case funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name), pointers=[("const", "restrict"), ("restrict",)], qualifiers=["const"]) expression = gem.reshape(gem.Variable(name, (size, 1)), (size,), ()) return funarg, [], expression if not isinstance(fiat_element, MixedElement): # Interior facet integral funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name), pointers=[("const", "restrict"), ("restrict",)], qualifiers=["const"]) expression = gem.reshape( gem.Variable(name, (2 * size, 1)), (2, size), () ) return funarg, [], expression # Interior facet integral + mixed / vector element # Here we need to reorder the coefficient values. # # Incoming ordering: E1+ E1- E2+ E2- E3+ E3- # Required ordering: E1+ E2+ E3+ E1- E2- E3- # # Each of E[n]{+,-} is a vector of basis function coefficients for # subelement E[n]. # # There are two code generation method to reorder the values. # We have not done extensive research yet as to which way yield # faster code. if mode == 'manual_loop': # In this case we generate loops outside the GEM abstraction # to reorder the values. A whole E[n]{+,-} block is copied by # a single loop. name_ = name + "_" shape = (2, size) funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name_), pointers=[("const", "restrict"), ("restrict",)], qualifiers=["const"]) prepare = [coffee.Decl(SCALAR_TYPE, coffee.Symbol(name, rank=shape))] expression = gem.Variable(name, shape) offset = 0 i = coffee.Symbol("i") for element in fiat_element.elements(): space_dim = element.space_dimension() loop_body = coffee.Assign(coffee.Symbol(name, rank=(0, "i"), offset=((1, 0), (1, offset))), coffee.Symbol(name_, rank=("i", 0), offset=((1, 2 * offset), (1, 0)))) prepare.append(coffee_for(i, space_dim, loop_body)) loop_body = coffee.Assign(coffee.Symbol(name, rank=(1, "i"), offset=((1, 0), (1, offset))), coffee.Symbol(name_, rank=("i", 0), offset=((1, 2 * offset + space_dim), (1, 0)))) prepare.append(coffee_for(i, space_dim, loop_body)) offset += space_dim return funarg, prepare, expression elif mode == 'list_tensor': # In this case we generate a gem.ListTensor to do the # reordering. Every single element in a E[n]{+,-} block is # referenced separately. funarg = coffee.Decl(SCALAR_TYPE, coffee.Symbol(name), pointers=[("const", "restrict"), ("restrict",)], qualifiers=["const"]) variable = gem.Variable(name, (2 * size, 1)) facet_0 = [] facet_1 = [] offset = 0 for element in fiat_element.elements(): space_dim = element.space_dimension() for i in range(offset, offset + space_dim): facet_0.append(gem.Indexed(variable, (i, 0))) offset += space_dim for i in range(offset, offset + space_dim): facet_1.append(gem.Indexed(variable, (i, 0))) offset += space_dim expression = gem.ListTensor(numpy.array([facet_0, facet_1])) return funarg, [], expression
def convert_tensorelement(element): scalar_element = create_element(element.sub_elements()[0]) return finat.TensorFiniteElement(scalar_element, element.reference_value_shape())
def compile_coordinate_element(ufl_coordinate_element): """Generates C code for changing to reference coordinates. :arg ufl_coordinate_element: UFL element of the coordinates :returns: C code as string """ from tsfc import default_parameters from tsfc.fiatinterface import create_element from firedrake.pointeval_utils import ssa_arrays, c_print from FIAT.reference_element import TensorProductCell as two_product_cell import sympy as sp import numpy as np # Set code generation parameters set_float_formatting(default_parameters()["precision"]) def dX_norm_square(topological_dimension): return " + ".join("dX[{0}]*dX[{0}]".format(i) for i in range(topological_dimension)) def X_isub_dX(topological_dimension): return "\n".join("\tX[{0}] -= dX[{0}];".format(i) for i in range(topological_dimension)) def is_affine(ufl_element): return ufl_element.cell().is_simplex( ) and ufl_element.degree() <= 1 and ufl_element.family() in [ "Discontinuous Lagrange", "Lagrange" ] def inside_check(ufl_cell, fiat_cell): dim = ufl_cell.topological_dimension() point = tuple(sp.Symbol("X[%d]" % i) for i in range(dim)) return " && ".join( "(%s)" % arg for arg in fiat_cell.contains_point(point, epsilon=1e-14).args) def init_X(fiat_element): f_float = format["floating point"] f_assign = format["assign"] fiat_cell = fiat_element.get_reference_element() vertices = np.array(fiat_cell.get_vertices()) X = np.average(vertices, axis=0) return "\n".join( f_assign("X[%d]" % i, f_float(v)) for i, v in enumerate(X)) def to_reference_coordinates(ufl_cell, fiat_element): f_decl = format["declaration"] f_float_decl = format["float declaration"] # Get the element cell name and geometric dimension. cell = ufl_cell gdim = cell.geometric_dimension() tdim = cell.topological_dimension() code = [] # Symbolic tabulation tabs = fiat_element.tabulate( 1, np.array([[sp.Symbol("X[%d]" % i) for i in range(tdim)]])) tabs = sorted((d, value.reshape(value.shape[:-1])) for d, value in tabs.iteritems()) # Generate code for intermediate values s_code, d_phis = ssa_arrays([v for k, v in tabs], prefix="t") phi = d_phis.pop(0) for name, value in s_code: code += [f_decl(f_float_decl, name, c_print(value))] # Cell coordinate data C = np.array([[sp.Symbol("C[%d][%d]" % (i, j)) for j in range(gdim)] for i in range(fiat_element.space_dimension())]) # Generate physical coordinates x = phi.dot(C) for i, e in enumerate(x): code += ["\tx[%d] = %s;" % (i, e)] # Generate Jacobian grad_phi = np.vstack(reversed(d_phis)) J = np.transpose(grad_phi.dot(C)) for i, row in enumerate(J): for j, e in enumerate(row): code += ["\tJ[%d * %d + %d] = %s;" % (i, tdim, j, e)] # Get code snippets for Jacobian, inverse of Jacobian and mapping of # coordinates from physical element to the FIAT reference element. code += ["compute_jacobian_inverse_%s(K, detJ, J);" % cellname[cell]] # FIXME: use cell orientations! # if needs_orientation: # code_ += [format["orientation"]["ufc"](tdim, gdim)] x = np.array([sp.Symbol("x[%d]" % i) for i in range(gdim)]) x0 = np.array([sp.Symbol("x0[%d]" % i) for i in range(gdim)]) K = np.array( [[sp.Symbol("K[%d]" % (i * gdim + j)) for j in range(gdim)] for i in range(tdim)]) dX = K.dot(x - x0) for i, e in enumerate(dX): code += ["\tdX[%d] = %s;" % (i, e)] return "\n".join(code) # Create FIAT element element = create_element(ufl_coordinate_element, vector_is_mixed=False) cell = ufl_coordinate_element.cell() # calculate_basisvalues, vdim = calculate_basisvalues(cell, element) extruded = isinstance(element.get_reference_element(), two_product_cell) code = { "geometric_dimension": cell.geometric_dimension(), "topological_dimension": cell.topological_dimension(), "inside_predicate": inside_check(cell, element.get_reference_element()), "to_reference_coords": to_reference_coordinates(cell, element), "init_X": init_X(element), "max_iteration_count": 1 if is_affine(ufl_coordinate_element) else 16, "convergence_epsilon": 1e-12, "dX_norm_square": dX_norm_square(cell.topological_dimension()), "X_isub_dX": X_isub_dX(cell.topological_dimension()), "extruded_arg": ", int nlayers" if extruded else "", "nlayers": ", f->n_layers" if extruded else "", } evaluate_template_c = """#include <math.h> #include <firedrake_geometry.h> struct ReferenceCoords { double X[%(geometric_dimension)d]; double J[%(geometric_dimension)d * %(topological_dimension)d]; double K[%(topological_dimension)d * %(geometric_dimension)d]; double detJ; }; static inline void to_reference_coords_kernel(void *result_, double *x0, int *return_value, double **C) { struct ReferenceCoords *result = (struct ReferenceCoords *) result_; const int space_dim = %(geometric_dimension)d; /* * Mapping coordinates from physical to reference space */ double *X = result->X; %(init_X)s double x[space_dim]; double *J = result->J; double *K = result->K; double detJ; double dX[%(topological_dimension)d]; int converged = 0; for (int it = 0; !converged && it < %(max_iteration_count)d; it++) { %(to_reference_coords)s if (%(dX_norm_square)s < %(convergence_epsilon)g * %(convergence_epsilon)g) { converged = 1; } %(X_isub_dX)s } result->detJ = detJ; // Are we inside the reference element? *return_value = %(inside_predicate)s; } static inline void wrap_to_reference_coords(void *result_, double *x, int *return_value, double *coords, int *coords_map%(extruded_arg)s, int cell); int to_reference_coords(void *result_, struct Function *f, int cell, double *x) { int return_value; wrap_to_reference_coords(result_, x, &return_value, f->coords, f->coords_map%(nlayers)s, cell); return return_value; } """ return evaluate_template_c % code