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.Coefficient(ufl.FunctionSpace(domain, ufl_coordinate_element)) expr = ufl_utils.preprocess_expression(expr) expr = ufl_utils.simplify_abs(expr) builder = firedrake_interface.KernelBuilderBase() builder.domain_coordinate[domain] = C 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 __init__(self, mesh, element, name): self._ufl_function_space = ufl.FunctionSpace(mesh.ufl_mesh(), element) self.name = name self.comm = mesh.comm self._mesh = mesh self.dof_dset = op2.GlobalDataSet(self.make_dat()) self.node_set = self.dof_dset.set
def __init__(self, mesh): """ Create function that evaluates to the facet area/length on each facet. *Arguments* mesh a :py:class:`Mesh <dolfin.cpp.Mesh>`. *Example of usage* .. code-block:: python mesh = UnitSquare(4,4) fa = FacetArea(mesh) """ # Handle MultiMesh if isinstance(mesh, cpp.MultiMesh): mesh = mesh.part(0) # Initialize C++ part cpp.FacetArea.__init__(self, mesh) # Initialize UFL part # NB! This is defined as a piecewise constant function for each cell, not for each facet! ufl_element = ufl.FiniteElement("Discontinuous Lagrange", mesh.ufl_cell(), 0) ufl_function_space = ufl.FunctionSpace(mesh.ufl_domain(), ufl_element) ufl.Coefficient.__init__(self, ufl_function_space, count=self.id())
def __init__(self, cell=None, element=None, domain=None, name=None, label=None): # Some messy cell/domain handling for compatibility, will be # straightened out later if domain is None: ufl_domain = None else: if isinstance(domain, ufl.domain.AbstractDomain): ufl_domain = domain else: # Probably getting a Mesh here, from existing dolfin # tests. Will be the same later anyway. ufl_domain = domain.ufl_domain() if cell is None: cell = ufl_domain.ufl_cell() # Initialise base class ufl_function_space = ufl.FunctionSpace(ufl_domain, element) ufl.Coefficient.__init__(self, ufl_function_space, count=self.id()) name = name or "f_" + str(ufl.Coefficient.count(self)) label = label or "User defined expression" self._cpp_object.rename(name, label)
def test_custom_quadrature(compile_args): ve = ufl.VectorElement("P", "triangle", 1) mesh = ufl.Mesh(ve) e = ufl.FiniteElement("P", mesh.ufl_cell(), 2) V = ufl.FunctionSpace(mesh, e) u, v = ufl.TrialFunction(V), ufl.TestFunction(V) points = [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [0.5, 0.5], [0.0, 0.5], [0.5, 0.0]] weights = [1 / 12] * 6 a = u * v * ufl.dx(metadata={"quadrature_rule": "custom", "quadrature_points": points, "quadrature_weights": weights}) forms = [a] compiled_forms, module = ffcx.codegeneration.jit.compile_forms(forms, cffi_extra_compile_args=compile_args) ffi = cffi.FFI() form = compiled_forms[0][0] default_integral = form.create_cell_integral(-1) A = np.zeros((6, 6), dtype=np.float64) w = np.array([], dtype=np.float64) c = np.array([], dtype=np.float64) coords = np.array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0], dtype=np.float64) default_integral.tabulate_tensor( ffi.cast("double *", A.ctypes.data), ffi.cast("double *", w.ctypes.data), ffi.cast("double *", c.ctypes.data), ffi.cast("double *", coords.ctypes.data), ffi.NULL, ffi.NULL, 0) # Check that A is diagonal assert np.count_nonzero(A - np.diag(np.diagonal(A))) == 0
def __init__(self, value, cell=None, name=None): """ Create constant-valued function with given value. *Arguments* value The value may be either a single scalar value, or a tuple/list of values for vector-valued functions, or nested lists or a numpy array for tensor-valued functions. cell Optional argument. A :py:class:`Cell <ufl.Cell>` which defines the geometrical dimensions the Constant is defined for. name Optional argument. A str which overrules the default name of the Constant. The data type Constant represents a constant value that is unknown at compile-time. Its values can thus be changed without requiring re-generation and re-compilation of C++ code. *Examples of usage* .. code-block:: python p = Constant(pi/4) # scalar C = Constant((0.0, -1.0, 0.0)) # constant vector """ # TODO: Either take mesh instead of cell, or drop cell and let # grad(c) be undefined. if cell is not None: cell = ufl.as_cell(cell) ufl_domain = None array = numpy.array(value) rank = len(array.shape) floats = list(map(float, array.flat)) # Create UFL element and initialize constant if rank == 0: ufl_element = ufl.FiniteElement("Real", cell, 0) cpp.Constant.__init__(self, floats[0]) elif rank == 1: ufl_element = ufl.VectorElement("Real", cell, 0, dim=len(floats)) cpp.Constant.__init__(self, floats) else: ufl_element = ufl.TensorElement("Real", cell, 0, shape=array.shape) cpp.Constant.__init__(self, list(array.shape), floats) # Initialize base classes ufl_function_space = ufl.FunctionSpace(ufl_domain, ufl_element) ufl.Coefficient.__init__(self, ufl_function_space, count=self.id()) # Set name as given or automatic name = name or "f_%d" % self.count() self.rename(name, "a Constant")
def __init__(self, mesh, element, name=None, real_tensorproduct=False): super(FunctionSpace, self).__init__() if type(element) is ufl.MixedElement: raise ValueError("Can't create FunctionSpace for MixedElement") finat_element = create_element(element) if isinstance(finat_element, finat.TensorFiniteElement): # Retrieve scalar element finat_element = finat_element.base_element # Used for reconstruction of mixed/component spaces self.real_tensorproduct = real_tensorproduct sdata = get_shared_data(mesh, finat_element, real_tensorproduct=real_tensorproduct) # 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_function_space = ufl.FunctionSpace(mesh.ufl_mesh(), element) self._shared_data = sdata self._mesh = mesh self.rank = len(self.shape) r"""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.value_size = int(numpy.prod(self.shape, dtype=int)) r"""The total number of degrees of freedom at each function space node.""" self.name = name r"""The (optional) descriptive name for this space.""" self.node_set = sdata.node_set r"""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) r"""A :class:`pyop2.DataSet` representing the function space degrees of freedom.""" self.comm = self.node_set.comm self.finat_element = finat_element self.extruded = sdata.extruded self.offset = sdata.offset self.cell_boundary_masks = sdata.cell_boundary_masks self.interior_facet_boundary_masks = sdata.interior_facet_boundary_masks
def set_coordinates(self, domain): """Prepare the coordinate field. :arg domain: :class:`ufl.Domain` """ # Create a fake coordinate coefficient for a domain. f = ufl.Coefficient(ufl.FunctionSpace(domain, domain.ufl_coordinate_element())) self.domain_coordinate[domain] = f self.coordinates_arg = self._coefficient(f, "macro_coords")
def test_matvec(compile_args): """Test evaluation of linear rank-0 form. Evaluates expression c * A_ij * f_j where c is a Constant, A_ij is a user specified constant matrix and f_j is j-th component of user specified vector-valued finite element function (in P1 space). """ e = ufl.VectorElement("P", "triangle", 1) mesh = ufl.Mesh(e) V = ufl.FunctionSpace(mesh, e) f = ufl.Coefficient(V) a_mat = np.array([[1.0, 2.0], [1.0, 1.0]]) a = ufl.as_matrix(a_mat) expr = ufl.Constant(mesh) * ufl.dot(a, f) points = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) obj, module = ffcx.codegeneration.jit.compile_expressions( [(expr, points)], cffi_extra_compile_args=compile_args) ffi = cffi.FFI() kernel = obj[0][0] c_type, np_type = float_to_type("double") A = np.zeros((3, 2), dtype=np_type) f_mat = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) # Coefficient storage XYXYXY w = np.array(f_mat.T.flatten(), dtype=np_type) c = np.array([0.5], dtype=np_type) # Coords storage XYXYXY coords = np.array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0], dtype=np.float64) kernel.tabulate_expression( ffi.cast('{type} *'.format(type=c_type), A.ctypes.data), ffi.cast('{type} *'.format(type=c_type), w.ctypes.data), ffi.cast('{type} *'.format(type=c_type), c.ctypes.data), ffi.cast('double *', coords.ctypes.data)) # Check the computation against correct NumPy value assert np.allclose(A, 0.5 * np.dot(a_mat, f_mat).T) # Prepare NumPy array of points attached to the expression length = kernel.num_points * kernel.topological_dimension points_kernel = np.frombuffer( ffi.buffer(kernel.points, length * ffi.sizeof("double")), np.double) points_kernel = points_kernel.reshape(points.shape) assert np.allclose(points, points_kernel) # Check the value shape attached to the expression value_shape = np.frombuffer( ffi.buffer(kernel.value_shape, kernel.num_components * ffi.sizeof("int")), np.intc) assert np.allclose(expr.ufl_shape, value_shape)
def test_ufl_only_spatialcoordinate(): mesh = ufl.Mesh(ufl.VectorElement("P", ufl.triangle, 1)) V = ufl.FunctionSpace(mesh, ufl.FiniteElement("P", ufl.triangle, 2)) x, y = ufl.SpatialCoordinate(mesh) expr = x * y - y**2 + x W = V to_element = create_element(W.ufl_element()) ast, oriented, needs_cell_sizes, coefficients, first_coeff_fake_coords, *_ = compile_expression_dual_evaluation( expr, to_element, coffee=False) assert first_coeff_fake_coords is True
def test_ufl_only_simple(): mesh = ufl.Mesh(ufl.VectorElement("P", ufl.triangle, 1)) V = ufl.FunctionSpace(mesh, ufl.FiniteElement("P", ufl.triangle, 2)) v = ufl.Coefficient(V) expr = ufl.inner(v, v) W = V to_element = create_element(W.ufl_element()) ast, oriented, needs_cell_sizes, coefficients, first_coeff_fake_coords, *_ = compile_expression_dual_evaluation( expr, to_element, coffee=False) assert first_coeff_fake_coords is False
def test_rank1(compile_args): """Tests evaluation of rank-1 form. Builds a linear operator which takes vector-valued functions in P1 space and evaluates expression [u_y, u_x] + grad(u_x) at specified points. """ e = ufl.VectorElement("P", "triangle", 1) mesh = ufl.Mesh(e) V = ufl.FunctionSpace(mesh, e) u = ufl.TrialFunction(V) expr = ufl.as_vector([u[1], u[0]]) + ufl.grad(u[0]) points = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) obj, module = ffcx.codegeneration.jit.compile_expressions( [(expr, points)], cffi_extra_compile_args=compile_args) ffi = cffi.FFI() kernel = obj[0][0] c_type, np_type = float_to_type("double") # 2 components for vector components of expression # 3 points of evaluation # 6 degrees of freedom for rank1 form A = np.zeros((3, 2, 6), dtype=np_type) # Coefficient storage XYXYXY w = np.array([0.0], dtype=np_type) c = np.array([0.0], dtype=np_type) # Coords storage XYXYXY coords = np.array(points.flatten(), dtype=np.float64) kernel.tabulate_expression( ffi.cast('{type} *'.format(type=c_type), A.ctypes.data), ffi.cast('{type} *'.format(type=c_type), w.ctypes.data), ffi.cast('{type} *'.format(type=c_type), c.ctypes.data), ffi.cast('double *', coords.ctypes.data)) f = np.array([[1.0, 2.0, 3.0], [-4.0, -5.0, 6.0]]) # Apply the operator on some test input data u_ffcx = np.einsum("ijk,k", A, f.T.flatten()) # Compute the correct values using NumPy # Gradf0 is gradient of f[0], each component of the gradient is constant gradf0 = np.array( [[f[0, 1] - f[0, 0], f[0, 1] - f[0, 0], f[0, 1] - f[0, 0]], [f[0, 2] - f[0, 0], f[0, 2] - f[0, 0], f[0, 2] - f[0, 0]]]) u_correct = np.array([f[1], f[0]]) + gradf0 assert np.allclose(u_ffcx, u_correct.T)
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") sdata = get_shared_data(mesh, 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) in {ufl.TensorElement, ufl.VectorElement}: # The number of "free" dofs is given by reference_value_shape, # not value_shape due to symmetry specifications rvs = element.reference_value_shape() # This requires that the sub element is not itself a # tensor element (which is checked by the top level # constructor of function spaces) sub = element.sub_elements()[0].value_shape() self.shape = rvs[:len(rvs) - len(sub)] else: self.shape = () self._ufl_function_space = ufl.FunctionSpace(mesh.ufl_mesh(), element) self._shared_data = sdata self._mesh = mesh self.rank = len(self.shape) r"""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.value_size = int(numpy.prod(self.shape, dtype=int)) r"""The total number of degrees of freedom at each function space node.""" self.name = name r"""The (optional) descriptive name for this space.""" self.node_set = sdata.node_set r"""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) r"""A :class:`pyop2.DataSet` representing the function space degrees of freedom.""" self.comm = self.node_set.comm # Need to create finat element again as sdata does not # want to carry finat_element. self.finat_element = create_element(element) # Used for reconstruction of mixed/component spaces. # sdata carries real_tensorproduct. self.real_tensorproduct = sdata.real_tensorproduct self.extruded = sdata.extruded self.offset = sdata.offset self.cell_boundary_masks = sdata.cell_boundary_masks self.interior_facet_boundary_masks = sdata.interior_facet_boundary_masks
def set_coordinates(self, domain): """Prepare the coordinate field. :arg domain: :class:`ufl.Domain` """ # Create a fake coordinate coefficient for a domain. f = ufl.Coefficient(ufl.FunctionSpace(domain, domain.ufl_coordinate_element())) self.domain_coordinate[domain] = f self.coordinates_args, expression = prepare_coordinates( f, "coordinate_dofs", interior_facet=self.interior_facet) self.coefficient_map[f] = expression
def __init__(self, spaces, name=None): super(MixedFunctionSpace, self).__init__() self._spaces = tuple(IndexedFunctionSpace(i, s, self) for i, s in enumerate(spaces)) mesh, = set(s.mesh() for s in spaces) self._ufl_function_space = ufl.FunctionSpace(mesh.ufl_mesh(), ufl.MixedElement(*[s.ufl_element() for s in spaces])) self.name = name or "_".join(str(s.name) for s in spaces) self._subspaces = {} self._mesh = mesh self.comm = self.node_set.comm
def test_ufl_only_shape_mismatch(): mesh = ufl.Mesh(ufl.VectorElement("P", ufl.triangle, 1)) V = ufl.FunctionSpace(mesh, ufl.FiniteElement("RT", ufl.triangle, 1)) v = ufl.Coefficient(V) expr = ufl.inner(v, v) assert expr.ufl_shape == () W = V to_element = create_element(W.ufl_element()) assert to_element.value_shape == (2, ) with pytest.raises(ValueError): ast, oriented, needs_cell_sizes, coefficients, first_coeff_fake_coords, *_ = compile_expression_dual_evaluation( expr, to_element, coffee=False)
def __init__(self, mesh): "Create function that evaluates to the mesh coordinates at each vertex." # Initialize C++ part cpp.MeshCoordinates.__init__(self, mesh) # Initialize UFL part ufl_element = mesh.ufl_domain().ufl_coordinate_element() if ufl_element.family() != "Lagrange" or ufl_element.degree() != 1: cpp.dolfin_error("specialfunctions.py", "initialize MeshCoordinates", "dolfin::MeshCoordinates only supports affine meshes") ufl_function_space = ufl.FunctionSpace(mesh.ufl_domain(), ufl_element) ufl.Coefficient.__init__(self, ufl_function_space, count=self.id())
def test_estimated_degree(): cell = ufl.tetrahedron mesh = ufl.Mesh(ufl.VectorElement('P', cell, 1)) V = ufl.FunctionSpace(mesh, ufl.FiniteElement('P', cell, 1)) f = ufl.Coefficient(V) u = ufl.TrialFunction(V) v = ufl.TestFunction(V) a = u * v * ufl.tanh(ufl.sqrt(ufl.sinh(f) / ufl.sin(f**f))) * ufl.dx handler = MockHandler() logger.addHandler(handler) with pytest.raises(RuntimeError): compile_form(a) logger.removeHandler(handler)
def __init__(self, value): self.dat, rank, shape = _globalify(value) cell = None domain = None if rank == 0: e = ufl.FiniteElement("Real", cell, 0) elif rank == 1: e = ufl.VectorElement("Real", cell, 0, shape[0]) elif rank == 2: e = ufl.TensorElement("Real", cell, 0, shape=shape) fs = ufl.FunctionSpace(domain, e) super(Constant, self).__init__(fs) self._repr = 'Constant(%r, %r)' % (self.ufl_element(), self.count())
def __init__(self, value, domain=None): # Init also called in mesh constructor, but constant can be built without mesh utils._init() self.dat, rank, shape = _globalify(value) cell = None if domain is not None: domain = ufl.as_domain(domain) cell = domain.ufl_cell() if rank == 0: e = ufl.FiniteElement("Real", cell, 0) elif rank == 1: e = ufl.VectorElement("Real", cell, 0, shape[0]) elif rank == 2: e = ufl.TensorElement("Real", cell, 0, shape=shape) fs = ufl.FunctionSpace(domain, e) super(Constant, self).__init__(fs) self._repr = 'Constant(%r, %r)' % (self.ufl_element(), self.count())
def __init__(self, value, cell, name=None, symmetry=None): cell = ufl.as_cell(cell) ufl_domain = None array = numpy.array(value) rank = len(array.shape) floats = list(map(float, array.flat)) assert rank == 2 ufl_element = ufl.TensorElement("Real", cell, 0, shape=array.shape, symmetry=symmetry) cpp.Constant.__init__(self, list(array.shape), floats) ufl_function_space = ufl.FunctionSpace(ufl_domain, ufl_element) ufl.Coefficient.__init__(self, ufl_function_space, count=self.id()) name = name or "f_%d" % self.count() self.rename(name, "a Constant")
def V(request, mesh): return ufl.FunctionSpace(mesh, request.param("CG", mesh.ufl_cell(), 2))
def compile_expression_dual_evaluation(expression, to_element, *, domain=None, interface=None, parameters=None, coffee=False): """Compile a UFL expression to be evaluated against a compile-time known reference element's dual basis. Useful for interpolating UFL expressions into e.g. N1curl spaces. :arg expression: UFL expression :arg to_element: A FInAT element for the target space :arg domain: optional UFL domain the expression is defined on (required when expression contains no domain). :arg interface: backend module for the kernel interface :arg parameters: parameters object :arg coffee: compile coffee kernel instead of loopy kernel """ import coffee.base as ast import loopy as lp # Just convert FInAT element to FIAT for now. # Dual evaluation in FInAT will bring a thorough revision. to_element = to_element.fiat_equivalent if any(len(dual.deriv_dict) != 0 for dual in to_element.dual_basis()): raise NotImplementedError( "Can only interpolate onto dual basis functionals without derivative evaluation, sorry!" ) if parameters is None: parameters = default_parameters() else: _ = default_parameters() _.update(parameters) parameters = _ # Determine whether in complex mode complex_mode = is_complex(parameters["scalar_type"]) # Find out which mapping to apply try: mapping, = set(to_element.mapping()) except ValueError: raise NotImplementedError( "Don't know how to interpolate onto zany spaces, sorry") expression = apply_mapping(expression, mapping, domain) # Apply UFL preprocessing expression = ufl_utils.preprocess_expression(expression, complex_mode=complex_mode) # Initialise kernel builder if interface is None: if coffee: import tsfc.kernel_interface.firedrake as firedrake_interface_coffee interface = firedrake_interface_coffee.ExpressionKernelBuilder else: # Delayed import, loopy is a runtime dependency import tsfc.kernel_interface.firedrake_loopy as firedrake_interface_loopy interface = firedrake_interface_loopy.ExpressionKernelBuilder builder = interface(parameters["scalar_type"]) arguments = extract_arguments(expression) argument_multiindices = tuple( builder.create_element(arg.ufl_element()).get_indices() for arg in arguments) # Replace coordinates (if any) unless otherwise specified by kwarg if domain is None: domain = expression.ufl_domain() assert domain is not None # Collect required coefficients first_coefficient_fake_coords = False coefficients = extract_coefficients(expression) if has_type(expression, GeometricQuantity) or any( fem.needs_coordinate_mapping(c.ufl_element()) for c in coefficients): # Create a fake coordinate coefficient for a domain. coords_coefficient = ufl.Coefficient( ufl.FunctionSpace(domain, domain.ufl_coordinate_element())) builder.domain_coordinate[domain] = coords_coefficient builder.set_cell_sizes(domain) coefficients = [coords_coefficient] + coefficients first_coefficient_fake_coords = True builder.set_coefficients(coefficients) # Split mixed coefficients expression = ufl_utils.split_coefficients(expression, builder.coefficient_split) # Translate to GEM kernel_cfg = dict( interface=builder, ufl_cell=domain.ufl_cell(), # FIXME: change if we ever implement # interpolation on facets. integral_type="cell", argument_multiindices=argument_multiindices, index_cache={}, scalar_type=parameters["scalar_type"]) if all( isinstance(dual, PointEvaluation) for dual in to_element.dual_basis()): # This is an optimisation for point-evaluation nodes which # should go away once FInAT offers the interface properly qpoints = [] # Everything is just a point evaluation. for dual in to_element.dual_basis(): ptdict = dual.get_point_dict() qpoint, = ptdict.keys() (qweight, component), = ptdict[qpoint] assert allclose(qweight, 1.0) assert component == () qpoints.append(qpoint) point_set = PointSet(qpoints) config = kernel_cfg.copy() config.update(point_set=point_set) # Allow interpolation onto QuadratureElements to refer to the quadrature # rule they represent if isinstance(to_element, FIAT.QuadratureElement): assert allclose(asarray(qpoints), asarray(to_element._points)) quad_rule = QuadratureRule(point_set, to_element._weights) config["quadrature_rule"] = quad_rule expr, = fem.compile_ufl(expression, **config, point_sum=False) # In some cases point_set.indices may be dropped from expr, but nothing # new should now appear assert set(expr.free_indices) <= set( chain(point_set.indices, *argument_multiindices)) shape_indices = tuple(gem.Index() for _ in expr.shape) basis_indices = point_set.indices ir = gem.Indexed(expr, shape_indices) else: # This is general code but is more unrolled than necssary. dual_expressions = [] # one for each functional broadcast_shape = len(expression.ufl_shape) - len( to_element.value_shape()) shape_indices = tuple(gem.Index() for _ in expression.ufl_shape[:broadcast_shape]) expr_cache = {} # Sharing of evaluation of the expression at points for dual in to_element.dual_basis(): pts = tuple(sorted(dual.get_point_dict().keys())) try: expr, point_set = expr_cache[pts] except KeyError: point_set = PointSet(pts) config = kernel_cfg.copy() config.update(point_set=point_set) expr, = fem.compile_ufl(expression, **config, point_sum=False) # In some cases point_set.indices may be dropped from expr, but # nothing new should now appear assert set(expr.free_indices) <= set( chain(point_set.indices, *argument_multiindices)) expr = gem.partial_indexed(expr, shape_indices) expr_cache[pts] = expr, point_set weights = collections.defaultdict(list) for p in pts: for (w, cmp) in dual.get_point_dict()[p]: weights[cmp].append(w) qexprs = gem.Zero() for cmp in sorted(weights): qweights = gem.Literal(weights[cmp]) qexpr = gem.Indexed(expr, cmp) qexpr = gem.index_sum( gem.Indexed(qweights, point_set.indices) * qexpr, point_set.indices) qexprs = gem.Sum(qexprs, qexpr) assert qexprs.shape == () assert set(qexprs.free_indices) == set( chain(shape_indices, *argument_multiindices)) dual_expressions.append(qexprs) basis_indices = (gem.Index(), ) ir = gem.Indexed(gem.ListTensor(dual_expressions), basis_indices) # Build kernel body return_indices = basis_indices + shape_indices + tuple( chain(*argument_multiindices)) return_shape = tuple(i.extent for i in return_indices) return_var = gem.Variable('A', return_shape) if coffee: return_arg = ast.Decl(parameters["scalar_type"], ast.Symbol('A', rank=return_shape)) else: return_arg = lp.GlobalArg("A", dtype=parameters["scalar_type"], shape=return_shape) return_expr = gem.Indexed(return_var, return_indices) # TODO: one should apply some GEM optimisations as in assembly, # but we don't for now. ir, = impero_utils.preprocess_gem([ir]) impero_c = impero_utils.compile_gem([(return_expr, ir)], return_indices) index_names = dict( (idx, "p%d" % i) for (i, idx) in enumerate(basis_indices)) # Handle kernel interface requirements builder.register_requirements([ir]) # Build kernel tuple return builder.construct_kernel(return_arg, impero_c, index_names, first_coefficient_fake_coords)
def ufl_function_space(self): from firedrake.ufl_expr import reconstruct_element return ufl.FunctionSpace( self._mesh, reconstruct_element(self._topological.ufl_element(), cell=self._mesh.ufl_cell()))
def coordinate_coefficient(domain): """Create a fake coordinate coefficient for a domain.""" return ufl.Coefficient(ufl.FunctionSpace(domain, domain.ufl_coordinate_element()))
def holzapfel_ogden(mesh, q, p, nf=0): # Based on https://gist.github.com/meg-simula/3ab3fd63264c8cf1912b # # Original credit note: # "Original implementation by Gabriel Balaban, # modified by Marie E. Rognes" from ufl import (Constant, VectorConstant, Identity, Coefficient, TestFunction, conditional, det, diff, dot, exp, grad, inner, tr, variable) # Define some random parameters a = Constant(mesh) b = Constant(mesh) a_s = Constant(mesh) b_s = Constant(mesh) a_f = Constant(mesh) b_f = Constant(mesh) a_fs = Constant(mesh) b_fs = Constant(mesh) # For more fun, make these general vector fields rather than # constants: e_s = VectorConstant(mesh) e_f = VectorConstant(mesh) # Define the isochoric energy contribution def isochoric(C): I_1 = tr(C) I4_f = dot(e_f, C*e_f) I4_s = dot(e_s, C*e_s) I8_fs = dot(e_s, C*e_f) def heaviside(x): return conditional(x < 1, 0, 1) def scaled_exp(a, b, x): return a/(2*b)*(exp(b*x) - 1) E_1 = scaled_exp(a, b, I_1 - 3) E_f = heaviside(I4_f)*scaled_exp(a_f, b_f, (I4_f - 1)**2) E_s = heaviside(I4_s)*scaled_exp(a_s, b_s, (I4_s - 1)**2) E_3 = scaled_exp(a_fs, b_fs, I8_fs**2) E = E_1 + E_f + E_s + E_3 return E # Define mesh and function space V = ufl.FunctionSpace(mesh, ufl.VectorElement("CG", mesh.ufl_cell(), q)) u = Coefficient(V) v = TestFunction(V) # Misc elasticity related tensors and other quantities I = Identity(mesh.ufl_cell().topological_dimension()) F = grad(u) + I F = variable(F) J = det(F) Cbar = J**(-2/3)*F.T*F # Define energy Psi = isochoric(Cbar) # Find first Piola-Kirchhoff tensor P = diff(Psi, F) # Define the variational formulation it = inner(P, grad(v)) P = ufl.FunctionSpace(mesh, ufl.VectorElement('P', mesh.ufl_cell(), p)) f = [ufl.Coefficient(P) for _ in range(nf)] return ufl.derivative(reduce(ufl.inner, list(map(ufl.div, f)) + [it])*ufl.dx, u)