Esempio n. 1
0
def collapse_mul(bmat):
    '''A*B*C to single matrix'''
    # A0 * A1 * ...
    A, B = bmat.chain[0], bmat.chain[1:]

    if len(B) == 1:
        B = B[0]
        # Two matrices
        if is_petsc_mat(A) and is_petsc_mat(B):
            A_ = as_petsc(A)
            B_ = as_petsc(B)
            assert A_.size[1] == B_.size[0]
            C_ = PETSc.Mat()
            A_.matMult(B_, C_)

            return PETScMatrix(C_)
        # One of them is a number
        elif is_petsc_mat(A) and is_number(B):
            A_ = as_petsc(A)
            C_ = A_.copy()
            C_.scale(B)
            return PETScMatrix(C_)

        elif is_petsc_mat(B) and is_number(A):
            B_ = as_petsc(B)
            C_ = B_.copy()
            C_.scale(A)
            return PETScMatrix(C_)
        # Some compositions
        else:
            return collapse(collapse(A) * collapse(B))
    # Recurse
    else:
        return collapse_mul(collapse(A) * collapse(reduce(operator.mul, B)))
Esempio n. 2
0
def collapse_mul(bmat):
    '''A*B*C to single matrix'''
    # A0 * A1 * ...
    A, B = bmat.chain[0], bmat.chain[1:]

    if len(B) == 1:
        B = B[0]
        # Two matrices
        if is_petsc_mat(A) and is_petsc_mat(B):
            A_ = as_petsc(A)
            B_ = as_petsc(B)
            assert A_.size[1] == B_.size[0]
            C_ = PETSc.Mat()
            A_.matMult(B_, C_)

            return PETScMatrix(C_)
        # One of them is a number
        elif is_petsc_mat(A) and is_number(B):
            A_ = as_petsc(A)
            C_ = A_.copy()
            C_.scale(B)
            return PETScMatrix(C_)

        elif is_petsc_mat(B) and is_number(A):
            B_ = as_petsc(B)
            C_ = B_.copy()
            C_.scale(A)
            return PETScMatrix(C_)
        # Some compositions
        else:
            return collapse(collapse(A)*collapse(B))
    # Recurse
    else:
        return collapse_mul(collapse(A)*collapse(reduce(operator.mul, B)))                                    
Esempio n. 3
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)
Esempio n. 4
0
def get_dims(thing):
    '''
    Size of Rn vector or operator Rn to Rm. We return None for scalars
    and raise when such an operator cannot be established, i.e. there 
    are consistency checks going on 
    '''
    if is_petsc_vec(thing): return thing.size()

    if is_petsc_mat(thing): return (thing.size(0), thing.size(1))

    if is_number(thing): return None

    # Now let's handdle block stuff
    # Multiplication
    if isinstance(thing, block_mul):
        A, B = thing.chain[0], thing.chain[1:]

        dims_A, dims_B = get_dims(A), get_dims(B[0])
        # A number does not change
        if dims_A is None:
            return dims_B
        if dims_B is None:
            return dims_A
        # Otherwise, consistency
        if len(B) == 1:
            assert len(dims_A) == len(dims_B)
            assert dims_A[1] == dims_B[0]
            return (dims_A[0], dims_B[1])
        else:
            dims_B = get_dims(reduce(operator.mul, B))

            assert len(dims_A) == len(dims_B)
            assert dims_A[1] == dims_B[0]
            return (dims_A[0], dims_B[1])
    # +, -
    elif isinstance(thing, (block_add, block_sub)):
        A, B = thing.A, thing.B
        if is_number(A):
            return get_dims(B)

        if is_number(B):
            return get_dims(A)

        dims = get_dims(A)
        assert dims == get_dims(B), (dims, get_dims(B))
        return dims
    # T
    elif isinstance(thing, block_transpose):
        dims = get_dims(thing.A)
        return (dims[1], dims[0])
    # Some things in cbc.block know their matrix representation
    # E.g. InvLumpDiag...
    elif hasattr(thing, 'A'):
        assert is_petsc_mat(thing.A)
        return get_dims(thing.A)

    raise ValueError('Cannot get_dims of %r, %s' % (type(thing), thing))
Esempio n. 5
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)
Esempio n. 6
0
def assemble(form):
    '''Assemble multidimensional form'''
    # In the base case we want to fall trough the custom assemblers
    # for trace/average/restriction problems until something that
    # dolfin can handle (hopefully)
    modules = (
        xii.assembler.trace_assembly,  # Codimension 1
        xii.assembler.average_assembly,  # Codimension 2
        xii.assembler.restriction_assembly,  # Codimension 0
        xii.assembler.point_trace_assembly)  # Dimensiom 0

    if isinstance(form, Form):
        arity = form_arity(form)
        # Try with our reduced assemblers
        for module in modules:
            tensor = module.assemble_form(form, arity)
            if tensor is not None:
                return tensor
        # Fallback
        return df.assemble(form)

    # We might get number
    if is_number(form): return form

    # Recurse
    if isinstance(form, list): form = np.array(form, dtype='object')

    blocks = np.array(map(assemble, form.flatten())).reshape(form.shape)

    return (block_vec if blocks.ndim == 1 else block_mat)(blocks)
Esempio n. 7
0
def assemble(form):
    '''Assemble multidimensional form'''
    # In the base case we want to fall trough the custom assemblers
    # for trace/average/restriction problems until something that
    # dolfin can handle (hopefully)
    modules = (
        xii.assembler.trace_assembly,  # To Codimension 1
        xii.assembler.
        average_assembly,  # To Codimension 2 via surface of bding curve
        xii.assembler.extension_assembly,  # From dim 1 to 2
        xii.assembler.restriction_assembly,
        xii.assembler.injection_assembly)  # Between Codimension 0

    names = ('trace', 'average', 'extension', 'restriction', 'injection')

    if isinstance(form, Form):
        arity = form_arity(form)
        # Try with our reduced assemblers
        for name, module in zip(names, modules):
            tensor = module.assemble_form(form, arity)
            if tensor is not None:
                return tensor
        # Fallback
        return df.assemble(form)

    # We might get number
    if is_number(form): return form

    shape = shape_list(form)
    # Recurse
    blocks = reshape_list(map(assemble, flatten_list(form)), shape)

    return (block_vec if len(shape) == 1 else block_mat)(blocks)
Esempio n. 8
0
def assemble(form):
    '''Assemble multidimensional form'''
    # In the base case we want to fall trough the custom assemblers
    # for trace/average/restriction problems until something that
    # dolfin can handle (hopefully)
    modules = (
        xii.assembler.trace_assembly,  # Codimension 1
        xii.assembler.average_assembly,  # Codimension 2
        xii.assembler.restriction_assembly)  # Codimension 0

    if isinstance(form, Form):
        arity = form_arity(form)
        # Try with our reduced assemblers
        for module in modules:
            tensor = module.assemble_form(form, arity)
            if tensor is not None:
                return tensor
        # Fallback
        return df.assemble(form)

    # We might get number
    if is_number(form): return form

    shape = shape_list(form)
    # Recurse
    blocks = reshape_list(map(assemble, flatten_list(form)), shape)

    return (block_vec if len(shape) == 1 else block_mat)(blocks)
Esempio n. 9
0
    def __init__(self, radius, degree):
        # Make constant function
        if is_number(radius):
            assert radius > 0
            self.radius = lambda x0, r=radius: r
        # Then this must map points on centerline to radius
        else:
            self.radius = radius
        # NOTE: Let s by the arch length coordinate of the centerline of the
        # cylinder. A tangent to 1d mesh at s is a normal to the plane in which
        # we draw a circle C(n, r) with radius r. For reduction of 3d we compute
        #
        # |2*pi*R(s)|^-1\int_{C(n, r)} u dl =
        #
        # |2*pi*R(s)|^-1\int_{-pi}^{pi} u(s + t1*R(s)*cos(theta) + t2*R(s)*sin(theta))R*d(theta) =
        #
        # |2*pi*R(s)|^-1\int_{-1}^{1} u(s + t1*R(s)*cos(pi*t) + t2*R(s)*sin(pi*t))*pi*R*dt =
        #
        # 1/2*sum_q u(s + t1*R(s)*cos(pi*x_q) + t2*R(s)*sin(pi*x_q))

        xq, wq = leggauss(degree)
        self.__weights__ = wq * 0.5  # Scale down by 0.5

        # Precompute trigonometric part (only shift by tangents t1, t2)
        self.cos_xq = np.cos(np.pi * xq).reshape((-1, 1))
        self.sin_xq = np.sin(np.pi * xq).reshape((-1, 1))
Esempio n. 10
0
def assemble(form):
    '''Assemble multidimensional form'''
    # In the base case we want to fall trough the custom assemblers
    # for trace/average/restriction problems until something that 
    # dolfin can handle (hopefully)
    modules = (xii.assembler.trace_assembly,        # Codimension 1
               xii.assembler.average_assembly,      # Codimension 2
               xii.assembler.restriction_assembly)  # Codimension 0
    
    if isinstance(form, Form):
        arity = form_arity(form)
        # Try with our reduced assemblers
        for module in modules:
            tensor = module.assemble_form(form, arity)
            if tensor is not None:
                return tensor
        # Fallback
        return df.assemble(form)

    # We might get number
    if is_number(form): return form

    shape = shape_list(form)
    # Recurse
    blocks = reshape_list(map(assemble, flatten_list(form)), shape)
    
    return (block_vec if len(shape) == 1 else block_mat)(blocks)
Esempio n. 11
0
def collapse(bmat):
    '''Collapse what are blocks of bmat'''
    # Single block cases
    # Do nothing
    if is_petsc_mat(bmat) or is_number(bmat) or is_petsc_vec(bmat):
        return bmat

    # Multiplication
    if isinstance(bmat, block_mul):
        return collapse_mul(bmat)
    # +
    elif isinstance(bmat, block_add):
        return collapse_add(bmat)
    # -
    elif isinstance(bmat, block_sub):
        return collapse_sub(bmat)
    # T
    elif isinstance(bmat, block_transpose):
        return collapse_tr(bmat)
    # Some things in cbc.block know their matrix representation
    # E.g. InvLumpDiag...
    elif hasattr(bmat, 'A'):
        assert is_petsc_mat(bmat.A)
        return bmat.A

    raise ValueError('Do not know how to collapse %r' % type(bmat))
Esempio n. 12
0
    def __init__(self, P, degree):
        if isinstance(P, (tuple, list, np.ndarray)):
            assert all(is_number(Pi) for Pi in P)
            self.P = lambda x0, p=P: p
        else:
            self.P = P

        # Weights for [-1, 1] for 2d will do the tensor product
        self.xq, self.wq = leggauss(degree)
Esempio n. 13
0
def collapse(bmat):
    '''Collapse what are blocks of bmat'''
    # Single block cases
    # Do nothing
    if is_petsc_mat(bmat) or is_number(bmat) or is_petsc_vec(bmat):
        return bmat

    if isinstance(bmat, (Vector, Matrix, GenericVector)):
        return bmat

    # Multiplication
    if isinstance(bmat, block_mul):
        return collapse_mul(bmat)
    # +
    elif isinstance(bmat, block_add):
        return collapse_add(bmat)
    # -
    elif isinstance(bmat, block_sub):
        return collapse_sub(bmat)
    # T
    elif isinstance(bmat, block_transpose):
        return collapse_tr(bmat)

    # Some things in cbc.block know their matrix representation
    # This is typically diagonals like InvLumpDiag etc
    elif hasattr(bmat, 'v'):
        # So now we make that diagonal matrix
        diagonal = bmat.v

        n = diagonal.size
        mat = PETSc.Mat().createAIJ(comm=COMM, size=[[n, n], [n, n]], nnz=1)
        mat.assemblyBegin()
        mat.setDiagonal(diagonal)
        mat.assemblyEnd()

        return PETScMatrix(mat)
    # Some operators actually have matrix repre (HsMG)
    elif hasattr(bmat, 'matrix'):
        return bmat.matrix

    # Try:
    elif hasattr(bmat, 'collapse'):
        return bmat.collapse()
    
    elif hasattr(bmat, 'create_vec'):
        x = bmat.create_vec()
        columns = []
        for ei in Rn_basis(x):
            y = bmat*ei
            columns.append(csr_matrix(convert(y).get_local()))
        bmat = (sp_vstack(columns).T).tocsr()

        return numpy_to_petsc(bmat)
    
    raise ValueError('Do not know how to collapse %r' % type(bmat))
Esempio n. 14
0
    def __init__(self, P, degree):
        if isinstance(P, (tuple, list, np.ndarray)):
            assert all(is_number(Pi) for Pi in P)
            self.P = lambda x0, p=P: p
        else:
            self.P = P

        xq, wq = leggauss(degree)
        # Repeat for each edge
        self.__weights__ = 0.5 * np.tile(wq, 4)
        self.__xq__ = xq  # Point for edge -1 -- 1
Esempio n. 15
0
    def __init__(self, radius, degree):
        # Make constant function
        if is_number(radius):
            assert radius > 0
            self.radius = lambda x0, r=radius: r
        # Then this must map points on centerline to radius
        else:
            self.radius = radius

        # Will use Gauss quadrature on [-1, 1]
        self.xq, self.wq = leggauss(degree)
Esempio n. 16
0
def collect(bmat):
    '''Extract aplha_j, s_j pairs'''
    if isinstance(bmat, InterpolationMatrix):
        return [[1, bmat.s]]

    if isinstance(bmat, block_add):
        return collect(bmat.A) + collect(bmat.B)

    if isinstance(bmat, block_sub):
        b = collect(bmat.B)
        return collect(bmat.A) + [[-bi[0], bi[1]] for bi in b]

    if isinstance(bmat, block_mul):
        assert len(bmat.chain) == 2
        A, B = bmat.chain

        if isinstance(A, InterpolationMatrix):
            assert is_number(B)
            return [[B, A.s]]
        else:
            assert is_number(A) and isinstance(B, InterpolationMatrix), (A, B)
            return [[A, B.s]]
Esempio n. 17
0
def collect(bmat):
    '''Extract aplha_j, s_j pairs'''
    if isinstance(bmat, InterpolationMatrix):
        return [[1, bmat.s]]

    if isinstance(bmat, block_add):
        return collect(bmat.A) + collect(bmat.B)

    if isinstance(bmat, block_sub):
        b = collect(bmat.B)
        return collect(bmat.A) + [[-bi[0], bi[1]] for bi in b]

    if isinstance(bmat, block_mul):
        assert len(bmat.chain) == 2
        A, B = bmat.chain
        
        if isinstance(A, InterpolationMatrix):
            assert is_number(B)
            return [[B, A.s]]
        else:
            assert is_number(A) and isinstance(B, InterpolationMatrix), (A, B)
            return [[A, B.s]]
Esempio n. 18
0
    def __init__(self, radius, degree):
        # Make constant function
        if is_number(radius):
            assert radius > 0
            self.radius = lambda x0, r=radius: r
        # Then this must map points on centerline to radius
        else:
            self.radius = radius

        # Will use quadrature from quadpy over unit disk in z=0 plane
        # and center (0, 0, 0)
        quad = quadpy.disk.Lether(degree)
        self.xq, self.wq = quad.points, quad.weights
Esempio n. 19
0
def block_mat_to_numpy(bmat):
    '''Collapsing block mat of matrices to scipy's bmat'''
    # A single matrix
    if is_petsc_mat(bmat):
        bmat = as_petsc(bmat)
        return csr_matrix(bmat.getValuesCSR()[::-1], shape=bmat.size)
    # 0
    if is_number(bmat):
        return None  # What bmat accepts
    # Recurse on blocks
    blocks = np.array(map(block_mat_to_numpy, bmat.blocks.flatten()))
    blocks = blocks.reshape(bmat.blocks.shape)
    # The bmat
    return numpy_block_mat(blocks).tocsr()
Esempio n. 20
0
def block_mat_to_numpy(bmat):
    '''Collapsing block mat of matrices to scipy's bmat'''
    # A single matrix
    if is_petsc_mat(bmat):
        bmat = as_petsc(bmat)
        return csr_matrix(bmat.getValuesCSR()[::-1], shape=bmat.size)
    # 0
    if is_number(bmat):
        return None  # What bmat accepts
    # Recurse on blocks
    blocks = np.array(map(block_mat_to_numpy, bmat.blocks.flatten()))
    blocks = blocks.reshape(bmat.blocks.shape)
    # The bmat
    return numpy_block_mat(blocks).tocsr()
Esempio n. 21
0
    def __init__(self, radius, degree):
        # Make constant function
        if is_number(radius):
            assert radius > 0
            self.radius = lambda x0, r=radius: r
        # Then this must map points on centerline to radius
        else:
            self.radius = radius

        from quadpy.sphere import Lebedev

        integrator = Lebedev(str(degree))
        self.xq = integrator.points
        self.__weights__ = integrator.weights
Esempio n. 22
0
def is_well_defined(bmat):
    '''Is expression of the form that inverse accepts'''
    A, M = None, None
    for mat in crawl(bmat):
        if is_number(mat): continue
        
        if A is None:
            A, M = mat.A, mat.M
        else:
            if not ((A.size(0) == mat.A.size(0)) and (A.size(1) == mat.A.size(1))): 
                return False
            if not ((M.size(0) == mat.M.size(0)) and (M.size(1) == mat.M.size(1))): 
                return False
    return True
Esempio n. 23
0
def is_well_defined(bmat):
    '''Is expression of the form that inverse accepts'''
    A, M = None, None
    for mat in crawl(bmat):
        if is_number(mat): continue

        if A is None:
            A, M = mat.A, mat.M
        else:
            if not ((A.size(0) == mat.A.size(0)) and
                    (A.size(1) == mat.A.size(1))):
                return False
            if not ((M.size(0) == mat.M.size(0)) and
                    (M.size(1) == mat.M.size(1))):
                return False
    return True
Esempio n. 24
0
def crawl(bmat):
    '''Return terminals in cbc.block expression that are relevant for inverse'''
    if isinstance(bmat, block_mul):
        for block in bmat.chain:
            for item in crawl(block):
                yield item

    if isinstance(bmat, (block_add, block_sub)):
        for item in crawl(bmat.A):
            yield item

        for item in crawl(bmat.B):
            yield item

    if is_number(bmat) or isinstance(bmat, InterpolationMatrix):
        yield bmat
Esempio n. 25
0
def crawl(bmat):
    '''Return terminals in cbc.block expression that are relevant for inverse'''
    if isinstance(bmat, block_mul):
        for block in bmat.chain:
            for item in crawl(block):
                yield item

    if isinstance(bmat, (block_add, block_sub)):
        for item in crawl(bmat.A):
            yield item

        for item in crawl(bmat.B):
            yield item

    if is_number(bmat) or isinstance(bmat, InterpolationMatrix):
        yield bmat
Esempio n. 26
0
def Average(v, line_mesh, radius, quadrature_degree=8, surface='cylinder'):
    '''
    Annotated function for being surface average around line mesh. 
    For surface == 'cylinder' this means that 

        (Average(v))(x) = |C_R(x)|\int_{C_R(x)} v(y) dy for every point 

    x on the line_mesh. C_R(x) is a center of radius R(x) centered at x
    with normal vector determined by tangent of the line_mesh segment @ 
    x. For surface == 'sphere' we integrate over a surface of a ball 
    at x with radius. This integral is computed numerically with given quad.
    degree.

    If radius == 0, this reduction is understood as 3d-1d trace. In this 
    case the reduced function must be in some CG space!
    '''
    # Prevent Trace(grad(u)). But it could be interesting to have this
    assert is_terminal(v)
    assert average_cell(v) == line_mesh.ufl_cell()

    # Some sanity check for the radius
    if isinstance(radius, int) and radius == 0:
        v_family = v.ufl_element().family()
        # If we have en embedded mesh this mean that we want trace on en
        # edge and this make it well defined (only?) for CG
        if hasattr(line_mesh, 'parent_entity_map'):
            assert v_family == 'Lagrange', '3d1d trace undefined for %s' % v_family
        # Otherise the hope is that we will eval in cell interior which
        print '\tUsing 3d-1d trace!!!!'

        radius = None  # Signal to avg_mat

    if is_number(radius): assert radius > 0

    if isinstance(v, df.Coefficient):
        v = df.Function(v.function_space(), v.vector())

    v.average_ = {
        'mesh': line_mesh,
        'radius': radius,
        'quad_degree': quadrature_degree,
        'surface': surface
    }

    return v
Esempio n. 27
0
def Average(v, line_mesh, radius, quadrature_degree=8, surface='cylinder'):
    '''
    Annotated function for being surface average around line mesh. 
    For surface == 'cylinder' this means that 

        (Average(v))(x) = |C_R(x)|\int_{C_R(x)} v(y) dy for every point 

    x on the line_mesh. C_R(x) is a center of radius R(x) centered at x
    with normal vector determined by tangent of the line_mesh segment @ 
    x. For surface == 'sphere' we integrate over a surface of a ball 
    at x with radius. This integral is computed numerically with given quad.
    degree.

    If radius == 0, this reduction is understood as 3d-1d trace. In this 
    case the reduced function must be in some CG space!
    '''
    # Prevent Trace(grad(u)). But it could be interesting to have this
    assert is_terminal(v)
    assert average_cell(v) == line_mesh.ufl_cell()

    # Some sanity check for the radius
    if isinstance(radius, int) and radius == 0:
        v_family = v.ufl_element().family()
        # If we have en embedded mesh this mean that we want trace on en
        # edge and this make it well defined (only?) for CG
        if hasattr(line_mesh, 'parent_entity_map'):
            assert v_family == 'Lagrange', '3d1d trace undefined for %s' % v_family
        # Otherise the hope is that we will eval in cell interior which
        print '\tUsing 3d-1d trace!!!!'
        
        radius = None  # Signal to avg_mat
        
    if is_number(radius): assert radius > 0

    if isinstance(v, df.Coefficient):
        v =  df.Function(v.function_space(), v.vector())

    v.average_ = {'mesh': line_mesh, 'radius': radius, 'quad_degree': quadrature_degree,
                  'surface': surface}

    return v
Esempio n. 28
0
def collapse(bmat):
    '''Collapse what are blocks of bmat'''
    # Single block cases
    # Do nothing
    if is_petsc_mat(bmat) or is_number(bmat) or is_petsc_vec(bmat):
        return bmat

    if isinstance(bmat, (Vector, Matrix, GenericVector)):
        return bmat

    # Multiplication
    if isinstance(bmat, block_mul):
        return collapse_mul(bmat)
    # +
    elif isinstance(bmat, block_add):
        return collapse_add(bmat)
    # -
    elif isinstance(bmat, block_sub):
        return collapse_sub(bmat)
    # T
    elif isinstance(bmat, block_transpose):
        return collapse_tr(bmat)
    # Some things in cbc.block know their matrix representation
    # This is typically diagonals like InvLumpDiag etc
    elif hasattr(bmat, 'v'):
        # So now we make that diagonal matrix
        diagonal = bmat.v

        n = diagonal.size
        mat = PETSc.Mat().createAIJ(comm=COMM, size=[[n, n], [n, n]], nnz=1)
        mat.assemblyBegin()
        mat.setDiagonal(diagonal)
        mat.assemblyEnd()

        return PETScMatrix(mat)
    # Some operators actually have matrix repre (HsMG)
    elif hasattr(bmat, 'matrix'):
        return bmat.matrix

    raise ValueError('Do not know how to collapse %r' % type(bmat))
Esempio n. 29
0
def collapse(bmat):
    '''Collapse what are blocks of bmat'''
    # Single block cases
    # Do nothing
    if is_petsc_mat(bmat) or is_number(bmat) or is_petsc_vec(bmat):
        return bmat

    if isinstance(bmat, (Vector, Matrix, GenericVector)):
        return bmat

    # Multiplication
    if isinstance(bmat, block_mul):
        return collapse_mul(bmat)
    # +
    elif isinstance(bmat, block_add):
        return collapse_add(bmat)
    # -
    elif isinstance(bmat, block_sub):
        return collapse_sub(bmat)
    # T
    elif isinstance(bmat, block_transpose):
        return collapse_tr(bmat)
    # Some things in cbc.block know their matrix representation
    # This is typically diagonals like InvLumpDiag etc
    elif hasattr(bmat, 'v'):
        # So now we make that diagonal matrix
        diagonal = bmat.v

        n = diagonal.size
        mat = PETSc.Mat().createAIJ(comm=COMM, size=[[n, n], [n, n]], nnz=1)
        mat.assemblyBegin()
        mat.setDiagonal(diagonal)
        mat.assemblyEnd()

        return PETScMatrix(mat)
    # Some operators actually have matrix repre (HsMG)
    elif hasattr(bmat, 'matrix'):
        return bmat.matrix

    raise ValueError('Do not know how to collapse %r' % type(bmat))
Esempio n. 30
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
Esempio n. 31
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
Esempio n. 32
0
def cylinder_average_matrix(V, TV, radius, quad_degree):
    '''Averaging matrix'''
    mesh = V.mesh()
    line_mesh = TV.mesh()
    # We are going to perform the integration with Gauss quadrature at
    # the end (PI u)(x):
    # A cell of mesh (an edge) defines a normal vector. Let P be the plane
    # that is defined by the normal vector n and some point x on Gamma. Let L
    # be the circle that is the intersect of P and S. The value of q (in Q) at x
    # is defined as
    #
    #                    q(x) = (1/|L|)*\int_{L}g(x)*dL
    #
    # which simplifies to g(x) = (1/(2*pi*R))*\int_{-pi}^{pi}u(L)*R*d(theta) and
    # or                       = (1/2) * \int_{-1}^{1} u (L(pi*s)) * ds
    # This can be integrated no problemo once we figure out L. To this end, let
    # t_1 and t_2 be two unit mutually orthogonal vectors that are orthogonal to
    # n. Then L(pi*s) = p + R*t_1*cos(pi*s) + R*t_2*sin(pi*s) can be seen to be
    # such that i) |x-p| = R and ii) x.n = 0 [i.e. this the suitable
    # parametrization]
    
    # Clearly we can scale the weights as well as precompute
    # cos and sin terms.
    xq, wq = leggauss(quad_degree)
    wq *= 0.5
    cos_xq = np.cos(np.pi*xq).reshape((-1, 1))
    sin_xq = np.sin(np.pi*xq).reshape((-1, 1))

    if is_number(radius):
         radius = lambda x, radius=radius: radius 

    mesh_x = TV.mesh().coordinates()
    # The idea for point evaluation/computing dofs of TV is to minimize
    # the number of evaluation. I mean a vector dof if done naively would
    # have to evaluate at same x number of component times.
    value_size = TV.ufl_element().value_size()

    # Eval at points will require serch
    tree = mesh.bounding_box_tree()
    limit = mesh.num_cells()

    TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1))
    TV_dm = TV.dofmap()
    V_dm = V.dofmap()
    # For non scalar we plan to make compoenents by shift
    if value_size > 1:
        TV_dm = TV.sub(0).dofmap()

    Vel = V.element()               
    basis_values = np.zeros(V.element().space_dimension()*value_size)
    with petsc_serial_matrix(TV, V) as mat:

        for line_cell in cells(line_mesh):
            # Get the tangent => orthogonal tangent vectors

            v0, v1 = mesh_x[line_cell.entities(0)]
            n = v0 - v1

            t1 = np.array([n[1]-n[2], n[2]-n[0], n[0]-n[1]])
    
            t2 = np.cross(n, t1)
            t1 /= np.linalg.norm(t1)
            t2 = t2/np.linalg.norm(t2)

            # The idea is now to minimize the point evaluation
            scalar_dofs = TV_dm.cell_dofs(line_cell.index())
            scalar_dofs_x = TV_coordinates[scalar_dofs]
            for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x):
                # Get radius and integration points
                rad = radius(avg_point)
         
                integration_points = avg_point + rad*t1*sin_xq + rad*t2*cos_xq

                data = {}
                for index, ip in enumerate(integration_points):
                    c = tree.compute_first_entity_collision(Point(*ip))
                    if c >= limit: continue

                    Vcell = Cell(mesh, c)
                    vertex_coordinates = Vcell.get_vertex_coordinates()
                    cell_orientation = Vcell.orientation()
                    Vel.evaluate_basis_all(basis_values, ip, vertex_coordinates, cell_orientation)

                    cols_ip = V_dm.cell_dofs(c)
                    values_ip = basis_values*wq[index]
                    # Add
                    for col, value in zip(cols_ip, values_ip.reshape((-1, value_size))):
                        if col in data:
                            data[col] += value
                        else:
                            data[col] = value
                            
                # The thing now that with data we can assign to several
                # rows of the matrix
                column_indices = np.array(data.keys(), dtype='int32')
                for shift in range(value_size):
                    row = scalar_row + shift
                    column_values = np.array([data[col][shift] for col in column_indices])
                    mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES)
            # On to next avg point
        # On to next cell
    return PETScMatrix(mat)
Esempio n. 33
0
def sphere_average_matrix(V, TV, radius, quad_degree):
    '''Averaging matrix over the sphere'''
    mesh = V.mesh()
    line_mesh = TV.mesh()
    # Lebedev below need off degrees
    if quad_degree % 2 == 0: quad_degree += 1
    # NOTE: this is a dependency
    from quadpy.sphere import Lebedev

    integrator = Lebedev(quad_degree)
    xq = integrator.points
    wq = integrator.weights
    
    if is_number(radius):
         radius = lambda x, radius=radius: radius 

    mesh_x = TV.mesh().coordinates()
    # The idea for point evaluation/computing dofs of TV is to minimize
    # the number of evaluation. I mean a vector dof if done naively would
    # have to evaluate at same x number of component times.
    value_size = TV.ufl_element().value_size()

    # Eval at points will require serch
    tree = mesh.bounding_box_tree()
    limit = mesh.num_cells()

    TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1))
    TV_dm = TV.dofmap()
    V_dm = V.dofmap()
    # For non scalar we plan to make compoenents by shift
    if value_size > 1:
        TV_dm = TV.sub(0).dofmap()

    Vel = V.element()               
    basis_values = np.zeros(V.element().space_dimension()*value_size)
    with petsc_serial_matrix(TV, V) as mat:

        for line_cell in cells(line_mesh):
            # The idea is now to minimize the point evaluation
            scalar_dofs = TV_dm.cell_dofs(line_cell.index())
            scalar_dofs_x = TV_coordinates[scalar_dofs]
            for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x):
                # Get radius and integration points
                rad = radius(avg_point)
                # Scale and shift the unit sphere to the point
                integration_points = xq*rad + avg_point

                data = {}
                for index, ip in enumerate(integration_points):
                    c = tree.compute_first_entity_collision(Point(*ip))
                    if c >= limit: continue

                    Vcell = Cell(mesh, c)
                    vertex_coordinates = Vcell.get_vertex_coordinates()
                    cell_orientation = Vcell.orientation()
                    Vel.evaluate_basis_all(basis_values, ip, vertex_coordinates, cell_orientation)

                    cols_ip = V_dm.cell_dofs(c)
                    values_ip = basis_values*wq[index]
                    # Add
                    for col, value in zip(cols_ip, values_ip.reshape((-1, value_size))):
                        if col in data:
                            data[col] += value
                        else:
                            data[col] = value
                            
                # The thing now that with data we can assign to several
                # rows of the matrix
                column_indices = np.array(data.keys(), dtype='int32')
                for shift in range(value_size):
                    row = scalar_row + shift
                    column_values = np.array([data[col][shift] for col in column_indices])
                    mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES)
            # On to next avg point
        # On to next cell
    return PETScMatrix(mat)
Esempio n. 34
0
def cylinder_average_matrix(V, TV, radius, quad_degree):
    '''Averaging matrix'''
    mesh = V.mesh()
    line_mesh = TV.mesh()
    # We are going to perform the integration with Gauss quadrature at
    # the end (PI u)(x):
    # A cell of mesh (an edge) defines a normal vector. Let P be the plane
    # that is defined by the normal vector n and some point x on Gamma. Let L
    # be the circle that is the intersect of P and S. The value of q (in Q) at x
    # is defined as
    #
    #                    q(x) = (1/|L|)*\int_{L}g(x)*dL
    #
    # which simplifies to g(x) = (1/(2*pi*R))*\int_{-pi}^{pi}u(L)*R*d(theta) and
    # or                       = (1/2) * \int_{-1}^{1} u (L(pi*s)) * ds
    # This can be integrated no problemo once we figure out L. To this end, let
    # t_1 and t_2 be two unit mutually orthogonal vectors that are orthogonal to
    # n. Then L(pi*s) = p + R*t_1*cos(pi*s) + R*t_2*sin(pi*s) can be seen to be
    # such that i) |x-p| = R and ii) x.n = 0 [i.e. this the suitable
    # parametrization]

    # Clearly we can scale the weights as well as precompute
    # cos and sin terms.
    xq, wq = leggauss(quad_degree)
    wq *= 0.5
    cos_xq = np.cos(np.pi * xq).reshape((-1, 1))
    sin_xq = np.sin(np.pi * xq).reshape((-1, 1))

    if is_number(radius):
        radius = lambda x, radius=radius: radius

    mesh_x = TV.mesh().coordinates()
    # The idea for point evaluation/computing dofs of TV is to minimize
    # the number of evaluation. I mean a vector dof if done naively would
    # have to evaluate at same x number of component times.
    value_size = TV.ufl_element().value_size()

    # Eval at points will require serch
    tree = mesh.bounding_box_tree()
    limit = mesh.num_cells()

    TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1))
    TV_dm = TV.dofmap()
    V_dm = V.dofmap()
    # For non scalar we plan to make compoenents by shift
    if value_size > 1:
        TV_dm = TV.sub(0).dofmap()

    Vel = V.element()
    basis_values = np.zeros(V.element().space_dimension() * value_size)
    with petsc_serial_matrix(TV, V) as mat:

        for line_cell in cells(line_mesh):
            # Get the tangent => orthogonal tangent vectors

            v0, v1 = mesh_x[line_cell.entities(0)]
            n = v0 - v1

            t1 = np.array([n[1] - n[2], n[2] - n[0], n[0] - n[1]])

            t2 = np.cross(n, t1)
            t1 /= np.linalg.norm(t1)
            t2 = t2 / np.linalg.norm(t2)

            # The idea is now to minimize the point evaluation
            scalar_dofs = TV_dm.cell_dofs(line_cell.index())
            scalar_dofs_x = TV_coordinates[scalar_dofs]
            for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x):
                # Get radius and integration points
                rad = radius(avg_point)

                integration_points = avg_point + rad * t1 * sin_xq + rad * t2 * cos_xq

                data = {}
                for index, ip in enumerate(integration_points):
                    c = tree.compute_first_entity_collision(Point(*ip))
                    if c >= limit: continue

                    Vcell = Cell(mesh, c)
                    vertex_coordinates = Vcell.get_vertex_coordinates()
                    cell_orientation = Vcell.orientation()
                    Vel.evaluate_basis_all(basis_values, ip,
                                           vertex_coordinates,
                                           cell_orientation)

                    cols_ip = V_dm.cell_dofs(c)
                    values_ip = basis_values * wq[index]
                    # Add
                    for col, value in zip(cols_ip,
                                          values_ip.reshape((-1, value_size))):
                        if col in data:
                            data[col] += value
                        else:
                            data[col] = value

                # The thing now that with data we can assign to several
                # rows of the matrix
                column_indices = np.array(data.keys(), dtype='int32')
                for shift in range(value_size):
                    row = scalar_row + shift
                    column_values = np.array(
                        [data[col][shift] for col in column_indices])
                    mat.setValues([row], column_indices, column_values,
                                  PETSc.InsertMode.INSERT_VALUES)
            # On to next avg point
        # On to next cell
    return PETScMatrix(mat)
Esempio n. 35
0
def sphere_average_matrix(V, TV, radius, quad_degree):
    '''Averaging matrix over the sphere'''
    mesh = V.mesh()
    line_mesh = TV.mesh()
    # Lebedev below need off degrees
    if quad_degree % 2 == 0: quad_degree += 1
    # NOTE: this is a dependency
    from quadpy.sphere import Lebedev

    integrator = Lebedev(quad_degree)
    xq = integrator.points
    wq = integrator.weights

    if is_number(radius):
        radius = lambda x, radius=radius: radius

    mesh_x = TV.mesh().coordinates()
    # The idea for point evaluation/computing dofs of TV is to minimize
    # the number of evaluation. I mean a vector dof if done naively would
    # have to evaluate at same x number of component times.
    value_size = TV.ufl_element().value_size()

    # Eval at points will require serch
    tree = mesh.bounding_box_tree()
    limit = mesh.num_cells()

    TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1))
    TV_dm = TV.dofmap()
    V_dm = V.dofmap()
    # For non scalar we plan to make compoenents by shift
    if value_size > 1:
        TV_dm = TV.sub(0).dofmap()

    Vel = V.element()
    basis_values = np.zeros(V.element().space_dimension() * value_size)
    with petsc_serial_matrix(TV, V) as mat:

        for line_cell in cells(line_mesh):
            # The idea is now to minimize the point evaluation
            scalar_dofs = TV_dm.cell_dofs(line_cell.index())
            scalar_dofs_x = TV_coordinates[scalar_dofs]
            for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x):
                # Get radius and integration points
                rad = radius(avg_point)
                # Scale and shift the unit sphere to the point
                integration_points = xq * rad + avg_point

                data = {}
                for index, ip in enumerate(integration_points):
                    c = tree.compute_first_entity_collision(Point(*ip))
                    if c >= limit: continue

                    Vcell = Cell(mesh, c)
                    vertex_coordinates = Vcell.get_vertex_coordinates()
                    cell_orientation = Vcell.orientation()
                    Vel.evaluate_basis_all(basis_values, ip,
                                           vertex_coordinates,
                                           cell_orientation)

                    cols_ip = V_dm.cell_dofs(c)
                    values_ip = basis_values * wq[index]
                    # Add
                    for col, value in zip(cols_ip,
                                          values_ip.reshape((-1, value_size))):
                        if col in data:
                            data[col] += value
                        else:
                            data[col] = value

                # The thing now that with data we can assign to several
                # rows of the matrix
                column_indices = np.array(data.keys(), dtype='int32')
                for shift in range(value_size):
                    row = scalar_row + shift
                    column_values = np.array(
                        [data[col][shift] for col in column_indices])
                    mat.setValues([row], column_indices, column_values,
                                  PETSc.InsertMode.INSERT_VALUES)
            # On to next avg point
        # On to next cell
    return PETScMatrix(mat)
Esempio n. 36
0
def get_dims(thing):
    '''
    Size of Rn vector or operator Rn to Rm. We return None for scalars
    and raise when such an operator cannot be established, i.e. there 
    are consistency checks going on 
    '''
    if is_petsc_vec(thing): return thing.size()

    if is_petsc_mat(thing): return (thing.size(0), thing.size(1))
    
    if is_number(thing): return None
    
    # Now let's handdle block stuff
    # Multiplication
    if isinstance(thing, block_mul):
        A, B = thing.chain[0], thing.chain[1:]

        dims_A, dims_B = get_dims(A), get_dims(B[0])
        # A number does not change
        if dims_A is None:
            return dims_B
        if dims_B is None:
            return dims_A
        # Otherwise, consistency
        if len(B) == 1:
            assert len(dims_A) == len(dims_B) 
            assert dims_A[1] == dims_B[0], (dims_A, dims_B) 
            return (dims_A[0], dims_B[1])
        else:
            dims_B = get_dims(reduce(operator.mul, B))
            
            assert len(dims_A) == len(dims_B) 
            assert dims_A[1] == dims_B[0], (dims_A, dims_B)
            return (dims_A[0], dims_B[1])
    # +, -
    if isinstance(thing, (block_add, block_sub)):
        A, B = thing.A, thing.B
        if is_number(A):
            return get_dims(B)

        if is_number(B):
            return get_dims(A)

        dims = get_dims(A)
        assert dims == get_dims(B), (dims, get_dims(B))
        return dims
    # T
    if isinstance(thing, block_transpose):
        dims = get_dims(thing.A)
        return (dims[1], dims[0])
    
    # Some things in cbc.block know their maE.g. InvLumpDiag...Almost last resort
    if hasattr(thing, 'A'):
        assert is_petsc_mat(thing.A)
        return get_dims(thing.A)

    if hasattr(thing, '__sizes__'):
        return thing.__sizes__
    
    if hasattr(thing, 'create_vec'):
        return (thing.create_vec(0).size(), thing.create_vec(1).size())

    raise ValueError('Cannot get_dims of %r, %s' % (type(thing), thing))