OpenFOAMのソルバーでtensorflowの関数をincludeしてwmakeしたい【追記：2017.12.16】

# 最初に
　現時点でまだ実現できていません。
もし何か気づいた点などがございましたら小さなことでも是非コメントをいただけたらと思います。
【追記：2017.12.16】コンパイルまでできました。

# 動機
CFD屋だけどAIで遊びたい。

# 登場する用語
### OpenFOAM
　言わずと知れた流体解析ツールボックス。最初の敷居が高いのが玉に瑕。今回はバージョン5.xを使用した。またソースからg++でコンパイルしたものを使用した。
### wmake
　OpenFOAMのビルダ(?)。例えばコンパイラがgccの場合はそのg++コマンドをいい感じに吐き出してくれるスクリプト。
実行ファイルを標準のものと重複しないように作成し、コマンドまで作成してくれる優れもの。
色々なコンパイラに対応している。
### tensorflow
　言わずと知れたGoogle謹製のDeep Learning用ライブラリ。バージョンは2.0を使用。
### bazel
　tensorflowの推奨するGoogle謹製のビルダ。標準ではXLAというコンパイラを使用するらしい。
# 今回の概要
 - tensorflowをビルド
 - ヘッダファイルをとってくる。
 - 何もしないユーティリティを作る。
 - Makeディレクトリをいじる。
 - wmakeでコンパイル
 - tensorflowのオブジェクトを入れてコンパイル

# やったこと
### tendorflowのビルド
[bazelをインストール](https://docs.bazel.build/versions/master/install-ubuntu.html)してからtensorflowをソースコードからビルドする。
[Tensorflowをc++のcmakeしてるプロジェクトで使いたい](https://qiita.com/Tacha-S/items/5bb18c631868b6b15517)を参考にさせていただいた。

```bash:installTF.sh
git clone tensorflow/tensorflow
cd tensorflow
./configure
bazel build -c opt --copt=-march=native tensorflow:libtensorflow.so
```

### ヘッダファイルをとってくる。
先のQiita記事内の[スクリプト](https://gist.github.com/saitodev/3cde48806a32272962899693700d9669#file-copy_tensorflow_headers-sh)を参考にした。
色々修正し最終的にヘッダファイルをコピーするスクリプトはこんな感じになった。

```bash:getHeaderTF.sh

#!/bin/bash
HEADER_DIR=$HOME/OpenFOAM/tensorflow/lnInclude　※各自のtensorflowインストール場所

if [ ! -e $HEADER_DIR ];
then
    mkdir -p $HEADER_DIR
fi

cd tensorflow

find tensorflow/core -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;
find tensorflow/cc   -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;
find tensorflow/c    -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;

find bazel-genfiles    -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;
find bazel-tensorflow    -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;

find third_party/eigen3 -follow -type f -exec cp --parents {} $HEADER_DIR \;

pushd bazel-genfiles
find tensorflow -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;
popd

pushd bazel-tensorflow/external/protobuf_archive/src
find google -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;
popd

pushd bazel-tensorflow/external/eigen_archive
find Eigen       -follow -type f -exec cp --parents {} $HEADER_DIR \;
find unsupported -follow -type f -exec cp --parents {} $HEADER_DIR \;
popd

pushd ~/.cache/bazel/ユーザー名/謎の文字列/external
find eigen_archive -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;
find protobuf_archive -follow -type f -name "*.h" -exec cp --parents {} $HEADER_DIR \;
popd
```

※最後のユーザー名と謎の文字列のbazelでtensorflowをインストールした時に生成されるディレクトリ
tensorflow/bazel-binのリンク先から確認できる。

### tensorflowの関数の関係ないユーティリティを作る。
適当なユーティリティを作成した。
ただしその前にヘッダファイルをtensorflowのものとOpenFOAMのものとそれぞれ適当にとってきている。
注意すべきはtensorflowのものを最初にincludeする必要がある。
原因はnamespaceかmacroのようだが詳細は未調査。

```C++:verySimpleFoam.C
// from tensorflow
#include "tensorflow/cc/ops/image_ops.h"

// from OpenFOAM
#include "Time.H"

int main(int argc, char* argv[]) {
  Foam::Info << "OK, Google" << Foam::endl;
  return 0;
}
```


### Makeディレクトリをいじる。
wmakeをするためにヘッダの場所とlib so(shared object)の種類を設定する。。
wmake(gccオプション)でlibを設定するには、まず-Lに続けてディレクトリ名を入れたあとに-lに続けてlibの名前の一部を書く。
例えば"libtensorflow.so"を読む場合には"-ltensorflow"となる。

```bash:Make/files
verySimpleFoam.C

EXE = $(FOAM_USER_APPBIN)/verySimpleFoam
```

```bash:Make/options
EXE_INC = \
    -I$(LIB_SRC)/mesh/blockMesh/lnInclude \
    -I$(LIB_SRC)/meshTools/lnInclude \
    -I/home/***/OpenFOAM/tensorflow/lnInclude \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/eigen_archive/Eigen \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/protobuf_archive/src \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-genfiles \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-tensorflow/external/nsync/public

EXE_LIBS = \
    -lblockMesh \
    -lmeshTools \
    -L/home/***/OpenFOAM/tensorflow/bazel-bin/tensorflow \
    -ltensorflow \
    -ltensorflow_framework
```

### コンパイル
紆余曲折を経てなんとかコンパイルはできた。
※コンパイルは通るがエラーメッセージがいっぱい出る。
コーディングルールがOpenFOAMとGoogleとで違うようだ。
こういうものはOpenFOAM-x.x.x/etc/wmake/rules
のなかに警告を無視する項目を設定できるため、ここに追加する。

```bash:wmake/rules/linux64Gcc/cc
c++WARN     = -Wall -Wextra -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -Wsign-compare
```

-Wold-style-cast をはずし-Wsign-compareを追加した。

ただ実行すると

```bash:error
verySimpleFoam: error while loading shared libraries: libtensorflow.so: cannot open shared object file: No such file or directory
```

となる。
実行時にlibを読むためにはパスを通す必要があるらしい

```
export LD_LIBRARY_PATH=~/OpenFOAM/tensorflow/bazel-bin/tensorflow:$LD_LIBRARY_PATH
```

を直前で実行する。
自分はOpenFOAM/etc/bashrcに追加した。これでまず無事にverySimpleFoamを実行できた。

```output
OK, Google
```

### tensorflowのオブジェクトを呼び出してみる。

```C++:verySimple2Foam.C
// from tensorflow
#include "tensorflow/cc/ops/image_ops.h"

// from OpenFOAM
#include "Time.H"

int main(int argc, char* argv[]) {
  tensorflow::Scope root {tensorflow::Scope::NewRootScope()};
  Foam::Info << "OK, Google" << Foam::endl;
  return 0;
}
```

#### コンパイル
コンパイルできない。

```エラーメッセージ
g++ -std=c++11 -m64 -Dlinux64 -DWM_ARCH_OPTION=64 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -O3  -DNoRepository -ftemplate-depth-100 -I/home/***/OpenFOAM/OpenFOAM-5.x/src/mesh/blockMesh/lnInclude -I/home/***/OpenFOAM/OpenFOAM-5.x/src/meshTools/lnInclude -I/home/***/OpenFOAM/tensorflow/lnInclude -I/home/***/OpenFOAM/tensorflow/lnInclude/eigen_archive/Eigen -I/home/***/OpenFOAM/tensorflow/lnInclude/protobuf_archive/src -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-genfiles -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-tensorflow/external/nsync/public -IlnInclude -I. -I/home/***/OpenFOAM/OpenFOAM-5.x/src/OpenFOAM/lnInclude -I/home/***/OpenFOAM/OpenFOAM-5.x/src/OSspecific/POSIX/lnInclude   -fPIC -c verySimpleFoam.C -o /home/***/OpenFOAM/OpenFOAM-5.x/platforms/linux64GccDPInt32Opt/applications/utilities/myMeshTool/verySimpleFoam/verySimpleFoam.o
g++ -std=c++11 -m64 -Dlinux64 -DWM_ARCH_OPTION=64 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -O3  -DNoRepository -ftemplate-depth-100 -I/home/***/OpenFOAM/OpenFOAM-5.x/src/mesh/blockMesh/lnInclude -I/home/***/OpenFOAM/OpenFOAM-5.x/src/meshTools/lnInclude -I/home/***/OpenFOAM/tensorflow/lnInclude -I/home/***/OpenFOAM/tensorflow/lnInclude/eigen_archive/Eigen -I/home/***/OpenFOAM/tensorflow/lnInclude/protobuf_archive/src -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-genfiles -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-tensorflow/external/nsync/public -IlnInclude -I. -I/home/***/OpenFOAM/OpenFOAM-5.x/src/OpenFOAM/lnInclude -I/home/***/OpenFOAM/OpenFOAM-5.x/src/OSspecific/POSIX/lnInclude   -fPIC -Xlinker --add-needed -Xlinker --no-as-needed /home/***/OpenFOAM/OpenFOAM-5.x/platforms/linux64GccDPInt32Opt/applications/utilities/myMeshTool/verySimpleFoam/verySimpleFoam.o -L/home/***/OpenFOAM/OpenFOAM-5.x/platforms/linux64GccDPInt32Opt/lib \
    -lblockMesh -lmeshTools -lpthread -L/home/***/OpenFOAM/tensorflow/bazel-bin/tensorflow -ltensorflow -ltensorflow_framework -lOpenFOAM -ldl  \
     -lm -o /home/***/OpenFOAM/inabower-5.x/platforms/linux64GccDPInt32Opt/bin/verySimpleFoam
/home/***/OpenFOAM/OpenFOAM-5.x/platforms/linux64GccDPInt32Opt/applications/utilities/myMeshTool/verySimpleFoam/verySimpleFoam.o: 関数 `main' 内:
verySimpleFoam.C:(.text.startup+0x1d): `tensorflow::Scope::NewRootScope()' に対する定義されていない参照です
verySimpleFoam.C:(.text.startup+0x4c): `tensorflow::Scope::~Scope()' に対する定義されていない参照です
verySimpleFoam.C:(.text.startup+0x75): `tensorflow::Scope::~Scope()' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
/home/***/OpenFOAM/OpenFOAM-5.x/wmake/makefiles/general:140: ターゲット '/home/***/OpenFOAM/inabower-5.x/platforms/linux64GccDPInt32Opt/bin/verySimpleFoam' のレシピで失敗しました
make: *** [/home/***/OpenFOAM/inabower-5.x/platforms/linux64GccDPInt32Opt/bin/verySimpleFoam] エラー 1

```

tensorflow側の関数を呼び出せないようだ。
この原因は
 - soがちゃんと呼び出せていない
 - soの中にこの関数が入っていない（一番最初のコンパイルの不備）
 - libのパスがおかしい
などが考えられるが、現在調査中。

# 最後に
誰かやってみてくれると嬉しいです。
色々やりすぎて僕のPCには今OpenFOAMとtensorflowが10個ずつくらい入っているため、この辺がわちゃわちゃしてるのが原因ってだけかもしれません。
年末までに上手く行ったらまたadvent calenderに投稿したいと思います。

# コンパイルできた【追記：2017.12.16】
原因はtensorflowのbuild。


```bash:installTF.sh
git clone tensorflow/tensorflow
cd tensorflow
./configure
bazel build -c opt --copt=-march=native tensorflow:libtensorflow.so
```

でなく


```bash:
bazel build -c opt --copt=-march=native tensorflow:libtensorflow_cc.so
```

をする必要がある。
名前の違うだけの同じものができるのかと思っていたが全然違うもののようだ。
今後も使いたい関数によっては別のコンパイルをする必要がありそう。
bazelの使い方についてちゃんと掘り下げる必要がありそうだ。


```bash:Make/files
verySimpleFoam.C

EXE = $(FOAM_USER_APPBIN)/verySimpleFoam
```

```bash:Make/options
EXE_INC = \
    -I$(LIB_SRC)/mesh/blockMesh/lnInclude \
    -I$(LIB_SRC)/meshTools/lnInclude \
    -I/home/***/OpenFOAM/tensorflow/lnInclude \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/eigen_archive/Eigen \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/protobuf_archive/src \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-genfiles \
    -I/home/***/OpenFOAM/tensorflow/lnInclude/bazel-tensorflow/external/nsync/public

EXE_LIBS = \
    -lblockMesh \
    -lmeshTools \
    -L/home/***/OpenFOAM/tensorflow/bazel-bin/tensorflow \
    -ltensorflow_cc \
    -ltensorflow_framework
```

```C++:verySimple2Foam.C
// from tensorflow
#include "tensorflow/cc/ops/image_ops.h"

// from OpenFOAM
#include "Time.H"

int main(int argc, char* argv[]) {
  tensorflow::Scope root {tensorflow::Scope::NewRootScope()};
  Foam::Info << "OK, Google" << Foam::endl;
  return 0;
}
```

でコンパイルできる。
実行すると

```bash:output
2017-12-16 13:12:36.157197: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA
OK, Google
```

最初に入るメッセージは「もっとCPUを最適化できますよ」というメッセージ。
非表示にするためには、[TensorFlow（v1.1.0）を立ち上げるたびに出てくるワーニングをなんとかする](https://qiita.com/tanemaki/items/e3daa1947a34f63ab6e3)を参考に、

```bash:
$ export TF_CPP_MIN_LOG_LEVEL=2
```

を実行する。

```bash:output
OK, Google
```

うまくいく。
ようやくディープラーニングのスタート地点に立てそう。。。