Fluent UDF: NODE_POS_UPDATED で付与された MOVED フラグは NODE_POS_MOVABLE コマンドでリセットできる
問題編
DEFINE_GRID_MOTION
を使って板とかを曲げたりしたいとき、メッシュ内の各 node は複数の face にまたがっている可能性があるので、UDF マニュアルのサンプルにあるように、
#include "udf.h" DEFINE_GRID_MOTION(my_dyn_mesh, domain, dt, time, dtime) { Thread *tp = DT_THREAD(dt); // tp = thread pointer face_t f; // face index Node *np; // np = node pointer begin_f_loop(f,tp) { f_node_loop(f,tp,n) { np = F_NODE(f,tp,n); if ( NODE_POS_NEED_UPDATE(np) ) { NODE_POS_UPDATED(np); // Update the flag do_something; } } } end_f_loop(f,tp); }
というように、
if( NODE_POS_NEED_UPDATE(np) )
で「既に訪れたかどうか」のフラグを調べて、NODE_POS_UPDATED(np);
コマンドで「訪れた」という情報に書き換える。
しかし、「計算開始直後など、時間進行を伴わずに全ノードをループして何かをしたい」ということがある(自分はあった)。このとき、何も考えずにループ構造をコピペしてしまうと、1回目のループで既にフラグが「既に訪れた」ことになっているので、2回目のループでは「全ての」node をスキップしてしまう。つまり何も起きない。この問題にしばらく悩んでいた。つまり、
DEFINE_GRID_MOTION(my_dyn_mesh, domain, dt, time, dtime) { Thread *tp = DT_THREAD(dt); // tp = thread pointer face_t f; // face index Node *np; // np = node pointer // First loop begin_f_loop(f,tp) { f_node_loop(f,tp,n) { np = F_NODE(f,tp,n); if ( NODE_POS_NEED_UPDATE(np) ) { NODE_POS_UPDATED(np); do_something; } } } end_f_loop(f,tp); // Another loop begin_f_loop(f,tp) { f_node_loop(f,tp,n) { np = F_NODE(f,tp,n); if ( NODE_POS_NEED_UPDATE(np) ) { NODE_POS_UPDATED(np); do_something_else; // This doesn't work! } } } end_f_loop(f,tp); }
ということだ。…と思っていた。調査編でわかるように、実際には単に「訪れたかどうか」ではなかったのだが…。
調査編
まず、フラグの実体はなんなのか。
適当なフォルダ(C:\Program Files\ANSYS Inc\v191\fluent\fluent19.1.0\
とか)において、Devas で NODE_POS_NEED_UPDATE
や NODE_POS_UPDATED
を検索すると、dynamesh_tools.h
というヘッダファイルにヒットする。
/* node marks */ #define NODE_NULL 0x0000 #define NODE_FIXED 0x0001 #define NODE_MOVABLE 0x0002 #define NODE_CONTACT 0x0004 #define NODE_MOVED 0x0008 #define NODE_ADAPT 0x0010 #define NODE_DEFORMED 0x0020 <中略> #define NODE_POS_MOVABLE(v) \ (NODE_CLEAR_MARK(v, NODE_MOVED | NODE_FIXED | NODE_ADAPT), \ NODE_SET_MARK(v, NODE_MOVABLE)) #define NODE_POS_NEED_UPDATE(v) (NODE_MARK(v) & NODE_MOVABLE) #define NODE_POS_UPDATED(v) \ (NODE_CLEAR_MARK(v, NODE_MOVABLE | NODE_FIXED | NODE_ADAPT), \ NODE_SET_MARK(v, NODE_MOVED)) #define NODE_POS_UPDATED_P(v) (NODE_MARK(v) & NODE_MOVED) <略>
とある。
まず、フラグは TURE/FALSE のような2値ではなく、複数の値をとる。それがソース冒頭の NODE_NULL
, NODE_FIXED
, ... というやつだ。
そしてこのフラグに関連するコマンドは「現在のフラグ状況を調べるコマンド」と「フラグをセットするコマンド」の2種類に分かれる。
ここでは MOVABLE と MOVED 関連のみを示したが、実際には他のフラグに対応するコマンドもある。
現在のフラグ状況を調べるコマンド: - NODE_POS_NEED_UPDATE: MOVABLE かどうかを調べる - NODE_POS_UPDATED_P: MOVED かどうかを調べる
フラグをセットするコマンド: - NODE_POS_MOVABLE: MOVED 等のフラグを CLEAR_MARK して、MOVABLE をセットする - NODE_POS_UPDATED: MOVABLE 等のフラグを CLEAR_MARK して、MOVED をセットする
となっている。調べるコマンドは基本的に suffix として _P
が付くのだが、なぜか MOVABLE だけは MOVABLE_P
ではなく NEED_UPDATE
となっていて一貫性に欠ける。
解決編
したがって、解決策としては、最初のループの後でフラグリセット用のループを回して、そこで NODE_POS_MOVABLE
を実行する、ということになる。つまり、
DEFINE_GRID_MOTION(my_dyn_mesh, domain, dt, time, dtime) { Thread *tp = DT_THREAD(dt); // tp = thread pointer face_t f; // face index Node *np; // np = node pointer // First loop begin_f_loop(f,tp) { f_node_loop(f,tp,n) { np = F_NODE(f,tp,n); if ( NODE_POS_NEED_UPDATE(np) ) // Check the MOVED flag { NODE_POS_UPDATED(np); // Set MOVED flag, clear any other flags do_something; } } } end_f_loop(f,tp); // Loop for resetting the MOVED flag begin_f_loop(f,tp) { f_node_loop(f,tp,n) { np = F_NODE(f,tp,n); NODE_POS_MOVABLE(np); // Clear MOVED flag (and other flags if any), set the MOVABLE flag } } end_f_loop(f,tp); // Another loop begin_f_loop(f,tp) { f_node_loop(f,tp,n) { np = F_NODE(f,tp,n); if ( NODE_POS_NEED_UPDATE(np) ) // Check the MOVED flag { NODE_POS_UPDATED(np); // Set MOVED flag, clear any other flags do_something_else; // This works! } } } end_f_loop(f,tp); }
ということだ。
ところで、こうやってループを複数書いてると、end_f_loop(f,tp);
を忘れてハマりがちなので注意したい(ハマった)。