2. 二维泊松方程PINN求解
任务目标
构建一个物理信息神经网络(PINN),求解如下二维泊松方程:
∇²u = −2π²sin(πx)sin(πy),(x,y) ∈ [0,1]×[0,1]
边界条件为Dirichlet型:u = 0 在全部四条边界上。该方程具有解析解 u_exact = sin(πx)sin(πy),可用于定量评估精度。
实现要点
采用全连接神经网络作为解函数近似器,通过自动微分计算二阶偏导数构造残差损失。将边界条件作为硬约束直接乘入网络输出,避免使用惩罚项系数调优。训练后与解析解对比,计算相对L2误差。
完整代码
import torch
import torch.nn as nn
import numpy as np
# 硬约束边界条件的PINN
class PINN(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(2, 50), nn.Tanh(),
nn.Linear(50, 50), nn.Tanh(),
nn.Linear(50, 50), nn.Tanh(),
nn.Linear(50, 1)
)
def forward(self, x, y):
# 硬约束:边界处输出为0
u = self.net(torch.cat([x, y], dim=1))
return x * (1-x) * y * (1-y) * u
def loss_fn(model, x, y):
x.requires_grad_(True)
y.requires_grad_(True)
u = model(x, y)
# 一阶导数
u_x = torch.autograd.grad(u, x, grad_outputs=torch.ones_like(u), create_graph=True)[0]
u_y = torch.autograd.grad(u, y, grad_outputs=torch.ones_like(u), create_graph=True)[0]
# 二阶导数
u_xx = torch.autograd.grad(u_x, x, grad_outputs=torch.ones_like(u_x), create_graph=True)[0]
u_yy = torch.autograd.grad(u_y, y, grad_outputs=torch.ones_like(u_y), create_graph=True)[0]
# 泊松方程残差
f = -2 * np.pi**2 * torch.sin(np.pi*x) * torch.sin(np.pi*y)
residual = u_xx + u_yy - f
return torch.mean(residual**2)
# 训练配置
torch.manual_seed(42)
model = PINN()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
# 训练循环
n_iter = 5000
for it in range(n_iter):
# 内部配点采样
x = torch.rand(256, 1) * 2 - 1 # 映射到[-1,1]以改善训练
y = torch.rand(256, 1) * 2 - 1
# 坐标变换回[0,1]
x = (x + 1) / 2
y = (y + 1) / 2
loss = loss_fn(model, x, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (it+1) % 1000 == 0:
print(f'Iter {it+1}, Loss: {loss.item():.2e}')
# 精度评估
with torch.no_grad():
# 测试网格
x_test = torch.linspace(0, 1, 101)
y_test = torch.linspace(0, 1, 101)
X, Y = torch.meshgrid(x_test, y_test, indexing='ij')
X_flat = X.reshape(-1, 1)
Y_flat = Y.reshape(-1, 1)
u_pred = model(X_flat, Y_flat).reshape(101, 101)
u_exact = torch.sin(np.pi * X) * torch.sin(np.pi * Y)
# 相对L2误差
l2_error = torch.norm(u_pred - u_exact) / torch.norm(u_exact)
print(f'相对L2误差: {l2_error.item():.4e}')
运行方式
需要安装PyTorch(CPU版本即可)和NumPy。直接运行上述脚本,训练约5000次迭代后观察损失下降与最终误差输出。
验证方法与判定标准
训练完成后观察两点:
- 损失函数应持续下降至约1e-5以下(因随机种子固定,结果可复现);
- 相对L2误差应小于1e-2,表明PINN解与解析解吻合良好。
若误差偏大,可增加迭代次数至10000或调整网络宽度。边界处由于硬约束设计,u值恒为0,满足Dirichlet条件。
上图展示了PINN求解流程:坐标输入经全连接网络映射后,乘以硬约束因子确保边界条件精确满足;自动微分计算二阶导数构造物理残差损失,Adam优化器迭代更新网络参数;最终与解析解对比评估精度。