不到100行,通过PINN求解CFD槽道流,免费源码随便抄

学术   2024-07-12 12:27   法国  

ML: PINN教程实例之1D槽道流

本教材通过OpenFOAM环境直挂libtorch机器学习库,调用PINN来对其进行求解。libtorch是PyTorch的C++ API。可以理解为是PyTorch的C++版本。

CFD中的一维管道流可以通过1D传输方程来进行。首先回顾以下基本的控制方程:

在一个充分发展的管道里面,速度型线的分布可以用上面这个方程来表示。东岳流体网的[boundaryFoam解析]里曾讨论过,若已知的值,则可以获得的解。

本文将其假定为。因此这个方程可以理解为一个泊松方程,上边界为零法向梯度,下边界的(固定值边界条件)。同时假定粘度为定值:。因此,求解方程可以写为:

至此,需要给定到PINN的参数即为上面这个方程,以及无滑移壁面边界条件。

PINN与有限体积法FVM PINN与FVM是完全两条路。二者都是求解PDE的方法。FVM是从数学上严格推导出离散的数值解。PINN更像是通过神经网络去拟合出一个数值解。

PINN网格划分

在PINN里面同样需要进行网格划分。然而PINN领域的人一般不将其称之为网格,而叫做配点(Collocation Points)。PINN在求解过程中,需要保证配点上满足:

  1. 方程约束,也即方程2;
  2. 边界约束,也即本文给定的速度为0的边界条件;

在OpenFOAM libtorch环境下,可以通过下面几行来生成网格:

// Computational domain
double dy = 0.00125;
auto init = torch::full({}, 1.0);
auto mesh = 
    torch::arange(-0.05, 0, dy, torch::requires_grad()).unsqueeze(1);
int cellN = static_cast<int>(0.05/dy);

上述几行命令生成的网格mesh为从-0.05至0,网格单元大小为0.00125dy,网格数量为0.05/0.00125cellN

有关OpenFOAM libtorch环境下的tensor使用,请参考东岳流体网的 [ML: libtorch张量]

机器学习里面的tensor与计算物理里面的tensor不太一样。一个很明显的区别就是,计算物理里面的tensor大部分情况下是正方形的,比如剪切应力是的。但是机器学习里面的tensor什么样的都有,很少有的。详情请参考 计算物理的tensor,机器学习的tensor,两个一样么?

神经网络搭建

在OpenFOAM libtorch环境下,可以通过下述代码搭建神经网络:

class NN
:
    public torch::nn::Module 
{
    torch::nn::Sequential net_;

public:

    NN()
    {
        net_ = register_module
        (
            "net"
            torch::nn::Sequential
            (
                torch::nn::Linear(1,8),
                torch::nn::Tanh(),
                torch::nn::Linear(8,1)
            )
        );
    }

    auto forward(torch::Tensor x)
    {
        return net_->forward(x);
    }
};

在这里主要的是,输入层为1,隐藏层具有8个神经元,输出层为1。其可以理解为,输入一个网格坐标,就会输出一个速度值。在这里我们使用的是Tanh激活函数。

超参数以及自动微分

PINN在训练网络的时候,需要指定梯度下降的方法。这部分理论请参考东岳流体网的 [ML: 手算反向传播与自动微分]。具体的,本算例训练10000次,采用AdamW梯度更新方法。学习率为0.001。损失计算的方法采用均方差最小化。在OpenFOAM libtorch环境下的代码可以写为

int iter = 1;
int IterationNum = 100000; //训练10000次
double learning_rate = 0.001; //学习率为0.001
auto crit = torch::nn::MSELoss(); //损失函数均方差最小化
auto model = std::make_shared<NN>();
auto opti = 
    std::make_shared<torch::optim::AdamW> //AdamW梯度更新方法
    (
        model->parameters(), 
        torch::optim::AdamWOptions(learning_rate)
    );

在训练的过程中,每次都需要将网格信息输入到神经网络,然后其会预测出一个速度值,这可以通过下述代码来实现:

auto upred = model->forward(mesh);

不同于FVM,在PINN中,导数的计算是通过自动微分来实现。这部分理论请参考东岳流体网的 [ML: 手算反向传播与自动微分]。在OpenFOAM libtorch环境下的代码可以写为

auto dudy = 
    torch::autograd::grad
    (
        {upred},
        {mesh},
        {torch::ones_like(upred)},
        true,
        true
    )[0];
auto dudyy = 
    torch::autograd::grad
    (
        {dudy},
        {mesh},
        {torch::ones_like(upred)},
        true,
        true
    )[0];

上面的代码声明的dudy以及dudyy分别是以及

损失

在计算了导数之后,要定义损失。本算例定义两类损失:

  1. 方程损失;
  2. 边界损失;

首先看方程损失。下列代码定义方程2中的源项:

auto dpdxByNu = torch::full({cellN}, 1.2e-05/1e-8);

结合刚才计算的dudyy,可以对比二者的差值(理想情况下需要是)。

auto loss_pde = crit(-dudyy.reshape({cellN}), dpdxByNu);

下面定义边界损失,首先定义下边界处的速度值

auto ubottom = torch::full_like(upred[0], 0.0375);

我们希望预测的upred在第0个网格点上的速度,与给定的0.0375无差异,那么损失就可以定义为:

auto loss_bottom = crit(upred[0], ubottom);

类似的,我们可以定义上边界零法向梯度的损失:

auto dudyTop = dudy[cellN - 1][0];
auto loss_top = crit(dudyTop, 0.0*dudyTop);

在有了方程损失以及边界损失之后,我们可以计算总损失:

auto loss = loss_pde + loss_bd;

计算总损失后,可以进行反向传播。在迭代的过程中,总体思想就是通过预测的upred,来计算各项损失,最终要求损失最小。在这个情况下,即同时满足方程损失以及边界损失。也即满足偏微分方程的解。

运行算例

请首先按照CFD中文网的 [OpenFOAM libtorch tutorial step by step] 来配置OpenFOAM libtorch环境。也可以在东岳流体网下载配置好的 [虚拟机]

将源代码直接创立一个.C文件后,即可通过wmake来编译。编译后可以进行训练。

最终求解器会输出相关的信息,如dudyy等。在这里我们将PINN预测的速度与OpenFOAM的boundaryFoam预测的速度进行对比,如下图:

阅读原文 免费下载源代码

CFD界
更多的原创CFD开脑洞算法
 最新文章