def create_coordinate_map(o): """Return a compiled UFC coordinate_mapping object""" try: # Create a compiled coordinate map from an object with the # ufl_mesh attribute cmap_ptr = jit.ffcx_jit(o.ufl_domain()) except AttributeError: # FIXME: It would be good to avoid the type check, but ffc_jit # supports other objects so we could get, e.g., a compiled # finite element if isinstance(o, ufl.domain.Mesh): cmap_ptr = jit.ffcx_jit(o) else: raise TypeError( "Cannot create coordinate map from an object of type: {}". format(type(o))) except Exception: print("Failed to create compiled coordinate map") raise # Wrap compiled coordinate map and return ffi = FFI() cmap = cpp.fem.create_coordinate_map(ffi.cast("uintptr_t", cmap_ptr)) return cmap
def __init__(self, ufl_expression: ufl.core.expr.Expr, x: np.ndarray, form_compiler_parameters: dict = {}, jit_parameters: dict = {}): """Create dolfinx Expression. Represents a mathematical expression evaluated at a pre-defined set of points on the reference cell. This class closely follows the concept of a UFC Expression. This functionality can be used to evaluate a gradient of a Function at the quadrature points in all cells. This evaluated gradient can then be used as input to a non-FEniCS function that calculates a material constitutive model. Parameters ---------- ufl_expression Pure UFL expression x Array of points of shape (num_points, tdim) on the reference element. form_compiler_parameters Parameters used in FFCX compilation of this Expression. Run `ffcx --help` in the commandline to see all available options. jit_parameters Parameters controlling JIT compilation of C code. Note ---- This wrapper is responsible for the FFCX compilation of the UFL Expr and attaching the correct data to the underlying C++ Expression. """ assert x.ndim < 3 num_points = x.shape[0] if x.ndim == 2 else 1 x = np.reshape(x, (num_points, -1)) mesh = ufl_expression.ufl_domain().ufl_cargo() # Compile UFL expression with JIT ufc_expression = jit.ffcx_jit(mesh.mpi_comm(), (ufl_expression, x), form_compiler_parameters=form_compiler_parameters, jit_parameters=jit_parameters) self._ufl_expression = ufl_expression self._ufc_expression = ufc_expression # Setup data (evaluation points, coefficients, constants, mesh, value_size). # Tabulation function. ffi = cffi.FFI() fn = ffi.cast("uintptr_t", ufc_expression.tabulate_expression) value_size = ufl.product(self.ufl_expression.ufl_shape) ufl_coefficients = ufl.algorithms.extract_coefficients(ufl_expression) coefficients = [ufl_coefficient._cpp_object for ufl_coefficient in ufl_coefficients] ufl_constants = ufl.algorithms.analysis.extract_constants(ufl_expression) constants = [ufl_constant._cpp_object for ufl_constant in ufl_constants] self._cpp_object = cpp.function.Expression(coefficients, constants, mesh, x, fn, value_size)
def __init__(self, form: ufl.Form, form_compiler_parameters: dict = {}, jit_parameters: dict = {}): """Create dolfinx Form Parameters ---------- form Pure UFL form form_compiler_parameters See :py:func:`ffcx_jit <dolfinx.jit.ffcx_jit>` jit_parameters See :py:func:`ffcx_jit <dolfinx.jit.ffcx_jit>` Note ---- This wrapper for UFL form is responsible for the actual FFCX compilation and attaching coefficients and domains specific data to the underlying C++ Form. """ # Extract subdomain data from UFL form sd = form.subdomain_data() self._subdomains, = list(sd.values()) # Assuming single domain domain, = list(sd.keys()) # Assuming single domain mesh = domain.ufl_cargo() if mesh is None: raise RuntimeError("Expecting to find a Mesh in the form.") # Compile UFL form with JIT ufc_form = jit.ffcx_jit( mesh.mpi_comm(), form, form_compiler_parameters=form_compiler_parameters, jit_parameters=jit_parameters) # For every argument in form extract its function space function_spaces = [ func.ufl_function_space()._cpp_object for func in form.arguments() ] # Prepare coefficients data. For every coefficient in form take # its C++ object. original_coefficients = form.coefficients() coeffs = [original_coefficients[ufc_form.original_coefficient_position( i)]._cpp_object for i in range(ufc_form.num_coefficients)] # Create dictionary of of subdomain markers (possible None for # some dimensions subdomains = {cpp.fem.IntegralType.cell: self._subdomains.get("cell"), cpp.fem.IntegralType.exterior_facet: self._subdomains.get("exterior_facet"), cpp.fem.IntegralType.interior_facet: self._subdomains.get("interior_facet"), cpp.fem.IntegralType.vertex: self._subdomains.get("vertex")} # Prepare dolfinx.cpp.fem.Form and hold it as a member ffi = cffi.FFI() self._cpp_object = cpp.fem.create_form(ffi.cast("uintptr_t", ufc_form), function_spaces, coeffs, [c._cpp_object for c in form.constants()], subdomains, mesh)
def __init__(self, mesh: Mesh, element: typing.Union[ufl.FiniteElementBase, ElementMetaData], cppV: typing.Optional[_cpp.fem.FunctionSpace] = None, form_compiler_params: dict = {}, jit_params: dict = {}): """Create a finite element function space.""" # Create function space from a UFL element and existing cpp # FunctionSpace if cppV is not None: assert mesh is None ufl_domain = cppV.mesh.ufl_domain() super().__init__(ufl_domain, element) self._cpp_object = cppV return # Initialise the ufl.FunctionSpace if isinstance(element, ufl.FiniteElementBase): super().__init__(mesh.ufl_domain(), element) else: e = ElementMetaData(*element) ufl_element = ufl.FiniteElement(e.family, mesh.ufl_cell(), e.degree, form_degree=e.form_degree) super().__init__(mesh.ufl_domain(), ufl_element) # Compile dofmap and element and create DOLFIN objects (self._ufcx_element, self._ufcx_dofmap), module, code = jit.ffcx_jit( mesh.comm, self.ufl_element(), form_compiler_params=form_compiler_params, jit_params=jit_params) ffi = cffi.FFI() cpp_element = _cpp.fem.FiniteElement( ffi.cast("uintptr_t", ffi.addressof(self._ufcx_element))) cpp_dofmap = _cpp.fem.create_dofmap( mesh.comm, ffi.cast("uintptr_t", ffi.addressof(self._ufcx_dofmap)), mesh.topology, cpp_element) # Initialize the cpp.FunctionSpace self._cpp_object = _cpp.fem.FunctionSpace(mesh, cpp_element, cpp_dofmap)
def __init__(self, mesh: cpp.mesh.Mesh, element: typing.Union[ufl.FiniteElementBase, ElementMetaData], cppV: typing.Optional[cpp.function.FunctionSpace] = None): """Create a finite element function space.""" # Create function space from a UFL element and existing cpp # FunctionSpace if cppV is not None: assert mesh is None ufl_domain = cppV.mesh.ufl_domain() super().__init__(ufl_domain, element) self._cpp_object = cppV return # Initialise the ufl.FunctionSpace if isinstance(element, ufl.FiniteElementBase): super().__init__(mesh.ufl_domain(), element) else: e = ElementMetaData(*element) ufl_element = ufl.FiniteElement(e.family, mesh.ufl_cell(), e.degree, form_degree=e.form_degree) super().__init__(mesh.ufl_domain(), ufl_element) # Compile dofmap and element and create DOLFIN objects ufc_element, ufc_dofmap_ptr = jit.ffcx_jit( self.ufl_element(), form_compiler_parameters=None, mpi_comm=mesh.mpi_comm()) ffi = cffi.FFI() ufc_element = fem.dofmap.make_ufc_finite_element( ffi.cast("uintptr_t", ufc_element)) cpp_element = cpp.fem.FiniteElement(ufc_element) ufc_dofmap = fem.dofmap.make_ufc_dofmap( ffi.cast("uintptr_t", ufc_dofmap_ptr)) cpp_dofmap = cpp.fem.create_dofmap(ufc_dofmap, mesh) # Initialize the cpp.FunctionSpace self._cpp_object = cpp.function.FunctionSpace(mesh, cpp_element, cpp_dofmap)
def _form(form): """"Compile a single UFL form""" # Extract subdomain data from UFL form sd = form.subdomain_data() subdomains, = list(sd.values()) # Assuming single domain domain, = list(sd.keys()) # Assuming single domain mesh = domain.ufl_cargo() if mesh is None: raise RuntimeError("Expecting to find a Mesh in the form.") ufcx_form, module, code = jit.ffcx_jit( mesh.comm, form, form_compiler_params=form_compiler_params, jit_params=jit_params) # For each argument in form extract its function space V = [arg.ufl_function_space()._cpp_object for arg in form.arguments()] # Prepare coefficients data. For every coefficient in form take its # C++ object. original_coefficients = form.coefficients() coeffs = [ original_coefficients[ ufcx_form.original_coefficient_position[i]]._cpp_object for i in range(ufcx_form.num_coefficients) ] constants = [c._cpp_object for c in form.constants()] # Subdomain markers (possibly None for some dimensions) subdomains = { _cpp.fem.IntegralType.cell: subdomains.get("cell"), _cpp.fem.IntegralType.exterior_facet: subdomains.get("exterior_facet"), _cpp.fem.IntegralType.interior_facet: subdomains.get("interior_facet"), _cpp.fem.IntegralType.vertex: subdomains.get("vertex") } return formcls(ufcx_form, V, coeffs, constants, subdomains, mesh, code)
eps = 0.5 * (ufl.grad(u) + ufl.grad(u).T) sigma = E / (1. - nu ** 2) * ((1. - nu) * eps + nu * ufl.Identity(2) * ufl.tr(eps)) return sigma a00 = ufl.inner(sigma, tau) * ufl.dx a10 = - ufl.inner(sigma, ufl.grad(v)) * ufl.dx a01 = - ufl.inner(sigma_u(u), tau) * ufl.dx f = ufl.as_vector([0.0, 1.0 / 16]) b1 = form(- ufl.inner(f, v) * ds(1)) # JIT compile individual blocks tabulation kernels nptype = "complex128" if np.issubdtype(PETSc.ScalarType, np.complexfloating) else "float64" ffcxtype = "double _Complex" if np.issubdtype(PETSc.ScalarType, np.complexfloating) else "double" ufcx_form00, _, _ = ffcx_jit(mesh.comm, a00, form_compiler_parameters={"scalar_type": ffcxtype}) kernel00 = getattr(ufcx_form00.integrals(0)[0], f"tabulate_tensor_{nptype}") ufcx_form01, _, _ = ffcx_jit(mesh.comm, a01, form_compiler_parameters={"scalar_type": ffcxtype}) kernel01 = getattr(ufcx_form01.integrals(0)[0], f"tabulate_tensor_{nptype}") ufcx_form10, _, _ = ffcx_jit(mesh.comm, a10, form_compiler_parameters={"scalar_type": ffcxtype}) kernel10 = getattr(ufcx_form10.integrals(0)[0], f"tabulate_tensor_{nptype}") ffi = cffi.FFI() cffi_support.register_type(ffi.typeof('double _Complex'), numba.types.complex128) c_signature = numba.types.void( numba.types.CPointer(numba.typeof(PETSc.ScalarType())), numba.types.CPointer(numba.typeof(PETSc.ScalarType())), numba.types.CPointer(numba.typeof(PETSc.ScalarType())), numba.types.CPointer(numba.types.double), numba.types.CPointer(numba.types.int32), numba.types.CPointer(numba.types.uint8))
def __init__(self, ufl_expression: ufl.core.expr.Expr, X: np.ndarray, form_compiler_params: dict = {}, jit_params: dict = {}, dtype=PETSc.ScalarType): """Create DOLFINx Expression. Represents a mathematical expression evaluated at a pre-defined set of points on the reference cell. This class closely follows the concept of a UFC Expression. This functionality can be used to evaluate a gradient of a Function at the quadrature points in all cells. This evaluated gradient can then be used as input to a non-FEniCS function that calculates a material constitutive model. Args: ufl_expression: Pure UFL expression X: Array of points of shape `(num_points, tdim)` on the reference element. form_compiler_params: Parameters used in FFCx compilation of this Expression. Run ``ffcx --help`` in the commandline to see all available options. jit_params: Parameters controlling JIT compilation of C code. Notes: This wrapper is responsible for the FFCx compilation of the UFL Expr and attaching the correct data to the underlying C++ Expression. """ assert X.ndim < 3 num_points = X.shape[0] if X.ndim == 2 else 1 _X = np.reshape(X, (num_points, -1)) mesh = ufl_expression.ufl_domain().ufl_cargo() # Compile UFL expression with JIT if dtype == np.float32: form_compiler_params["scalar_type"] = "float" if dtype == np.float64: form_compiler_params["scalar_type"] = "double" elif dtype == np.complex128: form_compiler_params["scalar_type"] = "double _Complex" else: raise RuntimeError( f"Unsupported scalar type {dtype} for Expression.") self._ufcx_expression, _, self._code = jit.ffcx_jit( mesh.comm, (ufl_expression, _X), form_compiler_params=form_compiler_params, jit_params=jit_params) self._ufl_expression = ufl_expression # Tabulation function. ffi = cffi.FFI() # Prepare coefficients data. For every coefficient in form take # its C++ object. original_coefficients = ufl.algorithms.extract_coefficients( ufl_expression) coeffs = [ original_coefficients[ self._ufcx_expression.original_coefficient_positions[i]]. _cpp_object for i in range(self._ufcx_expression.num_coefficients) ] ufl_constants = ufl.algorithms.analysis.extract_constants( ufl_expression) constants = [constant._cpp_object for constant in ufl_constants] arguments = ufl.algorithms.extract_arguments(ufl_expression) if len(arguments) == 0: self._argument_function_space = None elif len(arguments) == 1: self._argument_function_space = arguments[0].ufl_function_space( )._cpp_object else: raise RuntimeError( "Expressions with more that one Argument not allowed.") def create_expression(dtype): if dtype is np.float32: return _cpp.fem.create_expression_float32 elif dtype is np.float64: return _cpp.fem.create_expression_float64 elif dtype is np.complex128: return _cpp.fem.create_expression_complex128 else: raise NotImplementedError(f"Type {dtype} not supported.") self._cpp_object = create_expression(dtype)( ffi.cast("uintptr_t", ffi.addressof(self._ufcx_expression)), coeffs, constants, mesh, self.argument_function_space)
def __init__(self, form: ufl.Form, form_compiler_parameters: dict = {}, jit_parameters: dict = {}): """Create dolfinx Form Parameters ---------- form Pure UFL form form_compiler_parameters Parameters used in FFCX compilation of this form. Run `ffcx --help` in the commandline to see all available options. jit_parameters Parameters controlling JIT compilation of C code. Note ---- This wrapper for UFL form is responsible for the actual FFCX compilation and attaching coefficients and domains specific data to the underlying C++ Form. """ # Extract subdomain data from UFL form sd = form.subdomain_data() self._subdomains, = list(sd.values()) # Assuming single domain domain, = list(sd.keys()) # Assuming single domain mesh = domain.ufl_cargo() # Compile UFL form with JIT ufc_form = jit.ffcx_jit( form, form_compiler_parameters=form_compiler_parameters, jit_parameters=jit_parameters, mpi_comm=mesh.mpi_comm()) # For every argument in form extract its function space function_spaces = [ func.ufl_function_space()._cpp_object for func in form.arguments() ] # Prepare dolfinx.Form and hold it as a member ffi = cffi.FFI() self._cpp_object = cpp.fem.create_form(ffi.cast("uintptr_t", ufc_form), function_spaces) # Need to fill the form with coefficients data # For every coefficient in form take its C++ object original_coefficients = form.coefficients() for i in range(self._cpp_object.num_coefficients()): j = self._cpp_object.original_coefficient_position(i) self._cpp_object.set_coefficient( i, original_coefficients[j]._cpp_object) # Constants are set based on their position in original form original_constants = [c._cpp_object for c in form.constants()] self._cpp_object.set_constants(original_constants) if mesh is None: raise RuntimeError("Expecting to find a Mesh in the form.") # Attach mesh (because function spaces and coefficients may be # empty lists) if not function_spaces: self._cpp_object.set_mesh(mesh) # Attach subdomains to C++ Form if we have them subdomains = self._subdomains.get("cell") if subdomains: self._cpp_object.set_cell_domains(subdomains) subdomains = self._subdomains.get("exterior_facet") if subdomains: self._cpp_object.set_exterior_facet_domains(subdomains) subdomains = self._subdomains.get("interior_facet") if subdomains: self._cpp_object.set_interior_facet_domains(subdomains) subdomains = self._subdomains.get("vertex") if subdomains: self._cpp_object.set_vertex_domains(subdomains)