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求解二维泊松方程流程 输入 (x, y) 全连接网络 3层×50神经元 硬约束乘子 x(1-x)y(1-y) u 自动微分 → 残差损失 L = mean((u_xx+u_yy−f)²) Adam 优化 解析解对比 相对L2误差

上图展示了PINN求解流程:坐标输入经全连接网络映射后,乘以硬约束因子确保边界条件精确满足;自动微分计算二阶导数构造物理残差损失,Adam优化器迭代更新网络参数;最终与解析解对比评估精度。