def Mult(self,rhs,usol): usol[:] = 0. res = usol.CreateVector() projres = usol.CreateVector() proj = Projector(mask=self.CutVh.FreeDofs(),range=True) fnew = rhs #f.vec.data projres.data = proj*fnew #f.vec normf = Norm(projres) for it in range(self.maxit): usol.data = self.MGpre*fnew res.data = fnew - self.mats[-1]*usol projres.data = proj*res res_norm = Norm(projres) / normf if self.printinfo: print("it =", it+1, " ||res||_2 =", res_norm) if res_norm < self.tol: break #udraw = GridFunction( self.CutVh ) #udraw.components[1].Set( solution[1], BND ) #udraw.vec.data += usol.vec return usol
def __init__(self, mat: BaseMatrix, pre: Optional[Preconditioner] = None, freedofs: Optional[BitArray] = None, tol: float = None, maxiter: int = 100, atol: float = None, callback: Optional[Callable[[int, float], None]] = None, callback_sol: Optional[Callable[[BaseVector], None]] = None, printrates: bool = False): super().__init__() if atol is None and tol is None: tol = 1e-12 self.mat = mat assert (freedofs is None) != (pre is None ) # either pre or freedofs must be given self.pre = pre if pre else Projector(freedofs, True) self.tol = tol self.atol = atol self.maxiter = maxiter self.callback = callback self.callback_sol = callback_sol self.printrates = printrates self.residuals = [] self.iterations = 0
def __init__(self, mat: BaseMatrix, pre: Optional[Preconditioner] = None, freedofs: Optional[BitArray] = None, conjugate: bool = False, tol: float = 1e-12, maxsteps: int = 100, callback: Optional[Callable[[int, float], None]] = None, printing=False, abstol=None): super().__init__() self.mat = mat assert (freedofs is None) != (pre is None ) # either pre or freedofs must be given self.pre = pre if pre else Projector(freedofs, True) self.conjugate = conjugate self.tol = tol self.abstol = abstol self.maxsteps = maxsteps self.callback = callback self._tmp_vecs = [self.mat.CreateRowVector() for i in range(3)] self.logger = logging.getLogger("CGSolver") self.printing = printing if printing: self.handler = handler = logging.StreamHandler() self.logger.addHandler(handler) self.logger.setLevel(logging.INFO) self.errors = [] self.iterations = 0
def BVP(bf, lf, gf, pre=None, maxsteps=200, tol=1e-8, print=True, inverse="umfpack"): """ Solve a linear boundary value problem """ from ngsolve import Projector from ngsolve.krylovspace import CG r = lf.vec.CreateVector() r.data = lf.vec if bf.condense: r.data += bf.harmonic_extension_trans * r # zero local dofs innerbits = gf.space.FreeDofs(False) & ~gf.space.FreeDofs(True) Projector(innerbits, False).Project(gf.vec) if pre: CG(mat=bf.mat, rhs=r, pre=pre, sol=gf.vec, tol=tol, maxsteps=maxsteps, initialize=False, printrates=print) else: inv = bf.mat.Inverse(gf.space.FreeDofs(bf.condense), inverse=inverse) r.data -= bf.mat * gf.vec gf.vec.data += inv * r if bf.condense: gf.vec.data += bf.harmonic_extension * gf.vec gf.vec.data += bf.inner_solve * r
def PreconditionedRichardson(a, rhs, pre=None, freedofs=None, maxit=100, tol=1e-8, dampfactor=1.0, printing=True): """ Preconditioned Richardson Iteration Parameters ---------- a : BilinearForm The left hand side of the equation to solve rhs : Vector The right hand side of the equation. pre : Preconditioner If provided the preconditioner is used. freedofs : BitArray The FreeDofs on which the Richardson iteration acts. If argument is 'None' then the FreeDofs of the underlying FESpace is used. maxit : int Number of maximal iteration for Richardson iteration. If the maximal number is reached before the tolerance is reached a warning is displayed. tol : double Tolerance of the residuum. Richardson iteration stops if residuum < tolerance*initial_residuum is reached. dampfactor : float Set the damping factor for the Richardson iteration. If it is 1 then no damping is done. Values greater than 1 are allowed. printing : bool Set if Richardson iteration should print informations about the actual iteration like the residuum. Returns ------- (vector) Solution vector of the Preconditioned Richardson iteration. """ u = rhs.CreateVector() r = rhs.CreateVector() u[:] = 0 projector = Projector( freedofs if freedofs else a.space.FreeDofs(coupling=a.condense), False) r.data = rhs # r.data = rhs - a.mat*u r.data -= projector * r it = 0 initial_res_norm = Norm(r) if printing: print("it =", it, " ||res||_2 =", initial_res_norm) for it in range(1, maxit + 1): u.data += dampfactor * (pre * r if pre else r) r.data = rhs - a.mat * u r.data -= projector * r res_norm = Norm(r) if printing: print("it =", it, " ||res||_2 =", res_norm) if res_norm < tol * initial_res_norm: break else: print("Warning: Preconditioned Richardson did not converge to TOL") return u
def Mult(self,rhs,usol): proj = Projector(mask=self.fes.FreeDofs(),range=True) usol[:] = 0. # projres = rhs.CreateVector() projres.data = proj*rhs normf = Norm(projres) # LinDof = self.linmgiter.getSpaceDim() if( type(self.fes) == ngsolve.comp.CompoundFESpace or type(self.fes) == ngsolve.comp.FESpace): numspaces = len( self.fes.components ) HOdof = [ self.fes.components[i].ndof for i in range(numspaces) ] else: HOdof = [ self.fes.ndof ] # print('lindof, hdof: ') print(LinDof,HOdof) oldres = Norm(projres) for it in range(self.maxit): # initial smoothing self.blockjac.Smooth(usol, rhs, self.nu) res = usol.CreateVector() # perform multiplication more efficiently ... # only 'upper' part (vertex dofs) needed res.data = rhs -self.a.mat * usol #eliminate boundary condition projres.data = proj*res mgrhs = self.linmgiter.createVec() # copy data from two domains # ... should be done in combination with vertexdofs ... #mgrhs.Range(0, LinDof[0]).data = projres.Range(0, LinDof[0] ) #mgrhs.Range( LinDof[0], len(mgrhs) ).data = projres.Range(HOdof[0], HOdof[0] + LinDof[1] ) mgrhs.Range(0, LinDof[0]).data = projres.Range(0, LinDof[0] ) if( len(LinDof) > 1 ): offsetc = 0 offsetf = 0 for i in range( len(LinDof) -1 ): offsetc += LinDof[i] offsetf += HOdof[i] mgrhs.Range( offsetc, offsetc + LinDof[i+1] ).data = projres.Range(offsetf, offsetf + LinDof[i+1] ) # coarse grid correction: multigrid on linears cup = mgrhs.CreateVector() cup.data = self.linmgiter*mgrhs #maxit=1,printinfo=False) # update solution # ... should be done in combination with vertexdofs ... usol.Range(0, LinDof[0] ).data += cup.Range(0, LinDof[0]) if( len(LinDof) > 1 ): offsetc = 0 offsetf = 0 for i in range( len(LinDof) -1 ): offsetc += LinDof[i] offsetf += HOdof[i] usol.Range( offsetf, offsetf + LinDof[i+1] ).data += cup.Range( offsetc, offsetc + LinDof[i+1] ) ''' usol.Range(0, LinDof[0] ).data += cup.Range(0, LinDof[0]) usol.Range( HOdof[0], HOdof[0] + LinDof[1]).data += cup.Range( LinDof[0], len(mgrhs) ) ''' # compute residual for measurement res.data = rhs - self.a.mat*usol projres.data = proj*res res_norm = Norm(projres) print("tg-it =", it+1, "\t ||res||_2 = {0:.2E}".format(res_norm), "\t reduction: {0:.2f}".format(res_norm/oldres)) if res_norm < self.tol*normf: break oldres = res_norm return usol
def GMRes(A, b, pre=None, freedofs=None, x=None, maxsteps=100, tol=1e-7, innerproduct=None, callback=None, restart=None, startiteration=0, printrates=True): """ Restarting preconditioned gmres solver for A*x=b. Minimizes the preconditioned residuum pre*(b-A*x) Parameters ---------- A : BaseMatrix The left hand side of the linear system. b : BaseVector The right hand side of the linear system. pre : BaseMatrix = None The preconditioner for the system. If no preconditioner is given, the freedofs of the system must be given. freedofs : BitArray = None Freedofs to solve on, only necessary if no preconditioner is given. x : BaseVector = None Startvector, if given it will be modified in the routine and returned. Will be created if not given. maxsteps : int = 100 Maximum iteration steps. tol : float = 1e-7 Tolerance to be computed to. Gmres breaks if norm(pre*(b-A*x)) < tol. innerproduct : function = None Innerproduct to be used in iteration, all orthogonalizations/norms are computed with respect to that inner product. callback : function = None If given, this function is called with the solution vector x in each step. Only for debugging purposes, since it requires the overhead of computing x in every step. restart : int = None If given, gmres is restarted with the current solution x every 'restart' steps. startiteration : int = 0 Internal value to count total number of iterations in restarted setup, no user input required here. printrates : bool = True Print norm of preconditioned residual in each step. """ if not innerproduct: innerproduct = lambda x, y: y.InnerProduct(x, conjugate=True) norm = Norm else: norm = lambda x: sqrt(innerproduct(x, x).real) is_complex = b.is_complex if not pre: assert freedofs is not None pre = Projector(freedofs, True) n = len(b) m = maxsteps if not x: x = b.CreateVector() x[:] = 0 if callback: xstart = x.CreateVector() xstart.data = x else: xstart = None sn = Vector(m, is_complex) cs = Vector(m, is_complex) sn[:] = 0 cs[:] = 0 r = b.CreateVector() tmp = b.CreateVector() tmp.data = b - A * x r.data = pre * tmp Q = [] H = [] Q.append(b.CreateVector()) r_norm = norm(r) if abs(r_norm) < tol: return x Q[0].data = 1. / r_norm * r beta = Vector(m + 1, is_complex) beta[:] = 0 beta[0] = r_norm def arnoldi(A, Q, k): q = b.CreateVector() tmp.data = A * Q[k] q.data = pre * tmp h = Vector(m + 1, is_complex) h[:] = 0 for i in range(k + 1): h[i] = innerproduct(Q[i], q) q.data += (-1) * h[i] * Q[i] h[k + 1] = norm(q) if abs(h[k + 1]) < 1e-12: return h, None q *= 1. / h[k + 1].real return h, q def givens_rotation(v1, v2): if v2 == 0: return 1, 0 elif v1 == 0: return 0, v2 / abs(v2) else: t = sqrt((v1.conjugate() * v1 + v2.conjugate() * v2).real) cs = abs(v1) / t sn = v1 / abs(v1) * v2.conjugate() / t return cs, sn def apply_givens_rotation(h, cs, sn, k): for i in range(k): temp = cs[i] * h[i] + sn[i] * h[i + 1] h[i + 1] = -sn[i].conjugate() * h[i] + cs[i].conjugate() * h[i + 1] h[i] = temp cs[k], sn[k] = givens_rotation(h[k], h[k + 1]) h[k] = cs[k] * h[k] + sn[k] * h[k + 1] h[k + 1] = 0 def calcSolution(k): mat = Matrix(k + 1, k + 1, is_complex) for i in range(k + 1): mat[:, i] = H[i][:k + 1] rs = Vector(k + 1, is_complex) rs[:] = beta[:k + 1] y = mat.I * rs if xstart: x.data = xstart for i in range(k + 1): x.data += y[i] * Q[i] for k in range(m): startiteration += 1 h, q = arnoldi(A, Q, k) H.append(h) if q is None: break Q.append(q) apply_givens_rotation(h, cs, sn, k) beta[k + 1] = -sn[k].conjugate() * beta[k] beta[k] = cs[k] * beta[k] error = abs(beta[k + 1]) if printrates: print("Step", startiteration, ", error = ", error) if callback: calcSolution(k) callback(x) if error < tol: break if restart and k + 1 == restart and not (restart == maxsteps): calcSolution(k) del Q return GMRes(A, b, freedofs=freedofs, pre=pre, x=x, maxsteps=maxsteps - restart, callback=callback, tol=tol, innerproduct=innerproduct, restart=restart, startiteration=startiteration, printrates=printrates) calcSolution(k) return x
def BVP(bf, lf, gf, \ pre=None, pre_flags={}, \ solver=None, solver_flags={}, maxsteps=200, tol=1e-8, print=True, inverse="umfpack", needsassembling=True): """ Solve a linear boundary value problem A(u,v) = f(v) Parameters ---------- bf : BilinearForm provides the matrix. lf : LinearForm provides the right hand side. gf : GridFunction provides the solution vector pre : Basematrix or class or string = None used if an iterative solver is used can be one of * a preconditioner object * a preconditioner class * a preconditioner class name pre_flags : dictionary = { } flags used to create preconditioner """ from ngsolve import Projector, Preconditioner from ngsolve.krylovspace import CG if isinstance(pre, type): pre = pre(bf, **pre_flags) if not needsassembling: pre.Update() if isinstance(pre, str): pre = Preconditioner(bf, pre, **pre_flags) if not needsassembling: pre.Update() if needsassembling: bf.Assemble() lf.Assemble() r = lf.vec.CreateVector() r.data = lf.vec if bf.condense: r.data += bf.harmonic_extension_trans * r # zero local dofs innerbits = gf.space.FreeDofs(False) & ~gf.space.FreeDofs(True) Projector(innerbits, False).Project(gf.vec) if solver: inv = solver(mat=bf.mat, pre=pre, **solver_flags) r.data -= bf.mat * gf.vec gf.vec.data += inv * r elif pre: CG(mat=bf.mat, rhs=r, pre=pre, sol=gf.vec, tol=tol, maxsteps=maxsteps, initialize=False, printrates=print) else: inv = bf.mat.Inverse(gf.space.FreeDofs(bf.condense), inverse=inverse) r.data -= bf.mat * gf.vec gf.vec.data += inv * r if bf.condense: gf.vec.data += bf.harmonic_extension * gf.vec gf.vec.data += bf.inner_solve * r