動機
最近、諸事情で様々な環境でOpenFOAMを使うことがおおくなりました。
OpenFOAMでソルバを使えるようにするといえば、Allwmake
コマンドですが、このコマンドありえんくらい時間がかかるので、かなりのボトルネックになる可能性があります。
実際、すべてのユーティリティをmakeしなくてもよく、実は一部のコマンドはそこまで多くwmakeしなくてもいいのです。
(例えばicoFoam
に燃焼系のライブラリは不要です)
そこで、必要なソルバを使えるようにするために、必要最低限のmakeで済ませるコマンドがほしいなと言うことで作りました。
auto_wmakeの使い方や紹介
コチラにおいてあります。 README.mdに書いてあるとおりだと思いますので、何かわからない場合はブログのコメントや知ってる方はtwitterからよろしくお願いします。 cargoで管理してるとかんたんにinstallできていいですね
技術的な話
なぜRust?
特に理由はありません。 書いてみたかった go langでも良かったかもしれないですが、個人的にRustのほうが好きなのでRustにしました。 Pythonはグラフ生成が思ったより遅かったのでやめました
生成されるファイル名、依存関係の抽出
各ライブラリどうしの依存関係は get_make_target
Make/options, 各ライブラリの生成されるファイルの名前は Make/files からうまく取ってきています。 例外的な文字列処理の塊で正直気合です
依存関係から依存関係グラフの作成
各ライブラリ同士の依存関係をグラフにいい感じにまとめます。 get_edgesで先程の関数を使って辺を抽出、
出てきた辺は
以下のコードのようにしてグラフにします。
graph
が隣接リスト, in_degree
は入次数を表しています。
グラフは、(依存する側)->(依存される側) と定義しており、入次数が大きければ大きいほど、いろんなライブラリに依存していることになります。
let mut graph : HashMap<String, Vec<String>> = HashMap::new(); let mut in_degree : HashMap<String, i32> = HashMap::new(); for edge in edges { in_degree.entry(edge.1.clone()).or_insert(0); let deg = in_degree.entry(edge.0.clone()).or_insert(0); *deg += 1; graph.entry(edge.0.clone()).or_insert(Vec::new()); let from = graph.entry(edge.1.clone()).or_insert(Vec::new()); from.push(edge.0); }
wmakeをする始点の探索
入次数が0のところがwmakeの始点です。 get_zero_in_degreeがこれに対応しています。
wmakeをする順番
計算が終了したら、終了したディレクトリから辺が伸びている先のノードの in_degree
を1減らし、0になったタイミングでキューに追加します。
これを繰り返し、最終的にキューが尽きたら終了です。
while let Some(target) = queue.pop_front() { cnt += 1; // output progress println!("[{}/{}] {}", cnt, size, target); wmake(&target, &jobs_string, is_stdout_detail); let nexts = graph.get_mut(&target).unwrap(); for nxt in nexts { *in_degree.get_mut(nxt).unwrap() -= 1; if in_degree.get(nxt).unwrap() == &0 { queue.push_back(nxt.to_string()); } } }
例外的なwmake
wmake
そのもの、Pstretam
, OSspecific
に関しては少し特殊なmakeをしなければならないため、例外的にmakeを挟んでいます。
これのせいでおそらくいくらかのバージョンのOpenFOAMでは、auto_wmake
が動かなくなる可能性があります。
例外的なwmakeはinit_buildに対応しています。
その他機能
auto_wmake
は最初はコマンドライン引数に、makeしたいディレクトリのパスを与えていたのですが、icoFoam
へのパスが長すぎてめんどくさくなったので、applicationsフォルダの中にあるものは省略可能にしました。
やり方としては、Make/filesの中身を見て、一致したものを始点として auto_wmake
するだけです。
また、この際に打ち間違えのリコメンド機能を実装してみました。applicationフォルダの中にあるbinの名前はすべてコチラで持っているので、レーベンシュタイン距離 が最も近いものをリコメンドしてくれます。 結構かんたんなアルゴリズムではありますが、かなり良い精度でリコメンドしてくれます。
終わりに
今回始めてrustでリポジトリを作りましたが、インストールがかんたんで、所有権の移動等も慣れればとりあえずコンパイルは通ってくれるようになってくれました(美しいかどうかはさておき…) とりあえず困ったらC++でしたが少しずつrustに置き換えていく予定です。
もし、このソースコードのだめなところを見つけたら教えていただけると幸いです。 issueプルリク等待っています。よろしくお願いします。