こんにちは。
今回は、拡散律速凝集の様をシミュレートしてみます。
これまで分子の衝突やランジュバン方程式など、ブラウン運動についていろいろやってきました。
(今更ですけどタイトルはLJポテンシャルになってますが、温度を変えないならブラウン運動にはあまり関係ありません…)
今回もその関連で、「拡散律速凝集」を扱ってみます。
目次
拡散律速凝集とは
拡散律速凝集は、核となる部分に何か分子がくっついていって塊が大きくなっていくことを指します。
私は化学系の人間なのですが、得られた試料で結晶ができるときにそのような形が見えたりします。
英語では「diffusion-limited aggregation」なので、略して「DLA」とも言われるらしい。
プログラム化する
ランダムウォーク
ブラウン運動している分子が核にくっついていくことで凝集しますが、ここではブラウン運動としてランダムウォークを使ってみます。
int n = 100; Particle[] particles = new Particle[n]; void setup(){ size(500, 500); noStroke(); for(int i = 0; i < n; i ++){ particles[i] = new Particle(random(-width/2, width/2), random(-height/2, height/2)); } } void draw(){ background(0); translate(width/2, height/2); for(int i = 0; i < n; i ++){ particles[i].move(); particles[i].display(); } } class Particle{ float x, y; float d; float v; Particle(float _x, float _y){ x = _x; y = _y; d = 20; v = 5; } void move(){ x += random(-v, v); y += random(-v, v); } void display(){ fill(255); ellipse(x, y, d, d); } }
核との衝突判定
では、核を中心に置き、周りの分子がくっついたところで固定する、という処理を加えます。
int n = 100; Particle[] particles = new Particle[n]; void setup(){ size(500, 500); noStroke(); particles[0] = new Particle(0, 0, false); //核 for(int i = 1; i < n; i ++){ particles[i] = new Particle(random(-width/2, width/2), random(-height/2, height/2), true); } } void draw(){ background(0); translate(width/2, height/2); for(int i = 0; i < n; i ++){ particles[i].move(); particles[i].display(); particles[i].check(); } } class Particle{ float x, y; float d; float v; boolean moving; Particle(float _x, float _y, boolean _m){ x = _x; y = _y; moving = _m; //核とくっついたかどうか d = 20; v = 5; } void move(){ if(moving == true){ //動いているとき x += random(-v, v); y += random(-v, v); } else{ //核とくっついたとき v = 0; } } void display(){ if(moving == true){ //動いているとき fill(255); //白 } else{ //核とくっついたとき fill(255, 0, 0); //赤 } ellipse(x, y, d, d); } //核との衝突判定 void check(){ for(int i = 0; i < n; i ++){ //周りの分子全てについて if(moving == false && particles[i].moving == true){ //注目している分子が核で、周りの分子iが動いていて if(dist(x, y, particles[i].x, particles[i].y) < d/2 + particles[i].d/2){ //距離が半径の和と同じとき particles[i].moving = false; //周りの分子iを固定する } } } } }
実行すると、
それっぽい形になりました。
ArrayListを使う
このままでは凝集していくにつれて周りの分子が減っていくので、凝集の速さが遅くなります。
そこで「ArrayList」を使い、周りの分子を増やしながら凝集されていくようにしてみます。
int n = 100; ArrayList<Particle> particles = new ArrayList<Particle>(); void setup(){ size(500, 500); noStroke(); particles.add(new Particle(0, 0, false)); for(int i = 1; i < n; i ++){ particles.add(new Particle(random(-width/2, width/2), random(-height/2, height/2), true)); } } void draw(){ background(0); translate(width/2, height/2); //分子を増やす for(int i = 0; i < 5; i ++){ //5個増やす float r = random(0, 4); float newx = 0; float newy = 0; //どの向きから増やすかを決める if(r < 1){ newx = random(-width/2, width/2); newy = -height/2; } else if(1 <= r && r < 2){ newx = random(-width/2, width/2); newy = height/2; } else if(2 <= r && r < 3){ newx = -width/2; newy = random(-height/2, height/2); } else if(3 <= r && r < 4){ newx = width/2; newy = random(-height/2, height/2); } particles.add(new Particle(newx, newy, true)); } for(Particle p: particles){ p.move(); p.display(); p.check(); } //画面外に出た分子は消す for(int i = 0; i < particles.size(); i ++){ Particle p = particles.get(i); if((p.x < -width/2 || width/2 < p.x) || (p.y < -height/2 || height/2 < p.y)){ particles.remove(i); } } } class Particle{ float x, y; float d; float v; boolean moving; Particle(float _x, float _y, boolean _m){ x = _x; y = _y; moving = _m; d = 20; v = 5; } void move(){ if(moving == true){ x += random(-v, v); y += random(-v, v); } else{ v = 0; } } void display(){ if(moving == true){ fill(255); } else{ fill(255, 0, 0); } ellipse(x, y, d, d); } void check(){ for(Particle p: particles){ if(moving == false && p.moving == true){ if(dist(x, y, p.x, p.y) < d/2 + p.d/2){ p.moving = false; } } } } }
実行すると、
外側から分子が増えながら、凝集していく様子が見えます。
n = 10000
、d = 5
としてみると、
(分子数が増えると描画するのが大変なので、moving = false
のものだけを描画するようにしたほうがいいでしょう。)
まとめ
以上で拡散律速凝集を実装できました。
パラメーターを変えればまた違った凝集具合になると思うので、いろいろいじって遊んでみると面白いかもしれません。
分子同士がぶつかるようにしても変わるかも。
最後まで読んでいただき、ありがとうございました。
また次回。