Haskell粒子模拟 - 计算粒子的速度

时间:2015-04-12 11:15:28

标签: algorithm haskell functional-programming simulation particle-system

我正在使用Haskell开发粒子模拟程序。对于其中一个函数,我试图根据所有周围粒子的质量和速度来确定模拟中所有粒子的新速度。

该功能具有以下形式:

accelerate :: Float -> [Particle] -> [Particle]

粒子是一种数据类型,包含质量,位置矢量和速度矢量,' Float' argument表示模拟中相应时间步长的增量时间

我想在计算每个粒子相对于列表中其他粒子的速度时,可以用来遍历列表的可能函数。

我能想到的一种可能方法:

  1. 假设还有另一个功能' velocityCalculator'其定义如下:

    velocityCalculator :: Particle -> Particle -> (Float,Float)
    
  2. 这需要两个粒子并返回第一个粒子的更新速度矢量。

    1. 申请foldl;使用上述函数作为二元运算符,粒子和粒子列表作为参数,即

      foldl velocityCalculator particle particleList
      
    2. 遍历粒子列表,将foldl应用于每个元素并构建包含具有更新速度的粒子的新列表

    3. 我不确定这是否是最有效的方法,因此非常感谢任何建议和改进。

      请注意 - >正如我所说,我只是在寻找建议而不是答案!

      谢谢!

2 个答案:

答案 0 :(得分:3)

好像你已经开始使用foldl了。例如

  1. 遍历粒子列表,将foldl应用于每个元素并构建包含具有更新速度的粒子的新列表
  2. 真的没有意义。根据一些二进制汇总函数,您将foldl应用于列表以将其减少为"汇总值"。将它应用于单个粒子并没有多大意义。

    我正在回答这个问题,假设您在编写程序时遇到问题 - 在担心效率之前,通常最好这样做。如果我错了,请告诉我。

    我不确定你想用什么规则来更新速度,但我认为它是某种成对力模拟,例如重力或电磁。如果是这样,这里有一些提示将指导您找到解决方案。

    type Vector = (Float, Float)
    
    -- Finds the force exerted on one particle by the other.
    -- Your code will be simplified if this returns (0,0) when the two
    -- particles are the same. 
    findForce :: Particle -> Particle -> Vector
    
    -- Find the sum of all forces exerted on the particle
    -- by every particle in the list. 
    totalForce :: [Particle] -> Particle -> Vector
    
    -- Takes a force and a particle to update, returns the same particle with
    -- updated velocity. 
    updateVelocity :: Vector -> Particle -> Particle
    
    -- Calculate mutual forces and update all particles' velocities
    updateParticles :: [Particle] -> [Particle]
    

    这些功能中的每一个都很短,一两行。如果您需要进一步提示要使用哪些高阶函数,请注意您尝试编写的函数的类型,并注意

    map :: (a -> b) -> [a] -> [b]           -- takes a list, returns a list
    filter :: (a -> Bool) -> [a] -> [a]     -- takes a list, returns a list
    foldl :: (a -> b -> a) -> a -> [b] -> a -- takes a list, returns something else
    foldr :: (a -> b -> b) -> b -> [a] -> b -- takes a list, returns something else
    

答案 1 :(得分:2)

如果你给每个Particle一个particle_id :: Int ID,你可以通过记忆加速2倍,然后定义:

forceOf a b | particle_id a > particle_id b = -(forceOf b a)
            | otherwise = (pos a - pos b) *:. charge a * charge b / norm (pos a - pos b) ^ 3

其中(*:.) :: Vector -> Double -> Vector是向量标量乘法,因此以上是1/r^2力法则。请注意,我们在此处记住pos a - pos b,然后我们还会记住forceOf a b以用作forceOf b a

然后您想使用dvs = [dt * sum (map (forceOf a) particles) / mass a | a <- particles]获取速度变化列表,然后zipWith (+) (map velocity particles) dvs

一个问题是这种方法在数值不确定性方面做得不好:时间$ t + 1 $的所有内容都是基于$ t $时的真实情况。你可以通过求解矩阵方程开始解决这个问题;代替v + = v + dt * M v(其中v = v(t)且v + = v(t + 1) ),你可以写v + = v + dt * M v + ,这样你就有了v + =( 1 - dt * M -1 v。这通常可以在数值上更稳定。混合两种溶液50/50可能更好。 v + = v + dt * M (v + v + )/ 2.这里有很多选项。