diff --git a/lib/solvers/LowestOrderLocalMg.m b/lib/solvers/LowestOrderLocalMg.m deleted file mode 100644 index af461468a373e915b4e6311ad4c124673e485c52..0000000000000000000000000000000000000000 --- a/lib/solvers/LowestOrderLocalMg.m +++ /dev/null @@ -1,177 +0,0 @@ -% LowestOrderLocalMg (subclass of MGSolver) Solves linear equations -% stemming from lowest order AFEM computations iteratively using a -% multigrid solver where one iteration is characterized by Vcycle(0,1) -% (no pre-/one post-smoothing step). -% On every level, local Jacobi smoothing is employed on changed patches -% and the smoothed update is weighted by an optimal step size. -% -% See also [Innerberger, Miraçi, Praetorius, Streitberger; Optimal -% computational costs of AFEM with optimal local hp-robust multigrid -% solver]. - -classdef LowestOrderLocalMg < MGSolver - %% properties - properties (GetAccess=public,SetAccess=protected) - nLevels - end - - properties (Access=protected) - fes - blf - matrix - smoother - P - intergridMatrix - freeVertices - freeVerticesOld - changedPatches - otherSolver - end - - %% event data - properties (Access=private) - listenerHandle - end - - %% methods - methods (Access=public) - function obj = LowestOrderLocalMg(fes, blf, P) - arguments - fes FeSpace - blf BilinearForm - P Prolongation - end - obj = obj@MGSolver(); - - assert(fes.finiteElement.order == 1, ... - 'LowestOrderLocalMg only works for lowest order finite elements.') - assert(isempty(blf.b), ... - 'Multigrid solvers only tested for symmetric problems.') - - obj.nLevels = 0; - - mesh = fes.mesh; - obj.fes = fes; - obj.P = P; - obj.blf = blf; - - obj.listenerHandle = mesh.listener('IsAboutToRefine', @obj.getChangedPatches); - obj.otherSolver = OptimalVcycleMultigridSolver(P1JacobiSmoother(fes, blf, P)); - end - - function setupSystemMatrix(obj, A) - obj.otherSolver.setupSystemMatrix(A); - obj.nLevels = obj.nLevels + 1; - - L = obj.nLevels; - obj.freeVerticesOld = obj.freeVertices; - obj.freeVertices = getFreeDofs(obj.fes); - - obj.matrix{L} = A; - obj.smoother{L} = full(diag(obj.matrix{L})).^(-1); - - if L >= 2 - obj.intergridMatrix{L} = obj.P.matrix(obj.freeVertices, obj.freeVerticesOld); - obj.changedPatches{L} = find(obj.changedPatches{L}(obj.freeVertices)); - end - setupSystemMatrix@MGSolver(obj, A); - end - - function setupRhs(obj, b, varargin) - setupRhs@MGSolver(obj, b, varargin{:}); - obj.otherSolver.setupRhs(b, varargin{:}); - end - - function step(obj) - obj.otherSolver.applyStoppingCriterion(); - obj.otherSolver.step(); - step@MGSolver(obj); - - assert(all(abs(obj.Cresidual - obj.otherSolver.Cresidual) < 1e-8, 'all')) - assert(all(abs(obj.residualCNorm - obj.otherSolver.residualCNorm.^2) < 1e-8, 'all')) - assert(all(abs(obj.iterationCount - obj.otherSolver.iterationCount) < 1e-8, 'all')) - assert(all(abs(obj.x - obj.otherSolver.x) < 1e-8, 'all')) - end - - % Geometric MultiGrid - function [Cx, algError2] = Vcycle(obj, res) - assert(isequal(size(res, 1), size(obj.A, 1)), ... - 'Setup for multilevel iteration not complete!') - - % if there is only one coarse level: exact solve - if obj.nLevels == 1 - Cx = obj.A \ res; - algError2 = scalarProduct(Cx, obj.A*Cx); - return - end - - L = obj.nLevels; - algError2 = zeros(1, size(res, 2)); % built-in estimator of the algebraic error - - % descending cascade in P1: no smoothing - residual{L} = res; - for k = L:-1:2 - residual{k-1} = obj.intergridMatrix{k}'*residual{k}; - end - - % exact solve on coarsest level to compute accumulative lifting - % of the residual (sigma) - sigma = obj.matrix{1} \ residual{1}; - algError2 = algError2 + scalarProduct(sigma, obj.matrix{1}*sigma); - - % ascending cascade in P1 AND local smoothing - for k = 2:L - sigma = obj.intergridMatrix{k}*sigma; - uptres = residual{k} - obj.matrix{k}*sigma; %updated residual - rho = localSmoothing(obj, k, uptres); - [aeUpd, sUpd] = computeOptimalUpdate(obj.matrix{k}, uptres, rho); - sigma = sigma + sUpd; - algError2 = algError2 + aeUpd; - end - - Cx = sigma; - end - end - - methods (Access=protected) - % callback function to be executed before mesh refinement: - % -> patches change for all vertices adjacent to refined edges and - % all new vertices - function getChangedPatches(obj, mesh, bisecData) - nCOld = mesh.nCoordinates; - nCNew = mesh.nCoordinates + nnz(bisecData.bisectedEdges) + ... - bisecData.nRefinedElements'*bisecData.nInnerNodes; - bisectedEdgeNodes = unique(mesh.edges(:,bisecData.bisectedEdges)); - obj.changedPatches{obj.nLevels+1} = false(nCNew, 1); - idx = [unique(bisectedEdgeNodes); ((nCOld+1):nCNew)']; - obj.changedPatches{obj.nLevels+1}(idx) = true; - end - - function rho = localSmoothing(obj, k, res) - idx = obj.changedPatches{k}; - rho = zeros(size(res)); - rho(idx,:) = obj.smoother{k}(idx).*res(idx,:); - end - end -end - -%% auxiliary functions -% error correction with optimal stepsize -function [etaUpdate, sigmaUpdate] = computeOptimalUpdate(A, res, rho) - rhoArho = scalarProduct(rho, A*rho); - if max(abs(rho)) < eps - lambda = 1; - else - lambda = scalarProduct(res, rho) ./ rhoArho; - end - sigmaUpdate = lambda.*rho; - etaUpdate = rhoArho.*(lambda.^2); - - if any(lambda > 3) - warning('MG step-sizes no longer bound by d+1. Optimality of step size cannot be guaranteed!') - end -end - -function a = scalarProduct(x, y) - a = sum(x.*y, 1); -end diff --git a/lib/solvers/chooseIterativeSolver.m b/lib/solvers/chooseIterativeSolver.m index 02d735c3b8fb17f82f0f0c2bae9c2a5658f8383a..fa30db22d2f6701c3fc1c43c78c4b31d43c63937 100644 --- a/lib/solvers/chooseIterativeSolver.m +++ b/lib/solvers/chooseIterativeSolver.m @@ -53,13 +53,13 @@ switch class switch variant case {"", "lowOrderVcycle"} if order == 1 - solver = LowestOrderLocalMg(fes, blf, P); + solver = OptimalVcycleMultigridSolver(P1JacobiSmoother(fes, blf, P)); else solver = LocalMgLowOrderVcycle(fes, blf); end case "highOrderVcycle" if order == 1 - solver = LowestOrderLocalMg(fes, blf, P); + solver = OptimalVcycleMultigridSolver(P1JacobiSmoother(fes, blf, P)); else solver = LocalMgHighOrderVcycle(fes, blf, P); end