03. 1 次元移流と周期境界
解く方程式
時間積分:陽的オイラー法
空間離散化:1 次精度の風上差分。
a > 0なので、左隣との差を使います。
時間刻みは、クーラン数 \(C = a \Delta t / \Delta x\) が 0 より大きく 1 以下になるように選びます。 ここでは \(C = 0.37\) を使います。
格子とゴーストセル
物理セル NX 個の左右に、1 点ずつゴーストセルを置きます。
風上差分は左隣 u[i-1] しか読まないので左の 1 点で足りますが、MISO と同じく左右対称に置きます。
周期境界では、毎ステップ、ゴーストセルを反対側の物理セルの値で埋めてから更新します。
index 0 | 1 ... NX | NX+1
ghost physical cells ghost
実装する内容
長さ
NX + 2の配列uとu_newを、それぞれArray1D<double, Backend>として確保します。物理セルの初期値は周期的なガウシアンとします。 \(L = 1\)、\(x_0 = 0.5\)、\(\sigma = 0.05\) とし、物理セル
iの座標をx = (i - 1) * L / NXとします。 \(d = x - x_0\) を周期の幅 \(L\) で折り返して \(-L/2 \le d \le L/2\) にしてから、\(\exp(-d^2 / 2\sigma^2)\) を計算します。 折り返しにはutil::fmod(d, L)(dをLで割った余り。結果は-LとLの間)を使い、余りがL/2より大きければLを引き、-L/2より小さければLを足します。この初期値の式は、厳密解との比較でも使うので関数にします。 ラムダ式の中から呼ぶので、
__host__ __device__ inline double gaussian(double x)のように__host__ __device__を付けておきます(CPU ビルドでは何もしない指定です)。各ステップの前に、左のゴーストセル
u[0]へ最後の物理セルu[NX]を、右のゴーストセルu[NX + 1]へ最初の物理セルu[1]を写します。 この処理も、Range1D{0, 1}(1 回だけ実行する範囲)のfor_eachで書きます。物理セルの範囲
Range1D{1, NX + 1}のfor_eachで、更新前のuを読み、更新後の値をu_newへ書きます。ステップの最後に
std::swap(u, u_new)で 2 つの配列の役割を入れ替えます。std::swapには<utility>を読み込みます。
Array1D はコピーできませんが、ムーブはできます(第 4 章)。
std::swap はムーブを使って中身のポインタを入れ替えるので、要素はコピーしません。
入れ替えの後、u という名前の変数は前の u_new のメモリを持っています。
View は取り出したときのポインタを持ち続けるので、入れ替えの後は u.view() で View を取り直します。
ゴーストセルを埋める処理と更新の処理を、それぞれ配列を引数に取る関数にして、関数の中で View を取り出すと、取り直しを忘れません。
更新前の配列へ途中で書き込まないので、各セルは同じ時刻の隣の値を読めます(第 7 章の「実行順に頼らない」)。
結果の確認
まず NX = 256 で 1 ステップだけ計算し、同じ入力とゴーストセルからホスト側の for 文で求めた値と比べます。
初期の振幅 1 を基準に、最大絶対誤差が 1e-14 以下であることを確かめます。
次に時間を進め、初期分布を距離 \(a t\) だけ平行移動した厳密解と比べます。 厳密解は、初期値の式の \(x\) を \(x - a t\) に置き換え、同じ周期の折り返しを使って求めます。 相対 L2 誤差は、物理セルだけで次のように求めます。
分子と分母の和は、前の課題の reduce で求められます(reduce を 2 回呼びます)。
NX を 256、512、1024 と増やし、\(C\) と終了時刻を揃えて誤差の変化を調べます。
たとえばステップ数を 200、400、800 とすれば、同じ時刻まで進みます。
この方法は 1 次精度なので、十分細かい格子では、格子幅を半分にすると誤差もおよそ半分になります。
CPU で 1 ステップの比較と時間発展を確かめたら、第 8 章の MPI へ進めます。 GPU で同じ計算を動かす準備は、第 9 章で学びます。
GPU で実行する段階
CUDA でも同じ入力で、1 ステップの比較と時間発展を行います。
ラムダ式の中から呼ぶ gaussian には __host__ __device__ が必要です。
FMA などにより、CPU と GPU では丸めが異なる場合があります。
完全一致を一律に求めず、上の許容誤差と、格子を細かくしたときの誤差の変化で確かめます。