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 setup_solver(self): shift = (2.0*pi*self.eigenmode_guess)**2 self.esolver = SLEPcEigenSolver(self._A, self._B) self.esolver.parameters["solver"] = "krylov-schur" self.esolver.parameters["tolerance"] = 1e-12 self.esolver.parameters["spectral_transform"] = "shift-and-invert" self.esolver.parameters["spectral_shift"] = shift self.esolver.parameters["spectrum"] = "target magnitude"
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 __init__(self, V, A, B=None, bcs=None): self.V = V if bcs is not None: self._set_boundary_conditions(bcs) A = self._assemble_if_form(A) if B is not None: B = self._assemble_if_form(B) self._set_operators(A, B) if self.B is not None: self.eigen_solver = SLEPcEigenSolver(self.condensed_A, self.condensed_B) else: self.eigen_solver = SLEPcEigenSolver(self.condensed_A)
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 _assemble(self): # Get input: self.gamma = self.Parameters['gamma'] if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta'] else: self.beta = 0.0 self.Vm = self.Parameters['Vm'] if self.Parameters.has_key('m0'): self.m0 = self.Parameters['m0'].copy(deepcopy=True) isFunction(self.m0) else: self.m0 = Function(self.Vm) self.mtrial = TrialFunction(self.Vm) self.mtest = TestFunction(self.Vm) self.mysample = Function(self.Vm) self.draw = Function(self.Vm) # Assemble: self.R = assemble(inner(nabla_grad(self.mtrial), \ nabla_grad(self.mtest))*dx) self.M = PETScMatrix() assemble(inner(self.mtrial, self.mtest) * dx, tensor=self.M) # preconditioner is Gamma^{-1}: if self.beta > 1e-16: self.precond = self.gamma * self.R + self.beta * self.M else: self.precond = self.gamma * self.R + (1e-14) * self.M # Discrete operator K: self.K = self.gamma * self.R + self.beta * self.M # Get eigenvalues for M: self.eigsolM = SLEPcEigenSolver(self.M) self.eigsolM.solve() # Solver for M^{-1}: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(self.M) # Solver for K^{-1}: self.solverK = LUSolver() self.solverK.parameters['reuse_factorization'] = True self.solverK.parameters['symmetric'] = True self.solverK.set_operator(self.K)
def evaluate_evp(basis): """Evaluate EVP""" assert HAVE_SLEPC # get FEniCS function space V = basis._fefs # Define basis and bilinear form u = TrialFunction(V) v = TestFunction(V) a = inner(nabla_grad(u), nabla_grad(v)) * dx # Assemble stiffness form A = PETScMatrix() assemble(a, tensor=A) # Create eigensolver eigensolver = SLEPcEigenSolver(A) # Compute all eigenvalues of A x = \lambda x print "Computing eigenvalues..." eigensolver.solve() return eigensolver
def slepc_solver(A, M, is_hermitian): '''Smallest eigenvalue''' #TODO Set this up with preconditioners!! solver = SLEPcEigenSolver(A, M) solver.parameters['spectrum'] = 'smallest magnitude' if is_hermitian: solver.parameters['problem_type'] = 'gen_hermitian' else: raise NotImplementedError solver.solve(3) eigw = [solver.get_eigenpair(i)[0] for i in range(solver.get_number_converged())] return eigw
def _assemble(self): # Get input: self.gamma = self.Parameters['gamma'] if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta'] else: self.beta = 0.0 self.Vm = self.Parameters['Vm'] if self.Parameters.has_key('m0'): self.m0 = self.Parameters['m0'].copy(deepcopy=True) isFunction(self.m0) else: self.m0 = Function(self.Vm) self.mtrial = TrialFunction(self.Vm) self.mtest = TestFunction(self.Vm) self.mysample = Function(self.Vm) self.draw = Function(self.Vm) # Assemble: self.R = assemble(inner(nabla_grad(self.mtrial), \ nabla_grad(self.mtest))*dx) self.M = PETScMatrix() assemble(inner(self.mtrial, self.mtest)*dx, tensor=self.M) # preconditioner is Gamma^{-1}: if self.beta > 1e-16: self.precond = self.gamma*self.R + self.beta*self.M else: self.precond = self.gamma*self.R + (1e-14)*self.M # Discrete operator K: self.K = self.gamma*self.R + self.beta*self.M # Get eigenvalues for M: self.eigsolM = SLEPcEigenSolver(self.M) self.eigsolM.solve() # Solver for M^{-1}: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(self.M) # Solver for K^{-1}: self.solverK = LUSolver() self.solverK.parameters['reuse_factorization'] = True self.solverK.parameters['symmetric'] = True self.solverK.set_operator(self.K)
def compute_eig(M, filename, parameters=[]): """ Compute eigenvalues of a PETScMatrix M, and print to filename """ mpirank = MPI.rank(M.mpi_comm()) if mpirank == 0: print '\t\tCompute eigenvalues' eigsolver = SLEPcEigenSolver(M) eigsolver.parameters.update(parameters) eigsolver.solve() if mpirank == 0: print '\t\tSort eigenvalues' eig = [] for ii in range(eigsolver.get_number_converged()): eig.append(eigsolver.get_eigenvalue(ii)[0]) eig.sort() print '\t\tPrint results to file' np.savetxt(filename, np.array(eig))
def solve(self): """ Find eigenvalues for transformed mesh. """ self.progress("Building mesh.") # build transformed mesh mesh = self.refineMesh() # dim = mesh.topology().dim() if self.bcLast: mesh = transform_mesh(mesh, self.transformList) Robin, Steklov, shift, bcs = get_bc_parts(mesh, self.bcList) else: Robin, Steklov, shift, bcs = get_bc_parts(mesh, self.bcList) mesh = transform_mesh(mesh, self.transformList) # boundary conditions computed on non-transformed mesh # copy the values to transformed mesh fun = FacetFunction("size_t", mesh, shift) fun.array()[:] = bcs.array()[:] bcs = fun ds = Measure('ds', domain=mesh, subdomain_data=bcs) V = FunctionSpace(mesh, self.method, self.deg) u = TrialFunction(V) v = TestFunction(V) self.progress("Assembling matrices.") wTop = Expression(self.wTop) wBottom = Expression(self.wBottom) # # build stiffness matrix form # s = dot(grad(u), grad(v)) * wTop * dx # add Robin parts for bc in Robin: s += Constant(bc.parValue) * u * v * wTop * ds(bc.value + shift) # # build mass matrix form # if len(Steklov) > 0: m = 0 for bc in Steklov: m += Constant( bc.parValue) * u * v * wBottom * ds(bc.value + shift) else: m = u * v * wBottom * dx # assemble # if USE_EIGEN: # S, M = EigenMatrix(), EigenMatrix() # tempv = EigenVector() # else: S, M = PETScMatrix(), PETScMatrix() # tempv = PETScVector() if not np.any(bcs.array() == shift + 1): # no Dirichlet parts assemble(s, tensor=S) assemble(m, tensor=M) else: # # with EIGEN we could # apply Dirichlet condition symmetrically # completely remove rows and columns # # Dirichlet parts are marked with shift+1 # # temp = Constant(0)*v*dx bc = DirichletBC(V, Constant(0.0), bcs, shift + 1) # assemble_system(s, temp, bc, A_tensor=S, b_tensor=tempv) # assemble_system(m, temp, bc, A_tensor=M, b_tensor=tempv) assemble(s, tensor=S) bc.apply(S) assemble(m, tensor=M) # bc.zero(M) # if USE_EIGEN: # M = M.sparray() # M.eliminate_zeros() # print M.shape # indices = M.indptr[:-1] - M.indptr[1:] < 0 # M = M[indices, :].tocsc()[:, indices] # S = S.sparray()[indices, :].tocsc()[:, indices] # print M.shape # # solve the eigenvalue problem # self.progress("Solving eigenvalue problem.") eigensolver = SLEPcEigenSolver(S, M) eigensolver.parameters["problem_type"] = "gen_hermitian" eigensolver.parameters["solver"] = "krylov-schur" if self.target is not None: eigensolver.parameters["spectrum"] = "target real" eigensolver.parameters["spectral_shift"] = self.target else: eigensolver.parameters["spectrum"] = "smallest magnitude" eigensolver.parameters["spectral_shift"] = -0.01 eigensolver.parameters["spectral_transform"] = "shift-and-invert" eigensolver.solve(self.number) self.progress("Generating eigenfunctions.") if eigensolver.get_number_converged() == 0: return None eigf = [] eigv = [] if self.deg > 1: mesh = refine(mesh) W = FunctionSpace(mesh, 'CG', 1) for i in range(eigensolver.get_number_converged()): pair = eigensolver.get_eigenpair(i)[::2] eigv.append(pair[0]) u = Function(V) u.vector()[:] = pair[1] eigf.append(interpolate(u, W)) return eigv, eigf
class EigenSolver(AbstractEigenSolver): def __init__(self, V, A, B=None, bcs=None): self.V = V if bcs is not None: self._set_boundary_conditions(bcs) A = self._assemble_if_form(A) if B is not None: B = self._assemble_if_form(B) self._set_operators(A, B) if self.B is not None: self.eigen_solver = SLEPcEigenSolver(self.condensed_A, self.condensed_B) else: self.eigen_solver = SLEPcEigenSolver(self.condensed_A) @staticmethod @overload def _assemble_if_form(mat: Form): return assemble(mat, keep_diagonal=True) @staticmethod @overload def _assemble_if_form(mat: ParametrizedTensorFactory): return evaluate(mat) @staticmethod @overload def _assemble_if_form(mat: Matrix.Type()): return mat def _set_boundary_conditions(self, bcs): # List all local and constrained local dofs local_dofs = set() constrained_local_dofs = set() for bc in bcs: dofmap = bc.function_space().dofmap() local_range = dofmap.ownership_range() local_dofs.update(list(range(local_range[0], local_range[1]))) constrained_local_dofs.update([ dofmap.local_to_global_index(local_dof_index) for local_dof_index in bc.get_boundary_values().keys() ]) # List all unconstrained dofs unconstrained_local_dofs = local_dofs.difference( constrained_local_dofs) unconstrained_local_dofs = list(unconstrained_local_dofs) # Generate IS accordingly comm = bcs[0].function_space().mesh().mpi_comm() for bc in bcs: assert comm == bc.function_space().mesh().mpi_comm() self._is = PETSc.IS().createGeneral(unconstrained_local_dofs, comm) def _set_operators(self, A, B): if hasattr(self, "_is"): # there were Dirichlet BCs (self.A, self.condensed_A) = self._condense_matrix(A) if B is not None: (self.B, self.condensed_B) = self._condense_matrix(B) else: (self.B, self.condensed_B) = (None, None) else: (self.A, self.condensed_A) = (as_backend_type(A), as_backend_type(A)) if B is not None: (self.B, self.condensed_B) = (as_backend_type(B), as_backend_type(B)) else: (self.B, self.condensed_B) = (None, None) 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 set_parameters(self, parameters): self.eigen_solver.parameters.update(parameters) def solve(self, n_eigs=None): assert n_eigs is not None self.eigen_solver.solve(n_eigs) def get_eigenvalue(self, i): return self.eigen_solver.get_eigenvalue(i) def get_eigenvector(self, i): # Helper functions if has_pybind11(): cpp_code = """ #include <pybind11/pybind11.h> #include <dolfin/la/PETScVector.h> #include <dolfin/la/SLEPcEigenSolver.h> PetscInt get_converged(std::shared_ptr<dolfin::SLEPcEigenSolver> eigen_solver) { PetscInt num_computed_eigenvalues; EPSGetConverged(eigen_solver->eps(), &num_computed_eigenvalues); return num_computed_eigenvalues; } void get_eigen_pair(std::shared_ptr<dolfin::SLEPcEigenSolver> eigen_solver, std::size_t i, std::shared_ptr<dolfin::PETScVector> condensed_real_vector, std::shared_ptr<dolfin::PETScVector> condensed_imag_vector) { 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_converged", &get_converged); m.def("get_eigen_pair", &get_eigen_pair); } """ cpp_module = compile_cpp_code(cpp_code) get_converged = cpp_module.get_converged get_eigen_pair = cpp_module.get_eigen_pair else: def get_converged(eigen_solver): return eigen_solver.eps().getConverged() def get_eigen_pair(eigen_solver, i, condensed_real_vector, condensed_imag_vector): eigen_solver.eps().getEigenpair(i, condensed_real_vector.vec(), condensed_imag_vector.vec()) # Get number of computed eigenvectors/values num_computed_eigenvalues = get_converged(self.eigen_solver) if (i < num_computed_eigenvalues): # 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 get_eigen_pair(self.eigen_solver, i, condensed_real_vector, condensed_imag_vector) # 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)) else: raise RuntimeError("Requested eigenpair has not been computed")
class BilaplacianPrior(GaussianPrior): """Gaussian prior Parameters must be a dictionary containing: gamma = multiplicative factor applied to <Grad u, Grad v> term beta = multiplicative factor applied to <u,v> term (default=0.0) m0 = mean (or reference parameter when used as regularization) Vm = function space for parameter cost = 1/2 * (m-m0)^T.R.(m-m0)""" def _assemble(self): # Get input: self.gamma = self.Parameters['gamma'] if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta'] else: self.beta = 0.0 self.Vm = self.Parameters['Vm'] if self.Parameters.has_key('m0'): self.m0 = self.Parameters['m0'].copy(deepcopy=True) isFunction(self.m0) else: self.m0 = Function(self.Vm) self.mtrial = TrialFunction(self.Vm) self.mtest = TestFunction(self.Vm) self.mysample = Function(self.Vm) self.draw = Function(self.Vm) # Assemble: self.R = assemble(inner(nabla_grad(self.mtrial), \ nabla_grad(self.mtest))*dx) self.M = PETScMatrix() assemble(inner(self.mtrial, self.mtest)*dx, tensor=self.M) # preconditioner is Gamma^{-1}: if self.beta > 1e-16: self.precond = self.gamma*self.R + self.beta*self.M else: self.precond = self.gamma*self.R + (1e-14)*self.M # Discrete operator K: self.K = self.gamma*self.R + self.beta*self.M # Get eigenvalues for M: self.eigsolM = SLEPcEigenSolver(self.M) self.eigsolM.solve() # Solver for M^{-1}: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(self.M) # Solver for K^{-1}: self.solverK = LUSolver() self.solverK.parameters['reuse_factorization'] = True self.solverK.parameters['symmetric'] = True self.solverK.set_operator(self.K) def Minvpriordot(self, vect): """Here M.Gamma^{-1} = K M^{-1} K""" mhat = Function(self.Vm) self.solverM.solve(mhat.vector(), self.K*vect) return self.K * mhat.vector() def apply_sqrtM(self, vect): """Compute M^{1/2}.vect from Vector() vect""" sqrtMv = Function(self.Vm) for ii in range(self.Vm.dim()): r, c, rx, cx = self.eigsolM.get_eigenpair(ii) RX = Vector(rx) sqrtMv.vector().axpy(np.sqrt(r)*np.dot(rx.array(), vect.array()), RX) return sqrtMv.vector() def Ldot(self, vect): """Here L = K^{-1} M^{1/2}""" Lb = Function(self.Vm) self.solverK.solve(Lb.vector(), self.apply_sqrtM(vect)) return Lb.vector()
class ELSolver(ModeSolver): """ fully tensorial elastic mode solver """ def __init__(self,mesh,materials): super(ELSolver, self).__init__(mesh, materials) self.q_b = 0.0 self.lagrange_order = 2 self._VComplex = None def assemble_matrices(self): V = VectorElement("Lagrange", self.mesh.ufl_cell(), self.lagrange_order, dim = 3) self._VComplex = FunctionSpace(self.mesh, V*V) u = TrialFunction(self._VComplex) (ur, ui) = split(u) v = TestFunction(self._VComplex) (vr, vi) = split(v) A_ij = None B_ij = None # construct matrices for each domain if not isinstance(self.materials, collections.Iterable): material = self.materials C = as_matrix(material.el.C) rho = material.el.rho DX = material.domain A_ij = LHS(self.q_b, C, ur, ui, vr, vi, DX) B_ij = RHS(rho, ur, ui, vr, vi, DX) else: counter = 0 # a work around for summing forms for material in self.materials: C = as_matrix(material.el.C) rho = material.el.rho DX = material.domain if counter == 0: A_ij = LHS(self.q_b, C, ur, ui, vr, vi, DX) B_ij = RHS(rho, ur, ui, vr, vi, DX) counter += 1 else: a_ij = LHS(self.q_b, C, ur, ui, vr, vi, DX) b_ij = RHS(rho, ur, ui, vr, vi, DX) A_ij += a_ij B_ij += b_ij # assemble the matrices assemble(A_ij, tensor=self._A) assemble(B_ij, tensor=self._B) def setup_solver(self): shift = (2.0*pi*self.eigenmode_guess)**2 self.esolver = SLEPcEigenSolver(self._A, self._B) self.esolver.parameters["solver"] = "krylov-schur" self.esolver.parameters["tolerance"] = 1e-12 self.esolver.parameters["spectral_transform"] = "shift-and-invert" self.esolver.parameters["spectral_shift"] = shift self.esolver.parameters["spectrum"] = "target magnitude" def set_clamped_walls(self): bcs = build_clamped_walls(self._VComplex) bcs.apply(self._A) bcs.apply(self._B) def set_free_walls(self): # this is the natural BC when no changes are made # to the assembled matrices pass def calculate_power(self, eigenvalue_index): r, c, rx, cx = self.esolver.get_eigenpair(eigenvalue_index) u = Function(self._VComplex, rx) omega_sqrd = r*(1e9)**2 # construct matrices for each domain if not isinstance(self.materials, collections.Iterable): material = self.materials rho = material.el.rho DX = material.domain norm = assemble(dot(u, rho*u)*DX) p = 0.5*omega_sqrd*norm return p else: p = 0.0 for material in self.materials: rho = material.el.rho DX = material.domain norm = assemble(dot(u, rho*u)*DX) p += 0.5*omega_sqrd*norm return p def compute_eigenvalues(self): self.esolver.solve(self.n_modes) if self.esolver.get_number_converged()==0: print( 'Eigensolver did not calculate eigenmodes. Increase log level') for i in range(0,self.esolver.get_number_converged(),1): r, c = self.esolver.get_eigenvalue(i) try: f_b = (r)**0.5/(2*pi) print('Frequency {} GHz'.format(f_b)) if self.plot_eigenmodes == True: # TODO save plots to a folder r, c, xr, xi = self.esolver.get_eigenpair(i) f = Function(self._VComplex, xr) plot_displacement(self.mesh, f) except: print("Found negative frequency") def extract_field(self, eigenvalue_index): r, c, xr, xi = self.esolver.get_eigenpair(eigenvalue_index) f = Function(self._VComplex, xr) f_b = (r)**0.5/(2*pi) return (f, f_b)
def standard_solver(K, M): solver = SLEPcEigenSolver(K, M) solver.parameters['spectrum'] = 'smallest magnitude' solver.parameters['tolerance'] = 1e-4 solver.parameters['problem_type'] = 'pos_gen_non_hermitian' return solver
class BilaplacianPrior(GaussianPrior): """Gaussian prior Parameters must be a dictionary containing: gamma = multiplicative factor applied to <Grad u, Grad v> term beta = multiplicative factor applied to <u,v> term (default=0.0) m0 = mean (or reference parameter when used as regularization) Vm = function space for parameter cost = 1/2 * (m-m0)^T.R.(m-m0)""" def _assemble(self): # Get input: self.gamma = self.Parameters['gamma'] if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta'] else: self.beta = 0.0 self.Vm = self.Parameters['Vm'] if self.Parameters.has_key('m0'): self.m0 = self.Parameters['m0'].copy(deepcopy=True) isFunction(self.m0) else: self.m0 = Function(self.Vm) self.mtrial = TrialFunction(self.Vm) self.mtest = TestFunction(self.Vm) self.mysample = Function(self.Vm) self.draw = Function(self.Vm) # Assemble: self.R = assemble(inner(nabla_grad(self.mtrial), \ nabla_grad(self.mtest))*dx) self.M = PETScMatrix() assemble(inner(self.mtrial, self.mtest) * dx, tensor=self.M) # preconditioner is Gamma^{-1}: if self.beta > 1e-16: self.precond = self.gamma * self.R + self.beta * self.M else: self.precond = self.gamma * self.R + (1e-14) * self.M # Discrete operator K: self.K = self.gamma * self.R + self.beta * self.M # Get eigenvalues for M: self.eigsolM = SLEPcEigenSolver(self.M) self.eigsolM.solve() # Solver for M^{-1}: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(self.M) # Solver for K^{-1}: self.solverK = LUSolver() self.solverK.parameters['reuse_factorization'] = True self.solverK.parameters['symmetric'] = True self.solverK.set_operator(self.K) def Minvpriordot(self, vect): """Here M.Gamma^{-1} = K M^{-1} K""" mhat = Function(self.Vm) self.solverM.solve(mhat.vector(), self.K * vect) return self.K * mhat.vector() def apply_sqrtM(self, vect): """Compute M^{1/2}.vect from Vector() vect""" sqrtMv = Function(self.Vm) for ii in range(self.Vm.dim()): r, c, rx, cx = self.eigsolM.get_eigenpair(ii) RX = Vector(rx) sqrtMv.vector().axpy( np.sqrt(r) * np.dot(rx.array(), vect.array()), RX) return sqrtMv.vector() def Ldot(self, vect): """Here L = K^{-1} M^{1/2}""" Lb = Function(self.Vm) self.solverK.solve(Lb.vector(), self.apply_sqrtM(vect)) return Lb.vector()
def solve(self): """ Find eigenvalues for transformed mesh. """ self.progress("Building mesh.") # build transformed mesh mesh = self.refineMesh() # dim = mesh.topology().dim() if self.bcLast: mesh = transform_mesh(mesh, self.transformList) Robin, Steklov, shift, bcs = get_bc_parts(mesh, self.bcList) else: Robin, Steklov, shift, bcs = get_bc_parts(mesh, self.bcList) mesh = transform_mesh(mesh, self.transformList) # boundary conditions computed on non-transformed mesh # copy the values to transformed mesh fun = FacetFunction("size_t", mesh, shift) fun.array()[:] = bcs.array()[:] bcs = fun ds = Measure('ds', domain=mesh, subdomain_data=bcs) V = FunctionSpace(mesh, self.method, self.deg) u = TrialFunction(V) v = TestFunction(V) self.progress("Assembling matrices.") wTop = Expression(self.wTop, degree=self.deg) wBottom = Expression(self.wBottom, degree=self.deg) # # build stiffness matrix form # s = dot(grad(u), grad(v))*wTop*dx # add Robin parts for bc in Robin: s += Constant(bc.parValue)*u*v*wTop*ds(bc.value+shift) # # build mass matrix form # if len(Steklov) > 0: m = 0 for bc in Steklov: m += Constant(bc.parValue)*u*v*wBottom*ds(bc.value+shift) else: m = u*v*wBottom*dx # assemble # if USE_EIGEN: # S, M = EigenMatrix(), EigenMatrix() # tempv = EigenVector() # else: S, M = PETScMatrix(), PETScMatrix() # tempv = PETScVector() if not np.any(bcs.array() == shift+1): # no Dirichlet parts assemble(s, tensor=S) assemble(m, tensor=M) else: # # with EIGEN we could # apply Dirichlet condition symmetrically # completely remove rows and columns # # Dirichlet parts are marked with shift+1 # # temp = Constant(0)*v*dx bc = DirichletBC(V, Constant(0.0), bcs, shift+1) # assemble_system(s, temp, bc, A_tensor=S, b_tensor=tempv) # assemble_system(m, temp, bc, A_tensor=M, b_tensor=tempv) assemble(s, tensor=S) bc.apply(S) assemble(m, tensor=M) # bc.zero(M) # if USE_EIGEN: # M = M.sparray() # M.eliminate_zeros() # print M.shape # indices = M.indptr[:-1] - M.indptr[1:] < 0 # M = M[indices, :].tocsc()[:, indices] # S = S.sparray()[indices, :].tocsc()[:, indices] # print M.shape # # solve the eigenvalue problem # self.progress("Solving eigenvalue problem.") eigensolver = SLEPcEigenSolver(S, M) eigensolver.parameters["problem_type"] = "gen_hermitian" eigensolver.parameters["solver"] = "krylov-schur" if self.target is not None: eigensolver.parameters["spectrum"] = "target real" eigensolver.parameters["spectral_shift"] = self.target else: eigensolver.parameters["spectrum"] = "smallest magnitude" eigensolver.parameters["spectral_shift"] = -0.01 eigensolver.parameters["spectral_transform"] = "shift-and-invert" eigensolver.solve(self.number) self.progress("Generating eigenfunctions.") if eigensolver.get_number_converged() == 0: return None eigf = [] eigv = [] if self.deg > 1: mesh = refine(mesh) W = FunctionSpace(mesh, 'CG', 1) for i in range(eigensolver.get_number_converged()): pair = eigensolver.get_eigenpair(i)[::2] eigv.append(pair[0]) u = Function(V) u.vector()[:] = pair[1] eigf.append(interpolate(u, W)) return eigv, eigf
def solve(self, n=None): """ solve(n=None): Solve the eigenvalue problem and return the converged eigenvalues n indicates the number of eigenvalues to be calculated """ # Standard eigenvalue problem if self.b is None: A = PETScMatrix() assemble(self.a, tensor=A) if self.bc: self.bc.apply(A) solver = SLEPcEigenSolver(A) solver.solve() # Generalized eigenvalue problem else: A = PETScMatrix() assemble(self.a, tensor=A) B = PETScMatrix() assemble(self.b, tensor=B) # Set options for eigensolver solver = SLEPcEigenSolver(A, B) for key, key_info in self.parameters.iterdata(): solver.parameters[key] = self.parameters[key] if n is None: n = A.size(0) if self.bc: self.bc.apply(A) self.bc.apply(B) solver.solve(n) # Pick real part of eigenvalues computed m = solver.get_number_converged() complex_eps = 0.001 eigenvalues = [solver.get_eigenvalue(i)[0] for i in range(m) if abs(solver.get_eigenvalue(i)[1]) < complex_eps] if len(eigenvalues) == 0: raise RuntimeError("No real-valued eigenvalues found") # Compute all eigenvalues if eigenvalue is zero and not only # testing for stability if (n < A.size(0) and abs(eigenvalues[0]) < ascot_parameters["eps"] and not ascot_parameters["only_stable"]): info("Only zero eigenvalues detected. Computing all.") solver.solve(A.size(0)) m = solver.get_number_converged() eigenvalues = [solver.get_eigenvalue(i)[0] for i in range(m) if abs(solver.get_eigenvalue(i)[1]) < 0.1] return eigenvalues
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
class EigenSolver(AbstractEigenSolver): def __init__(self, V, A, B=None, bcs=None): self.V = V if bcs is not None: self._set_boundary_conditions(bcs) A = self._assemble_if_form(A) if B is not None: B = self._assemble_if_form(B) self._set_operators(A, B) if self.B is not None: self.eigen_solver = SLEPcEigenSolver(self.condensed_A, self.condensed_B) else: self.eigen_solver = SLEPcEigenSolver(self.condensed_A) @staticmethod @overload def _assemble_if_form(mat: Form): return assemble(mat, keep_diagonal=True) @staticmethod @overload def _assemble_if_form(mat: ParametrizedTensorFactory): return evaluate(mat) @staticmethod @overload def _assemble_if_form(mat: Matrix.Type()): return mat def _set_boundary_conditions(self, bcs): # List all local and constrained local dofs local_dofs = set() constrained_local_dofs = set() for bc in bcs: dofmap = bc.function_space().dofmap() local_range = dofmap.ownership_range() local_dofs.update(list(range(local_range[0], local_range[1]))) constrained_local_dofs.update([ dofmap.local_to_global_index(local_dof_index) for local_dof_index in bc.get_boundary_values().keys() ]) # List all unconstrained dofs unconstrained_local_dofs = local_dofs.difference( constrained_local_dofs) unconstrained_local_dofs = list(unconstrained_local_dofs) # Generate IS accordingly comm = bcs[0].function_space().mesh().mpi_comm() for bc in bcs: assert comm == bc.function_space().mesh().mpi_comm() self._is = PETSc.IS().createGeneral(unconstrained_local_dofs, comm) def _set_operators(self, A, B): if hasattr(self, "_is"): # there were Dirichlet BCs (self.A, self.condensed_A) = self._condense_matrix(A) if B is not None: (self.B, self.condensed_B) = self._condense_matrix(B) else: (self.B, self.condensed_B) = (None, None) else: (self.A, self.condensed_A) = (as_backend_type(A), as_backend_type(A)) if B is not None: (self.B, self.condensed_B) = (as_backend_type(B), as_backend_type(B)) else: (self.B, self.condensed_B) = (None, None) 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 set_parameters(self, parameters): # Helper functions cpp_code = """ #include <pybind11/pybind11.h> #include <dolfin/la/PETScLUSolver.h> // defines PCFactorSetMatSolverType macro for PETSc <= 3.8 #include <dolfin/la/SLEPcEigenSolver.h> void throw_error(PetscErrorCode ierr, std::string reason); void set_linear_solver(std::shared_ptr<dolfin::SLEPcEigenSolver> eigen_solver, std::string lu_method) { ST st; KSP ksp; PC pc; PetscErrorCode ierr; ierr = EPSGetST(eigen_solver->eps(), &st); if (ierr != 0) throw_error(ierr, "EPSGetST"); ierr = STGetKSP(st, &ksp); if (ierr != 0) throw_error(ierr, "STGetKSP"); ierr = KSPGetPC(ksp, &pc); if (ierr != 0) throw_error(ierr, "KSPGetPC"); ierr = STSetType(st, STSINVERT); if (ierr != 0) throw_error(ierr, "STSetType"); ierr = KSPSetType(ksp, KSPPREONLY); if (ierr != 0) throw_error(ierr, "KSPSetType"); ierr = PCSetType(pc, PCLU); if (ierr != 0) throw_error(ierr, "PCSetType"); ierr = PCFactorSetMatSolverType(pc, lu_method.c_str()); if (ierr != 0) throw_error(ierr, "PCFactorSetMatSolverType"); } void throw_error(PetscErrorCode ierr, std::string reason) { throw std::runtime_error("Error in set_linear_solver: reason " + reason + ", error code " + std::to_string(ierr)); } PYBIND11_MODULE(SIGNATURE, m) { m.def("set_linear_solver", &set_linear_solver); } """ cpp_module = compile_cpp_code(cpp_code) set_linear_solver = cpp_module.set_linear_solver if "spectral_transform" in parameters and parameters[ "spectral_transform"] == "shift-and-invert": parameters["spectrum"] = "target real" if "linear_solver" in parameters: set_linear_solver(self.eigen_solver, parameters["linear_solver"]) parameters.pop("linear_solver") self.eigen_solver.parameters.update(parameters) def solve(self, n_eigs=None): assert n_eigs is not None self.eigen_solver.solve(n_eigs) assert self.eigen_solver.get_number_converged() >= n_eigs def get_eigenvalue(self, i): assert i < self.eigen_solver.get_number_converged() return self.eigen_solver.get_eigenvalue(i) 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))
class EMSolver(ModeSolver): """ electromagnetic mode solver """ def __init__(self,mesh,materials, wavelength): super(EMSolver, self).__init__(mesh, materials) self.wavelength = wavelength self.mesh_unit = 1e-6 self._combined_space = None self._finite_element = None self.nedelec_order = 1 self.lagrange_order = 1 self._k_o_squared = (2.0*pi/self.wavelength)**2 def assemble_matrices(self): nedelec = FiniteElement( "Nedelec 1st kind H(curl)", self.mesh.ufl_cell(), self.nedelec_order) lagrange = FiniteElement( "Lagrange", self.mesh.ufl_cell(), self.lagrange_order) self._finite_element = nedelec*lagrange self._combined_space = FunctionSpace(self.mesh, nedelec*lagrange) # define the test and trial functions from the combined space (N_i, L_i) = TestFunctions(self._combined_space) (N_j, L_j) = TrialFunctions(self._combined_space) # construct matrices for each domain if not isinstance(self.materials, collections.Iterable): material = self.materials u_r = material.em.u_r e_r = material.em.e_r DX = material.domain (A_ij, B_ij) = em_weak_form(N_i, N_j, L_i, L_j, u_r, e_r, self._k_o_squared,DX) else: counter = 0 # a work around for summing forms for material in self.materials: u_r = material.em.u_r e_r = material.em.e_r DX = material.domain if counter == 0: (A_ij, B_ij) = em_weak_form(N_i, N_j, L_i, L_j, u_r, e_r, self._k_o_squared, DX) counter += 1 else: (a_ij, b_ij) = em_weak_form(N_i, N_j, L_i, L_j, u_r, e_r, self._k_o_squared, DX) A_ij += a_ij B_ij += b_ij # assemble the matrices assemble(A_ij, tensor=self._A) assemble(B_ij, tensor=self._B) def setup_solver(self): self.esolver= SLEPcEigenSolver(self._A, self._B) shift = -(2.0*pi*self.eigenmode_guess/self.wavelength)**2 self.esolver.parameters["solver"] = "krylov-schur" self.esolver.parameters["tolerance"] = 1e-12 self.esolver.parameters["spectral_transform"] = "shift-and-invert" self.esolver.parameters["spectrum"] = "target magnitude" self.esolver.parameters["spectral_shift"] = shift def set_electric_walls(self): bcs = build_electric_walls(self._combined_space) bcs.apply(self._A) bcs.apply(self._B) def extract_normalized_field(self, eigenvalue_index): """ extract and rescale the E field from the solver note that Ex, Ey, are real, Ez is imaginary """ r, c, rx, cx = self.esolver.get_eigenpair(eigenvalue_index) e_raw = Function(self._combined_space, rx) n_eff = ((-r)**0.5)*self.wavelength/(2*pi) gamma = (abs(r))**0.5 E = as_vector([e_raw[0], e_raw[1], gamma*e_raw[2]]) return (E, n_eff) def calculate_power(self, eigenvalue_index, ng): """ 1. note the normalization of Ez in the equations 2. assume u_r = 1 everywhere """ r, c, rx, cx = self.esolver.get_eigenpair(eigenvalue_index) f = Function(self._combined_space, rx) gamma_sqrd = abs(r) (Et, Ez) = split(f) # construct matrices for each domain if not isinstance(self.materials, collections.Iterable): material = self.materials e_r = material.em.e_r DX = material.domain int_t = assemble(dot(Et,Et)*DX) int_z = assemble(Ez*Ez*DX)*gamma_sqrd power = 0.5*(c0/ng*eps0*e_r*(int_t+ int_z)) return power else: power = 0.0 for material in self.materials: e_r = material.em.e_r DX = material.domain int_t = assemble(dot(Et,Et)*DX) int_z = assemble(Ez*Ez*DX)*gamma_sqrd power += 0.5*(c0/ng*eps0*e_r*(int_t + int_z)) return power def compute_eigenvalues(self): self.esolver.solve(self.n_modes) if self.esolver.get_number_converged()==0: print('Eigensolver did not converge') for i in range(0,self.esolver.get_number_converged(),1): r, c = self.esolver.get_eigenvalue(i) try: f_b = ((-r)**0.5)*self.wavelength/(2*pi) print('Effective index: {} '.format(f_b)) if self.plot_eigenmodes == True: # TODO save plots to a folder r, c, xr, xi = self.esolver.get_eigenpair(i) e_raw = Function(self._combined_space, xr) plot_transverse_field(e_raw) except: print("Found negative frequency")