def _jacobian_matrix_assemble(self, jacobian_matrix_input: GenericMatrix, petsc_jacobian: PETSc.Mat): self.jacobian_matrix = PETScMatrix(petsc_jacobian) self.jacobian_matrix.zero() self.jacobian_matrix += jacobian_matrix_input # Make sure to keep nonzero pattern, as dolfin does by default, because this option is apparently # not preserved by the sum petsc_jacobian.setOption(PETSc.Mat.Option.KEEP_NONZERO_PATTERN, True)
def avg_mat(V, TV, reduced_mesh, data): ''' A mapping for computing the surface averages of function in V in the space TV. Surface averaging is defined as (Pi u)(x) = |C_R(x)|int_{C_R(x)} u(y) dy with C_R(x) the circle of radius R(x) centered at x with a normal parallel with the edge tangent. ''' assert TV.mesh().id() == reduced_mesh.id() # It is most natural to represent Pi u in a DG space assert TV.ufl_element().family() == 'Discontinuous Lagrange' # Compatibility of spaces assert V.dolfin_element().value_rank() == TV.dolfin_element().value_rank() assert V.ufl_element().value_shape() == TV.ufl_element().value_shape() assert average_cell(V) == TV.mesh().ufl_cell() assert V.mesh().geometry().dim() == TV.mesh().geometry().dim() radius = data['radius'] # 3d-1d trace if radius is None: return PETScMatrix(trace_3d1d_matrix(V, TV, reduced_mesh)) quad_degree = data['quad_degree'] # Surface averages if data['surface'] == 'cylinder': Rmat = cylinder_average_matrix(V, TV, radius, quad_degree) # The sphere else: Rmat = sphere_average_matrix(V, TV, radius, quad_degree) return PETScMatrix(Rmat)
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 solve(self, mesh, num=5): """ Solve for num eigenvalues based on the mesh. """ # conforming elements V = FunctionSpace(mesh, "CG", self.degree) u = TrialFunction(V) v = TestFunction(V) # weak formulation a = inner(grad(u), grad(v)) * dx b = u * v * ds A = PETScMatrix() B = PETScMatrix() A = assemble(a, tensor=A) B = assemble(b, tensor=B) # find eigenvalues eigensolver = SLEPcEigenSolver(A, B) eigensolver.parameters["spectral_transform"] = "shift-and-invert" eigensolver.parameters["problem_type"] = "gen_hermitian" eigensolver.parameters["spectrum"] = "smallest real" eigensolver.parameters["spectral_shift"] = 1.0E-10 eigensolver.solve(num + 1) # extract solutions lst = [ eigensolver.get_eigenpair(i) for i in range(1, eigensolver.get_number_converged()) ] for k in range(len(lst)): u = Function(V) u.vector()[:] = lst[k][2] lst[k] = (lst[k][0], u) # pair (eigenvalue,eigenfunction) return np.array(lst)
def test_nest_matrix(pushpop_parameters): # Create Matrices and insert into nest A00 = PETScMatrix() A01 = PETScMatrix() A10 = PETScMatrix() mesh = UnitSquareMesh(12, 12) V = FunctionSpace(mesh, "Lagrange", 2) Q = FunctionSpace(mesh, "Lagrange", 1) u, v = TrialFunction(V), TestFunction(V) p, q = TrialFunction(Q), TestFunction(Q) assemble(u * v * dx, tensor=A00) assemble(p * v * dx, tensor=A01) assemble(u * q * dx, tensor=A10) AA = PETScNestMatrix([A00, A01, A10, None]) # Create compatible RHS Vectors and insert into nest u = PETScVector() p = PETScVector() x = PETScVector() A00.init_vector(u, 1) A01.init_vector(p, 1) AA.init_vectors(x, [u, p])
def __init__(self, mesh, materials): """ init class always require mesh and materials to build the weak form """ self.mesh = mesh self.materials = materials #FIXME throw error if not all domains are initilized self.eigenmode_guess = 1.0 self.n_modes = 10 # number of modes to solve for self.plot_eigenmodes = True self._A = PETScMatrix() self._B = PETScMatrix() self.esolver = SLEPcEigenSolver(self._A, self._B)
def cg1_cr_interpolation_matrix(mesh, constrained_domain=None): ''' Compute matrix that allows fast interpolation of CG1 function to Couzeix-Raviart space. ''' CG1 = VectorFunctionSpace(mesh, 'CG', 1, constrained_domain=constrained_domain) CR = VectorFunctionSpace(mesh, 'CR', 1, constrained_domain=constrained_domain) # Get the matrix with approximate sparsity as the interpolation matrix u = TrialFunction(CG1) v = TestFunction(CR) I = PETScMatrix() assemble(dot(u, v) * dx, tensor=I) # Fill the interpolation matrix d = mesh.geometry().dim() compiled_i_module.compute_cg1_cr_interpolation_matrix(I, d) return I
def block_mat_to_petsc(bmat): '''Block mat to PETScMatrix via assembly''' # This is beautiful but slow as hell :) def iter_rows(matrix): for i in range(matrix.size(0)): yield matrix.getrow(i) row_sizes, col_sizes = get_sizes(bmat) row_offsets = np.cumsum([0] + list(row_sizes)) col_offsets = np.cumsum([0] + list(col_sizes)) with petsc_serial_matrix(row_offsets[-1], col_offsets[-1]) as mat: row = 0 for row_blocks in bmat.blocks: # Zip the row iterators of the matrices together for indices_values in itertools.izip(*map(iter_rows, row_blocks)): indices, values = zip(*indices_values) indices = [ list(index + offset) for index, offset in zip(indices, col_offsets) ] indices = sum(indices, []) row_values = np.hstack(values) mat.setValues([row], indices, row_values, PETSc.InsertMode.INSERT_VALUES) row += 1 return PETScMatrix(mat)
def collapse(x): """Compute an explicit matrix representation of an operator. For example, given a block_ mul object M=A*B, collapse(M) performs the actual matrix multiplication. """ # Since _collapse works recursively, this method is a user-visible wrapper # to print timing, and to check input/output arguments. from time import time from dolfin import PETScMatrix, info, warning T = time() res = _collapse(x) if getattr(res, 'transposed', False): # transposed matrices will normally be converted to a non-transposed # one by matrix multiplication or addition, but if the transpose is the # outermost operation then this doesn't work. res.M.transpose() info('computed explicit matrix representation %s in %.2f s' % (str(res), time() - T)) result = PETScMatrix(res.M) # Sanity check. Cannot trust EpetraExt.Multiply always, it seems. from block import block_vec v = x.create_vec() block_vec([v]).randomize() xv = x * v err = (xv - result * v).norm('l2') / (xv).norm('l2') if (err > 1e-3): raise RuntimeError( 'collapse computed wrong result; ||(a-a\')x||/||ax|| = %g' % err) return result
def project(expr, space): u, v = TrialFunction(space), TestFunction(space) a = inner(u, v)*dx L = inner(expr, v)*dx A, b = PETScMatrix(), PETScVector() assemble_system(a, L, A_tensor=A, b_tensor=b) uh = Function(space) x = uh.vector() solve(A, x, b, 'lu') lmax = SLEPcEigenSolver(A) lmax.parameters["spectrum"] = "largest magnitude" lmax.parameters["problem_type"] = "hermitian" lmax.solve(2) lmax = max([lmax.get_eigenpair(i)[0] for i in range(lmax.get_number_converged())]) lmin = SLEPcEigenSolver(A) lmin.parameters["spectrum"] = "smallest magnitude" lmin.parameters["problem_type"] = "hermitian" lmin.solve(2) lmin = max([lmin.get_eigenpair(i)[0] for i in range(lmin.get_number_converged())]) print space.dim(), 'Cond number', lmax/lmin return uh
def diagonal_matrix(size, A=1): '''Dolfin A*I serial only''' d = PETSc.Vec().createWithArray(A*np.ones(size)) I = PETSc.Mat().createAIJ(size=size, nnz=1) I.setDiagonal(d) return PETScMatrix(I)
def get_work_dolfin_mat(self, key, comm, can_be_destroyed=None, can_be_shared=None): """Get working DOLFIN matrix by key. ``can_be_destroyed=True`` tells that it is probably favourable to not store the matrix unless it is shared as it will not be used ever again, ``None`` means that it can be destroyed but it is not probably favourable and ``False`` forbids the destruction. ``can_be_shared`` tells if a work matrix can be the same with work matrices for other keys.""" # TODO: Add mechanism for sharing DOLFIN mats # NOTE: Maybe we don't really need sharing. If only persistent matrix # is convection then there is nothing to be shared. # Check if requested matrix is in scratch dolfin_mat = self.scratch.get(key, None) # Allocate new matrix otherwise if dolfin_mat is None: if isinstance(comm, PETSc.Comm): comm = comm.tompi4py() dolfin_mat = PETScMatrix(comm) # Store or pop the matrix as requested if can_be_destroyed in [False, None]: self.scratch[key] = dolfin_mat else: assert can_be_destroyed is True self.scratch.pop(key, None) return dolfin_mat
def injection_matrix(Vc, Vf, fine_mesh, data): '''Injection mapping from Vc to Vf''' mesh_c = Vc.mesh() assert fine_mesh.id() == Vf.mesh().id() mesh_f = Vf.mesh() if data['not_nested_method'] == 'interpolate': return df.PETScDMCollection.create_transfer_matrix(Vc, Vf) elif data['not_nested_method'] == 'project': raise ValueError('Missing projection') # Fallback to our interpolate with lookup, which, however is slower # to `create_transfer_matrix` tdim = mesh_f.topology().dim() # Refine was used to create it keys, fine_to_coarse = list( zip(*list(fine_mesh.parent_entity_map[mesh_c.id()][tdim].items()))) fine_to_coarse = np.array(fine_to_coarse, dtype='uintp') fine_to_coarse[np.argsort(keys)] = fine_to_coarse # The idea is to evaluate Vf's degrees of freedom at basis functions of Vc fdmap = Vf.dofmap() Vf_dof = DegreeOfFreedom(Vf) cdmap = Vc.dofmap() Vc_basis_f = FEBasisFunction(Vc) # Column values visited_rows = [False] * Vf.dim() row_values = np.zeros(Vc_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(Vf, Vc) as mat: for f_cell, c_cell in enumerate(fine_to_coarse): Vc_basis_f.cell = c_cell # These are the colums coarse_dofs = cdmap.cell_dofs(c_cell) Vf_dof.cell = f_cell fine_dofs = fdmap.cell_dofs(f_cell) for local, dof in enumerate(fine_dofs): if visited_rows[dof]: continue else: visited_rows[dof] = True Vf_dof.dof = local # Evaluete coarse basis foos here for local_c, dof_c in enumerate(coarse_dofs): Vc_basis_f.dof = local_c row_values[local_c] = Vf_dof.eval(Vc_basis_f) # Insert mat.setValues([dof], coarse_dofs, row_values, PETSc.InsertMode.INSERT_VALUES) return PETScMatrix(mat)
def trace_mat(V, TV, trace_mesh, data): ''' A mapping for computing traces of function in V in TV. If f in V then g in TV has coefficients equal to dofs_{TV}(trace V). Trace is understood as D -> D-1. ''' # Compatibility of spaces assert V.dolfin_element().value_rank() == TV.dolfin_element().value_rank() assert V.ufl_element().value_shape() == TV.ufl_element().value_shape() assert trace_cell(V) == TV.mesh().ufl_cell() assert V.mesh().geometry().dim() == TV.mesh().geometry().dim() # FIXME: trace element checking if trace_mesh is not None: assert trace_mesh.id() == TV.mesh().id() restriction = data['restriction'] normal = data['normal'] tag_data = data['tag_data'] # Restriction is defined using the normal if restriction: assert normal is not None, 'R is %s' % restriction # Typically with CG spaces - any parent cell can set the valeus if not restriction: Tmat = trace_mat_no_restrict(V, TV, trace_mesh, tag_data=tag_data) else: if restriction in ('+', '-'): Tmat = trace_mat_one_restrict(V, TV, restriction, normal, trace_mesh, tag_data) else: assert restriction in ('avg', 'jump') Tmat = trace_mat_two_restrict(V, TV, restriction, normal, trace_mesh, tag_data) return PETScMatrix(Tmat)
def assemble_stiffness(self, mesh): V = FunctionSpace(mesh, "Lagrange", self.p) u = TrialFunction(V) v = TestFunction(V) k = inner(grad(u), grad(v)) * dx K = PETScMatrix() assemble(k, tensor=K) return K, V
def assemble_mass(self, mesh): V = FunctionSpace(mesh, "Lagrange", self.p) u = TrialFunction(V) v = TestFunction(V) m = u * v * dx M = PETScMatrix() assemble(m, tensor=M) return M
def numpy_to_petsc(mat): '''Build PETScMatrix with array structure''' # Dense array to matrix if isinstance(mat, np.ndarray): return numpy_to_petsc(csr_matrix(mat)) # Sparse A = PETSc.Mat().createAIJ(size=mat.shape, csr=(mat.indptr, mat.indices, mat.data)) return PETScMatrix(A)
def transpose_matrix(A): '''Create a transpose of PETScMatrix/PETSc.Mat''' if isinstance(A, PETSc.Mat): At = PETSc.Mat() # Alloc A.transpose(At) # Transpose to At return At At = transpose_matrix(as_backend_type(A).mat()) return PETScMatrix(At)
def _condense_matrix(self, mat): mat = as_backend_type(mat) petsc_version = PETSc.Sys().getVersionInfo() if petsc_version["major"] == 3 and petsc_version["minor"] <= 7: condensed_mat = mat.mat().getSubMatrix(self._is, self._is) else: condensed_mat = mat.mat().createSubMatrix(self._is, self._is) return mat, PETScMatrix(condensed_mat)
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))
def diagonal_matrix(size, A): '''Dolfin A*I serial only''' if isinstance(A, (int, float)): d = PETSc.Vec().createWithArray(A*np.ones(size)) else: d = as_backend_type(A).vec() I = PETSc.Mat().createAIJ(size=size, nnz=1) I.setDiagonal(d) I.assemble() return PETScMatrix(I)
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 zero_matrix(nrows, ncols): '''Zero matrix''' mat = csr_matrix((np.zeros(nrows, dtype=float), # Data # Rows, cols = so first col in each row is 0 (np.arange(nrows), np.zeros(nrows, dtype=int))), shape=(nrows, ncols)) A = PETSc.Mat().createAIJ(size=[[nrows, nrows], [ncols, ncols]], csr=(mat.indptr, mat.indices, mat.data)) A.assemble() return PETScMatrix(A)
def restriction_mat(V, TV, rmesh, data): ''' A mapping for computing restriction of function in V in TV. If f in V then g in TV has coefficients equal to dofs_{TV}(trace V). The TV space is assumed to be setup on subdmoain of V mesh. ''' # Compatibility of spaces assert V.ufl_element() == TV.ufl_element() # Check that rmesh came from OverlapMesh assert V.mesh().id() in rmesh.parent_entity_map return PETScMatrix(restriction_matrix(V, TV, rmesh))
def test_lu_cholesky(): """Test that PETScLUSolver selects LU or Cholesky solver based on symmetry of matrix operator. """ from petsc4py import PETSc mesh = UnitSquareMesh(mpi_comm_world(), 12, 12) V = FunctionSpace(mesh, "Lagrange", 1) u, v = TrialFunction(V), TestFunction(V) A = PETScMatrix(mesh.mpi_comm()) assemble(Constant(1.0)*u*v*dx, tensor=A) # Check that solver type is LU solver = PETScLUSolver(mesh.mpi_comm(), A, "petsc") pc_type = solver.ksp().getPC().getType() assert pc_type == "lu" # Set symmetry flag A.mat().setOption(PETSc.Mat.Option.SYMMETRIC, True) # Check symmetry flags symm = A.mat().isSymmetricKnown() assert symm[0] == True assert symm[1] == True # Check that solver type is Cholesky since matrix has now been # marked as symmetric solver = PETScLUSolver(mesh.mpi_comm(), A, "petsc") pc_type = solver.ksp().getPC().getType() assert pc_type == "cholesky" # Re-assemble, which resets symmetry flag assemble(Constant(1.0)*u*v*dx, tensor=A) solver = PETScLUSolver(mesh.mpi_comm(), A, "petsc") pc_type = solver.ksp().getPC().getType() assert pc_type == "lu"
def test_lu_cholesky(): """Test that PETScLUSolver selects LU or Cholesky solver based on symmetry of matrix operator. """ from petsc4py import PETSc mesh = UnitSquareMesh(MPI.comm_world, 12, 12) V = FunctionSpace(mesh, "Lagrange", 1) u, v = TrialFunction(V), TestFunction(V) A = PETScMatrix(mesh.mpi_comm()) assemble(Constant(1.0)*u*v*dx, tensor=A) # Check that solver type is LU solver = PETScLUSolver(mesh.mpi_comm(), A, "petsc") pc_type = solver.ksp().getPC().getType() assert pc_type == "lu" # Set symmetry flag A.mat().setOption(PETSc.Mat.Option.SYMMETRIC, True) # Check symmetry flags symm = A.mat().isSymmetricKnown() assert symm[0] == True assert symm[1] == True # Check that solver type is Cholesky since matrix has now been # marked as symmetric solver = PETScLUSolver(mesh.mpi_comm(), A, "petsc") pc_type = solver.ksp().getPC().getType() assert pc_type == "cholesky" # Re-assemble, which resets symmetry flag assemble(Constant(1.0)*u*v*dx, tensor=A) solver = PETScLUSolver(mesh.mpi_comm(), A, "petsc") pc_type = solver.ksp().getPC().getType() assert pc_type == "lu"
def test_petsc4py_matrix(pushpop_parameters): "Test PETScMatrix <-> petsc4py.PETSc.Mat conversions" parameters["linear_algebra_backend"] = "PETSc" # Assemble a test matrix mesh = UnitSquareMesh(4, 4) V = FunctionSpace(mesh, "Lagrange", 1) u, v = TrialFunction(V), TestFunction(V) a = u * v * dx A1 = assemble(a) # Test conversion dolfin.PETScMatrix -> petsc4py.PETSc.Mat A1 = as_backend_type(A1) M1 = A1.mat() # Copy and scale matrix with petsc4py M2 = M1.copy() M2.scale(2.0) # Test conversion petsc4py.PETSc.Mat -> PETScMatrix A2 = PETScMatrix(M2) assert (A1.array() * 2.0 == A2.array()).all()
def test_petsc4py_matrix(pushpop_parameters): "Test PETScMatrix <-> petsc4py.PETSc.Mat conversions" parameters["linear_algebra_backend"] = "PETSc" # Assemble a test matrix mesh = UnitSquareMesh(4, 4) V = FunctionSpace(mesh, "Lagrange", 1) u, v = TrialFunction(V), TestFunction(V) a = u*v*dx A1 = assemble(a) # Test conversion dolfin.PETScMatrix -> petsc4py.PETSc.Mat A1 = as_backend_type(A1) M1 = A1.mat() # Copy and scale matrix with petsc4py M2 = M1.copy() M2.scale(2.0) # Test conversion petsc4py.PETSc.Mat -> PETScMatrix A2 = PETScMatrix(M2) assert (A1.array()*2.0 == A2.array()).all()
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 assemble_lui_stiffness(self, g, f, mesh, robin_boundary): V = FunctionSpace(mesh, "Lagrange", self.p) u = TrialFunction(V) v = TestFunction(V) n = FacetNormal(mesh) robin = MeshFunction('size_t', mesh, mesh.topology().dim() - 1) robin_boundary.mark(robin, 1) ds = Measure('ds', subdomain_data=robin) a = inner(grad(u), grad(v)) * dx b = (1 - g) * (inner(grad(u), n)) * v * ds(1) c = f * u * v * ds(1) k = lhs(a + b - c) K = PETScMatrix() assemble(k, tensor=K) return K, V
def row_matrix(rows): '''Short and fat matrix''' ncols, = set(row.size() for row in rows) nrows = len(rows) indptr = np.cumsum(np.array([0]+[ncols]*nrows)) indices = np.tile(np.arange(ncols), nrows) data = np.hstack([row.get_local() for row in rows]) mat = csr_matrix((data, indices, indptr), shape=(nrows, ncols)) A = PETSc.Mat().createAIJ(size=[[nrows, nrows], [ncols, ncols]], csr=(mat.indptr, mat.indices, mat.data)) A.assemble() return PETScMatrix(A)
def numpy_to_petsc(mat): '''Build PETScMatrix with array structure''' # Dense array to matrix if isinstance(mat, np.ndarray): if mat.ndim == 1: vec = PETSc.Vec().createWithArray(mat) vec.assemble() return PETScVector(vec) return numpy_to_petsc(csr_matrix(mat)) # Sparse A = PETSc.Mat().createAIJ(comm=COMM, size=mat.shape, csr=(mat.indptr, mat.indices, mat.data)) A.assemble() return PETScMatrix(A)