Python 使用numpy在Pygame中进行更高效的风洞模拟
我是一名航空航天专业的学生,正在为我们的python编程课程做一个学校项目。任务是仅使用Pygame和numpy创建程序。我决定创建一个风洞模拟,模拟二维机翼上的气流。我想知道是否有一种更有效的方法可以从编程的角度进行计算。我将解释该计划: 我在此附上一张图片: 采用涡面法模拟(稳态)流场。基本上,我使用的是Nx乘以Ny点的网格,在每个点上都有一个速度矢量(u,v)。然后使用Pygame,我将这些网格点映射为圆形,以便它们类似于影响区域。网格点是下图中的灰色圆圈: 我创建N个粒子,并通过如下迭代确定其速度: 创建粒子列表。Python 使用numpy在Pygame中进行更高效的风洞模拟,python,numpy,pygame,fluid-dynamics,Python,Numpy,Pygame,Fluid Dynamics,我是一名航空航天专业的学生,正在为我们的python编程课程做一个学校项目。任务是仅使用Pygame和numpy创建程序。我决定创建一个风洞模拟,模拟二维机翼上的气流。我想知道是否有一种更有效的方法可以从编程的角度进行计算。我将解释该计划: 我在此附上一张图片: 采用涡面法模拟(稳态)流场。基本上,我使用的是Nx乘以Ny点的网格,在每个点上都有一个速度矢量(u,v)。然后使用Pygame,我将这些网格点映射为圆形,以便它们类似于影响区域。网格点是下图中的灰色圆圈: 我创建N个粒子,并通过如下
创建网格列表 对于网格列表中的每个网格点:
对于粒子列表中的每个粒子:
如果粒子A在网格点n(xn,yn)的影响区域内:
粒子A的速度=网格点n处的速度 在Pygame中可视化所有内容 这种基本方法是我在Pygame中想象流程的唯一方法。模拟效果很好,但是如果我增加网格点的数量(增加流场的精度),性能会下降。我的问题是,仅仅使用pygame和numpy是否有更有效的方法来实现这一点 我在此附上代码:
import pygame,random,sys,numpy
from Flow import Compute
from pygame.locals import *
import random, math, sys
#from PIL import Image
pygame.init()
Surface = pygame.display.set_mode((1000,600))
#read the airfoil geometry from a dat file
with open ('./resources/naca0012.dat') as file_name:
x, y = numpy.loadtxt(file_name, dtype=float, delimiter='\t', unpack=True)
#parameters used to describe the flow
Nx=30# 30 column grid
Ny=10#10 row grid
N=20#number of panels
alpha=0#angle of attack
u_inf=1#freestream velocity
#compute the flow field
u,v,X,Y= Compute(x,y,N,alpha,u_inf,Nx,Ny)
#The lists used for iteration
Circles = []
Particles= []
Velocities=[]
#Scaling factors used to properly map the potential flow datapoints into Pygame
magnitude=400
vmag=30
umag=30
panel_x= numpy.multiply(x,magnitude)+315
panel_y= numpy.multiply(-y,magnitude)+308
#build the grid suited for Pygame
grid_x= numpy.multiply(X,magnitude)+300
grid_y= numpy.multiply(Y,-1*magnitude)+300
grid_u =numpy.multiply(u,umag)
grid_v =numpy.multiply(v,-vmag)
panelcoordinates= zip(panel_x, panel_y)
# a grid area
class Circle:
def __init__(self,xpos,ypos,vx,vy):
self.radius=16
self.x = xpos
self.y = ypos
self.speedx = 0
self.speedy = 0
#create the grid list
for i in range(Ny):
for s in range(Nx):
Circles.append(Circle(int(grid_x[i][s]),int(grid_y[i][s]),grid_u[i][s],grid_v[i][s]))
Velocities.append((grid_u[i][s],grid_v[i][s]))
#a particle
class Particle:
def __init__(self,xpos,ypos,vx,vy):
self.image = pygame.Surface([10, 10])
self.image.fill((150,0,0))
self.rect = self.image.get_rect()
self.width=4
self.height=4
self.radius =2
self.x = xpos
self.y = ypos
self.speedx = 30
self.speedy = 0
#change particle velocity if collision with grid point
def CircleCollide(Circle,Particle):
Particle.speedx = int(Velocities[Circles.index((Circle))][0])
Particle.speedy = int(Velocities[Circles.index((Circle))][1])
#movement of particles
def Move():
for Particle in Particles:
Particle.x += Particle.speedx
Particle.y += Particle.speedy
#create particle streak
def Spawn(number_of_particles):
for i in range(number_of_particles):
i=i*(300/number_of_particles)
Particles.append(Particle(0, 160+i,1,0))
#create particles again if particles are out of wake
def Respawn(number_of_particles):
for Particle in Particles:
if Particle.x >1100:
Particles.remove(Particle)
if Particles==[]:
Spawn(number_of_particles)
#Collsion detection using pythagoras and distance formula
def CollisionDetect():
for Circle in Circles:
for Particle in Particles:
if Particle.y >430 or Particle.y<160:
Particles.remove(Particle)
if math.sqrt( ((Circle.x-Particle.x)**2) + ((Circle.y-Particle.y)**2) ) <= (Circle.radius+Particle.radius):
CircleCollide(Circle,Particle)
#draw everything
def Draw():
Surface.fill((255,255,255))
#Surface.blit(bg,(-300,-83))
for Circle in Circles:
pygame.draw.circle(Surface,(245,245,245),(Circle.x,Circle.y),Circle.radius)
for Particle in Particles:
pygame.draw.rect(Surface,(150,0,0),(Particle.x,Particle.y,Particle.width,Particle.height),0)
#pygame.draw.rect(Surface,(245,245,245),(Circle.x,Circle.y,1,16),0)
for i in range(len(panelcoordinates)-1):
pygame.draw.line(Surface,(0,0,0),panelcoordinates[i],panelcoordinates[i+1],3)
pygame.display.flip()
def GetInput():
keystate = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == QUIT or keystate[K_ESCAPE]:
pygame.quit();sys.exit()
def main():
#bg = pygame.image.load("pressure.png")
#bg = pygame.transform.scale(bg,(1600,800))
#thesize= bg.get_rect()
#bg= bg.convert()
number_of_particles=10
Spawn(number_of_particles)
clock = pygame.time.Clock()
while True:
ticks = clock.tick(60)
GetInput()
CollisionDetect()
Move()
Respawn(number_of_particles)
Draw()
if __name__ == '__main__': main()
导入pygame、random、sys、numpy
从流导入计算
从pygame.locals导入*
导入随机、数学、系统
#从PIL导入图像
pygame.init()
Surface=pygame.display.set_模式((1000600))
#从dat文件中读取机翼几何图形
以open('./resources/naca0012.dat')作为文件名:
x、 y=numpy.loadtxt(文件名,dtype=float,分隔符='\t',unpack=True)
#用于描述流量的参数
Nx=30#30柱网轴线
Ny=10#10排网格
N=20#面板数量
α=0#迎角
u_inf=1#自由流流速
#计算流场
u、 v,X,Y=Compute(X,Y,N,alpha,u_-inf,Nx,Ny)
#用于迭代的列表
圆圈=[]
粒子=[]
速度=[]
#用于将潜在流数据点正确映射到Pygame的比例因子
震级=400
vmag=30
umag=30
面板x=numpy.乘(x,量级)+315
面板y=numpy.multiply(-y,幅值)+308
#构建适合Pygame的网格
网格x=numpy.乘(x,量级)+300
网格y=numpy.乘(y,-1*量级)+300
网格_=numpy.multiply(u,umag)
网格v=numpy.multiply(v,-vmag)
面板坐标=zip(面板x,面板y)
#网格区域
班级圈子:
定义初始化(自我、XPO、YPO、vx、vy):
自半径=16
self.x=xpos
self.y=ypos
self.speedx=0
self.speeded=0
#创建网格列表
对于范围内的i(纽约):
对于范围内的s(Nx):
圆.append(圆(int(grid_x[i][s])、int(grid_y[i][s])、grid_[i][s]、grid_v[i][s]))
附加((网格[i][s],网格[v[i][s]))
#粒子
类粒子:
定义初始化(自我、XPO、YPO、vx、vy):
self.image=pygame.Surface([10,10])
自映像填充((150,0,0))
self.rect=self.image.get_rect()
自宽=4
自身高度=4
自半径=2
self.x=xpos
self.y=ypos
self.speedx=30
self.speeded=0
#如果与栅格点碰撞,则更改粒子速度
def CircleCollide(圆形,颗粒):
Particle.speedx=int(速度[Circles.index((Circle))][0])
Particle.speedy=int(速度[Circles.index((Circle))][1])
#粒子运动
def Move():
对于粒子中的粒子:
Particle.x+=Particle.speedx
Particle.y+=Particle.s
#创建粒子条纹
def繁殖(粒子数):
对于范围内的i(粒子数):
i=i*(300/粒子数)
附加粒子(粒子(0,160+i,1,0))
#如果粒子脱离尾迹,请再次创建粒子
def Respawn(粒子数):
对于粒子中的粒子:
如果粒子.x>1100:
粒子。移除(粒子)
如果粒子=[]:
繁殖(粒子数)
#利用毕达哥拉斯公式和距离公式进行冲突检测
def CollisionDetect():
对于圆形中的圆形:
对于粒子中的粒子:
如果Particle.y>430或Particle.y我已经整理了您的代码并做了一些更改,即为您的类添加作用域并引入了更多。如果不进一步了解流程
,我无法完全测试这一点,但如果您能回复我,我可以做更多。这里我假设“流场”可以通过numpy.meshgrid
函数来模拟
import pygame,numpy,sys
import pygame.locals
import math
class Particle:
def __init__(self,xpos,ypos,vx,vy):
self.size = numpy.array([4,4])
self.radius =2
self.pos = numpy.array([xpos,ypos])
self.speed = numpy.array([30,0])
self.rectangle = numpy.hstack((self.pos,self.size))
def move(self):
self.pos += self.speed
self.rectangle = numpy.hstack((self.pos,self.size))
def distance(self,circle1):
return math.sqrt(numpy.sum((circle1.pos - self.pos)**2))
def collision(self,circle1):
result = False
if self.pos[1] >430 or self.pos[1]<160:
result = True
if self.distance(circle1) <= (circle1.radius+self.radius):
self.speed = circle1.speed
return result
class Particles:
def __init__(self,num_particles):
self.num = num_particles
self.particles =[]
self.spawn()
def spawn(self):
for i in range(self.num):
i=i*(300/self.num)
self.particles.append(Particle(0, 160+i,1,0))
def move(self):
for particle in self.particles:
particle.move()
if particle.pos[0] >1100:
self.particles.remove(particle)
if not self.particles: self.spawn()
def draw(self):
for particle in self.particles:
pygame.draw.rect(Surface,(150,0,0),particle.rectangle,0)
def collisiondetect(self,circle1):
for particle in self.particles:
if particle.collision(circle1):
self.particles.remove(particle)
def GetInput():
keystate = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.locals.QUIT or keystate[pygame.locals.K_ESCAPE]:
pygame.quit()
sys.exit()
#draw everything
def Draw(sw,cir):
Surface.fill((255,255,255))
cir.draw()
for i in range(panelcoordinates.shape[1]):
pygame.draw.line(Surface,(0,0,0),panelcoordinates[0,i-1],panelcoordinates[0,i],3)
sw.draw()
pygame.display.flip()
# a grid area
class Circle:
def __init__(self,xpos,ypos,vx,vy):
self.radius=16
self.pos = numpy.array([xpos,ypos])
self.speed = numpy.array([vx,vy])
class Circles:
def __init__(self,columns,rows):
self.list = []
grid_x,grid_y = numpy.meshgrid(numpy.linspace(0,1000,columns),numpy.linspace(200,400,rows))
grid_u,grid_v = numpy.meshgrid(numpy.linspace(20,20,columns),numpy.linspace(-1,1,rows))
for y in range(rows):
for x in range(columns):
c1= Circle(int(grid_x[y,x]),int(grid_y[y,x]),grid_u[y,x],grid_v[y,x])
self.list.append(c1)
def draw(self):
for circle in self.list:
pygame.draw.circle(Surface,(245,245,245),circle.pos,circle.radius)
def detectcollision(self,parts):
for circle in self.list:
parts.collisiondetect(circle)
if __name__ == '__main__':
#initialise variables
number_of_particles=10
Nx=30
Ny=10
#circles and particles
circles1 = Circles(Nx,Ny)
particles1 = Particles(number_of_particles)
#read the airfoil geometry
panel_x = numpy.array([400,425,450,500,600,500,450,425,400])
panel_y = numpy.array([300,325,330,320,300,280,270,275,300])
panelcoordinates= numpy.dstack((panel_x,panel_y))
#initialise PyGame
pygame.init()
clock = pygame.time.Clock()
Surface = pygame.display.set_mode((1000,600))
while True:
ticks = clock.tick(6)
GetInput()
circles1.detectcollision(particles1)
particles1.move()
Draw(particles1,circles1)
导入pygame、numpy、sys
输入pygame.locals
输入数学
类粒子:
定义初始化(自我、XPO、YPO、vx、vy):
self.size=numpy.array([4,4])
自半径=2
self.pos=numpy.array([xpos,ypos])
self.speed=numpy.array([30,0])
self.rectangle=numpy.hstack((self.pos,self.size))
def移动(自我):
self.pos+=自速度
self.rectangle=numpy.hstack((self.pos,self.size))
def距离(自身,圆圈1):
返回数学sqrt(numpy.sum((circle1.pos-self.pos)**2))
def碰撞(自身,圆圈1):
结果=错误
如果self.pos[1]>430或self.pos[1]代码中的一个瓶颈可能是冲突检测<代码>碰撞检测()
计算每个粒子和每个圆之间的距离。然后,如果检测到碰撞,CircleCollide()
会在圆圈
中查找圆圈的索引(线性搜索),以便可以从速度
中的相同索引中检索速度。显然,改进的时机已经成熟
首先,Circle
类在speedx/speedy属性中已经有了速度,因此speeds
可以是elim
# You may already have these values from creating grid_x etc.
# if not, you only need to calculated them once, because the
# circles don't move
circle_spacing_x = Circles[1].x - Circles[0].x
circle_spacing_y = Circles[Nx].y - Circles[0].y
circle_first_x = Circles[0].x - circle_spacing_x / 2
circle_first_y = Circles[0].y - circle_spacing_y / 2
def CollisionDetect():
for particle in Particles:
if particle.y >430 or particle.y<160:
Particles.remove(particle)
continue
c = (particle.x - circle_first_x) // circle_spacing_x
r = (particle.y - circle_first_y) // circle_spacing_y
circle = Circles[r*Nx + c]
if ((circle.x - particle.x)**2 + (circle.y - particle.y)**2
<= (circle.radius+particle.radius)**2):
particle.speedx = int(circle.speedx)
particle.speedy = int(circle.speedy)