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)))
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)))
def collapse_sub(bmat): '''A - B to single matrix''' A, B = bmat.A, bmat.B # Base case if is_petsc_mat(A) and is_petsc_mat(B): A_ = as_petsc(A) B_ = as_petsc(B) assert A_.size == B_.size C_ = A_.copy() # C = A - B C_.axpy(-1., B_, PETSc.Mat.Structure.DIFFERENT) return PETScMatrix(C_) # Recurse return collapse_sub(collapse(A) - collapse(B))
def multTranspose(self, mat, x, y): '''y = A.T*x''' AT = block_transpose(self.A) y *= 0 x_bvec = PETScVector(x) y_bvec = AT * x_bvec y.axpy(1., as_petsc(y_bvec))
def collapse_tr(bmat): '''to Transpose''' # Base A = bmat.A if is_petsc_mat(A): A_ = as_petsc(A) C_ = PETSc.Mat() A_.transpose(C_) return PETScMatrix(C_) # Recurse return collapse_tr(collapse(bmat))
def setup_preconditioner(W, which, eps=None): ''' This is a block diagonal preconditioner based on H1 x H^{-0.5} ''' from xii.linalg.matrix_utils import as_petsc from numpy import hstack from petsc4py import PETSc from hsmg import HsNorm assert len(eps) == 2 mu_value, lmbda_value = eps V, Q = W # H1 u, v = TrialFunction(V), TestFunction(V) b00 = inner(grad(u), grad(v)) * dx + inner(u, v) * dx A = as_backend_type(assemble(b00)) # Attach rigid deformations to A # Functions Z = [ interpolate(Constant((1, 0)), V), interpolate(Constant((0, 1)), V), interpolate(Expression(('x[1]', '-x[0]'), degree=1), V) ] # The basis Z = VectorSpaceBasis([z.vector() for z in Z]) Z.orthonormalize() A.set_nullspace(Z) A.set_near_nullspace(Z) A = as_petsc(A) # Setup the preconditioner in petsc pc = PETSc.PC().create() pc.setType(PETSc.PC.Type.HYPRE) pc.setOperators(A) # Other options opts = PETSc.Options() opts.setValue('pc_hypre_boomeramg_cycle_type', 'V') opts.setValue('pc_hypre_boomeramg_relax_type_all', 'symmetric-SOR/Jacobi') opts.setValue('pc_hypre_boomeramg_coarsen_type', 'Falgout') pc.setFromOptions() # Wrap for cbc.block B00 = BlockPC(pc) # The Q norm via spectral Qi = Q.sub(0).collapse() B11 = inverse(VectorizedOperator(HsNorm(Qi, s=-0.5), Q)) return block_diag_mat([B00, B11])
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()
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
def nest(tensor, elim_zeros_tol=1E-15, W=None): '''Block mat/vec -> PETSc.Nest mat/vec''' # Convert block-vector if isinstance(tensor, block_vec): vecs = [as_petsc(bi) for bi in tensor] # FIXME: handle numbers vec = PETSc.Vec().createNest(vecs) vec.assemble() return vec # Convert block-matrix if isinstance(tensor, block_mat): # Maybe we have preconditioner if any( isinstance(block, precond) for block in tensor.blocks.flatten()): return nrows, ncols = tensor.blocks.shape A = [[None for j in range(ncols)] for i in range(nrows)] for i in range(nrows): for j in range(ncols): if isinstance(tensor[i][j], (int, float)): # assert i != j, (i, j, tensor[i][j]) block = None else: # Optimize for zeros and insert None block = as_petsc(convert(tensor[i][j])) if block.norm(2) < elim_zeros_tol: block = None A[i][j] = block mat = PETSc.Mat().createNest(A) mat.assemble() return mat
def pc_mat(pc, block): '''Set pc for (non-block) operator''' if isinstance(block, LU): pc.setType('lu') pc.setFactorPivot(1E-18) return block.A if isinstance(block, SUPERLU_LU): pc.setType('lu') pc.setFactorPivot(1E-18) pc.setFactorSolverType('superlu') return block.A if isinstance(block, AMG): pc.setType('hypre') return block.A if isinstance(block, Elasticity): pc.setType('gamg') return block.A # FIXME: Very add hoc support for sum, this should recursive if isinstance(block, block_add): this, that = block.A, block.B assert isinstance(this, precond) and isinstance(that, precond) pc.setType('composite') pc.setCompositeType(PETSc.PC.CompositeType.ADDITIVE) for sub, op in enumerate((this, that)): # Fake it pc_ = PETSc.PC().create() A = pc_mat(pc_, op) pc.addCompositePC(pc_.getType()) pc_sub = pc.getCompositePC(sub) # Make it pc_mat(pc_sub, op) pc_sub.setOperators(as_petsc(A)) mat = diagonal_matrix(op.A.size(0), 1) return mat assert False, type(block)
def setup_preconditioner(W, which, eps=None): ''' This is a block diagonal preconditioner based on H1 x L2 x H^{-0.5} ''' from block.algebraic.petsc import LumpedInvDiag from xii.linalg.matrix_utils import as_petsc from numpy import hstack from petsc4py import PETSc from hsmg import HsNorm V, Q, Y = W # H1 u, v = TrialFunction(V), TestFunction(V) b00 = inner(grad(u), grad(v)) * dx + inner(u, v) * dx # NOTE: since interpolation is broken with MINI I don't interpolate # here the RM basis to attach the vectros to matrix A = assemble(b00) A = as_petsc(A) # Setup the preconditioner in petsc pc = PETSc.PC().create() pc.setType(PETSc.PC.Type.HYPRE) pc.setOperators(A) # Other options opts = PETSc.Options() opts.setValue('pc_hypre_boomeramg_cycle_type', 'V') opts.setValue('pc_hypre_boomeramg_relax_type_all', 'symmetric-SOR/Jacobi') opts.setValue('pc_hypre_boomeramg_coarsen_type', 'Falgout') pc.setFromOptions() # Wrap for cbc.block B00 = BlockPC(pc) p, q = TrialFunction(Q), TestFunction(Q) B11 = LumpedInvDiag(assemble(inner(p, q) * dx)) # The Y norm via spectral Yi = Y.sub(0).collapse() B22 = inverse(VectorizedOperator(HsNorm(Yi, s=-0.5), Y)) return block_diag_mat([B00, B11, B22])
def setup_preconditioner(W, which, eps=None): ''' This is a block diagonal preconditioner based on H1 x L2 x H^{-0.5} ''' from block.algebraic.petsc import LumpedInvDiag from xii.linalg.matrix_utils import as_petsc from numpy import hstack from petsc4py import PETSc from hsmg import HsNorm V, Q, Y = W # H1 u, v = TrialFunction(V), TestFunction(V) b00 = inner(grad(u), grad(v))*dx + inner(u, v)*dx # NOTE: since interpolation is broken with MINI I don't interpolate # here the RM basis to attach the vectros to matrix A = assemble(b00) A = as_petsc(A) # Setup the preconditioner in petsc pc = PETSc.PC().create() pc.setType(PETSc.PC.Type.HYPRE) pc.setOperators(A) # Other options opts = PETSc.Options() opts.setValue('pc_hypre_boomeramg_cycle_type', 'V') opts.setValue('pc_hypre_boomeramg_relax_type_all', 'symmetric-SOR/Jacobi') opts.setValue('pc_hypre_boomeramg_coarsen_type', 'Falgout') pc.setFromOptions() # Wrap for cbc.block B00 = BlockPC(pc) p, q = TrialFunction(Q), TestFunction(Q) B11 = LumpedInvDiag(assemble(inner(p, q)*dx)) # The Y norm via spectral Yi = Y.sub(0).collapse() B22 = inverse(VectorizedOperator(HsNorm(Yi, s=-0.5), Y)) return block_diag_mat([B00, B11, B22])
def mult(self, mat, x, y): '''y = A*x''' y *= 0 x_bvec = PETScVector(x) y_bvec = self.A * x_bvec y.axpy(1., as_petsc(y_bvec))
def matvec(self, x): y = self.create_vec(0) self.pc.apply(as_petsc(x), as_petsc(y)) return y
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)