大概思路是对的,但是查老师可能会出现幻觉,小心食用 😃
这段代码是在初始化迭代法求解器,构建迭代矩阵和分裂矩阵。以下是详细解释:
python
if init_from_func or init_from_input:
# 1. 存储刚度矩阵
self.stiff_p = stiff_p
# 2. 矩阵分裂:将刚度矩阵分解为下三角、对角、上三角部分
l_p = np.tril(self.stiff_p, -1) # 严格下三角部分
d_p = np.diag(np.diag(self.stiff_p)) # 对角部分
u_p = np.triu(self.stiff_p, 1) # 严格上三角部分
# 3. 组合常用矩阵块
lu_p = l_p + u_p # 非对角部分(L+U)
ld_p = l_p + d_p # 下三角+对角(L+D)
# 4. 根据求解方法选择分裂方式
if self.solver_method in ['Jacobi', 'CG', 'MG-J']:
self.m_p = d_p # Jacobi方法:M = D
self.n_p = lu_p # N = L + U
elif self.solver_method in ['GS', 'MG-GS']:
self.m_p = ld_p # Gauss-Seidel方法:M = L + D
self.n_p = u_p # N = U
else:
raise ValueError('Incorrect solver method')
# 5. 计算 M⁻¹N(迭代矩阵的关键组成部分)
self.m_inv_n_p = np.linalg.solve(self.m_p, self.n_p)
# 6. 构造完整的迭代矩阵 G
self.g_mat = (np.eye(stiff_p.shape[0]) - configs.OMEGA *
np.linalg.solve(self.m_p, self.stiff_p))
🔍 关键概念说明:
1. 矩阵分裂(Matrix Splitting)
迭代法的核心思想:A = M - N
,其中:
M
是容易求逆的矩阵N
是剩余部分
2. 不同方法的分裂方式
Jacobi方法:
A = D - (L + U)
M = D, N = L + U
迭代矩阵:G = I - ω·D⁻¹·A
Gauss-Seidel方法:
A = (L + D) - U
M = L + D, N = U
迭代矩阵:G = I - (L + D)⁻¹·A
3. 变量含义:
self.m_p
: 预处理矩阵 Mself.n_p
: 剩余矩阵 Nself.m_inv_n_p
: M⁻¹N,用于迭代更新self.g_mat
: 完整的迭代矩阵 G
🎯 迭代矩阵的作用:
迭代公式:
u^{(k+1)} = G·u^{(k)} + M⁻¹·f
其中 G = I - ω·M⁻¹·A
是迭代矩阵
收敛性分析:
- 当
G
的谱半径 ρ(G) < 1 时,迭代收敛 - 特征值分析可以预测收敛速率
📊 应用场景:
- 'Jacobi': 标准Jacobi迭代
- 'CG': 可能用于预处理共轭梯度法
- 'MG-J': 多重网格中的Jacobi光滑器
- 'GS': Gauss-Seidel迭代
- 'MG-GS': 多重网格中的Gauss-Seidel光滑器
这段代码为后续的迭代求解和收敛性分析准备了必要的矩阵组件。
这段代码实现了在迭代过程中更新各种误差指标的功能。以下是详细解释:
python
def update_metrics(self, u_approx, index):
# 1. 计算残差
residual = utils.compute_residual(self.stiff_p, self.f_p, u_approx,
non_bc=self.solvers[0].non_bc)
self.residuals[index, :] = residual
# 2. 计算误差
error = u_approx - self.u_true
self.errors[index, :] = error
# 3. 计算残差范数和误差范数
self.residual_norms[index] = np.sqrt(np.mean(residual ** 2))
self.error_norms[index] = np.sqrt(np.mean(error ** 2))
# 4. 计算模态误差(将误差投影到特征模态上)
if configs.DIMENSIONS == 1:
scores = np.linalg.solve(self.eigenvectors, self.errors[index, 1:-1])
else:
scores = np.linalg.solve(self.eigenvectors,
self.errors[index, self.solvers[0].non_bc])
self.modes_errors[index, :] = \
scores[np.array(self.modes_of_interest)-1]
🔍 详细步骤说明:
1. 计算残差
python
residual = utils.compute_residual(self.stiff_p, self.f_p, u_approx, non_bc=self.solvers[0].non_bc)
- 计算当前解的残差:
r = f - A·u
non_bc
参数排除边界点(只考虑内部点)
2. 计算误差
python
error = u_approx - self.u_true
- 计算与真实解的误差:
e = u_approx - u_exact
3. 计算范数
python
self.residual_norms[index] = np.sqrt(np.mean(residual ** 2)) # L2范数
self.error_norms[index] = np.sqrt(np.mean(error ** 2)) # L2范数
- 计算残差和误差的L2范数(均方根)
4. 计算模态误差(核心部分)
python
# 1D情况:排除边界点
if configs.DIMENSIONS == 1:
scores = np.linalg.solve(self.eigenvectors, self.errors[index, 1:-1])
# 多维情况:使用non_bc掩码
else:
scores = np.linalg.solve(self.eigenvectors,
self.errors[index, self.solvers[0].non_bc])
# 提取感兴趣模态的误差
self.modes_errors[index, :] = scores[np.array(self.modes_of_interest)-1]
🎯 模态误差计算原理:
数学基础:
误差向量可以表示为特征模态的线性组合:
e = Σ c_i · φ_i
其中 c_i
是模态系数(即模态误差)
计算方法:
通过求解线性系统:
Φ · c = e
其中:
Φ
: 特征向量矩阵(列是特征模态)c
: 模态系数向量e
: 误差向量
结果:
scores[i]
就是第 i+1
个模态的误差幅度
📊 在文献中的应用:
这正好对应了Zhang等(2024)论文图2C第四列,用于显示:
- 模态1(低频):收敛最慢,DeepONet重点处理
- 模态5(中频):中等收敛速度
- 模态10(高频):收敛最快,Jacobi处理效果好
💡 索引调整:
python
scores[np.array(self.modes_of_interest)-1]
-1
是因为Python索引从0开始,而模态编号从1开始- 例如:模态1对应
scores[0]
,模态5对应scores[4]
这个函数是分析迭代法频谱特性的核心工具,帮助理解不同频率分量的收敛行为。