Fortranの次を考える ― Juliaで始める数値シミュレーション基盤
はじめに
数値シミュレーションの実装言語を考えるとき、Fortranは今でも有力な選択肢です。特に、長い期間をかけて検証されてきた河川・氾濫・流出解析のコードが手元にある場合、それを無理に置き換える理由はありません。
一方で、新しいモデルを試したり、パラメータ同定や機械学習と組み合わせたり、将来的にGPUへ展開したりすることまで考えると、もう少し試行錯誤しやすい環境も欲しくなります。
そこで今回は、数値計算用の新しい選択肢として Julia を試してみることにしました。
いきなり既存のFortranソルバーを置き換えるのではなく、まずは小さな1D河川計算から始め、同じ条件でFortran版と比較します。計算結果、水収支、計算時間を確認しながら、どこまでJuliaを使えるかを段階的に見ていく方針です。
この記事は、Juliaへ全面移行することを前提にしたものではありません。
既存のFortran実装を基準として残しながら、Juliaを新しい数値シミュレーション基盤の候補として評価していきます。
1. なぜJuliaなのか
少し前から、AI向けの新しいプログラミング言語としてMojoなども気になっていました。
ただ、今回やりたいことを整理すると、中心にあるのはAIそのものではありません。
降雨
↓
流出
↓
河川1D
↓
氾濫2D
↓
土砂移動
↓
観測値との比較
↓
パラメータ同定
まず必要なのは、こうした数値シミュレーションを無理なく書けることです。
その上で、
- 最適化
- 自動微分
- パラメータ同定
- 機械学習
- GPU計算
へ広げられることが重要になります。
この条件で考えると、最初に試す言語としてJuliaはかなり自然な選択でした。
2. Juliaは数値計算を前提に設計されている
Juliaは、高水準な記述と数値計算性能の両立を目的として設計された言語です。
公式ドキュメントでは、型推論とJITコンパイル、LLVMを利用してコードを生成すること、optional typingやmultiple dispatchを備えていることが説明されています。
また、数値計算では重要になる配列処理についても、単純なループを避けて必ずベクトル化しなければならない言語ではありません。Juliaの公式ドキュメントでも、スカラーの配列アクセスを含む通常のループを、読みやすい形のまま効率よく実行できることが説明されています。
これは、自分で数値ソルバーを書く場合にはかなり大きな利点です。
例えば、2次元配列に流入量を加えるだけなら、素直に次のように書けます。
function add_source!(h, source, dt)
for j in axes(h, 2)
for i in axes(h, 1)
h[i, j] += dt * source[i, j]
end
end
return nothing
end
! は、「この関数が引数の内容を変更する」というJuliaで一般的に使われる命名上の慣例です。
水理計算では、毎タイムステップで大きな配列を更新します。新しい配列を何度も作るより、必要な配列を再利用する方が扱いやすい場面が多いため、この書き方は相性が良さそうです。
3. Fortranから見ても違和感が少ない
Juliaを試してみようと思った理由の一つに、Fortranとの距離が比較的近いことがあります。
Juliaの多次元配列は**列優先(column-major)**です。
これはFortranと同じです。
Fortran
│
├─ column-major array
│
Julia
│
└─ column-major array
もちろん、言語仕様やメモリモデルまでFortranと同じという意味ではありません。
ただし、既存の数値計算コードを読みながらJuliaへ移していく場合、配列の走査順序を考えるうえで共通点があるのは助かります。
例えば2次元配列なら、Juliaでは先頭の添字を内側のループにする形が基本になります。
for j in axes(a, 2)
for i in axes(a, 1)
a[i, j] = a[i, j] + 1.0
end
end
このあたりは、Fortranで格子計算を書いてきた場合にも理解しやすい部分です。
4. Pythonを捨てるためのJuliaではない
Juliaを導入するとしても、Pythonをなくす必要はありません。
役割を分けた方が分かりやすいと考えています。
想定している役割は次のようになります。
| 役割 | 主な候補 |
|---|---|
| Web UI | OpenLayers / TypeScript |
| API・Run管理 | RustまたはPython |
| 数値ソルバー | Julia |
| 基準となる既存計算 | Fortran |
| データ解析・既存AI資産 | Python |
| 将来のGPU計算 | Julia + CUDA.jl など |
Juliaにすべてを集約するのではなく、数値計算を担当させます。
5. JuliaとFortranは競争させない
今回、一番避けたいのは、
Juliaの方が新しいからFortranより良い
という進め方です。
数値計算では、新しい言語へ書き換えただけで正しくなるわけではありません。
むしろ、すでに動いているFortran版があるのであれば、それを基準計算として利用できます。
この形であれば、Julia版を作るたびに既存結果と比較できます。
言語の移植作業と、数値モデルそのものの改良を混ぜないことも重要です。
6. 最初から2D氾濫計算には行かない
最終的には2D氾濫計算まで試したいと考えています。
ただし、最初から大きなモデルをJuliaへ持っていくと、計算結果が違ったときに原因を切り分けにくくなります。
そのため、最初は単純な1D河道から始めます。
上流境界
↓
Node 001
│
Reach 001
│
Node 002
│
Reach 002
│
Node 003
↓
下流境界
まず確認するのは、
- 1本の河道
- 単純な初期条件
- 明確な上流・下流境界
- 少ない計算点
- 一定の時間刻み
程度です。
ここでFortran版と計算結果を比較できる状態を作ってから、合流や分岐へ進みます。
7. 最初のロードマップ
現時点では、次の順番を考えています。
| Version | 内容 |
|---|---|
r001 |
Julia実行環境と基本的な数値計算 |
r002 |
単一河道の1D計算 |
r003 |
Node–Reach河道網 |
r004 |
河道区間への横流入 |
r005 |
降雨流出との接続 |
r006 |
2D氾濫計算 |
r007 |
1D–2D連成 |
r008 |
並列化・GPU検証 |
r009 |
パラメータ同定・AI連携 |
流れを図にすると、次のようになります。
この段階では、GPU化を急ぎません。
まずCPU版で計算結果を一致させ、その後に高速化へ進みます。
8. SciMLを使えることも大きい
Juliaには、Scientific Machine Learningを扱うSciMLのエコシステムがあります。
その中でも DifferentialEquations.jl は、微分方程式を扱うための代表的なパッケージです。
2026年時点の DifferentialEquations.jl v8では、using DifferentialEquations で読み込まれる範囲が整理され、基本的にはODEソルバーが中心になっています。SDE、DDE、境界値問題などは、それぞれ対応するパッケージを明示的に利用する構成です。
ここは古い記事を読むと現在の構成と違う場合があるので注意が必要です。
今回の河川・氾濫ソルバーを、最初から DifferentialEquations.jl に載せる必要はありません。
Local Inertial法や有限体積法のように、自分で格子更新を書く部分については、普通のJuliaコードとして実装できます。
その上で、
- 時間積分
- 非線形方程式
- 感度解析
- 自動微分
- パラメータ推定
など、SciMLを利用する意味があるところから取り入れていく方がよさそうです。
特に SciMLSensitivity.jl では、微分方程式の解に対する感度解析や、自動微分との連携が用意されています。
これは将来的なモデル同定を考えると興味深い部分です。
観測水位・観測流量
│
▼
Julia Solver
│
▼
計算結果
│
▼
誤差評価
│
▼
パラメータ更新
例えば粗度係数や流出モデルのパラメータを調整するとき、こうした仕組みを利用できる可能性があります。
ただし、実際の水理モデルで自動微分がそのまま有効かどうかは、モデルを実装してから確認する必要があります。分岐処理、乾湿境界、制限関数などを含む計算では、単純なODEの例と同じようには扱えない場合があります。
ここは実装後に検証します。
9. GPUはCPU版を確認してから
Juliaでは CUDA.jl を利用してNVIDIA GPUを扱えます。
公式ドキュメントでは、高水準なGPU配列から、低レベルなCUDAカーネルまで扱えることが説明されています。
そのため将来的には、
Julia Solver
│
├── CPU Array
│
└── GPU Array
↓
NVIDIA GPU
という構成も検討できます。
ただし、最初からGPUへ移すつもりはありません。
水理計算では、
- 隣接セル参照
- 境界条件
- wet / dry処理
- 構造物
- 1D–2D接続
- 河川ネットワーク
などが入るため、単純な行列演算とは違います。
まずCPU上で正しい計算を作り、プロファイルを取って、本当に時間を使っている場所を確認してからGPU化を考えます。
10. 実行環境はDockerで固定する
最初の検証では、Juliaのバージョンを固定します。
2026年10月5日時点で、Julia公式サイトのCurrent Stable Releaseは Julia 1.13.1 です。リリース日は2026年9月25日です。
Docker Hubにも公式のJuliaイメージがあり、1.13.1-bookworm タグが公開されています。
最初はこれを使います。
Dockerfile
FROM julia:1.13.1-bookworm
WORKDIR /workspace
CMD ["julia", "--project=."]
compose.yaml
services:
julia:
build:
context: .
working_dir: /workspace
volumes:
- .:/workspace
REPLを起動する場合は、
docker compose run --rm julia
スクリプトを実行する場合は、
docker compose run --rm julia julia --project=. src/main.jl
とします。
latest ではなくバージョンを固定するのは、後から同じ条件で計算を再現しやすくするためです。
11. 最初のディレクトリ構成
r001 は大きくしません。
例えば次の程度から始めます。
julia_hydraulic_r001/
├── Dockerfile
├── compose.yaml
├── Project.toml
├── Manifest.toml
├── src/
│ ├── main.jl
│ └── solver/
│ └── numerical_core.jl
├── test/
│ └── runtests.jl
└── cases/
└── simple_channel/
├── input/
└── expected/
cases/simple_channel/expected には、既存のFortran版で計算した基準結果を置けるようにします。
これによって、
Juliaコードを変更
↓
テスト実行
↓
Fortran基準結果と比較
という流れを作れます。
12. 性能比較で気をつけること
JuliaはJITコンパイルを使います。
そのため、最初の1回だけを測定してFortranと比較すると、コンパイル時間が混ざります。
比較するときは、
Julia起動
↓
初回実行
↓
コンパイル
↓
ウォームアップ完了
↓
繰り返し計測
と分けて考える必要があります。
定常的なソルバー性能を見る場合は、初回コンパイルとは分離して測ります。
Juliaでは BenchmarkTools.jl も広く使われているため、性能比較用のテストではこれを利用する予定です。
また、Fortran側についても、
- コンパイラ
- 最適化オプション
- 浮動小数点精度
- OpenMPの有無
- CPUスレッド数
を記録します。
単純に「Juliaが何秒、Fortranが何秒」だけでは比較しません。
13. 高速化は正しさを確認してから
Juliaには、
@inbounds
@simd
など、高速化に利用できる仕組みがあります。
ただし、最初から使うつもりはありません。
例えば @inbounds は配列境界チェックを省略するため、添字が間違っているコードに付けると問題を見つけにくくなります。
@simd についても、反復間に依存関係がないことなど、前提を理解したうえで使う必要があります。
最初は、
正しいコード
↓
テスト
↓
Fortranとの比較
↓
プロファイル
↓
必要な部分だけ高速化
とします。
数値ソルバーでは、数%速くすることより、まず計算結果を説明できることの方が重要です。
14. 「一致」の判断も決めておく
数値計算では、異なる言語やコンパイラで完全に同じビット列になるとは限りません。
浮動小数点演算の順序や、コンパイラ最適化などで微小な差が出ることがあります。
そのため、Julia版の確認では単純なファイル一致ではなく、少なくとも次を見ます。
| 項目 | 確認内容 |
|---|---|
| 流量 | 時系列、ピーク値、ピーク時刻 |
| 水位 | 時系列、最大値 |
| 水収支 | 流入・流出・貯留量の整合 |
| 境界条件 | 指定値が正しく反映されているか |
| 安定性 | NaN、Inf、異常振動がないか |
| 計算時間 | ウォームアップ後の実行時間 |
| 再現性 | 同じ入力で同じ結果が得られるか |
許容誤差については、モデルと使用する浮動小数点精度を決めた段階で設定します。
ここを曖昧なまま「一致した」と判断しないようにします。
15. JuliaとMojoはどちらか一方ではない
今回、最初にJuliaを選びましたが、Mojoを候補から外したわけではありません。
役割が少し違うと考えています。
| 観点 | Julia | Mojo |
|---|---|---|
| 数値計算環境 | すでに充実 | 発展中 |
| 科学技術計算のパッケージ | 多い | これから |
| 自作ソルバー | 向いている | 向いている |
| 低レベル制御 | 可能 | 強く意識されている |
| GPU | CUDA.jlなど | 重要な設計対象 |
| 今回の最初の検証 | 向いている | 後段で検討 |
まずJuliaでモデルを作り、性能上の問題が具体的に見えてから、その部分を別の実装へ切り出す方が判断しやすいはずです。
最初から言語を増やす必要はありません。
16. 目指す形
最終的には、次のような構成を考えています。
Juliaを単体の計算プログラムとして使うのではなく、Webから実行できるシミュレーション基盤の数値計算コアとして扱います。
既存のFortranソルバーは、少なくとも移行・検証期間中は残します。
17. まずはr001から
最初にやることは地味です。
Juliaを起動する
↓
配列計算を確認する
↓
テストを書く
↓
小さな数値計算を書く
↓
Dockerで同じ結果を再現する
ここまでを r001 とします。
その次の r002 で、単一河道の1D計算へ進みます。
この段階では、新しい数値手法を追加しません。既存のFortran版とできるだけ同じ条件にそろえ、言語を変えたことで結果がどうなるかを確認します。
その比較ができてから、Node–Reach化、横流入、降雨流出、2D氾濫へ進めます。
18. まとめ
Juliaを選んだ理由は、「新しい言語だから」ではありません。
今回必要なのは、
数値計算を書きやすい
+
性能を確認しながら改善できる
+
Fortranと比較しやすい
+
将来、最適化やAIへ広げられる
+
GPUも検討できる
という環境です。
Juliaは、この条件を試す価値がある言語だと考えています。
ただし、実際に水理計算へ使えるかどうかは、これから確認します。
まずは、
Fortran版とJulia版で同じ1D河川計算を実行し、流量・水位・水収支・計算時間を比較する。
ここから始めます。
次回は r001 として、Docker上にJulia 1.13.1の実行環境を作り、配列計算、テスト、簡単な数値計算まで動かしてみます。
参考資料
-
Julia Documentation
https://docs.julialang.org/ -
Julia Performance Tips
https://docs.julialang.org/en/v1/manual/performance-tips/ -
Julia Arrays
https://docs.julialang.org/en/v1/manual/arrays/ -
Julia Downloads
https://julialang.org/downloads/ -
DifferentialEquations.jl
https://docs.sciml.ai/DiffEqDocs/stable/ -
SciMLSensitivity.jl
https://docs.sciml.ai/SciMLSensitivity/stable/ -
Julia Official Docker Image
https://hub.docker.com/_/julia



