我希望将电动力学引入我以前的理想气体模拟中,这只是一个矩形内的一堆粒子。
编辑它似乎我使用了错误的等式,这就是我的初始问题的原因。
我正在使用python 3.5 IDLE环境,numpy
用于矢量数学,pygame
用于可视化。我正在使用的方程是:
electric field和magnetic field
到目前为止,我的代码正在运行。但要真实地重新创建物理,这些字段需要以速度c
进行扩展。此外,在计算力时,我必须使用延迟时间的数据,但每次重新编写时都会重写。我可以存储该数据还是内存重?
你建议采用什么方法?
代码使用Ball类,其实例是粒子。它有一个与之关联的方法,可以计算E(r)
和B(r)
。为了计算作用于粒子i
的力,我将所有这些力加在i
的位置,并计算作用在i
上的力。
这是代码,其中'质量'用作粒子的半径以及电荷,z维度仅用于交叉积。相关代码是受if electrodynamics == true
语句影响的代码:
import pygame
import time
import random as r
import numpy as np
pygame.init()
display_width = 800
display_height = 600
electrodynamics = True
show_field = False
Ball_num = 7
c = 10
red = (255,0,0)
white = (255,255,255)
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('bouncy')
clock = pygame.time.Clock()
def normalized(a):
b = np.linalg.norm(a)
if not(b == 0):
return a/b
else:
return np.zeros((1,3), dtype = float)
def tot_EM_field_at_charge(charges, charge):
EM = np.array([[0.,0.,0.],[0.,0.,0.]], dtype = float)
for q in charges:
EM = EM + q.EM_field(charge.position)
return EM
def Force_on_bally(field, charge):
c = np.cross(charge.velocity, field[1])
force = charge.mass*(field[0] + np.cross(charge.velocity, field[1]))
return force
class arrow(object):
def __init__(self, length, x, y, charges):
self.length = length
self.position = np.array([x,y,0])
self.field = tot_EM_field_at_charge(charges, self)
self.field_mag= np.linalg.norm(self.field[0])
if self.field_mag == 0:
self.field_direction = np.zeros(3,)
else:
self.field_direction = self.field[0]/self.field_mag
self.position_end()
self.color()
self.show()
def position_end(self):
self.position_2 = self.position + self.field_direction * self.length
return self.position_2
def color(self):
self.color = self.field_mag
if self.color < 0.05:
self.color = (46,120,255)
elif self.color < 0.1:
self.color = (147,145,252)
elif self.color < 0.3:
self.color = (249,23,28)
elif self.color < 0.6:
self.color =(251,139,33)
elif self.color < 1:
self.color = (255,255,127)
else:
self.color = (255,255,255)
return self.color
def show(self):
pygame.draw.line(gameDisplay, self.color,(int(self.position[0]), int(self.position[1])), (int(self.position_2[0]), int(self.position_2[1])))
class Ball(object):
def __init__(self, x, y, m, c, v_x, v_y):
self.position = np.array([x,y,0], dtype = float)
self.position_2 = np.array([x,y,0], dtype = float)
self.velocity = np.array([v_x, v_y,0], dtype = float)
self.velocity_2 = np.array([v_x, v_y,0], dtype = float)
self.acceleration = np.array([0.,0.,0.], dtype = float)
self.mass = m
self.color = c
def acceleration_compute(self,force):
a = force/self.mass
self.acceleration += a
def move(self):
self.velocity += self.acceleration
self.position += self.velocity
self.acceleration *= 0
def show(self):
pygame.draw.circle(gameDisplay, self.color, [int(self.position[0]), int(self.position[1])], self.mass)
def Edgelord(self):
if ((self.position[0] + self.velocity[0] >= display_width-self.mass) and self.velocity[0] > 0):
self.velocity[0] *= -1
self.position[0] = display_width - self.mass + self.velocity[0]
elif ((self.position[0] + self.velocity[0] - self.mass <= 0) and self.velocity[0] < 0 ):
self.velocity[0] *= -1
self.position[0] = self.mass + self.velocity[0]
elif ((self.position[1] + self.velocity[1] >= display_height - self.mass) and self.velocity[1] > 0):
self.velocity[1] *= -1
self.position[1] = display_height - self.mass + self.velocity[1]
elif ((self.position[1] + self.velocity[1] - self.mass <= 0) and self.velocity[1] < 0 ):
self.position[1] = self.mass -self.velocity[1]
self.velocity[1] *= -1
def EM_field(self, R):
radius = np.linalg.norm(R - self.position)
if radius != 0:
unitradius = (R - self.position)/radius
else:
unitradius = np.zeros(3, )
if np.linalg.norm(radius) != 0 and np.dot(unitradius, self.velocity)!=1:
charge = self.mass / (1 - np.dot(unitradius, self.velocity) ** 3)
if radius < self.mass:
radius = self.mass
radius2 = radius ** 2
velocity_in_c = self.velocity/c
oneMinusV2 = 1 - np.dot(velocity_in_c, velocity_in_c)
uMinusV = unitradius - velocity_in_c
aCrossUmV = np.cross(uMinusV, self.acceleration);
Eleft = (oneMinusV2 * (unitradius - velocity_in_c)) / radius2
Eright = np.cross(unitradius, aCrossUmV) / (radius*c**2)
E = charge * (Eleft - Eright)
#E = np.zeros(3, )
B = np.cross(unitradius/c, ((charge*c**2) * (Eleft - Eright)))
EM_field = np.array([E,B], dtype = float)
else:
EM_field = np.zeros((2,3), dtype = float)
return EM_field
ballys = []
for i in range(Ball_num):
#ballys.insert(i, Ball(r.randrange(300,display_width - 5, 10),r.randrange(200,display_height/2,1) , r.randrange(5,10,1),(r.randint(1,255),r.randint(1,255),r.randint(1,255)), r.randint(-200,200)/1000, r.randint(-200,200)/1000))
ballys.insert(i, Ball(200 + i*50, 220 + i*20 , 10,(r.randint(1,255),r.randint(1,255),r.randint(1,255)),0, 0 ))
#ballys.append( Ball(300 + 50, 300, 10,(r.randint(1,255),r.randint(1,255),r.randint(1,255)),10, 0 ))
up = np.zeros(3,)
down = np.zeros(3,)
right = np.array([0.,0.,0.])
left = np.array([0.,0.,0.])
grav = np.array([0.,0.1,0.])
repulsion = np.array([0.,0.,0.])
crashed = False
while not crashed :
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
left = np.array([-0.1, 0.,0.])
if event.key == pygame.K_RIGHT:
right = np.array([0.1,0.,0.])
if event.key == pygame.K_DOWN:
down = np.array([0.,0.1,0.])
if event.key == pygame.K_UP:
up = np.array([0.,-0.1,0.])
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN :
right = np.array([0.,0.,0.])
left = np.array([0.,0.,0.])
down = np.zeros(3,)
up = np.zeros(3,)
gameDisplay.fill(white)
if show_field == True:
for i in range(display_width//20):
for j in range(display_height//20):
arry = arrow(8, 10 + i*20, 10 + j*20 , ballys)
if electrodynamics == True:
for bally in ballys:
bally.acceleration_compute(Force_on_bally(tot_EM_field_at_charge(ballys, bally), bally))
for i, bally in enumerate(ballys):
#if electrodynamics == True:
# bally.acceleration_compute(Force_on_bally(tot_EM_field_at_charge(ballys, bally), bally))
bally.Edgelord()
bally.acceleration_compute(up)
bally.acceleration_compute(down)
bally.acceleration_compute(right)
bally.acceleration_compute(left)
#ballys[i].acceleration_compute(grav * ballys[i].mass)
for bally2 in ballys[i+1:]:
#checks collisions
if np.linalg.norm(bally.position - bally2.position) <= bally.mass + bally2.mass :
bally.velocity_2 = (bally.mass * bally.velocity + bally2.mass * bally2.velocity + bally2.mass *(bally2.velocity - bally.velocity))/ (bally.mass + bally2.mass)
bally2.velocity_2 = (bally.mass * bally2.velocity + bally.mass * bally.velocity + bally.mass *(bally.velocity - bally2.velocity))/ (bally2.mass + bally.mass)
#prevents balls getting stuck in each other and assignes new velocitys
if not(np.linalg.norm(bally.position + bally.velocity_2 - (bally2.position + bally2.velocity_2) ) <= bally.mass + bally2.mass):
bally.velocity = bally.velocity_2
bally2.velocity = bally2.velocity_2
bally.Edgelord()
bally.move()
bally.show()
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
答案 0 :(得分:1)
查看您的代码,我认为问题来自1/r
和1/r²
计算。实际上,当你除以一个非常小的值时,你最终会得到非常大的值,这会使你的模拟爆炸。
当粒子的速度足够接近彼此时,会发生小r
值。然后你解决它们之间的碰撞,但损坏已经完成。你计算两种力,将它们整合为新的速度,然后根据需要解决碰撞和反速度,但速度已经很大。
为了验证我的假设,我只是试图阻止计算中使用的radius
太大,例如:
if radius < ball.mass:
radius = ball.mass
我用11颗颗粒进行模拟(没有这种情况,它在1或2分钟内爆炸)。模拟已经运行了15分钟左右,并且尚未爆炸(并且不会爆炸)。
顺便说一句,为了确保问题不是来自您在编辑中观察到的内容,我注释掉了ballys[i].acceleration_compute(grav * ballys[i].mass)
行。看来模拟在没有它的情况下仍会爆炸(并且在确保半径不太小时也是稳定的)
编辑:Here is the code I ran and from which I observed a correct behaviour