引言:
在科学计算和工程应用中,我们经常遇到复杂的微分方程,这些方程的解析解往往难以直接获得。因此,我们需要借助数值方法来逼近真实解。Operator Splitting Methods(算子分裂法)就是一种常用的求解偏微分方程的数值技术。这些方法的基本思想是将一个复杂的微分方程分解为几个更简单的子问题,然后分别求解这些子问题,最后将这些解组合起来得到原方程的近似解。
分数步长法( 也是一种算子分裂法)
在很多教材和文献中介绍的分数步长实际上是一种算子分裂法, 又被称为Lie-Trotter splitting。
我们针对偏微分方程 来讨论,首先我们将其重写为算子形式:
,其中 分别代表 (x) 和 (y) 方向的偏微分算子。
对于 ,Lie-Trotter Splitting 的基本步骤是:
从时间 到 ,首先使用算子 更新解:
然后使用算子 (B) 更新解:
这样,我们就通过两个简单的步骤近似求解了原方程。
从算子的角度来介绍算子分裂
我们考虑两个算子 多事线性算子, 而且都是不依赖于时间的, 那么我们可以形式的写出
的初值问题的解,
当然, 这里的 这个算子往往无法简单的计算的, 需要近似, 我们可以从一个时刻
处的 解
近似计算得到下一个时刻的数值解
这里就是用的向前欧拉法(这里的近似可以和 这个泰勒展开式联系起来), 上面式子中的
是单位映射.
再考虑空间离散, 在解决涉及两个或更多不同操作算子(如A和B)的初值问题(IVP)时,例如,一种最精确的方法是直接离散化整个表达式
,并使用高阶的时间步长方法来进行数值求解。这种方法之所以被认为是精确的,是因为它直接考虑了A和B两个操作算子的综合影响,没有忽略它们之间的任何潜在交互。
然而,尽管这种方法在理论上具有高精度,但在实际应用中并不总是可行或经济的。首先,不是所有的系统或问题都可以方便地以这种方式进行离散化。某些复杂的系统可能包含难以直接处理的操作算子,或者离散化整个表达式可能导致计算上的困难。
其次,即使可以离散化Au + Bu,使用高阶时间步长方法也可能非常耗费计算资源。高阶方法通常需要在每个时间步长进行更多的计算,这可能导致计算时间的显著增加。此外,对于大规模或高维度的系统,高阶方法的内存需求也可能非常高,使得这种方法在实际应用中变得不切实际。
因此,尽管直接离散化Au + Bu并使用高阶时间步长方法是一种理论上精确的方法,但在实际中可能由于各种限制而无法应用。在这种情况下,可能需要考虑其他替代方法,如算子分裂(Operator Splitting)技术。算子分裂方法通过将问题分解为更小的、更易于处理的子问题来避免直接处理整个复杂系统。虽然这种方法可能在精度上有所妥协,但它通常具有更低的计算成本和更高的可行性,使得它在实际应用中成为一种实用的选择。
特殊情况:A和B是线性的
当A和B都是线性算子时,我们可以定义以下解算子:
:表示当初值问题为 ,且初始条件为 时的解。
:表示当初值问题为 ,且初始条件为 时的解。
:表示当初值问题为 ,且初始条件为 时的解。
真实解:
对于初值问题 ,其真实解在
时刻可以表示为:
Lie-Trotter分裂:
Lie-Trotter分裂方法是一种近似求解技术,它将问题的解分为两步来分别处理A和B算子。在时刻的近似解为:
Strang分裂:
Strang分裂是Lie-Trotter分裂的一种改进,它在处理A和B算子之间加入了一个半步长的A算子处理步骤。在 时刻的近似解表示为:
这种方法通常比Lie-Trotter分裂具有更高的精度。
SWSS(Strang-with-Symmetric-Stages)分裂:
SWSS分裂是一种更复杂的分裂方法,它试图通过在两个算子之间实现某种对称性来提高精度。在 时刻的近似解为:
这种方法在某些情况下可能提供更稳定的数值解。
这些分裂方法都是用于近似求解包含多个算子的微分方程的有效工具,特别是在处理复杂系统时,它们可以显著降低计算复杂度并提高数值解的效率。
在数学和物理中,算子(Operator)通常被定义为一个函数,它接受一个函数作为输入并输出另一个函数。当考虑两个算子A和B是否可以交换时,我们关心的是它们的组合顺序是否影响结果。如果对于所有函数f,都有A(B(f)) = B(A(f)),那么我们称算子A和B是可交换的。
举几个简单的例子来说明算子是否可交换:
微分和乘法:对于函数f(x)和常数c,微分算子D(定义为Df = f')和乘法算子M_c(定义为M_cf = c f)在大多数情况下是不可交换的。即 (除非c 是常函数,否则 ),所以D和M_c不可交换。
我们定义 [A,B ] = AB - BA , 因此 A,B 可交换, 等价于 [A,B] = 0. 上面的例子中[D, M_c] = c'
现在,我们考虑在A和B不可交换的情况下,几种分裂方法的误差是多少。分裂方法常用于解决形如ut = Au + Bu的初值问题(IVP),其中A和B是微分算子。由于直接离散化Au + Bu并进行高阶时间步进可能不可行或成本过高,我们采用分裂方法。
Lie-trotter分裂(后面简称为 Lie splitting):在这种方法中,我们交替应用A和B的离散化。如果A和B不可交换,那么Lie分裂的局部截断误差是O(Δt^2),其中Δt是时间步长。这是因为[A, B]u(t)(其中[A, B] = AB - BA是A和B的交换子)在误差项中出现,并且当A和B不可交换时,这一项不为零。 这里的 [A,B]:表示算符A和B的交换子,即[A,B]=AB−BA。交换子[A,B]的存在表明,当子系统A和B的演化算符不交换时(即AB=BA),会产生额外的截断误差。这反映了子系统之间的相互作用对数值解精度的影响。
Strang分裂:Strang分裂是Lie分裂的一种改进,它使用了一种对称的方式来应用A和B的离散化。当A和B不可交换时,Strang分裂的局部截断误差是O(Δt^3)。这意味着与Lie分裂相比,Strang分裂在Δt较小时具有更高的精度。这里的[A,[A,B]]和[B,[A,B]]是高阶交换子,它们反映了算符A和B之间的非交换性对截断误差的影响。这些项在Δt3的系数中起主导作用,并决定了Strang splitting方法的精度。需要注意的是,上述表达式中的系数可能因具体问题和算符A、B的具体形式而有所不同,但总体来说,Strang splitting的截断误差是 ,这比Lie-Trotter splitting的要高一个阶数。
SWSS分裂(如果指的是某种特定的分裂方法):关于SWSS分裂的局部截断误差,没有足够的信息来给出精确的形式。通常,分裂方法的精度取决于其如何平衡A和B的影响以及它们之间的相互作用。如果SWSS分裂设计得当,当A和B不可交换时,其局部截断误差至少应该与Strang分裂相当或更好。
需要注意的是,这些误差估计都是局部的,即它们描述了在一个时间步长内的误差行为。全局误差(即在整个时间区间上的累积误差)会依赖于这些局部误差以及时间步长的数量。
当算符A和B可交换时,即满足AB=BA,所有的分裂方法(如Lie splitting, Strang splitting, 等)都会给出精确的结果。这是因为当算符可交换时,它们的指数算符也可以交换,即 ,从而消除了由于算符顺序不同而产生的任何误差。
然而,在一般情况下,算符A和B是不可交换的,即 。在这种情况下,不同的分裂方法会有不同的精度:
Lie splitting是全局一阶准确的。这意味着当时间步长Δt趋近于0时,近似解与真实解的误差是 。Lie splitting的简单形式导致它只能提供有限的精度。
Strang splitting 是全局二阶准确的。这意味着在时间步长Δt趋近于0时,近似解与真实解的误差是 。Strang splitting的对称性设计提高了其精度,使得它在处理不可交换算符时比Lie splitting更为精确。
全局精度指的是在整个时间积分区间上的总体精度,而不仅仅是单个时间步长的局部精度。全局精度通常比局部截断误差的阶数要低一阶,因为误差会在多个时间步长上累积。
另外,SWSS(可能是某种特定的高阶分裂方法)也是全局二阶准确的,那么它与Strang splitting在全局精度上是相当的。
总结来说,当算符可交换时,所有分裂方法都是精确的;当算符不可交换时,不同的分裂方法具有不同的全局精度,其中Lie-trotter splitting是一阶准确的,而Strang splitting和某些高阶方法(如SWSS)可能是二阶准确的。
举例:
例1: 考虑对流-扩散方程
其中u_t 是 u 对时间 t 的偏导数,u_x 和 u_{xx} 分别是 u 对空间变量 x 的一阶和二阶偏导数。c 和 d 是常数,分别代表对流系数和扩散系数。
该方程的解可以表示为 u(x,t)=h(x−ct,t),其中 h 满足 。这实际上是一个变量代换,将原对流-扩散方程转化为一个纯扩散方程。
现在,我们将方程的左边拆分为两部分:
这里,A 和 B 可以看作是对应于对流和扩散项的操作符。
接下来,我们计算 ABu 和 BAu:
由于 ,我们得出结论
,即操作符 A 和 B 是可交换的。
因此,根据前面的讨论,当操作符可交换时,所有的分裂方法都是精确的。在这种情况下,我们可以使用最简单的Lie-trotter splitting方法来求解对流-扩散方程,而不需要担心由于操作符的非交换性而引起的误差。
总结来说,由于对流和扩散操作符在这个特定的问题中是可交换的,我们可以使用Lie splitting方法来精确地求解对流-扩散方程。这大大简化了数值求解的复杂性。
例2: 给定对流-反应方程:
定义算符A和B如下:
现在我们来计算ABu和BAu:
我们可以看到 ,因此
,即算符A和B不可交换。
由于算符不可交换,我们不能简单地使用Lie-trotter splitting并保持二阶精度。在这种情况下,使用Strang splitting是更好的选择,因为它可以提供二阶全局精度,即使对于不可交换的算符也是如此。
例3: 在二维对流方程的例子中,
我们可以将其拆分为两个一维对流方程,分别对应x方向和y方向的对流:
(1)
(2)
这里定义了算符A和B,其中 和
。
现在,我们验证算符A和B是否可交换。考虑两个算符的连续作用:
由于,我们得出结论
,即算符A和B是可交换的。
这意味着,如果我们能够精确地解决每个分裂步骤(即精确地解决每个一维对流方程),那么分裂本身不会引入任何误差。换句话说,如果我们有:
的精确解,那么按照这种方式进行维度分裂将不会产生任何由分裂导致的误差。
然而,在实际应用中,我们通常无法精确解决这些方程,而是采用某种时间离散化方案(如欧拉法、龙格-库塔法等)。一旦我们引入了时间离散化,即使算符是可交换的,分裂方案的结果也可能会与直接解决原始二维方程的结果有所不同。这是因为时间离散化本身会引入误差,而这些误差在分裂方案中可能会累积或传播的方式与在整体方案中不同。
总结来说,虽然从算符可交换性的角度来看,维度分裂本身在这个例子中不会引入误差,但实际计算中由于时间离散化等数值方法的使用,结果可能会有所差异。因此,在实际应用中,我们仍然需要谨慎选择时间步长和离散化方案,以确保分裂方法的准确性和稳定性。
时间离散对是否分裂带来的影响
对于二维对流方程
我们首先写出不做分裂,直接使用向前欧拉法和空间迎风格式得到的离散化方程。
不做分裂的格式:
假设空间网格为 ,时间步长为 Δt,空间步长分别为 Δx 和 Δy,假设 a>0,b>0,则迎风格式将使用网格点
来近似
和
在网格点 的值。因此,向前欧拉法结合迎风格式的离散化方程为:
整理后得到:
使用Lie-trotter splitting的格式:
在使用Lie splitting时,我们将对流方程拆分为两个一维对流方程,并分别对每个方程应用向前欧拉法和迎风格式。
对于 x 方向的对流:
离散化后得到:
整理后:
对于 y 方向的对流:
离散化后得到:
整理后:
为了更容易与不分裂的格式进行比较,我们可以将Lie splitting中的中间步骤 用其表达式替换到最终的
的表达式中。这样,我们可以得到一个只包含
和其邻近点的表达式。
首先,我们有 的表达式:
现在,我们将这个表达式代入 的公式中:
代入 和
的表达式,我们得到:
为了和L和不分裂的格式进行比较, 我们把上面的表达式进一步的写成
在这个重新组织的表达式中,第一行是没有使用Lie splitting时的更新公式。而第二行(用红色标记)则代表了由于使用Lie splitting而引入的额外项。这些额外项反映了在x方向和y方向对流分步处理后产生的交叉效应。我们可以看到,Lie splitting的格式包含了更多的项,并且涉及到了更多的网格点,这是因为它在两个方向上分别进行了对流步骤的计算。与不分裂的格式相比, Lie splitting的格式在形式上更为复杂,因为它考虑了中间步骤的对流影响。这种复杂性可能会引入额外的数值耗散和色散,但也可能提高某些情况下的稳定性和适用性。在实际应用中,需要根据问题的具体需求和计算资源的限制来选择适合的格式。
为了比较不分裂的格式和Lie splitting格式,我们可以编写一个北太天元的脚本来模拟一个简单的二维对流问题。
%北太天元代码 比较不分裂和 Lie splitting method 求解空间二维的对流方程
% 参数设置
Nx = 51; % x方向上的网格点数
Ny = 51; % y方向上的网格点数
Lx = 10; % x方向上的长度
Ly = 10; % y方向上的长度
T = 1; % 总时间
dt = 0.001; % 时间步长
dx = Lx / (Nx - 1); % x方向上的空间步长
dy = Ly / (Ny - 1); % y方向上的空间步长
a = 1; % x方向对流速度
b = 1; % y方向对流速度
% 真实解函数
u_exact = @(x, y, t) sin(pi * (x - a * t) / Lx) + sin(pi * (y - b * t) / Ly);
% 初始化网格
[X, Y] = meshgrid(linspace(0, Lx, Nx), linspace(0, Ly, Ny));
% 初始条件
U_initial = u_exact(X, Y, 0);
% 初始化数值解矩阵
U_nosplit = U_initial;
U_liesplit = U_initial;
% 时间循环
for t = 0:dt:T
% 不分裂格式更新
U_nosplit_old = U_nosplit;
/*
for i = 2:Nx-1
for j = 2:Ny-1
U_nosplit(i,j) = U_nosplit_old(i,j) - a*dt/dx*(U_nosplit_old(i,j) - U_nosplit_old(i-1,j)) ...
- b*dt/dy*(U_nosplit_old(i,j) - U_nosplit_old(i,j-1));
end
end
*/
U_nosplit(2:Nx-1, 2:Ny-1) = U_nosplit(2:Nx-1, 2:Ny-1) ...
- a*dt/dx *(U_nosplit(2:Nx-1, 2:Ny-1) - U_nosplit(1:Nx-2, 2:Ny-1) ) ...
- b*dt/dy *(U_nosplit(2:Nx-1, 2:Ny-1) - U_nosplit(1:Nx-2, 1:Ny-2) );
% 应用边界条件(使用真实解)
U_nosplit(1,:) = u_exact(0, linspace(0,Ly, Ny), t);
U_nosplit(Nx,:) = u_exact(Lx, linspace(0,Ly, Ny), t);
U_nosplit(:,1) = u_exact(linspace(0,Lx,Nx), 0, t);
U_nosplit(:,Ny) = u_exact(linspace(0,Lx,Nx), Ly, t);
% Lie splitting格式更新
U_liesplit_old = U_liesplit;
% X方向对流
/*
for i = 2:Nx
for j = 1:Ny
U_temp(i,j) = U_liesplit_old(i,j) - a*dt/(2*dx)*(U_liesplit_old(i,j) - U_liesplit_old(i-1,j));
end
end
*/
U_temp(2:Nx,1:Ny) = U_liesplit_old(2:Nx,1:Ny) - a*dt/(2*dx)*(U_liesplit_old(2:Nx,1:Ny) - U_liesplit_old(1:Nx-1,1:Ny));
% 应用X方向边界条件
U_temp(1,:) = u_exact(0, linspace(0,Ly,Ny), t + dt/2);
U_temp(Nx,:) = u_exact(Lx, linspace(0,Ly,Ny), t + dt/2);
% Y方向对流
/*
for i = 1:Nx
for j = 2:Ny
U_liesplit(i,j) = U_temp(i,j) - b*dt/(2*dy)*(U_temp(i,j) - U_temp(i,j-1));
end
end
*/
U_temp(1:Nx,2:Ny) = U_liesplit_old(1:Nx,2:Ny) - a*dt/(2*dx)*(U_liesplit_old(1:Nx,2:Ny) - U_liesplit_old(1:Nx,1:Ny-1));
% 应用Y方向边界条件
U_liesplit(:,1) = u_exact(linspace(0,Lx,Nx), 0, t + dt);
U_liesplit(:,Ny) = u_exact(linspace(0,Lx,Nx), Ly, t + dt);
end
% 计算误差
error_nosplit = abs(U_nosplit - u_exact(X, Y, T));
error_liesplit = abs(U_liesplit - u_exact(X, Y, T));
% 绘制误差图
figure;
subplot(1,2,1); mesh(X, Y, error_nosplit); title('Error (No Splitting)');
subplot(1,2,2); mesh(X, Y, error_liesplit); title('Error (Lie Splitting)');
