線状降水帯予測プロトタイプ Step 1 完了編 ー Open-MeteoとDIASのMSMを共通形式へそろえる
はじめに
線状降水帯の予測方法を試すため、まずは数値予報データを安定して扱えるところから作っています。
r001では、いきなり「線状降水帯を判定する」処理には進みません。
最初のStep 1では、
- Open-Meteo経由でJMA MSMを取得する
- DIASから気象庁MSMの実GRIB2を取得する
- 取得元の違いを吸収する
- 時刻、単位、格子の持ち方を共通化する
- Zarrへ保存する
- 後から同じデータを再検証できるようにする
ところまでを作りました。
今回、fix012をUbuntu 24.04 + Docker環境で実データまで通し、Step 1の完了条件をすべて確認できました。
最終的な流れは次のようになります。
Step 2以降は、Open-MeteoかDIASかを意識せず、この共通データだけを使います。
Step 1の完成条件
Step 1では、次の状態までを完成条件にしました。
また、ソースについても、
Docker build
Ruff format
Ruff check
mypy
pytest
source contract
package source check
をすべて通すことにしました。
1. 実行環境
今回確認した環境です。
OS Ubuntu 24.04
Container Docker Engine / Docker Compose
Python Dockerコンテナ内
計算基盤 NumPy
GRIB2 cfgrib + ecCodes
格子保存 xarray + Zarr
静的検査 Ruff / mypy
テスト pytest
Step 1はデータ取得と形式変換が中心なので、GPUは使っていません。
Numbaもまだ必須にはしていません。
3時間積算や雨域処理を実装するStep 2以降で、実際の処理時間を計測してから必要な箇所だけ使う予定です。
2. fix012をビルドする
参考のために、ZIPファイルを置いておきますので、ダウンロードして活用してください。
まず環境変数ファイルを作ります。
cp .env.example .env
sed -i "s/^LOCAL_UID=.*/LOCAL_UID=$(id -u)/" .env
sed -i "s/^LOCAL_GID=.*/LOCAL_GID=$(id -g)/" .env
Docker imageをクリーンビルドします。
docker compose build --no-cache
今回の実機では、Ubuntu 24.04をベースにしたimageが正常に作成できました。
Image linear-rainband-step1:r001-fix012 Built
3. 配布物を8項目で検証する
ビルド後、Step 1の検証スクリプトを実行します。
docker compose run --rm step1 \
./scripts/validate_package.sh
最終結果は次のようになりました。
[1/8] Environment / dependency consistency
No broken requirements found.
linear-rainband-step1 0.1.12
ruff 0.16.7
mypy 1.20.2
[2/8] Shell / Python syntax
[3/8] ruff format
25 files already formatted
[4/8] ruff check
All checks passed!
[5/8] mypy
Success: no issues found in 18 source files
[6/8] pytest
........................... [100%]
[7/8] CLI / source contract
SOURCE CONTRACT PASS
[8/8] package source files
PACKAGE SOURCE CHECK PASS
PASS: Step 1 fix012 package validation
ここで、
- Python構文
- Shell構文
- Ruff format
- Ruff lint
- mypy
- pytest
- CLI
- Provider契約
- 配布ファイル構成
までをまとめて確認しています。
4. Open-MeteoからJMA MSMを取得する
最初のデータ源としてOpen-MeteoのJMA MSMを使います。
実行は次のコマンドです。
docker compose run --rm step1 \
rainband-step1 fetch-openmeteo \
--config config/step1.yaml
今回の設定では、
緯度 34.60 ~ 35.00
経度 137.50 ~ 138.00
緯度間隔 0.05°
経度間隔 0.0625°
予報時間 12時間
を対象にしています。
実際の取得結果は、
run_id latest_20260914T141745Z
shape (12, 9, 9)
となりました。
内部では、
Open-Meteo API
↓
地点ごとの時系列
↓
time × latitude × longitude
↓
ForecastRun
↓
Zarr
↓
manifest
という形に変換しています。
5. Open-Meteo側の保存結果をverifyする
生成したmanifestを指定して再検証します。
docker compose run --rm step1 \
rainband-step1 verify \
--manifest \
/workspace/data/manifests/openmeteo_jma_msm/latest_20260914T141745Z.json
結果は、
run_id latest_20260914T141745Z
raw_files 2
grid ...latest_20260914T141745Z.zarr
status PASS
でした。
manifestの中身も確認しました。
{
"dimensions": {
"time": 12,
"latitude": 9,
"longitude": 9
},
"variables": [
"precipitation",
"source_latitude",
"source_longitude"
],
"time_start": "2026-09-14T14:00:00.000000000",
"time_end": "2026-09-15T01:00:00.000000000",
"time_interval_hours": [
1.0
],
"precipitation_min_mm": 0.0,
"precipitation_max_mm": 85.0999984741211,
"nan_count": 0
}
単にAPIへ接続できたというだけではなく、
- 実際の降水予測値が入っている
- 時間間隔が1時間
- 欠測がない
- Zarrとして保存できる
- 保存後に再読込できる
- manifestから再検証できる
ところまで確認しています。
6. DIASから実MSM GRIB2を取得する
DIASアカウント申請を登録して、ログインできることを確認してください。登録できていないのGPVのデータはダウンロードできません。
次に、Open-Meteoとは別経路で、DIASに保存されている気象庁MSMの実GRIB2を使います。
DIASの対象データセットは、
Dataset ID: GPV
です。
Step 1ではMSMの地上面データだけを対象にしました。
検索対象は、
MSM_GPV_Rjp_Lsurf
です。
今回は1つの初期時刻だけに絞り、
2026-06-12 21:00 UTC
について、次の3ファイルを取得しました。
Z__C_RJTD_20260612210000_MSM_GPV_Rjp_Lsurf_FH00-15_grib2.bin
Z__C_RJTD_20260612210000_MSM_GPV_Rjp_Lsurf_FH16-33_grib2.bin
Z__C_RJTD_20260612210000_MSM_GPV_Rjp_Lsurf_FH34-39_grib2.bin
DIASの共通ダウンロードシステムで検索結果をこの3ファイルに絞り、公式のdownload.pyを生成します。
取得には、この公式スクリプトを使います。
./scripts/run_dias_official_download.sh \
~/vol/linear_rainband_r001_step1_fix012/download.py
実際の結果は、
file_hashes.csv OK
...Lsurf_FH00-15_grib2.bin OK
...Lsurf_FH16-33_grib2.bin OK
...Lsurf_FH34-39_grib2.bin OK
DIAS download finished.
となりました。
DIAS内部のURLを推測してスクレイピングする処理は入れていません。
公式Web画面と公式download.pyを使っています。
usernameとpasswordが必要です。
7. 分割されたMSMファイルを確認する
ダウンロード後、Step 1側からファイルを確認します。
docker compose run --rm step1 \
rainband-step1 list-dias
結果は、
init_time_utc segments files
2026-06-12T21:00:00+00:00 FH00-15, FH16-33, FH34-39 3
でした。
つまり、
2026-06-12 21:00 UTC
│
├─ FH00-15
├─ FH16-33
└─ FH34-39
が、同じ初期時刻に属する3分割ファイルとして正しく認識できています。
8. 実MSM GRIB2から降水量を取り出す
ここは実データを使って初めて分かった点でした。
最初は、cfgribが返す変数名からprecipitationやtpを探していました。
しかし、実際のJMA MSM GRIB2では、その方法では降水量を安定して特定できませんでした。
そこで、変数名ではなく、GRIB2の公式パラメータ番号を使います。
MSM地上面の総降水量は、
parameterCategory = 1
parameterNumber = 8
です。
Step 1では、
GRIB2
↓
parameterCategory = 1
parameterNumber = 8
↓
総降水量
として直接抽出します。
これでcfgrib側の変数名に依存しなくなりました。
9. 実MSMでは降水量の単位がunknownになった
もう1点、実GRIB2を使って分かったことがあります。
cfgrib/ecCodesで総降水量を読み込むと、今回のファイルでは単位文字列が、
unknown
になりました。
そのままでは、
ValueError:
unsupported precipitation unit in MSM GRIB2: 'unknown'
として停止します。
ここでunknownを何でも許可するのは危険です。
そこでfix012では、
DIAS MSM Lsurf
↓
parameterCategory = 1
parameterNumber = 8
↓
公式に総降水量だと確認
↓
units = unknown
↓
公式仕様の kg/m² を採用
↓
1 kg/m² = 1 mmとして内部単位mmへ
としました。
重要なのは、unknownを一般的に許していないことです。
公式GRIB2キー、
parameterCategory = 1
parameterNumber = 8
で総降水量と確認できた経路だけに限定しています。
元の情報も、
source_units
source_grib_units
unit_resolution
として属性に残します。
10. DIASの3ファイルを1つのForecastRunへまとめる
修正後、実データを取り込みます。
docker compose run --rm step1 \
rainband-step1 import-dias-dir
実際の結果は、
run_id run_20260612T2100Z
shape (39, 505, 481)
manifest /workspace/data/manifests/dias_msm_archive/run_20260612T2100Z.json
init_time_utc=2026-06-12T21:00:00+00:00
segments=3
となりました。
つまり、
FH00-15
FH16-33
FH34-39
↓
1つのForecastRun
↓
time × latitude × longitude
↓
39 × 505 × 481
へまとめられています。
11. DIAS側もmanifestから再検証する
最後に、DIAS側もZarrと元ファイルを再検証します。
docker compose run --rm step1 \
rainband-step1 verify \
--manifest \
/workspace/data/manifests/dias_msm_archive/run_20260612T2100Z.json
結果は、
run_id run_20260612T2100Z
raw_files 3
grid /workspace/data/processed/grid/dias_msm_archive/run_20260612T2100Z.zarr
manifest /workspace/data/manifests/dias_msm_archive/run_20260612T2100Z.json
status PASS
でした。
manifestの内容は、
{
"dimensions": {
"time": 39,
"latitude": 505,
"longitude": 481
},
"variables": [
"precipitation"
],
"time_start": "2026-06-12T22:00:00.000000000",
"time_end": "2026-06-14T12:00:00.000000000",
"time_interval_hours": [
1.0
],
"precipitation_min_mm": 0.0,
"precipitation_max_mm": 107.0,
"nan_count": 0
}
となっています。
初期時刻は21:00 UTCですが、1時間降水量の最初の有効時刻は22:00 UTCです。
これは、
22:00の値
= 21:00~22:00の1時間降水量
として扱うためです。
この時間の持ち方は、次のStep 2で3時間積算雨量を作るときに重要となります。
12. 最終的に確認できたこと
Step 1では、最終的に次をすべて実データで確認できました。
| 項目 | 結果 |
|---|---|
| Docker build | PASS |
| Ruff format | PASS |
| Ruff check | PASS |
| mypy | PASS |
| pytest | PASS |
| source contract | PASS |
| package source check | PASS |
| Open-Meteo JMA MSM実通信 | PASS |
| Open-Meteo ForecastRun生成 | PASS |
| Open-Meteo Zarr保存・再読込 | PASS |
| Open-Meteo manifest verify | PASS |
DIAS公式download.py
|
PASS |
| DIAS MSM Lsurf 3ファイル取得 | PASS |
| DIAS分割ファイル認識 | PASS |
| 実MSM GRIB2読込 | PASS |
| GRIB2公式キーから降水量抽出 | PASS |
units=unknownの安全な解決 |
PASS |
| 3ファイルを1 ForecastRunへ結合 | PASS |
| DIAS Zarr保存・再読込 | PASS |
| DIAS manifest verify | PASS |
これでStep 1は完了です。
13. Step 1で作ったデータの境界
ここで大事なのは、Step 1ではまだ「線状降水帯らしい雨域かどうか」を判定していないことです。
Step 1は、
外部の数値予報データ
↓
信頼できる共通データ
までです。
最終的にStep 2へ渡すのは、
ForecastRun
├─ time
├─ latitude
├─ longitude
└─ precipitation [mm]
です。
Open-MeteoでもDIASでも、この形にそろえます。
この形を作っておけば、将来LFMやLEPSへ切り替えても、Step 2以降の処理を大きく作り直さずに済みます。
14. r001全体ではまだStep 1
今回完成したのは、r001全体のStep 1です。
r001
│
├─ Step 1 データ取得と共通形式 COMPLETE
│
├─ Step 2 3時間積算雨量
│
├─ Step 3 強雨域候補抽出
│
├─ Step 4 雨域追跡
│
├─ Step 5 複数予測比較
│
└─ Step 6 過去事例検証・地図表示
Step 6まで通したところで、MSMを使ったPrototype r001を完成とします。
r002以降は、
r002 LFM追加
r003 LEPS追加
r004 確率校正
r005 機械学習による補正
という流れを予定しています。
15. 次は3時間積算雨量
Step 2では、今回作ったForecastRunだけを入力にします。
まずNumPyで実装します。
処理は、
22:00
23:00
00:00
の3つの1時間降水量を足して、
21:00~00:00
の3時間積算雨量を作るところから始めます。
ここでも、欠測を0 mmとして埋める処理にはしません。
1時間間隔が連続していることを確認してから積算します。
Numbaを使うかどうかは、実データでNumPy版の処理時間を測った後に判断します。
まとめ
Step 1は、見た目には「データを読むだけ」の工程ですが、実データを通してみると、
- APIごとのデータ構造の違い
- GRIB2の分割ファイル
- cfgribが返す変数名
- GRIB2のパラメータ番号
- 単位文字列
unknown - 時刻の定義
- 保存後の再現性
- Docker内の権限
- Ruffやmypyを含む配布物検査
など、先に片付けておくべき点がかなりありました。
ここをStep 1で整理したことで、Step 2以降は、
どこから取ったデータか
ではなく、
ForecastRunの降水量をどう解析するか
に集中できます。
次は、この1時間降水量から3時間積算雨量を作ります。
参考資料
-
気象庁 メソモデル(MSM)格子点データ
https://www.data.jma.go.jp/suishin/cgi-bin/catalogue/make_product_page.cgi?id=MesModel -
気象庁 GRIB2仕様資料
https://www.data.jma.go.jp/suishin/catalogue/format/GmdCpd-GEPS-HGPV-JPN_format.pdf -
DIAS 気象庁提供のGPVアーカイブ
https://search.diasjp.net/ja/dataset/GPV -
DIAS 一括ダウンロードスクリプト利用マニュアル
https://data.diasjp.net/dl/pdf/DIAS_DL_script_manual_ja.pdf -
Open-Meteo JMA API
https://open-meteo.com/en/docs/jma-api


