Processing math: 100%

2015年12月25日金曜日

熱伝導方程式を陰解法で解いてみるで

熱伝導方程式の数値解法は割と現代(近代)科学の真骨頂やと思ったのでまとめるで。 こういう風に数式からプログラムまでなんとなくまとめてある資料はなかったので大学生とかは重宝すると思ってるで。 けどコピーはいかんで。

熱伝導方程式

一次元の熱伝導方程式は次式で表される。
\begin{equation} \frac{\partial T}{\partial t} = \alpha \frac{\partial^2T}{\partial^2x} \end{equation} Tは温度、\alphaは温度伝導率であり、次式より求まる。 \begin{equation} \alpha=\frac{\lambda}{\rho c_{p}} \end{equation} \lambdaは熱伝導率、\rhoは密度、c_{p}は定圧比熱である。

偏導関数の近似

差分法では偏導関数を前進差分、後退差分で次のように近似する。 \begin{equation} \frac{\partial f}{\partial x}=\frac{f_{i+1}-f_{i}}{\Delta x} \end{equation} \begin{equation} \frac{\partial f}{\partial x}=\frac{f_{i}-f_{i-1}}{\Delta x} \end{equation} 2階の偏導関数は1階の偏導関数であるから前進差分と後退差分を用いれば次式を得られる。 \begin{equation} \frac{\partial^2 f}{\partial^2 x} = \frac{f_{i+1}-2f_{i}+f_{i-1}}{\Delta x^2} \end{equation}

陽解法

前進差分を用いて数値解を求める方法は陽解法と呼ばれる。 T_{x,t}として陽解法による差分近似式は次式になる。 \begin{equation} \frac{T_{i,j+1} - T_{i,j}}{\Delta t} = \alpha\frac{T_{i+1,j}-2T_{i,j}+T_{i-1,j}}{\Delta x^2} \end{equation} T_{i,j+1}について解くと次式になる。ここで\gamma\frac{\Delta t}{\Delta x^2}となる。 \begin{equation} T_{i,j+1}=\alpha \gamma T_{i+1,j} + (1-2 \alpha \gamma)T_{i,j}+\alpha \gamma T_{i-1,j} \end{equation}

陰解法

後退差分を用いて数値解を求める方法は陰解法と呼ばれる。 差分近似式は次式となる。 \begin{equation} \frac{T_{i,j+1}-T_{i,j}}{\Delta t} = \alpha \frac{T_{i+1,j+1}-2T_{i,j+1}+T_{i-1,j+1}}{\Delta x^2} \end{equation} これを整理すると次式になる。 \begin{equation} -\alpha \gamma T_{i+1,j+1}+(1+2 \alpha \gamma)T_{i,j+1} - \alpha \gamma T_{i-1,j+1} = T_{i,j} \end{equation} 陽解法とは異なりT_{i,j}から、次の時刻でのT_{i-1,j+1},T_{i,j+1},T_{i+1,j+1}の3点を求める。 前式を行列で表すと次の式のようになる。 \left( \begin{array}{ccccc} 1+2 \alpha \gamma & -\alpha \gamma & 0 & \ldots & 0 \\ -\alpha \gamma & 1+2 \alpha \gamma & -\alpha \gamma & \ldots & 0 \\ & \vdots & & \ddots & \vdots \\ 0 & \ldots & -\alpha \gamma & 1+2 \alpha \gamma & -\alpha \gamma \\ 0 & \ldots & 0 & -\alpha \gamma & 1+2 \alpha \gamma \end{array} \right) \left( \begin{array}{c} T_{1,j+1} \\ T_{2,j+1} \\ \vdots \\ T_{M-1,j+1} \\ T_{M,j+1} \end{array} \right) = \left( \begin{array}{c} T_{1,j} \\ T_{2,j} \\ \vdots \\ T_{M-1,j} \\ T_{M,j} \end{array} \right) 前式の連立一次方程式を数値解法によって解く。このとき行列のT_{i,j+1}T_{M,j+1}には境界条件を設定する。

連立一次方程式の数値解法

熱伝導方程式を陰解法で解くためには連立一次方程式を解く必要がある。 連立一次方程式を解く方法は数多くあるが、ここでは反復法のヤコビ反復法とガウス・サイデル法について記載する。 以下の三元連立一次方程式を考える。 \begin{equation} \left( \begin{array}{ccc} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{array} \right) \left( \begin{array}{c} x_{1} \\ x_{2} \\ x_{3} \end{array} \right) = \left( \begin{array}{c} b_{1} \\ b_{2} \\ b_{3} \end{array} \right) \end{equation} 前式の行列は次式に書き下せる。 \begin{eqnarray} \begin{cases} a_{11} x_{1} + a_{12} x_{2} + a_{13} x_{3} = b_{1} & \\ a_{21} x_{1} + a_{22} x_{2} + a_{23} x_{3} = b_{2} & \\ a_{31} x_{1} + a_{32} x_{2} + a_{33} x_{3} = b_{3} & \\ \end{cases} \end{eqnarray} この時、x_{i}について次式が導かれる。 \begin{eqnarray} \begin{cases} x_{1}^{(k+1)} = \cfrac{b_{1} - a_{12}x_{2}^{(k)} - a_{12}x_{3}^{(k)}}{a_{11}} & \\ x_{2}^{(k+1)} = \cfrac{b_{2} - a_{21}x_{1}^{(k)} - a_{22}x_{3}^{(k)}}{a_{22}} & \\ x_{3}^{(k+1)} = \cfrac{b_{3} - a_{31}x_{1}^{(k)} - a_{31}x_{3}^{(k)}}{a_{33}} \end{cases} \end{eqnarray} x_{i}^{(0)}を初期値として与え、上式に基づきx_{i}^{(0)},x_{i}^{(1)},x_{i}^{(2)},...と計算する。 反復回数をkとし、kを十分大きくすると,x_{i}^{(k)}は解に収束し、n元連立一次方程式の場合は次式により計算する。 \begin{equation} x_{i}^{(k+1)} = \frac{b_{i}}{a_{ii}} - \sum_{j=1,j \ne i}^{n} \frac{a_{ij}}{a_{ii}}x_{j} \end{equation} このように解を求める方法がヤコビ反復法である。 解が収束したかどうかはx_{i}^{(k)}x_{i}^{(k+1)}の値を比べてその差が許容誤差内に収まっているかどうかで判断する。 ヤコビ反復法ではkステップのxだけを使ってk+1の値を求める。 ガウス・サイデル法ではx_{1},x_{2}...x_{n}と順番に計算した場合、x_{2}^{k+1}を求める時にはx_{1}^{k+1}がすでに計算されており、これらの最新の値をk+1の値の計算で使う。

プログラム例

陽解法の場合、CFL条件によりタイムステップ幅に厳しい条件を付ける必要がある。 陰解法の場合はタイムステップ幅により解が不安定になることはない。 ここでは陰解法を用いてガウスサイデル法により一次元の熱伝導方程式を解いたPythonのプログラム例とその結果を以下に示す。
#coding: utf-8
L=0.1 #長さ
M=10 #分割数
dx=L/M #空間刻み
dt=1.0 #時間刻み
N=100 #時間ステップ数
alpha=80.2/(7874.0*440.0) #熱伝導率
gamma=dt/dx**2
a=alpha*gamma
T=[273.15 for i in range(M+1)]
A=[[0.0 for i in range(M+1)] for j in range(M+1)]
EPS=1e-15
def gaussseidel(T,preT,A):
while True:
d=0.0
for i in range(M+1):
sum=0.0
for j in range(M+1):
if i != j:
sum+=(A[i][j]/A[i][i])*T[j]
n=preT[i]/A[i][i]-sum
d+=abs(n-T[i])
T[i]=n
if d < EPS:
break
if __name__=='__main__':
for i in range(1,M):
A[i][i-1] = -a
A[i][i] = 1+2*a
A[i][i+1] = -a
A[0][0]=1.0
A[M][M]=1.0
f = open('output','w')
for i in range(1,N):
T[0]=300.0
T[M]=T[M-1]
preT = list(T)
gaussseidel(T,preT,A)
f.write('# t=%ss\n'%(i*dt))
for j in range(M+1):
f.write('%s, %s\n'%(j*dx,T[j]))
f.write('\n\n')
f.close()
view raw heat.py hosted with ❤ by GitHub

最後の絵はプログラムの出力をプロットしたもので右軸が長さ、上軸が温度を表し、時間ごとに左の線から右の線に変わっている。 図を見ると最初は左端だけ300度であるのが徐々に全体に温度が拡がっていく様子がわかる。

実践的な解法

後退差分による差分近似式をT_{i,j+1}について整理すると次式になる。 \begin{equation} T_{i,j+1}=\frac{T_{i,j}+\alpha \gamma (T_{i+1,j+1} + T_{i-1,j+1})}{1+2 \alpha \gamma} \end{equation} 上式を線形反復式として解くこともできる。この場合は行列を意識することがなく簡潔になる。 プログラム例は以下になり、同様の結果を得ることができる。
#coding: utf-8
L=0.1 #長さ
M=10 #分割数
dx=L/M #空間刻み
dt=1.0 #時間刻み
N=100 #時間ステップ数
alpha=80.2/(7874.0*440.0) #熱伝導率
gamma=dt/dx**2
a=alpha*gamma
T=[273.15 for i in range(M+1)]
EPS=1e-15
def gaussseidel(T,preT):
while True:
d=0.0
for i in range(1,M):
n=(preT[i]+a*(T[i+1]+T[i-1]))/(1+2*a)
d+=abs(n-T[i])
T[i]=n
if d < EPS:
break
if __name__=='__main__':
f = open('output','w')
for i in range(1,N):
T[0]=300.0
T[M]=T[M-1]
preT = list(T)
gaussseidel(T,preT)
f.write('# t=%ss\n'%(i*dt))
for j in range(M+1):
f.write('%s, %s\n'%(j*dx,T[j]))
f.write('\n\n')
f.close()
view raw heat2.py hosted with ❤ by GitHub

正直をいうと教科書的なガウスサイデル法を使っている例はほとんどなくて実践的な解法に記載したようなサンプルが多かったので絶賛混乱中や。

2015年10月13日火曜日

クロスプラットフォームの幻想

今現在クロスプラットフォームに対応しました!という場合、Mac,Windows,Unixとかだろう。 モバイルだったらiOS,Androidもあるかもしれない。

この場合、バージョンは一年に一回上がるとして対応するバージョンが5個増えることになる。 その間にも新しいOSが増えるかもしれない。 アーキテクチャが変わるかもしれない。 新しいアーキテクチャが出てくるかもしれない。 呼び出していたAPIがなくなるかもしれない。 ブラウザとかが関わるのであればさらに掛け算して評価が必要だ。 Javaや.NETだったらRuntimeが対応していないかもしれない。 3DTouchのような操作やGUIの見た目が微妙に違うかもしれない。 ライセンス形態が変わるかもしれない。 Windows3.1とか古いOSはもう対応しなくていいかもしれない。 同じものでもOSごとに性能が違うかもしれない。 仮想環境で動かさないといけないかもしれない。 今だとコンテナ技術なんてのもある。 TVとか家電用のOSもあるかもしれない。 使いたい言語をライセンス的に使ったらいけないかもしれない。

要はJavaが言っていたような Write Once Run Anywhere というのは幻想で、 こんなに色んな技術が出てくるとハードウェアやOSに合う方法や言語を使うのが 結局早くなってしまうんでないかなあと思った次第です。

2015年10月9日金曜日

コピペプログラミング

ほとんどこのブログをqiita的に使っていたので素直にqiitaに書くことにした。 qiitaとかstackoverflowとか見て思うのが最近はほとんどググれば答えが載っていること。 訳のわからないバグもエラーコードで大体理由がわかってしまう。 コピペプログラミングはダメだとかいう人もいるけどそもそも意味がわかっていなければコピペで組むこともできないわけで。 ソフトウエアはオブジェクト指向に代表されるように組み合わせるが基本だと思うので、コピペプログラミングも技術の一つと言ってもいいと思う。 実はコピペプログラミングは生産性を10倍ぐらい上げているはずだと思うんだけどそういうことを研究している人はいるんだろうか?

2015年9月24日木曜日

古き良きteapot


3D的なことをしたくなったのでデバッグ用にいろいろOpenGLを調べていたのですが今はOpenGL3.1ごろやらで必須になったCore Profileとかいうのが主流らしいです。 ただデバッグ用に確認したいだけで高速化を求めなければGLUTで十分だなあと思った次第です。 teapotを回転するだけですが時代を感じてよいですね。
#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
typedef struct {
int width;
int height;
char* title;
float field_of_view_angle;
float z_near;
float z_far;
} glutWindow;
typedef struct {
int down;
int pos[2];
float angle[2];
} glutMouse;
glutWindow win;
glutMouse m;
float g_rotation = 0;
float g_rotation_speed = 0.2f;
void display() {
// Clear Screen and Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Define a viewing transformation
gluLookAt(4, 2, 0, 0, 0, 0, 0, 1, 0);
// Push and pop the current matrix stack.
// This causes that translations and rotations on this matrix wont influence
// others.
glPushMatrix();
glColor3f(1, 0, 0);
// Rotate the teapot
glRotated(90, 0.0, 1.0, 0.0);
glRotated(m.angle[0], 0.0, 1.0, 0.0);
glRotated(m.angle[1], 1.0, 0.0, 0.0);
// Draw the teapot
glutSolidTeapot(1);
glPopMatrix();
g_rotation += g_rotation_speed;
glutSwapBuffers();
}
void initialize() {
// select projection matrix
glMatrixMode(GL_PROJECTION);
// set the viewport
glViewport(0, 0, win.width, win.height);
// set matrix mode
glMatrixMode(GL_PROJECTION);
// reset projection matrix
glLoadIdentity();
GLfloat aspect = (GLfloat)win.width / win.height;
// set up a perspective projection matrix
gluPerspective(win.field_of_view_angle, aspect, win.z_near, win.z_far);
// specify which matrix is the current matrix
glMatrixMode(GL_MODELVIEW);
glShadeModel(GL_SMOOTH);
// specify the clear value for the depth buffer
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
// specify implementation-specific hints
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
GLfloat amb_light[] = {0.1, 0.1, 0.1, 1.0};
GLfloat diffuse[] = {0.6, 0.6, 0.6, 1};
GLfloat specular[] = {0.7, 0.7, 0.3, 1};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb_light);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glShadeModel(GL_SMOOTH);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
glEnable(GL_LIGHTING);
glClearColor(0.0, 0.0, 0.0, 1.0);
}
void keyboard(unsigned char key, int mousePositionX, int mousePositionY) {
switch (key) {
case 'q':
exit(0);
break;
default:
break;
}
}
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON) {
if (state == GLUT_DOWN) {
m.pos[0] = x;
m.pos[1] = y;
m.down = 1;
}
if (state == GLUT_UP) {
m.down = 0;
}
}
}
void motion(int x, int y) {
int mx, my;
if (m.down) {
mx = (double)(x - m.pos[0]);
my = (double)(y - m.pos[1]);
m.pos[0] = x;
m.pos[1] = y;
m.angle[0] += mx;
m.angle[1] += my;
glutPostRedisplay();
}
}
int main(int argc, char** argv) {
// set window values
win.width = 640;
win.height = 480;
win.title = "teapot";
win.field_of_view_angle = 45;
win.z_near = 1.0f;
win.z_far = 500.0f;
// set mouse values;
m.down = 0;
m.angle[0] = m.angle[1] = 0.0;
// initialize and run program
glutInit(&argc, argv); // GLUT initialization
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); // Display Mode
glutInitWindowSize(win.width, win.height); // set window size
glutCreateWindow(win.title); // create Window
glutDisplayFunc(display); // register Display Function
glutIdleFunc(display); // register Idle Function
glutKeyboardFunc(keyboard); // register Keyboard Handler
glutMouseFunc(mouse);
glutMotionFunc(motion);
initialize();
glutMainLoop(); // run GLUT mainloop
return 0;
}
view raw teapot.c hosted with ❤ by GitHub

2015年9月19日土曜日

C++からCを呼んでみる

かなり使い古されていると思うけどC++からCを呼んでみました。 数値計算や簡単なロジックだけであればC言語だけで十分なのでC++の闇に触れないでC言語で作って他の言語から呼ぶのが、 今から性能を求めるシステムを作る場合はよいのではないかと思います。
#include "c.h"
#include <stdio.h>
char* hello(){
return "hello";
}
view raw c.c hosted with ❤ by GitHub
#ifdef __cplusplus
extern "C" {
char* hello();
}
#endif
view raw c.h hosted with ❤ by GitHub
#include "c.h"
#include <iostream>
using namespace std;
int main(){
cout<<hello()<<endl;
return 0;
}
view raw cpp.cpp hosted with ❤ by GitHub
build:
gcc -c c.c -o c.o
g++ -c cpp.cpp -o cpp.o
g++ -o c+cpp cpp.o c.o
view raw Makefile hosted with ❤ by GitHub

2015年9月15日火曜日

iPhoneでOpenGLを使って四角を書く

iPhoneで四角だけを書くものを作りました。 OpenGL ESがわからない上にObjective-Cはしんどいです。 これ正方形を書いたつもりなんだけど長くなっているということは 解像度によって調整しないといけないということなんやろうか。。 ただこの前作った流体シミュレーションと組み合わせると面白いことができそうです。
#import "GameViewController.h"
#import <OpenGLES/ES2/glext.h>
typedef struct
{
float Position[3];
}Vertex;
typedef struct
{
float Color[4];
}Color;
const Vertex gVertices[]={
{-0.5,-0.5,0},
{0.5,-0.5,0},
{-0.5,0.5,0},
{0.5,0.5,0}
};
const GLubyte gIndices[]={
0,1,2,3
};
Color gColors[] = {
{1,0,0,1},
{0,1,0,1},
{0,0,1,1},
{1,1,1,1}
};
@interface GameViewController () {
}
@property (strong,nonatomic) EAGLContext *context;
@property (strong,nonatomic) GLKBaseEffect *effect;
@property (nonatomic) GLuint vertexBuffer;
@property (nonatomic) GLuint indexBuffer;
@property (nonatomic) GLuint colorBuffer;
@end
@implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(@"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[self setupGL];
}
- (void)dealloc
{
[self tearDownGL];
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self isViewLoaded] && ([[self view] window] == nil)) {
self.view = nil;
[self tearDownGL];
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
}
- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
self.effect =[[GLKBaseEffect alloc] init];
self.effect.colorMaterialEnabled = YES;
glEnable(GL_DEPTH_TEST);
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(gVertices), gVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)NULL);
glGenBuffers(1, &_colorBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(gColors), gColors, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(Color), (void *)NULL);
glGenBuffers(1, &_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(gIndices), gIndices, GL_STATIC_DRAW);
}
- (void)tearDownGL
{
[EAGLContext setCurrentContext:self.context];
}
#pragma mark - GLKView and GLKViewController delegate methods
- (void)update
{
return;
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render the object with GLKit
[self.effect prepareToDraw];
glDrawElements(GL_TRIANGLE_STRIP, 6,GL_UNSIGNED_BYTE, 0);
}
#pragma mark - OpenGL ES 2 shader compilation
@end

2015年9月13日日曜日

もくもくする何か

もくもくする何かを作ってみました。 ナビエストークス方程式的な流体をスタガード格子でコンパクトに書いているサンプルがないので作ってみました。 もともと同じようなものがあったのでほとんど佐野ってきています。 時間ができたらiPhoneアプリみたいにしたいですね。
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#define START_FOR(X,Y) for(int j=0;j<Y;j++){for(int i=0;i<X;i++){
#define END_FOR }}
#define SWAP(i,j) {double ** tmp=i;i=j;j=tmp;}
#define MIN(i,j) (i>j?j:i)
#define MAX(i,j) (i<j?j:i)
#define CLIP(src,min,max) MAX(min,MIN(max,src))
#define DT 0.1 //Time Step
#define M 1.0 //Grid Size
#define N 64 //Cell Numbers
#define W 512 //Window Size
#define T 200 //Iteration Numbers
#define V 0.01 //Viscocity
const double H = M/N; //Cell Size
int mx_prev,my_prev,mstat; //Mouse
double **s,**s_swap; //Steam
double **f[2]; //Force (0:x,1:y)
double **u, **v, **u_swap, **v_swap; //Velocity
double **d, **p, **o; //Divergence,Pressure,Vorticity
int show_v,show_g; //Show Flag(Velocity,Grid)
double ** alloc2d(int n, int m) {
double ** ptr = (double **)malloc(sizeof(double *) * n);
for(int i = 0; i < n; i++){
ptr[i] = (double *)malloc(sizeof(double) * m);
}
return ptr;
}
void free2d(double **ptr, int n){
for(int i=0; i < n; i++){
free(ptr[i]);
}
free(ptr);
}
void init(){
if(!s) s = alloc2d(N,N);
if(!s_swap) s_swap = alloc2d(N,N);
if(!f[0]) f[0] = alloc2d(N,N);
if(!f[1]) f[1] = alloc2d(N,N);
if(!u) u = alloc2d(N+1,N);
if(!v) v = alloc2d(N,N+1);
if(!u_swap) u_swap = alloc2d(N+1,N);
if(!v_swap) v_swap = alloc2d(N,N+1);
if(!d) d = alloc2d(N,N);
if(!p) p = alloc2d(N,N);
if(!o) o = alloc2d(N,N);
START_FOR(N+1,N)
u[i][j] = 0.0;
u_swap[i][j] = 0.0;
END_FOR
START_FOR(N,N+1)
v[i][j] = 0.0;
v_swap[i][j] = 0.0;
END_FOR
START_FOR(N,N)
d[i][j] = 0.0;
p[i][j] = 0.0;
o[i][j] = 0.0;
s[i][j] = 0.0;
s_swap[i][j] = 0.0;
f[0][i][j] = 0.0;
f[1][i][j] = 0.0;
END_FOR
show_v = show_g = 0;
}
void final(){
if(u)free(u);u=NULL;
if(v)free(v);v=NULL;
if(u_swap)free(u_swap);u_swap=NULL;
if(v_swap)free(v_swap);v_swap=NULL;
if(p)free(p);p=NULL;
if(d)free(d);d=NULL;
if(o)free(o);o=NULL;
if(s)free(s);s=NULL;
if(s_swap)free(s_swap);s_swap=NULL;
if(f[0])free(f[0]);f[0]=NULL;
if(f[1])free(f[1]);f[1]=NULL;
}
double u_ref(int i, int j){
return u[CLIP(i,0,N)][CLIP(j,0,N-1)];
}
double v_ref(int i, int j){
return v[CLIP(i,0,N-1)][CLIP(j,0,N)];
}
double s_ref(int i, int j){
return s[CLIP(i,0,N-1)][CLIP(j,0,N-1)];
}
double p_ref(int i, int j){
return p[CLIP(i,0,N-1)][CLIP(j,0,N-1)];
}
void enforce_boundary(){
START_FOR(N+1,N)
if(i==0 || i==N) u[i][j] = 0;
END_FOR
START_FOR(N,N+1)
if(j==0 || j==N) v[i][j] = 0;
END_FOR
}
void vorticity_confinement(){
//Compute Vorticity
START_FOR(N, N)
o[i][j] = ((v_ref(i+1,j)-v_ref(i-1,j))-(u_ref(i,j+1)-u_ref(i,j-1))) * 0.5 * H;
END_FOR
START_FOR(N, N)
if(i==0 || i==N-1 || j==0 || j==N-1) continue;
//Compute Gradient of Vorticity Length
double h[2]= {
(fabs(o[i+1][j])-fabs(o[i-1][j])) * 0.5 * H,
(fabs(o[i][j+1])-fabs(o[i][j-1])) * 0.5 * H
};
double h_len = hypot(h[0],h[1]);
if(h_len > 0){
double e = 10;
double n[2] = { h[0]/h_len, h[1]/h_len};
f[0][i][j] += e * H * (n[1] * o[i][j]);
f[1][i][j] += e * H * -(n[0] * o[i][j]);
}
END_FOR
}
void compute_force(){
START_FOR(N,N)
if(i==0)continue;
u[i][j] += f[0][i][j] + f[0][i-1][j];
END_FOR
START_FOR(N,N)
if(j==0)continue;
v[i][j] += f[1][i][j] + f[1][i][j-1];
END_FOR
}
double interpolation(double **src, double x, double y, int width, int height){
x = CLIP(x,0.0,width);
y = CLIP(y,0.0,height);
int i = MIN(x,width-2);
int j = MIN(y,height-2);
return ((i+1-x)*src[i][j]+(x-i)*src[i+1][j])*(j+1-y)+
((i+1-x)*src[i][j+1]+(x-i)*src[i+1][j+1])*(y-j);
}
void semiLagragian(double **src, double **u, double **v,int width, int height, double **out){
START_FOR(width,height)
out[i][j]=interpolation(src, i-N*u[i][j]*DT, j-N*v[i][j]*DT, width, height);
END_FOR
}
void compute_advection(){
//Compute Fluid Velocity At Each Staggerd Faces And Cell Centers
double **uy = alloc2d(N+1,N);
double **vx = alloc2d(N,N+1);
double **cx = alloc2d(N,N);
double **cy = alloc2d(N,N);
START_FOR(N+1,N)
uy[i][j] = (v_ref(i-1,j) + v_ref(i-1,j+1) + v_ref(i,j+1) + v_ref(i,j)) * 0.25;
END_FOR
START_FOR(N,N+1)
vx[i][j] = (u_ref(i,j-1) + u_ref(i,j) + u_ref(i+1, j) + u_ref(i+1,j-1)) * 0.25;
END_FOR
START_FOR(N,N)
cx[i][j] = (u[i][j] + u[i+1][j]) * 0.5;
cy[i][j] = (v[i][j] + v[i][j+1]) * 0.5;
END_FOR
semiLagragian(u,u,uy,N+1,N,u_swap);
semiLagragian(v,vx,v,N,N+1,v_swap);
semiLagragian(s,cx,cy,N,N,s_swap);
free2d(uy,N+1);
free2d(vx,N);
free2d(cx,N);
free2d(cy,N);
SWAP(u,u_swap);
SWAP(v,v_swap);
SWAP(s,s_swap);
}
void compute_diffuse(){
double a = DT * V;
START_FOR(N+1,N)
u_swap[i][j] = u_ref(i,j) + a * (u_ref(i-1,j) + u_ref(i+1,j) + u_ref(i,j-1) + u_ref(i,j+1) - 4 * u_ref(i,j));
END_FOR
START_FOR(N,N+1)
v_swap[i][j] = v_ref(i,j) + a * (v_ref(i-1,j) + v_ref(i+1,j) + v_ref(i,j-1) + v_ref(i,j+1) - 4 * v_ref(i,j));
END_FOR
START_FOR(N,N)
s_swap[i][j] = s_ref(i,j) + a * (s_ref(i-1,j) + s_ref(i+1,j) + s_ref(i,j-1) + s_ref(i,j+1) - 4 * s_ref(i,j));
END_FOR
SWAP(u,u_swap);
SWAP(v,v_swap);
SWAP(s,s_swap);
}
void compute_divergence(){
START_FOR(N, N)
d[i][j] = ((u[i+1][j]-u[i][j])+(v[i][j+1]-v[i][j]))/H;
END_FOR
}
void compute_pressure(){
double h2 = H * H;
for(int k=0; k < T; k++){
START_FOR(N,N)
p[i][j] = ((p_ref(i+1,j)+p_ref(i-1,j)+p_ref(i,j+1)+p_ref(i, j-1))-h2*d[i][j])/4;
END_FOR
}
}
void subtract_pressure(){
START_FOR(N+1,N)
if(i>0 && i<N) u[i][j] -= (p[i][j]-p[i-1][j])/H;
END_FOR
START_FOR(N,N+1)
if(j>0 && j<N) v[i][j] -= (p[i][j]-p[i][j-1])/H;
END_FOR
}
void computeStep(){
enforce_boundary();
vorticity_confinement();
compute_force();
compute_advection();
compute_diffuse();
compute_divergence();
compute_pressure();
subtract_pressure();
}
void computePostDisplay(){
START_FOR(N,N)
f[0][i][j]=0;
f[1][i][j]=0;
END_FOR
}
void idle(){
computeStep();
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y){
switch(key){
case 'q':final();exit(0);break;
case 'v':show_v = !show_v;break;
case 'g':show_g = !show_g;break;
}
}
void mouse(int button, int state, int x, int y){
mx_prev = x;
my_prev = y;
mstat = state;
}
void motion(int x, int y){
if(mstat == GLUT_DOWN){
double cx = x/(double)W;
double cy = M-y/(double)W;
double dx = (x-mx_prev)/(double)W;
double dy = -(y-my_prev)/(double)W;
int i = CLIP(cx*N, 0, N-1);
int j = CLIP(cy*N, 0, N-1);
f[0][i][j]+=CLIP(M*N*dx, -M/N/DT,M/N/DT);
f[1][i][j]+=CLIP(M*N*dy, -M/N/DT,M/N/DT);
int r = N/64;
for(int u=-r; u<=r; u++){
for(int v=-r; v<=r; v++){
if(hypot(u,v)<=r){
s[i+u][j+v] = 2.0;
}
}
}
}
mx_prev = x;
my_prev = y;
}
void drawBitmapString(const char *string){
while(*string){
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *string++);
}
}
void displayText(){
glColor4f(1.0,1.0,1.0,1.0);
glRasterPos2d(0.05,0.95);
drawBitmapString("Press \"q\" to close window");
}
void displayConcentration(double **c){
START_FOR(N-1,N-1)
double pos[2]={i*H+H*0.5,j*H+H*0.5};
double s = 2000;
glBegin(GL_QUADS);
glColor4d(c[i][j]>0,0,c[i][j]<0,fabs(c[i][j]*s));
glVertex2d(pos[0],pos[1]);
glColor4d(c[i+1][j]>0,0,c[i+1][j]<0,fabs(c[i+1][j]*s));
glVertex2d(pos[0]+H,pos[1]);
glColor4d(c[i+1][j+1]>0,0,c[i+1][j+1]<0,fabs(c[i+1][j+1]*s));
glVertex2d(pos[0]+H,pos[1]+H);
glColor4d(c[i][j+1]>0,0,c[i][j+1]<0,fabs(c[i][j+1]*s));
glVertex2d(pos[0],pos[1]+H);
glEnd();
END_FOR
}
void displaySteam(){
START_FOR(N-1,N-1)
double pos[2]={i*H+H*0.5,j*H+H*0.5};
glBegin(GL_QUADS);
glColor4d(s[i][j],s[i][j],s[i][j],s[i][j]);
glVertex2d(pos[0],pos[1]);
glColor4d(s[i+1][j],s[i+1][j],s[i+1][j],s[i+1][j]);
glVertex2d(pos[0]+H,pos[1]);
glColor4d(s[i+1][j+1],s[i+1][j+1],s[i+1][j+1],s[i+1][j+1]);
glVertex2d(pos[0]+H,pos[1]+H);
glColor4d(s[i][j+1],s[i][j+1],s[i][j+1],s[i][j+1]);
glVertex2d(pos[0],pos[1]+H);
glEnd();
END_FOR
}
void displayGrid(){
glColor4d(0.4,0.4,0.4,0.5);
for (int i=0; i<N+1; i++){
glBegin(GL_LINES);
glVertex2d(0.0, H*i);
glVertex2d(1.0, H*i);
glEnd();
}
for (int i=0; i<N+1; i++){
glBegin(GL_LINES);
glVertex2d(H*i, 0.0);
glVertex2d(H*i, 1.0);
glEnd();
}
}
void displayVelocity(){
glColor4d(1.0,1.0,0.0,1.0);
START_FOR(N, N)
double pos[2]={i*H+H*0.5,j*H+H*0.5};
double vel[2]={(u[i][j]+u[i+1][j])*0.5,(v[i][j]+v[i][j+1])*0.5};
glBegin(GL_LINES);
glVertex2d(pos[0],pos[1]);
glVertex2d(pos[0]+vel[0],pos[1]+vel[1]);
glEnd();
END_FOR
}
void display(){
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if(show_g)displayGrid();
if(show_v)displayVelocity();
displaySteam();
displayText();
glutSwapBuffers();
computePostDisplay();
}
void reshape(int w, int h){
glViewport(0,0,w,h);
glLoadIdentity();
glOrtho(0.0,M,0.0,M,-1.0,1.0);
}
int main(int argc, char * argv[]){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100,100);
glutInitWindowSize(W,W);
glutCreateWindow("steam2d");
glutIdleFunc(idle);
glutKeyboardFunc(keyboard);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
init();
glutMainLoop();
return 0;
}
view raw fluid2d.c hosted with ❤ by GitHub

2015年8月16日日曜日

いろんな言語でOOPする

そろそろObjective-Cの勉強もしたくなったのいろんな言語でOOPしてみました。 Javaで書いた1+2だけをするプログラムををC++/Objective-C/Swiftで書いてみます。 これで少しiPhoneアプリのソースも読めそうです。 あとObjective-Cの硬い感じに慣れるとSwiftのチャラっとした感じは受け付けないのも少し理解できました。 ただ結局iPhoneもAndroidもロジックは同じC++が使えるのでC++が結局生き残るだろうなとも思います。 リソース管理はスマートポインタで少し改善できているし、最終的にC++でないとできないことも多いだろうし。

Java

interface Calculator{
int add();
}
view raw Calculator.java hosted with ❤ by GitHub
class CalculatorImpl implements Calculator{
private int a,b;
CalculatorImpl(int a, int b){
this.a = a;
this.b = b;
}
public int add(){
int c = a + b;
return c;
}
}
class Main{
public static void main(String args[]){
Calculator calc = new CalculatorImpl(1,2);
int result = calc.add();
System.out.println(result);
}
}
view raw Main.java hosted with ❤ by GitHub

C++

#include "calculator.h"
calculator::calculator(int a, int b){
this->a = a;
this->b = b;
}
int calculator::add(){
int c = a + b;
return c;
}
view raw calculator.cpp hosted with ❤ by GitHub
#ifndef Calc_calc_h
#define Calc_calc_h
class calculator{
private:
int a,b;
public:
calculator(int a, int b);
int add();
};
#endif
view raw calculator.h hosted with ❤ by GitHub
#include <iostream>
#include <memory>
#include "calculator.h"
using namespace std;
int main(int argc, const char * argv[]) {
//calculator *calc = new calculator(1,2);
unique_ptr<calculator> calc(new calculator(1,2));
int result = calc->add();
cout << result << endl;
//delete calc;
return 0;
}
view raw main.cpp hosted with ❤ by GitHub

Objective-C

#ifndef Calc_objc_Calculator_h
#define Calc_objc_Calculator_h
#import <Foundation/Foundation.h>
@interface Calculator : NSObject{
@private
int a,b;
}
-(id)initWithNum:(int)a b: (int)b;
-(int)add;
@end
#endif
view raw calculator.h hosted with ❤ by GitHub
#import <Foundation/Foundation.h>
#import "Calculator.h"
@implementation Calculator
-(id)initWithNum:(int)a b:(int)b{
if(self = [super init]){
self->a = a;
self->b = b;
}
return self;
}
-(int)add{
int c = a + b;
return c;
}
@end
view raw calculator.m hosted with ❤ by GitHub
#import "Calculator.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Calculator * calc = [[Calculator alloc]initWithNum: 1 b:2];
int result = [calc add];
NSLog(@"%d",result);
}
return 0;
}
view raw main.m hosted with ❤ by GitHub

Swift

class Calculator:CalculatorProtocol{
var a: Int
var b: Int
init(a: Int, b: Int){
self.a = a
self.b = b
}
func add() -> Int{
let c = a + b
return c
}
}
import Foundation
protocol CalculatorProtocol{
func add() -> Int
}
var calc = Calculator(a:1,b:2)
var result = calc.add()
println(result)
view raw main.swift hosted with ❤ by GitHub

2015年6月6日土曜日

ポインタの威力

C言語の復習にポインタの威力を改めて勉強してみました。 やはり 3つ以上のポインタは訳がわからなくなりそうですね。
#include <stdio.h>
int main(void){
int *p1[2][2],**p2[2],***p3;
int i[4]={100,200,300,400};
p1[0][0]=&i[0];
p1[0][1]=&i[1];
p1[1][0]=&i[2];
p1[1][1]=&i[3];
p2[0]=p1[0];
p2[1]=p1[1];
p3=p2;
for(int i=0;i<2;i++){
for(int j=0; j<2; j++){
printf("p[%d][%d]=%p %d\n",i,j,p3,*(**p3+i*2+j));
}
}
}
view raw triple.c hosted with ❤ by GitHub

2015年3月10日火曜日

Singleton Collection

いろんな言語でSingletonを実装してみると言語ごとの違いがよく出て面白い。 ただ、C言語はかなりノリで作ったものなので、こうこともできるという参考ということで。
#include <stdio.h>
#include <stdlib.h>
typedef struct singleton{
int id;
struct singleton* (*getInstance)();
} Singleton;
static Singleton *instance;
Singleton *getInstance(){
if(instance == NULL){
instance = (Singleton *)malloc(sizeof(Singleton));
}
return instance;
}
Singleton* createSingleton(){
Singleton *instance = getInstance();
instance->getInstance = getInstance;
return instance;
}
int main(){
Singleton *s1 = createSingleton();
Singleton *s2 = createSingleton();
s1->getInstance()->id=10;
s2->getInstance()->id=20;
Singleton *obj = s1->getInstance();
//if obj->id is 20, it may be singleton.
printf("%d",obj->id );
}
view raw singleton.c hosted with ❤ by GitHub
final class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
view raw Singleton.java hosted with ❤ by GitHub
var Singleton = (function(){
var instance;
function init(){
return {}
}
return {
getInstance: function(){
if(!instance){
instance = init();
}
return instance;
}
}
})();
view raw singleton.js hosted with ❤ by GitHub

2015年2月11日水曜日

glMatrix2.x系の互換性

WebGLのサンプルを見ているとよくglMatrix0.9を使っていることがあるけど新しいglMatrix2.2を使うと若干引数の位置とかが違ったのでメモ。

mat4.perspective(60, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0,projectionMatrix);
mat4.identity(modelViewMatrix);
mat4.lookAt([8, 5, -10],[0, 0, 0],[0, 1, 0],modelViewMatrix);
view raw glmatrix09.js hosted with ❤ by GitHub
mat4.perspective(projectionMatrix, Math.PI * (60/180), gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
mat4.identity(modelViewMatrix);
mat4.lookAt(modelViewMatrix,[8, 5, -10],[0, 0, 0],[0, 1, 0]);
view raw glmatrix2x.js hosted with ❤ by GitHub

2015年1月27日火曜日

明けましておめでとうございます。

明けましておめでとうございます。
WebGLとGithub Pagesの勉強がてら日章旗を描いてみました。

  http://sanofc.github.io/webgl/08/ 

色々めんどくさいことも多いですが、来年ははためかせたいです。。

2015年1月23日金曜日

一人バイキング

楽しかったです。