dockerを使ってWRFをインストールしてみた


# 作戦
[Weather Research & Forecasting Model (WRF)](https://github.com/wrf-model/WRF) はユーザー登録をすれば誰でも使える最先端の気象モデルだが，自分のような初心者にはインストールが難しい．[こちらの記事](https://qiita.com/TrtlGard/items/6ff6fb713af77546dc69)では，以下のようなワンクリックで完結する神インストールスクリプトが紹介されていて衝撃を受けた．

https://github.com/bakamotokatas/WRF-Install-Script

しかし，debian系Linux専用．最近流行り(?)の[docker](https://www.docker.com)という高速な仮想環境(?)でubuntuを構築すればMacでも使えるかも．．！

ということで上記インストールスクリプトを利用し，Apple silicon Mac（または非debian系Linux
やWindows
）で**dockerにUbuntuコンテナを作成しWRFを動かしてみよう**，が今回の作戦．

（追記2024/12/13：WindowsはWSLのUbuntuに下記のインストールスクリプトが有効でdockerは不要でした．再掲ですが以下にわかり易くまとめられています．

https://qiita.com/TrtlGard/items/6ff6fb713af77546dc69

追記ここまで）

なお，dockerもWRFも初心者なので詳しい＆正確な説明は諦めています．dockerの説明は探せば色々出てきますが，[サプーさんの説明動画](https://youtu.be/B5tSZr_QqXw?si=87n5jV7IJ71jkGRS)が個人的にはわかりやすかったです（照）

<!--
:::note
WRFインストール済みのdockerイメージも公開されているようです (https://github.com/NCAR/WRF_DOCKER) ．自分は試してませんがこちらで十分なら以下は不要です．
:::
-->

## メリット
- OSに関わらずインストール方法を固定できる
- 自力でのライブラリ構築の必要がない
- ほぼ最新版のWRFを使える（スクリプト管理者に感謝）
- (dockerの勉強になる)

## デメリット
- Intel fortranはデフォルトでは使えない（gfortran, openmpiが使われる）
- dockerとはいえ仮想環境であることのオーバーヘッドはある
- Apple silicon Macの場合x86_64へエミュレートする分更にオーバーヘッドになる
- Macではコンテナ起動時にGUIの操作が要求されるためSSHに向かない（解決法模索中）



# 環境
- MacBoock Air (2020, M1)
- macOS Sequoia 15.1.1
- docker desktop Version 4.36.0
- Docker version 27.3.1
- WRF4.6.1

# Dockerのインストール

Macの場合は以下を参照．

https://qiita.com/cleyera_f/items/bdd3d33f13527604a663

設定からCPUやメモリの上限を設定できるので，余裕を持たせておくと良いと思う．

Linux
やWindows
の方はすみませんが各々セットアップしてください．


# Ubuntuコンテナの作成

[こちら](https://www.infra-linux.com/menu-docker4/ubuntu2404/#google_vignette)を参考にした．以下順次解説する．
（追記2024/12/25：ここでは用いないがDockerFileを用いるとほぼすべでの工程を自動化できるらしい）

ホームディレクトリに`docker`ディレクトリを作成し，基本的にはそこで作業する．

```shell
$ cd
$ mkdir docker
$ cd docker
$ mkdir ubuntu_volume
```

`ubuntu_volume`は後ほど使うが，コンテナの一部を格納する場所．

`~/docker`内で以下のように`compose.yaml`を作成する．
```compose.yaml
services:
  ubuntu:
    image: ubuntu:24.04
    container_name: ubuntu_container
    tty: true
    platform: linux/amd64
    volumes:
      - "ubuntu_volume:/home/ubuntu"

volumes:
  ubuntu_volume:
     driver_opts:
      type: none
      device: ${PWD}/ubuntu_volume
      o: bind
```

- `services`で`ubuntu`というサービスを定義．
- `image`で利用するイメージを指定する．イメージは初回起動時にダウンロードされる．
- `container_name`はコンテナ名．サービス名と異なる扱い．
- `tty: true`は端末を利用するために必要．
- `platform`の箇所はApple silicon Macで利用するために必要．IntelかAMDのCPUマシンの場合は消す．

:::note
Apple siliconは`aarch64`というARM系のアーキテクチャ．ここでのWRFは`x86_64`を想定しており，そのままだとインストールに失敗する．上記により`x86_64`としてRosetta2がエミュレートしてくれるらしい．アーキテクチャは`uname -m`コマンドで確認できる．
:::

- `volumes`は簡単に言えばコンテナの一部（ここでは`/home/ubuntu`以下）の保存場所を指定している．その直下にあるもう一つの`volumes`では`ubuntu_volume`という「名前付きボリューム」を先ほど作成した`ubuntu_volume`ディレクトリにマウントするよう指定している．ボリュームに関しては以下の記事が詳しい．

https://qiita.com/Hirochon/items/121f172fbbae8c8ad749

https://qiita.com/P-man_Brown/items/6d6e870acc1720f04486?utm_source=chatgpt.com

次に以下のコマンドでコンテナを起動する．**必ず`compose.yaml`のあるディレクトリで実行すること**．

```shell
$ docker compose up -d
```

:::note warn
一度起動した後にcompose.yamlを編集するとコンテナが初期化されてしまうことがあるので注意．`--rm`オプションをつけたdockerコマンド操作も同様なので禁物．逆に最初からやり直したい場合は`docker rm ubuntu_container`で削除できる．
:::

コンテナの生成・起動の確認は`docker ps`コマンド．

```shell
$ docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS          PORTS     NAMES
e4edfa990d25   ubuntu:24.04   "/bin/bash"   22 minutes ago   Up 22 minutes             ubuntu_container
```

上記の感じなら多分OK．

## コンテナの停止と再起動の確認

手順には含まないが，いざという時困るので先に解説．どの方法が最適か理解していないが，ひとまず`compose`を使って安定した方法を紹介．

### 1. コンテナからログアウト
exitするだけ．コンテナは稼働し続ける．

### 2. コンテナの停止

```shell
$ docker stop ubuntu_container
```
引数はコンテナ名を与える．
`docker ps`で`ubuntu_container`が消えているか確認．
```shell
$ docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED        STATUS          PORTS     NAMES
```

### 3. コンテナの再起動
```shell
$ docker compose up -d
```
`-d`はバックグラウンドで起動するオプションらしい．
（オフラインだと失敗することがあり要ネット接続．オフラインOKな方法を知ったら追記するかも）


### 4. コンテナへの再ログイン
`compose.yaml`のあるディレクトリへ移動し，以下を実行．
```shell
$ docker compose exec ubuntu /bin/bash
```
ここではコンテナ名ではなくサービス名を与える．

# スクリプト実行の準備

dockerコンテナへのログイン
```
$ docker compose exec ubuntu /bin/bash
```
rootユーザーとしてログインされる．後のライブラリインストールで**非ルートユーザーを使う必要がある**．自動的に`ubuntu`というユーザーが生成されていたので用いる．

rootとしてコンテナにログインした後，まず`ubuntu`ユーザーのパスワードを更新し，sudoユーザーに登録しておく．（結構重要）


```shell
root@e4edfa990d25:/# passwd ubuntu
New password:
Retype new password:
passwd: password updated successfully
root@e4edfa990d25:/# gpasswd -a ubuntu sudo
Adding user ubuntu to group sudo
```

まずはaptを最新にする．

```shell
root@e4edfa990d25:/# apt update
```

aptを使って`sudo`をインストール．他にも必要なものがあればここで．

```shell
root@e4edfa990d25:/# apt install sudo
root@e4edfa990d25:/# apt install wget
root@e4edfa990d25:/# apt install vim  # エディタ
root@e4edfa990d25:/# apt install less  # ビューワ
```

`ubuntu`ユーザーで`sudo`が使えるか確認．

```shell
root@e4edfa990d25:/# su - ubuntu
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ubuntu@e4edfa990d25:~$ sudo ls -a
[sudo] password for ubuntu:
.  ..  .bash_history  .bash_logout  .bashrc  .profile  .sudo_as_admin_successful
```

OK．

（2025/04/18追記）
場合によってはホストからボリュームへアクセスできないのでコンテナ内で'/home/ubuntu'の権限を調整すると良い．

# WRFインストールスクリプトの実行

例の[WRF-Install-Script](https://github.com/bakamotokatas/WRF-Install-Script)よりお好きなバージョンを選択．今回は4.6.1

![Screenshot 2024-12-07 at 20.23.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/c2be9c83-f381-0308-1f6c-f6b3f88970fd.png)

`Raw`をクリック．

![Screenshot 2024-12-07 at 20.25.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/09833eba-e232-d72d-b8dd-3188519a6763.png)

そのページのアドレスをコピー，端末でwgetする．これはコンテナ外からでも良い．

```shell
$ cd ~/docker/ubuntu_volume
$ wget https://raw.githubusercontent.com/bakamotokatas/WRF-Install-Script/refs/heads/master/WRF4.6.1_Install.bash
```

コンテナ内にログインし，ダウンロードしたスクリプトを実行する（ボリュームに保存したものはコンテナ内と完全に同期）．WRFとWPS(前処理システム)がインストールされる．容量は約52GB．
```shell
ubuntu@e4edfa990d25:~$ bash WRF4.6.1_Install.bash
```

環境によるが40~60分かかる．終了したら生成物を確認する．
```shell
ubuntu@b07cbba8e72e:~/Build_WRF$ ls
LIBRARIES  WPS-4.6.0  WPSV4.6.0.TAR.gz  WPS_GEOG  WRF-4.6.1-ARW  WRFV4.6.1.tar.gz  geog_high_res_mandatory.tar.gz
ubuntu@b07cbba8e72e:~/Build_WRF$ ls WRF-4.6.1-ARW/run/*exe
WRF-4.6.1-ARW/run/ndown.exe  WRF-4.6.1-ARW/run/real.exe  WRF-4.6.1-ARW/run/tc.exe  WRF-4.6.1-ARW/run/wrf.exe
ubuntu@b07cbba8e72e:~/Build_WRF$ ls WPS-4.6.0/*exe
WPS-4.6.0/geogrid.exe  WPS-4.6.0/metgrid.exe  WPS-4.6.0/ungrib.exe
```

きちんと実行ファイルが生成されている（驚嘆）

# ARWpostのインストール

使わない人は省略可能．

以下のARWpost(後処理システム)インストールの箇所を参考にする．

https://qiita.com/TrtlGard/items/6ff6fb713af77546dc69

先ほどのページを参考にホームディレクトリに`ARWpost_install.bash`を以下のように作成する．`# remove download file` `# rename WRF directory`のブロックは除いてある（好みの問題）
```ARWpost_install.bash
#!/bin/bash
# From install_wrf.sh
##  Download and install required library and data files for WRF.
##  License: LGPL
##  Jamal Khan <jamal.khan@legos.obs-mip.fr>

# grads install
sudo apt update
sudo apt -y upgrade
sudo apt -y install grads unzip default-jre

# ARWpost install
cd ~/Build_WRF
wget -c https://www2.mmm.ucar.edu/wrf/src/ARWpost_V3.tar.gz
tar -xvzf ARWpost_V3.tar.gz -C ~/Build_WRF
cd ~/Build_WRF/ARWpost
cd arch
cp Config.pl Config.pl_backup
sed -i '79s/.*/  $response = 3 ;/' Config.pl
cd ..
./clean
sed -i -e 's/-lnetcdf/-lnetcdff -lnetcdf/g' ~/Build_WRF/ARWpost/src/Makefile
./configure #3
sed -i -e 's/-C -P/-P/g' ~/Build_WRF/ARWpost/configure.arwp
sed -i -e 's/-frecord-marker=4/-frecord-marker=4 -fallow-argument-mismatch/' ~/Build_WRF/ARWpost/configure.arwp
./compile
```

コンテナ内で実行する．

```shell
ubuntu@b07cbba8e72e:~$ bash ARWpost_install.bash
```

実行ファイルを確認．

```shell
ubuntu@b07cbba8e72e:~/Build_WRF$ ls ARWpost/*exe
ARWpost/ARWpost.exe
```

完了:beer:

もう`.tar.gz`ファイルは不要なので消したい人は消す．（コンテナ内・外どちらからでも消せる）

```shell
ubuntu@b07cbba8e72e:~/Build_WRF$ rm *tar.gz *TAR.gz
```

# WRFを使ってみる

あとはお好きなように，，ですが，[先ほどのページ](https://qiita.com/TrtlGard/items/6ff6fb713af77546dc69)を参考に動作確認してみる．以降は単なる千番煎じです．

1と2は完了しているので`3.気象データダウンロード`から実行してゆく．

## 気象データダウンロード

### UCARからNFLダウンロードスクリプトを取得
Mac（以下，コンテナ外）のブラウザで[こちら](https://rda.ucar.edu/datasets/ds083.2/)から2024年8月30日（台風10号上陸時）のファイルを選択しcshスクリプトをダウンロード．

<details><summary>図説</summary>

![Screenshot 2024-12-08 at 9.34.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/18582a9f-f51b-bf96-1135-64a1e7bf4a9e.png)

![Screenshot 2024-12-08 at 9.35.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/51d87afe-199c-9a06-dc69-59b847e5016e.png)

![Screenshot 2024-12-08 at 10.39.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/7048096a-2786-8d4b-31b2-b12929165475.png)

![Screenshot 2024-12-08 at 9.40.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/c86a41d3-715e-7b17-429d-2408a0c310a5.png)

![Screenshot 2024-12-08 at 9.41.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/1aba2ca8-0261-8add-297f-fad41e0cd83c.png)

![Screenshot 2024-12-08 at 9.45.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/d5103f38-b0b1-06f7-e25a-2dcafb65c4ae.png)

</details>

cshのダウンロードが完了したら，**コンテナ外から**`ubuntu_volume/Build_WRF`にアクセス，`FNL_DATA`ディレクトリを作ってcshをコピーする．

```shell
$ cd ~/docker/ubuntu_volume/Build_WRF
$ mkdir FNL_DATA
$ cp ~/Downloads/rda-download.csh .
$ csh rda-download.csh
```

（`compose.yaml`でボリュームを設定していない場合はコンテナ外からcshをコンテナ内へ送る必要がある．[参考](https://qiita.com/buntafujikawa/items/04c3bdd93ac3520c53eb)）


## WRF実行

以下，単なる動作確認につき，再現性を高めるチューニングは行なっていない．

詳しいことは使い慣れた人に聞く，[公式のBest Practices](https://www2.mmm.ucar.edu/wrf/users/namelist_best_prac_wrf.html)，[User's Guide](https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/index.html)を読む，など．

### `namelist.wps`を編集

```shell
$ cd ../WPS-4.6.0
$ vi namelist.wps  # お好きな方法で
```

`max_dom` `start_date` `end_date` `interval_seconds` を修正．1ドメインで計算するので2列目は無視される．

```text
 max_dom = 1,
 start_date = '2024-08-30_00:00:00','2019-09-04_12:00:00',
 end_date   = '2024-08-30_18:00:00','2019-09-04_12:00:00',
 interval_seconds = 21600
```

計算や出力を緯度経度格子にしたい場合は`map_proj = 'mercator',`とする．今回は`lambert`
```text
map_proj = 'lambert',
```

`ref_lat` `ref_lon` `stand_lon`を修正．[気象庁のメソ客観解析](https://www.jmbsc.or.jp/jp/offline/info/users_guide/users_guide_analysis.pdf)が参考になる．
```text
 ref_lat   =  35.00,
 ref_lon   = 135.00,
 truelat1  =  30.0,
 truelat2  =  60.0,
 stand_lon = 135.0,
```

そのほかは弄らず．

### geogrid.exe, ungrib.exe, metgrid.exeの実行

NFLデータへのリンクの作成．
```shell
$ ./link_grib.csh ../FNL_DATA/*grib2
```

`GRIBFILE.AAA` ~ `GRIBFILE.AAD` が生成されていればOK．以下，**コンテナ内へログイン**して順次実行．
（追記2024/12/25：ここでは実行ファイルの実行のためにコンテナ内へログインしているが，もし`docker exec`で実行ファイルをホストから実行できれば完全にログイン不要となる）

```shell
ubuntu@b07cbba8e72e:~/Build_WRF/WPS-4.6.0$ ./geogrid.exe
ubuntu@b07cbba8e72e:~/Build_WRF/WPS-4.6.0$ ln -sf ungrib/Variable_Tables/Vtable.GFS Vtable
ubuntu@b07cbba8e72e:~/Build_WRF/WPS-4.6.0$ ./ungrib.exe
ubuntu@b07cbba8e72e:~/Build_WRF/WPS-4.6.0$ ./metgrid.exe
```

### `namelist.input`の編集

移動とエディタの起動（コンテナ外）．

```shell
$ cd ../WRF-4.6.1-ARW/run/
$ vi namelist.input  # お好きな方法で
```

`run_hours` `start_*` `end_*` `interval_seconds`を編集．

```text
&time_control
 run_days                            = 0,
 run_hours                           = 18,
 run_minutes                         = 0,
 run_seconds                         = 0,
 start_year                          = 2024, 2019,
 start_month                         = 08,   09,
 start_day                           = 30,   04,
 start_hour                          = 0,   12,
 end_year                            = 2024, 2019,
 end_month                           = 08,   09,
 end_day                             = 30,   06,
 end_hour                            = 18,   00,
 interval_seconds                    = 21600
```

あとは`&domains`の`max_dom`を`1`に変更するのも忘れずに．その他はひとまずデフォルト．

### real.exe, wrf.exeの実行

metgridしたファイルのリンクを貼り，順次実行．

```shell
ubuntu@b07cbba8e72e:~/Build_WRF/WRF-4.6.1-ARW/run$ ln -s ~/Build_WRF/WPS-4.6.0/met*nc .
ubuntu@b07cbba8e72e:~/Build_WRF/WRF-4.6.1-ARW/run$ ./real.exe
```

今回は4並列で計算．

```shell
ubuntu@b07cbba8e72e:~/Build_WRF/WRF-4.6.1-ARW/run$ mpirun -np 4 ./wrf.exe
```

約15分で計算終了:airplane:

### ARWpostによる変換

使わない人は省略可．

`ARWpost`ディレクトリへ移動し出力データのリンクを作成．

```shell
ubuntu@b07cbba8e72e:~/Build_WRF/WRF-4.6.1-ARW/run$ cd ../../ARWpost
ubuntu@b07cbba8e72e:~/Build_WRF/ARWpost$ ln -s ../WRF-4.6.1-ARW/run/wrfout* .
```

`namelist.ARWpost`を編集．詳細は[User's Guide](https://www2.mmm.ucar.edu/wrf/users/wrf_users_guide/build/html/post_processing.html#edit-the-namelist-arwpost-file)

オリジナルは保存しておいた方が良い．

```shell
ubuntu@b07cbba8e72e:~/Build_WRF/ARWpost$ cp namelist.ARWpost namelist.ARWpost.org
```

編集した例．

```namelist.ARWpost
&datetime
 start_date = '2024-08-30_00:00:00',
 end_date   = '2024-08-30_18:00:00',
 interval_seconds = 3600,
 tacc = 0,
 debug_level = 0,
/

&io
 input_root_name = './wrfout_d01_*'
 output_root_name = './test'
 plot = 'list'
 fields = 'height,pressure,tk,tc,umet,vmet,u10m,v10m,slp,geopt,max_dbz'
 mercator_defs = .true.
/

! Below is a list of all available diagnostics
! fields = 'height,geopt,theta,tc,tk,td,td2,rh,rh2,umet,vmet,pressure,u10m,v10m,wdir,wspd,wd10,ws10,slp,mcape,mcin,lcl,lfc,cape,cin,dbz,max_dbz,clfr'

&interp
 interp_method = 1,
 interp_levels = 1000.,950.,900.,850.,800.,750.,700.,650.,600.,550.,500.,450.,400.,350.,300.,250.,200.,150.,100.,
/
```

実行．

```shell
ubuntu@b07cbba8e72e:~/Build_WRF/ARWpost$ ./ARWpost.exe
```

`test.dat` `test.ctl`が生成されていれば成功．

## 計算結果の描画

（`compose.yaml`でボリュームを設定していない場合はコンテナ内の計算結果をコンテナ外から取り出す[参考](https://qiita.com/RyotaIshii/items/25beeafaa836b4f4819c)）

試しにPythonを使って10時間積分した生データ(`wrfout_d01_2024-08-30_10:00:00`)の積算降水を作図．投影系に関しては`namelist.wps`で設定した値を使う．[stackoverflow](https://stackoverflow.com/questions/77230195/issue-plotting-wrf-data-with-lambert-projection-coastlines-overlay-not-being-di)を参考にした．

```python3
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("Agg")  # 念の為
import cartopy.crs as ccrs

ds = xr.open_dataset("./wrfout_d01_2024-08-30_10:00:00")

da = ds.RAINC[0]+ds.RAINNC[0]
x = da.XLONG.values
y = da.XLAT.values

map_proj = ccrs.LambertConformal(
    central_latitude=30,
    central_longitude=140,
    standard_parallels=(30, 60),
)
# map_proj = ccrs.PlateCarree()  # namelist.wpsでmap_proj = 'mercator'の場合こちら

ax=plt.axes(projection=map_proj)
ax.coastlines()

s = ax.contourf(x,y,da,transform=ccrs.PlateCarree())
plt.colorbar(s)

plt.savefig("test.png")
plt.close()
```

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544845/c6db7b3d-bb83-3e0d-a31a-eb7ec282f911.png)


雨が降っていることを確認 :umbrella:

# 終わりに

これで確実にコンパイルを成功させられそうな雰囲気．とはいえ冒頭に書いたようにデメリットもあり万人向けでない．

（追記2024/12/25：dockerを工夫すれば全工程を自動化できそう．自動化は作業を減らすが，ライブラリ構築を通した経験値も減り，トレードオフ．状況に応じた良い塩梅がある気はする．モダンなプログラム言語はライブラリの環境構築を意識することはあまりなく，その使用感を目指したつもり．pipやaddPkgで気象モデルを動かせる未来は来るのか,,?）

dockerは初心者だがそれなりに恩恵を感じた．いつかもっと勉強したい．

エンディアンについては今回ケアレス．環境によっては注意かも．

WRFに関する電子情報を残してこられた歴々たる諸先輩方へ感謝申し上げます．

## 更新情報
:pencil: 2024/12/11 コンテナのボリュームを有効活用するよう一部修正しました
:pencil: 2024/12/13 Windowsに関する追記
:pencil: 2024/12/25 dockerに関する追記，終わりにに追記