粒子法による
3
次元地形の侵食解析プログラムの開発
大坪晃輔
草間晴幸
1
研究背景
/
目的
本研究では
CG
で表現される地形形状に関し,
3
次元地形に
対して侵食シミュレーションを行う手法について取り扱う.シ
ミュレーションはボクセルデータによる
3
次元地形と,粒子法
(SPH
法
)
による流体の組み合わせによって構成される.流体を
表す粒子の流速といった情報を参照し
,
ボクセルデータの濃度
値を変更することで地形の侵食を再現できる.
粒子法は流体解析の手法であり,格子法に比べ液体の自由表
面を取り扱うことに優れている
1).
Kriˇstof(2009)
2)では
SPH
法による流体を用い,高さマップに水流を流すことで,洗掘と
堆積の現象を再現している.また粒子法と剛体等の衝突ついて
は多くの研究がなされている.本研究でも流体とボクセル地形
との相互作用は主要な問題となるが,
Harada(2007)
3)の壁境
界計算手法を参考とする.
侵食計算に関してはボクセルデータへ適用するための計算を
行うが,実際に堤防の侵食に関する室内実験を行っている
Fujisawa(2011)
4)を参考に結果の検証を行う.
最終的には侵食シミュレーションを用いて,
3
次元地形を侵
食し地形形状を得ることを目的とする.
また侵食シミュレーションは開発環境として
Unity3D(Unity
Technologies http://unity3d.com/)
を用いており,同ゲーム
エンジンでコンパイルされる
C#
スクリプトによって処理の大
部分が記述されている.これらのコードの一部と照会しつつシ
ミュレーションの内容について紹介するが,
Unity3D
のライブ
ラリを利用している部分がある.
2
粒子法による流体解析
2.1 SPH
法
粒子法では流体を多くの粒子の集合として表現する.流体の
支配方程式は格子法を用いる場合には次のように表される
5).
このとき
v
は流速,
ρ
は密度,P
は圧力,
g
は重力等の外力,
µ
は粘性係数を表す.
∂ ρ
∂
t
+
∇ · (ρv) = 0
ρ
(
∂v
∂
t
+ v
· ∇v
)
=
−∇P + ρg + µ∇
2v
(1)
最初の式が質量保存則を表し,
2
つ目の式が運動量保存則を表し
図
-1
シミュレーションの様子
ている.粒子法における流体を記述する場合,粒子が受け持つ
質量が一定であることから,質量保存則を省略することができ
る.また粒子が流速と共に移動することから,移流項
v
· ∇v
も
また省略し,左辺を統合して
Dv/Dt
とすることができる.
実 際 の 粒 子 の 速 度 変 化 の 計 算 に つ い て は ,式
(1)
と ,
Becker(2007)
6)で採用された右辺各項についての近似式に基づ
いて行う.
ある粒子における密度,圧力といった物理量は周囲の粒子に
重み関数をかけた離散化式によって求めることができる
5).こ
のとき
W
が重み関数であり,h
は重み関数の影響半径を表す.
r
は座標を表し,m
は質量,また添字の
j
は近傍粒子を表す.
A
S(r) =
∑
jm
jA
jρ
jW (r − r
j,
h)
(2)
シミュレーションは
0.002
秒
(
本シミュレーションの場合
)
毎
にステップを繰り返すことによって進行するが,
1
ステップの
内容は次のようになる.
1.
近傍粒子探索格子の更新
2.
密度・圧力の計算
3.
圧力項・粘性項の計算
4.
粒子の移動
またこの計算ステップは
C#
のコードでは次のように実装されている.
ソースコード
1
計算ステップ
public virtual void s t e p _ f r a m e (){ grid . u p d a t e _ g r i d () ; // p r o c e s s 1 // p r o c e s s 2 grid . d e l e g a t e _ v a l i d (new P a r t i c l e G r i d . N e i g h b o r P r o c e s s ( c a l c _ d e n s i t y _ p r e s s u r e ) ) ; // p r o c e s s 3 grid . d e l e g a t e _ v a l i d (new P a r t i c l e G r i d . N e i g h b o r P r o c e s s ( c a l c _ f o r c e ) ) ; // p r o c e s s 4 grid . d e l e g a t e _ v a l i d (new P a r t i c l e G r i d . P r o c e s s ( c a l c _ p o s i t i o n ) ) ; }
計算では各粒子について幾つかの計算結果を保持し,参照す
る必要があるが,粒子は次のような形で情報を保持している.
ソースコード
2
粒子の持つ情報
public struct P a r t i c l e S t a t e {public bool isFixed ;
public float density , p r e s s u r e ; public V e c t o r 3 velocity , force ; public float b o u n d a r y ;
public V e c t o r 3 b o u n d a r y _ g r a d i e n t ; }
isFixed
は粒子を固定するかの判定であり,流体を表す際に
は
false
に設定される.
density
,
pressure
は密度・圧力計算の
結果で,圧力項と粘性項計算の際に利用する.
force
は圧力項,
粘性項,外力の合計で位置移動の際に利用,
velocity
は位置移
動と合わせて更新する.
boundary
,
boundary gradient
は壁
境界計算の結果を格納する値である.
2.2
近傍粒子探索
粒子法の計算では近傍粒子の探索を効率的に行うことが重要で
ある.本研究では
Fujisawa(2013)
7)を参考に,
Green(2010)
8)と同様の手法を実装した.
まず計算範囲となる空間を格子状に分割する.この格子と粒
子の関係を図
-2
に示す.
図
-2
格子と粒子の関係
粒子は何れかのセル
(
格子で分割された領域
)
に内包されるこ
とになるが,この時セルの
1
辺は粒子の重み関数の影響範囲の
半径よりも長い
.
したがって中央の太線で示されたセルについ
て見る場合,内包された粒子がセルの外縁に寄っている場合でも,
点線で囲われた
9
つの近傍セルより外には影響半径が届かない
ことが保証されている.線分で結ばれた粒子を見ても,
9
つの
セルに入らない限り近傍粒子
j
に含む必要がないことが分かる.
これを利用し,最終的に近傍粒子
j
を
NeighborCue
に格納す
ることで以後の粒子法の計算を可能としている.
2.3
密度・圧力の計算
最初に式
(2)
から密度を求める
5).
ρ
S(r) =
∑
jm
jρ
jρ
jW
poly6(r − r
j,
h) =
∑
jm
jW (r − r
j,
h)
C#
コードでは次のように表される.また以下のコードで示
される関数は
1
つの粒子に対する処理を記述したものであり,
全ての有効な粒子に対して引数
i
を変えて呼ばれることになる.
また第二引数
neighbor cue
には該当粒子i
からみた近傍粒子が
格納されている.
ソースコード
3
密度・圧力の計算
void c a l c _ d e n s i t y _ p r e s s u r e (int i , int[] n e i g h b o r _ c u e ) {
float weight = 0.0 f ;
P a r t i c l e center = grid . p a r t i c l e s [ i ]; // Sum N e i g h b o r P a r t i c l e s
for(int cue_i =0; cue_i +1 < n e i g h b o r _ c u e . Length ; cue_i +=2) {
int start = n e i g h b o r _ c u e [ cue_i ]; int end = n e i g h b o r _ c u e [ cue_i + 1]; if( start == -1)
c o n t i n u e;
for(int cue_j = start ; cue_j < end ; ++ cue_j ) weight += p o l y 6 _ k e r n e l ( V e c t o r 3 . D i s t a n c e ( grid .
p a r t i c l e s [ grid . g e t _ i n d e x ( cue_j ) ]. position , center . p o s i t i o n ) , e f f e c t i v e R a d i u s ) ; } p a r t i c l e _ s t a t e s [ i ]. d e n si t y = weight * mass ; p a r t i c l e _ s t a t e s [ i ]. p r e s s u r e = p r e s s u r e C o e f f i c i e n t * ( Mathf . Pow ( p a r t i c l e _ s t a t e s [ i ]. d e n s i t y / initialDensity , 7.0 f ) - 1.0 f ) ; }
関数内には総和
∑
jを行うための
2
重ループが存在している.
ループ内では
weight
に対して関数
poly6 kernal
の戻り値が加
算されていくが,これが重み関数を表す.重み関数は用途に
よって異なるものを使用し,密度計算では
Poly6
カーネルを用
いるが,その形状を図
-3
の左に示す.
これを次の式によって求める.
W
pol y6(r, h) = α
(h
2− r
2)
3(0 ≤ r ≤ h)
0
otherwise
ただし
α =
4
π
h
8,
315
64π
h
9(2D, 3D)
1.0 -1.0 1.0 2.0 1.0 -1.0 -15 -10 -5
(a) poly6 (b) spiky(gradient)
図
-3
重み関数
W
poly6,∇W
spikyまた
C#
コードでは次のように表される.
ソースコード
4 Poly6
カーネル
public float p o l y 6 _ k e r n e l (float r , float h ) {
if( r < h )
return c o n s t a n t _ p o l y 6 * Mathf . Pow ( h * h - r * r , 3.0 f ) ;
else
return 0.0 f ; }
float c o n s t a n t _ p o l y 6 = 315.0 f / (64.0 f * Mathf . PI * Mathf . Pow ( e f f e c t i v e R a d i u s , 9.0 f ) ) ;
尚,
constant poly6
は定数であり,その他の重み関数でも同様
に定数を用いている.掲載コードでは定義時に値を計算している
が,
effectiveRadius(
重み関数の影響半径
)
がコンパイル時に不
定である場合には,定義より後の適切なタイミングで計算を行う.
圧力は密度から求めることができるが,
M¨
uller(2003)
5)と同
じく定常状態からの変位を圧力として用いる
9).
P = k(ρ − ρ
0)
(
k
はガス定数
)
しかしながら本シミュレーションでは,次節で述べる圧力
項・粘性項による
Dv/Dt
の計算を
6)
に基いて行うため,次の
式で圧力を計算する.
P = B
((
ρ
ρ
0)
γ− 1
)
ただし
γ = 7,
B =
ρ
0c
2 Sγ
Becker(2007)
6)では計算領域の寸法などからC
S≈ 88.5m/s
,
B ≈ 1119kPa
と し て い る .本 研 究 で は
C
S≈ 140.0m/s
,
B ≈ 2795kPa
として良好な結果を得ている.
2.4
圧力項・粘性項の計算
圧力項は流体の圧力を均衡に保つよう作用する力で,次式で
適用する
6).
Dv
Dt
=
−
∑
jim
jP
iρ
2 i+
P
jρ
2 j∇W
spiky(r
i− r
j,
h)
また粘性項を次式で適用する
6).
Dv
Dt
=
−
∑
jim
jΠ
i j∇W
spiky(r
i− r
j,
h) (v
i j· r
i j< 0)
0
(v
i j· r
i j≥ 0)
だたし
Π
i j=
−ν
(
v
i j· r
i j|r
i j|
2+ ε
h
2)
, ν =
2α
hc
Sρ
i+ ρ
jα
は
0.08
から
0.5
の間で選択すると良いとされる.ここで
ε
h
2は
r = 0
の場合に分母が
0
となる事を防ぐための値であり,
ε = 0.01
である.尚,
v
i jは
v
i− v
jを指し,粒子
j
から見た粒子
i
の流速
を表す.したがって
2
つの粒子が近づく際の緩衝を記述している.
C#
コードでは次のように表される.
ソースコード
5
圧力項・粘性項の計算
void c a l c _ f o r c e (int i , int[] n e i g h b o r _ c u e ) { P a r t i c l e center = grid . p a r t i c l e s [ i ]; p a r t i c l e _ s t a t e s [ i ]. force = V e c t o r 3 . zero ; V e c t o r 3 p r e s s u r e _ f o r c e = V e c t o r 3 . zero , v i s c o s i t y _ f o r c e = Vector3 . zero ; V e c t o r 3 r e l a t i v e P o s i t i o n , r e l a t i v e V e l o c i t y , t e m p _ k e r n e l ; float c e n t e r _ p r e s s u r e = p a r t i c l e _ s t a t e s [ i ]. p r e s s u r e /
Mathf . Pow ( p a r t i c l e _ s t a t e s [ i ]. density , 2.0 f ) ; for(int cue_i =0; cue_i +1 < n e i g h b o r _ c u e . Length ; cue_i +=2) {
int start = n e i g h b o r _ c u e [ cue_i ]; int end = n e i g h b o r _ c u e [ cue_i + 1]; if( start == -1)
c o n t i n u e;
for(int cue_j = start ; cue_j < end ; ++ cue_j ) {
int j = grid . g e t _ i n d e x ( cue_j ) ;
r e l a t i v e P o s i t i o n = center . p o s i t i o n - grid . p a r t i c l e s [ j ]. p o s i t i o n ; float r = r e l a t i v e P o s i t i o n . m a g n i t u d e ; if( r >= e f f e c t i v e R a d i u s ) c o n t i n u e; // p r e s s u r e float n e i g h b o r _ p r e s s u r e = p a r t i c l e _ s t a t e s [ j ]. p r e s s u r e / Mathf . Pow ( p a r t i c l e _ s t a t e s [ j ]. density , 2.0 f ) ; t e m p _ k e r n e l = s p i k y _ g r a d i e n t (r , r e l a t i v e P o s i t i o n , e f f e c t i v e R a d i u s ) ; p r e s s u r e _ f o r c e += ( c e n t e r _ p r e s s u r e + n e i g h b o r _ p r e s s u r e ) * t e m p _ k e r n e l ; // v i s c o s i t y r e l a t i v e V e l o c i t y = p a r t i c l e _ s t a t e s [ j ]. v e l o c i t y -p a r t i c l e _ s t a t e s [ i ]. v e l o c i t y ;
float dot = V e c t o r 3 . Dot ( r e l a t i v e V e l o c i t y , r e l a t i v e P o s i t i o n ) ; if( d o t _ p o s i t i o n _ v e l o c i t y < 0.0 f ) v i s c o s i t y _ f o r c e -= t e m p _ k e r n e l * dot / ( r e l a t i v e P o s i t i o n . s q r M a g n i t u d e + s t a b i l i t y ) * v i s c o s i t y / ( p a r t i c l e _ s t a t e s [ i ]. d e n s i t y + p a r t i c l e _ s t a t e s [ j ]. d e n s i t y ) ; } } p a r t i c l e _ s t a t e s [ i ]. force = mass * ( v i s c o s i t y _ f o r c e -p r e s s u r e _ f o r c e ) ; }
また重み関数である
Spiky
カーネルの勾配は次のように求められる.
∇W
s pik y(r, h) = α
(h − r)
2r
r
(0 ≤ r ≤ h)
0
otherwise
ただし
α =
−
30
π
h
5,
−
45
π
h
6(2D, 3D)
ソースコード
6 Spiky
カーネル
(
勾配
)
public Ve c t o r 3 s p i k y _ g r a d i e n t (float r , Vector3 r e l a t i v e _ p o s i t i o n , float h ) { if( r > 0.0 f ) { float q = h - r ; return c o n s t a n t _ s p i k y _ g r a d i e n t * r e l a t i v e _ p o s i t i o n * q * q / r ; } else return Random . o n U n i t S p h e r e * 0.0001 f * c o n s t a n t _ s p i k y _ g r a d i e n t ; }
float c o n s t a n t _ s p i k y _ g r a d i e n t = -45.0 f / ( Mathf . PI * Mathf . Pow ( e f f e c t i v e R a d i u s , 6.0 f ) ) ;
2.5
粒子の移動
圧力項と粘性項に外力を加え,ステップ毎の粒子の移動を行う.
ソースコード
7
粒子の移動
void c a l c _ p o s i t i o n (int index ) {
if(! p a r t i c l e _ s t a t e s [ index ]. i s F i x ed ) {
p a r t i c l e _ s t a t e s [ index ]. v e l o c i t y += p a r t i c l e _ s t a t e s [ index ]. force * t i m e S t e p + ( e n a b l e _ g r a v i t y == true
? gravity * t i m e S t e p : Vector3 . zero ) ;
grid . p a r t i c l e s [ index ]. p o s i t i o n += p a r t i c l e _ s t a t e s [ index ]. v e l o c i t y * t i m e S t e p ; } }
3
ボクセル地形と壁境界計算
ここでボクセルデータとは
3
次元の濃度分布を格子点で読み
取り,濃度値の
3
次元配列としたものを指す.
地形の描画にはマーチング・キューブ法によって生成される
メッシュを用いる
10).
MC
法
(
マーチング・キューブ法
)
はボク
セルデータを可視化する手法であり,格子点
8
つに囲まれた立
方体領域
(
キューブ
)
について,格子点における濃度値がそれぞ
れ閾値を超えているか否かを判定する
11).これには
2
8=
256
通りのパターンがあり,対応するメッシュを割り当てることで
閾値の等値面をメッシュとして生成することができる.
3.1
粒子・壁境界の距離
本シミュレーションでは流体解析を行う空間に地形表現のた
めのボクセルデータが重なって存在し,これが壁境界を規定し
ている.したがってボクセルデータを元に壁境界を直接求める
ことができれば,効率的にシミュレーションを行うことができる.
粒子がボクセル表面と隣接するとき,粒子は
1
つのキューブに
内包されることになる.キューブ内の任意の点i
における濃度値を
d
iとすると,点i
を囲む
8
つの格子点の濃度値を線形補間すること
で
d
i,及び
n
iを求めることができる.そして壁境界までの距離
l
Bを
d
iが勾配に従って閾値に近づくとして次のように求める.
(l
B)
i=
d
i− ISO
|n
i|
勾配を求める様子を
2
次元で表したものを図
-4
に示す.
(1-x)
x
1.0
0.6
y
n
(1-y)
0.2
0.0
図
-4
勾配の導出
図
-5
では
1
つのキューブについて,格子点の濃度値に従い
マーチング・キューブ法によって傾斜をもつ上向きの面が張ら
れている.ここで高さ約
0.5
の平面上に粒子を並べ,それぞれ
について線形補間による壁境界位置を求めた.平面上の粒子と
線分で繋がれた粒子が,その粒子における最近傍の境界点を示
している.メッシュの面とは異なり湾曲した曲面を描くもの
の,このような例では適切に境界を求めることが可能である.
図
-5
線形補間によって得られる境界位置
3.2
壁境界の作用
Harada(2007)
3)では壁重み関数を用いた効率的な壁境界計
算を行っている.壁重み関数とは,均一に平坦に並べられた壁
粒子群に粒子が近づいた時,粒子が受ける反発力,摩擦力と
いった力が粒子と壁との距離のみをパラメータとして表現でき
るとするものである.
壁粒子が粒子に及ぼす影響を流体粒子間の作用と分離する
と,粒子の密度は重み関数を利用して次のように表される
3).
ρ
S(r) =
∑
j∈fluidm
jW
i j+
F
idensity(l)
境界計算を含めた圧力項の計算は次のようになる.
( Dv
Dt
)
press=
−
∑
( ji)∈fluidm
jP
iρ
2 i+
P
jρ
2 j∇W
i j+
F
press i(l)
粘性項の計算には,境界に対する垂直方向と,水平方向の
2
つの壁重み関数を用いて次のように計算する.
( Dv
Dt
)
vis=
−
∑
( ji)∈fluidm
jΠ
i j∇W
i j+
(
v
nF
ivis(n)(l)
)
∗+ v
pF
ivis(p)(l)
壁重み関数は計測用のモデルによって事前に計算を行う.今
回使用したモデルを図
-6
に示す.壁境界を表す固定粒子の間隔
は
a = 0.55
として規定している.
ah
h
壁粒子 計測点粒子図
-6
壁重み関数の計測モデル
計測モデルを用い,
1
つの計測粒子と壁境界との距離を徐々に変
えつつ,計測粒子が受ける影響をプロットする.これは次のように
SPH
法の処理を改変したコードによって計測することができる.
ソースコード
8
壁重み関数の計測
public void bake () {
for(int i =0; i < keys . Length ; ++ i ) { grid . p a r t i c l e s [ index ]. p o s i t i o n = t r a n s f o r m . p o s i t i o n + Ve c t o r 3 . up * keys [ i ] * e f f e c t i v e R a d i u s ; grid . u p d a t e _ g r i d () ; sph . s t e p _ f r a m e () ; // d e n s i t y grid . d e l e g a t e _ s e l e c t e d (new P a r t i c l e G r i d . N e i g h b o r P r o c e s s ( b a k e _ d e n s i t y ) , index ) ; data . density . AddKey (new K e y f r a m e ( keys [ i ] , sph .
P a r t i c l e S t a t e s [ index ]. d e n s i t y / mass ) ) ; // p r e s s u r e v i s c o s i t y
grid . d e l e g a t e _ s e l e c t e d (new P a r t i c l e G r i d . N e i g h b o r P r o c e s s ( b a k e _ f o r c e ) , index ) ;
data . p r e s s u r e _ f o r c e . AddKey (new K e y f r a m e ( keys [ i ] , r e p u l s i o n . y ) ) ;
data . f r i c t i o n . AddKey (new K e y f r a m e ( keys [ i ] , f r i c t i o n ) ) ; data . a d h e s i v e . AddKey (new K e y f r a m e ( keys [ i ] , a d h e s i v e ) ) ; }
}
void b a k e _ f o r c e (int i , int[] n e i g h b o r _ c u e ) {
P a r t i c l e S y s t e m . P a r t i c l e center = grid . p a r t i c l e s [ i ]; p r e s s u r e _ f o r c e = V e c to r 3 . zero ;
f r i c t i o n = a d h e s i v e = 0.0 f ;
V e c t o r 3 r e l a t i v e P o s i t i o n , r e l a t i v e V e l o c i t y , t e m p _ k e r n e l ; float c o e f _ c e n t e r _ p r e s s u r e = 1.0 f / Mathf . Pow ( sph .
P a r t i c l e S t a t e s [ i ]. density , 2.0 f ) ;
for(int cue_i =0; cue_i +1 < n e i g h b o r _ c u e . Length ; cue_i +=2) {
int start = n e i g h b o r _ c u e [ cue_i ]; int end = n e i g h b o r _ c u e [ cue_i + 1]; if( start == -1)
c o n t i n u e;
for(int cue_j = start ; cue_j < end ; ++ cue_j ) {
int j = grid . g e t _ i n d e x ( cue_j ) ; if( i == j ) c o n t i n u e; // p r e s s u r e r e l a t i v e P o s i t i o n = center . p o s i t i o n - grid . p a r t i c l e s [ j ]. p o s i t i o n ; float r = r e l a t i v e P o s i t i o n . m a g n i t u d e ; if( r > e f f e c t i v e R a d i u s ) c o n t i n u e; float n e i g h b o r _ p r e s s u r e = sph . P a r t i c l e S t a t e s [ j ]. p r e s s u r e / Mathf . Pow ( sph . P a r t i c l e S t a t e s [ j ]. density , 2.0 f ) ; t e m p _ k e r n e l = s p i k y _ g r a d i e n t (r , r e l a t i v e P o s i t i o n , e f f e c t i v e R a d i u s ) ; // p r e s s u r e p r e s s u r e _ f o r c e += ( c o e f _ c e n t e r _ p r e s s u r e * Mathf . Max ( sph . P a r t i c l e S t a t e s [ i ]. pressure , 0.0 f ) + n e i g h b o r _ p r e s s u r e ) * t e m p _ k e r n e l ; // f r i c t i o n r e l a t i v e V e l o c i t y = sph . P a r t i c l e S t a t e s [ j ]. v e l o c i t y -Vector3 . right ; float d o t _ p o s i t i o n _ v e l o c i t y = V e c t o r 3 . Dot ( r e l a t i v e V e l o c i t y , r e l a t i v e P o s i t i o n ) ; if( d o t _ p o s i t i o n _ v e l o c i t y < 0.0 f ) f r i c t i o n -= ( t e m p _ k e r n e l * d o t _ p o s i t i o n _ v e l o c i t y / ( r e l a t i v e P o s i t i o n . s q r M a g n i t u d e + sph . d e s b r u m _ s t a b i l i t y ) * v i s c o s i t y * sph . d e s b r u m _ v i s c o s i t y / ( sph . P a r t i c l e S t a t e s [ i ]. density + sph . P a r t i c l e S t a t e s [ j ]. density ) ) . x ; // a d h e s i v e r e l a t i v e V e l o c i t y = sph . P a r t i c l e S t a t e s [ j ]. v e l o c i t y -Vector3 . up ; d o t _ p o s i t i o n _ v e l o c i t y = V e c t o r 3 . Dot ( r e l a t i v e V e l o c i t y , r e l a t i v e P o s i t i o n ) ; if( d o t _ p o s i t i o n _ v e l o c i t y < 0.0 f ) a d h e s i v e -= ( t e m p _ k e r n e l * d o t _ p o s i t i o n _ v e l o c i t y / ( r e l a t i v e P o s i t i o n . s q r M a g n i t u d e + sph . d e s b r u m _ s t a b i l i t y ) * v i s c o s i t y * sph . d e s b r u m _ v i s c o s i t y / ( sph . P a r t i c l e S t a t e s [ i ]. density + sph . P a r t i c l e S t a t e s [ j ]. density ) ) . y ; } } }
計測によって得られた壁重み関数の曲線を図
-7
に示す.
300
40
0
h(m) (kg/m )30
h(m)-20
-40
(m/s )20
h(m) (m/s )20
h(m) (m/s )2粘性(垂直成分)
粘性(平行成分)
密度
圧力
図
-7
壁重み関数の計測結果
4
侵食メカニズム
単位質量あたりの水流に削り取られる土砂の量は次の数式で
表される
2, 12).
dM
bdt
=
−
∑
jL
2 bε( j)
(3)
ここで
L
bは地形の単位長さを,
ε
は流体粒子ごとの侵食の作
用を示す.
ε
は流体のせん断応力によるものとされ,せん断応
力
τ
によって次のように表される.
ε =
K
ε(τ − τ
c)
但し
τ =
Kθ
nここで
K
εは侵食の強度,
τ
cは限界せん断力,K
はせん断応力
の定数,
θ
はせん断速度を示す.K = 1
とし,また,べき乗数
n
は擬塑性流体に適用される値である
0.5
を用いる
2).
せん断速度は粒子速度のうち地形表面と平行な成分
v
pから
求めることができ,次の近似式で求める.
θ =
v
pl
ここで
l
は粒子と地形表面との距離を示す.
4.1
格子点の侵食計算
ボクセルデータに対する侵食を表現するため,各格子点につ
いて次の式に基づく濃度値の増減を計算する.
dM
bdt
=
−K
ε∑
j
{( |v
i j| − v
i j· ˆr
i jl
)
n− τ
c}
(|r
i j| < h)
0
(|r
i j| ≥ h)
ここで各格子点の座標は
r
iであり,j
は
r
iを中心とした近傍粒
子を示す.また式
(3)
の
L
2 bは表面積を表すが,ボクセルに適用
することから,これを一定として
K
εに組み込む.
C#
コードでは次のように実装されている.
ソースコード
9
格子点の侵食計算
void c r o s s _ p o i n t _ e r o s i o n (int x , int y , int z , int[] n e i g h b o r _ c u e ) { float e r o s i o n _ r a t e = 0.0 f ; V e c t o r 3 r e l a t i v e P o s i t i o n , r e l a t i v e V e l o c i t y ; float r_min = 0.4 f * sph . e f f e c t i v e R a d i u s ; float r_base = 0.4 f * sph . e f f e c t i v e R a d i u s ;
for(int cue_i =0; cue_i +1 < n e i g h b o r _ c u e . Length ; cue_i +=2) {
int start = n e i g h b o r _ c u e [ cue_i ]; int end = n e i g h b o r _ c u e [ cue_i + 1]; if( start == -1)
c o n t i n u e;
for(int cue_j = start ; cue_j < end ; ++ cue_j ) {
int j = s p h _ g r i d . g e t _ i n d e x ( cue_j ) ;
r e l a t i v e P o s i t i o n = v o x e l _ g r i d . c e l l _ p o s i t i o n (x , y , z ) - s p h _ g r i d . p a r t i c l e s [ j ]. p o s i t i o n ;
float r = r e l a t i v e P o s i t i o n . m a g n i t u d e ;
r = r - Mathf . Min ( sph . P a r t i c l e S t a t e s [ j ]. boundary , r_base ) ; // R e c t i f i e d
if( r > e r o s i o n R a d i u s ) c o n t i n u e;
r e l a t i v e V e l o c i t y = sph . P a r t i c l e S t a t e s [ j ]. v e l o c i t y ; float s h e a r _ r a t e = r e l a t i v e V e l o c i t y . m a g n i t u d e
-Mathf . Abs ( V e c t or 3 . Dot ( r e l a t i v e V e l o c i t y , r e l a t i v e P o s i t i o n . n o r m a l i z e d ) ) ;
s h e a r _ r a t e = s h e a r _ r a t e / Mathf . Max (r , r_min ) ; e r o s i o n _ r a t e += Mathf . Max (( Mathf . Pow ( shear_rate , 0.5
f ) - c r i t i c a l _ s t r e s s ) , 0.0 f ) ; }
}
// A p p l y to v o x e l data ( R e c t i f i e d )
erode [x ,y , z ] = (byte) Mathf . Min ( erode [x ,y , z ] + Mathf . C e i l T o I n t ( e r o s i o n _ r a t e * b y t e F i t ) , 255) ; int t r a n s f e r = Mathf . F l o o r T o I n t ( erode [x ,y , z ] *
e r o d e _ s t r e n g t h / b y t e F i t ) ;
v o x e l _ g r i d . voxel [x ,y , z ] = (byte) Mathf . Min ( v o x e l _ g r i d . voxel [x ,y , z ] + transfer , 255) ;
erode [x ,y , z ] = (byte) ( erode [x ,y , z ] % ( byteFit / e r o d e _ s t r e n g t h ) ) ; }
尚,上記のコードは幾つかの修正処理が追加されたものである.
4.2
越流破堤モデル
侵食の検証の為に堤防の越流侵食を再現するモデルを用い
る.これは河川,海岸等の堤防について,増水,高潮によって
水位が堤高を超えた状況を示す.
水流は堤体を越えて堤内側に流れ落ちるが,この射流によって
堤体は侵食を受ける.越流破堤現象については
Fujisawa(2011)
4)を参考とする.射流による侵食は堤体の材質や越流の量によっ
て侵食の特徴を変える
4).図
-8
にその概略図を示す.
部分侵食 全体侵食図
-8
侵食の種類
堤体が比較的高い抵抗力を持つ場合,天端付近
(
堤体の上面部
)
では侵食を受けにくい.射流が流れ落ちるにしたがって流速が
増し,限界点から下で侵食を受けることとなる
4).このような
侵食をここでは部分侵食と呼び,再現を試みることとした.
シミュレーションで用いる堤体モデルを図
-9
のようにボクセ
ルデータによって作成した.尚奥行き方向の寸法は
1.2[m]
と
なっている.また図中に重ねて,試験的に行ったシミュレー
ションの結果を示す.
WL
GL
2.4
4.8
1.2
(m)
(m)
3.6 2.4 1.2 0 4.8 1.2 3.6 6.0 8.4 粒子発生 80sec 500sec 250sec 160sec 侵食面 最小 最大図
-9
堤防モデル
試験の結果から以下の修正点が見つかった.
•
壁境界計算における摩擦係数の決定
•
不安定粒子による過剰侵食への対処
•
射流を減速する階段パターンへの対処
•
壁境界による引力の発揮
•
タイムステップあたりの侵食量に関する分解能の拡張
4.3
壁境界による引力
修正の中で,壁境界計算の圧力項に引力を発揮させるよう計
算の変更を行った.下流で部分侵食が始まった場合,それを広
げる為には水流が凹部に入り込む必要がある.しかし引力が発
揮されなければ凹部への侵入は限られたものとなり,部分侵食
パターンを再現することは難しいことが分かった.
そこで壁粒子が発揮する斥力のみを取り出してプロットして
いた圧力項の壁重み関数に加え,引力のみを取り出しプロット
した壁重み関数を用意した.
図
-10
の
(a)
が斥力のみを計測した壁重み関数であり,
(b)
は
引力のみを計測したもの,
(c)
は計測粒子の密度値を操作せず,
粒子にかかる力をそのまま計測した曲線である.ここで
(c)
に
おいて力が
0
となる距離を
l
Rとする.
曲線
(a)
を
F
repul,曲線
(b)
を
F
attractとし,圧力項の計算を
40 30 20 10 0 -10 h(m) -20 (m/s )2 lR l (a) (b) (c) 300 0 100 200 h(m) (kg/m )3 (h, ρ )B 密度 (l , ρ )