2011-08-18 43 views
5

Tôi đã tạo ra một hình ảnh nhỏ của các hạt trong python. Tôi đang tính toán sự dịch chuyển của các hạt trong không gian 2D với trọng số bằng không. Khi mỗi hạt thu hút tất cả các hạt khác dựa trên khối lượng hạt và khoảng cách.Tối ưu hóa tính hấp dẫn đối với các hạt trong không gian trọng lực 2d

Tôi đã thực hiện một hình ảnh trong pygame và mọi thứ hoạt động như kế hoạch (với caluclation), tuy nhiên tôi cần phải tối ưu hóa tính toán một cách gián tiếp. Ngày nay, hệ thống có thể tính toán khoảng 100-150 hạt trong một tốc độ khung hình thấp. Tôi đặt tất cả các tính toán trong một chủ đề riêng biệt mà đã cho tôi một số chi tiết nhưng không gần như những gì tôi muốn.

Tôi đã xem xét scipy và gumpy nhưng kể từ khi tôi không có nhà khoa học hoặc mathguru tôi chỉ nhận được bối rối. Có vẻ như tôi đang đi đúng hướng nhưng tôi không biết đầu mối.

Tôi cần tính toán tất cả sức hấp dẫn trên tất cả các hạt tôi phải lặp trong vòng lặp. Và kể từ khi tôi cần phải tìm nếu có bất kỳ va chạm, tôi phải làm như vậy trên một lần nữa.

Nó phá vỡ trái tim tôi viết rằng loại mã ....

NumPy có khả năng tính toán mảng với mảng, tuy nhiên tôi đã không tìm thấy bất kỳ điều gì để tính toán tất cả các mục trong mảng với tất cả các mục từ cùng một mảng khác. Có cái nào không? Nếu vậy tôi có thể tạo ra và vài mảng và tính toán nhanh hơn nhiều và phải có một chức năng để có được chỉ số từ 2 mảng nơi họ phù hợp giá trị (Collitiondetect IOW)

Đây là ngày nay tính hấp dẫn/collsion:

class Particle: 
    def __init__(self): 
     self.x = random.randint(10,790) 
     self.y = random.randint(10,590) 
     self.speedx = 0.0 
     self.speedy = 0.0 
     self.mass = 4 

#Attraction  
for p in Particles: 
    for p2 in Particles: 
     if p != p2: 
      xdiff = P.x - P2.x 
      ydiff = P.y - P2.y 
      dist = math.sqrt((xdiff**2)+(ydiff**2)) 
      force = 0.125*(p.mass*p2.mass)/(dist**2) 
      acceleration = force/p.mass 
      xc = xdiff/dist 
      yc = ydiff/dist 
      P.speedx -= acceleration * xc 
      P.speedy -= acceleration * yc 
for p in Particles: 
    p.x += p.speedx 
    p.y += p.speedy 

#Collision 
for P in Particles: 
    for P2 in Particles: 
     if p != P2: 
      Distance = math.sqrt( ((p.x-P2.x)**2) + ((p.y-P2.y)**2) ) 
      if Distance < (p.radius+P2.radius): 
       p.speedx = ((p.mass*p.speedx)+(P2.mass*P2.speedx))/(p.mass+P2.mass) 
       p.speedy = ((p.mass*p.speedy)+(P2.mass*P2.speedy))/(p.mass+P2.mass) 
       p.x = ((p.mass*p.x)+(P2.mass*P2.x))/(p.mass+P2.mass) 
       p.y = ((p.mass*p.y)+(P2.mass*P2.y))/(p.mass+P2.mass) 
       p.mass += P2.mass 
       p.radius = math.sqrt(p.mass) 
       Particles.remove(P2) 
+0

Bạn đã xem [Psyco] (http://psyco.sourceforge.net/) hoặc [Viết mô-đun C/C++] (http://docs.python.org/extending/extending.html)? – nagisa

+3

Bài viết này xem xét các phương pháp phổ biến để tối ưu hóa mô phỏng hấp dẫn, bao gồm cả Barnes-Hut. Các chuyên gia thường làm điều đó trong 3D, nhưng tôi tin rằng các trường hợp 2D là tất cả tương tự. http://www.cs.hut.fi/~ctl/NBody.pdf –

+0

nếu bạn không hài lòng với toán học ("Tôi không có nhà khoa học hoặc mathguru tôi chỉ bị lẫn lộn") thì tôi nghĩ bạn cần tìm một thư viện thực hiện điều này. xem http://stackoverflow.com/questions/6381137/python-physics-library http://stackoverflow.com/questions/2298517/are-any-of-these-quad-tree-libraries-any-good –

Trả lời

1

(Điều này có thể sẽ được đưa ra trong nhận xét nhưng tôi không có danh tiếng cần thiết để làm điều đó)

Tôi không thấy cách bạn thực hiện bước thời gian. Bạn có

P.speedx -= acceleration * xc 
P.speedy -= acceleration * yc 

nhưng để có được tốc độ mới tại thời điểm t + delta_t bạn sẽ làm gì

P.speedx -= acceleration * xc * delta_t 
P.speedy -= acceleration * yc * delta_t 

và sau đó cập nhật vị trí như vậy:

P.x = P.x + P.speedx * delta_t 
P.y = P.y + P.speedy * delta_t 

Sau đó, để lo ngại tốc độ của bạn . Có lẽ nó sẽ là tốt hơn để lưu trữ các thông tin hạt không phải trong một lớp học nhưng trong mảng numpy? Nhưng tôi không nghĩ bạn có thể tránh được vòng lặp.

Ngoài ra, bạn đã xem wikipedia, ở đó nó mô tả một số phương pháp để tăng tốc độ tính toán.

(sửa do bình luận của Mike)

+0

Biến 'xc' và' yc' là các thành phần của vectơ đơn vị trỏ từ hạt i đến j. –

+0

@Mike: Tôi hiểu rồi. Nhưng sau đó tăng tốc_x - = tăng tốc * xc. Tôi đoán những gì Ztripez đang làm là ngầm giả sử một bước thời gian là 1. – Mauro

+0

Thật vậy. Cách "chính xác" sẽ bao gồm một thông số dt một cách rõ ràng, như bạn đã đề xuất. Vì anh ta không có gì, nó cứ như thể anh ta bước tới một đơn vị thời gian. Tôi rất khuyên bạn nên bao gồm một tham số bước như 1 là một đơn vị lớn obscenely để bước về phía trước trong thời gian, đặc biệt là kể từ khi ông đang sử dụng một phương pháp Forward Euler. –

4

Tôi đã làm việc về vấn đề này trước đây, và một trong những điều tôi đã nhìn thấy trong quá khứ để tăng tốc tính toán va chạm là để thực sự lưu trữ một danh sách các hạt lân cận.

Về cơ bản, ý tưởng là bên trong tính toán trọng lực của bạn, bạn làm như sau:

for (int i = 0; i < n; i++) 
{ 
    for (int j = i + 1; j < n; j++) 
    { 
     DoGravity(Particle[i], Particle[j]); 
     if (IsClose(Particle[i], Particle[j])) 
     { 
      Particle[i].AddNeighbor(Particle[j]); 
      Particle[j].AddNeighbor(Particle[i]); 
     } 
    } 
} 

Sau đó, bạn chỉ đơn giản là vượt qua tất cả các hạt và bạn làm phát hiện va chạm trên mỗi trên lần lượt.Điều này thường là một cái gì đó giống như O(n) trong trường hợp tốt nhất, nhưng nó có thể dễ dàng làm suy giảm đến O(n^2) trong trường hợp xấu nhất.

Một cách khác là thử đặt các hạt của bạn bên trong một số Octree. Xây dựng một lên là một cái gì đó giống như O(n), sau đó bạn có thể truy vấn nó để xem nếu có bất cứ điều gì là gần nhau. Tại thời điểm đó bạn chỉ cần phát hiện va chạm trên các cặp. Làm điều này là O(n log n) Tôi tin.

Không chỉ vậy, nhưng bạn có thể sử dụng Octree để tăng tốc tính toán trọng lực. Thay vì hành vi O(n^2), nó cũng giảm xuống còn O(n log n). Hầu hết các triển khai Octree bao gồm một "tham số mở" kiểm soát tốc độ và độ chính xác của giao dịch bạn sẽ thực hiện. Vì vậy, Octrees có xu hướng kém chính xác hơn so với tính toán trực tiếp theo từng cặp và phức tạp để mã hóa, nhưng chúng cũng tạo ra các mô phỏng quy mô lớn có thể.

Nếu bạn sử dụng Octree theo cách này, bạn sẽ thực hiện những gì được gọi là Barnes-Hut Simulation.

Lưu ý: Vì bạn đang làm việc ở dạng 2D, tương tự 2D thành Octree được gọi là Quadtree. Xem bài viết Wikipedia sau đây để biết thêm thông tin: http://en.wikipedia.org/wiki/Quadtree

+0

Mô hình Barnes-Hut trông rất thú vị – ztripez

4

để tính nhanh, bạn cần lưu trữ x, y, speedx, nhanh chóng, m trong mảng có nhiều mảng. Ví dụ:

import numpy as np 

p = np.array([ 
    (0,0), 
    (1,0), 
    (0,1), 
    (1,1), 
    (2,2), 
], dtype = np.float) 

p là mảng 5x2 lưu trữ vị trí x, y của hạt. Để tính toán khoảng cách giữa mỗi cặp, bạn có thể sử dụng:

print np.sqrt(np.sum((p[:, np.newaxis] - p[np.newaxis, :])**2, axis=-1)) 

đầu ra là:

[[ 0.   1.   1.   1.41421356 2.82842712] 
[ 1.   0.   1.41421356 1.   2.23606798] 
[ 1.   1.41421356 0.   1.   2.23606798] 
[ 1.41421356 1.   1.   0.   1.41421356] 
[ 2.82842712 2.23606798 2.23606798 1.41421356 0.  ]] 

hoặc bạn có thể sử dụng cdist từ scipy:

from scipy.spatial.distance import cdist 
print cdist(p, p) 
+0

Ah cái nhìn này giống như một cái gì đó tôi yêu cầu. Tuy nhiên, tôi không có đầu mối làm thế nào để tiếp tục trên này – ztripez

5

Trước tiên, bạn có thể thử làm việc với số phức: các công thức hấp dẫn và động lực có liên quan rất đơn giản trong chủ nghĩa hình thức này và cũng có thể khá nhanh (vì NumPy có thể thực hiện phép tính bên trong, thay vì bạn xử lý các tọa độ x và y riêng biệt). Ví dụ: lực giữa hai hạt tại z và z 'chỉ đơn giản là:

(z-z')/abs(z-z')**3 

NumPy có thể tính số lượng rất nhanh, cho tất cả các cặp z/z'. Ví dụ, ma trận của tất cả các giá trị z-z 'chỉ được lấy từ mảng 1D Z của tọa độ là Z-Z[:, numpy.newaxis] (các thuật ngữ đường chéo [z = z'] yêu cầu một số dịch vụ đặc biệt, khi tính toán 1/abs(z-z')**3: chúng phải được đặt thành 0).

Đối với thời gian tiến hóa, bạn chắc chắn có thể sử dụng phương thức vi phân nhanh SciPy của các phương trình vi phân: chúng chính xác hơn nhiều so với tích hợp từng bước Euler.

Trong mọi trường hợp, việc delving vào NumPy sẽ rất hữu ích, đặc biệt nếu bạn dự định thực hiện các phép tính khoa học, vì NumPy rất nhanh.

+0

Như tốt đẹp như phương trình đó trông, hiện nó tổng quát ở tất cả các trường hợp 3D? Bởi vì điều này dường như chỉ làm việc cho phiên bản 2D Ztripez đã được đăng ở trên. –

+0

Sự khái quát hóa với 3D đơn giản là dạng véc tơ của định luật nghịch đảo vuông Newton: với hai điểm được xác định bởi các vector 3D M và M ', lực của M trên M' là (M-M ')/| M-M' | ** 3… Điều gì là tốt đẹp với những con số phức tạp là chúng rất giống với các vector 2D. – EOL

2

Không chắc chắn nếu điều này sẽ giúp bạn ra nhiều, nhưng nó là một phần của một giải pháp tôi đã làm việc trên cho cùng một vấn đề. Tôi đã không nhận thấy một sự gia tăng lớn trong hiệu suất làm theo cách này, vẫn bắt đầu xay để ngăn chặn khoảng 200 hạt, nhưng có lẽ nó sẽ cung cấp cho bạn một số ý tưởng.

C++ mô-đun để tính x và các thành phần y của lực hấp dẫn trên một mặt phẳng 2d:

#include <Python.h> 
#include <math.h> 

double _acceleration(double &Vxa, double &Vya, double &Vxb, double &Vyb, double xa, double ya, double xb, double yb, double massa, double massb) 
{ 
    double xdiff = xa - xb; 
    double ydiff = ya - yb; 
    double distance = sqrt(xdiff*xdiff + ydiff*ydiff) * pow(10, 5); 

    if (distance <= 0) 
     distance = 1; 

    double force = (6.674 * pow(10, -11))*(massa*massb)/(distance*distance); 

    double acca = force/massa; 
    double accb = force/massb; 
    double xcomponent = xdiff/distance; 
    double ycomponent = ydiff/distance; 

    Vxa -= acca * xcomponent; 
    Vya -= acca * ycomponent; 
    Vxb += accb * xcomponent; 
    Vyb += accb * ycomponent; 

    return distance; 
} 

static PyObject* gforces(PyObject* self, PyObject* args) 
{ 
    double Vxa, Vya, Vxb, Vyb, xa, ya, xb, yb, massa, massb, distance; 

    if (!PyArg_ParseTuple(args, "dddddddddd", &Vxa, &Vya, &Vxb, &Vyb, &xa, &ya, &xb, &yb, &massa, &massb)) 
     return NULL; 

    distance = _acceleration(Vxa, Vya, Vxb, Vyb, xa, ya, xb, yb, massa, massb); 

    return Py_BuildValue("ddddd", Vxa, Vya, Vxb, Vyb, distance); 
} 

static PyMethodDef GForcesMethods[] = { 
{"gforces", gforces, METH_VARARGS, "Calculate the x and y acceleration of two masses and the distance between the two."}, 
{NULL, NULL, 0, NULL} 
}; 

PyMODINIT_FUNC 
initgforces(void) 
{ 
(void) Py_InitModule("gforces", GForcesMethods); 
} 

Nếu bạn biên dịch này như một tập tin pyd bạn sẽ nhận được một đối tượng python mà bạn có thể nhập khẩu. Bạn có để có được tất cả các tùy chọn trình biên dịch và liên kết của bạn chính xác mặc dù. Tôi đang sử dụng dev-C++ và có các tùy chọn trình biên dịch của tôi được đặt thành -shared -o gforces.pyd và trình liên kết được đặt thành -lpython27 (đảm bảo bạn sử dụng cùng phiên bản bạn đã cài đặt) và thêm đường dẫn thư mục python vào thư mục và thư viện tab.

Đối tượng lấy đối số (p1.speedx, p1.speedy, p2.speedx, p2.speedy, p1.x, p1.y, p2.x, p2.y, p1.mass, p2.mass) và trả về p1.speedx mới, p1.speedy, p2.speedx, p2.speedy và khoảng cách giữa p1 p2.

Sử dụng các mô-đun trên, tôi cũng đã cố gắng để cắt ra một vài bước để phát hiện va chạm bằng cách so sánh khoảng cách trở lại với tổng bán kính của các hạt như vậy:

def updateForces(self):   #part of a handler class for the particles 
    prevDone = [] 
    for i in self.planetGroup: #planetGroup is a sprite group from pygame 
     prevDone.append(i.ID) 
     for j in self.planetGroup: 
      if not (j.ID in prevDone):    #my particles have unique IDs 
       distance = i.calcGForce(j)   #calcGForce just calls the above 
       if distance <= i.radius + j.radius: #object and assigns the returned 
        #collision handling goes here #values for the x and y speed 
                #components to the particles 

Hope this helps một chút. Bất kỳ lời khuyên nào khác hoặc chỉ ra các lỗi gộp về phía tôi đều được chào đón, tôi cũng mới làm quen với điều này.

Các vấn đề liên quan