多重网格方法

多重网格方法

基本思想

一般的迭代法是在一种固定的网格上进行迭代,当网格比较细时,计算量十分大。多重网格说的是,在计算细网格上的精确解时,其初值是比它粗一些网格上的精确解构造的,因而迭代次数少。当然,求较粗的网格上的精确解,它的初值就是有更粗一些的网格上的精确解所构造的,如此往复,直到一个相当粗的网格位置。多重网格法的求解过程是一个递归的过程。

算法过程

  • 预平滑
    选定一个初值,先采用一般方法,比如说权雅克比方法迭代一两次,求出一个近似解ulu_lul
  • 粗网格校正
    1、计算剩余量rl=fl−Alulr_l = f_l - A_lu_lrl=flAlul
    2、限制剩余量rl−1=Rl,l−1rlr_{l-1}=R_{l,l-1}r_lrl1=Rl,l1rl,其中的RRR为限制算子,它实质上是一个插值算子。
    3、在粗网格上,求下列问题精确解:
    Al−1∗el−1=rl−1A_{l-1}* e_{l-1}= r_{l-1}Al1el1=rl1
    其中,Al−1A_{l-1}Al1是在粗网格上离散后得到的矩阵(如果是有限元方法,就是刚度矩阵)。如果在这个粗网格上精确解不好求,继续放粗,递归调用整个过程。
    4、将el−1e_{l-1}el1延拓到粗网格上,以求解ele_lelel=Pl−1,lel−1e_l = P_{l-1,l}e_{l-1}el=Pl1,lel1,其中PPP为延拓算子,其实它是一个插值算子。
    5、计算粗网格上的近似解,仍然用ululul表示:
    ul=ul+elu_l = u_l + e_lul=ul+el
  • 后平滑
    以算得的ulu_lul为初值,采用一般迭代法再迭代个一两次,求解近似解。

一个简单的一维多重网格算法流程如下所示,这里的一般迭代方法选用的Weighted Jacobi方法。

在这里插入图片描述

数值实验与结果

以一维举例,取Nl=2l+1−1N_l = 2^{l+1}-1Nl=2l+11,表示划分出的点数(内点),即不包括边界。划分的网格数目为2l+12^{l+1}2l+1 ,则网格大小为hl=1/2l+1h_l = 1/2^{l+1}hl=1/2l+1,取Al=hl−2∗tridiag(−1,2,1)A_l = h_l^{-2}*\text{tridiag}(-1,2,1)Al=hl2tridiag(1,2,1),这时,限制算子Rl,l−1R_{l,l-1}Rl,l1Nl−1∗NlN_{l-1}*N_lNl1Nl的矩阵,磨光算子Pl−1,lP_{l-1,l}Pl1,lNl∗Nl−1N_l*N_{l-1}NlNl1的矩阵,他们长成形如下面这个样子:
在这里插入图片描述

据此,编写的matlab代码如下,使用的是环境是Matlab 2014a(盗版)。为了方便,我这里将主函数和函数放在一块了,实际使用时需要拆开。

clc
clear
l = 5;
fl = fl_gen(l);
N = 2^(l+1) - 1;
u0 = ones(N,1);
u_true = Al_gen(l)\fl;
m = 8;%算法迭代次数
ul = u0;
error_MG = norm((u_true - ul),2)/N;
for i=1:m
    ul = MG(l,fl,ul);
    error_MG(end+1) = norm((u_true - ul),2)/N;
end;

mm = 1000;
method = 'weighted_jacobi';%Richardson weighted_jacobi Gauss_Seidel SOR
w = 1/2;
ul = u0;
error_si_sol = norm((u_true - ul),2)/N;
for i = 1:m
    for j = 1:mm
        options = struct('A',Al_gen(l),'u_old',ul,'f',fl,'method',(method),'w',w);
        ul = simple_iter_solvers(options);
    end
    error_si_sol(end+1) = norm((u_true - ul),2)/N;
end

p1 = semilogy(error_MG);
hold on;
p2 = semilogy(error_si_sol);
legend('MG','weighted\_jacobi')
set(p1,'MarkerFaceColor',[0 0 1],'MarkerEdgeColor',[0 1 0],...
    'MarkerSize',10,...
    'Marker','o',...
    'LineStyle','--',...
    'Color',[1 0 0],...
    'DisplayName','MG');
set(p2,'MarkerFaceColor',[0 1 0],'MarkerEdgeColor',[1 0 0],...
    'MarkerSize',10,...
    'Marker','o',...
    'LineStyle','--',...
    'Color',[1 0 1]);

xlabel('迭代步数');
ylabel('误差');
title('MG和一般迭代法随迭代步增加收敛性质比较');


function ul = MG(l,fl,ul)
Al = Al_gen(l);
%rl = fl-Al*ul;
%if norm(rl,2)/norm(fl,2) < 10e-6 || l<1
if l == 1
  %  ul = zeros(2^(l+1)-1,1);
   % fl = fl_gen(1);
    ul = Al\fl;
    return;
end
%% 迭代方法的选择
method = 'weighted_jacobi';%可以改成其他方法Gauss_Seidel weighted_jacobi

%% 预平滑
%for i = 1:4
ul0 = ul;
options = struct('A',Al,'u_old',ul,'f',fl,'method',method,'w',1/2);
ul = simple_iter_solvers(options);
%end
%% 限制
[Pl,Rl] = PR_gen(l);
rl_1 = Rl*(fl-Al*ul0);
%rl_1 = Rl*(fl-Al*ul);
%% 粗网格校正
el_1 = MG(l-1,rl_1,zeros(2^l-1,1));
%% 延拓
ul = ul + Pl*el_1;
%% 后平滑
%for i = 1:4
options = struct('A',Al,'u_old',ul,'f',fl,'method',method,'w',1/2);
ul = simple_iter_solvers(options);
%end
end


function u_new = simple_iter_solvers(options)
%帮助信息:
%输入参数为options = struct('A',A,'u_old',u_old,'f',f,'method','method','w',w);
if nargin < 1, help(mfilename),
    printf('输入参数错误。')
end
%options = varargin;
A = options.A;
u_old = options.u_old;
f = options.f;
method = options.method;
D = diag(diag(A));
L = tril(A,-1);
U = triu(A,1);

if strcmp(method,'Richardson')==1
    w = options.w;
    u_new = u_old + w*(f-A*u_old);
end
if strcmp(method,'weighted_jacobi')==1
    w = options.w;
    %   u_new = u_old + w*D^-1*(f-A*u_old);
    u_new = u_old + w*(D\(f-A*u_old));
    %    u_new = u_new/4;
end
if strcmp(method,'Gauss_Seidel')==1
    u_new = u_old + (D+L)\(f-A*u_old);
end
if strcmp(method,'SOR')==1
    w = options.w;
    u_new = (D+w*L)\(w*f - (w*U+(w-1)*D)*u_old);
end

end



function [P,R] = PR_gen(l)
% 我们用PR_gen来生成限制算子R和磨光算子P,输入参数l表示较细网格划分了l次
N = 2^(l+1) - 1;
%h = 1/2^(l+1);
N_ = 2^l- 1;
%h_ = 1/2^(l);
p = 1/2*[1;2;1;zeros(N-3,1)];
P = zeros(N,N_);
for i = 0:N_-1
    P(:,i+1) = P(:,i+1) + circshift(p,2*i);
end
R = 1/2*P';
end


function fl = fl_gen(l)
NN = 2^(l+1);
h = 1/NN;
x = h:h:1-h;
fl = fun(x);
end
function fx = fun(x)
fx = sin(2*pi*x);
fx = fx';
%fx = ones(length(x),1);
%fx = zeros(length(x),1);
end



function A = Al_gen(l)
%% Al生成器
N = 2^(l+1) - 1;
h = 1/2^(l+1);
A = (1/h^2)*(diag(repmat(-1,N-1,1),-1)+diag(repmat(2,N,1))+diag(repmat(-1,N-1,1),1));
end

README:程序使用方法为打开main函数,设置好参数(层数l,一般迭代方法method、迭代步数m,初值u0等),直接运行。f函数在fl_gen子函数中设置,MG所用的一般迭代方法的选择在MG子函数中设置,可供选择的方法有Richardson、weighted_jacobi、Gauss_Seidel、SOR等。

我们想看到MG方法的收敛速度远快与一般迭代法,这里设置l=5l=5l=5,MG方法一直迭代到第1层,在第一层求解精确解。一般迭代法在程序中作为参数是可选的,我这里选的weighted jacobi方法。为了比较出效果和差距,这里的一般方法,我让其迭代1000次,算作一轮。MG算法和一般方法都跌打8轮,结果如下。

在这里插入图片描述

从图中可以看出,MG方法的误差,随着迭代步数的增加,是呈现指数级别降低的(这里plot对y坐标做了log处理,用了semilogy函数画图)。也就是说,一般迭代法,想要达到MG方法相同的精度,要做大几个量级数目的迭代。

打开matlab的探查,我们来看一下时间消耗。

在这里插入图片描述

从这可以看到,一般迭代法所用的时间消耗是MG方法的20多倍,但是从前面那个图上可以看到,在这种情况下,一般方法的收敛得依然没有MG方法快。当然,这只是一个大概的估计,因为在MG中也用了一般跌打法,且MG中也用到了AlA_lAl生成函数,这是比较费时间的。由此,可以得到的一个结论是,在相同时间消耗的情况下,MG方法达到的精度是一般方法无法企及的。

其它一些东西

1、之前程序问题了,花了好长时间查错。后来发现一直用w*A\bw*(A^-1*b),这是不对的。因为w*A\b等价于(w*A)\b,求逆符号\和乘号*是同级的,是按从左往右算,一个一个括号的问题,耗费了我大量时间。

2、在求Restriction步骤时,我发现Rl,l−1(fl−Alul)R_{l,l-1}(f_l-A_lu_l)Rl,l1(flAlul)中的ulu_lul使用预平滑之前的值,即初值,结果会更精确,我并不知道为什么。

3、matlab中semilogy函数,只不过是在刻度显示上,关于指数均匀增长,而y值是没有改变的。semilogy控制的只是坐标轴的剖分,按照指数等刻度均匀划分,并没有实际对y值取log。

<think>我们正在处理关于多重网格法的教程请求,用户希望了解多重网格法在数值计算和有限元方法中的应用。根据引用内容,多重网格法是一种用于求解大型科学与工程计算问题的有效方法,特别是针对偏微分方程离散后得到的代数方程组。引用[1]提到多重网格方法包括一般多重网格方法多水平方法,有时统称为多水平方法。引用[2]提到了有限元方法中不同的计算格式,而引用[3]和[4]则指出预处理技术(如多重网格法)可以加速求解,并且多重网格法在理论和应用上都取得了重要成果。因此,我们可以构建一个关于多重网格法的教程,包括其基本思想、算法步骤、在有限元中的应用以及实际案例。注意:由于是教程,我们需要从基础开始,逐步深入,并确保数学表达式的正确格式(行内用$...$,独立公式用$$...$$)。以下是我们计划的结构:1.多重网格法简介2.多重网格法的基本思想(包括几何多重网格和代数多重网格)3.多重网格法的算法步骤(以V循环为例)4.多重网格法在有限元求解中的应用5.一个简单的数值例子(可能是一维泊松方程)6.实际应用案例和进阶方向由于引用中提到了许学军的研究成果(Mortar元多重网格法、Robin型区域分解法等),我们也可以简要提及这些高级主题,但作为教程,重点应放在基础。现在开始撰写教程内容。</think>### 多重网格法教程(数值计算与有限元方法多重网格法是一种高效求解偏微分方程离散化后大型代数方程组的迭代方法,特别适用于有限元分析等数值计算场景。以下是关键教程要点: #### 一、基本思想 1. **多尺度分解** 利用不同网格层次捕捉解的不同频率分量: - 细网格:捕获高频误差分量 $e_h^{\text{high}}$ - 粗网格:捕获低频误差分量 $e_H^{\text{low}}$ $$e_h = e_h^{\text{high}} + I_H^h e_H^{\text{low}}$$ 其中 $I_H^h$ 为插值算子[^1]。 2. **误差平滑原理** 标准迭代法(如Jacobi)在细网格上快速消除高频误差,但对低频误差效果差。多重网格将低频误差转移到粗网格处理,显著加速收敛[^3]。 #### 二、算法流程(以V循环为例) ```mermaid graph TD A[细网格层 h] --> B[预平滑:3次迭代] B --> C[计算残差:r_h = b_h - A_h u_h] C --> D[限制:r_H = R r_h] D --> E[粗网格求解:A_H e_H = r_H] E --> F[插值:e_h = I e_H] F --> G[修正:u_h ← u_h + e_h] G --> H[后平滑:2次迭代] ``` #### 三、有限元中的关键实现 1. **网格转移算子** - 限制算子 $R$:通常采用注入法或加权平均 - 插值算子 $I$:线性或双线性插值 - 满足变分条件:$I = c R^T$($c$为常数) 2. **粗网格算子构造** Galerkin投影确保算子一致性: $$A_H = R A_h I$$ 3. **自适应策略** 根据误差分布动态调整网格层次: $$ \eta_K = \| \nabla(u_h - u_H) \|_{L^2(K)} $$ #### 四、数值算例(一维泊松方程) 求解 $-u''(x)=f(x)$,取 $f(x)=\sin(2\pi x)$,边界 $u(0)=u(1)=0$: ```python import numpy as np from scipy.sparse import diags def v_cycle(A_h, b_h, levels): # 实现V循环核心代码 if levels == 0: return np.linalg.solve(A_h.todense(), b_h) # 1. 预平滑 u_h = gauss_seidel(A_h, b_h, iterations=3) # 2. 残差限制 r_h = b_h - A_h @ u_h r_H = restrict(r_h) # 降维操作 # 3. 粗网格求解 A_H = coarsen(A_h) # Galerkin投影 e_H = v_cycle(A_H, r_H, levels-1) # 4. 插值修正 u_h += interpolate(e_H) # 5. 后平滑 return gauss_seidel(A_h, b_h, u0=u_h, iterations=2) ``` *注:完整代码需实现restrict/interpolate/coarsen函数[^4]* #### 五、性能优势 - **收敛速度与网格无关**:最优复杂度 $O(N)$ - **加速比**:比传统迭代法快 $10^2 \sim 10^3$ 倍 - **内存效率**:仅需存储2-3层网格数据 #### 六、应用场景 1. 结构力学中的应力分析 2. 计算流体动力学(CFD)模拟 3. 电磁场计算(Maxwell方程) 4. 地质建模中的大规模方程组求解[^1][^4]
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆嵩

有打赏才有动力,你懂的。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值