编程语言
首页 > 编程语言> > 加速Python中的计算(模拟磁场中的粒子)

加速Python中的计算(模拟磁场中的粒子)

作者:互联网

A用Python编写的程序速度有问题.该程序是“模拟磁场中的铁磁颗粒”,更具体地说是磁惰性液体.该程序可以工作,但与用C编写的相同程序相比非常缓慢,但我用Python编写了一个项目来研究.

总的来说,程序代码基于循环,有很多浮点计算.存在随机数量的粒子(产生随机位置),其在磁场的影响下彼此相互作用.

这是最初的立场:

http://i.stack.imgur.com/T15Bb.jpg

决赛:

http://i.stack.imgur.com/0nU5D.jpg

主循环(在SymMain.py中,具有k变量)迭代是时间步长,计算当时粒子在其中的坐标和作用在其上的力(吸引力和小排斥力).为了加快速度,我想使用并行处理来同时计算迭代次数,但这是不可能的,因为一次迭代中的计算取决于前一次迭代中的计算.

我不知道Python比C慢.例如,计算一次性步骤中529个粒子的模拟(在我的计算机上):

C~0.5s

Python~50s

那么,如何加快程序?请帮忙.

This code is only calculate, not drawing particles as in the pictures. The number of simulate partcile is on SymConst.py, this is nrH*nrL.

  

SymMain.py

#coding:windows-1250

from os import system
from SymCalc import *
from SymParticle import *


if __name__ == '__main__':
    App = SymCalc()
    App.MainLoop()

SymParticle.py

#coding:windows-1250

from random import randint
from math import *
from SymConst import *
from SymParticle import *

class SymCalc(object):
    def __init__(self):
        # declaration lists containing the properties of the particles
        ParticleList = []
        ParticleListTemp = []
        t = 0.0

        # the initial values ​​of particle
        for x in range(0, nParticle):
            ParticleList.append(Particle(x+1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 8e-6))
            ParticleListTemp.append(Particle(x+1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 8e-6))

        # generating random coordinates X, Y 
        for x in range(0, nParticle):
            self.Rand(ParticleList[x], x)

        # time steps
        for k in range(0, k0):
            print "Time step = {0}".format(k)

            # calculation of forces
            for i in range(0, nParticle):
                for j in range(0, i-1):
                    self.ParticleCalculate(ParticleList[i], ParticleList[j], 1)
                for j in range(i+1, nParticle):
                    self.ParticleCalculate(ParticleList[i], ParticleList[j], 1)

            # display data
            if(k%Constant == 0):
                for i in range(0, nParticle):
                    self.Print(k, i, ParticleList[i], t)

            # changing the position of the particle
            for i in range(0, nParticle):
                self.ChangeOfPosition (ParticleList[i], dt)

                # reset forces
                for j in range(0, nParticle):
                    self.ParticleCalculate(ParticleList[i], ParticleList[j], 0)
                    self.ParticleCalculate(ParticleList[i], ParticleListTemp[j], 0)

            # next time step
            t += dt


    # random coordinates of the particles
    def Rand(self, part, lp):
        l = lp%nrL    # współrzędna X pola      
        part.x = (l-nrL/2)*(L0/nrL) + ((randint(0,32767)%100)-50)*(L0/nrL-2*part.r)/99    # X

        h = (lp+1-(lp)%nrL)/nrL    # Y      
        part.y = (h-nrH/2)*(H0/nrH) + ((randint(0,32767)%100)-50)*(H0/nrH-2*part.r)/99    # współrzędna Y cząstki


    # calculating function of the force acting on the particles
    def ParticleCalculate(self, part, part_tmp, p):
        # auxiliary variables
        # r_4, dx, dy, rr, sum, sx, sy, chi_eff, tmp, frepx, frepy, f0
        md = []

        if(p == 0):
            part.frx = 0
            part.fry = 0
            part.fx = 0
            part.fy = 0

        if(p == 1):
            # versor coordinates connecting the geometrical center of the particle
            dx = part.x - part_tmp.x
            dy = part.y - part_tmp.y

            # the distance between the geometric means of the particle
            rr = sqrt(dx*dx + dy*dy)

            if(rr < 0.85*(part.r + part_tmp.r)):
               print "ERROR: Invalid distance between the particles! Simulation aborted..."

            if(rr >= 10*part.r):
                # magnetic dipoles
                chi_eff = (3.*(MI_P - 1.))/((MI_P - 1) + 3.)

                md.append((4.*MI_0*pi*part.r*part.r*part.r*chi_eff*M_H0)/3.0)
                md.append((4.*MI_0*pi*part_tmp.r*part_tmp.r*part_tmp.r*chi_eff*M_H0)/3.0)

                tmp = pow(rr,7)

                # first member
                sum = (5.*(md[0]*part.nx*dx + md[0]*part.ny*dy) * (md[1]*part_tmp.nx*dx + md[1]*part_tmp.ny*dy)) / tmp
                sx = sum*dx
                sy = sum*dy
                tmp = tmp / (rr*rr)

                # second member
                sum = (md[0]*part.nx*md[1]*part_tmp.nx + md[0]*part.ny*md[1]*part_tmp.ny) / tmp
                sx -= sum*dx
                sy -= sum*dy

                # third member
                sx -= (md[0]*(md[1]*part_tmp.nx*dx + md[1]*part_tmp.ny*dy)*part.nx + md[1]*(md[0]*part.nx*dx + md[0]*part.ny*dy)*part_tmp.nx)/tmp
                sy -= (md[0]*(md[1]*part_tmp.nx*dx + md[1]*part_tmp.ny*dy)*part.ny + md[1]*(md[0]*part.nx*dx + md[0]*part.ny*dy)*part_tmp.ny)/tmp

                # finally
                tmp = (-3./(4*pi*MI_0))
                sx *= tmp
                sy *= tmp
                part.fx += sx
                part.fy += sy

                # short-range repulsive force
                tmp = pow(rr,15)
                r_4 = pow((part.r+part.r),4)

                f0 = 3.*fabs(md[0])*fabs(md[1])/(4.*pi*MI_0*r_4)
                frepx = pow((part.r+part.r),15)*dx/(tmp*rr)*f0
                frepy = pow((part.r+part.r),15)*dy/(tmp*rr)*f0

                part.frx += frepx;
                part.fry += frepy;


    # change the position of the particle
    def ChangeOfPosition(self, part, dt):
        part.ddx = 0
        part.ddy = 0

        part.ddx = 1/(6*pi*part.r*eta)*(part.fx+part.frx)*dt
        part.ddy = 1/(6*pi*part.r*eta)*(part.fy+part.fry)*dt

        # particles new position value
        part.x += part.ddx
        part.y += part.ddy

        if(part.x < -L0/2):
            part.x += L0
        elif(part.x > L0/2):
            part.x -= L0
        elif(part.y < -H0/2):
            part.y += H0
        elif(part.y > H0/2):
            part.y -= H0



    # display data
    def Print(self, k, i, part, t):
        print "-"*50
        print "\nParticle {0}".format(i+1)
        print "The resultant magnetostatic force fx = {0}".format(part.fx - part.frx)
        print "The resultant magnetostatic force fy = {0}\n".format(part.fy - part.fry)
        if(i == nParticle-1):
            print "\n\t\t---t={0}[s]---".format(t)
        print "-"*50

SymParticle.py

#coding:windows-1250

class Particle(object):
    # generating a particle properties
    def __init__(self, num, x, y, fx, fy, frx, fry, nx, ny, ddx, ddy, r):
        self.num = num     
        self.x = x     
        self.y = y     
        self.fx = fx     
        self.fy = fy     
        self.frx = frx     
        self.fry = fry     
        self.nx = nx     
        self.ny = ny     
        self.ddx = ddx     
        self.ddy = ddy     
        self.r = r     

SymConst.py

#coding:windows-1250

### Constant
M_H0 = 3e4
MI_0 = 12.56e-7     
MI_P = 2000
eta = 0.1           
k0 = 10001
dt = 1e-6           # time step      
H0 = 5.95e-4
L0 = 5.95e-4
nrH = 4
nrL = 4
Constant = 100
nParticle = nrH*nrL    # number of particle

解决方法:

TL; DR – 使用PyPy!

要研究这个问题,首先使用Python cProfile模块来分析您的代码并找出所有时间的使用位置(将print语句注释掉):

$python -m cProfile -s time SymMain.py 
         30264977 function calls in 34.912 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  7370737   27.547    0.000   30.501    0.000 SymCalc.py:62(ParticleCalculate)
        1    3.626    3.626   34.909   34.909 SymCalc.py:8(__init__)
 11101110    1.715    0.000    1.715    0.000 {math.pow}
   160016    0.502    0.000    0.502    0.000 SymCalc.py:128(ChangeOfPosition)
  4440476    0.498    0.000    0.498    0.000 {method 'append' of 'list' objects}
  4440444    0.454    0.000    0.454    0.000 {math.fabs}
  2250226    0.287    0.000    0.287    0.000 {math.sqrt}
   500154    0.280    0.000    0.280    0.000 {range}
        1    0.001    0.001    0.002    0.002 random.py:40(<module>)
        1    0.001    0.001    0.001    0.001 hashlib.py:55(<module>)
        1    0.001    0.001    0.003    0.003 SymCalc.py:2(<module>)
     1616    0.000    0.000    0.000    0.000 SymCalc.py:151(Print)
        1    0.000    0.000   34.912   34.912 SymMain.py:3(<module>)
       32    0.000    0.000    0.000    0.000 SymParticle.py:4(__init__)
---8<--- snip ---8<---

在您的情况下,大部分时间都花在SymCalc.py:62(ParticleCalculate)函数中.看看那个函数,它似乎主要是内存访问和计算.这是查看PyPy的好例子!

PyPy is a fast, compliant alternative implementation of the Python
language (2.7.3 and 3.2.3). It has several advantages and distinct
features:

  • Speed: thanks to its Just-in-Time compiler, Python programs often run faster on PyPy. (What is a JIT compiler?)
  • Memory usage: large, memory-hungry Python programs might end up taking less space than they do in CPython.
  • Compatibility: PyPy is highly compatible with existing python code. It supports cffi and can run popular python libraries like
    twisted and django.
  • Sandboxing: PyPy provides the ability to run untrusted code in a fully secure way.
  • Stackless: PyPy comes by default with support for stackless mode, providing micro-threads for massive concurrency.
  • As well as other features.
$time pypy SymMain.py 
real    0m1.071s
user    0m1.025s
sys 0m0.042s

1.071s – 好多了!将其与我系统上的原始执行时间进行比较:

$time python SymMain.py 

real    0m29.766s
user    0m29.646s
sys 0m0.103s

标签:acceleration,python,physics,particles,simulation
来源: https://codeday.me/bug/20190831/1773299.html