我想编写一个模拟许多粒子碰撞的小程序,首先在2D中开始(我稍后将其扩展到3D),到(在3D中)模拟向Boltzmann分布的收敛,并且还要看看如何分布在2D中演变。
我还没有开始编程,所以请不要问代码示例,这是一个相当普遍的问题,应该可以帮助我开始。这个问题背后的物理问题对我没有任何问题,而是我必须模拟至少200-500个粒子才能达到相当好的速度分布。我想实时做到这一点。
现在,对于每个时间步,我将首先更新所有粒子的位置,然后检查碰撞,以更新新的速度矢量。然而,这包括很多检查,因为我必须看看每个粒子是否与其他每个粒子发生碰撞。 我发现this或多或少地发布了相同的问题,而且那里使用的方法也是我唯一能想到的问题。但是,我担心这不会在实时工作得很好,因为它会涉及太多的碰撞检查。
现在:即使这种方法在性能方面有所提升(比如40fps),有人可以想办法避免不必要的碰撞检查吗?
我自己的想法是将电路板(或3D:空间)分成具有至少粒子直径尺寸的正方形(立方体),并实现仅检查碰撞的方法,如果两个粒子的中心是在网格中的adjecent广场内...
我很乐意听到更多的想法,因为我希望尽可能多地增加粒子数量,并且仍在进行实时计算/模拟。
编辑:所有碰撞都是纯粹的弹性碰撞,没有任何其他力量对粒子进行处理。我将实现的初始情况由用户选择的一些变量决定,以选择随机起始位置和速度。
Edit2:我发现了关于粒子碰撞模拟的一篇很好且非常有用的论文here。希望它可以帮助一些对此更感兴趣的人。
答案 0 :(得分:7)
如果你想到这一点,在计划上移动的粒子实际上是一个三维系统,其中三维是x
,y
和时间(t
)。
假设“时间步长”从t0
变为t1
。对于每个粒子,您可以根据当前粒子位置,速度和方向创建从P0(x0, y0, t0)
到P1(x1, y1, t1)
的3D线段。
在3D网格中划分3D空间,并将每个3D线段链接到它穿过的单元格。
现在,应检查每个网格单元格。如果它链接到0或1段,则无需进一步检查(将其标记为已选中)。如果它包含2个或更多段,则需要检查它们之间的碰撞:计算3D碰撞点Pt
,缩短这两个段以在此时结束(并删除它们不再交叉的单元格的链接) ,根据粒子的新方向/速度,创建从Pt
到新计算的P1
点的两个新段。将这些新线段添加到网格中,并将单元格标记为已选中。向网格添加线段会将所有交叉单元格转换为未选中状态。
当您的网格中没有未经检查的单元格时,您已经解决了时间步问。
修改强>
答案 1 :(得分:5)
空间划分的一个很好的高级例子是考虑乒乓球的比赛,并检测球和桨之间的碰撞。
假设球拍在屏幕的左上角,并且球在屏幕的左下角附近......
--------------------
|▌ |
| |
| |
| ○ |
--------------------
每次球移动时都没有必要检查碰撞。相反,将比赛场分成中间两个。球是球场的左侧吗? (矩形算法中的简单点)
Left Right
|
---------|----------
|▌ | |
| | |
| | |
| ○ | |
---------|----------
|
如果答案是肯定的,再次分开左手边,这次是横向分开,所以我们有一个左上角和左下角分区。
Left Right
|
---------|----------
|▌ | |
| | |
----------- |
| | |
| ○ | |
---------|----------
|
这个球是否与屏幕一样位于屏幕的左上角?如果没有,不需要检查碰撞! 只需要测试位于同一分区中的对象是否相互冲突。通过一系列简单(且便宜)的内部矩形检查,您可以轻松地避免进行更昂贵的形状/几何碰撞检查。
您可以继续将空间拆分为越来越小的块,直到对象跨越两个分区。这是BSP背后的基本原理(在像Quake这样的早期3D游戏中开创的技术)。网络上有很多关于2维和3维空间划分的理论。
http://en.wikipedia.org/wiki/Space_partitioning
在2维中,您经常使用BSP或四叉树。在3个维度中,您经常使用八叉树。但是,基本原则保持不变。
答案 2 :(得分:1)
你可以按照“分而治之”的思路思考。这个想法是识别正交参数而不会相互影响。例如可以想到在2D(3D轴为3轴)的情况下沿2轴分离动量分量并独立计算碰撞/位置。识别这些参数的另一种方法可以是将彼此垂直移动的粒子分组。因此,即使它们受到影响,沿着这些线的净动量也不会改变。
我同意上面没有完全回答你的问题,但它传达了一个你可能会觉得有用的基本想法。
答案 3 :(得分:1)
让我们说在时间t,对于每个粒子,你有:
P position
V speed
和粒子A(i)和A(j)之间的N *(N-1)/ 2信息阵列,其中i <1。焦耳;您使用对称来评估上三角矩阵而不是完整的N *(N-1)网格。
MAT[i][j] = { dx, dy, dz, sx, sy, sz }.
这意味着对于粒子j,粒子j具有由三个分量dx,dy和dz组成的距离;和delta-vee乘以dt,即sx,sy,sz。
要移至瞬间t + dt,您可以根据速度暂时更新所有粒子的位置
px[i] += dx[i] // px,py,pz make up vector P; dx,dy,dz is vector V premultiplied by dt
py[i] += dy[i] // Which means that we could use "particle 0" as a fixed origin
pz[i] += dz[i] // except you can't collide with the origin, since it's virtual
然后检查整个N *(N-1)/ 2阵列并暂时计算每对粒子之间的新相对距离。
dx1 = dx + sx
dy1 = dy + sy
dz1 = dz + sz
DN = dx1*dx1+dy1*dy1+dz1*dz1 # This is the new distance
如果DN <粒径为D的D ^ 2,刚刚过去的dt中发生了碰撞。
然后你可以准确计算出这种情况发生的位置,即你计算碰撞的确切距离,你可以从旧的距离平方D2(dx * dx + dy * dy + dz * dz)和新DN进行计算:它
d't = [(SQRT(D2)-D)/(SQRT(D2)-SQRT(DN))]*dt
(在时间dt内以覆盖距离SQRT(D2)-SQRT(DN)的速度减小从SQRT(D2)到D的距离所需的时间)。 这使得假设从粒子i的参考框架看到的粒子j没有“过度”。
这是一个更加沉重的计算,但是当你发生碰撞时你只需要它。
知道了d,并且d“t = dt-d't,你可以使用dx * d't / dt等在Pi和Pj上重复位置计算,并获得粒子i和j的精确位置P.在碰撞瞬间;你更新速度,然后将其整合为剩余的d“t并在时间结束时得到位置。
请注意,如果我们停在此处,如果发生三粒子碰撞,此方法将会中断,并且只会处理两粒子碰撞。
因此,我们只是标记在粒子(i,j)的d t处发生碰撞而不是运行计算,并且在运行结束时,我们保存发生碰撞的最小值,并且在谁之间。
即,我们检查粒子25和110并发现0.7 dt的碰撞;然后我们发现在0.3 dt时110和139之间发生碰撞。没有早于0.3 dt的碰撞。
我们进入碰撞更新阶段,并“碰撞”110和139并更新它们的位置和速度。然后对每个(i,110)和(i,139)重复2 *(N-2)计算。
我们将发现可能仍然存在与粒子25的碰撞,但是现在在0.5 dt,并且可能,例如,另一个在0.9和80之间的139和80之间。 0.5 dt是新的最小值,因此我们重复25到110之间的碰撞计算,并重复,在算法中每次碰撞都会有轻微的“减速”。
如此实施,现在唯一的风险是“鬼魂碰撞”,即粒子在D>在时间t-dt从目标起的直径,并且在D>在时间t,直径在另一侧。
这只能通过选择一个dt来避免,以便在任何给定的dt中没有粒子的行程超过其自身直径的一半。实际上,您可以根据最快粒子的速度使用自适应dt。幽灵掠过碰撞仍然是可能的;进一步的改进是基于任何两个粒子之间的最近距离来减小dt。
这样,算法确实在碰撞附近显着减慢,但是当碰撞不太可能时,它会大大加速。如果两个粒子之间的最小距离(我们在循环期间几乎没有成本计算)是这样的,那么最快的粒子(我们也几乎没有任何成本发现)不能在不到50分的时间内覆盖它,那就是4900 %速度增加。
无论如何,在无冲突的通用情况下,我们现在已经完成了五个总和(实际上更像是由于数组索引的三十四个),三个产品和每个粒子对的几个赋值。如果我们包含(k,k)对来考虑粒子更新本身,我们到目前为止有一个很好的近似成本。
这种方法的优点是O(N ^ 2) - 它随着粒子的数量而变化 - 而不是O(M ^ 3) - 随着所涉及的空间体积而缩放。
我希望现代处理器上的C程序能够实时管理数万个数量级的粒子。
P.S。:这实际上非常类似于Nicolas Repiquet的方法,包括在多次碰撞的4D附近减速的必要性。
答案 4 :(得分:1)
直到两个粒子之间(或粒子与墙壁之间)发生碰撞,整合才是微不足道的。这里的方法是计算第一次碰撞的时间,积算到那时,然后计算第二次碰撞的时间,依此类推。我们将tw[i]
定义为i
粒子击中第一个墙所需的时间。尽管你必须考虑球体的直径,但它很容易计算。
计算两个粒子tc[i,j]
和i
之间碰撞的时间j
会花费更多的时间,并从距离的研究中得出{{1} }:
d
我们研究是否存在d^2=Δx(t)^2+Δy(t)^2+Δz(t)^2
正为t
,即d^2=D^2
粒子的直径(或粒子的两个半径的总和,如果你想要它们不同的话) )。现在,考虑RHS总和的第一项,
D
Δx(t)^2=(x[i](t)-x[j](t))^2=
Δx(t)^2=(x[i](t0)-x[j](t0)+(u[i]-u[j])t)^2=
出现的新术语定义了Δx(t)^2=(x[i](t0)-x[j](t0))^2+2(x[i](t0)-x[j](t0))(u[i]-u[j])t + (u[i]-u[j])^2t^2
坐标的两个粒子的运动定律,
x
x[i](t)=x[i](t0)+u[i]t
和x[j](t)=x[j](t0)+u[j]t
是初始配置的时间。然后让t0
成为(u[i],v[i],w[i])
- 粒子的速度的三个分量。对其他三个坐标和总结做同样的事情,我们得到i
中的二阶多项式方程,
t
,
,其中
at^2+2bt+c=0
a=(u[i]-u[j])^2+(v[i]-v[j])^2+(w[i]-w[j])^2
b=(x[i](t0)-x[j](t0))(u[i]-u[j]) + (y[i](t0)-y[j](t0))(v[i]-v[j]) + (z[i](t0)-z[j](t0))(w[i]-w[j])
现在,有许多标准来评估真实解决方案的存在等等......如果您想要优化它,可以在以后评估。在任何情况下,您都会获得c=(x[i](t0)-x[j](t0))^2 + (y[i](t0)-y[j](t0))^2 + (z[i](t0)-z[j](t0))^2-D^2
,如果它是复数或负数,则将其设置为加号无穷大。要加快速度,请记住tc[i,j]
是对称的,并且您还希望将tc[i,j]
设置为无穷大以方便使用。
然后,您获取数组tc[i,i]
和矩阵tmin
的最小tw
,并及时整合tc
。
您现在将tmin
减去tmin
和tw
的所有元素。
如果与tc
粒子的墙壁发生弹性碰撞,您只需翻转该粒子的速度,并仅为其他每个粒子重新计算i
和tw[i]
{ {1}}。
如果两个粒子发生碰撞,您会为每个其他tc[i,k]
重新计算k
和tw[i],tw[j]
。在3D中评估弹性碰撞并非易事,也许你可以使用这个
http://www.atmos.illinois.edu/courses/atmos100/userdocs/3Dcollisions.html
关于流程如何扩展,您的初始开销为tc[i,k],tc[j,k]
。然后,两个时间步长之间的积分为k
,撞到墙或碰撞需要O(n^2)
重新计算。但真正重要的是碰撞之间的平均时间如何与O(n)
成比例。在统计物理学中应该有一个答案: - )
如果您想根据时间绘制属性,请不要忘记添加更多中间时间步。
答案 5 :(得分:0)
您可以定义粒子之间的排斥力,与1 /(距离平方)成比例。在每次迭代时,计算粒子对之间的所有力,加上作用在每个粒子上的所有力,计算粒子加速度,然后计算粒子速度,最后计算粒子新位置。冲突将以这种方式自然处理。但是处理粒子与墙之间的相互作用是另一个问题,必须以其他方式处理。