Python 使用numpy在Pygame中进行更高效的风洞模拟

Python 使用numpy在Pygame中进行更高效的风洞模拟,python,numpy,pygame,fluid-dynamics,Python,Numpy,Pygame,Fluid Dynamics,我是一名航空航天专业的学生,正在为我们的python编程课程做一个学校项目。任务是仅使用Pygame和numpy创建程序。我决定创建一个风洞模拟,模拟二维机翼上的气流。我想知道是否有一种更有效的方法可以从编程的角度进行计算。我将解释该计划: 我在此附上一张图片: 采用涡面法模拟(稳态)流场。基本上,我使用的是Nx乘以Ny点的网格,在每个点上都有一个速度矢量(u,v)。然后使用Pygame,我将这些网格点映射为圆形,以便它们类似于影响区域。网格点是下图中的灰色圆圈: 我创建N个粒子,并通过如下

我是一名航空航天专业的学生,正在为我们的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)