def test_mixed_element_interpolation():
    mesh = create_unit_cube(MPI.COMM_WORLD, 3, 3, 3)
    el = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
    V = FunctionSpace(mesh, ufl.MixedElement([el, el]))
    u = Function(V)
    with pytest.raises(RuntimeError):
        u.interpolate(lambda x: np.ones(2, x.shape[1]))
Beispiel #2
0
def test_mixed_element(ElementType, space, cell, order):
    if cell == ufl.triangle:
        mesh = UnitSquareMesh(MPI.COMM_WORLD, 1, 1, CellType.triangle,
                              dolfinx.cpp.mesh.GhostMode.shared_facet)
    else:
        mesh = UnitCubeMesh(MPI.COMM_WORLD, 1, 1, 1, CellType.tetrahedron,
                            dolfinx.cpp.mesh.GhostMode.shared_facet)

    norms = []
    U_el = ufl.FiniteElement(space, cell, order)
    for i in range(3):
        U = FunctionSpace(mesh, U_el)

        u = ufl.TrialFunction(U)
        v = ufl.TestFunction(U)

        a = ufl.inner(u, v) * ufl.dx

        A = dolfinx.fem.assemble_matrix(a)
        A.assemble()
        norms.append(A.norm())

        U_el = ufl.MixedElement(U_el)

    for i in norms[1:]:
        assert np.isclose(norms[0], i)
Beispiel #3
0
def test_mixed_constant_bc(mesh_factory):
    """Test that setting a dirichletbc with on a component of a mixed
    function yields the same result as setting it with a function"""
    func, args = mesh_factory
    mesh = func(*args)
    tdim, gdim = mesh.topology.dim, mesh.geometry.dim
    boundary_facets = locate_entities_boundary(
        mesh, tdim - 1, lambda x: np.ones(x.shape[1], dtype=bool))
    TH = ufl.MixedElement([
        ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2),
        ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
    ])
    W = FunctionSpace(mesh, TH)
    U = Function(W)

    # Apply BC to component of a mixed space using a Constant
    c = Constant(mesh, (PETSc.ScalarType(2), PETSc.ScalarType(2)))
    dofs0 = locate_dofs_topological(W.sub(0), tdim - 1, boundary_facets)
    bc0 = dirichletbc(c, dofs0, W.sub(0))
    u = U.sub(0)
    set_bc(u.vector, [bc0])

    # Apply BC to component of a mixed space using a Function
    ubc1 = u.collapse()
    ubc1.interpolate(lambda x: np.full((gdim, x.shape[1]), 2.0))
    dofs1 = locate_dofs_topological((W.sub(0), ubc1.function_space), tdim - 1,
                                    boundary_facets)
    bc1 = dirichletbc(ubc1, dofs1, W.sub(0))
    U1 = Function(W)
    u1 = U1.sub(0)
    set_bc(u1.vector, [bc1])

    # Check that both approaches yield the same vector
    assert np.allclose(u.x.array, u1.x.array)
Beispiel #4
0
def test_mixed_interpolation(cell_type, order):
    """Test that interpolation is correct in a MixedElement."""
    mesh = one_cell_mesh(cell_type)
    tdim = mesh.topology.dim

    A = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), order)
    B = ufl.VectorElement("Lagrange", mesh.ufl_cell(), order)

    V = FunctionSpace(mesh, ufl.MixedElement([A, B]))
    v = Function(V)

    if tdim == 1:

        def f(x):
            return (x[0]**order, 2 * x[0])
    elif tdim == 2:

        def f(x):
            return (x[1], 2 * x[0]**order, 3 * x[1])
    else:

        def f(x):
            return (x[1], 2 * x[0]**order, 3 * x[2], 4 * x[0])

    v.interpolate(f)
    points = [random_point_in_cell(cell_type) for count in range(5)]
    cells = [0 for count in range(5)]
    values = v.eval(points, cells)

    for p, v in zip(points, values):
        assert np.allclose(v, f(p))
Beispiel #5
0
def test_mixed_interpolation():
    """Test that interpolation raised an exception."""
    mesh = one_cell_mesh(CellType.triangle)
    A = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
    B = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 1)
    v = Function(FunctionSpace(mesh, ufl.MixedElement([A, B])))
    with pytest.raises(RuntimeError):
        v.interpolate(lambda x: (x[1], 2 * x[0], 3 * x[1]))
 def __init__(self, spaces, name=None):
     super(MixedFunctionSpace, self).__init__()
     self._spaces = tuple(
         IndexedFunctionSpace(i, s, self) for i, s in enumerate(spaces))
     self._ufl_element = 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 = spaces[0].mesh()
Beispiel #7
0
    def __new__(cls, spaces, name=None):
        """
        :param spaces: a list (or tuple) of :class:`FunctionSpace`\s

        The function space may be created as ::

            V = MixedFunctionSpace(spaces)

        ``spaces`` may consist of multiple occurances of the same space: ::

            P1  = FunctionSpace(mesh, "CG", 1)
            P2v = VectorFunctionSpace(mesh, "Lagrange", 2)

            ME  = MixedFunctionSpace([P2v, P1, P1, P1])
        """

        # Check that function spaces are on the same mesh
        meshes = [space.mesh() for space in spaces]
        for i in xrange(1, len(meshes)):
            if meshes[i] is not meshes[0]:
                raise ValueError(
                    "All function spaces must be defined on the same mesh!")

        # Select mesh
        mesh = meshes[0]

        # Get topological spaces
        spaces = flatten(spaces)
        if mesh is mesh.topology:
            spaces = tuple(spaces)
        else:
            spaces = tuple(space.topological for space in spaces)

        # Ask object from cache
        self = ObjectCached.__new__(cls, mesh, spaces, name)
        if not self._initialized:
            self._spaces = [
                IndexedFunctionSpace(s, i, self) for i, s in enumerate(spaces)
            ]
            self._mesh = mesh.topology
            self._ufl_element = ufl.MixedElement(
                *[fs.ufl_element() for fs in spaces])
            self.name = name or '_'.join(str(s.name) for s in spaces)
            self._initialized = True
            dm = PETSc.DMShell().create()
            with self.make_dat().vec_ro as v:
                dm.setGlobalVector(v.duplicate())
            dm.setAttr('__fs__', weakref.ref(self))
            dm.setCreateFieldDecomposition(self.create_field_decomp)
            dm.setCreateSubDM(self.create_subdm)
            self._dm = dm
            self._ises = self.dof_dset.field_ises
            self._subspaces = []

        if mesh is not mesh.topology:
            self = WithGeometry(self, mesh)
        return self
Beispiel #8
0
def test_mixed_element_interpolation():
    def f(x):
        return np.ones(2, x.shape[1])

    mesh = UnitCubeMesh(MPI.COMM_WORLD, 3, 3, 3)
    el = ufl.FiniteElement("CG", mesh.ufl_cell(), 1)
    V = dolfinx.FunctionSpace(mesh, ufl.MixedElement([el, el]))
    u = dolfinx.Function(V)
    with pytest.raises(RuntimeError):
        u.interpolate(f)
Beispiel #9
0
 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
Beispiel #10
0
def reconstruct_element(element, cell=None):
    """Rebuild element with a new cell."""
    if cell is None:
        return element
    if isinstance(element, ufl.FiniteElement):
        family = element.family()
        degree = element.degree()
        return ufl.FiniteElement(family, cell, degree)
    if isinstance(element, ufl.VectorElement):
        family = element.family()
        degree = element.degree()
        dim = len(element.sub_elements())
        return ufl.VectorElement(family, cell, degree, dim)
    if isinstance(element, ufl.TensorElement):
        family = element.family()
        degree = element.degree()
        shape = element.value_shape()
        symmetry = element.symmetry()
        return ufl.TensorElement(family, cell, degree, shape, symmetry)
    if isinstance(element, ufl.EnrichedElement):
        eles = [
            reconstruct_element(sub, cell=cell) for sub in element._elements
        ]
        return ufl.EnrichedElement(*eles)
    if isinstance(element, ufl.RestrictedElement):
        return ufl.RestrictedElement(
            reconstruct_element(element.sub_element(), cell=cell),
            element.restriction_domain())
    if isinstance(element,
                  (ufl.TraceElement, ufl.InteriorElement, ufl.HDivElement,
                   ufl.HCurlElement, ufl.BrokenElement, ufl.FacetElement)):
        return type(element)(reconstruct_element(element._element, cell=cell))
    if isinstance(element, ufl.OuterProductElement):
        return ufl.OuterProductElement(element._A, element._B, cell=cell)
    if isinstance(element, ufl.OuterProductVectorElement):
        dim = len(element.sub_elements())
        return ufl.OuterProductVectorElement(element._A,
                                             element._B,
                                             cell=cell,
                                             dim=dim)
    if isinstance(element, ufl.OuterProductTensorElement):
        return element.reconstruct(cell=cell)
    if isinstance(element, ufl.MixedElement):
        eles = [
            reconstruct_element(sub, cell=cell)
            for sub in element.sub_elements()
        ]
        return ufl.MixedElement(*eles)
    raise NotImplementedError(
        "Don't know how to reconstruct element of type %s" % type(element))
    def __init__(self, spaces):
        """
        Create mixed finite element function space.

        *Arguments*
            spaces
                a list (or tuple) of :py:class:`FunctionSpaces
                <dolfin.functions.functionspace.FunctionSpace>`.

        *Examples of usage*
            The function space may be created by

            .. code-block:: python

                V = MixedFunctionSpace(spaces)

            ``spaces`` may consist of multiple occurances of the same space:

            .. code-block:: python

                P1  = FunctionSpace(mesh, "CG", 1)
                P2v = VectorFunctionSpace(mesh, "Lagrange", 2)

                ME  = MixedFunctionSpace([P2v, P1, P1, P1])

        """

        # Check arguments
        if not len(spaces) > 0:
            cpp.dolfin_error("functionspace.py",
                             "create mixed function space",
                             "Need at least one subspace")
        if not all(isinstance(V, FunctionSpaceBase) for V in spaces):
            cpp.dolfin_error("functionspace.py",
                             "create mixed function space",
                             "Invalid subspaces: " + str(spaces))
        #if not all(V.mesh() == spaces[0].mesh() for V in spaces):
        #    cpp.dolfin_error("functionspace.py", "Nonmatching meshes for mixed function space: " \
        #              + str([V.mesh() for V in spaces]))

        # Check that all spaces share same constrained_domain map

        # Create UFL element
        element = ufl.MixedElement(*[V.ufl_element() for V in spaces])

        # Initialize base class using mesh from first space
        FunctionSpaceBase.__init__(self, spaces[0].mesh(), element, constrained_domain=spaces[0].dofmap().constrained_domain)
Beispiel #12
0
    def __init__(self, spaces):
        """
        Create mixed finite element function space.

        *Arguments*
            spaces
                a list (or tuple) of :py:class:`FunctionSpaces
                <dolfin.functions.functionspace.FunctionSpace>`.

        *Examples of usage*
            The function space may be created by

            .. code-block:: python

                V = MixedFunctionSpace(spaces)

            ``spaces`` may consist of multiple occurances of the same space:

            .. code-block:: python

                P1  = FunctionSpace(mesh, "CG", 1)
                P2v = VectorFunctionSpace(mesh, "Lagrange", 2)

                ME  = MixedFunctionSpace([P2v, P1, P1, P1])

        """

        # Check arguments
        if not len(spaces) > 0:
            cpp.dolfin_error("functionspace.py", "create mixed function space",
                             "Need at least one subspace")
        if not all(isinstance(V, FunctionSpaceBase) for V in spaces):
            cpp.dolfin_error("functionspace.py", "create mixed function space",
                             "Invalid subspaces: " + str(spaces))

        # Create UFL element
        element = ufl.MixedElement(*[V.ufl_element() for V in spaces])

        # Get common mesh and constrained_domain, must all be the same
        mesh, constrained_domain \
        = _get_common_mesh_and_constrained_domain(spaces, "mixed")

        # Initialize base class using mesh from first space
        FunctionSpaceBase.__init__(self, mesh, element, \
                                   constrained_domain=constrained_domain)
Beispiel #13
0
def derivative(form, u, du=None, coefficient_derivatives=None):
    if du is None:
        # Get existing arguments from form and position the new one with the next argument number
        form_arguments = form.arguments()

        number = max([-1] + [arg.number() for arg in form_arguments]) + 1

        if any(arg.part() is not None for arg in form_arguments):
            cpp.dolfin_error(
                "formmanipulation.py", "compute derivative of form",
                "Cannot automatically create new Argument using "
                "parts, please supply one")
        part = None

        if isinstance(u, Function):
            V = u.function_space()
            du = Argument(V, number, part)

        elif isinstance(u, (list, tuple)) and all(
                isinstance(w, Function) for w in u):
            cpp.deprecation(
                "derivative of form w.r.t. a tuple of Coefficients", "1.7.0",
                "2.0.0", "Take derivative w.r.t. a single Coefficient on "
                "a mixed space instead.")

            # Get mesh
            mesh = u[0].function_space().mesh()
            if not all(mesh.id() == v.function_space().mesh().id() for v in u):
                cpp.dolfin_error(
                    "formmanipulation.py", "compute derivative of form",
                    "Don't know how to compute derivative with "
                    "respect to Coefficients on different meshes yet")

            # Build mixed element, space and argument
            element = ufl.MixedElement(
                [v.function_space().ufl_element() for v in u])
            V = FunctionSpace(mesh, element)
            du = ufl.split(Argument(V, number, part))

        else:
            cpp.dolfin_error("formmanipulation.py",
                             "compute derivative of form w.r.t. '%s'" % u,
                             "Supply Function as a Coefficient")

    return ufl.derivative(form, u, du, coefficient_derivatives)
Beispiel #14
0
def MixedFunctionSpace(spaces):
    """
    Create mixed finite element function space.

    *Arguments*
        spaces
            a list (or tuple) of :py:class:`FunctionSpaces
            <dolfin.functions.functionspace.FunctionSpace>`.

    *Examples of usage*
        The function space may be created by

        .. code-block:: python

            V = MixedFunctionSpace(spaces)

        ``spaces`` may consist of multiple occurances of the same space:

        .. code-block:: python

            P1  = FunctionSpace(mesh, "CG", 1)
            P2v = VectorFunctionSpace(mesh, "Lagrange", 2)

            ME  = MixedFunctionSpace([P2v, P1, P1, P1])

    """
    cpp.deprecation("'MixedFunctionSpace'", "1.7.0", "2.0.0",
                    "Use 'FunctionSpace(mesh, MixedElement(...))'.")

    # Check arguments
    if not len(spaces) > 0:
        cpp.dolfin_error("functionspace.py", "create mixed function space",
                         "Need at least one subspace")
    if not all(isinstance(V, FunctionSpace) for V in spaces):
        cpp.dolfin_error("functionspace.py", "create mixed function space",
                         "Invalid subspaces: " + str(spaces))

    # Get common mesh and constrained_domain, must all be the same
    mesh, constrained_domain = _get_common_mesh_and_constrained_domain(spaces)

    # Create UFL element
    element = ufl.MixedElement(*[V.ufl_element() for V in spaces])

    return FunctionSpace(mesh, element, constrained_domain=constrained_domain)
Beispiel #15
0
def test_monolithic(V1, V2, squaremesh_5):
    mesh = squaremesh_5

    Wel = ufl.MixedElement([V1.ufl_element(), V2.ufl_element()])
    W = dolfinx.FunctionSpace(mesh, Wel)

    u = dolfinx.Function(W)
    u0, u1 = ufl.split(u)

    v = ufl.TestFunction(W)
    v0, v1 = ufl.split(v)

    Phi = (ufl.sin(u0) - 0.5)**2 * ufl.dx(mesh) + (4.0 * u0 -
                                                   u1)**2 * ufl.dx(mesh)

    F = ufl.derivative(Phi, u, v)

    opts = PETSc.Options("monolithic")

    opts.setValue('snes_type', 'newtonls')
    opts.setValue('snes_linesearch_type', 'basic')

    opts.setValue('snes_rtol', 1.0e-10)
    opts.setValue('snes_max_it', 20)

    opts.setValue('ksp_type', 'preonly')
    opts.setValue('pc_type', 'lu')
    opts.setValue('pc_factor_mat_solver_type', 'mumps')

    problem = dolfiny.snesblockproblem.SNESBlockProblem([F], [u],
                                                        prefix="monolithic")
    sol, = problem.solve()

    u0, u1 = sol.split()
    u0 = u0.collapse()
    u1 = u1.collapse()

    assert np.isclose((u0.vector - np.arcsin(0.5)).norm(), 0.0)
    assert np.isclose((u1.vector - 4.0 * np.arcsin(0.5)).norm(), 0.0)
Beispiel #16
0
    def __init__(self, spaces, name=None):
        """
        :param spaces: a list (or tuple) of :class:`FunctionSpace`\s

        The function space may be created as ::

            V = MixedFunctionSpace(spaces)

        ``spaces`` may consist of multiple occurances of the same space: ::

            P1  = FunctionSpace(mesh, "CG", 1)
            P2v = VectorFunctionSpace(mesh, "Lagrange", 2)

            ME  = MixedFunctionSpace([P2v, P1, P1, P1])
        """

        if self._initialized:
            return
        self._spaces = [
            IndexedFunctionSpace(s, i, self)
            for i, s in enumerate(flatten(spaces))
        ]
        self._mesh = self._spaces[0].mesh()
        self._ufl_element = ufl.MixedElement(
            *[fs.ufl_element() for fs in self._spaces])
        self.name = name or '_'.join(str(s.name) for s in self._spaces)
        self.rank = 1
        self._index = None
        self._initialized = True
        dm = PETSc.DMShell().create()
        from firedrake.function import Function
        with Function(self).dat.vec_ro as v:
            dm.setGlobalVector(v.duplicate())
        dm.setAttr('__fs__', weakref.ref(self))
        dm.setCreateFieldDecomposition(self.create_field_decomp)
        dm.setCreateSubDM(self.create_subdm)
        self._dm = dm
        self._ises = self.dof_dset.field_ises
        self._subspaces = []
Beispiel #17
0
    def initialize(self, pc):
        """Set up the problem context. Take the original
        mixed problem and reformulate the problem as a
        hybridized mixed system.

        A KSP is created for the Lagrange multiplier system.
        """
        from firedrake import (FunctionSpace, Function, Constant,
                               TrialFunction, TrialFunctions, TestFunction,
                               DirichletBC, assemble)
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.formmanipulation import split_form
        from ufl.algorithms.replace import replace

        # Extract the problem context
        prefix = pc.getOptionsPrefix() + "hybridization_"
        _, P = pc.getOperators()
        self.cxt = P.getPythonContext()

        if not isinstance(self.cxt, ImplicitMatrixContext):
            raise ValueError("The python context must be an ImplicitMatrixContext")

        test, trial = self.cxt.a.arguments()

        V = test.function_space()
        mesh = V.mesh()

        if len(V) != 2:
            raise ValueError("Expecting two function spaces.")

        if all(Vi.ufl_element().value_shape() for Vi in V):
            raise ValueError("Expecting an H(div) x L2 pair of spaces.")

        # Automagically determine which spaces are vector and scalar
        for i, Vi in enumerate(V):
            if Vi.ufl_element().sobolev_space().name == "HDiv":
                self.vidx = i
            else:
                assert Vi.ufl_element().sobolev_space().name == "L2"
                self.pidx = i

        # Create the space of approximate traces.
        W = V[self.vidx]
        if W.ufl_element().family() == "Brezzi-Douglas-Marini":
            tdegree = W.ufl_element().degree()

        else:
            try:
                # If we have a tensor product element
                h_deg, v_deg = W.ufl_element().degree()
                tdegree = (h_deg - 1, v_deg - 1)

            except TypeError:
                tdegree = W.ufl_element().degree() - 1

        TraceSpace = FunctionSpace(mesh, "HDiv Trace", tdegree)

        # Break the function spaces and define fully discontinuous spaces
        broken_elements = ufl.MixedElement([ufl.BrokenElement(Vi.ufl_element()) for Vi in V])
        V_d = FunctionSpace(mesh, broken_elements)

        # Set up the functions for the original, hybridized
        # and schur complement systems
        self.broken_solution = Function(V_d)
        self.broken_residual = Function(V_d)
        self.trace_solution = Function(TraceSpace)
        self.unbroken_solution = Function(V)
        self.unbroken_residual = Function(V)

        # Set up the KSP for the hdiv residual projection
        hdiv_mass_ksp = PETSc.KSP().create(comm=pc.comm)
        hdiv_mass_ksp.setOptionsPrefix(prefix + "hdiv_residual_")

        # HDiv mass operator
        p = TrialFunction(V[self.vidx])
        q = TestFunction(V[self.vidx])
        mass = ufl.dot(p, q)*ufl.dx
        # TODO: Bcs?
        M = assemble(mass, bcs=None, form_compiler_parameters=self.cxt.fc_params)
        M.force_evaluation()
        Mmat = M.petscmat

        hdiv_mass_ksp.setOperators(Mmat)
        hdiv_mass_ksp.setUp()
        hdiv_mass_ksp.setFromOptions()
        self.hdiv_mass_ksp = hdiv_mass_ksp

        # Storing the result of A.inv * r, where A is the HDiv
        # mass matrix and r is the HDiv residual
        self._primal_r = Function(V[self.vidx])

        tau = TestFunction(V_d[self.vidx])
        self._assemble_broken_r = create_assembly_callable(
            ufl.dot(self._primal_r, tau)*ufl.dx,
            tensor=self.broken_residual.split()[self.vidx],
            form_compiler_parameters=self.cxt.fc_params)

        # Create the symbolic Schur-reduction:
        # Original mixed operator replaced with "broken"
        # arguments
        arg_map = {test: TestFunction(V_d),
                   trial: TrialFunction(V_d)}
        Atilde = Tensor(replace(self.cxt.a, arg_map))
        gammar = TestFunction(TraceSpace)
        n = ufl.FacetNormal(mesh)
        sigma = TrialFunctions(V_d)[self.vidx]

        # We zero out the contribution of the trace variables on the exterior
        # boundary. Extruded cells will have both horizontal and vertical
        # facets
        if mesh.cell_set._extruded:
            trace_bcs = [DirichletBC(TraceSpace, Constant(0.0), "on_boundary"),
                         DirichletBC(TraceSpace, Constant(0.0), "bottom"),
                         DirichletBC(TraceSpace, Constant(0.0), "top")]
            K = Tensor(gammar('+') * ufl.dot(sigma, n) * ufl.dS_h +
                       gammar('+') * ufl.dot(sigma, n) * ufl.dS_v)
        else:
            trace_bcs = [DirichletBC(TraceSpace, Constant(0.0), "on_boundary")]
            K = Tensor(gammar('+') * ufl.dot(sigma, n) * ufl.dS)

        # If boundary conditions are contained in the ImplicitMatrixContext:
        if self.cxt.row_bcs:
            raise NotImplementedError("Strong BCs not currently handled. Try imposing them weakly.")

        # Assemble the Schur complement operator and right-hand side
        self.schur_rhs = Function(TraceSpace)
        self._assemble_Srhs = create_assembly_callable(
            K * Atilde.inv * self.broken_residual,
            tensor=self.schur_rhs,
            form_compiler_parameters=self.cxt.fc_params)

        schur_comp = K * Atilde.inv * K.T
        self.S = allocate_matrix(schur_comp, bcs=trace_bcs,
                                 form_compiler_parameters=self.cxt.fc_params)
        self._assemble_S = create_assembly_callable(schur_comp,
                                                    tensor=self.S,
                                                    bcs=trace_bcs,
                                                    form_compiler_parameters=self.cxt.fc_params)

        self._assemble_S()
        self.S.force_evaluation()
        Smat = self.S.petscmat

        # Nullspace for the multiplier problem
        nullspace = create_schur_nullspace(P, -K * Atilde,
                                           V, V_d, TraceSpace,
                                           pc.comm)
        if nullspace:
            Smat.setNullSpace(nullspace)

        # Set up the KSP for the system of Lagrange multipliers
        trace_ksp = PETSc.KSP().create(comm=pc.comm)
        trace_ksp.setOptionsPrefix(prefix)
        trace_ksp.setOperators(Smat)
        trace_ksp.setUp()
        trace_ksp.setFromOptions()
        self.trace_ksp = trace_ksp

        split_mixed_op = dict(split_form(Atilde.form))
        split_trace_op = dict(split_form(K.form))

        # Generate reconstruction calls
        self._reconstruction_calls(split_mixed_op, split_trace_op)

        # NOTE: The projection stage *might* be replaced by a Fortin
        # operator. We may want to allow the user to specify if they
        # wish to use a Fortin operator over a projection, or vice-versa.
        # In a future add-on, we can add a switch which chooses either
        # the Fortin reconstruction or the usual KSP projection.

        # Set up the projection KSP
        hdiv_projection_ksp = PETSc.KSP().create(comm=pc.comm)
        hdiv_projection_ksp.setOptionsPrefix(prefix + 'hdiv_projection_')

        # Reuse the mass operator from the hdiv_mass_ksp
        hdiv_projection_ksp.setOperators(Mmat)

        # Construct the RHS for the projection stage
        self._projection_rhs = Function(V[self.vidx])
        self._assemble_projection_rhs = create_assembly_callable(
            ufl.dot(self.broken_solution.split()[self.vidx], q)*ufl.dx,
            tensor=self._projection_rhs,
            form_compiler_parameters=self.cxt.fc_params)

        # Finalize ksp setup
        hdiv_projection_ksp.setUp()
        hdiv_projection_ksp.setFromOptions()
        self.hdiv_projection_ksp = hdiv_projection_ksp
Beispiel #18
0
    def initialize(self, pc):
        """Set up the problem context. Take the original
        mixed problem and reformulate the problem as a
        hybridized mixed system.

        A KSP is created for the Lagrange multiplier system.
        """
        from firedrake import (FunctionSpace, Function, Constant,
                               TrialFunction, TrialFunctions, TestFunction,
                               DirichletBC)
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.formmanipulation import split_form
        from ufl.algorithms.replace import replace

        # Extract the problem context
        prefix = pc.getOptionsPrefix() + "hybridization_"
        _, P = pc.getOperators()
        self.ctx = P.getPythonContext()

        if not isinstance(self.ctx, ImplicitMatrixContext):
            raise ValueError(
                "The python context must be an ImplicitMatrixContext")

        test, trial = self.ctx.a.arguments()

        V = test.function_space()
        mesh = V.mesh()

        if len(V) != 2:
            raise ValueError("Expecting two function spaces.")

        if all(Vi.ufl_element().value_shape() for Vi in V):
            raise ValueError("Expecting an H(div) x L2 pair of spaces.")

        # Automagically determine which spaces are vector and scalar
        for i, Vi in enumerate(V):
            if Vi.ufl_element().sobolev_space().name == "HDiv":
                self.vidx = i
            else:
                assert Vi.ufl_element().sobolev_space().name == "L2"
                self.pidx = i

        # Create the space of approximate traces.
        W = V[self.vidx]
        if W.ufl_element().family() == "Brezzi-Douglas-Marini":
            tdegree = W.ufl_element().degree()

        else:
            try:
                # If we have a tensor product element
                h_deg, v_deg = W.ufl_element().degree()
                tdegree = (h_deg - 1, v_deg - 1)

            except TypeError:
                tdegree = W.ufl_element().degree() - 1

        TraceSpace = FunctionSpace(mesh, "HDiv Trace", tdegree)

        # Break the function spaces and define fully discontinuous spaces
        broken_elements = ufl.MixedElement(
            [ufl.BrokenElement(Vi.ufl_element()) for Vi in V])
        V_d = FunctionSpace(mesh, broken_elements)

        # Set up the functions for the original, hybridized
        # and schur complement systems
        self.broken_solution = Function(V_d)
        self.broken_residual = Function(V_d)
        self.trace_solution = Function(TraceSpace)
        self.unbroken_solution = Function(V)
        self.unbroken_residual = Function(V)

        shapes = (V[self.vidx].finat_element.space_dimension(),
                  np.prod(V[self.vidx].shape))
        domain = "{[i,j]: 0 <= i < %d and 0 <= j < %d}" % shapes
        instructions = """
        for i, j
            w[i,j] = w[i,j] + 1
        end
        """
        self.weight = Function(V[self.vidx])
        par_loop((domain, instructions),
                 ufl.dx, {"w": (self.weight, INC)},
                 is_loopy_kernel=True)

        instructions = """
        for i, j
            vec_out[i,j] = vec_out[i,j] + vec_in[i,j]/w[i,j]
        end
        """
        self.average_kernel = (domain, instructions)

        # Create the symbolic Schur-reduction:
        # Original mixed operator replaced with "broken"
        # arguments
        arg_map = {test: TestFunction(V_d), trial: TrialFunction(V_d)}
        Atilde = Tensor(replace(self.ctx.a, arg_map))
        gammar = TestFunction(TraceSpace)
        n = ufl.FacetNormal(mesh)
        sigma = TrialFunctions(V_d)[self.vidx]

        if mesh.cell_set._extruded:
            Kform = (gammar('+') * ufl.jump(sigma, n=n) * ufl.dS_h +
                     gammar('+') * ufl.jump(sigma, n=n) * ufl.dS_v)
        else:
            Kform = (gammar('+') * ufl.jump(sigma, n=n) * ufl.dS)

        # Here we deal with boundaries. If there are Neumann
        # conditions (which should be enforced strongly for
        # H(div)xL^2) then we need to add jump terms on the exterior
        # facets. If there are Dirichlet conditions (which should be
        # enforced weakly) then we need to zero out the trace
        # variables there as they are not active (otherwise the hybrid
        # problem is not well-posed).

        # If boundary conditions are contained in the ImplicitMatrixContext:
        if self.ctx.row_bcs:
            # Find all the subdomains with neumann BCS
            # These are Dirichlet BCs on the vidx space
            neumann_subdomains = set()
            for bc in self.ctx.row_bcs:
                if bc.function_space().index == self.pidx:
                    raise NotImplementedError(
                        "Dirichlet conditions for scalar variable not supported. Use a weak bc"
                    )
                if bc.function_space().index != self.vidx:
                    raise NotImplementedError(
                        "Dirichlet bc set on unsupported space.")
                # append the set of sub domains
                subdom = bc.sub_domain
                if isinstance(subdom, str):
                    neumann_subdomains |= set([subdom])
                else:
                    neumann_subdomains |= set(
                        as_tuple(subdom, numbers.Integral))

            # separate out the top and bottom bcs
            extruded_neumann_subdomains = neumann_subdomains & {
                "top", "bottom"
            }
            neumann_subdomains = neumann_subdomains - extruded_neumann_subdomains

            integrand = gammar * ufl.dot(sigma, n)
            measures = []
            trace_subdomains = []
            if mesh.cell_set._extruded:
                ds = ufl.ds_v
                for subdomain in sorted(extruded_neumann_subdomains):
                    measures.append({
                        "top": ufl.ds_t,
                        "bottom": ufl.ds_b
                    }[subdomain])
                trace_subdomains.extend(
                    sorted({"top", "bottom"} - extruded_neumann_subdomains))
            else:
                ds = ufl.ds
            if "on_boundary" in neumann_subdomains:
                measures.append(ds)
            else:
                measures.extend((ds(sd) for sd in sorted(neumann_subdomains)))
                markers = [int(x) for x in mesh.exterior_facets.unique_markers]
                dirichlet_subdomains = set(markers) - neumann_subdomains
                trace_subdomains.extend(sorted(dirichlet_subdomains))

            for measure in measures:
                Kform += integrand * measure

            trace_bcs = [
                DirichletBC(TraceSpace, Constant(0.0), subdomain)
                for subdomain in trace_subdomains
            ]

        else:
            # No bcs were provided, we assume weak Dirichlet conditions.
            # We zero out the contribution of the trace variables on
            # the exterior boundary. Extruded cells will have both
            # horizontal and vertical facets
            trace_subdomains = ["on_boundary"]
            if mesh.cell_set._extruded:
                trace_subdomains.extend(["bottom", "top"])
            trace_bcs = [
                DirichletBC(TraceSpace, Constant(0.0), subdomain)
                for subdomain in trace_subdomains
            ]

        # Make a SLATE tensor from Kform
        K = Tensor(Kform)

        # Assemble the Schur complement operator and right-hand side
        self.schur_rhs = Function(TraceSpace)
        self._assemble_Srhs = create_assembly_callable(
            K * Atilde.inv * AssembledVector(self.broken_residual),
            tensor=self.schur_rhs,
            form_compiler_parameters=self.ctx.fc_params)

        mat_type = PETSc.Options().getString(prefix + "mat_type", "aij")

        schur_comp = K * Atilde.inv * K.T
        self.S = allocate_matrix(schur_comp,
                                 bcs=trace_bcs,
                                 form_compiler_parameters=self.ctx.fc_params,
                                 mat_type=mat_type,
                                 options_prefix=prefix)
        self._assemble_S = create_assembly_callable(
            schur_comp,
            tensor=self.S,
            bcs=trace_bcs,
            form_compiler_parameters=self.ctx.fc_params,
            mat_type=mat_type)

        with timed_region("HybridOperatorAssembly"):
            self._assemble_S()

        Smat = self.S.petscmat

        nullspace = self.ctx.appctx.get("trace_nullspace", None)
        if nullspace is not None:
            nsp = nullspace(TraceSpace)
            Smat.setNullSpace(nsp.nullspace(comm=pc.comm))

        # Set up the KSP for the system of Lagrange multipliers
        trace_ksp = PETSc.KSP().create(comm=pc.comm)
        trace_ksp.setOptionsPrefix(prefix)
        trace_ksp.setOperators(Smat)
        trace_ksp.setUp()
        trace_ksp.setFromOptions()
        self.trace_ksp = trace_ksp

        split_mixed_op = dict(split_form(Atilde.form))
        split_trace_op = dict(split_form(K.form))

        # Generate reconstruction calls
        self._reconstruction_calls(split_mixed_op, split_trace_op)
import ufl
from firedrake import *

mesh = UnitSquareMesh(1, 1)

Vhat = FunctionSpace(mesh, 'CG', 2)
broken_elements = ufl.MixedElement(
    [ufl.BrokenElement(Vi.ufl_element()) for Vi in Vhat])
V = FunctionSpace(mesh, broken_elements)

u = TrialFunction(V)
v = TestFunction(V)

mass = inner(u, v) * dx
breakpoint()
M = assemble(mass).M
Beispiel #20
0
def white_noise(V, rng):
    '''
    '''
    # Create broken space for independent samples
    mesh = V.mesh()
    broken_elements = ufl.MixedElement(
        [ufl.BrokenElement(Vi.ufl_element()) for Vi in V])
    Vbrok = FunctionSpace(mesh, broken_elements)
    iid_normal = rng.normal(Vbrok, 0.0, 1.0)
    wnoise = Function(V)

    # Create mass expression, assemble and extract kernel
    u = TrialFunction(V)
    v = TestFunction(V)
    mass = inner(u, v) * dx
    mass_ker, *stuff = tsfc_interface.compile_form(mass, "mass", coffee=False)
    mass_code = loopy.generate_code_v2(
        mass_ker.kinfo.kernel.code).device_code()
    mass_code = mass_code.replace("void " + mass_ker.kinfo.kernel.name,
                                  "static void " + mass_ker.kinfo.kernel.name)

    # Add custom code for doing "Cholesky" decomp and applying to broken vector
    blocksize = mass_ker.kinfo.kernel.code.args[0].shape[0]

    cholesky_code = f"""
extern void dpotrf_(char *UPLO,
                    int *N,
                    double *A,
                    int *LDA,
                    int *INFO);

extern void dgemv_(char *TRANS,
                   int *M,
                   int *N,
                   double *ALPHA,
                   double *A,
                   int *LDA,
                   double *X,
                   int *INCX,
                   double *BETA,
                   double *Y,
                   int *INCY);

{mass_code}

void apply_cholesky(double *__restrict__ z,
                    double *__restrict__ b,
                    double const *__restrict__ coords)
{{
    char uplo[1];
    int32_t N = {blocksize}, LDA = {blocksize}, INFO = 0;
    int32_t i=0, j=0;
    uplo[0] = 'u';
    double H[{blocksize}*{blocksize}] = {{{{ 0.0 }}}};

    char trans[1];
    int32_t stride = 1;
    double one = 1.0;
    double zero = 0.0;

    {mass_ker.kinfo.kernel.name}(H, coords);

    uplo[0] = 'u';
    dpotrf_(uplo, &N, H, &LDA, &INFO);
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            if (j>i)
                H[i*N + j] = 0.0;

    trans[0] = 'T';
    dgemv_(trans, &N, &N, &one, H, &LDA, z, &stride, &zero, b, &stride);
}}
"""

    cholesky_kernel = op2.Kernel(cholesky_code,
                                 "apply_cholesky",
                                 ldargs=["-llapack", "-lblas"])

    # Construct arguments for par loop
    def get_map(x):
        return x.cell_node_map()

    i, _ = mass_ker.indices

    z_arg = vector_arg(op2.READ, get_map, i, function=iid_normal, V=Vbrok)
    b_arg = vector_arg(op2.INC, get_map, i, function=wnoise, V=V)
    coords = mesh.coordinates

    op2.par_loop(cholesky_kernel, mesh.cell_set, z_arg, b_arg,
                 coords.dat(op2.READ, get_map(coords)))

    return wnoise
Beispiel #21
0
def white_noise(V, rng=None):
    '''
    '''
    # If no random number generator provided make a new one
    if rng is None:
        pcg = _default_pcg
        rng = RandomGenerator(pcg)

    # Create broken space for independent samples
    mesh = V.mesh()
    broken_elements = ufl.MixedElement([ufl.BrokenElement(Vi.ufl_element()) for Vi in V])
    Vbrok = FunctionSpace(mesh, broken_elements)
    iid_normal = rng.normal(Vbrok, 0.0, 1.0)
    wnoise = Function(V)

    # We also need cell volumes for correction
    DG0 = FunctionSpace(mesh, 'DG', 0)
    vol = Function(DG0)
    vol.interpolate(CellVolume(mesh))

    # Create mass expression, assemble and extract kernel
    u = TrialFunction(V)
    v = TestFunction(V)
    mass = inner(u,v)*dx
    mass_ker, *stuff = tsfc_interface.compile_form(mass, "mass", coffee=False)
    mass_code = loopy.generate_code_v2(mass_ker.kinfo.kernel.code).device_code()
    mass_code = mass_code.replace("void " + mass_ker.kinfo.kernel.name,
                                  "static void " + mass_ker.kinfo.kernel.name)

    # Add custom code for doing "Cholesky" decomp and applying to broken vector
    blocksize = mass_ker.kinfo.kernel.code.args[0].shape[0]

    cholesky_code = f"""
extern void dpotrf_(char *UPLO,
                    int *N,
                    double *A,
                    int *LDA,
                    int *INFO);

extern void dgemv_(char *TRANS,
                   int *M,
                   int *N,
                   double *ALPHA,
                   double *A,
                   int *LDA,
                   double *X,
                   int *INCX,
                   double *BETA,
                   double *Y,
                   int *INCY);

{mass_code}

void apply_cholesky(double *__restrict__ z,
                    double *__restrict__ b,
                    double const *__restrict__ coords,
                    double const *__restrict__ volume)
{{
    char uplo[1];
    int32_t N = {blocksize}, LDA = {blocksize}, INFO = 0;
    int32_t i=0, j=0;
    uplo[0] = 'u';
    double H[{blocksize}*{blocksize}] = {{{{ 0.0 }}}};

    char trans[1];
    int32_t stride = 1;
    //double one = 1.0;
    double scale = 1.0/volume[0];
    double zero = 0.0;

    {mass_ker.kinfo.kernel.name}(H, coords);

    uplo[0] = 'u';
    dpotrf_(uplo, &N, H, &LDA, &INFO);
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            if (j>i)
                H[i*N + j] = 0.0;

    trans[0] = 'T';
    dgemv_(trans, &N, &N, &scale, H, &LDA, z, &stride, &zero, b, &stride);
}}
"""
    # Get the BLAS and LAPACK compiler parameters to compile the kernel
    if COMM_WORLD.rank == 0:
        petsc_variables = get_petsc_variables()
        BLASLAPACK_LIB = petsc_variables.get("BLASLAPACK_LIB", "")
        BLASLAPACK_LIB = COMM_WORLD.bcast(BLASLAPACK_LIB, root=0)
        BLASLAPACK_INCLUDE = petsc_variables.get("BLASLAPACK_INCLUDE", "")
        BLASLAPACK_INCLUDE = COMM_WORLD.bcast(BLASLAPACK_INCLUDE, root=0)
    else:
        BLASLAPACK_LIB = COMM_WORLD.bcast(None, root=0)
        BLASLAPACK_INCLUDE = COMM_WORLD.bcast(None, root=0)

    cholesky_kernel = op2.Kernel(cholesky_code,
                                 "apply_cholesky",
                                 include_dirs=BLASLAPACK_INCLUDE.split(),
                                 ldargs=BLASLAPACK_LIB.split())

    # Construct arguments for par loop
    def get_map(x):
        return x.cell_node_map()
    i, _ = mass_ker.indices

    z_arg = vector_arg(op2.READ, get_map, i, function=iid_normal, V=Vbrok)
    b_arg = vector_arg(op2.INC, get_map, i, function=wnoise, V=V)
    coords = mesh.coordinates
    volumes = vector_arg(op2.READ, get_map, i, function=vol, V=DG0)

    op2.par_loop(cholesky_kernel,
                 mesh.cell_set,
                 z_arg,
                 b_arg,
                 coords.dat(op2.READ, get_map(coords)),
                 volumes)

    return wnoise
Beispiel #22
0
    def initialize(self, pc):
        """Set up the problem context. Take the original
        mixed problem and reformulate the problem as a
        hybridized mixed system.

        A KSP is created for the Lagrange multiplier system.
        """
        from firedrake import (FunctionSpace, Function, Constant,
                               TrialFunction, TrialFunctions, TestFunction,
                               DirichletBC, assemble)
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.formmanipulation import split_form
        from ufl.algorithms.replace import replace

        # Extract the problem context
        prefix = pc.getOptionsPrefix() + "hybridization_"
        _, P = pc.getOperators()
        self.cxt = P.getPythonContext()

        if not isinstance(self.cxt, ImplicitMatrixContext):
            raise ValueError("The python context must be an ImplicitMatrixContext")

        test, trial = self.cxt.a.arguments()

        V = test.function_space()
        mesh = V.mesh()

        if len(V) != 2:
            raise ValueError("Expecting two function spaces.")

        if all(Vi.ufl_element().value_shape() for Vi in V):
            raise ValueError("Expecting an H(div) x L2 pair of spaces.")

        # Automagically determine which spaces are vector and scalar
        for i, Vi in enumerate(V):
            if Vi.ufl_element().sobolev_space().name == "HDiv":
                self.vidx = i
            else:
                assert Vi.ufl_element().sobolev_space().name == "L2"
                self.pidx = i

        # Create the space of approximate traces.
        W = V[self.vidx]
        if W.ufl_element().family() == "Brezzi-Douglas-Marini":
            tdegree = W.ufl_element().degree()

        else:
            try:
                # If we have a tensor product element
                h_deg, v_deg = W.ufl_element().degree()
                tdegree = (h_deg - 1, v_deg - 1)

            except TypeError:
                tdegree = W.ufl_element().degree() - 1

        TraceSpace = FunctionSpace(mesh, "HDiv Trace", tdegree)

        # Break the function spaces and define fully discontinuous spaces
        broken_elements = ufl.MixedElement([ufl.BrokenElement(Vi.ufl_element()) for Vi in V])
        V_d = FunctionSpace(mesh, broken_elements)

        # Set up the functions for the original, hybridized
        # and schur complement systems
        self.broken_solution = Function(V_d)
        self.broken_residual = Function(V_d)
        self.trace_solution = Function(TraceSpace)
        self.unbroken_solution = Function(V)
        self.unbroken_residual = Function(V)

        # Set up the KSP for the hdiv residual projection
        hdiv_mass_ksp = PETSc.KSP().create(comm=pc.comm)
        hdiv_mass_ksp.setOptionsPrefix(prefix + "hdiv_residual_")

        # HDiv mass operator
        p = TrialFunction(V[self.vidx])
        q = TestFunction(V[self.vidx])
        mass = ufl.dot(p, q)*ufl.dx
        # TODO: Bcs?
        M = assemble(mass, bcs=None, form_compiler_parameters=self.cxt.fc_params)
        M.force_evaluation()
        Mmat = M.petscmat

        hdiv_mass_ksp.setOperators(Mmat)
        hdiv_mass_ksp.setUp()
        hdiv_mass_ksp.setFromOptions()
        self.hdiv_mass_ksp = hdiv_mass_ksp

        # Storing the result of A.inv * r, where A is the HDiv
        # mass matrix and r is the HDiv residual
        self._primal_r = Function(V[self.vidx])

        tau = TestFunction(V_d[self.vidx])
        self._assemble_broken_r = create_assembly_callable(
            ufl.dot(self._primal_r, tau)*ufl.dx,
            tensor=self.broken_residual.split()[self.vidx],
            form_compiler_parameters=self.cxt.fc_params)

        # Create the symbolic Schur-reduction:
        # Original mixed operator replaced with "broken"
        # arguments
        arg_map = {test: TestFunction(V_d),
                   trial: TrialFunction(V_d)}
        Atilde = Tensor(replace(self.cxt.a, arg_map))
        gammar = TestFunction(TraceSpace)
        n = ufl.FacetNormal(mesh)
        sigma = TrialFunctions(V_d)[self.vidx]

        if mesh.cell_set._extruded:
            Kform = (gammar('+') * ufl.dot(sigma, n) * ufl.dS_h +
                     gammar('+') * ufl.dot(sigma, n) * ufl.dS_v)
        else:
            Kform = (gammar('+') * ufl.dot(sigma, n) * ufl.dS)

        # Here we deal with boundaries. If there are Neumann
        # conditions (which should be enforced strongly for
        # H(div)xL^2) then we need to add jump terms on the exterior
        # facets. If there are Dirichlet conditions (which should be
        # enforced weakly) then we need to zero out the trace
        # variables there as they are not active (otherwise the hybrid
        # problem is not well-posed).

        # If boundary conditions are contained in the ImplicitMatrixContext:
        if self.cxt.row_bcs:
            # Find all the subdomains with neumann BCS
            # These are Dirichlet BCs on the vidx space
            neumann_subdomains = set()
            for bc in self.cxt.row_bcs:
                if bc.function_space().index == self.pidx:
                    raise NotImplementedError("Dirichlet conditions for scalar variable not supported. Use a weak bc")
                if bc.function_space().index != self.vidx:
                    raise NotImplementedError("Dirichlet bc set on unsupported space.")
                # append the set of sub domains
                subdom = bc.sub_domain
                if isinstance(subdom, str):
                    neumann_subdomains |= set([subdom])
                else:
                    neumann_subdomains |= set(as_tuple(subdom, int))

            # separate out the top and bottom bcs
            extruded_neumann_subdomains = neumann_subdomains & {"top", "bottom"}
            neumann_subdomains = neumann_subdomains.difference(extruded_neumann_subdomains)

            integrand = gammar * ufl.dot(sigma, n)
            measures = []
            trace_subdomains = []
            if mesh.cell_set._extruded:
                ds = ufl.ds_v
                for subdomain in extruded_neumann_subdomains:
                    measures.append({"top": ufl.ds_t, "bottom": ufl.ds_b}[subdomain])
                trace_subdomains.extend(sorted({"top", "bottom"} - extruded_neumann_subdomains))
            else:
                ds = ufl.ds
            if "on_boundary" in neumann_subdomains:
                measures.append(ds)
            else:
                measures.append(ds(tuple(neumann_subdomains)))
                dirichlet_subdomains = set(mesh.exterior_facets.unique_markers) - neumann_subdomains
                trace_subdomains.append(sorted(dirichlet_subdomains))

            for measure in measures:
                Kform += integrand*measure

            trace_bcs = [DirichletBC(TraceSpace, Constant(0.0), subdomain) for subdomain in trace_subdomains]

        else:
            # No bcs were provided, we assume weak Dirichlet conditions.
            # We zero out the contribution of the trace variables on
            # the exterior boundary. Extruded cells will have both
            # horizontal and vertical facets
            trace_subdomains = ["on_boundary"]
            if mesh.cell_set._extruded:
                trace_subdomains.extend(["bottom", "top"])
            trace_bcs = [DirichletBC(TraceSpace, Constant(0.0), subdomain) for subdomain in trace_subdomains]

        # Make a SLATE tensor from Kform
        K = Tensor(Kform)

        # Assemble the Schur complement operator and right-hand side
        self.schur_rhs = Function(TraceSpace)
        self._assemble_Srhs = create_assembly_callable(
            K * Atilde.inv * AssembledVector(self.broken_residual),
            tensor=self.schur_rhs,
            form_compiler_parameters=self.cxt.fc_params)

        schur_comp = K * Atilde.inv * K.T
        self.S = allocate_matrix(schur_comp, bcs=trace_bcs,
                                 form_compiler_parameters=self.cxt.fc_params)
        self._assemble_S = create_assembly_callable(schur_comp,
                                                    tensor=self.S,
                                                    bcs=trace_bcs,
                                                    form_compiler_parameters=self.cxt.fc_params)

        self._assemble_S()
        self.S.force_evaluation()
        Smat = self.S.petscmat

        # Nullspace for the multiplier problem
        nullspace = create_schur_nullspace(P, -K * Atilde,
                                           V, V_d, TraceSpace,
                                           pc.comm)
        if nullspace:
            Smat.setNullSpace(nullspace)

        # Set up the KSP for the system of Lagrange multipliers
        trace_ksp = PETSc.KSP().create(comm=pc.comm)
        trace_ksp.setOptionsPrefix(prefix)
        trace_ksp.setOperators(Smat)
        trace_ksp.setUp()
        trace_ksp.setFromOptions()
        self.trace_ksp = trace_ksp

        split_mixed_op = dict(split_form(Atilde.form))
        split_trace_op = dict(split_form(K.form))

        # Generate reconstruction calls
        self._reconstruction_calls(split_mixed_op, split_trace_op)

        # NOTE: The projection stage *might* be replaced by a Fortin
        # operator. We may want to allow the user to specify if they
        # wish to use a Fortin operator over a projection, or vice-versa.
        # In a future add-on, we can add a switch which chooses either
        # the Fortin reconstruction or the usual KSP projection.

        # Set up the projection KSP
        hdiv_projection_ksp = PETSc.KSP().create(comm=pc.comm)
        hdiv_projection_ksp.setOptionsPrefix(prefix + 'hdiv_projection_')

        # Reuse the mass operator from the hdiv_mass_ksp
        hdiv_projection_ksp.setOperators(Mmat)

        # Construct the RHS for the projection stage
        self._projection_rhs = Function(V[self.vidx])
        self._assemble_projection_rhs = create_assembly_callable(
            ufl.dot(self.broken_solution.split()[self.vidx], q)*ufl.dx,
            tensor=self._projection_rhs,
            form_compiler_parameters=self.cxt.fc_params)

        # Finalize ksp setup
        hdiv_projection_ksp.setUp()
        hdiv_projection_ksp.setFromOptions()
        self.hdiv_projection_ksp = hdiv_projection_ksp
Beispiel #23
0
              encoding=XDMFFile.Encoding.HDF5) as file:
    file.write_mesh(mesh)

# Function spaces
r=1
# r=2
# r=3

# x_extents = mesh_bounding_box(mesh, 0)
# y_extents = mesh_bounding_box(mesh, 1)

# Measures
dx = ufl.Measure("dx", domain=mesh)
ds = ufl.Measure("ds", domain=mesh)

element = ufl.MixedElement([ufl.FiniteElement("Regge", ufl.triangle, r),
                            ufl.FiniteElement("Lagrange", ufl.triangle, r+1)])

V = FunctionSpace(mesh, element)
V_1 = V.sub(1).collapse()

sigma, u = ufl.TrialFunctions(V)
tau, v = ufl.TestFunctions(V)
def S(tau):
    return tau - ufl.Identity(2) * ufl.tr(tau)

# Discrete duality inner product 
# cf. eq. 4.5 Lizao Li's PhD thesis

def b(tau_S, v):
    n = FacetNormal(mesh)
    return inner(tau_S, grad(grad(v))) * dx \
Beispiel #24
0
def test_biharmonic():
    """Manufactured biharmonic problem.

    Solved using rotated Regge mixed finite element method. This is equivalent
    to the Hellan-Herrmann-Johnson (HHJ) finite element method in
    two-dimensions."""
    mesh = RectangleMesh(MPI.COMM_WORLD, [np.array([0.0, 0.0, 0.0]),
                                          np.array([1.0, 1.0, 0.0])], [32, 32], CellType.triangle)

    element = ufl.MixedElement([ufl.FiniteElement("Regge", ufl.triangle, 1),
                                ufl.FiniteElement("Lagrange", ufl.triangle, 2)])

    V = FunctionSpace(mesh, element)
    sigma, u = ufl.TrialFunctions(V)
    tau, v = ufl.TestFunctions(V)

    x = ufl.SpatialCoordinate(mesh)
    u_exact = ufl.sin(ufl.pi * x[0]) * ufl.sin(ufl.pi * x[0]) * ufl.sin(ufl.pi * x[1]) * ufl.sin(ufl.pi * x[1])
    f_exact = div(grad(div(grad(u_exact))))
    sigma_exact = grad(grad(u_exact))

    # sigma and tau are tangential-tangential continuous according to the
    # H(curl curl) continuity of the Regge space. However, for the biharmonic
    # problem we require normal-normal continuity H (div div). Theorem 4.2 of
    # Lizao Li's PhD thesis shows that the latter space can be constructed by
    # the former through the action of the operator S:
    def S(tau):
        return tau - ufl.Identity(2) * ufl.tr(tau)

    sigma_S = S(sigma)
    tau_S = S(tau)

    # Discrete duality inner product eq. 4.5 Lizao Li's PhD thesis
    def b(tau_S, v):
        n = FacetNormal(mesh)
        return inner(tau_S, grad(grad(v))) * dx \
            - ufl.dot(ufl.dot(tau_S('+'), n('+')), n('+')) * jump(grad(v), n) * dS \
            - ufl.dot(ufl.dot(tau_S, n), n) * ufl.dot(grad(v), n) * ds

    # Non-symmetric formulation
    a = inner(sigma_S, tau_S) * dx - b(tau_S, u) + b(sigma_S, v)
    L = inner(f_exact, v) * dx

    V_1 = V.sub(1).collapse()
    zero_u = Function(V_1)
    with zero_u.vector.localForm() as zero_u_local:
        zero_u_local.set(0.0)

    # Strong (Dirichlet) boundary condition
    boundary_facets = locate_entities_boundary(
        mesh, mesh.topology.dim - 1, lambda x: np.full(x.shape[1], True, dtype=bool))
    boundary_dofs = locate_dofs_topological((V.sub(1), V_1), mesh.topology.dim - 1, boundary_facets)

    bcs = [DirichletBC(zero_u, boundary_dofs, V.sub(1))]

    A = assemble_matrix(a, bcs=bcs)
    A.assemble()
    b = assemble_vector(L)
    apply_lifting(b, [a], [bcs])
    b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)

    # Solve
    solver = PETSc.KSP().create(MPI.COMM_WORLD)
    PETSc.Options()["ksp_type"] = "preonly"
    PETSc.Options()["pc_type"] = "lu"
    # PETSc.Options()["pc_factor_mat_solver_type"] = "mumps"
    solver.setFromOptions()
    solver.setOperators(A)

    x_h = Function(V)
    solver.solve(b, x_h.vector)
    x_h.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                           mode=PETSc.ScatterMode.FORWARD)

    # Recall that x_h has flattened indices.
    u_error_numerator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar(
        inner(u_exact - x_h[4], u_exact - x_h[4]) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM))
    u_error_denominator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar(
        inner(u_exact, u_exact) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM))

    assert(np.absolute(u_error_numerator / u_error_denominator) < 0.05)

    # Reconstruct tensor from flattened indices.
    # Apply inverse transform. In 2D we have S^{-1} = S.
    sigma_h = S(ufl.as_tensor([[x_h[0], x_h[1]], [x_h[2], x_h[3]]]))
    sigma_error_numerator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar(
        inner(sigma_exact - sigma_h, sigma_exact - sigma_h) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM))
    sigma_error_denominator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar(
        inner(sigma_exact, sigma_exact) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM))

    assert(np.absolute(sigma_error_numerator / sigma_error_denominator) < 0.005)