def compute_shapes(form: ufl.Form): """Computes the shapes of the cell tensor, coefficient & coordinate arrays for a form""" # Cell tensor elements = tuple(arg.ufl_element() for arg in form.arguments()) fiat_elements = (create_element(element) for element in elements) element_dims = tuple(fe.space_dimension() for fe in fiat_elements) A_shape = element_dims # Coefficient arrays ws_shapes = [] for coefficient in form.coefficients(): w_element = coefficient.ufl_element() w_dim = create_element(w_element).space_dimension() ws_shapes.append((w_dim,)) if len(ws_shapes) > 0: max_w_dim = sorted(ws_shapes, key=lambda w_dim: np.prod(w_dim))[-1] w_full_shape = (len(ws_shapes), ) + max_w_dim else: w_full_shape = (0,) # Coordinate dofs array num_vertices_per_cell = form.ufl_cell().num_vertices() gdim = form.ufl_cell().geometric_dimension() coords_shape = (num_vertices_per_cell, gdim) return A_shape, w_full_shape, coords_shape
def adjoint(form: ufl.Form, reordered_arguments=None) -> ufl.Form: """Compute adjoint of a bilinear form by changing the ordering (count) of the test and trial functions. The functions wraps ``ufl.adjoint``, and by default UFL will create new ``Argument`` s. To specify the ``Argument`` s rather than creating new ones, pass a tuple of ``Argument`` s as ``reordered_arguments``. See the documentation for ``ufl.adjoint`` for more details. """ if reordered_arguments is not None: return ufl.adjoint(form, reordered_arguments=reordered_arguments) # Extract form arguments arguments = form.arguments() if len(arguments) != 2: raise RuntimeError( "Cannot compute adjoint of form, form is not bilinear") if any(arg.part() is not None for arg in arguments): raise RuntimeError( "Cannot compute adjoint of form, parts not supported") # Create new Arguments in the same spaces (NB: Order does not matter # anymore here because number is absolute) v1 = function.Argument(arguments[1].function_space, arguments[0].number(), arguments[0].part()) v0 = function.Argument(arguments[0].function_space, arguments[1].number(), arguments[1].part()) # Return form with swapped arguments as new arguments return ufl.adjoint(form, reordered_arguments=(v1, v0))
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, form: ufl.Form, form_compiler_parameters: dict = None): """Create dolfin Form Parameters ---------- form Pure UFL form form_compiler_parameters Parameters used in JIT FFC compilation of this form Note ---- This wrapper for UFL form is responsible for the actual FFC compilation and attaching coefficients and domains specific data to the underlying C++ Form. """ self.form_compiler_parameters = form_compiler_parameters # 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.ffc_jit( form, form_compiler_parameters=self.form_compiler_parameters, mpi_comm=mesh.mpi_comm()) # Cast compiled library to pointer to ufc_form ffi = cffi.FFI() ufc_form = fem.dofmap.make_ufc_form(ffi.cast("uintptr_t", ufc_form)) # For every argument in form extract its function space function_spaces = [ func.function_space()._cpp_object for func in form.arguments() ] # Prepare dolfin.Form and hold it as a member self._cpp_object = cpp.fem.create_form(ufc_form, function_spaces) # Need to fill the form with coefficients data # For every coefficient in form take its CPP 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) 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)
def __init__(self, form: ufl.Form, form_compiler_parameters: dict = None): """Create dolfin Form Parameters ---------- form Pure UFL form form_compiler_parameters Parameters used in JIT FFC compilation of this form Note ---- This wrapper for UFL form is responsible for the actual FFC compilation and attaching coefficients and domains specific data to the underlying C++ Form. """ self.form_compiler_parameters = form_compiler_parameters # Add DOLFIN include paths (just the Boost path for special # math functions is really required) # FIXME: move getting include paths to elsewhere if self.form_compiler_parameters is None: self.form_compiler_parameters = { "external_include_dirs": jit.dolfin_pc["include_dirs"] } else: # FIXME: add paths if dict entry already exists self.form_compiler_parameters[ "external_include_dirs"] = jit.dolfin_pc["include_dirs"] # 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.ffc_jit( form, form_compiler_parameters=self.form_compiler_parameters, mpi_comm=mesh.mpi_comm()) # Cast compiled library to pointer to ufc_form ufc_form = fem.dofmap.make_ufc_form(ufc_form[0]) # For every argument in form extract its function space function_spaces = [ func.function_space()._cpp_object for func in form.arguments() ] # Prepare dolfin.Form and hold it as a member self._cpp_object = cpp.fem.Form(ufc_form, function_spaces) # Need to fill the form with coefficients data # For every coefficient in form take its CPP 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( j, original_coefficients[i]._cpp_object) 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") self._cpp_object.set_cell_domains(subdomains) subdomains = self._subdomains.get("exterior_facet") self._cpp_object.set_exterior_facet_domains(subdomains) subdomains = self._subdomains.get("interior_facet") self._cpp_object.set_interior_facet_domains(subdomains) subdomains = self._subdomains.get("vertex") self._cpp_object.set_vertex_domains(subdomains)
def __init__(self, a: ufl.Form, L: ufl.Form, bcs: typing.List[fem.DirichletBC] = [], u: fem.Function = None, petsc_options={}, form_compiler_parameters={}, jit_parameters={}): """Initialize solver for a linear variational problem. Parameters ---------- a A bilinear UFL form, the left hand side of the variational problem. L A linear UFL form, the right hand side of the variational problem. bcs A list of Dirichlet boundary conditions. u The solution function. It will be created if not provided. petsc_options Parameters that is passed to the linear algebra backend PETSc. For available choices for the 'petsc_options' kwarg, see the `PETSc-documentation <https://www.mcs.anl.gov/petsc/documentation/index.html>`. form_compiler_parameters Parameters used in FFCx compilation of this form. Run `ffcx --help` at the commandline to see all available options. Takes priority over all other parameter values, except for `scalar_type` which is determined by DOLFINx. jit_parameters Parameters used in CFFI JIT compilation of C code generated by FFCx. See `python/dolfinx/jit.py` for all available parameters. Takes priority over all other parameter values. .. code-block:: python problem = LinearProblem(a, L, [bc0, bc1], petsc_options={"ksp_type": "preonly", "pc_type": "lu"}) """ self._a = fem.Form(a, form_compiler_parameters=form_compiler_parameters, jit_parameters=jit_parameters) self._A = fem.create_matrix(self._a) self._L = fem.Form(L, form_compiler_parameters=form_compiler_parameters, jit_parameters=jit_parameters) self._b = fem.create_vector(self._L) if u is None: # Extract function space from TrialFunction (which is at the # end of the argument list as it is numbered as 1, while the # Test function is numbered as 0) self.u = fem.Function(a.arguments()[-1].ufl_function_space()) else: self.u = u self.bcs = bcs self._solver = PETSc.KSP().create( self.u.function_space.mesh.mpi_comm()) self._solver.setOperators(self._A) # Give PETSc solver options a unique prefix solver_prefix = "dolfinx_solve_{}".format(id(self)) self._solver.setOptionsPrefix(solver_prefix) # Set PETSc options opts = PETSc.Options() opts.prefixPush(solver_prefix) for k, v in petsc_options.items(): opts[k] = v opts.prefixPop() self._solver.setFromOptions()
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)
def __init__(self, a: ufl.Form, L: ufl.Form, bcs: typing.List[DirichletBCMetaClass] = [], u: _Function = None, petsc_options={}, form_compiler_params={}, jit_params={}): """Initialize solver for a linear variational problem. Args: a: A bilinear UFL form, the left hand side of the variational problem. L: A linear UFL form, the right hand side of the variational problem. bcs: A list of Dirichlet boundary conditions. u: The solution function. It will be created if not provided. petsc_options: Parameters that is passed to the linear algebra backend PETSc. For available choices for the 'petsc_options' kwarg, see the `PETSc documentation <https://petsc4py.readthedocs.io/en/stable/manual/ksp/>`_. form_compiler_params: Parameters used in FFCx compilation of this form. Run ``ffcx --help`` at the commandline to see all available options. jit_params: Parameters used in CFFI JIT compilation of C code generated by FFCx. See `python/dolfinx/jit.py` for all available parameters. Takes priority over all other parameter values. Example:: problem = LinearProblem(a, L, [bc0, bc1], petsc_options={"ksp_type": "preonly", "pc_type": "lu"}) """ self._a = _create_form(a, form_compiler_params=form_compiler_params, jit_params=jit_params) self._A = create_matrix(self._a) self._L = _create_form(L, form_compiler_params=form_compiler_params, jit_params=jit_params) self._b = create_vector(self._L) if u is None: # Extract function space from TrialFunction (which is at the # end of the argument list as it is numbered as 1, while the # Test function is numbered as 0) self.u = _Function(a.arguments()[-1].ufl_function_space()) else: self.u = u self._x = _cpp.la.petsc.create_vector_wrap(self.u.x) self.bcs = bcs self._solver = PETSc.KSP().create(self.u.function_space.mesh.comm) self._solver.setOperators(self._A) # Give PETSc solver options a unique prefix problem_prefix = "dolfinx_solve_{}".format(id(self)) self._solver.setOptionsPrefix(problem_prefix) # Set PETSc options opts = PETSc.Options() opts.prefixPush(problem_prefix) for k, v in petsc_options.items(): opts[k] = v opts.prefixPop() self._solver.setFromOptions() # Set matrix and vector PETSc options self._A.setOptionsPrefix(problem_prefix) self._A.setFromOptions() self._b.setOptionsPrefix(problem_prefix) self._b.setFromOptions()