仮眠プログラマーのつぶやき

自分がプログラムやっていて、思いついたことをつぶやいていきます。 2025年からzennに移行

自作ゲームやツール、ソースなどを公開しております。
①ポンコツ自動車シュライシュラー
DOWNLOAD
②流体力学ソース付き
汚いほうDOWNLOAD
綺麗なほうDOWNLOAD
③ミニスタヲズ
DOWNLOAD
④地下鉄でGO
DOWNLOAD
⑤ババドン
DOWNLOAD
⑥圧縮拳(ツール)
DOWNLOAD
⑦複写拳
DOWNLOAD
⑧布シミュレーション
DOWNLOAD
⑨minecraft巨大電卓地形データ
DOWNLOAD
⑩フリュードランダー
デジゲー博頒布α版
DOWNLOAD
⑪パズドラルート解析GPGPU版
DOWNLOAD
⑫ゲーム「流体de月面着陸」
DOWNLOAD

アルゴリズム

「FFTモジュール活用例2巨大整数の乗算」のソース

以下のソースはHSPコンテストに出した作品「FFTモジュール活用例2巨大整数の乗算」のソースです。
下の方にあるfftモジュールは著作権フリーなので自由に使って下さい



「fft」の使い方ですが、まずこの命令を使うには変数
「beki」
「LIST_SIZE」
「LIST_SIZE2」
「LIST_SIZE8」
の作成をしておいて下さい。

beki には高速フーリエ変換するサンプル数(2^n)のnを入れます。以下のソースで言うとサンプル数を8192個作りたかったので
beki=13
になっています。

LIST_SIZEには2^beki を入れて下さい
LIST_SIZE2はLIST_SIZE\2を、LIST_SIZE8にはLIST_SIZE×8を代入して下さい。


高速フーリエ変換したい実数(虚数)配列は当然ddim で作り、配列数はLIST_SIZEを指定して下さい
ここで注意ですがbeki には整数しか指定出来ませんので、変換する配列数は2のべき乗に限られます



命令「fft」の引数は3つで
・実数部(浮動小数点配列)
・虚数部(浮動小数点配列)
・逆変換か否か(1 or -1       -1で逆変換)

です。命令を実行すると実数部、虚数部に変換後の値が入ります。












boxf
color 255,128,128
font "MS ゴシック",27,1
mes "A"
color 255,255,255
pos 25,0
mes "×   ="
color 128,255,128
pos 60,0
mes "B"
pos 140,2
button goto "答えを計算",*jikkou
color 255,128,128
boxf 0,30,314,480
color 128,255,128
boxf 314,30,640,480
pos 10,66
sdim k1,32768/2;(2^24)
mesbox k1,290,404,1,32766/2-1
sdim k2,32768/2;(2^24)
pos 332,66
mesbox k2,290,404,1,32766/2-1



ddim d1,32768/4
ddim d2,32768/4
ddim chasr,32768/4
ddim chasi,32768/4
stop










;計算実行
*jikkou
clrobj
color 255,255,255
boxf
beki=13;beki 13では8192配列が作成され1配列あたり4桁入るので最終出力が32768桁までなら計算できることになる
LIST_SIZE=1<<beki
LIST_SIZE2=LIST_SIZE>>1
LIST_SIZE8=LIST_SIZE<<3
strk1=strlen(k1)
strk2=strlen(k2)
hot4=4
repeat 32768/4/2;入力の最大桁は32768桁の半分
ccnt=LIST_SIZE2-1-cnt
sitenk1=strk1-cnt*4-4
if sitenk1<=0:hot4=strk1-cnt*4:sitenk1=0
d1.ccnt=double(strmid(k1,sitenk1,hot4));
if sitenk1<=0:break
loop

hot4=4
repeat 32768/4/2
ccnt=LIST_SIZE2-1-cnt
sitenk1=strk2-cnt*4-4
if sitenk1<=0:hot4=strk2-cnt*4:sitenk1=0
d2.ccnt=double(strmid(k2,sitenk1,hot4));
if sitenk1<=0:break
loop

fft d1,d2,1;フーリエ変換

memcpy chasr,d1,8*LIST_SIZE,0,0
memcpy chasi,d2,8*LIST_SIZE,0,0


ReTm=0.0
imTm=0.0
ReTN=0.0
imTN=0.0
ReTm=chasr
ImTm=chasi
ReTN=chasr
ImTN=chasi
d1=0.5*(Retm*ImTm+ReTN*ImTN)
d2=0.25*( ((ReTN+ImTN)*(ReTN-ImTN)) + ( (ImTm+ReTm)*(ImTm-ReTm) ) )
itjn=LIST_SIZE-1
repeat LIST_SIZE-1,1;フーリエ変換後の各要素同士を掛け算。この部分はこちらのページを参考に作った→http://homepage2.nifty.com/m_kamada/math/fftmul.htm
ReTm=chasr.cnt
ImTm=chasi.cnt
ReTN=chasr.itjn
ImTN=chasi.itjn
d1.cnt=0.5*(Retm*ImTm+ReTN*ImTN)
d2.cnt=0.25*( (ReTN+ImTN)*(ReTN-ImTN) + (ImTm+ReTm)*(ImTm-ReTm) )
itjn-
loop


ddim chasr,1
ddim chasi,1

fft d1,d2,-1;逆変換
sdim k2,4
sdim k1,LIST_SIZE*4+80
dim kekka,LIST_SIZE


w=LIST_SIZE-1;繰り上げ
w1=LIST_SIZE-2
kisuu=10000
repeat LIST_SIZE-1;繰り上げ
d1.w+=0.5
d1.w-=d1.w\1
if d1.w>=kisuu{
kuriaged=int(d1.w/kisuu)
d1.w-=kisuu*kuriaged
d1.w1+=kuriaged
}
kekka.w=int(d1.w+0.5)
w-
w1-
loop
d1.w+=0.5
d1.w-=d1.w\1
kekka.w=int(d1.w+0.5)


onf=0
w=0
w1=0
sdim k2,12;文字列に出力
repeat LIST_SIZE-1
if onf=0{
if kekka.cnt!0{
onf=1
k2=str(kekka.cnt)
w1=strlen(k2)
k2=strf("%0"+w1+"d",kekka.cnt)
}
}else{
w1=4
k2=strf("%04d",kekka.cnt)
}
memcpy k1,k2,w1,w,0
w+=w1
loop

color 0,0,0
pos 0,0:mes "答え="
mesbox k1,630,430,0,LIST_SIZE*4+80

wait 5
sdim kekka,1;メモリ解放
sdim k2,1
sdim d1,1
sdim d2,1
sdim chasr,1
sdim chasi,1
stop







#deffunc fft array A,array B,int inv
ddim c,LIST_SIZE
ddim d,LIST_SIZE

repeat beki;fft
bekii=LIST_SIZE>>(cnt+1)
repeat LIST_SIZE2
t2 = cnt\bekii;
t0 = (cnt/bekii)*bekii*2 + t2;
t1 = t0+bekii;
r4=A(t1)
i4=B(t1);
rm0=A(t0)-r4
im0=B(t0)-i4;
pii=3.14159265358979323846264338328*t2/bekii*inv;
dsin=sin(pii)
dcos=cos(pii);
A(t0) += r4;
B(t0) += i4;
A(t1) = rm0*dcos-im0*dsin;
B(t1) = rm0*dsin+im0*dcos;
loop
loop


repeat LIST_SIZE;ビット逆順
ic=cnt/65536
ic = (ic&0x00005555)<<1 | (ic&0x0000AAAA)>>1;
ic = (ic&0x00003333)<<2 | (ic&0x0000CCCC)>>2;
ic = (ic&0x00000F0F)<<4 | (ic&0x0000F0F0)>>4;
ic = (ic&0x000000FF)<<8 | (ic&0x0000FF00)>>8;
iic=cnt\65536
iic = (iic&0x00005555)<<1 | (iic&0x0000AAAA)>>1;
iic = (iic&0x00003333)<<2 | (iic&0x0000CCCC)>>2;
iic = (iic&0x00000F0F)<<4 | (iic&0x0000F0F0)>>4;
iic = (iic&0x000000FF)<<8 | (iic&0x0000FF00)>>8;
ic=(iic*32768+ic/2)>>(31-beki);
C(cnt)=A(ic)
D(cnt)=B(ic)
loop

if inv=-1{
repeat LIST_SIZE
a.cnt=c.cnt/LIST_SIZE
b.cnt=d.cnt/LIST_SIZE
loop
}else{
memcpy a,c,LIST_SIZE8
memcpy b,d,LIST_SIZE8
}
return

FFTモジュール活用例1スペクトラムアナライザーのソース

以下のソースはHSPコンテストに出した作品「FFTモジュール活用例1スペクトラムアナライザー」のソースです。
下の方にあるfftモジュールは著作権フリーなので自由に使って下さい



「fft」の使い方ですが、まずこの命令を使うには変数
「beki」
「LIST_SIZE」
「LIST_SIZE2」
「LIST_SIZE8」
の作成をしておいて下さい。

beki には高速フーリエ変換するサンプル数(2^n)のnを入れます。以下のソースで言うとサンプル数を32768個作りたかったので
beki=15
になっています。

LIST_SIZEには2^beki を入れて下さい
LIST_SIZE2はLIST_SIZE\2を、LIST_SIZE8にはLIST_SIZE×8を代入して下さい。


高速フーリエ変換したい実数(虚数)配列は当然ddim で作り、配列数はLIST_SIZEを指定して下さい
ここで注意ですがbeki には整数しか指定出来ませんので、変換する配列数は2のべき乗に限られます



命令「fft」の引数は3つで
・実数部(浮動小数点配列)
・虚数部(浮動小数点配列)
・逆変換か否か(1 or -1       -1で逆変換)

です。命令を実行すると実数部、虚数部に変換後の値が入っています。


#const beki 15
#const LIST_SIZE 1<<beki
#const LIST_SIZE2 LIST_SIZE>>1
#const LIST_SIZE3 LIST_SIZE<<1
#const LIST_SIZE8 LIST_SIZE<<3
#const  lstdim LIST_SIZE3/4+11
 _MSGothic = "MS ゴシック"
font _MSGothic,80,1
mes "※音量注意!"
font _MSGothic,30,1
mes "本ソフトは音声再生機能があります。\n注意して下さい"
wait 100

ddim onryo,5
randomize
onryo=570.0,50.0,2.0,0.012,0.00007
pos 140
datamode=0;0でロードしたデータ、1ブラウン2ピンク3ホワイト4ブルー5パアプル
gosub*syokika

sdim pinkname,64,5
pinkname="ブラウンノイズ","ピンクノイズ","ホワイトノイズ","ブルーノイズ","パープルノイズ"

objsize 140,40
pos 40,0
button gosub "波形読込",*load
pos 180,0
button gosub "波形連続再生or停止",*saisei
pos 320,0
button gosub "カラードノイズ試験生成",*colorz

objsize 52,24
pos 165,213
button gosub "wav解析",*fft0
pos 445,257
button gosub "wav復元",*fft1


sdim aaaaa,300
aaaaa="x1\nx10\nx100\nx10^3\nx10^4\nx10^5\nx10^6\nx10^7"
yokojiku1=0
tatejiku1=0
objsize 40,24;縦軸
pos 0,160
combox tatejiku1,,aaaaa
pos 55,193;横軸
combox yokojiku1,,aaaaa


repeat -1;メインルーチン
mbeki=beki0
await 15
if mbeki!beki0:gosub*syokika
redraw 0
gosub*byouga
redraw 1
loop






*syokika
ddim schasr,LIST_SIZE
ddim schasi,LIST_SIZE
ddim schasr2,LIST_SIZE
ddim schasi2,LIST_SIZE
ddim itjr,LIST_SIZE
ddim itji,LIST_SIZE
ddim hakei,LIST_SIZE
ddim bunkai,LIST_SIZE
if datamode=5{;読み込みデータの解析
if sampkz<LIST_SIZE{
dialog "波形データが短いです"
}else{
repeat LIST_SIZE
dataitj=wpeek(wavedata,sampt*cnt+wbit*hidari)\(1<<(wbit*8))
if wbit=2:if dataitj>32767:dataitj=dataitj-65536
if wbit=1:dataitj-=128:dataitj*=256
schasr.cnt=double(dataitj)
loop
hakeistk hakei,schasr,schasi
}
}else{
gosub*rando
mcis1
fft itjr,itji,1
repeat LIST_SIZE;周波数ごとにいじる
scare=onryo.datamode*expf(logf(sqrt(1.7+cnt))*(datamode-2))
itjr.cnt*=scare
itji.cnt*=scare
loop

hakeistk bunkai,itjr,itji
mcsi2
fft itjr,itji,-1
mcsi1
hakeistk hakei,schasr,schasi
}
return


*load
dialog "wav",16,"WAVEファイル"
exist refstr
dim wavedata,strsize/4+1
bload refstr,wavedata
wbit=wavedata.4/8
streo=wavedata.5/65536;1でモノラル2でステレオ
sampt=wbit*streo;サンプリング単位(byte)
sampkz=(strsize-44)/sampt
hidari=0
if streo=2:dialog "音声はステレオです\n左の音声を使用しますか?\n(いいえ選択で右)",2:if stat=7:hidari=1
datamode=5;0でロードしたデータ、1ブラウン2ピンク3ホワイト4ブルー5パアプル
gosub*syokika
return


*rando
gk=0.0
repeat LIST_SIZE;ランダムノイズ作る
r=double(rnd(32768)-16384)
schasr.cnt=r
gk+=r
loop
gk/=LIST_SIZE
repeat LIST_SIZE
schasr.cnt-=gk
loop
return

*colorz
datamode=rnd(5)
gosub*syokika
dialog pinkname.datamode+"を生成しました"
return
*saisei
if playon{
mmstop:playon-
}else{
dim wf,lstdim
wf=1179011410,36+LIST_SIZE3,1163280727,544501094,16,65537,44100,88200,1048578,1635017060,(LIST_SIZE3-82)

repeat LIST_SIZE
wpoke wf,44+cnt*2,limit((hakei.cnt+32768)\65536,0,65535)
loop

memfile wf ; ストリームの直前で指定してください
mmload "MEM:a.wav",0,1 ; 画像の拡張子識別のためダミー名a.jpgを使用
sdim wf,64 ; もったいないのでbufのメモリ領域を小さくしておきます
mmplay 0:playon+
}
return

*byouga
pget -1:boxf
font _MSGothic,100
color 128,128,128
pos 140,193
mes "▼"
pos 420,200
mes "▲"
font _MSGothic,10
pos 8,183:mes "縦軸\n  /横軸"
tt="1"
repeat 5
pos 132.87712*cnt+25,450
mes tt+"Hz"
tt+="0"
loop


;グラフ形
repeat 2
color 200,200,200:boxf 40,47+255*cnt,640,191+255*cnt
loop



;波形
color ,165
yokoj600=LIST_SIZE>>yokojiku1
tatej600=expf(logf(10)*tatejiku1)
repeat 600
limi=limit(180-hakei(yokoj600*cnt/600)*tatej600*0.001983642578,50,180)
if cnt=0:pos cnt+40,limi
line cnt+40,limi
loop
;波形2
tatej600=1
bxs=40
clf=0
getkey key,1
repeat 600
mtatej600=tatej600
tatej600=int(expf(logf(LIST_SIZE-2)*(1.0*cnt/599.0)))
if mtatej600!tatej600{
limi=446-limit(logf(bunkai.tatej600)*12.5076816-144,0,144)
itjint=cnt+41
color clf*80,165
if abs(mousey-375)<73:if bxs<=mousex:if mousex<itjint:color :if key{
repeat tatej600-mtatej600,mtatej600+1
itjdl=expf(((446.0-mousey)*0.07995087+11.512925))
nanbai=itjdl/bunkai.cnt
schasr2.cnt*=nanbai
schasi2.cnt*=nanbai
bunkai.cnt=itjdl
loop
}
boxf bxs,limi,itjint-1,446
bxs=itjint
clf=1-clf
}
loop
return


*fft0
mcis1
fft itjr,itji,1
mcsi2
hakeistk bunkai,schasr2,schasi2
return

*fft1
mcis2
fft itjr,itji,-1
mcsi1
hakeistk hakei,schasr,schasi
return





;if 0 {
#deffunc fft array A,array B,int inv
ddim c,LIST_SIZE
ddim d,LIST_SIZE

repeat beki;fft
bekii=LIST_SIZE>>(cnt+1)
repeat LIST_SIZE2
t2 = cnt\bekii;
t0 = (cnt/bekii)*bekii*2 + t2;
t1 = t0+bekii;
r4=A(t1)
i4=B(t1);
rm0=A(t0)-r4
im0=B(t0)-i4;
pii=3.14159265358979323846264338328*t2/bekii*inv;
dsin=sin(pii)
dcos=cos(pii);
A(t0) += r4;
B(t0) += i4;
A(t1) = rm0*dcos-im0*dsin;
B(t1) = rm0*dsin+im0*dcos;
loop
loop


repeat LIST_SIZE;ビット逆順
ic=cnt/65536
ic = (ic&0x00005555)<<1 | (ic&0x0000AAAA)>>1;
ic = (ic&0x00003333)<<2 | (ic&0x0000CCCC)>>2;
ic = (ic&0x00000F0F)<<4 | (ic&0x0000F0F0)>>4;
ic = (ic&0x000000FF)<<8 | (ic&0x0000FF00)>>8;
iic=cnt\65536
iic = (iic&0x00005555)<<1 | (iic&0x0000AAAA)>>1;
iic = (iic&0x00003333)<<2 | (iic&0x0000CCCC)>>2;
iic = (iic&0x00000F0F)<<4 | (iic&0x0000F0F0)>>4;
iic = (iic&0x000000FF)<<8 | (iic&0x0000FF00)>>8;
ic=(iic*32768+ic/2)>>(31-beki);
C(cnt)=A(ic)
D(cnt)=B(ic)
loop

if inv=-1{
repeat LIST_SIZE
a.cnt=c.cnt/LIST_SIZE
b.cnt=d.cnt/LIST_SIZE
loop
}else{
memcpy a,c,LIST_SIZE8
memcpy b,d,LIST_SIZE8
}
return



#deffunc hakeistk array A,array B,array E
repeat LIST_SIZE
a.cnt=sqrt(B(cnt)*B(cnt)+E(cnt)*E(cnt))
loop
return


#deffunc mcis1
memcpy itjr,schasr,LIST_SIZE8
memcpy itji,schasi,LIST_SIZE8
return

#deffunc mcis2
memcpy itjr,schasr2,LIST_SIZE8
memcpy itji,schasi2,LIST_SIZE8
return

#deffunc mcsi1
memcpy schasr,itjr,LIST_SIZE8
memcpy schasi,itji,LIST_SIZE8
return

#deffunc mcsi2
memcpy schasr2,itjr,LIST_SIZE8
memcpy schasi2,itji,LIST_SIZE8
return
;}

円周率小数点以下第1000億桁まで計算するproject

HSPCLの開発やパズドラルート解析GPGPU版のOpneCL移植版や数値流体力学で大気シミュレーションなどやりたいことはまだいろいろあるのだが、ここで少し寄り道しようと思う。

「今度こそ円周率小数点以下第1000億~1兆桁くらいまで計算したい!」

昔からキチガイで円周率が無駄に大好きで小学生の頃よく覚えて今も3.14・・・・・・・・・・7982くらいまでならおもいだせるくらい好きなので、何年前かHSPコンテストに高速電子計算機と言う名で円周率を計算するソフトを出したくらいだ

これは高速化にも力を入れていて、HSP標準命令を使わず多倍長計算用プラグインをこしらえて重い処理はプラグインのほうに投げていたし、 円周率を求める公式には世界最速級のソフトで多用されているチュドノフスキーの公式を使った。当然ソースは計算の無駄のないようにした。


だが、これだけでは案の定計算速度が遅かった。
理由は3つある。一つは巨大桁の乗算に筆算法を用いていたこと、もうひとつはGPUではなくCPU1スレッドだけで計算していたこと、もうひとつはSSE4などで高速化を行なわなかったこと。
後者2つは合わせてもせいぜい10~100倍の速度差にしかならないが、乗算アルゴリズムをどうするかというのは、この円周率を計算する分野において非常に重要になってくる。1億桁の乗算でも15万倍近く計算時間に差が出、1兆桁なら多分1億倍くらい(?)処理量が違ってくるのである!


作品を出してからも乗算のアルゴリズムの勉強は続け、どうしてもFFTが理解できずkaratsuba法に逃げた事もあったが、最近FFTが実は流体シミュのCIP法なんかよりもよっぽど簡単だったんじゃないかということに気づきOpenCLに移植するところまではできた。


昔企画していた1億桁×1億桁の乗算はあまりに大変だとのことで一度凍結させたのが、このFFTの乗算アルゴリズムを使うことでいとも簡単に3000万桁×3000万桁の計算がHSPのしかも標準命令でできることがわかってしまったので、もっと上を目指してもいいんじゃないかということでこのプロジェクトを掲げてみた。
でもやはりいきなりは無理だろうから、まずは100万桁~1億桁の円周率を求めるプログラムを作ることにする。


早速だが企画内容はこうだ
使用言語はC++とOpenCL
数値計算部分は主にOpenCLつまりGPUで行なう
円周率を求めるのに使う公式はラマヌジャン系公式のチュドノフスキーの公式
チュドノフスキーの公式のΣの内部の計算手順はBinary Splitting 法を使用
√10005の計算にはニュートン法
最後の除算もニュートン法
ニュートン法やバイナリースプリッティングでところどころにでてくる多倍長の乗算はFFTを用いる
FFT自体の計算精度はdouble-double精度でやる
double-double精度の四則演算は当然GPU上でサポートされていないのでソフトウェア的に行なう
FFTの1つ1つの要素は基数を102502425600として計算
これはhttp://www.nextftp.com/swlabo/m0_pctech/hp_ultraprecision/up_815_3.htmを見てdouble-double精度なら100億配列要素までなら基数を10^11にしてギリギリいけると思ったからである。あと基数を102502425600にすればチュドノフスキーの公式の性質上、乗算を配列シフトで済むところがあってここでも利点があるためである。

目指す目標は2つのGPU(Radeon HD7970)を使って1分以内に1億桁を計算しファイルに出力すること。
http://ysrken.blog.fc2.com/blog-entry-38.html
このブログによればCPUだけでも1億桁1分を切るものが出ているようなのでとりあえずはGPGPUでアドバンテージがある分1分という目標は妥当かなと・・・
そして次のHSPコンテストではHSPCLの公開と同時に、高速電子式計算機,改を出そうと思う!


盛大な寄り道だが、これもまた今まで作ってきたプログラムの集大成みたいなものなので個人的にはぜひチャレンジしたい!

よし、まずは周波数間間引きFFTと時間間引きFFTの勉強から始めるとするか。 





------------------------------------------------------------------------------------------------------------------------------------------
2019/10追記
FMTをPythonとCUDAで実装しました。ガウスルジャンドルで3億桁まで求めた記事をQiitaに公開しました。1億桁1分は切れなかったよ・・・
https://qiita.com/Red_Black_GPGPU/items/e933e0d846b874b86c32
ソースはこちら
https://github.com/toropippi/FMT_QiitaSample

流体力学のプログラムを作りたい!その3

流体力学に関する記事はかなり久しぶりである。

約束通りCIP法の解説をしたいと思ったが、解説に使う労力があまりに多いため、また最近多忙なため、CIP法のソースの公開と、参考になったリンクを貼るのみとさせて頂く。

今までネット上に落ちているソースを元に独学で流体力学を学んできたが、ついにCIP法の難しさにより本を購入。

CIP法とJavaによるCGシミュレーション 単行本(ソフトカバー) – 2007/2/17
矢部 孝  (著), 尾形 陽一  (著), 滝沢 研二  (著)

といっても私でも理解できたのでかなりやさしいのでは。CIP法のソースが最初から最後まで乗っている解説本。
今回紹介する公開サンプルはそれのHSP移植かつ模倣にとどまる。

以下のスクリーンショットがHSP上で実行したものだ。
cip法流体.rar

cip有限音速
これは、音速が有限で、混合密度性流体である。
「cipcup格子法低音速、混合密度.hsp」の実行画面である。

画面の意味するものは、明るい部分が密度の高いもの、暗い部分が密度の低いもので、重力なし、境界は反対側につながっていて、初期条件は左半分が高密度で真下に向かう速度を持ち、右半分が低密度で真上に向かう速度を持つ。
流体を妨げる障害物はない。


そして以下のものは、本のソースを参考に自分で改変し、音速を無限、密度一定で非圧縮性流体にしたものだ。
当然CIP法なので、数値拡散がとても少なく、サンプル内のレンダリング動画を見てもらえれば分かる通り、160×120格子でも細かい渦が消えずに残っているのが分かる。
(もしCIP法でないと細かい渦が拡散してしまう)
これは障害物(壁)を3枚、左側に配置してある。
cip無限音速

壁の境界条件をどの段階で設定すればいいのかわからず、移流項計算、圧力項計算の前後に無駄に多くの壁境界条件設定をしているが、こんなにする意味は無い・・お陰で汚いソースである・・・・。



最後にお世話になりまくったサイト or PDFのご紹介。
特に参考になったリストは 
以下、その他参考になったサイト or PDF
http://www.4gamer.net/games/000/G000000/20070926041/
http://cfdsim.web.fc2.com/download_source.html
http://www.civil.hokudai.ac.jp/yasu/home.htm
http://www.exocortex.com/plugins/slipstreamvx
http://grantkot.com/Multiphase/Liquid.html
http://grantkot.com/MPM/Liquid.html
http://ja.wikipedia.org/wiki/CIP%E6%B3%95
http://news.mynavi.jp/articles/2007/10/09/cedec01/001.html
http://art-science.org/journal/v3n2/v3n2pp159/artsci-v3n2pp159.pdf
http://www.csp-consortium.org/action/csp_200910/nakane_200910.pdf

流体力学のプログラムを作りたい!その2

「その1」で流体の挙動を最低限シミュレートできたはずである。
次は少しステップアップして、流体の可視化と、流体の中に存在する「動く障害物」に対してどう処理するかというところを見ていこう。


【可視化 
これはかなり独学なところもあったが、基本は「渦度」を画面に出力すればらしい映像が得られる、という認識で間違いないと思う。

百聞は一見に如かず
画像左半分が128*128格子の渦度の可視化画面
o0512025611361324083

ちなみに右は圧力の可視化。

渦度を求める方法はいろいろすっ飛ばした結果以下のとおり。

a3a

渦度=(A+B+C+D)/4


AとCは、x速度の定義点におけるy速度、BとDはy速度の定義点におけるx速度を表している。
それぞれの速度は、定義点から外れているため、近傍4点から平均を取ってくるなどの方法をとってうまく計算する。
渦度の画面出力は、マイナス値を赤、プラス値を緑などとし、値の大きさと色をフィーリングで比例させればだいたい↑のようなレンダリング結果が得られるだろう。


あと計算高速化のアイディアとして、細かいことをきにしなくていいなら、平均処理をしないで渦度を求めることだってできる。
渦度=周囲4点の時計(又は反時計) 回りへ の速度を平均したもの
とラフに考えれば、格子の「角」における渦度はどうなるか。



a3b

これならx速度、y速度の値をそのまま使えるため計算コストが低く抑えられる。



次に、渦度だけでは画面が寂しいというのなら、幾万ものドット粒子を浮遊させるというのもよくある良い可視化手段である。
srtnh


粒子の移動速度は当然流体の速度と同じで、近傍の速度定義点から求めてやればいい。
a3c


ちなみに3次補間と比べ精度の低い線形補間(バイリニア補間)で求めても粒子の動きは見た目問題ないほど非常になめらかであった。


【流体の中で動く障害物  
流体の計算の醍醐味は、障害物の存在下で流体がどのような挙動を取るのかを見て楽しむことにある。

asdfgv

このように、移動する障害物を流体力学の計算にどのように組み込んだらいいのか。
ここでもスタガード格子ということで考えてみる。

まず格子ごとに、この格子が障害物か流体かを識別するための配列変数「kabe」、を作る。
可動障害物でも静止障害物でも、ある時刻tにおけるある格子が障害物であるときkabeは0の値を取り、流体であるとき1の値を取る。

次に、流体内に、移動しない障害物があるときの処理をおさらいする。
このとき、障害物の境界で強制的に流速を0にすれば良かったはずだ。
一方、障害物が移動することを考えると。
ここのサイトによると、このときの境界の条件は、流速=障害物の移動速度、となる。


最初に、一番単純化して格子の長さが1m×1mとして、1格子からなる最小単位の障害物がx方向に1m/sで動くプログラムを考える。
またΔt=0.2sとする。

最初のt=0.0sの時点では当然こうなる。
a5a
濃い灰色と薄い灰色の速度は固定である。そのため移流や圧力の影響を受けない。
また変数kabeは1と0だけで定義できる状態だ。


さて次にt=0.2sではどうなるか。
a5b
理想ではこうしたいところではあるが、障害物の側面が速度定義点から外れる上、1格子内に壁成分と流体成分が混じっているため、 変数kabeは1と0だけで定義できない状態となる。 

つまり正解はこうなる。
a5a
これはt=0.0sの状況と全く同じである。
これでいいのかと思うかもしれないが、これで良い!


そしてt=1.0sまでタイムステップを進めたとき、↓のようにすれば良い。
a5c


ということはt=0.8sの時点で一番誤差が大きくなり、1m^2の面積の格子に0.8m^2分の誤差流体が入っていることになる。
こんな言い方をしたくないが、こればかりは仕方がない。


そしてつぎに斜め移動の場合を考える。例えば斜め右下30°に1.0m/sの速度を持つ壁としたら、境界条件はx速度=0.87m/s 、y速度=0.5m/s
に固定すれば良い。
そこら辺はさっきのリンク ここ の下の方の説明画像を見ればわかると思う。



ところで話はかわり、タコマ橋という有名な橋があるのだが、これは完成後すぐに横風によるカルマン渦が発生して上下に振動し、ついには崩壊してしまったアメリカの橋だ。
このように、障害物(壁)が流体の力を受けて移動する場合の計算は、ポアソン方程式で圧力計算が終わったあとの障害物周囲の圧力を障害物に働く力として計算すればよい。


これで可動障害物の話は終わり。





最後に。

iPhoneアプリで面白い物を見つけた。
「wind tunnel lite」という格子法で計算されているCFDアプリだ。
指で流体を動かせる機能も付いている。
当然フルにスペックを使って計算されているはずなので電池消耗アプリとしていいかもしれない・・・(そんな馬鹿な)

そして風上差分の大欠点である数値拡散を抑えるため、CIP法の勉強を最近始めたので 
次の記事にはCIP法についてわかったことを書いていこうと思う。

今回お世話になった参考文献。
http://news.mynavi.jp/articles/2007/10/09/cedec01/006.html
(やはり西川善司さんの講座は参考になる)


次回:流体力学のプログラムを作りたい!その3
プロフィール

toropippi

記事検索
アクセスカウンター

    QRコード
    QRコード
    • ライブドアブログ