Exemple #1
0
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))
Exemple #3
0
    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)
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
    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()
Exemple #7
0
    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)
Exemple #8
0
    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()