利用紧凑型径向基函数神经网络对采样不足的干涉图进行相位解包(并行版)
date
Apr 22, 2025
slug
brffunwrapp
status
Published
tags
神经网络
summary
type
Post
这段代码只是导入了一系列用于科学计算和数据处理的Python库和模块,并没有执行具体的功能。根据导入的库,这可能是一个科学计算或数据分析的项目准备工作。具体导入的库包括:
- 科学计算基础库:
numpy (np)
- 用于数值计算的基础库scipy
- 科学计算生态系统,包含多种高级功能
- 性能优化工具:
numba
- 用于加速Python代码的JIT编译器jit
装饰器 - 用于加速函数执行速度
- 数学处理工具:
- 数学函数 (
sin
,cos
,sqrt
) - 线性代数函数 (
norm
,solve
,scipy.linalg
)
- 信号处理功能:
- 傅里叶变换 (
fft
,dct
,idct
) - 滤波器 (
butter
,filtfilt
) - 插值函数 (
interpolate
)
- 图划分库:
pymetis
- 常用于网格划分和科学计算中的图分割
- 实用工具:
multiprocessing.cpu_count
- 获取CPU核心数pickle
- 对象序列化time
- 时间测量- 以及IPython和系统相关的功能
这段代码设置了一些环境变量和NumPy打印选项,看起来是为后续的大规模科学计算或数据处理任务做准备。
这段代码是设置Matplotlib数据可视化库的显示参数,主要目的是配置图表的样式、字体和大小,让生成的图形具有一致的美观风格。具体功能包括:
- 导入可视化相关库:
- 导入matplotlib及其子模块,用于创建图表和处理图像
- 设置颜色映射:
- 将默认颜色映射设为'nipy_spectral'(一种适合科学数据的彩色映射方案)
- 配置Jupyter环境:
%matplotlib inline
命令使图表直接显示在Jupyter notebook中
- 字体设置:
- 设置使用"Latin Modern Roman"作为无衬线字体
- 指定默认使用无衬线字体系列
- 为数学公式文本设置"stix"字体集
- 缩放和字体大小:
- 定义可调整的缩放因子(
scale=1
) - 设置三种标准字体大小(小:25,中:30,大:35)
- 为图表的不同元素(轴标签、刻度、图例、标题等)分配相应的字体大小
此代码通常在数据分析或科学计算项目中使用,目的是确保生成的图表在论文、演示或报告中具有专业的外观和一致的样式。头部注释显示这是在一个名为"ttxsg"的用户的notebook中运行的。
加密内容
这段代码定义了一个名为
titled_plot_2D
的函数,是用于在现有图表对象中创建二维热图的工具。与之前的plot_2D
函数相比,这个函数设计为在多子图布局中使用。具体功能包括:- 在指定的轴上绘制热图:
- 接受预先创建的轴对象(
ax
)作为参数,而不是创建新的图表 - 调用
ax.label_outer()
确保在多子图排列中只显示最外层的标签
- 数据可视化控制:
- 将二维数据数组(
phi
)转置并翻转后显示为热图 - 通过
box
参数设置数据显示范围 - 支持双线性插值平滑显示
- 颜色映射设置:
- 使用指定的颜色映射方案(默认为"nipy_spectral")
- 可设置最小值截断(
min_cutoff
),低于该值的数据显示为白色 - 增加了最大值截断参数(
max_cutoff
),用于控制上限显示
- 额外图形元素:
- 添加颜色条及其标签
- 可选的等高线显示功能
需要注意的是,代码中存在一个问题:它引用了未定义的变量
fig
来创建颜色条。在实际使用时,这个fig
应该是包含ax
的图表对象,应该通过参数传入或从ax
获取。该函数与之前的plot_2D
相比,不创建新图表、不设置标题、不保存文件、不显示图表,专为多子图布局设计。加密内容
这段代码定义了三个使用Numba加速的函数,用于计算Wendland径向基函数(Radial Basis Function, RBF)及其导数。具体功能:
rbf(r)
:- 实现了Wendland ψ₃,₂ 径向基函数
- 这是一个具有紧支撑性的高阶多项式径向基函数
- 形式为多项式:1 + r²(-9.33...) + r⁴(70) + ...
drbf(r)
:- 计算Wendland ψ₃,₂ 函数的一阶导数
- 用于需要梯度信息的计算
d2rbf(r)
:- 计算Wendland ψ₃,₂ 函数的二阶导数
- 用于需要二阶微分信息的计算
代码中的注释提到这些函数没有对输入参数r进行范围检查(应满足0<r<1)。使用
@jit(nopython=True)
装饰器将这些函数编译为机器码,显著提高计算性能,这在需要大量计算径向基函数值的科学计算中非常重要。Wendland径向基函数常用于数值模拟、散点数据插值、网格自由方法和偏微分方程求解等领域,特别是在粒子方法(如SPH - 平滑粒子流体动力学)中被广泛应用。
径向基函数是一类特殊的数学函数,其值仅依赖于输入点与原点之间的距离(或与某个中心点的距离)。最简单的形式可以表示为:
φ(x) = φ(‖x‖)
其中‖x‖表示点x到原点的距离。
高阶多项式径向基函数特指那些由高次多项式构成的径向基函数,比如您代码中的Wendland ψ₃,₂函数。
- 径向对称性:函数值只取决于距离,不取决于方向
- 平滑性:高阶多项式RBF有较高阶的连续导数,保证了解的平滑性
- 紧支撑性:Wendland系列函数在一定距离以外严格为零,提高计算效率
- 多项式形式:使用多项式组合构建,便于计算微分和积分
代码中的Wendland ψ₃,₂函数:
- 具有紧支撑性,意味着在r>1时函数值为0
- 在支撑域内是C⁴连续的(有连续的四阶导数)
- 由不同次幂的多项式项组合构成
径向基函数(Radical Basis Function,RBF)方法是Powell在1985年提出的。所谓径向基函数,其实就是某种沿径向对称的标量函数。通常定义为空间中任一点x到某一中心c之间欧氏距离的单调函数,可记作k(||x-c||),其作用往往是局部的,即当x远离c时函数取值很小。 例如高斯径向基函数:
当年径向基函数的诞生主要是为了解决多变量插值的问题。可以看下面的图。具体的话是先在每个样本上面放一个基函数,图中每个蓝色的点是一个样本,然后中间那个图中绿色虚线对应的,就表示的是每个训练样本对应一个高斯函数(高斯函数中心就是样本点)。然后假设真实的拟合这些训练数据的曲线是蓝色的那根(最右边的图),如果我们有一个新的数据x1,我们想知道它对应的f(x1)是多少,也就是a点的纵坐标是多少。
那么由图可以看到,a点的纵坐标等于b点的纵坐标加上c点的纵坐标。而b的纵坐标是第一个样本点的高斯函数的值乘以一个大点权值得到的,c的纵坐标是第二个样本点的高斯函数的值乘以另一个小点的权值得到。而其他样本点的权值全是0,因为我们要插值的点x1在第一和第二个样本点之间,远离其他的样本点,那么插值影响最大的就是离得近的点,离的远的就没什么贡献了。所以x1点的函数值由附近的b和c两个点就可以确定了。拓展到任意的新的x,这些红色的高斯函数乘以一个权值后再在对应的x地方加起来,就可以完美的拟合真实的函数曲线了。

逐行解释:
@jit(nopython=True)
- 这是Numba的装饰器,将Python函数编译成高效的机器码,大幅提高执行速度。"nopython=True"表示使用纯编译模式,不回退到Python解释器。
def W(x):
- 定义一个名为W的函数,接收一个参数x。
while (x>np.pi):
- 当x的值大于π (3.14159...)时,执行循环体。
x-=2.*np.pi
- 将x的值减少2π。这是将过大的角度值向下调整。
while (x<=-np.pi):
- 当x的值小于或等于-π时,执行循环体。
x+=2.*np.pi
- 将x的值增加2π。这是将过小的角度值向上调整。
return x
- 返回调整后的x值。
功能总结:这个函数执行"角度归一化"操作,将任意角度值调整到(-π,π]区间内。这在处理角度、相位或周期性数据时非常有用,确保角度值始终保持在标准范围内。
逐行解释:
@jit(nopython=True)
- Numba装饰器,将函数编译成高效的机器码,大幅提高执行速度。"nopython=True"表示使用纯编译模式,不使用Python解释器。
def WF(x):
- 定义一个名为WF的函数,接收一个参数x(这里x应该是一个数组或列表)。
for i in range(len(x)):
- 创建一个循环,遍历x中的每个元素的索引,从0到len(x)-1。
x[i]=W(x[i])
- 对x中的每个元素应用之前定义的W函数(角度归一化函数),将结果保存回原数组的相同位置。
return x
- 返回修改后的数组x。
功能总结:这个函数是W函数的批量处理版本,用于对整个数组中的所有元素进行角度归一化,将每个值调整到(-π,π]区间内。它直接修改输入数组并返回该数组。
逐行解释:
@jit(nopython=True)
- Numba装饰器,将函数编译为机器码以提高执行速度。
def DTR(x):
- 定义名为DTR的函数,接收一个数组参数x。
n=len(x)
- 获取输入数组x的长度,存储在变量n中。
y=np.zeros(n)
- 创建一个与x等长的全零数组y,用于存储结果。
for i in range(0,n-1):
- 遍历从0到n-2的所有索引。
y[i]=x[i+1]-x[i]
- 计算数组x中相邻元素的差值,这实际上是计算离散导数或差分。
y[n-1]=y[n-3]+(y[n-2]-y[n-3])/((n-2)-(n-3))*((n-1)-(n-3))
- 为数组最后一个元素使用线性外推法估算值。简化后等价于y[n-1]=y[n-3]+2*(y[n-2]-y[n-3])
,即根据前两个差值的趋势预测最后一个差值。
return y
- 返回计算得到的差分数组。
功能总结:这个函数计算输入数组的离散导数(或一阶差分),即相邻元素之间的差值。对于最后一个元素,由于没有下一个值可计算差分,函数使用线性外推法估算。这种计算在信号处理、数值分析和时间序列分析中很常见。
逐行解释:
@jit(nopython=True)
- Numba装饰器,将函数编译为机器码以提高执行速度。
def DTC(x):
- 定义名为DTC的函数,接收一个数组参数x。
n=len(x)
- 获取输入数组x的长度,存储在变量n中。
y=np.zeros(n)
- 创建一个与x等长的全零数组y,用于存储结果。
for i in range(1,n-1):
- 遍历从1到n-2的所有索引(跳过首尾两个元素)。
y[i]=(x[i+1]-x[i-1])*.5
- 使用中心差分公式计算导数近似值:(f(x+h)-f(x-h))/(2h),其中h=1,即相当于(x[i+1]-x[i-1])/2。
y[0]=y[2]+(y[1]-y[2])/((1)-(2))*((0)-(2))
- 使用线性外推法估算首个元素的导数。化简后等价于y[0]=2*y[1]-y[2]
。
y[n-1]=y[n-3]+(y[n-2]-y[n-3])/((n-2)-(n-3))*((n-1)-(n-3))
- 使用线性外推法估算最后一个元素的导数。化简后等价于y[n-1]=2*y[n-2]-y[n-3]
。
return y
- 返回计算得到的导数近似值数组。
功能总结:该函数计算输入数组的数值导数,使用中心差分法(比之前的DTR函数更精确)。对于内部点,采用标准中心差分公式;对于边界点,使用基于内部点导数值的线性外推法估算。这种方法在数值分析、信号处理和科学计算中广泛用于近似计算函数导数。
逐行解释:
@jit(nopython=True)
- Numba装饰器,将函数编译为机器码以提高执行速度。
def D2T(x):
- 定义名为D2T的函数,接收一个数组参数x。
n=len(x)
- 获取输入数组x的长度,存储在变量n中。
y=np.zeros(n)
- 创建一个与x等长的全零数组y,用于存储结果。
for i in range(1,n-1):
- 遍历从1到n-2的所有索引(跳过首尾两个元素)。
y[i]=(x[i+1]-2*x[i]+x[i-1])
- 使用二阶中心差分公式计算二阶导数近似值:f(x+h)-2f(x)+f(x-h),其中h=1。这是离散版本的二阶导数公式。
y[0]=y[2]+(y[1]-y[2])/((1)-(2))*((0)-(2))
- 使用线性外推法估算首个元素的二阶导数。化简后等价于y[0]=2*y[1]-y[2]
。
y[n-1]=y[n-3]+(y[n-2]-y[n-3])/((n-2)-(n-3))*((n-1)-(n-3))
- 使用线性外推法估算最后一个元素的二阶导数。化简后等价于y[n-1]=2*y[n-2]-y[n-3]
。
return y
- 返回计算得到的二阶导数近似值数组。
功能总结:该函数计算输入数组的二阶数值导数近似值。对于内部点,使用标准的二阶中心差分公式;对于边界点,采用基于内部点的线性外推法估算。这种计算在数值分析中常用于求解微分方程、信号处理中的加速度计算、以及曲线的曲率分析等场景。
重要函数1 unpack_data
逐行解释:
def unpack_data(phi, mask):
- 定义函数,接收相位场数组phi
和掩码数组mask
作为参数。
2-3.
ni=len(phi[:,0])
和 nj=len(phi[0,:])
- 获取输入数组的维度,ni
是行数,nj
是列数。4-8. 计算掩码中值为1的点的总数:
9-10. 创建输出数组:
data=np.zeros(Nt*N_EQ)
- 存储每个选中点的12个特征值(N_EQ=12)
grid=np.zeros(Nt*2)
- 存储每个选中点的坐标(i,j)
k=0
- 初始化计数器,用于跟踪当前正在处理的点。
12-14. 创建x方向导数的存储数组:
15-18. 沿x方向计算各列的导数:
19-21. 创建y方向导数的存储数组:
22-25. 沿y方向计算各行的导数:
26-42. 遍历所有被掩码选中的点,提取并存储数据:
return data,grid
- 返回处理后的数据和对应的网格点坐标。
功能总结:该函数处理一个相位场(phi),计算其各方向的导数,然后提取被掩码选中点的特征。这些特征包括原始相位值和各种导数的三角函数表示,主要用于处理周期性变量(如角度)的数值分析。特别是通过使用sin和cos对角度进行编码,避免了角度在±π处的不连续性问题。
N_DOF=5
"DOF"代表"自由度"(),值为5表示系统中每个点有5个独立变量或参数。在相位场分析中,这可能表示:
- 用于描述相位角度的主要参数数量
- 或模型中每个数据点需要处理的变量数量
N_EQ=12
"EQ"代表"方程"()或"量"(),值为12正好对应
unpack_data
函数中为每个点存储的12个特征值:- 原始相位的余弦和正弦 (2个)
- x方向左侧差分的余弦和正弦 (2个)
- y方向左侧差分的余弦和正弦 (2个)
- x方向右侧差分的余弦和正弦 (2个)
- y方向右侧差分的余弦和正弦 (2个)
- x和y方向的二阶导数 (2个)
这些值用于相位场的数值处理和分析。
tiny=1e-15
这是一个极小的数值常量(10^-15),通常用于:
- 避免除零错误
- 作为比较阈值,判断数值是否"足够接近零"
- 防止数值不稳定性问题
在数值计算和科学编程中,设置这样的小常数是常见做法,特别是在处理浮点数运算时。
重要函数2 repack_data
@jit(nopython=True,cache=False)
- Numba装饰器,启用编译加速但禁用缓存。
def repack_data(w,grid,domain,ghost,ghost_domain,phi_in,phi_sync,sigma=5):
- 定义函数,接收多个参数:w
: RBF插值的权重系数grid
: 网格点坐标domain
: 计算域信息ghost
: 边界/虚拟点信息ghost_domain
: 虚拟点的域信息phi_in
: 输入相位场phi_sync
: 同步相位值sigma
: 缩放参数,默认值5
3-5. 准备合并域和虚拟点:
6-7. 初始化虚拟域标记,-1表示非虚拟点:
8-14. 循环处理所有其余域,合并域和虚拟点:
mget
函数看起来是从域结构中提取特定索引的点。这段代码将所有域的内部点和边界点合并到一个列表中,同时标记哪些是边界点
15-16. 计算总点数并创建域号码数组:
17-23. 为每个点分配所属域编号:
24-30. 准备输出数组:
sigma2=sigma**2
- 计算sigma的平方,优化后续计算。
32-60. 主循环,使用RBF插值重构相位场和导数:
return phi,d_phi_dx,d_phi_dy,d2_phi_dx2,d2_phi_dy2
- 返回重构后的相位场及其各阶导数。
功能总结:此函数使用径向基函数(RBF)进行插值,重建二维相位场及其导数。它处理多个计算域,结合基础相位值和RBF贡献,计算出每个网格点的精确相位值和各方向的一阶、二阶导数。这种方法在流体动力学、图像处理和非结构化网格上的数值模拟中常用,以实现高精度的函数重构和导数计算。这是科学计算中RBF方法的典型应用。
这个函数实现了一个基于径向基函数(RBF)的相位场重构,我会更详细地解释其工作原理和主要步骤。
repack_data
函数将离散采样点的数据,通过RBF插值技术重建成连续的相位场及其导数。这在处理复杂几何、非结构化网格或多区域问题时非常有用。代码处理的是多个计算域(domains):
domain[0]
(即Nk
)表示总共有多少个独立的计算域
- 每个域可能有自己的计算点和边界(ghost)点
径向基函数插值是一种通过加权叠加来重建函数的方法:
- 对于每个点,使用形如
aj + bj*(x-xj) + cj*(y-yj)
的多项式和RBF组合
rbf(r)
,drbf(r)
,d2rbf(r)
分别是基函数及其一阶、二阶导数
1. 准备域和点集合(第3-14行)
mget
函数看起来是从域结构中提取特定索引的点。这段代码将所有域的内部点和边界点合并到一个列表中,同时标记哪些是边界点。2. 为每个点分配所属域编号(第17-23行)
这段代码确保每个点都知道自己属于哪个计算域,这对多域问题的处理很重要。
3. 初始化输出数组(第24-30行)
创建用于存储重建结果的数组:原始相位场和各方向的导数。
4. RBF插值核心实现(第32-60行)
这是最复杂的部分,执行了实际的RBF插值:
- 对每个内部点,从同步值开始
- 遍历同域内所有点,如果在支撑半径内,计算其RBF贡献
- 使用链式法则计算一阶和二阶导数
- 公式中rbf/drbf/d2rbf是径向基函数及其导数
- N_DOF=5的含义:现在可以看出,每个点存储了5个参数:
w[j*N_DOF+0]
(aj): 常数项系数w[j*N_DOF+1]
(bj): x线性项系数w[j*N_DOF+2]
(cj): y线性项系数w[j*N_DOF+3]
(sxj): x方向缩放因子w[j*N_DOF+4]
(syj): y方向缩放因子
- RBF形式:代码使用的是紧支撑型RBF,只在距离小于1的区域内有贡献(rj2<=1)
- 域分解思想:只考虑同域内点的影响,这提高了并行计算效率
- 导数计算:直接通过RBF的解析导数获得,而不是使用有限差分,精度更高
这个函数是一个非常专业的数值计算工具,用于在复杂几何或多域问题中重建连续场及其导数。
重要函数3. 函数domain_split的详细解释
这个函数实现了网格点的域分解(Domain Decomposition)算法,这在并行计算和大规模数值模拟中非常重要。我会逐行解释其工作原理。
函数声明和初始化
- 函数接收四个参数:
grid
: 网格点坐标数组,格式为[x1,y1,x2,y2,...]n_domain
: 要划分的域数量overlap
: 域之间的重叠层数,默认为1keep
: 控制ghost点保留策略,默认为0
Nt=int(len(grid)/2)
: 计算网格点总数(因为每个点占用两个元素:x和y)
adj=np.empty(Nt,object)
: 创建邻接列表数组,存储每个点的相邻点
sigma=2**.5
: 设置邻居判定阈值为√2(对应于网格中的对角线距离)
构建邻接关系
这段代码为每个点建立邻接列表:
- 遍历所有点i,获取其坐标(x,y)
- 对于每个可能的邻点j,计算归一化距离
- 如果距离平方≤1(即实际距离≤sigma),则将j添加到i的邻接列表中
使用METIS进行图分割
- 调用pymetis库的图分割算法,将点分配到n_domain个域
•
n_domain
: 想要分割的域数量(比如3或4) •adjacency=adj
: 图的邻接列表,每个元素是一个点的所有相邻点列表
membership
数组记录每个点属于哪个域 一个数组,表示每个点被分配到哪个域(取值0到n_domain-1) •n_cuts
: 被切割的边的数量(跨域边的总数)
- 创建domain数组,存储每个域包含的点索引
•
domain
: 长度为n_domain的数组,其中domain[k]
包含所有属于第k个域的点的索引
初始化ghost点数据结构
这些数组用于存储每个域的ghost点(与其他域重叠的点)及其所属的原始域。
1. Ghost点(幽灵点)
Ghost点是域分解中关键的概念:
- 它们是位于域边界处的重叠区域中的点
- 这些点实际上属于其他域,但被复制到当前域用于计算
- 它们确保各个子域之间能够正确地交换信息
2. 代码中的变量含义
- ghost[k]:存储了域k需要从其他域"借用"的点的索引
- ghost_domain[k]:记录了
ghost[k]
中每个点实际所属的原始域编号
- ghost1和ghost1_domain:看起来可能是为了处理多层ghost点而设置的辅助数组,但在提供的代码中似乎没有被实际使用
计算ghost点(如果有重叠)
这个嵌套循环构建ghost点层:
- 根据overlap参数调整sigma值
- 对于每个域k中的每个点,检查其他域kk中的点
- 如果点距离在阈值范围内,将其添加为域k的ghost点
- 同时记录这些ghost点原本属于哪个域
去除重复的ghost点
移除重复的ghost点,同时保持ghost_domain中对应关系。
ghost点筛选策略
这段代码根据keep参数控制ghost点的保留策略:
- 如果keep=1:仅保留每个源域的一个代表性ghost点
- 如果keep>1:对每个源域进行子域分割,为每个子域选择代表点
数据格式整理和返回
最后,使用unravel函数(可能是将嵌套数组转换为特定格式)处理结果,然后返回域分解的三个主要结果:
- domain:各个计算域包含的点
- ghost:各个计算域的ghost点(重叠区域)
- ghost_domain:ghost点所属的原始域编号
总结:这个函数实现了一个高效的网格点域分解算法,适用于并行计算环境,通过控制域间重叠区域,可以有效提高并行数值计算的效率和精度。
1. Ghost点(幽灵点)
Ghost点是域分解中关键的概念:
- 它们是位于域边界处的重叠区域中的点
- 这些点实际上属于其他域,但被复制到当前域用于计算
- 它们确保各个子域之间能够正确地交换信息
2. 代码中的变量含义
- ghost[k]:存储了域k需要从其他域"借用"的点的索引
- ghost_domain[k]:记录了
ghost[k]
中每个点实际所属的原始域编号
- ghost1和ghost1_domain:看起来可能是为了处理多层ghost点而设置的辅助数组,但在提供的代码中似乎没有被实际使用
图形化示例
假设我们有一个网格被划分为2个域:
每个域的ghost点表示为:
其中"g"表示ghost点 - 域0需要域1边界上的点,域1需要域0边界上的点。
Ghost点计算代码详细解析
这段代码是计算域分解中ghost点的核心实现。我会从最基本概念到实现细节进行逐行讲解。
基本思路概述
这段代码的目的是:对于每个域,找出与其他域物理位置临近的点,作为该域的ghost点,以便在并行计算中处理域边界处的连续性。
- 功能: 检查是否需要创建ghost点层,并设置搜索半径
- 详细说明:
- 只有当
overlap>0
时才计算ghost点 sigma=2**.5*overlap
设置搜索半径,其中2**.5
是√2,大约1.414overlap
参数控制ghost点层数,值越大,重叠区域越宽
- 功能: 为每个域初始化ghost点存储数组
- 详细说明:
ghost[k]
将存储域k的所有ghost点索引ghost_domain[k]
将存储这些ghost点原本所属的域编号
- 功能: 遍历域k中的每个点,获取其坐标
- 详细说明:
domain[k][l]
是域k中第l个点的索引grid[2*int(...)]
获取该点的x坐标grid[2*int(...)+1]
获取该点的y坐标
- 功能: 遍历除当前域k以外的所有其他域
- 详细说明:
- 对每个不同的域
kk
,我们将检查它的点是否应该成为域k的ghost点
- 功能: 遍历域kk中的每个点,获取其坐标
- 详细说明:
domain[kk][ll]
是域kk中第ll个点的索引grid[2*int(...)]
获取该点的x坐标grid[2*int(...)+1]
获取该点的y坐标
- 功能: 计算距离缩放因子
- 详细说明:
- 这些是用于归一化距离计算的缩放参数
1./sigma
是搜索半径的倒数,使得标准化距离为1对应实际距离为sigma
- 功能: 计算两点间的归一化距离平方
- 详细说明:
- 这是点(x,y)和点(xj,yj)之间的欧几里得距离平方
- 乘以缩放因子后,相当于实际距离除以sigma的平方
- 使用缩放的目的是统一判定标准:当rj2<=1时,实际距离<=sigma
- 功能: 如果点足够近,将其添加为ghost点
- 详细说明:
- 当归一化距离平方<=1时,说明实际距离<=sigma
- 将域kk中的点
domain[kk][ll]
添加到域k的ghost点列表 - 同时记录这个ghost点原本属于域kk
工作过程示意图
假设我们有两个相邻的域(域0和域1),overlap=1:
算法将逐个检查域0中的每个点,判断是否有域1中的点在sigma距离内。同样地,也会检查域1中的每个点是否有域0中的点在sigma距离内。
最终的ghost点可能如下:
其中▣表示ghost点(来自相邻域的点)。
这种ghost点的计算方法确保了域之间有足够的信息交换,保证了数值计算的连续性和准确性。
这三个函数(
unravel
, get
, mget
)共同构成了一个系统,用于处理特殊格式的嵌套数组。让我逐一解释它们的功能。- 1. unravel函数 - 数组序列化
功能:将嵌套数组转化为扁平化的序列化格式
详细解释:
Ni=len(arr)
- 获取输入数组的长度(有多少个子数组)
uarr=np.array(Ni)
- 创建输出数组,首元素是子数组的数量
for i in range(Ni): uarr=np.append(uarr,len(arr[i]))
- 添加每个子数组的长度
for i in range(Ni): uarr=np.append(uarr,arr[i][:])
- 添加所有子数组的实际内容
return uarr
- 返回序列化后的数组
结果格式:
unravel函数详细示例 ,
我将通过一个具体示例逐步展示
unravel
函数如何工作,追踪变量变化过程。输入示例
假设我们有一个嵌套数组:
这是3个不同长度的子数组组成的列表。
执行步骤分解
步骤1:获取子数组数量
步骤2:创建输出数组,首元素是子数组的数量
注意:这里代码可能有小问题,应该是
np.array([Ni])
才能创建包含单个元素3的数组。步骤3:添加每个子数组的长度
执行过程:
- i=0:
np.append(uarr, 2)
→ uarr = [3, 2]
- i=1:
np.append(uarr, 3)
→ uarr = [3, 2, 3]
- i=2:
np.append(uarr, 1)
→ uarr = [3, 2, 3, 1]
步骤4:添加所有子数组的内容
执行过程:
- i=0:
np.append(uarr, [10, 20])
→ uarr = [3, 2, 3, 1, 10, 20]
- i=1:
np.append(uarr, [30, 40, 50])
→ uarr = [3, 2, 3, 1, 10, 20, 30, 40, 50]
- i=2:
np.append(uarr, [60])
→ uarr = [3, 2, 3, 1, 10, 20, 30, 40, 50, 60]
步骤5:返回结果
结果分析
最终序列化数组
[3, 2, 3, 1, 10, 20, 30, 40, 50, 60]
可以解读为:- 位置0:
3
- 表示有3个子数组
- 位置1-3:
2, 3, 1
- 表示每个子数组的长度
- 位置4及以后:
10, 20, 30, 40, 50, 60
- 所有子数组元素按顺序展开
使用序列化数组
通过这种特殊格式,
mget
函数可以提取出原始子数组:mget(uarr, 0)
会提取[10, 20]
mget(uarr, 1)
会提取[30, 40, 50]
mget(uarr, 2)
会提取[60]
这种序列化方式在并行计算中非常有用,因为它将不规则的嵌套结构转换为可以用简单索引访问的一维数组,同时保留了原始结构信息。
2. get函数 - 访问单个元素
功能:从序列化数组中获取第i个子数组的第j个元素
详细解释:
if (i>arr[0]): i=arr[0]
- 检查索引越界并修正
ind=arr[i+1]
- 获取第i个子数组的长度
N=1+arr[0]
- 初始化偏移量,跳过首元素和所有长度值
for k in range(i): N+=arr[k+1]
- 累加前i个子数组的长度,找到第i个子数组的起始位置
if (j>=ind): j=ind-1
- 检查j是否越界并修正
return arr[N+j]
- 返回第i个子数组中的第j个元素
- 3. mget函数 - 访问整个子数组
功能:从序列化数组中获取整个第i个子数组
详细解释:
if (i>arr[0]): i=arr[0]
- 检查索引越界并修正
N=1+arr[0]
- 初始化偏移量,跳过首元素和所有长度值
for k in range(i): N+=arr[k+1]
- 累加前i个子数组的长度,找到第i个子数组的起始位置
j=arr[i+1]
- 获取第i个子数组的长度
return arr[N:N+j]
- 返回第i个子数组的所有元素(切片)
假设我们有一个嵌套数组:
使用
unravel
后得到:解释:
- 第一个元素3:表示有3个子数组
- 接下来的3个元素[2, 3, 1]:表示每个子数组的长度
- 剩下的元素:所有子数组的内容连接在一起
mget(arr, 1)
会返回[3, 4, 5]get(arr, 1, 2)
会返回5
这种数据结构设计在域分解算法中特别有用,它允许高效存储和访问具有不同大小的多个域或子数组。
解析函数 corrected_error
这段代码定义了一个使用Numba加速的函数
corrected_error
,用于计算两组数值phi_s
和phi_d
之间的误差。这个函数看起来是为偏微分方程(PDE)的数值解法设计的,特别是用来处理域分解方法中的接口匹配问题。功能详解
- 装饰器
@jit(nopython=True,cache=False)
: - 这是Numba库的JIT(即时编译)装饰器,用于将Python函数编译成机器码以加速计算
nopython=True
强制完全编译模式,提供最大性能提升cache=False
禁用编译缓存
- 函数目的: 根据注释,这个函数处理的是"同时匹配左右导数"时产生的误差问题,通过对左右导数进行平均来校正误差。
- 基本初始化:
Nt
计算点的数量(假设每个点有N_EQ
个方程/变量)N_EQ
是一个常量,表示每个点的方程数(在代码其他部分定义)err
初始化为0,将累加所有误差项
- 误差计算循环:
关键特点:
- 前2项(索引0,1)直接计算差值的平方
- 接下来4项(索引2-5)先将索引2-5和6-9的值加起来,再与
phi_d
中对应的和比较 - 这正是注释提到的"平均左右导数"操作
- 最后2项(索引10,11)又回到直接计算差值的平方
- 返回结果:
- 返回累积误差的平方根,相当于计算L2范数误差
- 域分解方法(Domain Decomposition):在计算模拟中,当大型问题被分解为多个子问题时,子域边界处需要保证解的连续性和导数的连续性
- 界面匹配问题:不同区域之间需要匹配函数值和导数值
- 偏微分方程数值解法:特别是在使用高阶方法时,需要确保解在节点处的连续性和光滑性
这种误差计算方法通常用于优化算法中,以确保域分解方法的收敛性和精度。
RBF插值与导数计算详解
这段代码实现了基于径向基函数(RBF)的数值插值和微分计算,主要用于解决偏微分方程或散点数据拟合问题。让我详细解析其功能和流程:
核心功能
该函数
comp_data_split
实现了以下关键功能:- 函数值插值:使用RBF网络计算网格点上的函数值
- 导数计算:计算函数的一阶和二阶导数
- 雅可比矩阵构建:计算RBF参数对输出的偏导数(用于优化求解)
工作流程分析
1. 输入与初始化
grid
: 计算网格点的坐标(每点2个坐标,x和y)
data
: 目标值/训练数据
full_domain
: 需要计算的点索引集合
w
: RBF网络权重参数
bias
: 偏置值
sigma
: RBF尺度参数
2. 函数值与导数计算
此循环计算三类值:
phi
: 函数值本身(RBF插值)
d_phi_dx
,d_phi_dy
: 一阶导数
d2_phi_dx2
,d2_phi_dy2
: 二阶导数
3. 结果转换与存储
这部分代码将计算结果转换为特定格式,包括:
- 应用三角函数变换(可能用于周期边界条件)
- 将结果填入
phi_s
数组(预测值)
- 从
data
提取对应的目标值到phi_d
中
4. 雅可比矩阵计算
该部分计算函数和各阶导数对于RBF参数(a,b,c,sx,sy)的导数,用于:
- 基于梯度的参数优化
- 计算误差传播
- 线性化近似求解
技术细节
- RBF形式:每个基函数为局部二次多项式加RBF:
- 紧支撑:当
rj2<=1
时才计算贡献,提高计算效率
- 坐标缩放:通过
sxj
和syj
参数控制RBF的各向异性
LMA_2D_block函数的详细分析
这段代码实现了一个高级的Levenberg-Marquardt算法(LMA)用于多域RBF网络优化,主要用于偏微分方程求解或数据拟合。这是一个分块域分解方法的核心优化引擎。
主要功能
该函数实现了一个分阶段的多域优化过程,通过迭代调整RBF网络参数,使网络输出尽可能接近目标数据。
算法特点
- 多域块处理:处理多个子域及其ghost点
- 分阶段优化策略:从高阶导数开始,逐步包含低阶导数和函数值
- 自适应Levenberg-Marquardt:动态调整阻尼参数(λ)
- Ghost点耦合:通过ghost点实现域之间的信息交换
代码流程解析
1. 初始化阶段
- 设置三个优化阶段的参数和迭代上限
- 初始化LM算法的阻尼参数和调整因子
2. 数据结构准备
- 为每个子域创建必要的数据结构
- 合并正常域点和ghost点
3. 权重参数初始化
- 如果没有提供初始权重,随机初始化RBF参数
- 每个RBF中心有5个参数:3个权重(a,b,c)和2个尺度系数(sx,sy)
4. 多阶段优化循环
实现三阶段优化策略:
- 第一阶段:仅匹配二阶导数项(拉普拉斯算子)
- 第二阶段:匹配一阶和二阶导数
- 第三阶段:同时匹配函数值、一阶导数和二阶导数
5. LM算法核心实现
每次迭代的LM算法核心:
- 求解增广正规方程获得参数更新量
- 自适应调整阻尼参数λ:当误差降低时减小λ,误差增加时增大λ
- 动态在高斯-牛顿法和梯度下降之间平衡
6. 收敛判断
基于三个标准判断收敛:
- 误差变化小于阈值
- 已进行足够次数的迭代
- 根据阶段调整阈值严格程度