基于MindFlow定义符号化偏微分方程

下载Notebook下载样例代码查看源文件

偏微分方程(PDE)在工程应用中发挥着重要作用,大多数控制系统和人造复杂系统的物理学都是有PDE描述的。本文介绍如何基于MindFlow使用sympy定义PDE,构建基于物理神经网络(Physics-Informed Neural Networks,PINNs)的问题求解模型。利用 mindflow.pde.PDEWithLoss ,可以很容易以符号形式描绘偏微分方程,并计算所有方程的损失。这使得方程的描述简洁易懂,易于扩展。用户可继承 mindflow.pde.PDEWithLoss 实现自定义偏微分方程。

偏微分方程样例:Navier-Stokes equation

Navier-Stokes方程,被称为 N-S 方程,是流体力学领域的经典偏微分方程。在粘性不可压缩的情况下,无量纲 N-S 方程具有以下形式:

\[\frac{\partial u}{\partial x} + \frac{\partial v}{\partial y} = 0\]
\[\frac{\partial u} {\partial t} + u \frac{\partial u}{\partial x} + v \frac{\partial u}{\partial y} + \frac{\partial p}{\partial x} - \frac{1} {Re} (\frac{\partial^2u}{\partial x^2} + \frac{\partial^2u}{\partial y^2}) = 0\]
\[\frac{\partial v} {\partial t} + u \frac{\partial v}{\partial x} + v \frac{\partial v}{\partial y} + \frac{\partial p}{\partial y} - \frac{1} {Re} (\frac{\partial^2v}{\partial x^2} + \frac{\partial^2v}{\partial y^2}) = 0\]

其中 Re 表示雷诺数。

符号声明

[1]:
from sympy import symbols, Function

x, y, t = symbols('x y t')
u = Function('u')(x, y, t)
v = Function('v')(x, y, t)
p = Function('p')(x, y, t)

# independent variables
in_vars = [x, y, t]
print(in_vars)

# dependent variables
out_vars = [u, v, p]
print(out_vars)
[x, y, t]
[u(x, y, t), v(x, y, t), p(x, y, t)]

符号形式的偏微分方程

使用上述符号声明定义偏微分方程:

[2]:
import numpy as np
from sympy import diff

控制方程

[3]:
# Consider Reynolds number is 100
re = 100
number = np.float32(1.0 / re)

# X Momemtum
momentum_x = u.diff(t) + u * u.diff(x) + v * u.diff(y) + p.diff(x) - number*(diff(u, (x, 2)) + diff(u, (y, 2)))
print(momentum_x)

# Y Momemtum
momentum_y = v.diff(t) + u * v.diff(x) + v * v.diff(y) + p.diff(y) - number*(diff(v, (x, 2)) + diff(v, (y, 2)))
print(momentum_y)

# continuty
continuty = u.diff(x) + v.diff(y)
print(continuty)
u(x, y, t)*Derivative(u(x, y, t), x) + v(x, y, t)*Derivative(u(x, y, t), y) + Derivative(p(x, y, t), x) + Derivative(u(x, y, t), t) - 0.00999999977648258*Derivative(u(x, y, t), (x, 2)) - 0.00999999977648258*Derivative(u(x, y, t), (y, 2))
u(x, y, t)*Derivative(v(x, y, t), x) + v(x, y, t)*Derivative(v(x, y, t), y) + Derivative(p(x, y, t), y) + Derivative(v(x, y, t), t) - 0.00999999977648258*Derivative(v(x, y, t), (x, 2)) - 0.00999999977648258*Derivative(v(x, y, t), (y, 2))
Derivative(u(x, y, t), x) + Derivative(v(x, y, t), y)

边界条件

[4]:
bc_u = u
print(bc_u)

bc_v = v
print(bc_v)
u(x, y, t)
v(x, y, t)

初始条件

[5]:
ic_u = u
print(bc_u)

ic_v = v
print(ic_v)

ic_p = p
print(ic_p)
u(x, y, t)
v(x, y, t)
p(x, y, t)

问题建模样例

下面的 CylinderFlow 定义了二维非定常圆柱绕流问题。具体来说,它包括上面定义的三个部分:控制方程、初始条件和边界条件。

NavierStokes 基类,定义了输入和输出变量,以及控制方程。

用户可以通过重写 NavierStokes 类定义其他控制方程。

[6]:
from mindspore import nn
from mindflow.pde import PDEWithLoss


class NavierStokes(PDEWithLoss):
    def __init__(self, model, re=100, loss_fn=nn.MSELoss()):
        self.number = np.float32(1.0 / re)
        self.x, self.y, self.t = symbols('x y t')
        self.u = Function('u')(self.x, self.y, self.t)
        self.v = Function('v')(self.x, self.y, self.t)
        self.p = Function('p')(self.x, self.y, self.t)
        self.in_vars = [self.x, self.y, self.t]
        self.out_vars = [self.u, self.v, self.p]
        super(NavierStokes, self).__init__(model, self.in_vars, self.out_vars)
        self.loss_fn = loss_fn

    def pde(self):
        momentum_x = self.u.diff(self.t) + self.u * self.u.diff(self.x) + self.v * self.u.diff(self.y) +\
            self.p.diff(self.x) - self.number * (diff(self.u, (self.x, 2)) + diff(self.u, (self.y, 2)))
        momentum_y = self.v.diff(self.t) + self.u * self.v.diff(self.x) + self.v * self.v.diff(self.y) +\
            self.p.diff(self.y) - self.number * (diff(self.v, (self.x, 2)) + diff(self.v, (self.y, 2)))
        continuty = self.u.diff(self.x) + self.v.diff(self.y)

        equations = {"momentum_x": momentum_x, "momentum_y": momentum_y, "continuty": continuty}
        return equations

下面,我们使用 NavierStokes 基类定义初始条件和边界条件,以及损失函数。

[7]:
from mindspore import nn, ops, Tensor
from mindspore import dtype as mstype
from mindflow.pde import PDEWithLoss, NavierStokes, sympy_to_mindspore


class CylinderFlow(NavierStokes):
    def __init__(self, model, re=100, loss_fn=nn.MSELoss()):
        super(CylinderFlow, self).__init__(model, re=re, loss_fn=loss_fn)
        self.ic_nodes = sympy_to_mindspore(self.ic(), self.in_vars, self.out_vars)
        self.bc_nodes = sympy_to_mindspore(self.bc(), self.in_vars, self.out_vars)

    def bc(self):
        bc_u = self.u
        bc_v = self.v
        equations = {"bc_u": bc_u, "bc_v": bc_v}
        return equations

    def ic(self):
        ic_u = self.u
        ic_v = self.v
        ic_p = self.p
        equations = {"ic_u": ic_u, "ic_v": ic_v, "ic_p": ic_p}
        return equations

    def get_loss(self, pde_data, bc_data, bc_label, ic_data, ic_label):
        pde_res = self.parse_node(self.pde_nodes, inputs=pde_data)
        pde_residual = ops.Concat(1)(pde_res)
        pde_loss = self.loss_fn(pde_residual, Tensor(np.array([0.0]).astype(np.float32), mstype.float32))

        ic_res = self.parse_node(self.ic_nodes, inputs=ic_data)
        ic_residual = ops.Concat(1)(ic_res)
        ic_loss = self.loss_fn(ic_residual, ic_label)

        bc_res = self.parse_node(self.bc_nodes, inputs=bc_data)
        bc_residual = ops.Concat(1)(bc_res)
        bc_loss = self.loss_fn(bc_residual, bc_label)

        return pde_loss + ic_loss + bc_loss

详细案例参见 基于PINNs关于圆柱绕流的Navier-Stokes equation求解

Neumann边界条件定义样例

在数学中,Neumann边界条件也被称为常微分方程或偏微分方程的“第二类边界条件”。Neumann边界条件指定微分方程解边界处的微分。

下述 Poisson2D 问题定义了Dirichlet边界条件(bc_outer)和Neumann边界条件(bc_inner)。

[8]:
import sympy
from mindflow.pde import Poisson

class Poisson2D(Poisson):
    def __init__(self, model, loss_fn=nn.MSELoss()):
        super(Poisson2D, self).__init__(model, loss_fn=loss_fn)
        self.bc_outer_nodes = sympy_to_mindspore(self.bc_outer(), self.in_vars, self.out_vars)
        self.bc_inner_nodes = sympy_to_mindspore(self.bc_inner(), self.in_vars, self.out_vars)

    def bc_outer(self):
        bc_outer_eq = self.u
        equations = {"bc_outer": bc_outer_eq}
        return equations

    def bc_inner(self):
        bc_inner_eq = sympy.Derivative(self.u, self.normal) - 0.5
        equations = {"bc_inner": bc_inner_eq}
        return equations

    def get_loss(self, pde_data, bc_outer_data, bc_inner_data, bc_inner_normal):
        pde_res = self.parse_node(self.pde_nodes, inputs=pde_data)
        pde_loss = self.loss_fn(pde_res[0], Tensor(np.array([0.0]), mstype.float32))

        bc_inner_res = self.parse_node(self.bc_inner_nodes, inputs=bc_inner_data, norm=bc_inner_normal)
        bc_inner_loss = self.loss_fn(bc_inner_res[0], Tensor(np.array([0.0]), mstype.float32))

        bc_outer_res = self.parse_node(self.bc_outer_nodes, inputs=bc_outer_data)
        bc_outer_loss = self.loss_fn(bc_outer_res[0], Tensor(np.array([0.0]), mstype.float32))

        return pde_loss + bc_inner_loss + bc_outer_loss

详细案例参见 基于PINNs关于作用于圆环的Poisson equation求解