FluidX3Dのチュートリアル

# この記事について

少し話題のLBM計算用ソフトウェアFluidX3Dを自宅のPCで触ってみました。
ベンチマークとチュートリアルをやってみたので、そのときのメモです。

# FluidX3d
![](https://camo.githubusercontent.com/1a0504f4d2295ecca25520ad59d3e53e48a9ddc0f6b5b83fe147eeaa05d105d3/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f6f3354504e31343248784d2f6d617872657364656661756c742e6a7067)
> The fastest and most memory efficient lattice Boltzmann CFD software, running on all GPUs via OpenCL. Free for non-commercial use.

FluidX3DはLBM（格子ボルツマン法）を用いたCFDソフトウェアで、GPUを用いた高速演算ができます。  
非商用、非軍事の用途には自由に使えるよう、以下のリポジトリでソースコードが公開されています。

https://github.com/ProjectPhysX/FluidX3D

# 環境
今回は以下の環境で試しました
|||
|---|---|
| CPU | Intel core i5 13400F |
| GPU | NVIDIA Geforce RTX 3060 Ti 8GB|
| RAM | 32 GB |
| OS  | Windows 11 Home |


# インストール
公式のドキュメントに従えば問題なくできました。
ソースコードをリポジトリからクローンしてきて、Visual Studioでコンパイル、実行するようです。
 


https://github.com/ProjectPhysX/FluidX3D/blob/master/DOCUMENTATION.md

> 1. Download
Download and unzip the source code, or clone with git clone https://github.com/ProjectPhysX/FluidX3D.git.
> 2. Compiling the Source Code
There is no "installation" of the FluidX3D software. Instead, you have to compile the source code yourself.
I have made this as easy as possible and this documentation will guide you through it. Nontheless, some basic programming experience with C++ would be good, as all the setup scripts are written in C++.
First, compile the code as-is; this is the standard FP32 benchmark test case. By default, the fastest installed GPU will be selected automatically. Compile time is about 10 seconds.
> - Windows
> Download and install Visual Studio Community. In Visual Studio Installer, add:
>   - Desktop development with C++
>   - MSVC v142
>   - Windows 10 SDK
> 
> - Open FluidX3D.sln in Visual Studio Community.
> - Compile and run by clicking the ► Local Windows Debugger button.
> - To select a specific GPU, open Windows CMD in the FluidX3D folder (type cmd in File Explorer in the directory field and press Enter), then run bin\FluidX3D.exe 0 to select device 0. You can also select multiple GPUs with bin\FluidX3D.exe 0 1 3 6 if the setup is configured as multi-GPU.

Windows  10 SDKの代わりに、Windows 11 SDKでも問題なく動作しました。

# ベンチマーク
上記の手順でコンパイルすると、ベンチマークが走り出します。
LBMにおけるベンチマークの指標としてはMLUPs（Million Lattice Updates per Second）が使われることが多いようです。

https://lbm.mcfernandes.com/home/what-does-our-code-do


> The performance of Lattice Boltzmann simulations is typically measured in terms of MLUPS, or "Million Lattice Updates per Second." This means if your lattice has 1 million lattice sites and your simulation runs at a speed of 1 MLUPS, your lattice is updated once per second.


計算が終了すると、以下のようにPeakのMLUPsが表示されます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160678/3d7bb603-819f-8529-fbb0-a427c95f61a2.png)

https://github.com/ProjectPhysX/FluidX3D
> 🟢 GeForce RTX 3060 Ti	16.49	8	448	2644 (90%)	5129 (88%)	4718 (81%) 

のページにあるベンチマーク結果（2644 MLUPs）と比較すると、良好のようです。
CPUだとせいぜい数百くらいなので、そこそこでのGPUでもかなり高速化できていることがわかります。
数千万の格子点の計算が手元のPCであっという間に計算できるのは夢があります。
また、
> The fastest and most memory efficient lattice Boltzmann CFD software

というだけあって、GPUメモリの使用量も非常に少ないのがいいです。

なお、計算実行中はGPUの使用率が99 %となりました。CPUはそこまで使われてませんでした。



# チュートリアル（遠心ファン）

以下の動画で解説されているチュートリアルをやってみます。
チュートリアルに必要なコードはすでに用意されていて、それをuncommentしたり、いらないものをコメントアウトしてやればOKです。

https://www.youtube.com/watch?v=mhacLfz92h0

## 準備
### setup.cppの編集
計算に必要な設定は`setup.cpp`に書かれています。
まずはベンチマークの設定をコメントアウトします。
- 以下のような部分があると思うので、`void`の前に`/*`を入れてコメントアウトします。
- また`#ifdef BENCHMARK`と`#endif //BENCHMARK`の行を削除します。

```cpp
#ifdef BENCHMARK
void main_setup() { // benchmark; required extensions in defines.hpp: BENCHMARK, optionally FP16S or FP16C
	// ################################################################## define simulation box size, viscosity and volume force ###################################################################
	uint mlups = 0u; {

		//LBM lbm( 32u,  32u,  32u, 1.0f);
		//LBM lbm( 64u,  64u,  64u, 1.0f);
		//LBM lbm(128u, 128u, 128u, 1.0f);
		LBM lbm(256u, 256u, 256u, 1.0f); // default
		//LBM lbm(384u, 384u, 384u, 1.0f);
		//LBM lbm(512u, 512u, 512u, 1.0f);

		//const uint memory = 1488u; // memory occupation in MB (for multi-GPU benchmarks: make this close to as large as the GPU's VRAM capacity)
		//const uint3 lbm_N = resolution(float3(1.0f, 1.0f, 1.0f), memory); // input: simulation box aspect ratio and VRAM occupation in MB, output: grid resolution
		//LBM lbm(1u*lbm_N.x, 1u*lbm_N.y, 1u*lbm_N.z, 1u, 1u, 1u, 1.0f); // 1 GPU
		//LBM lbm(2u*lbm_N.x, 1u*lbm_N.y, 1u*lbm_N.z, 2u, 1u, 1u, 1.0f); // 2 GPUs
		//LBM lbm(2u*lbm_N.x, 2u*lbm_N.y, 1u*lbm_N.z, 2u, 2u, 1u, 1.0f); // 4 GPUs
		//LBM lbm(2u*lbm_N.x, 2u*lbm_N.y, 2u*lbm_N.z, 2u, 2u, 2u, 1.0f); // 8 GPUs

		// #########################################################################################################################################################################################
		for(uint i=0u; i<1000u; i++) {
			lbm.run(10u);
			mlups = max(mlups, to_uint((double)lbm.get_N()*1E-6/info.runtime_lbm_timestep_smooth));
		}
	} // make lbm object go out of scope to free its memory
	print_info("Peak MLUPs/s = "+to_string(mlups));
#if defined(_WIN32)
	wait();
#endif // Windows
} /**/
#endif // BENCHMARK
```

次に、radial fanのチュートリアル用の`main_setup()`を検索して探します。
みつかったらuncommentして、有効化します。

```cpp
void main_setup() { // radial fan; required extensions in defines.hpp: FP16S, MOVING_BOUNDARIES, SUBGRID, INTERACTIVE_GRAPHICS or GRAPHICS
	// ################################################################## define simulation box size, viscosity and volume force ###################################################################
	const uint3 lbm_N = resolution(float3(3.0f, 3.0f, 1.0f), 2000u); // input: simulation box aspect ratio and VRAM occupation in MB, output: grid resolution
	const float lbm_Re = 100000.0f;
	const float lbm_u = 0.12f;
	const uint lbm_T = 5000u;
	const uint lbm_dt = 10u;
	LBM lbm(lbm_N, units.nu_from_Re(lbm_Re, (float)lbm_N.x, lbm_u));
	// ###################################################################################### define geometry ######################################################################################
	// const float radius = 0.25f*(float)lbm_N.x;
	const float radius = 0.05f * (float)lbm_N.x;
	const float3 center = float3(lbm.center().x, lbm.center().y, 0.36f*radius);
	const float lbm_omega=lbm_u/radius, lbm_domega=lbm_omega*lbm_dt;
	Mesh* mesh = read_stl(get_exe_path()+"../stl/FAN_Solid_Bottom.stl", lbm.size(), center, 2.0f*radius); // https://www.thingiverse.com/thing:6113/files
	const uint Nx=lbm.get_Nx(), Ny=lbm.get_Ny(), Nz=lbm.get_Nz(); parallel_for(lbm.get_N(), [&](ulong n) { uint x=0u, y=0u, z=0u; lbm.coordinates(n, x, y, z);
		if(x==0u||x==Nx-1u||y==0u||y==Ny-1u||z==0u) lbm.flags[n] = TYPE_S; // all non periodic
	}); // ####################################################################### run simulation, export images and data ##########################################################################
	lbm.graphics.visualization_modes = VIS_FLAG_LATTICE|VIS_FLAG_SURFACE|VIS_Q_CRITERION;
	lbm.run(0u); // initialize simulation
	while(lbm.get_t()<lbm_T) { // main simulation loop
		lbm.voxelize_mesh_on_device(mesh, TYPE_S, center, float3(0.0f), float3(0.0f, 0.0f, lbm_omega));
		lbm.run(lbm_dt);
		mesh->rotate(float3x3(float3(0.0f, 0.0f, 1.0f), lbm_domega)); // rotate mesh
#if defined(GRAPHICS) && !defined(INTERACTIVE_GRAPHICS)
		if(lbm.graphics.next_frame(lbm_T, 30.0f)) {
			lbm.graphics.set_camera_free(float3(0.353512f*(float)Nx, -0.150326f*(float)Ny, 1.643939f*(float)Nz), -25.0f, 61.0f, 100.0f);
			lbm.graphics.write_frame();
		}
#endif // GRAPHICS && !INTERACTIVE_GRAPHICS
	}
	lbm.u.write_device_to_vtk();
} /**/
```

### defines.hppの編集
次に`defines.hpp`を編集します。このファイルでは必要なモジュール群を定義することができます。
必要なモジュールは、`setup.cpp`のコメントに書かれています。
```
required extensions in defines.hpp: FP16S, MOVING_BOUNDARIES, SUBGRID, INTERACTIVE_GRAPHICS or GRAPHICS
```
- FP16S
- MOVING_BOUNDARYES
- SUBGRID
- INTERACTIVE_GRAPHICS or  GRAPHICS

### ジオメトリ
必要なジオメトリ（遠心ファンのSTLファイル）をコメントアウトで記されているこちらのサイトからダウンロードします。

https://www.thingiverse.com/thing:6113/files

ダウンロードして解凍したファイルを、`stl`フォルダを作成してその中にコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160678/bd912b18-7102-3fd1-55ec-0a30c2219c93.png)

## 実行
ここまで準備できたら、あとは実行するだけです。
ベンチマークと同様、Visual Studioの`ローカルWindowsデバッガー`のボタンを押すと、コンパイルと計算が開始します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160678/caad78a0-d2d2-2062-b117-06df065cd8b7.png)


`INTERACTIVE_GRAPHICS` をONにしている場合は、全画面でアプリケーションが表示されます。
キーボードの`p`を押すと、計算が開始され、リアルタイムに計算結果が表示されます。
マウスでカメラの位置などを変更することができます。
デフォルトではＱ-Criterionが表示されているかと思います。
またキーボードの`h`を押すとヘルプが表示され、表示する物理量を変更したりすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160678/8b2b47e4-e2ea-3af0-8a45-f43c11b0463f.png)


`GRAPHICS` をONにしている場合は計算途中の画像が、`bin/export`フォルダに出力されます。
`GRAPHICS`モードのほうがGPUの負荷は小さそうな気がします。

## おまけ
### グリッドサイズの変更
このチュートリアルでは、グリッドサイズは使用するメモリ量から決まるようです。
```cpp
const uint3 lbm_N = resolution(float3(3.0f, 3.0f, 1.0f), 1000u); // input: simulation box aspect ratio and VRAM occupation in MB, output: grid resolution
```
の行で、メモリサイズ（`1000u`）の部分を大きくすると、その分グリッドが細かくなりました。

### 物体サイズの変更
物体サイズは、以下の行の`0.25f`の部分を変えてやると変わりました。

```cpp
const float radius = 0.25f * (float)lbm_N.x;
```

### VTKファイルの出力
while文の後、つまり計算のイテレーションが終了した後、に以下の一文を入れると、計算結果がvtkファイルをして出力されました。
```cpp
lbm.u.write_device_to_vtk();
```


# さいごに
もう少しいろいろ触ってみて、また追記しようと思います。