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在求解过程中,需要保证配点上满足:
方程约束,也即方程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
分别是以及。
损失
在计算了导数之后,要定义损失。本算例定义两类损失:
方程损失; 边界损失;
首先看方程损失。下列代码定义方程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预测的速度进行对比,如下图: