对流换热仿真-主题077_数据驱动建模-数据驱动建模
主题077:数据驱动建模
目录







引言
1.1 数据驱动建模的兴起
传统的计算流体力学(CFD)方法基于第一性原理,通过求解Navier-Stokes方程等控制方程来模拟流动和传热现象。然而,随着计算需求的不断增长和数据获取能力的提升,一种全新的范式——数据驱动建模正在改变工程仿真的面貌。
什么是数据驱动建模?
数据驱动建模是指利用大量数据(实验数据、仿真数据或观测数据)来构建数学模型,而不是完全依赖物理定律的显式表达。这种方法的核心思想是:
- 从数据中学习系统的内在规律
- 将物理知识作为约束融入模型
- 实现快速预测和实时仿真
1.2 为什么需要数据驱动建模?
传统CFD面临的挑战:
- 计算成本高:复杂的CFD模拟可能需要数天甚至数周的计算时间
- 实时性不足:对于在线监测和实时控制,传统CFD无法满足时效要求
- 参数化困难:多参数优化需要大量的重复计算
- 不确定性量化:蒙特卡洛方法需要成千上万次模拟
数据驱动建模的优势:
- 计算速度快:训练好的模型可以在毫秒级完成预测
- 实时性强:适合在线监测和数字孪生应用
- 参数化容易:可以快速评估不同参数组合
- 融合多源数据:可以整合实验数据、仿真数据和观测数据
1.3 本教程内容概述
本教程将系统介绍数据驱动建模在对流换热仿真中的应用:
- 机器学习和深度学习基础
- 神经网络在CFD中的应用
- 物理信息神经网络(PINN)原理与实现
- 数据同化方法
- 代理模型与降阶建模技术
- 5个完整的Python仿真案例,包含GIF动画
机器学习基础
2.1 机器学习概述
机器学习是人工智能的一个分支,其核心是让计算机从数据中学习规律,而不是通过显式编程。根据学习方式的不同,机器学习可以分为:
监督学习:
- 从标记数据中学习输入-输出映射
- 示例:回归、分类
- CFD应用:从CFD数据学习流动特征
无监督学习:
- 从未标记数据中发现隐藏结构
- 示例:聚类、降维
- CFD应用:流动模态分析
强化学习:
- 通过与环境交互学习最优策略
- 示例:游戏AI、机器人控制
- CFD应用:流动控制优化
2.2 深度学习基础
深度学习是机器学习的一个子领域,使用多层神经网络来学习数据的层次化表示。
人工神经元:
人工神经元是神经网络的基本单元,其数学模型为:
y = f ( ∑ i = 1 n w i x i + b ) y = f\left(\sum_{i=1}^{n} w_i x_i + b\right) y=f(i=1∑nwixi+b)
其中:
- x i x_i xi:输入特征
- w i w_i wi:权重
- b b b:偏置
- f f f:激活函数
常用激活函数:
| 激活函数 | 公式 | 特点 |
|---|---|---|
| Sigmoid | σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1+e^{-x}} σ(x)=1+e−x1 | 输出0-1,适合二分类 |
| Tanh | tanh ( x ) = e x − e − x e x + e − x \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} tanh(x)=ex+e−xex−e−x | 输出-1到1,零中心化 |
| ReLU | ReLU ( x ) = max ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x) | 计算简单,缓解梯度消失 |
| Leaky ReLU | max ( α x , x ) \max(\alpha x, x) max(αx,x) | 解决ReLU死亡问题 |
| Swish | x ⋅ σ ( x ) x \cdot \sigma(x) x⋅σ(x) | 自门控,性能较好 |
前馈神经网络:
前馈神经网络(多层感知机)是最基本的神经网络结构:
z [ l ] = W [ l ] a [ l − 1 ] + b [ l ] \mathbf{z}^{[l]} = \mathbf{W}^{[l]} \mathbf{a}^{[l-1]} + \mathbf{b}^{[l]} z[l]=W[l]a[l−1]+b[l]
a [ l ] = f ( z [ l ] ) \mathbf{a}^{[l]} = f(\mathbf{z}^{[l]}) a[l]=f(z[l])
其中 l l l表示层数。
2.3 训练神经网络
损失函数:
损失函数衡量模型预测与真实值之间的差距:
均方误差(MSE):
L MSE = 1 N ∑ i = 1 N ( y i − y ^ i ) 2 \mathcal{L}_{\text{MSE}} = \frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)^2 LMSE=N1i=1∑N(yi−y^i)2
平均绝对误差(MAE):
L MAE = 1 N ∑ i = 1 N ∣ y i − y ^ i ∣ \mathcal{L}_{\text{MAE}} = \frac{1}{N} \sum_{i=1}^{N} |y_i - \hat{y}_i| LMAE=N1i=1∑N∣yi−y^i∣
交叉熵损失:
L CE = − 1 N ∑ i = 1 N ∑ c = 1 C y i , c log ( y ^ i , c ) \mathcal{L}_{\text{CE}} = -\frac{1}{N} \sum_{i=1}^{N} \sum_{c=1}^{C} y_{i,c} \log(\hat{y}_{i,c}) LCE=−N1i=1∑Nc=1∑Cyi,clog(y^i,c)
优化算法:
梯度下降:
θ t + 1 = θ t − η ∇ θ L \theta_{t+1} = \theta_t - \eta \nabla_{\theta} \mathcal{L} θt+1=θt−η∇θL
随机梯度下降(SGD):
使用小批量数据计算梯度,加快训练速度。
Adam优化器:
结合了动量和自适应学习率的优点:
m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t mt=β1mt−1+(1−β1)gt
v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 vt=β2vt−1+(1−β2)gt2
m ^ t = m t 1 − β 1 t , v ^ t = v t 1 − β 2 t \hat{m}_t = \frac{m_t}{1-\beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1-\beta_2^t} m^t=1−β1tmt,v^t=1−β2tvt
θ t + 1 = θ t − η m ^ t v ^ t + ϵ \theta_{t+1} = \theta_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} θt+1=θt−ηv^t+ϵm^t
正则化技术:
L2正则化(权重衰减):
L total = L + λ ∑ i w i 2 \mathcal{L}_{\text{total}} = \mathcal{L} + \lambda \sum_{i} w_i^2 Ltotal=L+λi∑wi2
Dropout:
训练时随机丢弃一部分神经元,防止过拟合。
早停(Early Stopping):
当验证集性能不再提升时停止训练。
2.4 深度学习框架
PyTorch:
- 动态计算图,易于调试
- 学术界广泛使用
- 适合研究和原型开发
TensorFlow:
- 静态计算图,部署方便
- 工业界应用广泛
- TensorBoard可视化强大
JAX:
- 自动微分、XLA编译
- 适合高性能科学计算
- 函数式编程风格
神经网络在CFD中的应用
3.1 流动特征提取
神经网络可以自动从流场数据中提取有意义的特征。
卷积神经网络(CNN):
CNN特别适合处理网格化的流场数据:
( I ∗ K ) ( i , j ) = ∑ m ∑ n I ( i + m , j + n ) K ( m , n ) (\mathbf{I} * \mathbf{K})(i, j) = \sum_m \sum_n \mathbf{I}(i+m, j+n) \mathbf{K}(m, n) (I∗K)(i,j)=m∑n∑I(i+m,j+n)K(m,n)
在CFD中的应用:
- 流场可视化分析
- 涡结构识别
- 流动模态分解
案例:涡识别:
使用CNN自动识别流场中的涡结构,替代传统的Q准则或λ2准则。
3.2 流场预测
全连接网络预测:
使用全连接网络建立参数到流场的映射:
u ( x ; θ ) = NN ( x ; w ) \mathbf{u}(\mathbf{x}; \theta) = \text{NN}(\mathbf{x}; \mathbf{w}) u(x;θ)=NN(x;w)
卷积神经网络预测:
使用编码器-解码器结构:
输入参数 → 编码器 → 潜在空间 → 解码器 → 流场
U-Net架构:
U-Net在流场超分辨率和修正中表现出色:
- 编码器提取多尺度特征
- 跳跃连接保留细节信息
- 解码器重建高分辨率流场
3.3 湍流建模
雷诺应力建模:
使用神经网络替代传统的湍流模型:
τ i j RANS = NN ( S i j , Ω i j , … ) \tau_{ij}^{\text{RANS}} = \text{NN}(S_{ij}, \Omega_{ij}, \dots) τijRANS=NN(Sij,Ωij,…)
亚格子应力建模(LES):
τ i j SGS = NN ( S ˉ i j , Δ , … ) \tau_{ij}^{\text{SGS}} = \text{NN}(\bar{S}_{ij}, \Delta, \dots) τijSGS=NN(Sˉij,Δ,…)
深度强化学习控制:
使用强化学习优化流动控制策略:
- 状态:流场特征
- 动作:控制输入(射流、壁面运动等)
- 奖励:减阻或增强换热
3.4 传热预测
换热系数预测:
建立几何参数和流动条件到换热系数的映射:
h = NN ( R e , P r , geometry ) h = \text{NN}(Re, Pr, \text{geometry}) h=NN(Re,Pr,geometry)
温度场预测:
预测非均匀边界条件下的温度分布:
T ( x , t ) = NN ( x , t ; boundary conditions ) T(\mathbf{x}, t) = \text{NN}(\mathbf{x}, t; \text{boundary conditions}) T(x,t)=NN(x,t;boundary conditions)
物理信息神经网络(PINN)
4.1 PINN的基本思想
物理信息神经网络(Physics-Informed Neural Networks, PINN)是将物理定律嵌入神经网络的一种新方法。
核心思想:
不仅用数据训练网络,还用物理方程约束网络:
L total = L data + λ pde L pde + λ bc L bc \mathcal{L}_{\text{total}} = \mathcal{L}_{\text{data}} + \lambda_{\text{pde}} \mathcal{L}_{\text{pde}} + \lambda_{\text{bc}} \mathcal{L}_{\text{bc}} Ltotal=Ldata+λpdeLpde+λbcLbc
其中:
- L data \mathcal{L}_{\text{data}} Ldata:数据拟合损失
- L pde \mathcal{L}_{\text{pde}} Lpde:PDE残差损失
- L bc \mathcal{L}_{\text{bc}} Lbc:边界条件损失
PINN的优势:
- 数据效率高:即使数据稀疏也能获得合理结果
- 物理一致性:解满足物理定律
- 逆问题求解:可以反推未知参数
- 无网格方法:不需要传统CFD的网格
4.2 PINN的数学框架
问题设定:
考虑一般形式的PDE:
F [ u ] ( x , t ) = 0 , x ∈ Ω , t ∈ [ 0 , T ] \mathcal{F}[u](\mathbf{x}, t) = 0, \quad \mathbf{x} \in \Omega, t \in [0, T] F[u](x,t)=0,x∈Ω,t∈[0,T]
带有边界条件:
B [ u ] ( x , t ) = 0 , x ∈ ∂ Ω \mathcal{B}[u](\mathbf{x}, t) = 0, \quad \mathbf{x} \in \partial\Omega B[u](x,t)=0,x∈∂Ω
和初始条件:
u ( x , 0 ) = u 0 ( x ) u(\mathbf{x}, 0) = u_0(\mathbf{x}) u(x,0)=u0(x)
神经网络近似:
用神经网络 u NN ( x , t ; θ ) u_{\text{NN}}(\mathbf{x}, t; \theta) uNN(x,t;θ)近似解 u ( x , t ) u(\mathbf{x}, t) u(x,t)。
自动微分:
使用自动微分计算PDE残差:
L pde = 1 N f ∑ i = 1 N f ∣ F [ u NN ] ( x f i , t f i ) ∣ 2 \mathcal{L}_{\text{pde}} = \frac{1}{N_f} \sum_{i=1}^{N_f} |\mathcal{F}[u_{\text{NN}}](\mathbf{x}_f^i, t_f^i)|^2 Lpde=Nf1i=1∑Nf∣F[uNN](xfi,tfi)∣2
PyTorch和TensorFlow都提供了自动微分功能:
# PyTorch示例
u_pred = model(x, t)
u_x = torch.autograd.grad(u_pred, x, grad_outputs=torch.ones_like(u_pred), create_graph=True)[0]
u_xx = torch.autograd.grad(u_x, x, grad_outputs=torch.ones_like(u_x), create_graph=True)[0]
4.3 对流扩散方程的PINN求解
控制方程:
∂ u ∂ t + v ⋅ ∇ u = α ∇ 2 u + f \frac{\partial u}{\partial t} + \mathbf{v} \cdot \nabla u = \alpha \nabla^2 u + f ∂t∂u+v⋅∇u=α∇2u+f
PINN实现:
- 定义神经网络 u NN ( x , y , t ) u_{\text{NN}}(x, y, t) uNN(x,y,t)
- 计算自动微分得到 u t , u x , u y , u x x , u y y u_t, u_x, u_y, u_{xx}, u_{yy} ut,ux,uy,uxx,uyy
- 计算PDE残差:
r = u t + v x u x + v y u y − α ( u x x + u y y ) − f r = u_t + v_x u_x + v_y u_y - \alpha (u_{xx} + u_{yy}) - f r=ut+vxux+vyuy−α(uxx+uyy)−f
- 损失函数:
L = 1 N u ∑ ∣ u pred − u true ∣ 2 + 1 N f ∑ ∣ r ∣ 2 \mathcal{L} = \frac{1}{N_u} \sum |u_{\text{pred}} - u_{\text{true}}|^2 + \frac{1}{N_f} \sum |r|^2 L=Nu1∑∣upred−utrue∣2+Nf1∑∣r∣2
4.4 Navier-Stokes方程的PINN求解
不可压NS方程:
连续性方程:
∇ ⋅ u = 0 \nabla \cdot \mathbf{u} = 0 ∇⋅u=0
动量方程:
∂ u ∂ t + ( u ⋅ ∇ ) u = − 1 ρ ∇ p + ν ∇ 2 u \frac{\partial \mathbf{u}}{\partial t} + (\mathbf{u} \cdot \nabla) \mathbf{u} = -\frac{1}{\rho} \nabla p + \nu \nabla^2 \mathbf{u} ∂t∂u+(u⋅∇)u=−ρ1∇p+ν∇2u
PINN实现策略:
速度-压力联合求解:
定义网络输出: [ u , v , p ] = NN ( x , y , t ) [u, v, p] = \text{NN}(x, y, t) [u,v,p]=NN(x,y,t)
损失函数包含:
- 连续性方程残差
- x方向动量方程残差
- y方向动量方程残差
- 边界条件
- 观测数据(如有)
流函数-涡量形式:
对于二维流动,使用流函数 ψ \psi ψ:
u = ∂ ψ ∂ y , v = − ∂ ψ ∂ x u = \frac{\partial \psi}{\partial y}, \quad v = -\frac{\partial \psi}{\partial x} u=∂y∂ψ,v=−∂x∂ψ
自动满足连续性方程,减少约束数量。
4.5 传热问题的PINN求解
能量方程:
∂ T ∂ t + u ⋅ ∇ T = α ∇ 2 T \frac{\partial T}{\partial t} + \mathbf{u} \cdot \nabla T = \alpha \nabla^2 T ∂t∂T+u⋅∇T=α∇2T
与流动耦合:
- 先求解NS方程得到速度场
- 用速度场作为已知输入求解能量方程
- 或者同时求解流动和温度场
Boussinesq近似:
对于自然对流,浮力项:
f = − β ( T − T 0 ) g \mathbf{f} = -\beta (T - T_0) \mathbf{g} f=−β(T−T0)g
4.6 PINN的改进与扩展
自适应权重:
不同损失项的重要性可能不同,使用自适应权重:
λ pde = ∣ ∇ θ L data ∣ ∣ ∇ θ L pde ∣ \lambda_{\text{pde}} = \frac{|\nabla_{\theta} \mathcal{L}_{\text{data}}|}{|\nabla_{\theta} \mathcal{L}_{\text{pde}}|} λpde=∣∇θLpde∣∣∇θLdata∣
域分解PINN:
将计算域分解为多个子域,每个子域用一个网络:
- 减少单个网络的复杂度
- 提高训练效率
- 适合并行计算
时空自适应采样:
在残差大的区域增加采样点:
1. 在初始点集上训练
2. 评估PDE残差
3. 在残差大的区域添加新点
4. 重新训练
守恒律约束:
除了点-wise的PDE残差,还可以强制满足积分形式的守恒律。
数据同化方法
5.1 数据同化概述
数据同化是将观测数据与数值模型结合,以获得最优估计的方法。
基本问题:
给定:
- 数值模型预测: x f \mathbf{x}^f xf(先验估计)
- 观测数据: y o \mathbf{y}^o yo
求:最优估计 x a \mathbf{x}^a xa(后验估计)
应用背景:
- 天气预报
- 海洋环流模拟
- 实时流场重建
- 数字孪生
5.2 卡尔曼滤波
卡尔曼滤波(KF):
对于线性系统:
状态方程:
x k = F k x k − 1 + w k \mathbf{x}_k = \mathbf{F}_k \mathbf{x}_{k-1} + \mathbf{w}_k xk=Fkxk−1+wk
观测方程:
y k = H k x k + v k \mathbf{y}_k = \mathbf{H}_k \mathbf{x}_k + \mathbf{v}_k yk=Hkxk+vk
其中 w k \mathbf{w}_k wk和 v k \mathbf{v}_k vk是过程噪声和观测噪声。
卡尔曼滤波步骤:
- 预测:
x k f = F k x k − 1 a \mathbf{x}_k^f = \mathbf{F}_k \mathbf{x}_{k-1}^a xkf=Fkxk−1a
P k f = F k P k − 1 a F k T + Q k \mathbf{P}_k^f = \mathbf{F}_k \mathbf{P}_{k-1}^a \mathbf{F}_k^T + \mathbf{Q}_k Pkf=FkPk−1aFkT+Qk
- 更新:
K k = P k f H k T ( H k P k f H k T + R k ) − 1 \mathbf{K}_k = \mathbf{P}_k^f \mathbf{H}_k^T (\mathbf{H}_k \mathbf{P}_k^f \mathbf{H}_k^T + \mathbf{R}_k)^{-1} Kk=PkfHkT(HkPkfHkT+Rk)−1
x k a = x k f + K k ( y k − H k x k f ) \mathbf{x}_k^a = \mathbf{x}_k^f + \mathbf{K}_k (\mathbf{y}_k - \mathbf{H}_k \mathbf{x}_k^f) xka=xkf+Kk(yk−Hkxkf)
P k a = ( I − K k H k ) P k f \mathbf{P}_k^a = (\mathbf{I} - \mathbf{K}_k \mathbf{H}_k) \mathbf{P}_k^f Pka=(I−KkHk)Pkf
5.3 扩展卡尔曼滤波(EKF)
对于非线性系统:
状态方程:
x k = f ( x k − 1 ) + w k \mathbf{x}_k = f(\mathbf{x}_{k-1}) + \mathbf{w}_k xk=f(xk−1)+wk
观测方程:
y k = h ( x k ) + v k \mathbf{y}_k = h(\mathbf{x}_k) + \mathbf{v}_k yk=h(xk)+vk
线性化:
使用雅可比矩阵:
F k = ∂ f ∂ x ∣ x k − 1 a \mathbf{F}_k = \frac{\partial f}{\partial \mathbf{x}}\bigg|_{\mathbf{x}_{k-1}^a} Fk=∂x∂f xk−1a
H k = ∂ h ∂ x ∣ x k f \mathbf{H}_k = \frac{\partial h}{\partial \mathbf{x}}\bigg|_{\mathbf{x}_k^f} Hk=∂x∂h xkf
5.4 集合卡尔曼滤波(EnKF)
基本思想:
用集合(ensemble)表示概率分布,避免计算协方差矩阵。
算法步骤:
- 初始化:生成 N e N_e Ne个初始样本
- 预报:每个样本独立积分
- 更新:使用卡尔曼增益更新每个样本
优点:
- 适用于高维系统
- 不需要切线性模型
- 可以处理非高斯分布
缺点:
- 需要大量样本
- 样本间可能存在相关性
5.5 变分同化
三维变分同化(3D-Var):
最小化代价函数:
J ( x ) = 1 2 ( x − x b ) T B − 1 ( x − x b ) + 1 2 ( y − H ( x ) ) T R − 1 ( y − H ( x ) ) J(\mathbf{x}) = \frac{1}{2}(\mathbf{x} - \mathbf{x}^b)^T \mathbf{B}^{-1} (\mathbf{x} - \mathbf{x}^b) + \frac{1}{2}(\mathbf{y} - H(\mathbf{x}))^T \mathbf{R}^{-1} (\mathbf{y} - H(\mathbf{x})) J(x)=21(x−xb)TB−1(x−xb)+21(y−H(x))TR−1(y−H(x))
四维变分同化(4D-Var):
考虑时间演化的变分同化:
J ( x 0 ) = 1 2 ( x 0 − x b ) T B − 1 ( x 0 − x b ) + 1 2 ∑ i = 0 N ( y i − H ( x i ) ) T R i − 1 ( y i − H ( x i ) ) J(\mathbf{x}_0) = \frac{1}{2}(\mathbf{x}_0 - \mathbf{x}^b)^T \mathbf{B}^{-1} (\mathbf{x}_0 - \mathbf{x}^b) + \frac{1}{2} \sum_{i=0}^{N} (\mathbf{y}_i - H(\mathbf{x}_i))^T \mathbf{R}_i^{-1} (\mathbf{y}_i - H(\mathbf{x}_i)) J(x0)=21(x0−xb)TB−1(x0−xb)+21i=0∑N(yi−H(xi))TRi−1(yi−H(xi))
其中 x i = M 0 → i ( x 0 ) \mathbf{x}_i = M_{0 \to i}(\mathbf{x}_0) xi=M0→i(x0)是模式积分结果。
5.6 粒子滤波
基本思想:
用一组带权重的粒子表示后验分布。
算法步骤:
- 采样:从先验分布采样粒子
- 预测:每个粒子按系统模型演化
- 权重更新:根据观测计算权重
- 重采样:根据权重重采样
优点:
- 可以处理任意分布
- 适用于强非线性系统
缺点:
- 维度灾难
- 需要大量粒子
代理模型与降阶建模
6.1 代理模型概述
代理模型(Surrogate Model)是复杂模型的廉价近似,用于快速预测。
应用场景:
- 设计优化(需要大量评估)
- 不确定性量化(蒙特卡洛模拟)
- 实时预测(在线应用)
常用代理模型:
| 方法 | 特点 | 适用场景 |
|---|---|---|
| 多项式响应面(RSM) | 简单直观 | 低维问题 |
| Kriging/Gaussian Process | 提供不确定性估计 | 中等维度 |
| 径向基函数(RBF) | 插值能力强 | 非线性问题 |
| 神经网络 | 表达能力强 | 高维复杂问题 |
| 支持向量机(SVM) | 泛化能力强 | 分类和回归 |
6.2 高斯过程回归
高斯过程:
高斯过程是函数的分布,完全由均值函数和协方差函数决定:
f ( x ) ∼ G P ( m ( x ) , k ( x , x ′ ) ) f(\mathbf{x}) \sim \mathcal{GP}(m(\mathbf{x}), k(\mathbf{x}, \mathbf{x}')) f(x)∼GP(m(x),k(x,x′))
常用核函数:
平方指数核(RBF):
k ( x , x ′ ) = σ 2 exp ( − ∥ x − x ′ ∥ 2 2 l 2 ) k(\mathbf{x}, \mathbf{x}') = \sigma^2 \exp\left(-\frac{\|\mathbf{x} - \mathbf{x}'\|^2}{2l^2}\right) k(x,x′)=σ2exp(−2l2∥x−x′∥2)
Matérn核:
k ν ( x , x ′ ) = σ 2 2 1 − ν Γ ( ν ) ( 2 ν r l ) ν K ν ( 2 ν r l ) k_{\nu}(\mathbf{x}, \mathbf{x}') = \sigma^2 \frac{2^{1-\nu}}{\Gamma(\nu)} \left(\frac{\sqrt{2\nu} r}{l}\right)^{\nu} K_{\nu}\left(\frac{\sqrt{2\nu} r}{l}\right) kν(x,x′)=σ2Γ(ν)21−ν(l2νr)νKν(l2νr)
预测:
给定训练数据 X , y \mathbf{X}, \mathbf{y} X,y,预测新点 x ∗ \mathbf{x}_* x∗:
μ ∗ = k ∗ T K − 1 y \mu_* = \mathbf{k}_*^T \mathbf{K}^{-1} \mathbf{y} μ∗=k∗TK−1y
σ ∗ 2 = k ( x ∗ , x ∗ ) − k ∗ T K − 1 k ∗ \sigma_*^2 = k(\mathbf{x}_*, \mathbf{x}_*) - \mathbf{k}_*^T \mathbf{K}^{-1} \mathbf{k}_* σ∗2=k(x∗,x∗)−k∗TK−1k∗
6.3 本征正交分解(POD)
POD基本原理:
寻找一组基函数,使得数据在这组基上的投影能量最大。
数学推导:
给定快照矩阵 S = [ u 1 , u 2 , . . . , u N ] \mathbf{S} = [\mathbf{u}_1, \mathbf{u}_2, ..., \mathbf{u}_N] S=[u1,u2,...,uN],求解特征值问题:
R ϕ = λ ϕ \mathbf{R} \boldsymbol{\phi} = \lambda \boldsymbol{\phi} Rϕ=λϕ
其中 R = 1 N S S T \mathbf{R} = \frac{1}{N} \mathbf{S} \mathbf{S}^T R=N1SST是相关矩阵。
截断:
保留前 M M M个模态,使得能量占比:
E = ∑ i = 1 M λ i ∑ i = 1 N λ i > 0.99 E = \frac{\sum_{i=1}^{M} \lambda_i}{\sum_{i=1}^{N} \lambda_i} > 0.99 E=∑i=1Nλi∑i=1Mλi>0.99
Galerkin投影:
将控制方程投影到POD模态上,得到降阶模型:
d a d t = A a + B ( a ) \frac{d\mathbf{a}}{dt} = \mathbf{A}\mathbf{a} + \mathbf{B}(\mathbf{a}) dtda=Aa+B(a)
6.4 动态模态分解(DMD)
DMD基本原理:
从数据中提取动态特征,假设系统满足线性动力学:
x k + 1 = A x k \mathbf{x}_{k+1} = \mathbf{A} \mathbf{x}_k xk+1=Axk
算法步骤:
- 构建数据矩阵: X = [ x 1 , . . . , x N − 1 ] \mathbf{X} = [\mathbf{x}_1, ..., \mathbf{x}_{N-1}] X=[x1,...,xN−1], X ′ = [ x 2 , . . . , x N ] \mathbf{X}' = [\mathbf{x}_2, ..., \mathbf{x}_N] X′=[x2,...,xN]
- 计算近似线性算子: A = X ′ X + \mathbf{A} = \mathbf{X}' \mathbf{X}^+ A=X′X+
- 对 A \mathbf{A} A进行特征值分解
- 重构动力学
与POD的关系:
DMD可以看作是在POD基上的线性回归。
6.5 神经网络降阶模型
编码器-解码器结构:
高维流场 → 编码器 → 低维潜在空间 → 解码器 → 高维流场
自编码器降阶:
训练目标:
min θ ∥ u − Decoder ( Encoder ( u ) ) ∥ 2 \min_{\theta} \|\mathbf{u} - \text{Decoder}(\text{Encoder}(\mathbf{u}))\|^2 θmin∥u−Decoder(Encoder(u))∥2
LSTM预测:
在潜在空间用LSTM预测时间演化:
z t + 1 = LSTM ( z t ) \mathbf{z}_{t+1} = \text{LSTM}(\mathbf{z}_t) zt+1=LSTM(zt)
6.6 物理约束的降阶模型
问题:纯数据驱动的降阶模型可能不满足物理约束。
解决方案:
- 物理损失函数:
L = L reconstruction + λ L physics \mathcal{L} = \mathcal{L}_{\text{reconstruction}} + \lambda \mathcal{L}_{\text{physics}} L=Lreconstruction+λLphysics
- 结构保持降阶:
保持原始系统的守恒性质。
- 算子推断:
从数据推断低维算子,保持结构。
Python仿真案例
案例1:神经网络拟合非线性函数
本案例演示使用神经网络拟合复杂的非线性函数。
"""
案例1:神经网络拟合非线性函数
演示深度学习的基本原理
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
plt.switch_backend('Agg')
print("案例1:神经网络拟合非线性函数")
# 生成训练数据:复杂的非线性函数
def true_function(x, y):
"""真实函数:复杂的非线性关系"""
return np.sin(2*np.pi*x) * np.cos(2*np.pi*y) + 0.3*np.sin(4*np.pi*x*y)
# 生成数据
np.random.seed(42)
n_samples = 1000
x_train = np.random.uniform(0, 1, n_samples)
y_train = np.random.uniform(0, 1, n_samples)
z_train = true_function(x_train, y_train) + 0.1*np.random.randn(n_samples)
# 测试数据
x_test = np.linspace(0, 1, 50)
y_test = np.linspace(0, 1, 50)
X_test, Y_test = np.meshgrid(x_test, y_test)
Z_true = true_function(X_test, Y_test)
# 神经网络类
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
# 初始化权重
self.W1 = np.random.randn(input_size, hidden_size) * 0.1
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, hidden_size) * 0.1
self.b2 = np.zeros((1, hidden_size))
self.W3 = np.random.randn(hidden_size, output_size) * 0.1
self.b3 = np.zeros((1, output_size))
# 存储训练历史
self.loss_history = []
def relu(self, x):
return np.maximum(0, x)
def relu_derivative(self, x):
return (x > 0).astype(float)
def forward(self, X):
# 前向传播
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.relu(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
self.a2 = self.relu(self.z2)
self.z3 = np.dot(self.a2, self.W3) + self.b3
return self.z3
def backward(self, X, y, learning_rate):
m = X.shape[0]
# 前向传播
output = self.forward(X)
# 计算损失
loss = np.mean((output - y)**2)
# 反向传播
d_output = 2 * (output - y) / m
dW3 = np.dot(self.a2.T, d_output)
db3 = np.sum(d_output, axis=0, keepdims=True)
d_a2 = np.dot(d_output, self.W3.T)
d_z2 = d_a2 * self.relu_derivative(self.z2)
dW2 = np.dot(self.a1.T, d_z2)
db2 = np.sum(d_z2, axis=0, keepdims=True)
d_a1 = np.dot(d_z2, self.W2.T)
d_z1 = d_a1 * self.relu_derivative(self.z1)
dW1 = np.dot(X.T, d_z1)
db1 = np.sum(d_z1, axis=0, keepdims=True)
# 更新权重
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
self.W3 -= learning_rate * dW3
self.b3 -= learning_rate * db3
return loss
def train(self, X, y, epochs, learning_rate):
for epoch in range(epochs):
loss = self.backward(X, y, learning_rate)
self.loss_history.append(loss)
if epoch % 1000 == 0:
print(f"Epoch {epoch}, Loss: {loss:.6f}")
# 准备数据
X_train = np.column_stack([x_train, y_train])
y_train_reshaped = z_train.reshape(-1, 1)
# 创建和训练网络
nn = NeuralNetwork(input_size=2, hidden_size=64, output_size=1)
print("开始训练神经网络...")
nn.train(X_train, y_train_reshaped, epochs=10000, learning_rate=0.01)
# 预测
X_test_flat = np.column_stack([X_test.ravel(), Y_test.ravel()])
Z_pred = nn.forward(X_test_flat).reshape(X_test.shape)
# 可视化
fig = plt.figure(figsize=(16, 12))
# 真实函数
ax1 = fig.add_subplot(2, 3, 1, projection='3d')
ax1.plot_surface(X_test, Y_test, Z_true, cmap='viridis')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.set_zlabel('Z')
ax1.set_title('真实函数')
# 神经网络预测
ax2 = fig.add_subplot(2, 3, 2, projection='3d')
ax2.plot_surface(X_test, Y_test, Z_pred, cmap='viridis')
ax2.set_xlabel('X')
ax2.set_ylabel('Y')
ax2.set_zlabel('Z')
ax2.set_title('神经网络预测')
# 误差
ax3 = fig.add_subplot(2, 3, 3, projection='3d')
error = np.abs(Z_true - Z_pred)
ax3.plot_surface(X_test, Y_test, error, cmap='hot')
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
ax3.set_zlabel('Error')
ax3.set_title('绝对误差')
# 训练数据分布
ax4 = fig.add_subplot(2, 3, 4)
scatter = ax4.scatter(x_train, y_train, c=z_train, cmap='viridis', s=10)
ax4.set_xlabel('X')
ax4.set_ylabel('Y')
ax4.set_title('训练数据分布')
plt.colorbar(scatter, ax=ax4)
# 损失曲线
ax5 = fig.add_subplot(2, 3, 5)
ax5.semilogy(nn.loss_history)
ax5.set_xlabel('Epoch')
ax5.set_ylabel('Loss (log scale)')
ax5.set_title('训练损失曲线')
ax5.grid(True)
# 预测vs真实值
ax6 = fig.add_subplot(2, 3, 6)
ax6.scatter(Z_true.ravel(), Z_pred.ravel(), alpha=0.5, s=1)
ax6.plot([-2, 2], [-2, 2], 'r--', label='Perfect prediction')
ax6.set_xlabel('True Value')
ax6.set_ylabel('Predicted Value')
ax6.set_title('预测准确性')
ax6.legend()
ax6.grid(True)
plt.tight_layout()
plt.savefig('d:\\文档\\500仿真领域\\工程仿真\\对流换热仿真\\主题077_数据驱动建模\\仿真结果\\case1_neural_network_fitting.png', dpi=150, bbox_inches='tight')
plt.close()
print("案例1完成!")
案例2:PINN求解一维热传导方程
本案例演示使用物理信息神经网络求解热传导方程。
"""
案例2:PINN求解一维热传导方程
物理信息神经网络入门示例
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
plt.switch_backend('Agg')
print("案例2:PINN求解一维热传导方程")
# 物理参数
alpha = 0.1 # 热扩散系数
L = 1.0 # 域长度
T_final = 1.0 # 最终时间
# 神经网络类(简化版)
class PINN:
def __init__(self, layers):
self.layers = layers
self.weights = []
self.biases = []
# 初始化权重
for i in range(len(layers) - 1):
w = np.random.randn(layers[i], layers[i+1]) * np.sqrt(2.0 / layers[i])
b = np.zeros((1, layers[i+1]))
self.weights.append(w)
self.biases.append(b)
self.loss_history = {'total': [], 'ic': [], 'bc': [], 'pde': []}
def tanh(self, x):
return np.tanh(x)
def tanh_derivative(self, x):
return 1 - np.tanh(x)**2
def forward(self, x, t):
# 合并输入
X = np.hstack([x, t])
# 前向传播
self.activations = [X]
for i in range(len(self.weights) - 1):
z = np.dot(self.activations[-1], self.weights[i]) + self.biases[i]
a = self.tanh(z)
self.activations.append(a)
# 输出层(线性激活)
z = np.dot(self.activations[-1], self.weights[-1]) + self.biases[-1]
self.activations.append(z)
return z
def compute_derivatives(self, x, t):
"""计算自动微分"""
eps = 1e-5
# u
u = self.forward(x, t)
# u_t
u_t = (self.forward(x, t + eps) - self.forward(x, t - eps)) / (2 * eps)
# u_x
u_x = (self.forward(x + eps, t) - self.forward(x - eps, t)) / (2 * eps)
# u_xx
u_xx = (self.forward(x + eps, t) - 2*u + self.forward(x - eps, t)) / (eps**2)
return u, u_t, u_x, u_xx
def compute_loss(self, x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f):
"""计算总损失"""
# 初始条件损失
u_ic_pred = self.forward(x_ic, t_ic)
loss_ic = np.mean((u_ic_pred - u_ic)**2)
# 边界条件损失
u_bc_pred = self.forward(x_bc, t_bc)
loss_bc = np.mean((u_bc_pred - u_bc)**2)
# PDE残差损失
u_f, u_t, u_x, u_xx = self.compute_derivatives(x_f, t_f)
f = u_t - alpha * u_xx
loss_pde = np.mean(f**2)
# 总损失
loss_total = loss_ic + loss_bc + loss_pde
return loss_total, loss_ic, loss_bc, loss_pde
def train_step(self, x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f, lr):
"""单步训练(数值梯度)"""
eps = 1e-5
# 计算当前损失
loss_total, loss_ic, loss_bc, loss_pde = self.compute_loss(
x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f
)
# 更新权重(简化版梯度下降)
for i in range(len(self.weights)):
# 权重梯度
for j in range(self.weights[i].shape[0]):
for k in range(self.weights[i].shape[1]):
self.weights[i][j, k] += eps
loss_plus, _, _, _ = self.compute_loss(
x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f
)
grad = (loss_plus - loss_total) / eps
self.weights[i][j, k] -= eps + lr * grad
# 偏置梯度
for j in range(self.biases[i].shape[1]):
self.biases[i][0, j] += eps
loss_plus, _, _, _ = self.compute_loss(
x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f
)
grad = (loss_plus - loss_total) / eps
self.biases[i][0, j] -= eps + lr * grad
return loss_total, loss_ic, loss_bc, loss_pde
def train(self, x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f, epochs, lr):
"""训练网络"""
print("开始训练PINN...")
for epoch in range(epochs):
loss_total, loss_ic, loss_bc, loss_pde = self.train_step(
x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f, lr
)
self.loss_history['total'].append(loss_total)
self.loss_history['ic'].append(loss_ic)
self.loss_history['bc'].append(loss_bc)
self.loss_history['pde'].append(loss_pde)
if epoch % 1000 == 0:
print(f"Epoch {epoch}: Total={loss_total:.6f}, IC={loss_ic:.6f}, BC={loss_bc:.6f}, PDE={loss_pde:.6f}")
# 生成训练数据
# 初始条件:u(x, 0) = sin(pi*x)
x_ic = np.random.rand(100, 1) * L
t_ic = np.zeros((100, 1))
u_ic = np.sin(np.pi * x_ic)
# 边界条件:u(0, t) = u(1, t) = 0
x_bc = np.vstack([np.zeros((50, 1)), np.ones((50, 1)) * L])
t_bc = np.random.rand(100, 1) * T_final
u_bc = np.zeros((100, 1))
# 内部配点
x_f = np.random.rand(1000, 1) * L
t_f = np.random.rand(1000, 1) * T_final
# 创建和训练PINN
pinn = PINN(layers=[2, 32, 32, 32, 1])
pinn.train(x_ic, t_ic, u_ic, x_bc, t_bc, u_bc, x_f, t_f, epochs=5000, lr=0.001)
# 解析解
def analytical_solution(x, t, alpha, n_terms=20):
"""一维热传导方程的解析解"""
u = np.zeros_like(x)
for n in range(1, n_terms + 1):
u += np.sin(n * np.pi * x) * np.exp(-(n * np.pi)**2 * alpha * t)
u *= 2 / np.pi
return u
# 生成预测网格
x_pred = np.linspace(0, L, 100)
t_pred = np.linspace(0, T_final, 50)
X_pred, T_pred = np.meshgrid(x_pred, t_pred)
# PINN预测
U_pred = np.zeros_like(X_pred)
for i in range(X_pred.shape[0]):
for j in range(X_pred.shape[1]):
U_pred[i, j] = pinn.forward(
np.array([[X_pred[i, j]]]),
np.array([[T_pred[i, j]]])
)[0, 0]
# 解析解
U_analytical = analytical_solution(X_pred, T_pred, alpha)
# 可视化
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# PINN解
ax = axes[0, 0]
im = ax.contourf(X_pred, T_pred, U_pred, levels=20, cmap='hot')
ax.set_xlabel('x')
ax.set_ylabel('t')
ax.set_title('PINN预测')
plt.colorbar(im, ax=ax)
# 解析解
ax = axes[0, 1]
im = ax.contourf(X_pred, T_pred, U_analytical, levels=20, cmap='hot')
ax.set_xlabel('x')
ax.set_ylabel('t')
ax.set_title('解析解')
plt.colorbar(im, ax=ax)
# 误差
ax = axes[0, 2]
error = np.abs(U_pred - U_analytical)
im = ax.contourf(X_pred, T_pred, error, levels=20, cmap='viridis')
ax.set_xlabel('x')
ax.set_ylabel('t')
ax.set_title('绝对误差')
plt.colorbar(im, ax=ax)
# 损失曲线
ax = axes[1, 0]
ax.semilogy(pinn.loss_history['total'], label='Total', linewidth=2)
ax.semilogy(pinn.loss_history['ic'], label='IC', alpha=0.7)
ax.semilogy(pinn.loss_history['bc'], label='BC', alpha=0.7)
ax.semilogy(pinn.loss_history['pde'], label='PDE', alpha=0.7)
ax.set_xlabel('Epoch')
ax.set_ylabel('Loss (log scale)')
ax.set_title('训练损失曲线')
ax.legend()
ax.grid(True)
# 不同时刻的温度分布
ax = axes[1, 1]
times = [0, 0.2, 0.5, 1.0]
for t_val in times:
idx = np.argmin(np.abs(t_pred - t_val))
ax.plot(x_pred, U_pred[idx, :], label=f't={t_val}')
ax.set_xlabel('x')
ax.set_ylabel('u')
ax.set_title('不同时刻的温度分布')
ax.legend()
ax.grid(True)
# 空间-时间演化
ax = axes[1, 2]
ax.plot(x_pred, U_analytical[-1, :], 'b-', label='Analytical', linewidth=2)
ax.plot(x_pred, U_pred[-1, :], 'r--', label='PINN', linewidth=2)
ax.set_xlabel('x')
ax.set_ylabel('u')
ax.set_title(f't={T_final}时刻对比')
ax.legend()
ax.grid(True)
plt.tight_layout()
plt.savefig('d:\\文档\\500仿真领域\\工程仿真\\对流换热仿真\\主题077_数据驱动建模\\仿真结果\\case2_pinn_heat_equation.png', dpi=150, bbox_inches='tight')
plt.close()
print("案例2完成!")
案例3:高斯过程回归构建代理模型
本案例演示使用高斯过程回归构建计算昂贵的CFD模型的代理模型。
"""
案例3:高斯过程回归构建代理模型
使用GP替代昂贵的CFD计算
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
plt.switch_backend('Agg')
print("案例3:高斯过程回归构建代理模型")
# 高斯过程回归类
class GaussianProcess:
def __init__(self, kernel='rbf', noise=1e-5):
self.kernel_type = kernel
self.noise = noise
self.X_train = None
self.y_train = None
self.K = None
self.L = None
self.alpha = None
def rbf_kernel(self, X1, X2, length_scale=1.0, sigma=1.0):
"""RBF核函数"""
sqdist = cdist(X1, X2, 'sqeuclidean')
return sigma**2 * np.exp(-0.5 * sqdist / length_scale**2)
def fit(self, X, y, length_scale=1.0, sigma=1.0):
"""训练GP模型"""
self.X_train = X
self.y_train = y
self.length_scale = length_scale
self.sigma = sigma
# 计算核矩阵
self.K = self.rbf_kernel(X, X, length_scale, sigma)
self.K += self.noise * np.eye(len(X))
# Cholesky分解
self.L = np.linalg.cholesky(self.K)
# 求解
self.alpha = np.linalg.solve(self.L.T, np.linalg.solve(self.L, y))
def predict(self, X_test):
"""预测"""
# 计算测试点与训练点的核
K_s = self.rbf_kernel(self.X_train, X_test, self.length_scale, self.sigma)
# 预测均值
mu = np.dot(K_s.T, self.alpha)
# 预测方差
K_ss = self.rbf_kernel(X_test, X_test, self.length_scale, self.sigma)
v = np.linalg.solve(self.L, K_s)
var = np.diag(K_ss) - np.sum(v**2, axis=0)
std = np.sqrt(np.maximum(var, 0))
return mu, std
# 模拟昂贵的CFD函数:努塞尔数关联式
def expensive_cfd_simulation(Re, Pr):
"""
模拟昂贵的CFD计算:管内湍流换热
使用Gnielinski关联式作为"真实"CFD
"""
# Gnielinski关联式
f = (0.79 * np.log(Re) - 1.64)**(-2)
Nu = (f/8) * (Re - 1000) * Pr / (1 + 12.7 * np.sqrt(f/8) * (Pr**(2/3) - 1))
# 添加一些"CFD噪声"
Nu += 0.01 * Nu * np.random.randn(*Nu.shape)
return Nu
# 生成训练数据(少量昂贵的CFD点)
np.random.seed(42)
n_train = 20
Re_train = np.random.uniform(10000, 100000, n_train)
Pr_train = np.random.uniform(0.7, 10, n_train)
X_train = np.column_stack([np.log10(Re_train), np.log10(Pr_train)])
y_train = expensive_cfd_simulation(Re_train, Pr_train)
# 创建GP模型
gp = GaussianProcess()
print("训练GP模型...")
gp.fit(X_train, y_train, length_scale=0.5, sigma=50)
# 生成测试数据(密集网格)
Re_test = np.linspace(10000, 100000, 100)
Pr_test = np.linspace(0.7, 10, 100)
Re_grid, Pr_grid = np.meshgrid(Re_test, Pr_test)
X_test = np.column_stack([np.log10(Re_grid.ravel()), np.log10(Pr_grid.ravel())])
# GP预测
print("进行GP预测...")
mu, std = gp.predict(X_test)
mu = mu.reshape(Re_grid.shape)
std = std.reshape(Re_grid.shape)
# "真实"CFD(用于对比)
Nu_true = expensive_cfd_simulation(Re_grid, Pr_grid)
# 可视化
fig, axes = plt.subplots(2, 3, figsize=(16, 10))
# GP预测均值
ax = axes[0, 0]
im = ax.contourf(Re_grid, Pr_grid, mu, levels=20, cmap='viridis')
ax.scatter(Re_train, Pr_train, c='red', s=50, marker='x', label='Training points')
ax.set_xlabel('Re')
ax.set_ylabel('Pr')
ax.set_title('GP预测均值')
ax.set_xscale('log')
plt.colorbar(im, ax=ax)
ax.legend()
# 真实CFD
ax = axes[0, 1]
im = ax.contourf(Re_grid, Pr_grid, Nu_true, levels=20, cmap='viridis')
ax.set_xlabel('Re')
ax.set_ylabel('Pr')
ax.set_title('真实CFD结果')
ax.set_xscale('log')
plt.colorbar(im, ax=ax)
# 预测标准差(不确定性)
ax = axes[0, 2]
im = ax.contourf(Re_grid, Pr_grid, std, levels=20, cmap='hot')
ax.scatter(Re_train, Pr_train, c='blue', s=50, marker='x')
ax.set_xlabel('Re')
ax.set_ylabel('Pr')
ax.set_title('预测不确定性')
ax.set_xscale('log')
plt.colorbar(im, ax=ax)
# 误差分布
ax = axes[1, 0]
error = np.abs(mu - Nu_true)
im = ax.contourf(Re_grid, Pr_grid, error, levels=20, cmap='Reds')
ax.set_xlabel('Re')
ax.set_ylabel('Pr')
ax.set_title('绝对误差')
ax.set_xscale('log')
plt.colorbar(im, ax=ax)
# 1D切片对比(固定Pr=5)
ax = axes[1, 1]
Pr_fixed = 5.0
idx = np.argmin(np.abs(Pr_test - Pr_fixed))
ax.plot(Re_test, Nu_true[idx, :], 'b-', label='True CFD', linewidth=2)
ax.plot(Re_test, mu[idx, :], 'r--', label='GP Prediction', linewidth=2)
ax.fill_between(Re_test,
mu[idx, :] - 2*std[idx, :],
mu[idx, :] + 2*std[idx, :],
alpha=0.3, color='red', label='95% CI')
ax.scatter(Re_train[Pr_train > 4], y_train[Pr_train > 4],
c='green', s=50, marker='o', label='Training data')
ax.set_xlabel('Re')
ax.set_ylabel('Nu')
ax.set_title(f'Pr={Pr_fixed}时的预测对比')
ax.set_xscale('log')
ax.legend()
ax.grid(True)
# 误差统计
ax = axes[1, 2]
errors = (mu - Nu_true).ravel()
ax.hist(errors, bins=30, edgecolor='black', alpha=0.7)
ax.axvline(0, color='r', linestyle='--', linewidth=2)
ax.set_xlabel('Prediction Error')
ax.set_ylabel('Frequency')
ax.set_title(f'误差分布 (RMSE={np.sqrt(np.mean(errors**2)):.2f})')
ax.grid(True)
plt.tight_layout()
plt.savefig('d:\\文档\\500仿真领域\\工程仿真\\对流换热仿真\\主题077_数据驱动建模\\仿真结果\\case3_gaussian_process.png', dpi=150, bbox_inches='tight')
plt.close()
print("案例3完成!")
案例4:本征正交分解(POD)降阶模型
本案例演示使用POD从CFD数据中提取主要模态并构建降阶模型。
"""
案例4:本征正交分解(POD)降阶模型
从CFD数据中提取主要流动模态
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.linalg import eigh
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
plt.switch_backend('Agg')
print("案例4:本征正交分解(POD)降阶模型")
# 生成模拟CFD数据:圆柱绕流的不同时间步
# 简化模型:使用解析函数模拟卡门涡街
def generate_flow_field(x, y, t, Re=100):
"""生成模拟流场数据"""
nx, ny = len(x), len(y)
X, Y = np.meshgrid(x, y, indexing='ij')
# 圆柱位置
cx, cy = 2.0, 2.0
# 基础流动 + 涡脱落
u = np.ones_like(X) * 0.5
v = np.zeros_like(Y)
# 添加涡结构
for i, ti in enumerate(t):
St = 0.2 # 斯特劳哈尔数
omega = 2 * np.pi * St * 0.5 / 0.5 # 涡脱落频率
# 涡对
for j in range(3):
x_vortex = cx + (j+1) * 1.5 + 0.5 * ti
y_vortex_p = cy + 0.3 * np.sin(omega * ti + j * np.pi)
y_vortex_n = cy - 0.3 * np.sin(omega * ti + j * np.pi)
r_p = np.sqrt((X - x_vortex)**2 + (Y - y_vortex_p)**2)
r_n = np.sqrt((X - x_vortex)**2 + (Y - y_vortex_n)**2)
strength = 0.3 * np.exp(-0.1 * (j+1))
u += strength * (Y - y_vortex_p) / (r_p + 0.1)**2 * np.exp(-r_p/2)
u -= strength * (Y - y_vortex_n) / (r_n + 0.1)**2 * np.exp(-r_n/2)
v -= strength * (X - x_vortex) / (r_p + 0.1)**2 * np.exp(-r_p/2)
v += strength * (X - x_vortex) / (r_n + 0.1)**2 * np.exp(-r_n/2)
return u, v
# 生成快照数据
nx, ny = 60, 40
x = np.linspace(0, 10, nx)
y = np.linspace(0, 4, ny)
n_snapshots = 100
t_snapshots = np.linspace(0, 20, n_snapshots)
print(f"生成{n_snapshots}个流场快照...")
snapshots = []
for i, t in enumerate(t_snapshots):
u, v = generate_flow_field(x, y, [t])
# 合并u和v作为状态向量
state = np.concatenate([u.flatten(), v.flatten()])
snapshots.append(state)
snapshots = np.array(snapshots).T # 形状: (2*nx*ny, n_snapshots)
# POD分析
print("执行POD分析...")
# 计算均值并去均值
mean_flow = np.mean(snapshots, axis=1, keepdims=True)
snapshots_centered = snapshots - mean_flow
# 计算相关矩阵
C = np.dot(snapshots_centered.T, snapshots_centered) / n_snapshots
# 特征值分解
eigenvalues, eigenvectors = eigh(C)
# 按特征值降序排序
idx = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]
# 计算POD模态
pod_modes = np.dot(snapshots_centered, eigenvectors)
# 归一化
for i in range(pod_modes.shape[1]):
norm = np.sqrt(np.sum(pod_modes[:, i]**2))
if norm > 0:
pod_modes[:, i] /= norm
# 计算能量占比
energy = eigenvalues / np.sum(eigenvalues)
cumulative_energy = np.cumsum(energy)
# 可视化
fig, axes = plt.subplots(2, 3, figsize=(16, 10))
# 能量分布
ax = axes[0, 0]
ax.semilogy(range(1, 21), energy[:20], 'bo-', markersize=6)
ax.set_xlabel('Mode Number')
ax.set_ylabel('Energy')
ax.set_title('POD模态能量分布')
ax.grid(True)
# 累积能量
ax = axes[0, 1]
ax.plot(range(1, 21), cumulative_energy[:20], 'r-', linewidth=2)
ax.axhline(0.95, color='g', linestyle='--', label='95% Energy')
ax.axhline(0.99, color='b', linestyle='--', label='99% Energy')
ax.set_xlabel('Number of Modes')
ax.set_ylabel('Cumulative Energy')
ax.set_title('累积能量占比')
ax.legend()
ax.grid(True)
# 前4个POD模态
for i in range(4):
ax = axes[0 if i < 2 else 1, 2 if i % 2 == 0 else 0 if i == 1 else 1]
mode = pod_modes[:, i]
u_mode = mode[:nx*ny].reshape(nx, ny)
im = ax.contourf(x, y, u_mode.T, levels=20, cmap='RdBu_r')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title(f'POD Mode {i+1} (Energy: {energy[i]*100:.1f}%)')
plt.colorbar(im, ax=ax)
# 重构流场(使用前10个模态)
n_modes = 10
ax = axes[1, 2]
# 选择最后一个快照进行重构
snapshot_idx = -1
coefficients = np.dot(snapshots_centered[:, snapshot_idx], pod_modes[:, :n_modes])
reconstructed = mean_flow[:, 0] + np.dot(pod_modes[:, :n_modes], coefficients)
u_original = snapshots[:nx*ny, snapshot_idx].reshape(nx, ny)
u_reconstructed = reconstructed[:nx*ny].reshape(nx, ny)
# 显示重构误差
error = np.abs(u_original - u_reconstructed)
im = ax.contourf(x, y, error.T, levels=20, cmap='hot')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title(f'重构误差 ({n_modes} modes)')
plt.colorbar(im, ax=ax)
plt.tight_layout()
plt.savefig('d:\\文档\\500仿真领域\\工程仿真\\对流换热仿真\\主题077_数据驱动建模\\仿真结果\\case4_pod_analysis.png', dpi=150, bbox_inches='tight')
plt.close()
# 绘制重构对比图
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
ax = axes[0]
im = ax.contourf(x, y, u_original.T, levels=20, cmap='RdBu_r')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Original Flow')
plt.colorbar(im, ax=ax)
ax = axes[1]
im = ax.contourf(x, y, u_reconstructed.T, levels=20, cmap='RdBu_r')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title(f'Reconstructed ({n_modes} modes)')
plt.colorbar(im, ax=ax)
ax = axes[2]
im = ax.contourf(x, y, error.T, levels=20, cmap='hot')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Reconstruction Error')
plt.colorbar(im, ax=ax)
plt.tight_layout()
plt.savefig('d:\\文档\\500仿真领域\\工程仿真\\对流换热仿真\\主题077_数据驱动建模\\仿真结果\\case4_pod_reconstruction.png', dpi=150, bbox_inches='tight')
plt.close()
print("案例4完成!")
案例5:数据同化融合实验与仿真数据
本案例演示使用卡尔曼滤波融合实验数据和CFD仿真结果。
"""
案例5:数据同化融合实验与仿真数据
使用卡尔曼滤波融合多源数据
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
plt.switch_backend('Agg')
print("案例5:数据同化融合实验与仿真数据")
# 一维热传导问题的数据同化
# 真实系统(未知)
def true_system(x, t, alpha=0.1):
"""真实温度场"""
return np.sin(np.pi * x) * np.exp(-alpha * (np.pi)**2 * t)
# CFD模型(有偏差)
def cfd_model(x, t, alpha_cfd=0.08):
"""CFD模型(参数有偏差)"""
return np.sin(np.pi * x) * np.exp(-alpha_cfd * (np.pi)**2 * t)
# 生成网格
nx = 50
x = np.linspace(0, 1, nx)
dx = x[1] - x[0]
# 时间设置
dt = 0.01
nt = 100
t = np.arange(nt) * dt
# 初始化
T_true = np.zeros((nt, nx))
T_cfd = np.zeros((nt, nx))
T_da = np.zeros((nt, nx)) # 数据同化结果
# 初始条件
T_true[0, :] = np.sin(np.pi * x)
T_cfd[0, :] = np.sin(np.pi * x)
T_da[0, :] = np.sin(np.pi * x)
# 观测设置(稀疏观测)
n_obs = 5
obs_indices = np.linspace(5, nx-5, n_obs, dtype=int)
obs_noise = 0.05
# 卡尔曼滤波参数
# 状态转移矩阵(简化的一维热传导离散)
alpha_da = 0.09 # 数据同化使用的参数
r = alpha_da * dt / dx**2
F = np.eye(nx)
for i in range(1, nx-1):
F[i, i-1] = r
F[i, i] = 1 - 2*r
F[i, i+1] = r
# 观测矩阵(只观测特定位置)
H = np.zeros((n_obs, nx))
for i, idx in enumerate(obs_indices):
H[i, idx] = 1
# 协方差矩阵
Q = 0.001 * np.eye(nx) # 过程噪声
R = obs_noise**2 * np.eye(n_obs) # 观测噪声
P = 0.01 * np.eye(nx) # 初始误差协方差
print("运行数据同化...")
# 存储历史
obs_history = []
rmse_cfd = []
rmse_da = []
for n in range(1, nt):
# 真实系统演化
T_true[n, :] = true_system(x, t[n])
# CFD模型预测(有偏差)
T_cfd[n, :] = cfd_model(x, t[n])
# 数据同化:预测步骤
T_pred = F @ T_da[n-1, :]
P_pred = F @ P @ F.T + Q
# 生成观测(稀疏 + 噪声)
if n % 5 == 0: # 每5步观测一次
obs = T_true[n, obs_indices] + obs_noise * np.random.randn(n_obs)
obs_history.append((n, obs))
# 更新步骤
K = P_pred @ H.T @ np.linalg.inv(H @ P_pred @ H.T + R)
T_da[n, :] = T_pred + K @ (obs - H @ T_pred)
P = (np.eye(nx) - K @ H) @ P_pred
else:
T_da[n, :] = T_pred
P = P_pred
# 计算RMSE
rmse_cfd.append(np.sqrt(np.mean((T_cfd[n, :] - T_true[n, :])**2)))
rmse_da.append(np.sqrt(np.mean((T_da[n, :] - T_true[n, :])**2)))
# 生成GIF动画
print("生成GIF动画...")
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# 初始化图像
lines = []
for ax in axes:
line1, = ax.plot([], [], 'b-', label='True', linewidth=2)
line2, = ax.plot([], [], 'r--', label='CFD', linewidth=2)
line3, = ax.plot([], [], 'g:', label='Data Assimilation', linewidth=2)
lines.extend([line1, line2, line3])
# 设置坐标轴
for ax in axes:
ax.set_xlim(0, 1)
ax.set_ylim(-0.2, 1.2)
ax.set_xlabel('x')
ax.set_ylabel('Temperature')
ax.legend()
ax.grid(True)
axes[0].set_title('Temperature Distribution')
axes[1].set_title('Error (CFD vs True)')
axes[2].set_title('Error (DA vs True)')
def init():
for line in lines:
line.set_data([], [])
return lines
def update(frame):
n = frame
# 温度分布
lines[0].set_data(x, T_true[n, :])
lines[1].set_data(x, T_cfd[n, :])
lines[2].set_data(x, T_da[n, :])
# CFD误差
error_cfd = T_cfd[n, :] - T_true[n, :]
lines[3].set_data([], [])
lines[4].set_data(x, error_cfd)
lines[5].set_data([], [])
# DA误差
error_da = T_da[n, :] - T_true[n, :]
lines[6].set_data([], [])
lines[7].set_data([], [])
lines[8].set_data(x, error_da)
axes[0].set_title(f'Temperature Distribution (t={t[n]:.2f})')
return lines
# 创建动画
anim = FuncAnimation(fig, update, frames=range(0, nt, 2), init_func=init, blit=True)
anim.save('d:\\文档\\500仿真领域\\工程仿真\\对流换热仿真\\主题077_数据驱动建模\\仿真结果\\case5_data_assimilation.gif',
writer='pillow', fps=10, dpi=100)
plt.close()
# 绘制最终结果
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 最后时刻对比
ax = axes[0, 0]
ax.plot(x, T_true[-1, :], 'b-', label='True', linewidth=2)
ax.plot(x, T_cfd[-1, :], 'r--', label='CFD', linewidth=2)
ax.plot(x, T_da[-1, :], 'g:', label='Data Assimilation', linewidth=2)
ax.scatter(x[obs_indices], T_true[-1, obs_indices], c='orange', s=50, marker='o', label='Observations')
ax.set_xlabel('x')
ax.set_ylabel('Temperature')
ax.set_title(f'Final Time (t={t[-1]:.2f})')
ax.legend()
ax.grid(True)
# RMSE演化
ax = axes[0, 1]
ax.plot(t[1:], rmse_cfd, 'r-', label='CFD RMSE', linewidth=2)
ax.plot(t[1:], rmse_da, 'g-', label='DA RMSE', linewidth=2)
ax.set_xlabel('Time')
ax.set_ylabel('RMSE')
ax.set_title('RMSE Evolution')
ax.legend()
ax.grid(True)
# 误差分布
ax = axes[1, 0]
error_cfd_final = T_cfd[-1, :] - T_true[-1, :]
error_da_final = T_da[-1, :] - T_true[-1, :]
ax.plot(x, error_cfd_final, 'r-', label='CFD Error', linewidth=2)
ax.plot(x, error_da_final, 'g-', label='DA Error', linewidth=2)
ax.axhline(0, color='k', linestyle='--', alpha=0.5)
ax.set_xlabel('x')
ax.set_ylabel('Error')
ax.set_title('Final Error Distribution')
ax.legend()
ax.grid(True)
# 误差统计
ax = axes[1, 1]
ax.bar(['CFD', 'Data Assimilation'],
[np.mean(rmse_cfd), np.mean(rmse_da)],
color=['red', 'green'], alpha=0.7)
ax.set_ylabel('Mean RMSE')
ax.set_title('Average RMSE Comparison')
ax.grid(True, axis='y')
plt.tight_layout()
plt.savefig('d:\\文档\\500仿真领域\\工程仿真\\对流换热仿真\\主题077_数据驱动建模\\仿真结果\\case5_data_assimilation_summary.png', dpi=150, bbox_inches='tight')
plt.close()
print("案例5完成!")
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)