Beispiel #1
0
def test_matrix_assembly_block(mode):
    """Test assembly of block matrices and vectors into (a) monolithic
    blocked structures, PETSc Nest structures, and monolithic structures.
    """
    mesh = UnitSquareMesh(MPI.COMM_WORLD, 4, 8, ghost_mode=mode)

    p0, p1 = 1, 2
    P0 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p0)
    P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p1)

    V0 = dolfinx.function.FunctionSpace(mesh, P0)
    V1 = dolfinx.function.FunctionSpace(mesh, P1)

    def boundary(x):
        return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6)

    # Locate facets on boundary
    facetdim = mesh.topology.dim - 1
    bndry_facets = dolfinx.mesh.locate_entities_boundary(mesh, facetdim, boundary)

    bdofsV1 = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets)

    u_bc = dolfinx.function.Function(V1)
    with u_bc.vector.localForm() as u_local:
        u_local.set(50.0)
    bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofsV1)

    # Define variational problem
    u, p = ufl.TrialFunction(V0), ufl.TrialFunction(V1)
    v, q = ufl.TestFunction(V0), ufl.TestFunction(V1)
    f = 1.0
    g = -3.0
    zero = dolfinx.Function(V0)

    a00 = inner(u, v) * dx
    a01 = inner(p, v) * dx
    a10 = inner(u, q) * dx
    a11 = inner(p, q) * dx

    L0 = zero * inner(f, v) * dx
    L1 = inner(g, q) * dx

    a_block = [[a00, a01], [a10, a11]]
    L_block = [L0, L1]

    # Monolithic blocked
    A0 = dolfinx.fem.assemble_matrix_block(a_block, [bc])
    A0.assemble()
    b0 = dolfinx.fem.assemble_vector_block(L_block, a_block, [bc])
    assert A0.getType() != "nest"
    Anorm0 = A0.norm()
    bnorm0 = b0.norm()

    # Nested (MatNest)
    A1 = dolfinx.fem.assemble_matrix_nest(a_block, [bc])
    A1.assemble()
    Anorm1 = nest_matrix_norm(A1)
    assert Anorm0 == pytest.approx(Anorm1, 1.0e-12)

    b1 = dolfinx.fem.assemble_vector_nest(L_block)
    dolfinx.fem.apply_lifting_nest(b1, a_block, [bc])
    for b_sub in b1.getNestSubVecs():
        b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    bcs0 = dolfinx.cpp.fem.bcs_rows(dolfinx.fem.assemble._create_cpp_form(L_block), [bc])
    dolfinx.fem.set_bc_nest(b1, bcs0)
    b1.assemble()

    bnorm1 = math.sqrt(sum([x.norm()**2 for x in b1.getNestSubVecs()]))
    assert bnorm0 == pytest.approx(bnorm1, 1.0e-12)

    # Monolithic version
    E = P0 * P1
    W = dolfinx.function.FunctionSpace(mesh, E)
    u0, u1 = ufl.TrialFunctions(W)
    v0, v1 = ufl.TestFunctions(W)
    a = inner(u0, v0) * dx + inner(u1, v1) * dx + inner(u0, v1) * dx + inner(
        u1, v0) * dx
    L = zero * inner(f, v0) * ufl.dx + inner(g, v1) * dx

    bdofsW_V1 = dolfinx.fem.locate_dofs_topological((W.sub(1), V1), mesh.topology.dim - 1, bndry_facets)

    bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofsW_V1, W.sub(1))
    A2 = dolfinx.fem.assemble_matrix(a, [bc])
    A2.assemble()
    b2 = dolfinx.fem.assemble_vector(L)
    dolfinx.fem.apply_lifting(b2, [a], [[bc]])
    b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    dolfinx.fem.set_bc(b2, [bc])
    assert A2.getType() != "nest"
    assert A2.norm() == pytest.approx(Anorm0, 1.0e-9)
    assert b2.norm() == pytest.approx(bnorm0, 1.0e-9)
Beispiel #2
0
                 subdomain_data=subdomains,
                 metadata={"quadrature_degree": 4})
ds = ufl.Measure("ds",
                 domain=mesh,
                 subdomain_data=interfaces,
                 metadata={"quadrature_degree": 4})


# Inner ring velocity
def v_inner_(t=0.0, vt=v_inner_max, g=5, a=1, b=3):
    return vt * 0.25 * (np.tanh(g * (t - a)) + 1) * (np.tanh(-g * (t - b)) + 1)


# Function spaces
Ve = ufl.VectorElement("CG", mesh.ufl_cell(), 2)
Pe = ufl.FiniteElement("CG", mesh.ufl_cell(), 1)
Le = ufl.FiniteElement("CG", mesh.ufl_cell(), 2)

V = dolfinx.fem.FunctionSpace(mesh, Ve)
P = dolfinx.fem.FunctionSpace(mesh, Pe)
N = dolfinx.fem.FunctionSpace(mesh, Le)
T = dolfinx.fem.FunctionSpace(mesh, Le)

# Define functions
v = dolfinx.fem.Function(V, name="v")
p = dolfinx.fem.Function(P, name="p")
n = dolfinx.fem.Function(N, name="n")
t = dolfinx.fem.Function(T, name="t")

vt = dolfinx.fem.Function(V, name="vt")
pt = dolfinx.fem.Function(P, name="pt")
Beispiel #3
0
def test_quadrilateral_variant_spectral_q():
    element = create_element(
        ufl.FiniteElement('Q', ufl.quadrilateral, 3, variant='spectral'))
    assert isinstance(element.element.A, FIAT.GaussLobattoLegendre)
    assert isinstance(element.element.B, FIAT.GaussLobattoLegendre)
def test_quadrilateral_variant_spectral_dq():
    element = create_element(
        ufl.FiniteElement('DQ', ufl.quadrilateral, 1, variant='spectral'))
    assert isinstance(element.product.factors[0], finat.GaussLegendre)
    assert isinstance(element.product.factors[1], finat.GaussLegendre)
Beispiel #5
0
#In order to define the vec(function_forme), the ufl library is used.

#A VectorElement represents a combination of basic elements such that each
#component of a vector is represented by the basic element. The size is usually
#omitted, the default size equals the geometry dimension.

#ulf.VectorElement(<Type of the element>, <Geometry of the element>,
#degree=<Degree of element: 1 - Linear, 2 - Quadratic, etc.>, dim= <Target
#dimension of the element: 1 - Line, 2 - Area, 3 - Volume>)

#Lagrange is a familly type of elements -> polynomial functions of forme;
#The Lagrange elements are going to be defined in the mesh as such we take the
#geometry of elements present in the mesh.

element_u = ufl.VectorElement("Lagrange", mesh.ufl_cell(), degree=1, dim=2)
element_alpha = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), degree=1)

#After defining the Finite Element in ufl, a association with dolfinx is made.
#To inputs are necessary, the mesh and the element type created. In some sense,
#we obtain the "discretised model w/ elements definied".
V_u = dolfinx.fem.FunctionSpace(mesh, element_u)
V_alpha = dolfinx.fem.FunctionSpace(mesh, element_alpha)

#In this model, we also defines functions necessaries to solve the problem.
#This functions are definied in the entire space/model.
u = dolfinx.fem.Function(V_u,
                         name="Displacement")  #The discrete nodal valeus of
#the displacement
u_ = dolfinx.fem.Function(V_u, name="BC_Displacement")
u_imposed = dolfinx.fem.Function(V_u, name="Imposed_Displacement")
alpha = dolfinx.fem.Function(V_alpha, name="Damage")
def ufl_A(request, tensor_name):
    return ufl.FiniteElement(tensor_name, request.param, 1)
def test_interval_variant(family, variant, expected_cls):
    ufl_element = ufl.FiniteElement(family, ufl.interval, 3, variant=variant)
    assert isinstance(create_element(ufl_element), expected_cls)
p_z = μ * dolfinx.Constant(mesh, 1.0 * 0)
m_y = μ * dolfinx.Constant(mesh, 1.0 * 0)

F_x = μ * dolfinx.Constant(
    mesh, (2.0 * np.pi / L)**2 * E * I * 0)  # prescribed F_x: 2, 4
F_z = μ * dolfinx.Constant(
    mesh, (0.5 * np.pi / L)**2 * E * I * 0)  # prescribed F_z: 4, 8
M_y = μ * dolfinx.Constant(
    mesh, (2.0 * np.pi / L)**1 * E * I * 1)  # prescribed M_y: 1, 2

# Define integration measures
dx = ufl.Measure("dx", domain=mesh, subdomain_data=subdomains)
ds = ufl.Measure("ds", domain=mesh, subdomain_data=interfaces)

# Function spaces
Ue = ufl.FiniteElement("CG", mesh.ufl_cell(), p)
We = ufl.FiniteElement("CG", mesh.ufl_cell(), p)
Re = ufl.FiniteElement("CG", mesh.ufl_cell(), p)

U = dolfinx.FunctionSpace(mesh, Ue)
W = dolfinx.FunctionSpace(mesh, We)
R = dolfinx.FunctionSpace(mesh, Re)

# Define functions
u = dolfinx.Function(U, name='u')
w = dolfinx.Function(W, name='w')
r = dolfinx.Function(R, name='r')

δu = ufl.TestFunction(U)
δw = ufl.TestFunction(W)
δr = ufl.TestFunction(R)
Beispiel #9
0
    def __init__(self, value, cell=None, name=None):
        """
        Create constant-valued function with given value.

        *Arguments*
            value
                The value may be either a single scalar value, or a
                tuple/list of values for vector-valued functions, or
                nested lists or a numpy array for tensor-valued
                functions.
            cell
                Optional argument. A :py:class:`Cell
                <ufl.Cell>` which defines the geometrical
                dimensions the Constant is defined for.
            name
                Optional argument. A str which overrules the default
                name of the Constant.

        The data type Constant represents a constant value that is
        unknown at compile-time. Its values can thus be changed
        without requiring re-generation and re-compilation of C++
        code.

        *Examples of usage*

            .. code-block:: python

                p = Constant(pi/4)              # scalar
                C = Constant((0.0, -1.0, 0.0))  # constant vector

        """

        if cell is None:
            domain = None
        else:
            #deprecation_warning("The cell argument is no longer necessary.")
            domain = ufl.as_domain(cell)
            if not isinstance(domain, ufl.Domain):
                raise TypeError(
                    "Expected an ufl.Domain as the second argument")

        array = numpy.array(value)
        rank = len(array.shape)
        floats = list(map(float, array.flat))

        # Create UFL element and initialize constant
        if rank == 0:
            self._ufl_element = ufl.FiniteElement("Real", domain, 0)
            cpp.Constant.__init__(self, floats[0])
        elif rank == 1:
            self._ufl_element = ufl.VectorElement("Real", domain, 0,
                                                  len(floats))
            cpp.Constant.__init__(self, floats)
        else:
            self._ufl_element = ufl.TensorElement("Real",
                                                  domain,
                                                  0,
                                                  shape=array.shape)
            cpp.Constant.__init__(self, list(array.shape), floats)

        # Initialize base classes
        ufl.Coefficient.__init__(self, self._ufl_element, count=self.id())

        # Set name as given or automatic
        name = name or "f_%d" % self.count()
        self.rename(name, "a Constant")
Beispiel #10
0
    def __init__(self, value, cell=None, name=None):
        """
        Create constant-valued function with given value.

        *Arguments*
            value
                The value may be either a single scalar value, or a
                tuple/list of values for vector-valued functions, or
                nested lists or a numpy array for tensor-valued
                functions.
            cell
                Optional argument. A :py:class:`Cell
                <ufl.Cell>` which defines the geometrical
                dimensions the Constant is defined for.
            name
                Optional argument. A str which overrules the default
                name of the Constant.

        The data type Constant represents a constant value that is
        unknown at compile-time. Its values can thus be changed
        without requiring re-generation and re-compilation of C++
        code.

        *Examples of usage*

            .. code-block:: python

                p = Constant(pi/4)              # scalar
                C = Constant((0.0, -1.0, 0.0))  # constant vector

        """

        # TODO: Either take mesh instead of cell, or drop cell and let
        # grad(c) be undefined.
        if cell is not None:
            cell = ufl.as_cell(cell)
        ufl_domain = None

        array = numpy.array(value)
        rank = len(array.shape)
        if cpp.common.has_petsc_complex():
            value_list = list(map(numpy.complex128, array.flat))
        else:
            value_list = list(map(float, array.flat))

        # Create UFL element and initialize constant
        if rank == 0:
            ufl_element = ufl.FiniteElement("Real", cell, 0)
            self._cpp_object = cpp.function.Constant(value_list[0])
        elif rank == 1:
            ufl_element = ufl.VectorElement("Real",
                                            cell,
                                            0,
                                            dim=len(value_list))
            self._cpp_object = cpp.function.Constant(value_list)
        else:
            ufl_element = ufl.TensorElement("Real", cell, 0, shape=array.shape)
            self._cpp_object = cpp.function.Constant(list(array.shape),
                                                     value_list)

        # Initialize base classes
        ufl_function_space = ufl.FunctionSpace(ufl_domain, ufl_element)
        ufl.Coefficient.__init__(self, ufl_function_space, count=self.id())

        # Set name as given or automatic
        name = name or "f_%d" % self.count()
        self.rename(name)
Beispiel #11
0
import ufl

import timeit

from minidolfin.meshing import build_unit_cube_mesh
from minidolfin.dofmap import build_dofmap
from minidolfin.dofmap import build_sparsity_pattern
from minidolfin.dofmap import pattern_to_csr
from minidolfin.petsc import create_matrix_from_csr
from minidolfin.assembling import assemble

import dijitso
dijitso.set_log_level("debug")

# UFL form
element = ufl.FiniteElement("N1E", ufl.tetrahedron, 2)
u, v = ufl.TrialFunction(element), ufl.TestFunction(element)
omega2 = 1e3
a = (ufl.inner(ufl.curl(u), ufl.curl(v)) - omega2 * ufl.dot(u, v)) * ufl.dx

# Build mesh
mesh = build_unit_cube_mesh(1, 1, 1)
tdim = mesh.reference_cell.get_dimension()
print('Number cells: {}'.format(mesh.num_entities(tdim)))

# Build dofmap
dofmap = build_dofmap(element, mesh)
print('Number dofs: {}'.format(dofmap.dim))

# Build sparsity pattern
pattern = build_sparsity_pattern(dofmap)
Beispiel #12
0
def initialize_functions(mesh):

    STRESS_elem = ufl.TensorElement("DG", mesh.ufl_cell(),
                                    args.disp_degree - 1)
    DMG_elem = ufl.FiniteElement("Quadrature",
                                 mesh.ufl_cell(),
                                 degree=quad_degree_stress,
                                 quad_scheme="default")

    # Space for dashpot viscosity should ideally be Quadrature, but this wouldn't
    # work since we use it to update other non-Quadrature functions
    ETA_DASH_elem = ufl.FiniteElement("DG", mesh.ufl_cell(), 2)
    DISPL_elem = ufl.VectorElement("CG", mesh.ufl_cell(), args.disp_degree)
    TEMP_elem = ufl.FiniteElement("CG", mesh.ufl_cell(), temp_degree)
    HUM_elem = ufl.FiniteElement("CG", mesh.ufl_cell(), hum_degree)
    CO2_elem = ufl.FiniteElement("CG", mesh.ufl_cell(), co2_degree)

    STRESS = FunctionSpace(mesh, STRESS_elem)
    DMG = FunctionSpace(mesh, DMG_elem)
    ETA_DASH = FunctionSpace(mesh, ETA_DASH_elem)
    DISPL = FunctionSpace(mesh, DISPL_elem)
    TEMP = FunctionSpace(mesh, TEMP_elem)
    HUM = FunctionSpace(mesh, HUM_elem)
    CO2 = FunctionSpace(mesh, CO2_elem)

    W = []
    W.append(DISPL)
    W.append(TEMP)
    W.append(HUM)
    W.append(CO2)

    # Initial temperature
    temp0 = Function(W[1], name="temp0")
    temp1 = Function(W[1], name="temp1")

    # Initial humidity
    phi0 = Function(W[2], name="phi0")
    phi1 = Function(W[2], name="phi1")

    # Initial CO2 concentration
    co20 = Function(W[3], name="co20")
    co21 = Function(W[3], name="co21")

    # Displacement
    displ0 = Function(W[0], name="displ0")
    displ1 = Function(W[0], name="displ1")

    # Pack all internal variables
    # This is done just for brevity of solve method interface
    intern_var0 = OrderedDict()
    intern_var0["eta_dash"] = Function(ETA_DASH)
    intern_var0["caco3"] = Function(CO2, name="caco3")

    intern_var0["eps_cr_kel"] = Function(STRESS, name="eps_cr_kel")
    intern_var0["eps_cr_dash"] = Function(STRESS, name="eps_cr_dash")
    intern_var0["eps_sh_dr"] = Function(STRESS)
    intern_var0["eps_th"] = Function(STRESS)
    intern_var0["eps_eqv"] = Function(DMG, name="eps_eqv")
    intern_var0["sigma"] = Function(STRESS, name="sigma")
    intern_var0["dmg"] = Function(DMG, name="dmg")

    for i in range(mps.M):
        intern_var0[f"gamma_{i}"] = Function(STRESS)

    intern_var1 = OrderedDict()
    intern_var1["eta_dash"] = Function(intern_var0["eta_dash"].function_space)
    intern_var1["caco3"] = Function(intern_var0["caco3"].function_space)
    intern_var1["eps_cr_kel"] = Function(
        intern_var0["eps_cr_kel"].function_space)
    intern_var1["eps_cr_dash"] = Function(
        intern_var0["eps_cr_dash"].function_space)
    intern_var1["eps_sh_dr"] = Function(
        intern_var0["eps_sh_dr"].function_space)
    intern_var1["eps_th"] = Function(intern_var0["eps_th"].function_space)
    intern_var1["eps_eqv"] = Function(intern_var0["eps_eqv"].function_space)
    intern_var1["sigma"] = Function(intern_var0["sigma"].function_space)
    intern_var1["dmg"] = Function(intern_var0["dmg"].function_space)

    for i in range(mps.M):
        intern_var1[f"gamma_{i}"] = Function(
            intern_var0[f"gamma_{i}"].function_space)

    # Pack all "solved-for" quantities
    w0 = OrderedDict()
    w0["displ"] = displ0
    w0["temp"] = temp0
    w0["phi"] = phi0
    w0["co2"] = co20

    # Pack all "solved-for" quantities
    w1 = OrderedDict()
    w1["displ"] = displ1
    w1["temp"] = temp1
    w1["phi"] = phi1
    w1["co2"] = co21

    return w0, w1, intern_var0, intern_var1
Beispiel #13
0
def test_assembly_solve_taylor_hood(mesh):
    """Assemble Stokes problem with Taylor-Hood elements and solve."""
    P2 = function.VectorFunctionSpace(mesh, ("Lagrange", 2))
    P1 = function.FunctionSpace(mesh, ("Lagrange", 1))

    def boundary0(x):
        """Define boundary x = 0"""
        return x[0] < 10 * numpy.finfo(float).eps

    def boundary1(x):
        """Define boundary x = 1"""
        return x[0] > (1.0 - 10 * numpy.finfo(float).eps)

    # Locate facets on boundaries
    facetdim = mesh.topology.dim - 1
    bndry_facets0 = dolfinx.mesh.locate_entities_boundary(mesh, facetdim, boundary0)
    bndry_facets1 = dolfinx.mesh.locate_entities_boundary(mesh, facetdim, boundary1)

    bdofs0 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets0)
    bdofs1 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets1)

    u0 = dolfinx.Function(P2)
    u0.vector.set(1.0)
    u0.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
    bc0 = dolfinx.DirichletBC(u0, bdofs0)
    bc1 = dolfinx.DirichletBC(u0, bdofs1)

    u, p = ufl.TrialFunction(P2), ufl.TrialFunction(P1)
    v, q = ufl.TestFunction(P2), ufl.TestFunction(P1)

    a00 = inner(ufl.grad(u), ufl.grad(v)) * dx
    a01 = ufl.inner(p, ufl.div(v)) * dx
    a10 = ufl.inner(ufl.div(u), q) * dx
    a11 = None

    p00 = a00
    p01, p10 = None, None
    p11 = inner(p, q) * dx

    # FIXME
    # We need zero function for the 'zero' part of L
    p_zero = dolfinx.Function(P1)
    f = dolfinx.Function(P2)
    L0 = ufl.inner(f, v) * dx
    L1 = ufl.inner(p_zero, q) * dx

    # -- Blocked (nested)

    A0 = dolfinx.fem.assemble_matrix_nest([[a00, a01], [a10, a11]], [bc0, bc1])
    A0.assemble()
    A0norm = nest_matrix_norm(A0)
    P0 = dolfinx.fem.assemble_matrix_nest([[p00, p01], [p10, p11]], [bc0, bc1])
    P0.assemble()
    P0norm = nest_matrix_norm(P0)
    b0 = dolfinx.fem.assemble_vector_nest([L0, L1])
    dolfinx.fem.apply_lifting_nest(b0, [[a00, a01], [a10, a11]], [bc0, bc1])
    for b_sub in b0.getNestSubVecs():
        b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    bcs0 = dolfinx.cpp.fem.bcs_rows(dolfinx.fem.assemble._create_cpp_form([L0, L1]), [bc0, bc1])
    dolfinx.fem.set_bc_nest(b0, bcs0)
    b0.assemble()
    b0norm = b0.norm()

    ksp = PETSc.KSP()
    ksp.create(mesh.mpi_comm())
    ksp.setOperators(A0, P0)
    nested_IS = P0.getNestISs()
    ksp.setType("minres")
    pc = ksp.getPC()
    pc.setType("fieldsplit")
    pc.setFieldSplitIS(["u", nested_IS[0][0]], ["p", nested_IS[1][1]])
    ksp_u, ksp_p = pc.getFieldSplitSubKSP()
    ksp_u.setType("preonly")
    ksp_u.getPC().setType('lu')
    ksp_u.getPC().setFactorSolverType('superlu_dist')
    ksp_p.setType("preonly")

    def monitor(ksp, its, rnorm):
        # print("Num it, rnorm:", its, rnorm)
        pass

    ksp.setTolerances(rtol=1.0e-8, max_it=50)
    ksp.setMonitor(monitor)
    ksp.setFromOptions()
    x0 = b0.copy()
    ksp.solve(b0, x0)
    assert ksp.getConvergedReason() > 0

    # -- Blocked (monolithic)

    A1 = dolfinx.fem.assemble_matrix_block([[a00, a01], [a10, a11]], [bc0, bc1])
    A1.assemble()
    assert A1.norm() == pytest.approx(A0norm, 1.0e-12)
    P1 = dolfinx.fem.assemble_matrix_block([[p00, p01], [p10, p11]], [bc0, bc1])
    P1.assemble()
    assert P1.norm() == pytest.approx(P0norm, 1.0e-12)

    b1 = dolfinx.fem.assemble_vector_block([L0, L1], [[a00, a01], [a10, a11]],
                                           [bc0, bc1])

    assert b1.norm() == pytest.approx(b0norm, 1.0e-12)

    ksp = PETSc.KSP()
    ksp.create(mesh.mpi_comm())
    ksp.setOperators(A1, P1)
    ksp.setType("minres")
    pc = ksp.getPC()
    pc.setType('lu')
    pc.setFactorSolverType('superlu_dist')
    ksp.setTolerances(rtol=1.0e-8, max_it=50)
    ksp.setFromOptions()
    x1 = A1.createVecRight()
    ksp.solve(b1, x1)
    assert ksp.getConvergedReason() > 0
    assert x1.norm() == pytest.approx(x0.norm(), 1e-8)

    # -- Monolithic

    P2_el = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2)
    P1_el = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
    TH = P2_el * P1_el
    W = dolfinx.FunctionSpace(mesh, TH)
    (u, p) = ufl.TrialFunctions(W)
    (v, q) = ufl.TestFunctions(W)
    a00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * dx
    a01 = ufl.inner(p, ufl.div(v)) * dx
    a10 = ufl.inner(ufl.div(u), q) * dx
    a = a00 + a01 + a10

    p00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * dx
    p11 = ufl.inner(p, q) * dx
    p_form = p00 + p11

    f = dolfinx.Function(W.sub(0).collapse())
    p_zero = dolfinx.Function(W.sub(1).collapse())
    L0 = inner(f, v) * dx
    L1 = inner(p_zero, q) * dx
    L = L0 + L1

    bdofsW0_P2_0 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets0)
    bdofsW0_P2_1 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets1)

    bc0 = dolfinx.DirichletBC(u0, bdofsW0_P2_0, W.sub(0))
    bc1 = dolfinx.DirichletBC(u0, bdofsW0_P2_1, W.sub(0))

    A2 = dolfinx.fem.assemble_matrix(a, [bc0, bc1])
    A2.assemble()
    assert A2.norm() == pytest.approx(A0norm, 1.0e-12)
    P2 = dolfinx.fem.assemble_matrix(p_form, [bc0, bc1])
    P2.assemble()
    assert P2.norm() == pytest.approx(P0norm, 1.0e-12)

    b2 = dolfinx.fem.assemble_vector(L)
    dolfinx.fem.apply_lifting(b2, [a], [[bc0, bc1]])
    b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    dolfinx.fem.set_bc(b2, [bc0, bc1])
    b2norm = b2.norm()
    assert b2norm == pytest.approx(b0norm, 1.0e-12)

    ksp = PETSc.KSP()
    ksp.create(mesh.mpi_comm())
    ksp.setOperators(A2, P2)
    ksp.setType("minres")
    pc = ksp.getPC()
    pc.setType('lu')
    pc.setFactorSolverType('superlu_dist')

    def monitor(ksp, its, rnorm):
        # print("Num it, rnorm:", its, rnorm)
        pass

    ksp.setTolerances(rtol=1.0e-8, max_it=50)
    ksp.setMonitor(monitor)
    ksp.setFromOptions()
    x2 = A2.createVecRight()
    ksp.solve(b2, x2)
    assert ksp.getConvergedReason() > 0
    assert x0.norm() == pytest.approx(x2.norm(), 1e-8)
Beispiel #14
0
def test_assembly_solve_block(mode):
    """Solve a two-field mass-matrix like problem with block matrix approaches
    and test that solution is the same.
    """
    mesh = UnitSquareMesh(MPI.COMM_WORLD, 32, 31, ghost_mode=mode)
    P = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
    V0 = dolfinx.function.FunctionSpace(mesh, P)
    V1 = V0.clone()

    def boundary(x):
        return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6)

    # Locate facets on boundary
    facetdim = mesh.topology.dim - 1
    bndry_facets = dolfinx.mesh.locate_entities_boundary(mesh, facetdim, boundary)

    bdofsV0 = dolfinx.fem.locate_dofs_topological(V0, facetdim, bndry_facets)
    bdofsV1 = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets)

    u_bc0 = dolfinx.function.Function(V0)
    u_bc0.vector.set(50.0)
    u_bc0.vector.ghostUpdate(
        addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
    u_bc1 = dolfinx.function.Function(V1)
    u_bc1.vector.set(20.0)
    u_bc1.vector.ghostUpdate(
        addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
    bcs = [
        dolfinx.fem.dirichletbc.DirichletBC(u_bc0, bdofsV0),
        dolfinx.fem.dirichletbc.DirichletBC(u_bc1, bdofsV1)
    ]

    # Variational problem
    u, p = ufl.TrialFunction(V0), ufl.TrialFunction(V1)
    v, q = ufl.TestFunction(V0), ufl.TestFunction(V1)
    f = 1.0
    g = -3.0
    zero = dolfinx.Function(V0)

    a00 = inner(u, v) * dx
    a01 = zero * inner(p, v) * dx
    a10 = zero * inner(u, q) * dx
    a11 = inner(p, q) * dx
    L0 = inner(f, v) * dx
    L1 = inner(g, q) * dx

    def monitor(ksp, its, rnorm):
        pass
        # print("Norm:", its, rnorm)

    A0 = dolfinx.fem.assemble_matrix_block([[a00, a01], [a10, a11]], bcs)
    b0 = dolfinx.fem.assemble_vector_block([L0, L1], [[a00, a01], [a10, a11]],
                                           bcs)
    A0.assemble()
    A0norm = A0.norm()
    b0norm = b0.norm()
    x0 = A0.createVecLeft()
    ksp = PETSc.KSP()
    ksp.create(mesh.mpi_comm())
    ksp.setOperators(A0)
    ksp.setMonitor(monitor)
    ksp.setType('cg')
    ksp.setTolerances(rtol=1.0e-14)
    ksp.setFromOptions()
    ksp.solve(b0, x0)
    x0norm = x0.norm()

    # Nested (MatNest)
    A1 = dolfinx.fem.assemble_matrix_nest([[a00, a01], [a10, a11]], bcs)
    A1.assemble()
    b1 = dolfinx.fem.assemble_vector_nest([L0, L1])
    dolfinx.fem.apply_lifting_nest(b1, [[a00, a01], [a10, a11]], bcs)
    for b_sub in b1.getNestSubVecs():
        b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    bcs0 = dolfinx.cpp.fem.bcs_rows(dolfinx.fem.assemble._create_cpp_form([L0, L1]), bcs)
    dolfinx.fem.set_bc_nest(b1, bcs0)
    b1.assemble()

    b1norm = b1.norm()
    assert b1norm == pytest.approx(b0norm, 1.0e-12)
    A1norm = nest_matrix_norm(A1)
    assert A0norm == pytest.approx(A1norm, 1.0e-12)

    x1 = b1.copy()
    ksp = PETSc.KSP()
    ksp.create(mesh.mpi_comm())
    ksp.setMonitor(monitor)
    ksp.setOperators(A1)
    ksp.setType('cg')
    ksp.setTolerances(rtol=1.0e-12)
    ksp.setFromOptions()
    ksp.solve(b1, x1)
    x1norm = x1.norm()
    assert x1norm == pytest.approx(x0norm, rel=1.0e-12)

    # Monolithic version
    E = P * P
    W = dolfinx.function.FunctionSpace(mesh, E)
    u0, u1 = ufl.TrialFunctions(W)
    v0, v1 = ufl.TestFunctions(W)
    a = inner(u0, v0) * dx + inner(u1, v1) * dx
    L = inner(f, v0) * ufl.dx + inner(g, v1) * dx

    u0_bc = dolfinx.function.Function(V0)
    u0_bc.vector.set(50.0)
    u0_bc.vector.ghostUpdate(
        addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
    u1_bc = dolfinx.function.Function(V1)
    u1_bc.vector.set(20.0)
    u1_bc.vector.ghostUpdate(
        addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)

    bdofsW0_V0 = dolfinx.fem.locate_dofs_topological((W.sub(0), V0), facetdim, bndry_facets)
    bdofsW1_V1 = dolfinx.fem.locate_dofs_topological((W.sub(1), V1), facetdim, bndry_facets)

    bcs = [
        dolfinx.fem.dirichletbc.DirichletBC(u0_bc, bdofsW0_V0, W.sub(0)),
        dolfinx.fem.dirichletbc.DirichletBC(u1_bc, bdofsW1_V1, W.sub(1))
    ]

    A2 = dolfinx.fem.assemble_matrix(a, bcs)
    A2.assemble()
    b2 = dolfinx.fem.assemble_vector(L)
    dolfinx.fem.apply_lifting(b2, [a], [bcs])
    b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    dolfinx.fem.set_bc(b2, bcs)
    A2norm = A2.norm()
    b2norm = b2.norm()
    assert A2norm == pytest.approx(A0norm, 1.0e-12)
    assert b2norm == pytest.approx(b0norm, 1.0e-12)

    x2 = b2.copy()
    ksp = PETSc.KSP()
    ksp.create(mesh.mpi_comm())
    ksp.setMonitor(monitor)
    ksp.setOperators(A2)
    ksp.setType('cg')
    ksp.getPC().setType('jacobi')
    ksp.setTolerances(rtol=1.0e-12)
    ksp.setFromOptions()
    ksp.solve(b2, x2)
    x2norm = x2.norm()
    assert x2norm == pytest.approx(x0norm, 1.0e-10)
Beispiel #15
0
def test_assembly_solve_taylor_hood(mesh):
    """Assemble Stokes problem with Taylor-Hood elements and solve."""
    gdim = mesh.geometry.dim
    P2 = dolfinx.function.VectorFunctionSpace(mesh, ("Lagrange", 2))
    P1 = dolfinx.function.FunctionSpace(mesh, ("Lagrange", 1))

    def boundary0(x):
        """Define boundary x = 0"""
        return x[0] < 10 * numpy.finfo(float).eps

    def boundary1(x):
        """Define boundary x = 1"""
        return x[0] > (1.0 - 10 * numpy.finfo(float).eps)

    def initial_guess_u(x):
        u_init = numpy.row_stack((numpy.sin(x[0]) * numpy.sin(x[1]),
                                  numpy.cos(x[0]) * numpy.cos(x[1])))
        if gdim == 3:
            u_init = numpy.row_stack((u_init, numpy.cos(x[2])))
        return u_init

    def initial_guess_p(x):
        return -x[0]**2 - x[1]**3

    u_bc_0 = dolfinx.Function(P2)
    u_bc_0.interpolate(
        lambda x: numpy.row_stack(tuple(x[j] + float(j) for j in range(gdim))))

    u_bc_1 = dolfinx.Function(P2)
    u_bc_1.interpolate(
        lambda x: numpy.row_stack(tuple(numpy.sin(x[j]) for j in range(gdim))))

    facetdim = mesh.topology.dim - 1
    bndry_facets0 = locate_entities_boundary(mesh, facetdim, boundary0)
    bndry_facets1 = locate_entities_boundary(mesh, facetdim, boundary1)

    bdofs0 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets0)
    bdofs1 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets1)

    bcs = [
        dolfinx.DirichletBC(u_bc_0, bdofs0),
        dolfinx.DirichletBC(u_bc_1, bdofs1)
    ]

    u, p = dolfinx.Function(P2), dolfinx.Function(P1)
    du, dp = ufl.TrialFunction(P2), ufl.TrialFunction(P1)
    v, q = ufl.TestFunction(P2), ufl.TestFunction(P1)

    F = [
        inner(ufl.grad(u), ufl.grad(v)) * dx + inner(p, ufl.div(v)) * dx,
        inner(ufl.div(u), q) * dx
    ]
    J = [[derivative(F[0], u, du),
          derivative(F[0], p, dp)],
         [derivative(F[1], u, du),
          derivative(F[1], p, dp)]]
    P = [[J[0][0], None], [None, inner(dp, q) * dx]]

    # -- Blocked and monolithic

    Jmat0 = dolfinx.fem.create_matrix_block(J)
    Pmat0 = dolfinx.fem.create_matrix_block(P)
    Fvec0 = dolfinx.fem.create_vector_block(F)

    snes = PETSc.SNES().create(MPI.COMM_WORLD)
    snes.setTolerances(rtol=1.0e-15, max_it=10)

    snes.getKSP().setType("minres")
    snes.getKSP().getPC().setType("lu")
    snes.getKSP().getPC().setFactorSolverType("superlu_dist")

    problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs, P=P)
    snes.setFunction(problem.F_block, Fvec0)
    snes.setJacobian(problem.J_block, J=Jmat0, P=Pmat0)

    u.interpolate(initial_guess_u)
    p.interpolate(initial_guess_p)

    x0 = dolfinx.fem.create_vector_block(F)
    with u.vector.localForm() as _u, p.vector.localForm() as _p:
        dolfinx.cpp.la.scatter_local_vectors(x0, [_u.array_r, _p.array_r], [
            u.function_space.dofmap.index_map,
            p.function_space.dofmap.index_map
        ])
    x0.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                   mode=PETSc.ScatterMode.FORWARD)

    snes.solve(None, x0)

    assert snes.getConvergedReason() > 0

    # -- Blocked and nested

    Jmat1 = dolfinx.fem.create_matrix_nest(J)
    Pmat1 = dolfinx.fem.create_matrix_nest(P)
    Fvec1 = dolfinx.fem.create_vector_nest(F)

    snes = PETSc.SNES().create(MPI.COMM_WORLD)
    snes.setTolerances(rtol=1.0e-15, max_it=10)

    nested_IS = Jmat1.getNestISs()

    snes.getKSP().setType("minres")
    snes.getKSP().setTolerances(rtol=1e-12)
    snes.getKSP().getPC().setType("fieldsplit")
    snes.getKSP().getPC().setFieldSplitIS(["u", nested_IS[0][0]],
                                          ["p", nested_IS[1][1]])

    ksp_u, ksp_p = snes.getKSP().getPC().getFieldSplitSubKSP()
    ksp_u.setType("preonly")
    ksp_u.getPC().setType('lu')
    ksp_u.getPC().setFactorSolverType('superlu_dist')
    ksp_p.setType("preonly")
    ksp_p.getPC().setType('lu')
    ksp_p.getPC().setFactorSolverType('superlu_dist')

    problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs, P=P)
    snes.setFunction(problem.F_nest, Fvec1)
    snes.setJacobian(problem.J_nest, J=Jmat1, P=Pmat1)

    u.interpolate(initial_guess_u)
    p.interpolate(initial_guess_p)

    x1 = dolfinx.fem.create_vector_nest(F)
    for x1_soln_pair in zip(x1.getNestSubVecs(), (u, p)):
        x1_sub, soln_sub = x1_soln_pair
        soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                                    mode=PETSc.ScatterMode.FORWARD)
        soln_sub.vector.copy(result=x1_sub)
        x1_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                           mode=PETSc.ScatterMode.FORWARD)

    x1.set(0.0)
    snes.solve(None, x1)

    assert snes.getConvergedReason() > 0
    assert nest_matrix_norm(Jmat1) == pytest.approx(Jmat0.norm(), 1.0e-12)
    assert Fvec1.norm() == pytest.approx(Fvec0.norm(), 1.0e-12)
    assert x1.norm() == pytest.approx(x0.norm(), 1.0e-12)

    # -- Monolithic

    P2_el = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2)
    P1_el = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
    TH = P2_el * P1_el
    W = dolfinx.FunctionSpace(mesh, TH)
    U = dolfinx.Function(W)
    dU = ufl.TrialFunction(W)
    u, p = ufl.split(U)
    du, dp = ufl.split(dU)
    v, q = ufl.TestFunctions(W)

    F = inner(ufl.grad(u), ufl.grad(v)) * dx + inner(p, ufl.div(v)) * dx \
        + inner(ufl.div(u), q) * dx
    J = derivative(F, U, dU)
    P = inner(ufl.grad(du), ufl.grad(v)) * dx + inner(dp, q) * dx

    bdofsW0_P2_0 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2),
                                                       facetdim, bndry_facets0)
    bdofsW0_P2_1 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2),
                                                       facetdim, bndry_facets1)

    bcs = [
        dolfinx.DirichletBC(u_bc_0, bdofsW0_P2_0, W.sub(0)),
        dolfinx.DirichletBC(u_bc_1, bdofsW0_P2_1, W.sub(0))
    ]

    Jmat2 = dolfinx.fem.create_matrix(J)
    Pmat2 = dolfinx.fem.create_matrix(P)
    Fvec2 = dolfinx.fem.create_vector(F)

    snes = PETSc.SNES().create(MPI.COMM_WORLD)
    snes.setTolerances(rtol=1.0e-15, max_it=10)

    snes.getKSP().setType("minres")
    snes.getKSP().getPC().setType("lu")
    snes.getKSP().getPC().setFactorSolverType("superlu_dist")

    problem = NonlinearPDE_SNESProblem(F, J, U, bcs, P=P)
    snes.setFunction(problem.F_mono, Fvec2)
    snes.setJacobian(problem.J_mono, J=Jmat2, P=Pmat2)

    U.interpolate(lambda x: numpy.row_stack(
        (initial_guess_u(x), initial_guess_p(x))))

    x2 = dolfinx.fem.create_vector(F)
    x2.array = U.vector.array_r

    snes.solve(None, x2)

    assert snes.getConvergedReason() > 0
    assert Jmat2.norm() == pytest.approx(Jmat0.norm(), 1.0e-12)
    assert Fvec2.norm() == pytest.approx(Fvec0.norm(), 1.0e-12)
    assert x2.norm() == pytest.approx(x0.norm(), 1.0e-12)
def test_ufl():
    import ufl
    argyris = ufl.FiniteElement("Argyris", degree=6, cell=ufl.triangle)
def ufl_element(triangle_names):
    return ufl.FiniteElement(triangle_names, ufl.triangle, 2)
    def __init__(self, mesh, family, degree, form_degree=None,
                 constrained_domain=None, restriction=None):
        """
        Create finite element function space.

        *Arguments*
            mesh (:py:class:`Mesh <dolfin.cpp.Mesh>`)
                the mesh
            family (string)
                specification of the element family, see below for
                alternatives.
            degree (int)
                the degree of the element.
            form_degree (int)
                the form degree (FEEC notation, used when field is
                viewed as k-form)
            constrained_domain
                constrained subdomain with map function.
            restriction
                restriction of the element (e.g. to cell facets).

        Which families and degrees that are supported is determined by the
        form compiler used to generate the element, but typical families
        include

        =================================  =========
        Name                               Usage
        =================================  =========
        Argyris*                           "ARG"
        Arnold-Winther*                    "AW"
        Brezzi-Douglas-Fortin-Marini*      "BDFM"
        Brezzi-Douglas-Marini              "BDM"
        Bubble                             "B"
        Crouzeix-Raviart                   "CR"
        Discontinuous Lagrange             "DG"
        Hermite*                           "HER"
        Lagrange                           "CG"
        Mardal-Tai-Winther*                "MTW"
        Morley*                            "MOR"
        Nedelec 1st kind H(curl)           "N1curl"
        Nedelec 2nd kind H(curl)           "N2curl"
        Quadrature                         "Q"
        Raviart-Thomas                     "RT"
        Real                               "R"
        =================================  =========

        *only partly supported.

        *Examples of usage*
            To define a discrete function space over e.g. the unit square:

            .. code-block:: python

                mesh = UnitSquare(32,32)
                V = FunctionSpace(mesh, "CG", 1)

            Here, ``"CG"`` stands for Continuous Galerkin, implying the
            standard Lagrange family of elements. Instead of ``"CG"``, we
            could have written ``"Lagrange"``. With degree 1, we get the
            linear Lagrange element. Other examples include:

            .. code-block:: python

                # Discontinuous element of degree 0
                V = FunctionSpace(mesh, "DG", 0)

                # Brezzi-Douglas-Marini element of degree 2
                W = FunctionSpace(mesh, "BDM", 2)

                # Real element with one global degree of freedom
                R = FunctionSpace(mesh, "R", 0)

        """

        # Check arguments
        if not isinstance(mesh, (cpp.Mesh, cpp.Restriction)):
            cpp.dolfin_error("functionspace.py",
                             "create function space",
                             "Illegal argument, not a mesh or restriction: " + str(mesh))
        if not isinstance(family, str):
            cpp.dolfin_error("functionspace.py",
                             "create function space",
                             "Illegal argument for finite element family, not a string: " + str(family))
        if not isinstance(degree, int):
            cpp.dolfin_error("functionspace.py",
                             "create function space",
                             "Illegal argument for degree, not an integer: " + str(degree))

        # Create element
        # FIXME: Get a ufl.Domain instead of cell somehow.
        cell = mesh.ufl_cell() if isinstance(mesh, cpp.Mesh) else mesh.mesh().ufl_cell()
        element = ufl.FiniteElement(family, cell, degree,
                                    form_degree=form_degree)
        if restriction is not None:
            element = element[restriction]

        # Initialize base class
        FunctionSpaceBase.__init__(self, mesh, element, constrained_domain)

        self.___degree = degree
        self.___family = family
        self.___mesh = mesh if isinstance(mesh, cpp.Mesh) else mesh.mesh()
        self.___form_degree = form_degree
def ufl_B(tensor_name):
    return ufl.FiniteElement(tensor_name, ufl.interval, 1)
Beispiel #20
0
def _create_fiat_element(ufl_element):
    """Create FIAT element corresponding to given finite element."""

    # Get element data
    family = ufl_element.family()
    cell = ufl_element.cell()
    cellname = cell.cellname()
    degree = ufl_element.degree()

    # Check that FFC supports this element
    if family not in supported_families:
        raise RuntimeError("This element family (%s) is not supported by FFC." % family)

    # Create FIAT cell
    fiat_cell = reference_cell(cellname)

    # Handle the space of the constant
    if family == "Real":
        element = _create_fiat_element(ufl.FiniteElement("DG", cell, 0))
        element.__class__ = type('SpaceOfReals', (type(element), SpaceOfReals), {})
        return element

    if cellname == "quadrilateral":
        # Handle quadrilateral case by reconstructing the element with
        # cell TensorProductCell (interval x interval)
        quadrilateral_tpc = ufl.TensorProductCell(ufl.Cell("interval"), ufl.Cell("interval"))
        return FlattenedDimensions(
            _create_fiat_element(ufl_element.reconstruct(cell=quadrilateral_tpc)))
    elif cellname == "hexahedron":
        # Handle hexahedron case by reconstructing the element with cell
        # TensorProductCell (quadrilateral x interval). This creates
        # TensorProductElement(TensorProductElement(interval, interval),
        # interval) Therefore dof entities consists of nested tuples,
        # example: ((0, 1), 1)
        hexahedron_tpc = ufl.TensorProductCell(ufl.Cell("quadrilateral"), ufl.Cell("interval"))
        return FlattenedDimensions(
            _create_fiat_element(ufl_element.reconstruct(cell=hexahedron_tpc)))

    # FIXME: AL: Should this really be here?
    # Handle QuadratureElement
    if family == "Quadrature":
        # Compute number of points per axis from the degree of the element
        scheme = ufl_element.quadrature_scheme()
        assert degree is not None
        assert scheme is not None

        # Create quadrature (only interested in points)
        # TODO: KBO: What should we do about quadrature functions that live on ds, dS?
        # Get cell and facet names.
        points, weights = create_quadrature(cellname, degree, scheme)

        # Make element
        element = QuadratureElement(fiat_cell, points)
    else:
        # Check if finite element family is supported by FIAT
        if family not in FIAT.supported_elements:
            raise RuntimeError("Sorry, finite element of type \"%s\" are not supported by FIAT.",
                               family)

        ElementClass = FIAT.supported_elements[family]

        # Create tensor product FIAT finite element
        if isinstance(ufl_element, ufl.TensorProductElement):
            A = create_element(ufl_element.sub_elements()[0])
            B = create_element(ufl_element.sub_elements()[1])
            element = ElementClass(A, B)

        # Create normal FIAT finite element
        else:
            if degree is None:
                element = ElementClass(fiat_cell)
            else:
                element = ElementClass(fiat_cell, degree)

    if element.value_shape() != ufl_element.reference_value_shape():
        # Consistency check between UFL and FIAT elements.
        raise RuntimeError("Something went wrong in the construction of FIAT element from UFL element."
                           + "Shapes are {} and {}.".format(element.value_shape(),
                                                            ufl_element.reference_value_shape()))

    return element
def test_triangle_variant_spectral_fail():
    ufl_element = ufl.FiniteElement('DP', ufl.triangle, 2, variant='spectral')
    with pytest.raises(ValueError):
        create_element(ufl_element)
Beispiel #22
0
import ufl
from ufl import inner, curl, dx

from minidolfin.assembling import jit_compile_form

# for fixing https://github.com/FEniCS/ffcx/pull/25

cell = ufl.tetrahedron
element = ufl.FiniteElement("Nedelec 1st kind H(curl)", cell, 3)

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

a = inner(curl(u), curl(v)) * dx - inner(u, v) * dx

jit_compile_form(a, {
    "compiler": "ffc",
    "max_preintegrated_unrolled_table_size": 2000
})
Beispiel #23
0
    return np.isclose(x[1], 1.0)


# Lid velocity
def lid_velocity_expression(x):
    return np.stack((np.ones(x.shape[1]), np.zeros(x.shape[1])))


# We define two :py:class:`FunctionSpace
# <dolfinx.function.FunctionSpace>` instances with different finite
# elements. ``P2`` corresponds to piecewise quadratics for the velocity
# field and ``P1`` to continuous piecewise linears for the pressure
# field::

P2 = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2)
P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1)
V, Q = FunctionSpace(mesh, P2), FunctionSpace(mesh, P1)

# We can define boundary conditions::

# No-slip boundary condition for velocity field (`V`) on boundaries
# where x = 0, x = 1, and y = 0
noslip = Function(V)
with noslip.vector.localForm() as bc_local:
    bc_local.set(0.0)

facets = locate_entities_boundary(mesh, 1, noslip_boundary)
bc0 = DirichletBC(noslip, locate_dofs_topological(V, 1, facets))

# Driving velocity condition u = (1, 0) on top boundary (y = 1)
lid_velocity = Function(V)
Beispiel #24
0
    def __init__(self, value, name=None, cell=None):
        """
        Create constant-valued function with given value.

        *Arguments*
            value
                The value may be either a single scalar value, or a
                tuple/list of values for vector-valued functions, or
                nested lists or a numpy array for tensor-valued
                functions.
            cell
                Optional argument. A :py:class:`Cell
                <ufl.Cell>` which defines the geometrical
                dimensions the Constant is defined for.
            name
                Optional argument. A str which overrules the default
                name of the Constant.

        The data type Constant represents a constant value that is
        unknown at compile-time. Its values can thus be changed
        without requiring re-generation and re-compilation of C++
        code.

        *Examples of usage*

            .. code-block:: python

                p = Constant(pi/4)              # scalar
                C = Constant((0.0, -1.0, 0.0))  # constant vector

        """

        # TODO: Either take mesh instead of cell, or drop cell and let
        # grad(c) be undefined.
        if cell is not None:
            cell = ufl.as_cell(cell)
        ufl_domain = None

        array = numpy.array(value)
        rank = len(array.shape)
        floats = list(map(float, array.flat))

        # Create UFL element and initialize constant
        if rank == 0:
            ufl_element = ufl.FiniteElement("Real", cell, 0)
        elif rank == 1:
            ufl_element = ufl.VectorElement("Real", cell, 0, dim=len(floats))
        else:
            ufl_element = ufl.TensorElement("Real", cell, 0, shape=array.shape)

        # Initialize base classes
        ufl_function_space = ufl.FunctionSpace(ufl_domain, ufl_element)
        ufl.Coefficient.__init__(self, ufl_function_space)
        if name is None:
            self.name = "c" + str(Constant.constCount)
            Constant.constCount += 1
        else:
            self.name = name
        if isNumber(value):
            self._value = float(value)
        else:
            self._value = [float(v) for v in value]
        self.models = []
Beispiel #25
0
def hexahedral_element():
    """Compile list of Lagrange elements"""
    cell = ufl.hexahedron
    elements = [ufl.FiniteElement("Lagrange", cell, p) for p in range(1, 5)]
    compiled_elements, module = ffc.codegeneration.jit.compile_elements(elements)
    return elements, compiled_elements, module
Beispiel #26
0
def test_assembly_solve_block():
    """Solve a two-field nonlinear diffusion like problem with block matrix
    approaches and test that solution is the same.
    """
    mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 12, 11)
    p = 1
    P = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p)
    V0 = dolfinx.function.FunctionSpace(mesh, P)
    V1 = V0.clone()

    def bc_val_0(x):
        return x[0]**2 + x[1]**2

    def bc_val_1(x):
        return numpy.sin(x[0]) * numpy.cos(x[1])

    def initial_guess_u(x):
        return numpy.sin(x[0]) * numpy.sin(x[1])

    def initial_guess_p(x):
        return -x[0]**2 - x[1]**3

    def boundary(x):
        return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6)

    facetdim = mesh.topology.dim - 1
    bndry_facets = locate_entities_boundary(mesh, facetdim, boundary)

    u_bc0 = dolfinx.function.Function(V0)
    u_bc0.interpolate(bc_val_0)
    u_bc1 = dolfinx.function.Function(V1)
    u_bc1.interpolate(bc_val_1)

    bdofs0 = dolfinx.fem.locate_dofs_topological(V0, facetdim, bndry_facets)
    bdofs1 = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets)

    bcs = [
        dolfinx.fem.dirichletbc.DirichletBC(u_bc0, bdofs0),
        dolfinx.fem.dirichletbc.DirichletBC(u_bc1, bdofs1)
    ]

    # Block and Nest variational problem
    u, p = dolfinx.function.Function(V0), dolfinx.function.Function(V1)
    du, dp = ufl.TrialFunction(V0), ufl.TrialFunction(V1)
    v, q = ufl.TestFunction(V0), ufl.TestFunction(V1)

    f = 1.0
    g = -3.0

    F = [
        inner((u**2 + 1) * ufl.grad(u), ufl.grad(v)) * dx - inner(f, v) * dx,
        inner((p**2 + 1) * ufl.grad(p), ufl.grad(q)) * dx - inner(g, q) * dx
    ]

    J = [[derivative(F[0], u, du),
          derivative(F[0], p, dp)],
         [derivative(F[1], u, du),
          derivative(F[1], p, dp)]]

    # -- Blocked version
    Jmat0 = dolfinx.fem.create_matrix_block(J)
    Fvec0 = dolfinx.fem.create_vector_block(F)

    snes = PETSc.SNES().create(MPI.COMM_WORLD)
    snes.setTolerances(rtol=1.0e-15, max_it=10)

    snes.getKSP().setType("preonly")
    snes.getKSP().getPC().setType("lu")
    snes.getKSP().getPC().setFactorSolverType("superlu_dist")

    problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs)
    snes.setFunction(problem.F_block, Fvec0)
    snes.setJacobian(problem.J_block, J=Jmat0, P=None)

    u.interpolate(initial_guess_u)
    p.interpolate(initial_guess_p)

    x0 = dolfinx.fem.create_vector_block(F)
    dolfinx.cpp.la.scatter_local_vectors(
        x0, [u.vector.array_r, p.vector.array_r],
        [u.function_space.dofmap.index_map, p.function_space.dofmap.index_map])
    x0.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                   mode=PETSc.ScatterMode.FORWARD)

    snes.solve(None, x0)

    assert snes.getKSP().getConvergedReason() > 0
    assert snes.getConvergedReason() > 0

    J0norm = Jmat0.norm()
    F0norm = Fvec0.norm()
    x0norm = x0.norm()

    # -- Nested (MatNest)
    Jmat1 = dolfinx.fem.create_matrix_nest(J)
    Fvec1 = dolfinx.fem.create_vector_nest(F)

    snes = PETSc.SNES().create(MPI.COMM_WORLD)
    snes.setTolerances(rtol=1.0e-15, max_it=10)

    nested_IS = Jmat1.getNestISs()

    snes.getKSP().setType("fgmres")
    snes.getKSP().setTolerances(rtol=1e-12)
    snes.getKSP().getPC().setType("fieldsplit")
    snes.getKSP().getPC().setFieldSplitIS(["u", nested_IS[0][0]],
                                          ["p", nested_IS[1][1]])

    ksp_u, ksp_p = snes.getKSP().getPC().getFieldSplitSubKSP()
    ksp_u.setType("preonly")
    ksp_u.getPC().setType('lu')
    ksp_u.getPC().setFactorSolverType('superlu_dist')
    ksp_p.setType("preonly")
    ksp_p.getPC().setType('lu')
    ksp_p.getPC().setFactorSolverType('superlu_dist')

    problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs)
    snes.setFunction(problem.F_nest, Fvec1)
    snes.setJacobian(problem.J_nest, J=Jmat1, P=None)

    u.interpolate(initial_guess_u)
    p.interpolate(initial_guess_p)

    x1 = dolfinx.fem.create_vector_nest(F)
    for x1_soln_pair in zip(x1.getNestSubVecs(), (u, p)):
        x1_sub, soln_sub = x1_soln_pair
        soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                                    mode=PETSc.ScatterMode.FORWARD)
        soln_sub.vector.copy(result=x1_sub)
        x1_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                           mode=PETSc.ScatterMode.FORWARD)

    snes.solve(None, x1)

    assert snes.getKSP().getConvergedReason() > 0
    assert snes.getConvergedReason() > 0
    assert x1.getType() == "nest"
    assert Jmat1.getType() == "nest"
    assert Fvec1.getType() == "nest"

    J1norm = nest_matrix_norm(Jmat1)
    F1norm = Fvec1.norm()
    x1norm = x1.norm()

    assert J1norm == pytest.approx(J0norm, 1.0e-12)
    assert F1norm == pytest.approx(F0norm, 1.0e-12)
    assert x1norm == pytest.approx(x0norm, 1.0e-12)

    # -- Monolithic version
    E = P * P
    W = dolfinx.function.FunctionSpace(mesh, E)
    U = dolfinx.function.Function(W)
    dU = ufl.TrialFunction(W)
    u0, u1 = ufl.split(U)
    v0, v1 = ufl.TestFunctions(W)

    F = inner((u0**2 + 1) * ufl.grad(u0), ufl.grad(v0)) * dx \
        + inner((u1**2 + 1) * ufl.grad(u1), ufl.grad(v1)) * dx \
        - inner(f, v0) * ufl.dx - inner(g, v1) * dx
    J = derivative(F, U, dU)

    u0_bc = dolfinx.function.Function(V0)
    u0_bc.interpolate(bc_val_0)
    u1_bc = dolfinx.function.Function(V1)
    u1_bc.interpolate(bc_val_1)

    bdofsW0_V0 = dolfinx.fem.locate_dofs_topological((W.sub(0), V0), facetdim,
                                                     bndry_facets)
    bdofsW1_V1 = dolfinx.fem.locate_dofs_topological((W.sub(1), V1), facetdim,
                                                     bndry_facets)

    bcs = [
        dolfinx.fem.dirichletbc.DirichletBC(u0_bc, bdofsW0_V0, W.sub(0)),
        dolfinx.fem.dirichletbc.DirichletBC(u1_bc, bdofsW1_V1, W.sub(1))
    ]

    Jmat2 = dolfinx.fem.create_matrix(J)
    Fvec2 = dolfinx.fem.create_vector(F)

    snes = PETSc.SNES().create(MPI.COMM_WORLD)
    snes.setTolerances(rtol=1.0e-15, max_it=10)

    snes.getKSP().setType("preonly")
    snes.getKSP().getPC().setType("lu")
    snes.getKSP().getPC().setFactorSolverType("superlu_dist")

    problem = NonlinearPDE_SNESProblem(F, J, U, bcs)
    snes.setFunction(problem.F_mono, Fvec2)
    snes.setJacobian(problem.J_mono, J=Jmat2, P=None)

    U.interpolate(lambda x: numpy.row_stack(
        (initial_guess_u(x), initial_guess_p(x))))

    x2 = dolfinx.fem.create_vector(F)
    x2.array = U.vector.array_r

    snes.solve(None, x2)

    assert snes.getKSP().getConvergedReason() > 0
    assert snes.getConvergedReason() > 0

    J2norm = Jmat2.norm()
    F2norm = Fvec2.norm()
    x2norm = x2.norm()

    assert J2norm == pytest.approx(J0norm, 1.0e-12)
    assert F2norm == pytest.approx(F0norm, 1.0e-12)
    assert x2norm == pytest.approx(x0norm, 1.0e-12)
Beispiel #27
0
# Build form compiler parameters
form_compiler_parameters = {}
for p in args.form_compiler_parameters:
    k, v = p.split("=")
    form_compiler_parameters[k] = v

# Plane wave
omega2 = 15**2 + 11**2


def u_exact(x):
    return math.cos(-15 * x[0] + 12 * x[1])


# UFL form
element = ufl.FiniteElement("P", ufl.triangle, 3)
u, v = ufl.TrialFunction(element), ufl.TestFunction(element)
a = (ufl.inner(ufl.grad(u), ufl.grad(v)) - omega2 * ufl.dot(u, v)) * ufl.dx

# Build mesh
mesh = build_unit_square_mesh(args.n, args.n)
tdim = mesh.reference_cell.get_dimension()
print('Number cells: {}'.format(mesh.num_entities(tdim)))

# Build dofmap
dofmap = build_dofmap(element, mesh)
print('Number dofs: {}'.format(dofmap.dim))

# Run and time assembly
t = -timeit.default_timer()
A = assemble(dofmap, a, dtype=numpy.float32)
Beispiel #28
0
def test_matrix_assembly_block():
    """Test assembly of block matrices and vectors into (a) monolithic
    blocked structures, PETSc Nest structures, and monolithic structures
    in the nonlinear setting
    """
    mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 4, 8)

    p0, p1 = 1, 2
    P0 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p0)
    P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p1)

    V0 = dolfinx.function.FunctionSpace(mesh, P0)
    V1 = dolfinx.function.FunctionSpace(mesh, P1)

    def boundary(x):
        return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6)

    def initial_guess_u(x):
        return numpy.sin(x[0]) * numpy.sin(x[1])

    def initial_guess_p(x):
        return -x[0]**2 - x[1]**3

    def bc_value(x):
        return numpy.cos(x[0]) * numpy.cos(x[1])

    facetdim = mesh.topology.dim - 1
    bndry_facets = locate_entities_boundary(mesh, facetdim, boundary)

    u_bc = dolfinx.function.Function(V1)
    u_bc.interpolate(bc_value)
    bdofs = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets)
    bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofs)

    # Define variational problem
    du, dp = ufl.TrialFunction(V0), ufl.TrialFunction(V1)
    u, p = dolfinx.function.Function(V0), dolfinx.function.Function(V1)
    v, q = ufl.TestFunction(V0), ufl.TestFunction(V1)

    u.interpolate(initial_guess_u)
    p.interpolate(initial_guess_p)

    f = 1.0
    g = -3.0

    F0 = inner(u, v) * dx + inner(p, v) * dx - inner(f, v) * dx
    F1 = inner(u, q) * dx + inner(p, q) * dx - inner(g, q) * dx

    a_block = [[derivative(F0, u, du),
                derivative(F0, p, dp)],
               [derivative(F1, u, du),
                derivative(F1, p, dp)]]
    L_block = [F0, F1]

    # Monolithic blocked
    x0 = dolfinx.fem.create_vector_block(L_block)
    dolfinx.cpp.la.scatter_local_vectors(
        x0, [u.vector.array_r, p.vector.array_r],
        [u.function_space.dofmap.index_map, p.function_space.dofmap.index_map])
    x0.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                   mode=PETSc.ScatterMode.FORWARD)

    # Ghosts are updated inside assemble_vector_block
    A0 = dolfinx.fem.assemble_matrix_block(a_block, [bc])
    b0 = dolfinx.fem.assemble_vector_block(L_block,
                                           a_block, [bc],
                                           x0=x0,
                                           scale=-1.0)
    A0.assemble()
    assert A0.getType() != "nest"
    Anorm0 = A0.norm()
    bnorm0 = b0.norm()

    # Nested (MatNest)
    x1 = dolfinx.fem.create_vector_nest(L_block)
    for x1_soln_pair in zip(x1.getNestSubVecs(), (u, p)):
        x1_sub, soln_sub = x1_soln_pair
        soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                                    mode=PETSc.ScatterMode.FORWARD)
        soln_sub.vector.copy(result=x1_sub)
        x1_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                           mode=PETSc.ScatterMode.FORWARD)

    A1 = dolfinx.fem.assemble_matrix_nest(a_block, [bc])
    b1 = dolfinx.fem.assemble_vector_nest(L_block)
    dolfinx.fem.apply_lifting_nest(b1, a_block, [bc], x1, scale=-1.0)
    for b_sub in b1.getNestSubVecs():
        b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD,
                          mode=PETSc.ScatterMode.REVERSE)
    bcs0 = dolfinx.cpp.fem.bcs_rows(
        dolfinx.fem.assemble._create_cpp_form(L_block), [bc])
    dolfinx.fem.set_bc_nest(b1, bcs0, x1, scale=-1.0)
    A1.assemble()

    assert A1.getType() == "nest"
    assert nest_matrix_norm(A1) == pytest.approx(Anorm0, 1.0e-12)
    assert b1.norm() == pytest.approx(bnorm0, 1.0e-12)

    # Monolithic version
    E = P0 * P1
    W = dolfinx.function.FunctionSpace(mesh, E)
    dU = ufl.TrialFunction(W)
    U = dolfinx.function.Function(W)
    u0, u1 = ufl.split(U)
    v0, v1 = ufl.TestFunctions(W)

    U.interpolate(lambda x: numpy.row_stack(
        (initial_guess_u(x), initial_guess_p(x))))

    F = inner(u0, v0) * dx + inner(u1, v0) * dx + inner(u0, v1) * dx + inner(u1, v1) * dx \
        - inner(f, v0) * ufl.dx - inner(g, v1) * dx
    J = derivative(F, U, dU)

    bdofsW_V1 = dolfinx.fem.locate_dofs_topological((W.sub(1), V1), facetdim,
                                                    bndry_facets)

    bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofsW_V1, W.sub(1))
    A2 = dolfinx.fem.assemble_matrix(J, [bc])
    A2.assemble()
    b2 = dolfinx.fem.assemble_vector(F)
    dolfinx.fem.apply_lifting(b2, [J], bcs=[[bc]], x0=[U.vector], scale=-1.0)
    b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    dolfinx.fem.set_bc(b2, [bc], x0=U.vector, scale=-1.0)
    assert A2.getType() != "nest"
    assert A2.norm() == pytest.approx(Anorm0, 1.0e-12)
    assert b2.norm() == pytest.approx(bnorm0, 1.0e-12)
Beispiel #29
0
def test_quadrilateral_variant_spectral_dq_l2():
    element = create_element(
        ufl.FiniteElement('DQ L2', ufl.quadrilateral, 1, variant='spectral'))
    assert isinstance(element.element.A, FIAT.GaussLegendre)
    assert isinstance(element.element.B, FIAT.GaussLegendre)
Beispiel #30
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)