---
# System prepended metadata

title: nVidia Modulus Tutorial – Lid Driven Cavity Flow讀後心得
tags: [Modulus, AI]

---

nVidia Modulus Tutorial – Lid Driven Cavity Flow讀後心得
===

學習目標
---
- 使用Modulus中Geometry module產生2D Geometry
- 設定BC-Boundary Conditions
- 設定待解之PDF( Flow Equation)
- Loss function和network調教
- 使用Modulus作後處理

問題描述
---
![Modulus-1](https://hackmd.io/_uploads/rkB2Xm-vT.png)

上邊界以1m/s速度朝x方向移動，其餘邊界皆為靜止。雷諾數(Reynold number)設定為10。

使用案例設定
---
![Modulus-2](https://hackmd.io/_uploads/BJE6QX-DT.png)

以PINNs為基礎求解PDE的步驟：設定幾何形狀設定物理現象所對應之PDE產生neural network solver。如果對應到Modulus，所使用的module與class分別為：CSG/STL(產生point cloud)、TrainDomain class與Solver class。問題與Modulus的對應如上圖所示。

使用Modulus解決LDC包含以下幾個步驟：
Step 1：Importing the required packages
```
from sympy import Symbol, Eq, Abs

from modulus.solver import Solver
from modulus.dataset import TrainDomain, ValidationDomain
from modulus.data import Validation
from modulus.sympy_utils.geometry_2d import Rectangle
from modulus.csv_utils.csv_rw import csv_to_dict
from modulus.PDES import NaviewStokes
from modulus.controller impotr ModulusController
```
 
Step 2：產生幾何形狀
```
# make geometry
height = 0.1
width = 0.1
vel = 1.0

# define geometry
rec = Rectangle((-width / 2, -height / 2), (width / 2, height / 2))
geo = rec
```
 
可使用sample_boundary產生sample point，並使用var_to_vtk產生Paraview可顯示之.vtu檔。
```
samples = geo.sample_boundary(1000)
var_to_vtk(samples, './geo')
```
 
Step 3：定義邊界條件與設定LDC所對應之PDE
```
# define sympy varaibles to parametize domain curves
x, y = Symbol("x"), Symbol("y")

class LDCTrain(TRainDomain):
  define __init__(self, **config)
  super(LDCTrain, self)._init_()
  
  #top wall
  topwall = geo.boundary_bc(outvar_sympy={'u': vel, 'v': 0},
                        batch_size_per_area=10000,
                        lambda_sympy={'lambda_u': 1.0 -20 * Abs(x), # weight edges to be zero
                                      'lambda_v': 1.0},
                        criteria=Eq(y, height / 2))
   self.add(topwall, name="Topwall")
   
   # no slip
   bottomwall = geo.boundary_bc(outvar_sympy={'u': 0, 'v': 0},
                                batch_size_per_area=10000,
                                criteria-y < height / 2)
   self. add(bottomwall, name="NoSlip")

   # interior
   interior = geo.interior_bc(outvar_sympy={'continuity': 0, 'momentum_x': 0, 'momentum_y': 0},
                              bounds={x: (-width / 2, width / 2),
                                      y: (-height i 2, height 1'2)},
                              lambda_sympy={'lambda_continuity': geo.sdf,
                                            'lambda_momentum_x': geo.sdf,
                                            'lambda_momentum_y': geo.sdf},
                              batch_size_per_area=400000)
    self.add(interior, name="Interior")

```
 
Modulus solver的training data的來源為系統之BC與PDE，可由TrainDomain定義。另外，使用solver求解必須定義相關的loss function。
	Boundary Condition：使用boundary_bc函數設定。
	Equations to solve：考慮2D LDC，我們必須求解continuity equation與x,y方向的momentum equation。初始值為零表示不存在forcing/source項目。
Lambda_sympy表示loss function weighting。下圖可知在左上角與右上角有最大的不連續性，利用SDF(Signed Distance Field)設定可獲得較快的收斂與避免在邊界的不連續性。
 
![Modulus-3](https://hackmd.io/_uploads/H12RQ7Wvp.png)

Step 4：產生驗證資料

```
# validation data
mapping = {'Points:0': 'x', 'Points:1': 'y', 'U:0': 'u', 'U:1': 'v', 'p': 'p'}
openfoam_var = csv_to_dict('openfoam/cavity_uniformVe10.csv, mapping)
openfoam_var['x'] t= -width / 2 # center OpenFoam data
openfoam_var['y'] t= -height / 2 # center OpenFoam data
openfoam_invar_numpy = {key: value for key, value in openfoam_var.items() if key in ['x', 'y']}
openfoam_outvar_numpy = {key: value for key, value in openfoam_var.items() if key in ['u', 'v']}
class LDCVal (ValidationDomain):
  def_init_(self, **config):
  super(LDCVal, self).__init_
  val = Validation. from_numpy(openfoam_invar_numpy, openfoam_outvar_numpy)
  self.add(val, name='Val')
```
 
Modulus solver唯一基於物理條件之neural network solver，因此並不需要training data，但我們可以將CFD或其他solver的結果匯入為validating data。我們將OpenFOAM的計算結果以.csv方式匯入，視為solver的validation domain。

Step 5：建立Neural Network Solver
```
class LDCSolver (Solver):
  train_domain = LDCTrain
  val_domain = LDCVal

  def _init_(self, **config):
    super (LDCSolver, self).__init__(**config)
    self.equations = NavierStokes(nu=0.01, rho=1.0, dim=2,
                                  time=False).make_node()
    flow_net = self.arch.make_node(name='flow_net',
                                   inputs=['x', 'y'],
                                   outputs=['u', 'v', 'p'])
    self.nets = [flow_net]

    @classmethod
    def update_defaults(cls, defaults):
      defaults.update ({
          'network_dir': './network_checkpoint_ldc_2d',
          'decay_steps': 4000,
          max_steps':400000
      })
      
if __name__ == '__main__':
  ctr = ModulusController(LDCSolver)
  ctr.run()
```
solver繼承Modulus內建的Solver Class，需要設定train_domain與val_domain。
LDC所遵循的PDE為Navier-Stokes方程式，Modulus已有內建，需要給定的參數為：dim=2(2D)、ν(kinematic viscosity)=0.01 m2/sec與ρ(密度)=1.0kg/m31.


$$
Re = {ρVD\over u} = {νD\over \gamma} = {1.0 \times 0.1\over 0.01} = 10 
$$

Modulus預設的network包含6個hidden layer，每一個layer包含512個node。指定network的輸入為(x,y)，輸出為(u,v,p)

Step 6：執行Neural Network Solver
```
python ldc_2d.py
```




