2025/09/11公開

自分で例題解析を作成 - タケコプター

前回はFluidX3dをコンパイルして例題を1つ流してみました。
今回は前回の例題Radial funを参考に、独自の解析を行ってみます。
Radial funのように回転する羽根によって発生する渦を見る例題なら、3Dモデルの差し替えとパラメータの調整でいけそうです。
という事で、ドラえもんのタケコプターの解析を行ってみます。


3Dモデルの準備

タケコプター部分のみでは少し寂しいので、ドラえもん本体も解析に加えてみたいと思います。
ドラえもん本体の3Dモデルは適当な無料で利用できるサイトからダウンロードしました。

問題は羽根の部分です。
FluidX3dでの解析で用いる際に少し問題が生じるので羽根の部分は自作することにしました。
単純な形状で良いので3D Builderで作成しました。簡単にできるだろうと思いきや、難しかったです。
グリッドの解像度という問題にぶつかりました。
何も考えずに結構薄い羽根として作成すると、粗い解析グリッドでは形状が認識されないのです。
グリッドサイズはGPUのメモリサイズで上限が決まるのでそれほど細かくできません。
結局、かなり分厚い羽根とすることで、解析グリッド上で羽根の形状が認識されました。

main_setup()内の設定

解析の設定はsetup.cpp内の関数main_setup()内で行います。
Radial funの解析をコピーして、必要箇所を修正していきます。
関数main_setup()を以下に示します。

------

void main_setup() { // pera test; 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), 181u); // input: simulation box aspect ratio and VRAM occupation in MB, output: grid resolution
	const uint3 lbm_N = resolution(float3(2.3f, 2.3f, 2.0f), 3000u);
	const float lbm_Re = 100000.0f;
	const float lbm_u = 0.1f * 1.0f;
	const ulong lbm_T = 480000ull; //48000ull;
	const ulong lbm_dt = 10ull;
	LBM lbm(lbm_N, 1u, 1u, 1u, units.nu_from_Re(lbm_Re, (float)lbm_N.x, lbm_u));
	// ### define geometry ###
	const float radius = 0.25f * (float)lbm_N.x;
	const float3 center = float3(lbm.center().x, lbm.center().y, lbm.center().z);
	const float lbm_omega = lbm_u / radius, lbm_domega = lbm_omega * lbm_dt;
	Mesh* mesh = read_stl(get_exe_path() + "../stl/takekoputa-002.stl", lbm.size(), center, 2.0f * radius);
	
	mesh->translate(float3(0.0f, 0.0f, radius));
	const float3x3 rotation = float3x3(float3(1, 0, 0), radians(90.0f));
	Mesh* body = read_stl(get_exe_path() + "../stl/86Duino-Doraemon.stl", lbm.size(), center, rotation, 2.5f * radius);
	body->translate(float3(0.0f, 0.0f, -radius*0.4f));
	
	lbm.voxelize_mesh_on_device(body);
	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 (lbm.flags[n] != TYPE_S) lbm.u.z[n] = -lbm_u * 1.0f; //0.3f
		if (x == 0u || x == Nx - 1u || y == 0u || y == Ny - 1u || z == 0u || z == Nz - 1u) lbm.flags[n] = TYPE_E; // all other simulation box boundaries are inflow/outflow
	// ### run simulation, export images and data ###
	lbm.graphics.visualization_modes = VIS_FLAG_LATTICE | VIS_FLAG_SURFACE | VIS_Q_CRITERION;
	lbm.run(0u, lbm_T); // 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, lbm_T);
		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
	}
} 
------

要点のみ解説すると

Mesh* mesh = read_stl(get_exe_path() + "../stl/takekoputa-002.stl", lbm.size(), center, 2.0f * radius);
Mesh* body = read_stl(get_exe_path() + "../stl/86Duino-Doraemon.stl", lbm.size(), center, rotation, 2.5f * radius);

の所で、タケコプターとドラえもんの3D形状を読み込んでいます。
get_exe_path()でexeのあるパスを取得できるようなので、そこからの相対パスで3D形状ファイルのパスを記述すれば良さそうです。
read_stlで読み込むときに引数で形状のスケールも指定できるようです。

解析結果アニメーション


それらしい結果は得られました。
しかしタケコプターの羽根が2枚なので、Radial funのような大迫力の画にはなりませんでした。
オリジナルの解析を行う事で、FluidX3dの使い方を少しだけ深く学ぶことができました。


補足

ワイド画面でのアニメーションとしてYouTube動画リンクも載せておきます。


<< 前のページに戻る