基于PINN的一个简单的求解偏微分方程实例

#PINN#、#PDE#、#深度学习#

文章目录

 简介

本人是做化工模拟的一名学生,最近在学习PINN(Physics-Informed Neural Networks,物理信息神经网络)对管道动力学进行建模研究,PINN由于其对机理与数据的良好兼顾性非常热门,本文是作者对PINN入门研究过程的一个练手小题目,main code也是作者代码是基于pytorch2.6.0写的,发上来供各位参考,欢迎研究管道动力学和PINN的小伙伴一起讨论~

一、PINN简介

1.1PINN原理

PINN是一种结合了深度学习和物理学知识的机器学习模型,它通过将物理定律(通常以偏微分方程的形式)嵌入到神经网络的训练过程中,实现对物理现象的精确模拟和预测。这种模型不仅能够从数据中学习,还能确保预测结果符合物理规律,从而在数据稀缺或噪声较大的情况下,仍然能够进行有效的训练和预测。

1.2PINN步骤

  1. 定义物理问题和相应的物理定律:首先,需要明确所要解决的问题及其背后的物理原理,例如热传导、流体力学等,并将其转化为一组偏微分方程。

  2. 构建神经网络架构:根据问题的复杂程度,设计合适的神经网络结构,作为函数的逼近器。神经网络的输入通常是空间坐标、时间等变量,输出则是预测的物理量。

  3. 编码边界条件:将初始条件、边界值等物理约束编码到神经网络的损失函数中。这通常通过在网络的输入和输出上设置特定点的期望值来实现。

  4. 定义损失函数:损失函数是PINN模型训练中的关键部分,它包含数据误差项和物理信息误差项。数据误差项用于衡量网络预测值与真实数据之间的差异,而物理信息误差项则用于衡量网络预测值是否满足物理定律。通过调整这两项在损失函数中的权重,可以平衡数据拟合和物理约束的重要性。

  5. 训练网络:采用梯度下降或其他优化算法,最小化损失函数,同时更新神经网络的权重。在训练过程中,物理定律作为先验知识,为神经网络提供了额外的指导信息,使其能够更有效地学习和泛化。

  6. 验证结果:检查训练得到的网络解是否满足物理定律和边界条件,并通过可视化和数值测试评估其精度。

 二、问题描述

2.1求解的偏微分方程

2.2偏微分方程的边界条件

                                     

 三、基于pytorch2.6.0框架的代码实现

 3.1初始条件设定

"""西南石油大学  major:chemical engineering   @author:zhujy
简单的一个PINN求解偏微分方程实例"""

import torch
import matplotlib.pyplot as plt


epochs = 10000 # 训练代数

# 画图所需参数
h = 100  # 画图网格密度
N = 1000  # 内点的点数
N1 = 100  # 边界点的点数
N2 = 1000  # PDE的数据点


# 设置初始seed值,确保每次运行结果一样
def setup_seed(seed):
    torch.manual_seed(seed)


# 我的电脑没显卡,只能这样设置了。。。

# 设置随机数种子,设定固定的随机数以便结果可以复现
setup_seed(888888)

 3.2边界条件设定

# 规定边界条件以及对内点采样
def interior(n=N):
    # 内点
    x = torch.rand(n, 1)
    y = torch.rand(n, 1)
    cond = (2 - x ** 2) * torch.exp(-y)
    return x.requires_grad_(True), y.requires_grad_(True), cond


# 希望追踪x,y的参数,并计算它们的梯度

def down_yy(n=N1):
    # 边界条件1
    x = torch.rand(n, 1)
    y = torch.zeros_like(x)  # zeros_like和one_like 都是用全矩阵为0和1
    cond = x ** 2
    return x.requires_grad_(True), y.requires_grad_(True), cond


def up_yy(n=N1):
    # 边界条件2
    x = torch.rand(n, 1)
    y = torch.ones_like(x)
    cond = x ** 2 / torch.e
    return x.requires_grad_(True), y.requires_grad_(True), cond


def down(n=N1):
    # 边界条件3、
    x = torch.rand(n, 1)
    y = torch.zeros_like(x)
    cond = x ** 2
    return x.requires_grad_(True), y.requires_grad_(True), cond


def up(n=N1):
    # 边界条件4
    x = torch.rand(n, 1)
    y = torch.ones_like(x)
    cond = x ** 2 / torch.e
    return x.requires_grad_(True), y.requires_grad_(True), cond


def left(n=N1):
    # 边界条件5
    y = torch.rand(n, 1)
    x = torch.zeros_like(y)
    cond = torch.zeros_like(x)
    return x.requires_grad_(True), y.requires_grad_(True), cond


def right(n=N1):
    # 边界条件6
    y = torch.rand(n, 1)
    x = torch.ones_like(y)
    cond = torch.exp(-y)
    return x.requires_grad_(True), y.requires_grad_(True), cond


# 对内点进行处理
def data_interior(n=N2):
    x = torch.rand(n, 1)
    y = torch.rand(n, 1)
    cond = (x ** 2) * torch.exp(-y)
    return x.requires_grad_(True), y.requires_grad_(True), cond

 3.3搭建PINN神经网络

# 搭建Neural Network
# 神经网络是2个输入,到一个输出,中间有三个隐藏层,每层32个神经元
# MLP是最基础的全连接神经网络(多层感知机)
# 激活函数选择Tanh
class MLP(torch.nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        self.net = torch.nn.Sequential(
            torch.nn.Linear(2, 32),
            torch.nn.Tanh(),
            torch.nn.Linear(32, 32),
            torch.nn.Tanh(),
            torch.nn.Linear(32, 32),
            torch.nn.Tanh(),
            torch.nn.Linear(32, 32),
            torch.nn.Tanh(),
            torch.nn.Linear(32, 1),
        )

    def forward(self, x):
        return self.net(x)

3.4构造损失函数

# 规定损失函数LOSS,LOSS由两部分构成,一部分是数据误差,一部分是方程误差
# 损失函数使用MSE均方方差,每个数据点所产生的方差的平均值

loss = torch.nn.MSELoss()


# 规定梯度,order为阶数,高阶偏导利用嵌套求解
def gradients(u, x, order=1):
    if order == 1:
        return torch.autograd.grad(u, x, grad_outputs=torch.ones_like(u), create_graph=True, only_inputs=True, )[0]
    else:
        return gradients(gradients(u, x), x, order=order - 1)


# 计算PDE的损失
def l_interior(u):
    # 内点的损失函数
    # uxy就是u(x,y)是预测值,cond是真实值
    x, y, cond = interior()
    uxy = u(torch.cat([x, y], dim=1))
    # torch.cat函数是将两个元素按一定规律拼接起来
    return loss(gradients(uxy, x, 2) - gradients(uxy, y, 4), cond)


def l_down_yy(u):
    # 边界条件1的损失函数
    x, y, cond = down_yy()
    uxy = u(torch.cat([x, y], dim=1))
    # torch.cat函数是将两个元素按一定规律拼接起来
    return loss(gradients(uxy, y, 2), cond)


def l_up_yy(u):
    # 边界条件2的损失函数
    x, y, cond = up_yy()
    uxy = u(torch.cat([x, y], dim=1))
    # torch.cat函数是将两个元素按一定规律拼接起来
    return loss(gradients(uxy, y, 2), cond)


def l_down(u):
    # 边界条件3的损失函数
    x, y, cond = down()
    uxy = u(torch.cat([x, y], dim=1))
    # torch.cat函数是将两个元素按一定规律拼接起来
    return loss(uxy, cond)


def l_up(u):
    # 边界条件4的损失函数
    x, y, cond = up()
    uxy = u(torch.cat([x, y], dim=1))
    # torch.cat函数是将两个元素按一定规律拼接起来
    return loss(uxy, cond)


def l_left(u):
    # 边界条件5的损失函数
    x, y, cond = left()
    uxy = u(torch.cat([x, y], dim=1))
    # torch.cat函数是将两个元素按一定规律拼接起来
    return loss(uxy, cond)


def l_right(u):
    # 边界条6的损失函数
    x, y, cond = right()
    uxy = u(torch.cat([x, y], dim=1))
    # torch.cat函数是将两个元素按一定规律拼接起来
    return loss(uxy, cond)


# 构造数据损失
def l_data(u):
    x, y, cond = data_interior()
    uxy = u(torch.cat([x, y], dim=1))
    return loss(uxy, cond)

 3.5进行Training

# 训练Training
u = MLP()
opt = torch.optim.Adam(params=u.parameters(),lr=0.001)
# 初始化模型参数,并将它们传入Adam函数,构造出一个Adam优化器
# 可以通过给定lr的数值来确定学习率
loss_history = []

for i in range(epochs):
    opt.zero_grad()
    # 将这一轮的梯度清零,防止影响下一轮的更新
    LOSS = l_interior(u) + l_up_yy(u) + l_down_yy(u) + l_up(u) + l_down(u) + l_left(u) + l_right(u) + l_data(u)
    LOSS.backward()
    # 反向计算出各参数的梯度
    opt.step()
    # 更新网络中的参数
    loss_history.append(LOSS.item())
    # 记录当前epoch的损失值
    if i % 10 == 0:
        print(i,'LOSS:',LOSS.item())
# 绘制损失曲线
plt.plot(loss_history)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.grid(True)  # 可选,添加网格线以便更好地观察趋势
plt.show()

 3.6预测与验证

# predict
xc = torch.linspace(0, 1, h)
xm, ym = torch.meshgrid(xc, xc)
xx = xm.reshape(-1, 1)
yy = ym.reshape(-1, 1)
xy = torch.cat([xx, yy], dim=1)
u_pred = u(xy)
u_real = xx * xx * torch.exp(-yy)
u_error = torch.abs(u_pred - u_real)
u_pred_fig = u_pred.reshape(h, h)
u_real_fig = u_real.reshape(h, h)
u_error_fig = u_error.reshape(h, h)
print("最大误差为:", float(torch.max(u_error)))
print(xx)
print(xy)

 3.7结果可视化

绘制损失函数收敛曲线等结果图

# 作图
# 图一PINN数值解图
fig = plt.figure(1)
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(xm.detach().numpy(), ym.detach().numpy(), u_pred_fig.detach().numpy())
ax.text2D(0.5, 0.9, 'PINN', transform=ax.transAxes)
plt.show()
fig.savefig("PINN solve.png")

# 做真实解图
fig = plt.figure(2)
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(xm.detach().numpy(), ym.detach().numpy(), u_real_fig.detach().numpy())
ax.text2D(0.5, 0.9, 'real solve', transform=ax.transAxes)
plt.show()
fig.savefig("real solve.png")

# 误差图
fig = plt.figure(3)
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(xm.detach().numpy(), ym.detach().numpy(), u_error_fig.detach().numpy())
ax.text2D(0.5, 0.9, 'abs error', transform=ax.transAxes)
plt.show()
fig.savefig("abs error.png")

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值