Пример #1
0
def block_reshape(AA, offsets):
    '''Group rows/cols according to offsets'''
    nblocks = len(offsets)
    mat = block_mat([[0]*nblocks for _ in range(nblocks)])

    offsets = [0] + list(offsets)
    AA = AA.blocks
    for row, (ri, rj) in enumerate(zip(offsets[:-1], offsets[1:])):
        for col, (ci, cj) in enumerate(zip(offsets[:-1], offsets[1:])):
            if rj-ri == 1 and cj -ci == 1:
                mat[row][col] = AA[ri, ci]
            else:
                mat[row][col] = block_mat(AA[ri:rj, ci:cj])

    return mat
Пример #2
0
def main(n):
    '''Solves grad-div problem in 2d with HypreAMS preconditioning'''
    # Exact solution
    x, y = sp.symbols('x[0] x[1]')
    
    u = sp.sin(pi*x*(1-x)*y*(1-y))

    sp_div = lambda f: f[0].diff(x, 1) + f[1].diff(y, 1)

    sp_grad = lambda f: sp.Matrix([f.diff(x, 1), f.diff(y, 1)])

    sigma = sp_grad(u)    
    f = -sp_div(sigma) + u

    sigma_expr, u_expr, f_expr = map(as_expression, (sigma, u, f))

    # The discrete problem
    mesh = UnitSquareMesh(n, n)
    
    V = FunctionSpace(mesh, 'RT', 1)
    Q = FunctionSpace(mesh, 'DG', 0)
    W = (V, Q)

    sigma, u = map(TrialFunction, W)
    tau, v = map(TestFunction, W)

    a00 = inner(sigma, tau)*dx
    a01 = inner(div(tau), u)*dx
    a10 = inner(div(sigma), v)*dx
    a11 = -inner(u, v)*dx

    L0 = inner(Constant((0, 0)), tau)*dx
    L1 = inner(-f_expr, v)*dx

    AA = block_assemble([[a00, a01], [a10, a11]])
    bb = block_assemble([L0, L1])

    # b00 = inner(sigma, tau)*dx + inner(div(sigma), div(tau))*dx
    # B00 = LU(assemble(b00))
    B00 = HypreAMS(V)
    
    b11 = inner(u, v)*dx
    B11 = LumpedInvDiag(assemble(b11))

    BB = block_mat([[B00, 0], [0, B11]])
    
    AAinv = MinRes(AA, precond=BB, tolerance=1e-10, maxiter=500, show=2)

    # Compute solution
    sigma_h, u_h = AAinv * bb
    sigma_h, u_h = Function(V, sigma_h), Function(Q, u_h)

    niters = len(AAinv.residuals) - 1
    # error = sqrt(errornorm(sigma_expr, sigma_h, 'Hdiv', degree_rise=1)**2 +
    #              errornorm(u_expr, u_h, 'L2', degree_rise=1)**2)

    hmin = mesh.mpi_comm().tompi4py().allreduce(mesh.hmin(), pyMPI.MIN)
    error = 1.

    return hmin, V.dim()+Q.dim(), niters, error
Пример #3
0
def block_diag_mat(diagonal):
    '''A block diagonal matrix'''
    blocks = np.zeros((len(diagonal), ) * 2, dtype='object')
    for i, A in enumerate(diagonal):
        blocks[i, i] = A

    return block_mat(blocks)
Пример #4
0
def main(n):
    '''Solves grad-div problem in 2d with HypreAMS preconditioning'''
    # Exact solution
    x, y = sp.symbols('x[0] x[1]')

    u = sp.sin(pi * x * (1 - x) * y * (1 - y))

    sp_div = lambda f: f[0].diff(x, 1) + f[1].diff(y, 1)

    sp_grad = lambda f: sp.Matrix([f.diff(x, 1), f.diff(y, 1)])

    sigma = sp_grad(u)
    f = -sp_div(sigma) + u

    sigma_expr, u_expr, f_expr = list(map(as_expression, (sigma, u, f)))

    # The discrete problem
    mesh = UnitSquareMesh(n, n)

    V = FunctionSpace(mesh, 'RT', 1)
    Q = FunctionSpace(mesh, 'DG', 0)
    W = (V, Q)

    sigma, u = list(map(TrialFunction, W))
    tau, v = list(map(TestFunction, W))

    a00 = inner(sigma, tau) * dx
    a01 = inner(div(tau), u) * dx
    a10 = inner(div(sigma), v) * dx
    a11 = -inner(u, v) * dx

    L0 = inner(Constant((0, 0)), tau) * dx
    L1 = inner(-f_expr, v) * dx

    AA = block_assemble([[a00, a01], [a10, a11]])
    bb = block_assemble([L0, L1])

    # b00 = inner(sigma, tau)*dx + inner(div(sigma), div(tau))*dx
    # B00 = LU(assemble(b00))
    B00 = HypreAMS(V)

    b11 = inner(u, v) * dx
    B11 = LumpedInvDiag(assemble(b11))

    BB = block_mat([[B00, 0], [0, B11]])

    AAinv = MinRes(AA, precond=BB, tolerance=1e-10, maxiter=500, show=2)

    # Compute solution
    sigma_h, u_h = AAinv * bb
    sigma_h, u_h = Function(V, sigma_h), Function(Q, u_h)

    niters = len(AAinv.residuals) - 1
    # error = sqrt(errornorm(sigma_expr, sigma_h, 'Hdiv', degree_rise=1)**2 +
    #              errornorm(u_expr, u_h, 'L2', degree_rise=1)**2)

    hmin = mesh.mpi_comm().tompi4py().allreduce(mesh.hmin(), pyMPI.MIN)
    error = 1.

    return hmin, V.dim() + Q.dim(), niters, error
Пример #5
0
def as_block(monolithic, blocks):
    '''Turn monolithic operator into block-structured one with indices specified in blocks'''
    comm = mpi_comm_world()

    # We want list of PETSc.IS-es
    elm_type, = element_types(blocks)
    if elm_type is FunctionSpace:
        offsets = np.cumsum(np.r_[0, [Wi.dim() for Wi in blocks]])

        idx = []
        for first, last in zip(offsets[:-1], offsets[1:]):
            idx.append(PETSc.IS().createStride(last - first, first, 1))
        return as_block(monolithic, idx)

    elif elm_type in (list, np.ndarray):
        return as_block(monolithic, [
            PETSc.IS().createGeneral(np.asarray(block, dtype='int32'))
            for block in blocks
        ])

    assert elm_type is PETSc.IS

    # Break up Vector to block-vec
    if isinstance(monolithic, (GenericVector, Vector)):
        return as_block(as_backend_type(monolithic).vec(), blocks)

    if isinstance(monolithic, (GenericMatrix, Matrix)):
        return as_block(as_backend_type(monolithic).mat(), blocks)

    if isinstance(monolithic, PETSc.Vec):
        b = [
            PETSc.Vec().createWithArray(monolithic.getValues(block), comm=comm)
            for block in blocks
        ]
        for bi in b:
            bi.assemblyBegin()
            bi.assemblyEnd()
        return block_vec(list(map(PETScVector, b)))

    # Otherwise we have a Matrix
    try:
        monolithic.getSubMatrix
        A = [[
            PETScMatrix(monolithic.getSubMatrix(block_row, block_col))
            for block_col in blocks
        ] for block_row in blocks]
    # NOTE: 3.8+ does not ahve getSubMatrix, there is getLocalSubMatrix
    # but cannot get that to work - petsc error 73. So for now everything
    # is done with scipy
    except AttributeError:
        monolithic = csr_matrix(monolithic.getValuesCSR()[::-1],
                                shape=monolithic.size)

        A = [[
            numpy_to_petsc(monolithic[block_row.array, :][:, block_col.array])
            for block_col in blocks
        ] for block_row in blocks]
    # Done
    return block_mat(A)
Пример #6
0
def convert(bmat, algorithm='numpy'):
    '''
    Attempt to convert bmat to a PETSc(Matrix/Vector) object.
    If succed this is at worst a number.
    '''
    # Block vec conversion
    if isinstance(bmat, block_vec):
        array = block_vec_to_numpy(bmat)
        vec = PETSc.Vec().createWithArray(array)
        vec.assemble()
        return PETScVector(vec)
    
    # Conversion of bmat is bit more involved because of the possibility
    # that some of the blocks are numbers or composition of matrix operations
    if isinstance(bmat, block_mat):
        # Create collpsed bmat
        row_sizes, col_sizes = bmat_sizes(bmat)
        nrows, ncols = len(row_sizes), len(col_sizes)
        indices = itertools.product(list(range(nrows)), list(range(ncols)))

        blocks = np.zeros((nrows, ncols), dtype='object')
        for block, (i, j) in zip(bmat.blocks.flatten(), indices):
            # This might is guaranteed to be matrix or number
            A = collapse(block)

            if is_number(A):
                # Diagonal matrices can be anything provided square
                if i == j and row_sizes[i] == col_sizes[j]:
                    A = diagonal_matrix(row_sizes[i], A)
                else:
                    # Offdiagonal can only be zero
                    A = zero_matrix(row_sizes[i], col_sizes[j])
                #else:
                #    A = 0
            # The converted block
            blocks[i, j] = A
        # Now every block is a matrix/number and we can make a monolithic thing
        bmat = block_mat(blocks)

        assert all(is_petsc_mat(block) or is_number(block)
                   for block in bmat.blocks.flatten())
        
        # Opt out of monolithic
        if not algorithm:
            set_lg_map(bmat)
            return bmat
        
        # Monolithic via numpy (fast)
        # Convert to numpy
        array = block_mat_to_numpy(bmat)
        # Constuct from numpy
        bmat = numpy_to_petsc(array)
        set_lg_map(bmat)

        return bmat

    # Try with a composite
    return collapse(bmat)
Пример #7
0
def identity_matrix(V):
    '''u -> u for u in V'''
    if isinstance(V, FunctionSpace):
        return diagonal_matrix(V.dim(), 1)

    mat = block_mat([[0]*len(V) for _ in range(len(V))])
    for i in range(len(mat)):
        mat[i][i] = identity_matrix(V[i])
    return mat
Пример #8
0
def convert(bmat, algorithm='numpy'):
    '''
    Attempt to convert bmat to a PETSc(Matrix/Vector) object.
    If succed this is at worst a number.
    '''
    # Block vec conversion
    if isinstance(bmat, block_vec):
        array = block_vec_to_numpy(bmat)
        vec = PETSc.Vec().createWithArray(array)
        vec.assemble()
        return PETScVector(vec)
    
    # Conversion of bmat is bit more involved because of the possibility
    # that some of the blocks are numbers or composition of matrix operations
    if isinstance(bmat, block_mat):
        # Create collpsed bmat
        row_sizes, col_sizes = bmat_sizes(bmat)
        nrows, ncols = len(row_sizes), len(col_sizes)
        indices = itertools.product(range(nrows), range(ncols))
        
        blocks = np.zeros((nrows, ncols), dtype='object')
        for block, (i, j) in zip(bmat.blocks.flatten(), indices):
            # This might is guaranteed to be matrix or number
            A = collapse(block)

            if is_number(A):
                # Diagonal matrices can be anything provided square
                if i == j and row_sizes[i] == col_sizes[j]:
                    A = diagonal_matrix(row_sizes[i], A)
                else:
                    # Offdiagonal can only be zero
                    A = zero_matrix(row_sizes[i], col_sizes[j])
                #else:
                #    A = 0
            # The converted block
            blocks[i, j] = A
        # Now every block is a matrix/number and we can make a monolithic thing
        bmat = block_mat(blocks)

        assert all(is_petsc_mat(block) or is_number(block)
                   for block in bmat.blocks.flatten())
        
        # Opt out of monolithic
        if not algorithm: return bmat
        
        # Monolithic via numpy (fast)
        # Convert to numpy
        array = block_mat_to_numpy(bmat)
        # Constuct from numpy
        return numpy_to_petsc(array)

    # Try with a composite
    return collapse(bmat)
Пример #9
0
def block_tensor(obj):
    """Return either a block_vec or a block_mat, depending on the shape of the object"""
    from block import block_mat, block_vec
    import numpy
    if isinstance(obj, (block_mat, block_vec)):
        return obj

    from ufl import Form
    if isinstance(obj, Form):
        from .splitting import split_form
        obj = split_form(obj)
    blocks = numpy.array(obj)
    if len(blocks.shape) == 2:
        return block_mat(blocks)
    elif len(blocks.shape) == 1:
        return block_vec(blocks)
    else:
        raise RuntimeError("Not able to create block container of rank %d" %
                           len(blocks.shape))
Пример #10
0
def setup_problem(n, mms, params):
    '''Poisson solver'''
    mesh = UnitCubeMesh(n, n, n)
    
    V = FunctionSpace(mesh, 'CG', 1)

    # Just one
    boundaries = MeshFunction('size_t', mesh, 2, 0)
    for tag, subd in enumerate(mms.subdomains, 1):
        CompiledSubDomain(subd, tol=1E-10).mark(boundaries, tag)

    u_true,  = mms.solution
    bc = DirichletBC(V, u_true, boundaries, 1)

    f, = mms.rhs
    
    u, v = TrialFunction(V), TestFunction(V)
    a = inner(grad(u), grad(v))*dx
    L = inner(f, v)*dx

    A, b = assemble_system(a, L, bc)

    return block_mat([[A]]), block_vec([b]), [V]
Пример #11
0
def set_lg_map(mat):
    '''Set local-to-global-map on the matrix'''
    # NOTE; serial only - so we own everything but still sometimes we need
    # to tell that to petsc (especiialy when bcs are to be applied)

    if is_number(mat): return mat

    assert is_petsc_mat(mat) or isinstance(mat, block_mat), (type(mat))

    if isinstance(mat, block_mat):
        blocks = np.array(map(set_lg_map, mat.blocks.flatten())).reshape(mat.blocks.shape)
        return block_mat(blocks)

    comm = mpi_comm_world().tompi4py()
    # Work with matrix
    rowmap, colmap = range(mat.size(0)), range(mat.size(1))

    row_lgmap = PETSc.LGMap().create(rowmap, comm=comm)
    col_lgmap = PETSc.LGMap().create(colmap, comm=comm)

    as_petsc(mat).setLGMap(row_lgmap, col_lgmap)

    return mat
Пример #12
0
def set_lg_map(mat):
    '''Set local-to-global-map on the matrix'''
    # NOTE; serial only - so we own everything but still sometimes we need
    # to tell that to petsc (especiialy when bcs are to be applied)

    if is_number(mat): return mat

    assert is_petsc_mat(mat) or isinstance(mat, block_mat), (type(mat))

    if isinstance(mat, block_mat):
        blocks = np.array(map(set_lg_map, mat.blocks.flatten())).reshape(mat.blocks.shape)
        return block_mat(blocks)

    comm = mpi_comm_world().tompi4py()
    # Work with matrix
    rowmap, colmap = range(mat.size(0)), range(mat.size(1))

    row_lgmap = PETSc.LGMap().create(rowmap, comm=comm)
    col_lgmap = PETSc.LGMap().create(colmap, comm=comm)

    as_petsc(mat).setLGMap(row_lgmap, col_lgmap)

    return mat
Пример #13
0
L1 = inner(f, c) * dx

from block import block_assemble, block_bc, block_mat
from block.iterative import MinRes
from block.algebraic.petsc import ML, Cholesky

# lhs
AA = block_assemble([[a00, a01, a02], [a10, a11, a12], [a20, a21, 0]])
# rhs
b = block_assemble([L0, L1, 0])

# Collect boundary conditions
bcs = [[bc0, bc1], [], []]
block_bc(bcs, True).apply(AA).apply(b)

b22 = inner(p, q) * dx
B22 = assemble(b22)
# Possible action of preconditioner
BB = block_mat([[ML(AA[0, 0]), 0, 0], [0, Cholesky(AA[1, 1]), 0],
                [0, 0, Cholesky(B22)]])

AAinv = MinRes(AA, precond=BB, tolerance=1E-10, maxiter=500)
U, B, P = AAinv * b

p = Function(Q)
p.vector()[:] = P

# Plot solution
plot(p)
interactive()
Пример #14
0
def apply_bc(A, b, bcs, diag_val=1., return_apply_b=False):
    '''
    Apply block boundary conditions to block system A, b    
    '''
    # Specs of do nothing
    if bcs is None or not bcs or (not any(bc for bc in bcs)
                                  and isinstance(bcs, list)):
        return A, b

    # Allow for A, b be simple matrices. To proceed we wrap them as
    # block objects
    has_wrapped_A = False
    if not isinstance(A, block_mat):
        A = block_mat([[A]])
        has_wrapped_A = True

    if b is None:
        b = A.create_vec()

    if not isinstance(b, block_vec):
        assert has_wrapped_A
        b = block_vec([b])

    if isinstance(bcs, dict):
        assert all(0 <= k < len(A) for k in list(bcs.keys()))
        assert all(isinstance(v, list) for v in list(bcs.values()))
        bcs = [bcs.get(i, []) for i in range(len(A))]
    else:
        if has_wrapped_A:
            bcs = [bcs]

    # block boundary conditions is a list with bcs for each block of A, b
    assert len(A) == len(b) == len(bcs), (len(A), len(b), len(bcs))

    bcs_ = []
    for bc in bcs:
        assert isinstance(bc, (DirichletBC, list))
        if isinstance(bc, DirichletBC):
            bcs_.append([bc])
        else:
            bcs_.append(bc)
    bcs = bcs_

    if not bcs or not any(bcs): return A, b

    # Obtain a monolithic matrix
    AA, bb = list(map(convert, (A, b)))

    # PETSc guys
    AA, bb = as_backend_type(AA).mat(), as_backend_type(bb).vec()

    # We want to make a monotlithic matrix corresponding to function sp ace
    # where the spaces are serialized

    # Break apart for block
    offsets = [0]
    for bi in b:
        offsets.append(offsets[-1] + bi.size())

    assert AA.size[0] == AA.size[1] == offsets[-1], (AA.size[0], AA.size[1],
                                                     offsets)

    rows = []
    x_values = []
    # Each bc in the group has dofs numbered only wrt to its space.
    # We're after global numbering
    for shift, bcs_sub in zip(offsets, bcs):
        for bc in bcs_sub:
            # NOTE: bcs can be a dict or DirichletBC in which case we extract
            # the dict
            if isinstance(bc, DirichletBC): bc = bc.get_boundary_values()
            # Dofs and values for rhs
            rows.extend(shift + np.array(list(bc.keys()), dtype='int32'))
            x_values.extend(list(bc.values()))

    rows = np.hstack(rows)
    x_values = np.array(x_values)

    x = bb.copy()
    x.zeroEntries()
    x.setValues(rows, x_values)
    x.assemble()

    blocks = []
    for first, last in zip(offsets[:-1], offsets[1:]):
        blocks.append(PETSc.IS().createStride(last - first, first, 1))

    if return_apply_b:
        # We don't want to reassemble system and apply bcs (when they are
        # updated) to the (new) matrix and the rhs. Matrix is expensive
        # AND the change of bcs only effects the rhs. So what we build
        # here is a function which applies updated bcs only to rhs as
        # if by assemble_system (i.e. in a symmetric way)
        def apply_b(bb, AA=AA.copy(), dofs=rows, bcs=bcs, blocks=blocks):
            # Pick up the updated values
            values = np.array(
                sum((list(bc.get_boundary_values().values()) if isinstance(
                    bc, DirichletBC) else [] for bcs_sub in bcs
                     for bc in bcs_sub), []))
            # Taken from cbc.block
            c, Ac = AA.createVecs()

            c.zeroEntries()
            c.setValues(dofs, values)  # Forward
            c.assemble()

            AA.mult(c, Ac)  # Note: we work with the deep copy of A

            b = convert(bb)  # We cond't get view here ...
            b_vec = as_backend_type(b).vec()
            b_vec.axpy(-1, Ac)  # Elliminate
            b_vec.setValues(dofs, values)
            b_vec.assemble()

            bb *= 0  # ... so copy
            bb += as_block(b, blocks)

            return bb

    # Apply to monolithic
    len(rows) and AA.zeroRowsColumns(rows, diag=diag_val, x=x, b=bb)

    # Reasamble into block shape
    b = as_block(bb, blocks)
    A = as_block(AA, blocks)

    if has_wrapped_A: return A[0][0], b[0]

    if return_apply_b:
        return A, b, apply_b

    return A, b
Пример #15
0
V = VectorFunctionSpace(mesh, 'CG', 1)

u, v = TrialFunction(V), TestFunction(V)
dofs_x = V.sub(0).dofmap().dofs()
dofs_y = V.sub(1).dofmap().dofs()

M = assemble(inner(div(u), div(v))*dx)

# With cbc.block
Q = FunctionSpace(mesh, 'CG', 1)
p, q = TrialFunction(Q), TestFunction(Q)
block00 = assemble(inner(p.dx(0), q.dx(0))*dx)
block01 = assemble(inner(p.dx(1), q.dx(0))*dx)
block10 = assemble(inner(p.dx(0), q.dx(1))*dx)
block11 = assemble(inner(p.dx(1), q.dx(1))*dx)
Mb = block_mat([[block00, block01],
                [block10, block11]])

# The equivalence: let's see how the two operators act on representation of the same
# function. For cbc block the dofs of subspaces are serialized.
x_array = np.random.rand(V.dim())
x = Function(V).vector(); x.set_local(x_array); x.apply('insert')
# Reorder for cbc.block
xx_array = x_array[dofs_x]
xy_array = x_array[dofs_y]
xx = Function(Q).vector(); xx.set_local(xx_array); xx.apply('insert')
xy = Function(Q).vector(); xy.set_local(xy_array); xy.apply('insert')
xb = block_vec([xx, xy])

# Action
y = x.copy()
M.mult(x, y)
Пример #16
0
b_array = b.array()
print np.linalg.norm(b_array[dofs_y])
# (0, vy) - so first comp is 0
b = assemble(inner(vy, f) * dx)
b_array = b.array()
print np.linalg.norm(b_array[dofs_x])

# Consider the mass matrix
u = TrialFunction(V)
M = assemble(inner(u, v) * dx)

# With cbc.block
Q = FunctionSpace(mesh, 'CG', 1)
p, q = TrialFunction(Q), TestFunction(Q)
block = assemble(inner(p, q) * dx)
Mb = block_mat([[block, 0], [0, block]])

# The equivalence: let's see how the two operators act on representation of the same
# function. For cbc block the dofs of subspaces are serialized.
x_array = np.random.rand(V.dim())
x = Function(V).vector()
x.set_local(x_array)
x.apply('insert')
# Reorder for cbc.block
xx_array = x_array[dofs_x]
xy_array = x_array[dofs_y]
xx = Function(Q).vector()
xx.set_local(xx_array)
xx.apply('insert')
xy = Function(Q).vector()
xy.set_local(xy_array)
Пример #17
0
b_array = b.array()
print np.linalg.norm(b_array[dofs_y])
# (0, vy) - so first comp is 0
b = assemble(inner(vy, f)*dx)
b_array = b.array()
print np.linalg.norm(b_array[dofs_x])

# Consider the mass matrix
u = TrialFunction(V)
M = assemble(inner(u, v)*dx)

# With cbc.block
Q = FunctionSpace(mesh, 'CG', 1)
p, q = TrialFunction(Q), TestFunction(Q)
block = assemble(inner(p, q)*dx)
Mb = block_mat([[block, 0],
                [0, block]])

# The equivalence: let's see how the two operators act on representation of the same
# function. For cbc block the dofs of subspaces are serialized.
x_array = np.random.rand(V.dim())
x = Function(V).vector(); x.set_local(x_array); x.apply('insert')
# Reorder for cbc.block
xx_array = x_array[dofs_x]
xy_array = x_array[dofs_y]
xx = Function(Q).vector(); xx.set_local(xx_array); xx.apply('insert')
xy = Function(Q).vector(); xy.set_local(xy_array); xy.apply('insert')
xb = block_vec([xx, xy])

# Action
y = x.copy()
M.mult(x, y)
Пример #18
0
    mesh = UnitSquareMesh(32, 32)
    V = FunctionSpace(mesh, 'CG', 1)
    Q = FunctionSpace(mesh, 'DG', 0)
    W = [V, Q]

    u, p = map(TrialFunction, W)
    v, q = map(TestFunction, W)

    [[A00, A01],
     [A10, A11]] = [[assemble(inner(u, v) * dx),
                     assemble(inner(v, p) * dx)],
                    [assemble(inner(u, q) * dx),
                     assemble(inner(p, q) * dx)]]
    blocks = [[A00 * A00, A01 + A01], [2 * A10 - A10, A11 * A11 * A11]]

    AA = block_mat(blocks)

    t = Timer('x')
    t.start()
    X = convert(AA)
    print t.stop()

    t = Timer('x')
    t.start()
    Y = convert(AA, 'foo')
    print t.stop()

    X_ = X.array()
    X_[:] -= Y.array()
    print np.linalg.norm(X_, np.inf)
Пример #19
0
def mini_block(n):
    '''Just check MMS'''
    mesh = UnitSquareMesh(n, n)
    # Just approx
    f_space = VectorFunctionSpace(mesh, 'DG', 1)
    h_space = TensorFunctionSpace(mesh, 'DG', 1)

    u_int = interpolate(u_exact, VectorFunctionSpace(mesh, 'CG', 2))
    p_int = interpolate(p_exact, FunctionSpace(mesh, 'CG', 2))

    f = project(-div(grad(u_int)) + grad(p_int), f_space)
    h = project(-p_int * Identity(2) + grad(u_int), h_space)

    # ----------------

    V = VectorFunctionSpace(mesh, 'Lagrange', 1)
    Vb = VectorFunctionSpace(mesh, 'Bubble', 3)
    Q = FunctionSpace(mesh, 'Lagrange', 1)
    W = [V, Vb, Q]

    u, ub, p = list(map(TrialFunction, W))
    v, vb, q = list(map(TestFunction, W))

    n = FacetNormal(mesh)

    a = [[0] * len(W) for _ in range(len(W))]
    a[0][0] = inner(grad(u), grad(v)) * dx
    a[0][2] = -inner(div(v), p) * dx
    a[2][0] = -inner(div(u), q) * dx

    a[1][1] = inner(grad(ub), grad(vb)) * dx
    a[1][2] = -inner(div(vb), p) * dx
    a[2][1] = -inner(div(ub), q) * dx

    a[0][1] = inner(grad(v), grad(ub)) * dx
    a[1][0] = inner(grad(vb), grad(u)) * dx

    # NOTE: bubbles don't contribute to surface
    L = [
        inner(dot(h, n), v) * ds + inner(f, v) * dx,
        inner(f, vb) * dx,
        inner(Constant(0), q) * dx
    ]
    # Bubbles don't have bcs on the surface
    bcs = [[DirichletBC(W[0], u_exact, 'near(x[0], 0)')], [], []]

    AA = block_assemble(a)
    bb = block_assemble(L)

    block_bc(bcs, True).apply(AA).apply(bb)

    # Preconditioner
    B0 = AMG(AA[0][0])

    H1_Vb = inner(grad(ub), grad(vb)) * dx + inner(ub, vb) * dx
    B1 = LumpedInvDiag(assemble(H1_Vb))

    L2_Q = assemble(inner(p, q) * dx)
    B2 = LumpedInvDiag(L2_Q)

    BB = block_mat([[B0, 0, 0], [0, B1, 0], [0, 0, B2]])

    x0 = AA.create_vec()
    x0.randomize()

    AAinv = MinRes(AA,
                   precond=BB,
                   tolerance=1e-10,
                   maxiter=500,
                   relativeconv=True,
                   show=2,
                   initial_guess=x0)

    # Compute solution
    u, ub, p = AAinv * bb

    uh_ = Function(V, u)
    uhb = Function(Vb, ub)
    ph = Function(Q, p)

    uh = Sum([uh_, uhb], degree=1)

    return uh, ph, sum(Wi.dim() for Wi in W)
Пример #20
0
def apply_bc(A, b, bcs, diag_val=1.):
    '''
    Apply block boundary conditions to block system A, b    
    '''
    if not any(bcs): return A, b
    
    # Allow for A, b be simple matrices. To proceed we wrap them as
    # block objects
    has_wrapped_A = False
    if not isinstance(A, block_mat):
        A = block_mat([[A]])
        has_wrapped_A = True

    if b is None:
        b = A.create_vec()
    
    if not isinstance(b, block_vec):
        assert has_wrapped_A
        b = block_vec([b])
        bcs = [bcs]

    # block boundary conditions is a list with bcs for each block of A, b
    assert len(A) == len(b) == len(bcs)

    bcs_ = []
    for bc in bcs:
        assert isinstance(bc, (DirichletBC, list))
        if isinstance(bc, DirichletBC):
            bcs_.append([bc])
        else:
            bcs_.append(bc)
    bcs = bcs_

    if not bcs or not any(bcs): return A, b

    # Obtain a monolithic matrix
    AA, bb = map(convert, (A, b))
    # PETSc guys
    AA, bb = as_backend_type(AA).mat(), as_backend_type(bb).vec()

    # We want to make a monotlithic matrix corresponding to function sp ace
    # where the spaces are serialized
    
    # Break apart for block
    offsets = [0]
    for bi in b:
        offsets.append(offsets[-1] + bi.size())

    rows = []
    x_values = []
    # Each bc in the group has dofs numbered only wrt to its space.
    # We're after global numbering
    for shift, bcs_sub in zip(offsets, bcs): 
        for bc in bcs_sub:
            # NOTE: bcs can be a dict or DirichletBC in which case we extract
            # the dict
            if isinstance(bc, DirichletBC): bc = bc.get_boundary_values()
            # Dofs and values for rhs
            rows.extend(shift + np.array(bc.keys(), dtype='int32'))
            x_values.extend(bc.values())
            
    rows = np.hstack(rows)
    x_values = np.array(x_values)

    x = bb.copy()
    x.zeroEntries()
    x.setValues(rows, x_values)

    # Apply to monolithic
    len(rows) and AA.zeroRowsColumns(rows, diag=diag_val, x=x, b=bb)

    blocks = []
    for first, last in zip(offsets[:-1], offsets[1:]):
        blocks.append(PETSc.IS().createStride(last-first, first, 1))

    # Reasamble
    comm = mpi_comm_world()
    b = [PETSc.Vec().createWithArray(bb.getValues(block), comm=comm) for block in blocks]
    for bi in b:
        bi.assemblyBegin()
        bi.assemblyEnd()
    b = block_vec(map(PETScVector, b))

    # PETSc 3.7.x
    try:
        AA.getSubMatrix
        A = [[PETScMatrix(AA.getSubMatrix(block_row, block_col)) for block_col in blocks]
             for block_row in blocks]
    # NOTE: 3.8+ does not ahve getSubMatrix, there is getLocalSubMatrix
    # but cannot get that to work - petsc error 73. So for now everything
    # is done with scipy    
    except AttributeError:
        AA = csr_matrix(AA.getValuesCSR()[::-1], shape=AA.size)

        A = [[numpy_to_petsc(AA[block_row.array, :][:, block_col.array]) for block_col in blocks]
             for block_row in blocks]
    # Block mat
    A = block_mat(A)

    if has_wrapped_A: return A[0][0], b[0]
    
    return A, b
Пример #21
0
if __name__ == '__main__':
    from dolfin import *

    mesh = UnitSquareMesh(32, 32)
    V = FunctionSpace(mesh, 'CG', 1)
    Q = FunctionSpace(mesh, 'DG', 0)
    W = [V, Q]
    
    u, p = map(TrialFunction, W)
    v, q = map(TestFunction, W)

    [[A00, A01],
     [A10, A11]] = [[assemble(inner(u, v)*dx), assemble(inner(v, p)*dx)],
                    [assemble(inner(u, q)*dx), assemble(inner(p, q)*dx)]]
    blocks = [[A00*A00, A01+A01],
              [2*A10 - A10, A11*A11*A11]]
    
    AA = block_mat(blocks)

    t = Timer('x'); t.start()
    X = convert(AA)
    print t.stop()

    t = Timer('x'); t.start()
    Y = convert(AA, 'foo')
    print t.stop()

    X_ = X.array()
    X_[:] -= Y.array()
    print np.linalg.norm(X_, np.inf)
Пример #22
0
def pc_nest(pc, block_pc, Amat):
    '''Setup field split and its operators'''
    isets, jsets = Amat.getNestISs()
    # Sanity
    assert len(isets) == len(jsets)
    assert all(i.equal(j) for i, j in zip(isets, jsets)), [
        (i.array[[0, -1]], j.array[[0, -1]]) for i, j in zip(isets, jsets)
    ]

    # We target block matrices
    if len(isets) == 1:
        A = pc_mat(pc, block_pc[0][0])
        return as_petsc(A)

    grouped_isets = []
    # It may be that block_pc requires different grouping if indinces.
    # This is signaled by presence of RestrictionOperator
    if isinstance(block_pc, block_mul):
        Rt, block_pc, R = block_pc.chain
        assert Rt.A == R

        offsets = R.offsets

        for first, last in zip(R.offsets[:-1], R.offsets[1:]):
            if last - first == 1:
                grouped_isets.append(isets[first])
            else:
                group = isets[first]
                for i in range(first + 1, last):
                    group = group.union(isets[i])
                grouped_isets.append(group)
    else:
        grouped_isets = isets

    assert not isinstance(block_pc, block_mat) or set(
        block_pc.blocks.shape) == set((len(grouped_isets), ))

    pc.setFieldSplitIS(*[(str(i), iset)
                         for i, iset in enumerate(grouped_isets)])

    # The preconditioner will always be fieldsplit
    assert pc.getType() == 'fieldsplit'

    pc_ksps = pc.getFieldSplitSubKSP()

    nblocks, = set(block_pc.blocks.shape)

    # For now we can only do pure block_mats
    assert isinstance(block_pc, block_mat)
    # Consistency
    assert len(grouped_isets) == nblocks
    # ... and the have to be diagonal
    assert all(i == j or block_pc[i][j] == 0 for i in range(nblocks)
               for j in range(nblocks))

    Bmat = block_mat([[0 for i in range(nblocks)] for j in range(nblocks)])
    for i, pc_ksp in enumerate(pc_ksps):
        pc_ksp.setType('preonly')
        pci = pc_ksp.getPC()
        # Set individual preconds for blocks
        block = block_pc[i][i]
        mat = pc_mat(pci, block)

        Bmat[i][i] = mat
    # Return out for setOperators
    return nest(Bmat)
Пример #23
0
def lagrange_mixed(lmbda, mu, f, h, mesh, Z=None):
    '''
    Solves 

        -div(sigma) = f in  Omega
            sigma.n = h on  boundary

    where sigma(u) = 2*mu*eps(u) + lambda*div(u)*I. The problem is reformulated by
    Lagrange multiplier nu to inforce orthogonality with the space of rigid
    motions. To get robustnes in lmbda solid pressure p = lambda*div u is introduced. 
    The system to be solved with MinRes is 

        P*[A C  B; *[u, = P*[L,
           C' D 0;   p,      0,
           B' 0 0]   nu]     0]

    with P a precondtioner. We run on series of meshes to show mesh independence
    of the solver.
    '''
    if not isinstance(mesh, Mesh):
        # NOTE: You can precompute the 'symbolic' basis and pass it here
        return [lagrange_mixed(lmbda, mu, f, h, mesh_) for mesh_ in mesh]
    
    # For cube
    V = VectorFunctionSpace(mesh, 'CG', 2)
    Q = FunctionSpace(mesh, 'CG', 1)

    u, v = TrialFunction(V), TestFunction(V)
    p, q = TrialFunction(Q), TestFunction(Q)

    # Strain
    epsilon = lambda u: sym(grad(u))
    # Stress
    gdim = mesh.geometry().dim()
    sigma = lambda u: 2*mu*epsilon(u) + lmbda*tr(epsilon(u))*Identity(gdim)
    a = 2*mu*inner(sym(grad(u)), sym(grad(v)))*dx

    A = assemble(a)

    c = inner(div(v), p)*dx
    C = assemble(c)

    d = -(inner(p, q)/Constant(lmbda))*dx
    D = assemble(d)

    m = inner(u, v)*dx
    M = assemble(m)

    # NOTE: Avoiding use of Q space in the assembly - dense blocks!
    X = VectorFunctionSpace(mesh, 'R', 0, dim=6)
    Zh = rigid_motions.RMBasis(V, X, Z)  # L^2 orthogonal
    B = M*Zh

    # System operator
    AA = block_mat([[A,                  C, B], 
                    [block_transpose(C), D, 0],
                    [block_transpose(B), 0, 0]])

    # Right hand side
    L = inner(f, v)*dx + inner(h, v)*ds
    b0 = assemble(L)
    b1 = assemble(inner(Constant(0), q)*dx)
    # Equivalent to assemble(inner(Constant((0, )*6), q)*dx) but cheaper
    b2 = Function(X).vector()
    bb = block_vec([b0, b1, b2])

    # Block diagonal preconditioner
    IV = assemble(a + m)
    IQ = assemble(inner(p, q)*dx)
    IX = rigid_motions.identity_matrix(X)
    BB = block_mat([[AMG(IV), 0,              0], 
                    [0,       AMG(IQ),        0],
                    [0,       0,            IX]])

    # Solve, using random initial guess
    x0 = AA.create_vec()
    [as_backend_type(xi).vec().setRandom() for xi in x0]

    AAinv = MinRes(AA, precond=BB, initial_guess=x0, maxiter=120, tolerance=1E-8,
                   show=2, relativeconv=True)

    x = AAinv*bb

    # # Functions from coefficients
    # # uh = Function(V, x[0])     # Displacement
    # # ph = Function(Q, x[1])     # Solid pressure
    # # nuh = Zh.rigid_motion(x[2])  # Function in V

    niters = len(AAinv.residuals) - 1
    assert niters < 120

    P = rigid_motions.Projector(Zh)

    P*x0[0]  # to get orthogonality

    if MPI.rank(mesh.mpi_comm()) == 0:
        print '\033[1;37;31m%s\033[0m' % ('Orthogonality %g' % max(P.alphas))
        pass

    return V.dim() + Q.dim() + 6, niters
Пример #24
0
def lagrange_primal(lmbda, mu, f, h, mesh, Z=None):
    '''
    Solves 

        -div(sigma) = f in  Omega
            sigma.n = h on  boundary

    where sigma(u) = 2*mu*eps(u) + lambda*div(u)*I. The problem is reformulated by
    Lagrange multiplier p to inforce orthogonality with the space of rigid
    motions. The system to be solved with MinRes is 

        P*[A  B;*[u; = P*[L,
           B'  ]  p]      0]

    with P a precondtioner. We run on series of meshes to show mesh independence
    of the solver.
    '''
    if not isinstance(mesh, Mesh):
        # NOTE: You can precompute the 'symbolic' basis and pass it here
        return [lagrange_primal(lmbda, mu, f, h, mesh_) for mesh_ in mesh]

    # For cube
    V = VectorFunctionSpace(mesh, 'CG', 1)
    u, v = TrialFunction(V), TestFunction(V)
    # Strain
    epsilon = lambda u: sym(grad(u))
    # Stress
    gdim = mesh.geometry().dim()
    sigma = lambda u: 2 * mu * epsilon(u) + lmbda * tr(epsilon(u)) * Identity(
        gdim)
    # Energy of elastic deformation
    a = inner(sigma(u), epsilon(v)) * dx
    A = assemble(a)
    # Mass matrix for B
    m = inner(u, v) * dx
    M = assemble(m)

    # NOTE: Avoiding use of Q space in the assembly - dense blocks!
    Q = VectorFunctionSpace(mesh, 'R', 0, dim=6)
    Zh = rigid_motions.RMBasis(V, Q, Z)  # L^2 orthogonal
    B = M * Zh

    # System operator
    AA = block_mat([[A, B], [block_transpose(B), 0]])

    # Right hand side
    L = inner(f, v) * dx + inner(h, v) * ds
    b0 = assemble(L)
    # Equivalent to assemble(inner(Constant((0, )*6), q)*dx) but cheaper
    b1 = Function(Q).vector()
    bb = block_vec([b0, b1])

    # Block diagonal preconditioner
    AM = assemble(a + m)
    I = rigid_motions.identity_matrix(Q)
    BB = block_mat([[AMG(AM), 0], [0, I]])

    # Solve, using random initial guess
    x0 = AA.create_vec()
    [as_backend_type(xi).vec().setRandom() for xi in x0]

    AAinv = MinRes(AA,
                   precond=BB,
                   initial_guess=x0,
                   maxiter=100,
                   tolerance=1E-8,
                   show=2,
                   relativeconv=True)

    x = AAinv * bb

    # Functions from coefficients
    # uh = Function(V, x[0])     # Displacement
    # zh = Zh.rigid_motion(x[1])  # Function in V

    niters = len(AAinv.residuals) - 1
    assert niters < 100

    P = rigid_motions.Projector(Zh)
    P * x[0]  # to get orthogonality
    if MPI.rank(mesh.mpi_comm()) == 0:
        print '\033[1;37;31m%s\033[0m' % ('Orthogonality %g' % max(P.alphas))

    return V.dim() + 6, niters
Пример #25
0
from block.iterative import MinRes
from block.algebraic.petsc import ML, Cholesky

# lhs
AA = block_assemble([[a00, a01, a02],
                     [a10, a11, a12],
                     [a20, a21,   0]])
# rhs
b = block_assemble([L0, L1, 0])

# Collect boundary conditions
bcs = [[bc0, bc1], [], []]
block_bc(bcs, True).apply(AA).apply(b)

b22 = inner(p, q)*dx
B22 = assemble(b22)
# Possible action of preconditioner
BB = block_mat([[ML(AA[0, 0]), 0, 0],
                [0, Cholesky(AA[1, 1]), 0],
                [0, 0, Cholesky(B22)]])

AAinv = MinRes(AA, precond=BB, tolerance=1E-10, maxiter=500)
U, B, P = AAinv*b

p = Function(Q)
p.vector()[:] = P

# Plot solution
plot(p)
interactive()
Пример #26
0
def mini_block(n):
    '''Just check MMS'''
    mesh = UnitSquareMesh(n, n)
    # Just approx
    f_space = VectorFunctionSpace(mesh, 'DG', 1)
    h_space = TensorFunctionSpace(mesh, 'DG', 1)

    u_int = interpolate(u_exact, VectorFunctionSpace(mesh, 'CG', 2))
    p_int = interpolate(p_exact, FunctionSpace(mesh, 'CG', 2))
        
    f = project(-div(grad(u_int)) + grad(p_int), f_space)
    h = project(-p_int*Identity(2) + grad(u_int), h_space)

    # ----------------

    V = VectorFunctionSpace(mesh, 'Lagrange', 1)
    Vb = VectorFunctionSpace(mesh, 'Bubble', 3)
    Q = FunctionSpace(mesh, 'Lagrange', 1)
    W = [V, Vb, Q]

    u, ub, p = map(TrialFunction, W)
    v, vb, q = map(TestFunction, W)

    n = FacetNormal(mesh)

    a = [[0]*len(W) for _ in range(len(W))]
    a[0][0] = inner(grad(u), grad(v))*dx
    a[0][2] = - inner(div(v), p)*dx
    a[2][0] = - inner(div(u), q)*dx

    a[1][1] = inner(grad(ub), grad(vb))*dx
    a[1][2] = - inner(div(vb), p)*dx
    a[2][1] = - inner(div(ub), q)*dx

    a[0][1] = inner(grad(v), grad(ub))*dx
    a[1][0] = inner(grad(vb), grad(u))*dx

    # NOTE: bubbles don't contribute to surface
    L = [inner(dot(h, n), v)*ds + inner(f, v)*dx,
         inner(f, vb)*dx,
         inner(Constant(0), q)*dx]
    # Bubbles don't have bcs on the surface
    bcs = [[DirichletBC(W[0], u_exact, 'near(x[0], 0)')], [], []]

    AA = block_assemble(a)
    bb = block_assemble(L)

    block_bc(bcs, True).apply(AA).apply(bb)

    # Preconditioner
    B0 = AMG(AA[0][0])
    
    H1_Vb = inner(grad(ub), grad(vb))*dx + inner(ub, vb)*dx
    B1 = LumpedInvDiag(assemble(H1_Vb))
    
    L2_Q = assemble(inner(p, q)*dx)
    B2 = LumpedInvDiag(L2_Q)

    BB = block_mat([[B0, 0, 0],
                    [0, B1, 0],
                    [0, 0, B2]])
    
    x0 = AA.create_vec()
    x0.randomize()

    AAinv = MinRes(AA, precond=BB, tolerance=1e-10, maxiter=500, relativeconv=True, show=2,
                   initial_guess=x0)

    # Compute solution
    u, ub, p = AAinv * bb

    uh_ = Function(V, u)
    uhb = Function(Vb, ub)
    ph = Function(Q, p)
    
    uh = Sum([uh_, uhb], degree=1)
                
    return uh, ph, sum(Wi.dim() for Wi in W)