砂丘の風紋 — 平らな砂は、平らでいられない
なにか
砂浜を手でならしても、風が吹けばまた同じような間隔で縞が戻ってくる。雪原でも、水槽の底でも、砂時計をひっくり返した表面でも同じことが起きる。その「同じような間隔」はどこから来ているのか。
砂の運ばれ方は 2 つに分かれる。跳躍——風に持ち上げられて弧を描き、少し先へ落ちる。クリープ——落ちてきた粒に叩かれて、その場をじりじり転がる。前者は砂を遠くへ運び、後者は凸凹をならす。西森と大内(1993)は、この2つだけを書いた最小の模型で風紋が出ることを示した。
この作品の要点は、飛距離がその場の高さで決まるところにある。出っぱったところにある砂粒は風をよく受けて遠くまで飛び、へこんだところの砂は近くに落ちる。それだけで平らな砂面は不安定になる。
波長は「選ばれる」——粗大化しない
初期のでたらめな凸凹(振幅 0.059)から始めると、60 歩でもう波長 11.2 の縞が立っている。そこから振幅は伸びるが 200 歩で止まり(1.578 → 4000 歩で 1.624)、波長も 11.1〜11.3 の幅から動かない。
これは当たり前のことではない。凸凹が育つ仕組みの多くは、時間とともにだんだん大きな構造へ移っていく(粗大化)。そうなると「波長」という量は時間の関数でしかなく、値として書けない。この模型ではそうならず、波長は定まった値に落ち着く。歩数を 1000 から 32000 まで 32 倍に振っても 11.10 → 11.89 の 7% しか動かない。
格子の刻みを振っても同じで、512/1024/2048 マスで波長の比は動かない。砂の総量は 10⁻¹¹ の幅で保存されている(跳んだぶんだけどこかに落ちるので当然だが、実装が壊れていないことの確認になる)。
★つまみが役割を分担している
「風紋の間隔は砂粒の飛距離で決まる」というのはよく聞く説明だが、模型で実際につまみを振ってみると、その言い方がどのくらい正確かが分かる。
- 跳躍距離を 4 → 20 に上げると波長は 6.14 倍になる(つまみは 5 倍)。ほぼ比例だが、比 λ/L₀ は 1.47 から 1.71 へ少し上がる——「比例」と言い切るには 1 割ほど足りない
- 高さへの敏感さを 1 → 8 に上げても波長は 1.18 倍にしかならない。いっぽう振幅は 0.26 倍に落ちる
- クリープを 0.05 → 0.6 に上げても波長は 0.84 倍。振幅は 0.21 倍
★つまみが3つあっても、波長を握っているのは1つだけだった。あとの2つは、波長をほとんど動かさずに「模様の濃さ」だけを変える。ユークリッドリズムで「均等の二つの条件が、個数と並べ方を分担していた」のと同じ形——複数のつまみがあるとき、それぞれが別々の量を握っていることがある。
ならしすぎると、模様は立たない
クリープを強くしていくと、あるところで模様が育たなくなる。しきい値は D* ≈ 0.615(種を5通り変えても 0.612〜0.619)。それより上では、初期のでたらめな凸凹がならされて終わる。チューリングや結晶のかたちと同じで、模様には「立つか立たないか」の境目がある。
⚠️このしきい値を測るとき、道具の癖に一度だまされた。最初は「振幅が初期の 5 倍を超えたら模様が立った」と判定していて、二分探索は D* = 1.098 を返した。ところが実物を見ると、そこで出ていたのは波長 2.01 ——格子2マスおきに上下する振動だった。クリープをあらわに解く書き方は D < 1 でしか安定でなく、その外では格子の振動が発散していただけである。振幅が大きい=模様がある、ではない。いまは「NaN でない」と「波長が3マス以上」を一緒に見るようにして、直す前は落ちる検査を置いてある。
★自分で動かす
下の絵はこの場で計算している(あらかじめ描いた絵の切り替えではない)。上の図を作っているのと同じ 1 本のコード gallery/assets/ripple-live.js を、記事の中で呼んでいる。歩数を動かすと、平らな砂から風紋が育つところがそのまま見える。
- 模様は立ったか
- —
- 波長
- —
- 振幅
- —
- 山の尖り(歪度)
- —
- 砂の増減
- —
スライダーで決めた条件で、その歩数まで進めた砂の断面
跳躍距離だけを動かして、波長の数字だけが素直について来ることを見てほしい。高さへの敏感さとクリープを動かすと、波長はほとんど動かないのに絵の濃さが変わる。クリープを 0.62 あたりより上げると、模様は立たなくなる。
どう作ったか
高さの列を輪にして、毎歩ぜんぶのマスから同じ量の砂を飛ばし、着地点で受け取る。着地は丸めずに線形補間で2マスに分ける——ここが要で、丸めると高さのちがい b·h が 1 マスに満たないうちは飛距離が全マス同じになり、模様が永久に育たない(実際にそれで固まって、原因を探した)。波長はスペクトルの山の近く3本の重みつき平均で読む(山の位置は整数の波数にしか置けないので、長い波長ほど階段状に飛ぶ)。機械検査は17件: ①平らな砂から模様が育つ ②砂の総量が保存(9通り)③★歩数を16倍に振っても波長が動かない(粗大化しない=波長は選ばれている)④格子を512→2048に振っても波長/跳躍距離が動かない ⑤掃引18通りすべてがしきい値の内側 ⑥★波長を動かすのは跳躍距離だけ(6.14倍/1.18倍/0.84倍)⑦★敏感さとクリープは振幅を動かす(0.26倍・0.21倍)⑧⚠️ただし波長/跳躍距離は1.41〜1.80と動く=「比例」と言い切るには足りない ⑨★クリープのしきい値が種5通りで再現(0.612〜0.619)⑩⚠️D=1.0 の「振幅が大きい」は格子2マスの振動=模様と数えない(直す前はこれをしきい値と誤っていた)⑪D=1.3 では発散する ⑫歪度が正=山が鋭い ⑬⚠️着地を丸めると模様が育たない(直す前に実際に固まった)⑭記事のドラッグ版を偽DOMで実際に走らせる14項目 ⑮出荷SVGとドラッグ版が同じ1本を共有 ⑯出荷SVG2枚。ドラッグ版の検査の実体は tools/ripple-live-test.js。
→ チューリング(こちらも「一様が壊れて波長が選ばれる」棚。しくみは反応拡散)
出典
- 数理
- H. Nishimori and N. Ouchi, "Formation of ripple patterns and dunes by wind-blown sand", Phys. Rev. Lett. 71 (1993) 197(跳躍+クリープの最小模型)。⚠️ 実物の砂紋の間隔・粒径・風速と突き合わせたわけではない——確かめたのは「この最小模型から何が出るか」だけで、実物との比較はしていない
- 生成
- generators/ripple.js(跳躍とクリープの1歩・スペクトルと波長の読み取り・模様が立ったかの判定・クリープのしきい値の二分探索・つまみを振る掃引・図2枚。依存ゼロ・乱数は初期の凸凹だけ=種を決めれば答えも決まる)砂の数理は gallery/assets/ripple-live.js に置き、出荷SVGの生成器と記事のドラッグ版が同じ1本を共有している