2. 二维稳态热传导PINN模拟
任务目标
构建一个物理信息神经网络(PINN),求解如下二维稳态热传导方程(Possion方程):
∇²T = ∂²T/∂x² + ∂²T/∂y² = 0,定义域 Ω = [0,1]×[0,1]
边界条件:T(0,y)=0,T(1,y)=0,T(x,0)=0,T(x,1)=sin(πx)
该问题存在解析解 T_exact(x,y) = sin(πx)·sinh(πy)/sinh(π),用于定量验证PINN精度。
环境依赖
Python 3.9+,PyTorch 2.0+,NumPy,Matplotlib(可选,用于可视化)。无需额外物理库,PINN核心逻辑全部由PyTorch自动微分实现。
关键完整代码
import torch
import torch.nn as nn
import numpy as np
# ---------- 1. 网络定义 ----------
class PINN(nn.Module):
def __init__(self, n_hidden=4, n_neurons=20):
super().__init__()
layers = [nn.Linear(2, n_neurons), nn.Tanh()]
for _ in range(n_hidden - 1):
layers += [nn.Linear(n_neurons, n_neurons), nn.Tanh()]
layers.append(nn.Linear(n_neurons, 1))
self.net = nn.Sequential(*layers)
def forward(self, x, y):
return self.net(torch.cat([x, y], dim=1))
# ---------- 2. 解析解(用于验证) ----------
def exact_solution(x, y):
return torch.sin(np.pi * x) * torch.sinh(np.pi * y) / torch.sinh(np.pi)
# ---------- 3. 训练准备 ----------
torch.manual_seed(42)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = PINN().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
# 内部配点(残差点)
N_f = 2000
x_f = torch.rand(N_f, 1, device=device, requires_grad=True)
y_f = torch.rand(N_f, 1, device=device, requires_grad=True)
# 边界配点(每条边200个点)
N_b = 200
x_left = torch.zeros(N_b, 1, device=device)
y_left = torch.rand(N_b, 1, device=device)
x_right = torch.ones(N_b, 1, device=device)
y_right = torch.rand(N_b, 1, device=device)
x_bottom = torch.rand(N_b, 1, device=device)
y_bottom = torch.zeros(N_b, 1, device=device)
x_top = torch.rand(N_b, 1, device=device)
y_top = torch.ones(N_b, 1, device=device)
# 边界目标值
T_left = torch.zeros(N_b, 1, device=device)
T_right = torch.zeros(N_b, 1, device=device)
T_bottom = torch.zeros(N_b, 1, device=device)
T_top = torch.sin(np.pi * x_top)
# ---------- 4. 损失函数 ----------
def loss_fn():
# PDE残差:∇²T = 0
T = model(x_f, y_f)
dT_dx = torch.autograd.grad(T, x_f, grad_outputs=torch.ones_like(T), create_graph=True)[0]
dT_dy = torch.autograd.grad(T, y_f, grad_outputs=torch.ones_like(T), create_graph=True)[0]
d2T_dx2 = torch.autograd.grad(dT_dx, x_f, grad_outputs=torch.ones_like(dT_dx), create_graph=True)[0]
d2T_dy2 = torch.autograd.grad(dT_dy, y_f, grad_outputs=torch.ones_like(dT_dy), create_graph=True)[0]
pde_loss = torch.mean((d2T_dx2 + d2T_dy2) ** 2)
# 边界损失
bc_loss = (
torch.mean((model(x_left, y_left) - T_left) ** 2) +
torch.mean((model(x_right, y_right) - T_right) ** 2) +
torch.mean((model(x_bottom, y_bottom) - T_bottom) ** 2) +
torch.mean((model(x_top, y_top) - T_top) ** 2)
)
return pde_loss + bc_loss
# ---------- 5. 训练循环 ----------
epochs = 3000
for epoch in range(epochs):
optimizer.zero_grad()
loss = loss_fn()
loss.backward()
optimizer.step()
if (epoch + 1) % 500 == 0:
print(f'Epoch {epoch+1}/{epochs}, Loss: {loss.item():.2e}')
# ---------- 6. 验证:与解析解对比 ----------
with torch.no_grad():
# 在网格点上评估
x_grid = torch.linspace(0, 1, 21, device=device)
y_grid = torch.linspace(0, 1, 21, device=device)
X, Y = torch.meshgrid(x_grid, y_grid, indexing='ij')
x_flat = X.reshape(-1, 1)
y_flat = Y.reshape(-1, 1)
T_pred = model(x_flat, y_flat).reshape(21, 21)
T_exact = exact_solution(X, Y)
# 计算L2相对误差
l2_error = torch.norm(T_pred - T_exact) / torch.norm(T_exact)
print(f'L2相对误差: {l2_error.item():.4f}')
print(f'最大误差: {torch.max(torch.abs(T_pred - T_exact)).item():.4f}')
运行与验证
运行方式:将上述代码保存为 pinn_2d_heat.py,在终端执行 python pinn_2d_heat.py。训练约3000轮(CPU约2~3分钟,GPU约30秒)。
通过判定标准:
- 训练损失稳定下降至1e-4以下;
- L2相对误差低于0.05(5%),最大误差低于0.05;
- 若打印的误差明显偏高(如L2误差>0.1),可增加训练轮数至5000或调整网络宽度(如n_neurons=30)。
预期输出(确定性结果)
由于网络初始化固定(seed=42),训练过程确定,最终误差在固定范围内。典型收敛后损失约1e-5~1e-4,L2相对误差约0.01~0.03。具体数值以实际运行为准,上述范围作为判定参考。
常见问题
- 损失不下降:检查学习率是否过大(建议1e-3),或网络层数是否过深导致梯度消失;
- 边界误差大:确认边界点坐标与目标值一一对应,尤其顶部边界T=sin(πx)需使用对应x坐标计算;
- 训练速度慢:可减少N_f至1000或使用GPU加速。
上图展示了本案例的物理设定:矩形区域内部满足拉普拉斯方程,底部与两侧边界温度恒为0,顶部边界温度呈正弦分布。PINN通过最小化内部PDE残差与边界损失之和,学习满足全部约束的温度场。