くれなゐの雑記

例を上げて 自分で手を動かして学習できる入門記事を多めに書いています

OpenFOAMをallWmakeなしで必要なものだけwmakeするツール auto_wmakeを作りました

動機

最近、諸事情で様々な環境でOpenFOAMを使うことがおおくなりました。 OpenFOAMでソルバを使えるようにするといえば、Allwmake コマンドですが、このコマンドありえんくらい時間がかかるので、かなりのボトルネックになる可能性があります。

実際、すべてのユーティリティをmakeしなくてもよく、実は一部のコマンドはそこまで多くwmakeしなくてもいいのです。 (例えばicoFoamに燃焼系のライブラリは不要です)

そこで、必要なソルバを使えるようにするために、必要最低限のmakeで済ませるコマンドがほしいなと言うことで作りました。

auto_wmakeの使い方や紹介

github.com

コチラにおいてあります。 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プルリク等待っています。よろしくお願いします。