def test_analyze_extract():
    '''Test quering expressions for Clement interpolation.'''
    mesh = UnitSquareMesh(40, 40)
    V = FunctionSpace(mesh, 'CG', 1)
    u = TrialFunction(V)
    v = TestFunction(V)
    f = Function(V)
    g = Expression('x[0]', degree=1)
    c = Constant(1)
    n = FacetNormal(mesh)
    e = Function(FunctionSpace(mesh, 'CG', 2))
    x = Function(FunctionSpace(UnitIntervalMesh(100), 'CG', 2))

    # Raises
    expressions = (u, v, inner(u, v), inner(f, v), dot(grad(f), n),
                   inner(grad(f), grad(v)), inner(f, f) * dx, inner(f, f) * ds)
    count = 0
    for expr in expressions:
        try:
            ci._analyze_expr(expr)
        except ValueError:
            count += 1
    assert len(expressions) == count

    # Pass analysis
    expressions = (f, grad(f), inner(f, f) + inner(grad(f), grad(f)),
                   inner(f, g) + c, grad(f)[0], f + g, inner(f, g), c + e,
                   inner(grad(e), grad(f)), x + e, c, g)
    terminals = list(map(ci._analyze_expr, expressions))

    assert all(ci._extract_mesh(term) for i, term in enumerate(terminals[:9]))

    # Fails to extract
    count = 0
    for term in terminals[9:]:
        try:
            ci._extract_mesh(term)
        except ValueError:
            count += 1
    assert 3 == count
def test_analyze_extract():
    '''Test quering expressions for Clement interpolation.'''
    mesh = UnitSquareMesh(40, 40)
    V = FunctionSpace(mesh, 'CG', 1)
    u = TrialFunction(V)
    v = TestFunction(V)
    f = Function(V)
    g = Expression('x[0]', degree=1)
    c = Constant(1)
    n = FacetNormal(mesh)
    e = Function(FunctionSpace(mesh, 'CG', 2))
    x = Function(FunctionSpace(UnitIntervalMesh(100), 'CG', 2))
    
    # Raises
    expressions = (u, v, inner(u, v), inner(f, v), dot(grad(f), n),
                   inner(grad(f), grad(v)), inner(f, f)*dx, inner(f, f)*ds)
    count = 0
    for expr in expressions:
        try: ci._analyze_expr(expr)
        except ValueError:
            count += 1
    assert len(expressions) == count

    # Pass analysis
    expressions = (f, grad(f), inner(f, f) + inner(grad(f), grad(f)), inner(f, g)+c, 
                   grad(f)[0], f+g, inner(f, g), c+e, inner(grad(e), grad(f)),
                   x+e, c, g)
    terminals = map(ci._analyze_expr, expressions)

    assert all(ci._extract_mesh(term) for i, term in enumerate(terminals[:9]))

    # Fails to extract
    count = 0
    for term in terminals[9:]:
        try: ci._extract_mesh(term)
        except ValueError:
            count += 1
    assert 3 == count
def test_summation(mesh=None):
    '''Test logic of summation operator'''
    meshes = (IntervalMesh(100, -1, 2),
              RectangleMesh(Point(-1, -2), Point(2, 4), 10, 10),
              BoxMesh(Point(0, 0, 0), Point(1, 2, 3), 3, 3, 3))
   
    # Run across all dims
    if mesh is None: 
        return [test_summation(mesh) for mesh in range(len(meshes))]

    mesh = meshes[mesh]
    V = FunctionSpace(mesh, 'CG', 1)
    A = ci._construct_summation_operator(V)
    # Shape
    Q = FunctionSpace(mesh, 'DG', 0)
    assert (A.size(0), A.size(1)) == (V.dim(), Q.dim())
    # All nonzero values are 1
    entries = np.unique(A.array().flatten())
    assert all(near(e, 1, 1E-14) for e in entries[np.abs(entries) > 1E-14])

    # The action on a vector of cell volumes should give vector volumes of
    # supports of CG1 functions
    q = TestFunction(Q)
    volumes = assemble(inner(Constant(1), q)*dx)
    patch_volumes = Function(V).vector()
    A.mult(volumes, patch_volumes)

    patch_volumes = patch_volumes.array()
    # Check the logic manually
    dofmap = V.dofmap()
    first, last = dofmap.ownership_range()
    v2d = vertex_to_dof_map(V)

    tdim = mesh.topology().dim()
    mesh.init(0, tdim)
    offproc_b, offproc_dof = [], []
    my_incomplete_dofs = set([])
    for vertex in vertices(mesh):
        # It is alway meaning full to compute locally
        b = sum(Cell(mesh, index).volume() for index in vertex.entities(tdim))
    
        local_dof = v2d[vertex.index()]
        global_dof = dofmap.local_to_global_index(local_dof)
        is_owned = first <= global_dof < last
        # If the vertex is not shared the final answer can be computed and the
        # owner can compute the value with ci
        if not vertex.is_shared():
            assert is_owned
            value0 = b
            assert abs(patch_volumes[local_dof]-value0) < 1E-14
        
    # The way the offprocess things are handled is not supposed to be efficient
        else:
            # Each process collects global dof, b
            offproc_b.append(b)
            offproc_dof.append(global_dof)
            # Record dofs which the process will check later
            if is_owned: 
                my_incomplete_dofs.add(global_dof)
                # This what we will use to look up the value
                assert global_dof - first == local_dof

    # Communicate
    comm = mesh.mpi_comm().tompi4py()

    offproc_dof = np.array(offproc_dof)
    offproc_dof = comm.allgather(offproc_dof)

    offproc_b = np.array(offproc_b)
    offproc_b = comm.allgather(offproc_b)

    # Now the the process that own the dof can look up b from all the
    # processes sum them and compute the final result. Finally do the comparison
    offproc_dof = [vec.tolist() for vec in offproc_dof]
    offproc_b = [vec.tolist() for vec in offproc_b]
    for dof in my_incomplete_dofs:
        b = 0.
        # Look up
        for rank in range(comm.size):
            try:
                # Add if found
                index = offproc_dof[rank].index(dof)
                b += offproc_b[rank][index]

                del offproc_dof[rank][index]
                del offproc_b[rank][index]
            except ValueError:
                pass
        # Final
        value0 = b
        # Compare
        assert abs(patch_volumes[dof-first]-value0) < 1E-14
def test_ci(mesh=None):
    '''Test if clement interpolation works in parallel'''
    meshes = (IntervalMesh(100, -1, 2),
              RectangleMesh(Point(-1, -2), Point(2, 4), 10, 10),
              BoxMesh(Point(0, 0, 0), Point(1, 2, 3), 3, 3, 3))
   
    # Run across all dims
    if mesh is None: 
        return [test_ci(mesh) for mesh in range(len(meshes))]

    mesh = meshes[mesh]
    V = FunctionSpace(mesh, 'DG', 0)
    gdim = mesh.geometry().dim()
    lhs = interpolate(Expression('std::abs(%s)' % '+'.join(['x[%d]' % i for i in range(gdim)]),
                                 degree=1),
                      V)
    # Compute first the interpolant
    uh, CI = ci.clement_interpolate(lhs, True)
    uh_values = uh.vector().array()
    
    # Check the logic manually
    V = uh.function_space()
    dofmap = V.dofmap()
    first, last = dofmap.ownership_range()
    v2d = vertex_to_dof_map(V)

    tdim = mesh.topology().dim()
    mesh.init(0, tdim)
    offproc_bA, offproc_dof = [], []
    my_incomplete_dofs = set([])
    for vertex in vertices(mesh):
        # It is alway meaning full to compute locally
        patch_cells = [Cell(mesh, index) for index in vertex.entities(tdim)]
        volumes = [cell.volume() for cell in patch_cells]
        midpoints = np.array([[cell.midpoint()[i] for i in range(gdim)]
                              for cell in patch_cells])
        b = sum(lhs(mp)*volume for mp, volume in zip(midpoints, volumes))
        A = sum(volumes)
    
        local_dof = v2d[vertex.index()]
        global_dof = dofmap.local_to_global_index(local_dof)
        is_owned = first <= global_dof < last
        # If the vertex is not shared the final answer can be computed and the
        # owner can compure the value with uh
        if not vertex.is_shared():
            assert is_owned
            value0 = b/A
            assert abs(uh_values[local_dof]-value0) < 1E-14
        
    # The way the offprocess things are handled is not supposed to be efficient
        else:
            # Each process collects global dof, b, A
            offproc_bA.append([b, A])
            offproc_dof.append(global_dof)
            # Record dofs which the process will check later
            if is_owned: 
                my_incomplete_dofs.add(global_dof)
                # This what we will use to look up the value
                assert global_dof - first == local_dof

    # Communicate
    comm = mesh.mpi_comm().tompi4py()

    offproc_dof = np.array(offproc_dof)
    offproc_dof = comm.allgather(offproc_dof)

    offproc_bA = np.array(offproc_bA).flatten()
    offproc_bA = comm.allgather(offproc_bA)

    # Now the the process that own the dof can look up b, A from all the
    # processes sum them and compute the final result. Finally do the comparison
    offproc_dof = [vec.tolist() for vec in offproc_dof]
    offproc_bA = [vec.reshape((-1, 2)) for vec in offproc_bA]
    for dof in my_incomplete_dofs:
        bA = np.zeros(2)
        # Look up
        for rank in range(comm.size):
            try:
                # Add if found
                index = offproc_dof[rank].index(dof)
                bA += offproc_bA[rank][index]

                del offproc_dof[rank][index]
                offproc_bA[rank] = np.delete(offproc_bA[rank], index, 0)
            except ValueError:
                pass
        # Final
        b, A = bA
        value0 = b/A
        # Compare
        assert abs(uh_values[dof-first]-value0) < 1E-14
def test_averaging(mesh=None):
    '''Test logic of averaging operator'''
    meshes = (IntervalMesh(100, -1, 2),
              RectangleMesh(Point(-1, -2), Point(2, 4), 10, 10),
              BoxMesh(Point(0, 0, 0), Point(1, 2, 3), 3, 3, 3))
   
    # Run across all dims
    if mesh is None: 
        return [test_averaging(mesh) for mesh in range(len(meshes))]

    mesh = meshes[mesh]
    V = FunctionSpace(mesh, 'CG', 1)
    Q = FunctionSpace(mesh, 'DG', 0)
    q = TestFunction(Q)
    c = assemble(inner(Constant(1), q)*dx)

    A = ci._construct_averaging_operator(V, c)
    # Shape check
    assert (A.size(0), A.size(1)) == (V.dim(), Q.dim())

    # The action on a vector of 1 is n / sum(volumes of cell in patch) where n
    # is the number of cell in the patch
    ones = interpolate(Constant(1), Q).vector()
    avg_ones = Function(V).vector()
    A.mult(ones, avg_ones)

    avg_ones = avg_ones.array()
    # Check the logic manually
    dofmap = V.dofmap()
    first, last = dofmap.ownership_range()
    v2d = vertex_to_dof_map(V)

    tdim = mesh.topology().dim()
    mesh.init(0, tdim)
    offproc_data, offproc_dof = [], []
    my_incomplete_dofs = set([])
    for vertex in vertices(mesh):
        # It is alway meaning full to compute locally
        volume = sum(Cell(mesh, index).volume() for index in vertex.entities(tdim))
        count = len(vertex.entities(tdim))
    
        local_dof = v2d[vertex.index()]
        global_dof = dofmap.local_to_global_index(local_dof)
        is_owned = first <= global_dof < last
        # If the vertex is not shared the final answer can be computed and the
        # owner can compute the value with ci
        if not vertex.is_shared():
            assert is_owned
            value0 = count/volume
            assert abs(avg_ones[local_dof]-value0) < 1E-13, '%g %g' % (avg_ones[local_dof], value0)

    # The way the offprocess things are handled is not supposed to be efficient
        else:
            # Each process collects global dof, ...
            offproc_data.append([count, volume])
            offproc_dof.append(global_dof)
            # Record dofs which the process will check later
            if is_owned: 
                my_incomplete_dofs.add(global_dof)
                # This what we will use to look up the value
                assert global_dof - first == local_dof

    # Communicate
    comm = mesh.mpi_comm().tompi4py()

    offproc_dof = np.array(offproc_dof)
    offproc_dof = comm.allgather(offproc_dof)

    offproc_data = np.array(offproc_data).flatten()
    offproc_data = comm.allgather(offproc_data)

    # Now the the process that own the dof can look up ... from all the
    # processes sum them and compute the final result. Finally do the comparison
    offproc_dof = [vec.tolist() for vec in offproc_dof]
    offproc_data = [vec.reshape((-1, 2)) for vec in offproc_data]
    for dof in my_incomplete_dofs:
        count_volume = np.zeros(2)
        # Look up
        for rank in range(comm.size):
            try:
                # Add if found
                index = offproc_dof[rank].index(dof)
                count_volume += offproc_data[rank][index]

                del offproc_dof[rank][index]
                offproc_data[rank] = np.delete(offproc_data[rank], index, 0)
            except ValueError:
                pass
        # Final
        count, volume = count_volume
        value0 = count/volume
        # Compare
        assert abs(avg_ones[dof-first]-value0) < 1E-13, '%g %g' % (avg_ones[dof-first], value0)
def test_summation(mesh=None):
    '''Test logic of summation operator'''
    meshes = (IntervalMesh(100, -1,
                           2), RectangleMesh(Point(-1, -2), Point(2, 4), 10,
                                             10),
              BoxMesh(Point(0, 0, 0), Point(1, 2, 3), 3, 3, 3))

    # Run across all dims
    if mesh is None:
        return [test_summation(mesh) for mesh in range(len(meshes))]

    mesh = meshes[mesh]
    V = FunctionSpace(mesh, 'CG', 1)
    A = ci._construct_summation_operator(V)
    # Shape
    Q = FunctionSpace(mesh, 'DG', 0)
    assert (A.size(0), A.size(1)) == (V.dim(), Q.dim())
    # All nonzero values are 1
    entries = np.unique(A.array().flatten())
    assert all(near(e, 1, 1E-14) for e in entries[np.abs(entries) > 1E-14])

    # The action on a vector of cell volumes should give vector volumes of
    # supports of CG1 functions
    q = TestFunction(Q)
    volumes = assemble(inner(Constant(1), q) * dx)
    patch_volumes = Function(V).vector()
    A.mult(volumes, patch_volumes)

    patch_volumes = patch_volumes.get_local()
    # Check the logic manually
    dofmap = V.dofmap()
    first, last = dofmap.ownership_range()
    v2d = vertex_to_dof_map(V)

    tdim = mesh.topology().dim()
    mesh.init(0, tdim)
    offproc_b, offproc_dof = [], []
    my_incomplete_dofs = set([])
    for vertex in vertices(mesh):
        # It is alway meaning full to compute locally
        b = sum(Cell(mesh, index).volume() for index in vertex.entities(tdim))

        local_dof = v2d[vertex.index()]
        global_dof = dofmap.local_to_global_index(local_dof)
        is_owned = first <= global_dof < last
        # If the vertex is not shared the final answer can be computed and the
        # owner can compute the value with ci
        if not vertex.is_shared():
            assert is_owned
            value0 = b
            assert abs(patch_volumes[local_dof] - value0) < 1E-14

    # The way the offprocess things are handled is not supposed to be efficient
        else:
            # Each process collects global dof, b
            offproc_b.append(b)
            offproc_dof.append(global_dof)
            # Record dofs which the process will check later
            if is_owned:
                my_incomplete_dofs.add(global_dof)
                # This what we will use to look up the value
                assert global_dof - first == local_dof

    # Communicate
    #comm = mesh.mpi_comm().tompi4py()
    comm = MPI.comm_world

    offproc_dof = np.array(offproc_dof)
    offproc_dof = comm.allgather(offproc_dof)

    offproc_b = np.array(offproc_b)
    offproc_b = comm.allgather(offproc_b)

    # Now the the process that own the dof can look up b from all the
    # processes sum them and compute the final result. Finally do the comparison
    offproc_dof = [vec.tolist() for vec in offproc_dof]
    offproc_b = [vec.tolist() for vec in offproc_b]
    for dof in my_incomplete_dofs:
        b = 0.
        # Look up
        for rank in range(comm.size):
            try:
                # Add if found
                index = offproc_dof[rank].index(dof)
                b += offproc_b[rank][index]

                del offproc_dof[rank][index]
                del offproc_b[rank][index]
            except ValueError:
                pass
        # Final
        value0 = b
        # Compare
        assert abs(patch_volumes[dof - first] - value0) < 1E-14
def test_ci(mesh=None):
    '''Test if clement interpolation works in parallel'''
    meshes = (IntervalMesh(100, -1,
                           2), RectangleMesh(Point(-1, -2), Point(2, 4), 10,
                                             10),
              BoxMesh(Point(0, 0, 0), Point(1, 2, 3), 3, 3, 3))

    # Run across all dims
    if mesh is None:
        return [test_ci(mesh) for mesh in range(len(meshes))]

    mesh = meshes[mesh]
    V = FunctionSpace(mesh, 'DG', 0)
    gdim = mesh.geometry().dim()
    lhs = interpolate(
        Expression('std::abs(%s)' %
                   '+'.join(['x[%d]' % i for i in range(gdim)]),
                   degree=1), V)
    # Compute first the interpolant
    uh, CI = ci.clement_interpolate(lhs, True)
    uh_values = uh.vector().get_local()

    # Check the logic manually
    V = uh.function_space()
    dofmap = V.dofmap()
    first, last = dofmap.ownership_range()
    v2d = vertex_to_dof_map(V)

    tdim = mesh.topology().dim()
    mesh.init(0, tdim)
    offproc_bA, offproc_dof = [], []
    my_incomplete_dofs = set([])
    for vertex in vertices(mesh):
        # It is alway meaning full to compute locally
        patch_cells = [Cell(mesh, index) for index in vertex.entities(tdim)]
        volumes = [cell.volume() for cell in patch_cells]
        midpoints = np.array([[cell.midpoint()[i] for i in range(gdim)]
                              for cell in patch_cells])
        b = sum(lhs(mp) * volume for mp, volume in zip(midpoints, volumes))
        A = sum(volumes)

        local_dof = v2d[vertex.index()]
        global_dof = dofmap.local_to_global_index(local_dof)
        is_owned = first <= global_dof < last
        # If the vertex is not shared the final answer can be computed and the
        # owner can compure the value with uh
        if not vertex.is_shared():
            assert is_owned
            value0 = b / A
            assert abs(uh_values[local_dof] - value0) < 1E-14

    # The way the offprocess things are handled is not supposed to be efficient
        else:
            # Each process collects global dof, b, A
            offproc_bA.append([b, A])
            offproc_dof.append(global_dof)
            # Record dofs which the process will check later
            if is_owned:
                my_incomplete_dofs.add(global_dof)
                # This what we will use to look up the value
                assert global_dof - first == local_dof

    # Communicate
    comm = MPI.comm_world
    offproc_dof = np.array(offproc_dof)
    offproc_dof = comm.allgather(offproc_dof)

    offproc_bA = np.array(offproc_bA).flatten()
    offproc_bA = comm.allgather(offproc_bA)

    # Now the the process that own the dof can look up b, A from all the
    # processes sum them and compute the final result. Finally do the comparison
    offproc_dof = [vec.tolist() for vec in offproc_dof]
    offproc_bA = [vec.reshape((-1, 2)) for vec in offproc_bA]
    for dof in my_incomplete_dofs:
        bA = np.zeros(2)
        # Look up
        for rank in range(comm.size):
            try:
                # Add if found
                index = offproc_dof[rank].index(dof)
                bA += offproc_bA[rank][index]

                del offproc_dof[rank][index]
                offproc_bA[rank] = np.delete(offproc_bA[rank], index, 0)
            except ValueError:
                pass
        # Final
        b, A = bA
        value0 = b / A
        # Compare
        assert abs(uh_values[dof - first] - value0) < 1E-14
def test_averaging(mesh=None):
    '''Test logic of averaging operator'''
    meshes = (IntervalMesh(100, -1,
                           2), RectangleMesh(Point(-1, -2), Point(2, 4), 10,
                                             10),
              BoxMesh(Point(0, 0, 0), Point(1, 2, 3), 3, 3, 3))

    # Run across all dims
    if mesh is None:
        return [test_averaging(mesh) for mesh in range(len(meshes))]

    mesh = meshes[mesh]
    V = FunctionSpace(mesh, 'CG', 1)
    Q = FunctionSpace(mesh, 'DG', 0)
    q = TestFunction(Q)
    c = assemble(inner(Constant(1), q) * dx)

    A = ci._construct_averaging_operator(V, c)
    # Shape check
    assert (A.size(0), A.size(1)) == (V.dim(), Q.dim())

    # The action on a vector of 1 is n / sum(volumes of cell in patch) where n
    # is the number of cell in the patch
    ones = interpolate(Constant(1), Q).vector()
    avg_ones = Function(V).vector()
    A.mult(ones, avg_ones)

    avg_ones = avg_ones.get_local()
    # Check the logic manually
    dofmap = V.dofmap()
    first, last = dofmap.ownership_range()
    v2d = vertex_to_dof_map(V)

    tdim = mesh.topology().dim()
    mesh.init(0, tdim)
    offproc_data, offproc_dof = [], []
    my_incomplete_dofs = set([])
    for vertex in vertices(mesh):
        # It is alway meaning full to compute locally
        volume = sum(
            Cell(mesh, index).volume() for index in vertex.entities(tdim))
        count = len(vertex.entities(tdim))

        local_dof = v2d[vertex.index()]
        global_dof = dofmap.local_to_global_index(local_dof)
        is_owned = first <= global_dof < last
        # If the vertex is not shared the final answer can be computed and the
        # owner can compute the value with ci
        if not vertex.is_shared():
            assert is_owned
            value0 = count / volume
            assert abs(avg_ones[local_dof] -
                       value0) < 1E-13, '%g %g' % (avg_ones[local_dof], value0)

    # The way the offprocess things are handled is not supposed to be efficient
        else:
            # Each process collects global dof, ...
            offproc_data.append([count, volume])
            offproc_dof.append(global_dof)
            # Record dofs which the process will check later
            if is_owned:
                my_incomplete_dofs.add(global_dof)
                # This what we will use to look up the value
                assert global_dof - first == local_dof

    # Communicate
    comm = MPI.comm_world
    offproc_dof = np.array(offproc_dof)
    offproc_dof = comm.allgather(offproc_dof)

    offproc_data = np.array(offproc_data).flatten()
    offproc_data = comm.allgather(offproc_data)

    # Now the the process that own the dof can look up ... from all the
    # processes sum them and compute the final result. Finally do the comparison
    offproc_dof = [vec.tolist() for vec in offproc_dof]
    offproc_data = [vec.reshape((-1, 2)) for vec in offproc_data]
    for dof in my_incomplete_dofs:
        count_volume = np.zeros(2)
        # Look up
        for rank in range(comm.size):
            try:
                # Add if found
                index = offproc_dof[rank].index(dof)
                count_volume += offproc_data[rank][index]

                del offproc_dof[rank][index]
                offproc_data[rank] = np.delete(offproc_data[rank], index, 0)
            except ValueError:
                pass
        # Final
        count, volume = count_volume
        value0 = count / volume
        # Compare
        assert abs(avg_ones[dof - first] -
                   value0) < 1E-13, '%g %g' % (avg_ones[dof - first], value0)