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 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 vector(self): ''' A PETSc vector which is wired up with coefficient vectors of the components. So change to component changes this and vice versa ''' return PETScVector(self.petsc_vec())
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 test_vector(): "Test PETScVector interface" prefix = "my_vector_" x = PETScVector(mpi_comm_world()) x.set_options_prefix(prefix) assert x.get_options_prefix() == prefix x.init(300) assert x.get_options_prefix() == prefix
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)
def matvec(self, b): '''Reduce''' reshaped = [] for indices in self.mapping: if len(indices) == 1: reshaped.append(b.blocks[indices[0]]) else: reshaped.append(PETScVector(as_petsc_nest(block_vec([b.blocks[idx] for idx in indices])))) return block_vec(reshaped) if len(reshaped) > 1 else reshaped[0]
def __matvec__(self, b, A=bmat, indices=index_set, work=work): for index in indices: # Exact apply assign bj = PETScVector(as_backend_type(b).vec().getSubVector(index)) xj = A*bj work[index] = xj.get_local() x = self.create_vec() x.set_local(work) return x
def create_vec(self, dim=1): from dolfin import PETScVector if self.transposed: dim = 1 - dim if dim == 0: m = self.M.createVecRight() elif dim == 1: m = self.M.createVecLeft() else: raise ValueError('dim must be <= 1') return PETScVector(m)
def matvec(self, b): '''Reduce''' # print type(b), b.size(), self.offsets # assert b.size() == self.offsets[-1] reduced = [] for f, l in zip(self.offsets[:-1], self.offsets[1:]): if (l - f) == 1: reduced.append(b[f]) else: reduced.append(PETScVector(as_petsc_nest(block_vec(b.blocks[f:l])))) return block_vec(reduced) if len(reduced) > 1 else reduced[0]
def test_vector(): "Test PETScVector interface" prefix = "my_vector_" x = PETScVector(MPI.comm_world) x.set_options_prefix(prefix) assert x.get_options_prefix() == prefix x.init(300) assert x.get_options_prefix() == prefix
def test_petsc4py_vector(pushpop_parameters): "Test PETScVector <-> petsc4py.PETSc.Vec conversions" parameters["linear_algebra_backend"] = "PETSc" # Assemble a test matrix mesh = UnitSquareMesh(4, 4) V = FunctionSpace(mesh, "Lagrange", 1) v = TestFunction(V) a = v*dx b1 = assemble(a) # Test conversion dolfin.PETScVector -> petsc4py.PETSc.Vec b1 = as_backend_type(b1) v1 = b1.vec() # Copy and scale vector with petsc4py v2 = v1.copy() v2.scale(2.0) # Test conversion petsc4py.PETSc.Vec -> PETScVector b2 = PETScVector(v2) assert (b1.get_local()*2.0 == b2.get_local()).all()
def test_petsc4py_vector(pushpop_parameters): "Test PETScVector <-> petsc4py.PETSc.Vec conversions" parameters["linear_algebra_backend"] = "PETSc" # Assemble a test matrix mesh = UnitSquareMesh(4, 4) V = FunctionSpace(mesh, "Lagrange", 1) v = TestFunction(V) a = v * dx b1 = assemble(a) # Test conversion dolfin.PETScVector -> petsc4py.PETSc.Vec b1 = as_backend_type(b1) v1 = b1.vec() # Copy and scale vector with petsc4py v2 = v1.copy() v2.scale(2.0) # Test conversion petsc4py.PETSc.Vec -> PETScVector b2 = PETScVector(v2) assert (b1.get_local() * 2.0 == b2.get_local()).all()
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)
def __init__(self, energy, alpha, bcs, lb=None, ub=None): NonlinearProblem.__init__(self) self.energy = energy self.alpha = alpha self.V = self.alpha.function_space() self.denergy = derivative(self.energy, self.alpha, TestFunction(self.V)) self.ddenergy = derivative(self.denergy, self.alpha, TrialFunction(self.V)) if lb == None: lb = interpolate(Constant("0."), self.V) if ub == None: ub = interpolate(Constant("1."), self.V) self.lb = lb self.ub = ub self.bcs = bcs self.b = PETScVector() self.A = PETScMatrix()
def test_ref_count(pushpop_parameters): "Test petsc4py reference counting" parameters["linear_algebra_backend"] = "PETSc" mesh = UnitSquareMesh(3, 3) V = FunctionSpace(mesh, "P", 1) # Check u and x own the vector u = Function(V) x = as_backend_type(u.vector()).vec() assert x.refcount == 2 # Check decref del u gc.collect() # destroy u assert x.refcount == 1 # Check incref vec = PETScVector(x) assert x.refcount == 2
def transpmult(self, b): '''Unpack''' if isinstance(b, (Vector, GenericVector)): b = [b] else: b = b.blocks n = sum(map(len, self.index_sets)) unpacked = [0]*n for bi, block_dofs, blocks in zip(b, self.index_sets, self.mapping): if len(blocks) == 1: unpacked[blocks[0]] = bi else: x_petsc = as_backend_type(bi).vec() subvecs = [PETScVector(x_petsc.getSubVector(dofs)) for dofs in block_dofs] for j, subvec in zip(blocks, subvecs): unpacked[j] = subvec return block_vec(unpacked)
def transpmult(self, b): '''Unpack''' if isinstance(b, (Vector, GenericVector)): b = [b] else: b = b.blocks n = len(b) assert n == len(self.index_sets), self.index_sets unpacked = [] for bi, iset in zip(b, self.index_sets): if len(iset) == 0: unpacked.append(bi) else: x_petsc = as_backend_type(bi).vec() subvecs = map(lambda indices, x=x_petsc: PETScVector(x.getSubVector(indices)), iset) unpacked.extend(subvecs) return block_vec(unpacked)
def test_options_prefix(pushpop_parameters): "Test set/get prefix option for PETSc objects" def run_test(A, init_function): # Prefix prefix = "test_foo_" # Set prefix A.set_options_prefix(prefix) # Get prefix (should be empty since vector has been initialised) # assert not A.get_options_prefix() # Initialise vector init_function(A) # Check prefix assert A.get_options_prefix() == prefix # Try changing prefix post-intialisation (should throw error) # with pytest.raises(RuntimeError): # A.set_options_prefix("test") # Test vector def init_vector(x): x.init(100) x = PETScVector(MPI.comm_world) run_test(x, init_vector) # Test matrix def init_matrix(A): mesh = UnitSquareMesh(12, 12) V = FunctionSpace(mesh, "Lagrange", 1) u, v = TrialFunction(V), TestFunction(V) assemble(u * v * dx, tensor=A) A = PETScMatrix() run_test(A, init_matrix)
def eigensolve(A, B, V, params, small_enough=5000): '''Solve A*u = lB*u returning the eigenpairs''' # Do small enought exacty if V.dim() < small_enough: print 'Using scipy as dim(V) is %d' % V.dim() return exact_eigensolve(A, B, V, params) # NOTE: you configure this from command line # Here are some defaults my_params = {'-eps_tol': 1E-6, # cvrg tolerance '-eps_max_it': 10000, '-eps_smallest_magnitude': 'none', # which eigenvalues '-eps_nev': 3, # How many '-eps_monitor': 'none', '-eps_type': 'krylovschur'} for key, value in my_params.items(): if key not in params: params[key] = value opts = PETSc.Options() for key, value in params.items(): opts.setValue(key, None if value == 'none' else value) # Setup the eigensolver E = SLEPc.EPS().create() E.setOperators(A ,B) # type is -eps_type E.setProblemType(SLEPc.EPS.ProblemType.GHEP) # Using shift and invert spectral transformation with zero shift? # FIXME: spectral transform and precond to accelerate things if True: ST = E.getST() ST.setType('sinvert') KSP = ST.getKSP() KSP.setType('cg') PC = KSP.getPC() PC.setType('hypre') ksp_params = {'-st_ksp_rtol': 1E-8, # cvrg tolerance '-st_ksp_monitor_true_residual': 'none'} for key, value in ksp_params.items(): opts.setValue(key, None if value == 'none' else value) # PC.setType('lu') # PC.setFactorSolverPackage('mumps') KSP.setFromOptions() E.setFromOptions() E.solve() its = E.getIterationNumber() nconv = E.getConverged() assert nconv > 0 eigenpairs = [] for i in range(nconv): eigv = A.createVecLeft() eigw = E.getEigenpair(i, eigv).real eigenpairs.append((eigw, Function(V, PETScVector(eigv)))) return eigenpairs
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 create_vec(self, dim): if dim == 0: vec = self.A.createVecLeft() else: vec = self.A.createVecRight() return PETScVector(vec)
def create_vec(self, dim=1): from dolfin import PETScVector if dim > 1: raise ValueError('dim must be <= 1') return PETScVector(self.v.copy())
def test_poisson(k): # Polynomial order and mesh resolution nx_list = [4, 8, 16] # Error list error_u_l2, error_u_h1 = [], [] for nx in nx_list: mesh = UnitSquareMesh(nx, nx) # Define FunctionSpaces and functions V = FunctionSpace(mesh, "DG", k) Vbar = FunctionSpace(mesh, FiniteElement("CG", mesh.ufl_cell(), k)["facet"]) u_soln = Expression("sin(pi*x[0])*sin(pi*x[1])", degree=k + 1, domain=mesh) f = Expression("2*pi*pi*sin(pi*x[0])*sin(pi*x[1])", degree=k + 1) u, v = Function(V), TestFunction(V) ubar, vbar = Function(Vbar), TestFunction(Vbar) n = FacetNormal(mesh) h = CellDiameter(mesh) alpha = Constant(6 * k * k) penalty = alpha / h def facet_integral(integrand): return integrand('-') * dS + integrand('+') * dS + integrand * ds u_flux = ubar F_v_flux = grad(u) + penalty * outer(u_flux - u, n) residual_local = inner(grad(u), grad(v)) * dx residual_local += facet_integral(inner(outer(u_flux - u, n), grad(v))) residual_local -= facet_integral(inner(F_v_flux, outer(v, n))) residual_local -= f * v * dx residual_global = facet_integral(inner(F_v_flux, outer(vbar, n))) a_ll = derivative(residual_local, u) a_lg = derivative(residual_local, ubar) a_gl = derivative(residual_global, u) a_gg = derivative(residual_global, ubar) l_l = -residual_local l_g = -residual_global bcs = [DirichletBC(Vbar, u_soln, "on_boundary")] # Initialize static condensation assembler assembler = AssemblerStaticCondensation(a_ll, a_lg, a_gl, a_gg, l_l, l_g, bcs) A_g, b_g = PETScMatrix(), PETScVector() assembler.assemble_global_lhs(A_g) assembler.assemble_global_rhs(b_g) for bc in bcs: bc.apply(A_g, b_g) solver = PETScKrylovSolver() solver.set_operator(A_g) PETScOptions.set("ksp_type", "preonly") PETScOptions.set("pc_type", "lu") PETScOptions.set("pc_factor_mat_solver_type", "mumps") solver.set_from_options() solver.solve(ubar.vector(), b_g) assembler.backsubstitute(ubar._cpp_object, u._cpp_object) # Compute L2 and H1 norms e_u_l2 = assemble((u - u_soln)**2 * dx)**0.5 e_u_h1 = assemble(grad(u - u_soln)**2 * dx)**0.5 if mesh.mpi_comm().rank == 0: error_u_l2.append(e_u_l2) error_u_h1.append(e_u_h1) if mesh.mpi_comm().rank == 0: iterator_list = [1.0 / float(nx) for nx in nx_list] conv_u_l2 = compute_convergence(iterator_list, error_u_l2) conv_u_h1 = compute_convergence(iterator_list, error_u_h1) # Optimal rate of k + 1 - tolerance assert np.all(conv_u_l2 >= (k + 1.0 - 0.15)) # Optimal rate of k - tolerance assert np.all(conv_u_h1 >= (k - 0.1))
def _residual_vector_assemble(self, residual_form: ParametrizedTensorFactory, petsc_residual: PETSc.Vec): self.residual_vector = PETScVector(petsc_residual) evaluate(residual_form, tensor=self.residual_vector)
def solve(self): """ Perform one solution step (in time). """ begin("Cahn-Hilliard step") self.iters["CH"][-1] = self.data["solver"]["CH"]["nln"].solve( self.data["problem_ch"], self.data["sol_ch"].vector())[0] self.iters["CH"][0] += self.iters["CH"][-1] end() begin("Navier-Stokes step") # Update stabilization terms if self.data["model"].parameters["semi"]["sdstab"]: self.data["model"]._update_sd_stab_parameter() pcd_assembler = self.data.get("pcd_assembler", None) if pcd_assembler: # Symmetric assembly of the linear system A, b = PETScMatrix(self.comm()), PETScVector(self.comm()) pcd_assembler.system_matrix(A) pcd_assembler.rhs_vector(b) else: # Standard assembly of the linear system A = assemble(self.data["forms"]["lin"]["lhs"]) b = assemble(self.data["forms"]["lin"]["rhs"]) for bc in self.data["bcs_ns"]: bc.apply(A, b) if self._flags["fix_p"]: # Attach null space to PETSc matrix as_backend_type(A).set_nullspace(self.data["null_space"]) # Orthogonalize RHS vector b with respect to the null space self.data["null_space"].orthogonalize(b) if pcd_assembler: # Symmetric assembly of the preconditioner P = PETScMatrix(self.comm()) pcd_assembler.pc_matrix(P) # FIXME: Should we attach the null space also to preconditioner? # Probably not as 'set_nullspace' is related to KSP (not PC). # if P.empty(): # P = A # else: # as_backend_type(P).set_nullspace(self.data["null_space"]) P = A if P.empty() else P # Standard assembly of the preconditioner matrix # if self.data["forms"]["pcd"]["a_pc"] is not None: # P = assemble(self.data["forms"]["pcd"]["a_pc"]) # for bc in self.data["bcs_ns"]: # bc.apply(P) # else: # P = A self.data["solver"]["NS"].set_operators(A, P) if not self._flags["init_pcd_called"]: # only one call is allowed self.data["solver"]["NS"].init_pcd(pcd_assembler) self._flags["init_pcd_called"] = True else: # FIXME: Here we assume that LU solver is used assert self.data["solver"]["NS"].parameter_type() == 'lu_solver' self.data["solver"]["NS"].set_operator(A) # NOTE: Preconditioner matrix can't be set for LUSolver. self.iters["NS"][-1] = \ self.data["solver"]["NS"].solve(self.data["sol_ns"].vector(), b) info("Navier-Stokes solver finished in {} iterations".format( self.iters["NS"][-1])) self.iters["NS"][0] += self.iters["NS"][-1] if self._flags["fix_p"]: self._calibrate_pressure(self.data["sol_ns"], self.data["null_fcn"]) end()
def residual(self, snes, x, b): self.update_x(x) b_wrap = PETScVector(b) self.ass.assemble(b_wrap, self.alpha_dvec)
def get_eigenvector(self, i): assert i < self.eigen_solver.get_number_converged() # Initialize eigenvectors real_vector = PETScVector() imag_vector = PETScVector() self.A.init_vector(real_vector, 0) self.A.init_vector(imag_vector, 0) # Condense input vectors if hasattr(self, "_is"): # there were Dirichlet BCs condensed_real_vector = PETScVector(real_vector.vec().getSubVector( self._is)) condensed_imag_vector = PETScVector(imag_vector.vec().getSubVector( self._is)) else: condensed_real_vector = real_vector condensed_imag_vector = imag_vector # Get eigenpairs if dolfin_version.startswith( "2018.1"): # TODO remove when 2018.2.0 is released # Helper functions cpp_code = """ #include <pybind11/pybind11.h> #include <dolfin/la/PETScVector.h> #include <dolfin/la/SLEPcEigenSolver.h> void get_eigen_pair(std::shared_ptr<dolfin::SLEPcEigenSolver> eigen_solver, std::shared_ptr<dolfin::PETScVector> condensed_real_vector, std::shared_ptr<dolfin::PETScVector> condensed_imag_vector, std::size_t i) { const PetscInt ii = static_cast<PetscInt>(i); double real_value; double imag_value; EPSGetEigenpair(eigen_solver->eps(), ii, &real_value, &imag_value, condensed_real_vector->vec(), condensed_imag_vector->vec()); } PYBIND11_MODULE(SIGNATURE, m) { m.def("get_eigen_pair", &get_eigen_pair); } """ get_eigen_pair = compile_cpp_code(cpp_code).get_eigen_pair get_eigen_pair(self.eigen_solver, condensed_real_vector, condensed_imag_vector, i) else: self.eigen_solver.get_eigenpair(condensed_real_vector, condensed_imag_vector, i) # Restore input vectors if hasattr(self, "_is"): # there were Dirichlet BCs real_vector.vec().restoreSubVector(self._is, condensed_real_vector.vec()) imag_vector.vec().restoreSubVector(self._is, condensed_imag_vector.vec()) # Return as Function return (Function(self.V, real_vector), Function(self.V, imag_vector))
def _residual_vector_assemble(self, residual_form: Form, petsc_residual: PETSc.Vec): self.residual_vector = PETScVector(petsc_residual) assemble(residual_form, tensor=self.residual_vector)
NeumanBoundary().mark(boundaries, 1) # Define outer surface measure aware of Dirichlet and Neumann boundaries ds = Measure('ds', domain=mesh, subdomain_data=boundaries) # Define variational problem u = TrialFunction(V) d = u.geometric_dimension() v = TestFunction(V) f = Constant((0, 0, 0)) T = Constant((10**3, 0, 0)) a = inner(sigma(u), epsilon(v)) * dx L = dot(f, v) * dx + dot(T, v) * ds(1) A = PETScMatrix() b = PETScVector() assemble_system(a, L, bc, A_tensor=A, b_tensor=b) A = A.mat() b = b.vec() print('problem size: ', b.getSize()) # ========================================================================= # Construct the alist for systems on levels from fine to coarse # construct the transfer operators first ruse = [None] * (nl - 1) Alist = [None] * (nl) ruse[0] = Mat() puse[0].transpose(ruse[0]) Alist[0] = A
def _residual_vector_assemble(self, residual_vector_input: GenericVector, petsc_residual: PETSc.Vec): self.residual_vector = PETScVector(petsc_residual) to_petsc4py(residual_vector_input).swap(petsc_residual)
def boundary_D(x, on_boundary): return on_boundary and (near(x[0], 0, tol) or near(x[0], 1.0, tol)) bc = DirichletBC(V, u_D, boundary_D) u = TrialFunction(V) v = TestFunction(V) f = Expression("10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2) \ + pow(x[2] - 0.5, 2)) / 0.02)", degree=6) g = Expression("sin(5.0*x[0])*sin(5.0*x[1])", degree=6) a = dot(grad(u), grad(v)) * dx L = f * v * dx + g * v * ds A = PETScMatrix() b = PETScVector() assemble_system(a, L, bc, A_tensor=A, b_tensor=b) A = A.mat() b = b.vec() # ========================================================================= # Construct the alist for systems on levels from fine to coarse # construct the transfer operators first ruse = [None] * (nl - 1) Alist = [None] * (nl) ruse[0] = Mat() puse[0].transpose(ruse[0]) Alist[0] = A