1
0

はじめに

この記事は、Wernerモデルを使った風紋の生成 の続きです。

前回の記事では、Wernerモデルを使った風紋の生成方法を紹介しました。今回は、Wernerモデルのアルゴリズムを一部変更することで砂丘のパターンを生成します。

今回の記事で使用するコードは下記のリポジトリで公開しています。

使い方

主要なオプションのみ示します。 --help オプションで詳細を確認できます。

  • 広い領域をシミュレーションしたい場合にグリッドサイズを大きくしてください
  • 時間発展を長く追いたい場合にイテレーション数を大きくしてください
  • 砂丘生成モードを利用するには -d オプションをつけてください
  • 環境で利用できる砂量を -q オプションで指定してください
$ python simulate.py -h
usage: simulate.py [-h] [-w V] [-g N] [-i N] [-d] [-q Q] [-s N]
options:
  -g N, --grid_size N   grid size (default: 100)
  -i N, --num_iter N    number of iterations (default: 500)
  -d, --dune            apply settings for dune generation
  -q Q, --quantity Q    sand quantity. valid on dune generation (default: 1.0)

使用例

デフォルトで実行すると、砂量 1.0 で計算します。風はx方向(左から右)に吹いています。

python simulate.py -d -g 140

ランダムな高さの表面から三日月形のバルカン砂丘が生成され、風下に向かって流れていきます。クリックすると動画が開きます。

Dune pattern (sand quantity = 1.0) - YouTube

砂量を増やすとどうなるでしょうか。砂量 3.0 で計算してみます。

python simulate.py -d -q 3.0 -g 140

一列に並んだ尾根が形成され、隣り合う尾根と繋がったり離れたりしながら、風下に流れていきます。環境に供給される砂量が少ないとバルカン砂丘が、多いと横断砂丘が生成される観測事実と整合しています。

Dune pattern (sand quantity = 3.0) - YouTube

動作概要

Wernerモデルを使った風紋の生成 に示したアルゴリズムと大まかなフローは同じです。周期境界条件が設定されたグリッドに対して、差分法を適用します。風紋生成と同様に、次の時間ステップのグリッドを求めるために、2種類のルールを適用します。後述するように、ルールの内容が風紋生成とは一部異なっています。

for _ in range(num_iter):
    apply_jumping_rule()
    apply_rolling_rule()

apply_jumping_rule() 関数は風によって砂が飛ばされる過程を模擬しています。以下の現象を考慮します。

  • 勾配が大きいと砂の跳躍距離 jump_length が短い。勾配が小さいと砂の跳躍距離が長い
  • 勾配が大きいと飛砂量 sand_quantity が多い(上り坂)。勾配が小さいと飛砂量は少ない(下り坂)
  • 砂が無い場所($z < 0$)から砂は飛ばない

apply_rolling_rule() 関数は、砂が崩れる過程を模擬しており、拡散方程式の拡散項に相当する処理です。高さを均一にする効果があります。風紋のパターン生成と同じルールです。

パラメータチューニング

jump_lengthsand_quantity の関数形を、

z = \alpha + \beta \ \tanh \left( \gamma \ \frac{\partial z}{ \partial x} \right)

と仮定しています。$\alpha$, $\beta$, $\gamma$ はパラメータです。求める量が2つあるので、合計6個のパラメータがあります。今回は砂丘のパターンが明瞭に生成されるパラメータを見つけてハードコーディングしています。総当たりでパラメータを見つけましたが、数式を解析することでパラメータを見つけることもできると思います。

    def apply_jumping_rule(self) -> None:
        g = self.grid
        for _ in range(int(0.2 * g.x_size * g.y_size)):
            i, j = random.randint(0, g.x_size - 1), random.randint(0, g.y_size - 1)
            if g[i, j] > 0.0:
                jump_length = int(11.2 - 8.0 * np.tanh(8.0 * g.partial_x(i, j)))
                sand_quantity = 0.5 + 0.4 * np.tanh(2.0 * g.partial_x(i, j))
                g[i, j] -= sand_quantity
                g[i + jump_length, j] += sand_quantity

詳細はリポジトリを参照してください。

おわりに

Wernerモデルを使った砂丘パターンの生成方法と、代表的なパラメータにおける計算結果を示しました。簡単なルールを用いて現実の砂丘によく似たパターンが生成できて面白いと思いました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0