JavaScript中的N-Body重力模拟

5

我正在尝试使用JavaScript创建一个N-Body引力模拟:

http://jsfiddle.net/4M94x/


请注意,此链接是该项目的演示示例。
var Circle = function(c, r, cor, cof) { // Fix CoR & CoF // Had to add code for JSFiddle link :P
    this.c = c
    this.r = r
    this.m = r * r * Math.PI
    this.v = new Vector()
    this.cor = cor
    this.cof = cof
}

问题在于当你点击生成两个球(意外地被重命名为“粒子”)并将它们放在一起时,它们开始产生速度,并且越来越快地推动彼此...我该如何修复这个问题?顺便问一句,我的重力实现是否正确?

是的,将两个物体的质量设置为相同仍会生成速度-.- - user2039981
1个回答

24
这很容易解释:您正在使用欧拉正向作为求解ODE的求解器,在机械系统中,欧拉正向的系统误差会增加能量。欧拉反向会减少能量,因此交替使用显式和隐式欧拉方法的组合将使能量略微更加恒定。

但是,您可以使用同样甚至更少的工作量实现保持能量和其他物理不变量的二阶辛普森方法(隐式中点方法或Verlet-(Stoermer-Cromer-...-Newton-)方法)。

甚至还有更高阶的龙格-库塔方法,尽管不是辛普森方法,但也会以更高阶保持能量。有关Stoermer-Verlet-...-Newton方法的Hairer文章,请参见后印刷品预印刷版以及使用C ++的“Moving stars around”教程文本Ruby


关于物理学的说明:总体而言,实现非常简单且易于阅读。但是,引力作用是

g*m1*m2*(p2-p1)/norm(p2-p1)^3

作为负梯度

g*m1*m2/norm(p2-p1)

你只是在使用范数的平方,其中力应为重力势能的负梯度。

 g*m1*m2*ln(norm(p2-p1))

这适用于平面物理,但不适用于三维空间的二维截面。


可行代码

使用速度Verlet和能量守恒:

向圆对象添加一个新字段a=Vector(),并将update()函数中的一大堆内容替换为以下专用函数集合。

function compute_forces() {
    for (var i = 0; i < particles.length; i++) {
        var p = particles[i];
        p.a.set(0);

        for (var j = 0; j < i; j++) {
            var p2 = particles[j];

            var d = p.c.sub(p2.c);
            var norm = Math.sqrt(100.0 + d.lengthSq());
            var mag = gravity / (norm * norm * norm);

            p.a.set(p.a.sub(d.mul(mag * p2.m)));
            p2.a.set(p2.a.add(d.mul(mag * p.m)));

        }
    }

}


function do_collisions() {
    for (var i = 0; i < particles.length; i++) {
        var p = particles[i];
        for (var j = 0; j < i; j++) {
            var p2 = particles[j];

            if (checkCCCol(p, p2)) {
                resCCCol(p, p2);
            }
        }
    }
}


function do_physics(dt) {
    // do velocity Verlet 
    // with consistent state at interval half points
    // x += 0.5*v*dt
    for (var i1 = 0; i1 < particles.length; i1++) {
        var p1 = particles[i1];
        p1.c.set(p1.c.add(p1.v.mul(0.5 * dt)));
    }
    // a = A(x)
    compute_forces();
    // v += a*dt
    for (var i2 = 0; i2 < particles.length; i2++) {
        var p2 = particles[i2];
        p2.v.set(p2.v.add(p2.a.mul(dt)));
    }
    // x += 0.5*v*dt
    for (var i3 = 0; i3 < particles.length; i3++) {
        var p3 = particles[i3];
        p3.c.set(p3.c.add(p3.v.mul(0.5 * dt)));
    }
    do_collisions();
}

function update() {

    for (var k = 0; k < 4; k++) {
        do_physics(1.0 / 4);
    }

    render();

    RAF(update);
}

请查看http://jsfiddle.net/4XVPH/


基于粒子的质量着色的修改示例(希望更好地显示它们的相互作用),修复了一个错误,并添加了一些附加注释:http://jsfiddle.net/24mg6ctg/12/


嗯,在 do_physics 函数中,有一个参数是 do_physics(1/8),这不应该改成 do_physics((1/60)*(1/8)) 吗? - user2039981
那个100.0是从哪里来的? - user2039981
重力常数和时间单位(以及它们与长度单位的关系)在这个模拟中有些随意。但它确实有效。100作为10的平方给出了相对较小的重力势扰动。它的添加避免了在异乎寻常的情况下(初始化新磁盘时)除以零或产生极大的力。 - Lutz Lehmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接