2016-03-01 7 views
9

Ich bin ein Student der Luft- und Raumfahrt, der an einem Schulprojekt für unseren Python-Programmierkurs arbeitet. Die Zuweisung ist ein Programm nur mit Pygame und numpy erstellen. Ich beschloss, eine Windkanalsimulation zu erstellen, die den Luftstrom über einem zweidimensionalen Flügel simuliert. Ich habe mich gefragt, ob es eine effizientere Möglichkeit gibt, die Berechnung aus einer Programmierperspektive zu machen. Ich werde das Programm erklären:effizientere Windtunnelsimulation in Pygame, mit numpy

ich hier ein Bild beigefügt habe: enter image description here

Das (steady) Strömungsfeld ist die Wirbelplatte Methode modelliert. Grundsätzlich benutze ich ein Gitter von Nx mal Ny Punkten, wo an jedem Punkt ein Geschwindigkeits- (u, v) Vektor gegeben ist. Mit Pygame mappe ich dann diese Gitterpunkte als Kreise, so dass sie einem Einflussbereich ähneln. Die Gitterpunkte sind die grauen Kreise in dem folgenden Bild:

enter image description here

I N Partikel erzeugen und ihre Geschwindigkeiten bestimmen durch Iterieren wie folgt:

eine Liste von Partikeln erstellen.
erstellen Sie eine Rasterliste.

für jeden Gitterpunkt in Gittern:
  für jeden Partikel in der Liste der Teilchen:
    wenn liegt Teilchen A im Bereich des Einflusses des Gitterpunkt n (xn, yn):
      Partikel A seine Geschwindigkeit = Geschwindigkeit am Gitterpunkt n.

Visualisieren Sie alles in Pygame.

dieser grundlegende Weg war der einzige Weg, wie ich den Fluss in Pygame visualisieren konnte. Die Simulation funktioniert ziemlich gut, aber wenn ich die Anzahl der Gitterpunkte (erhöhen Sie die Genauigkeit des Strömungsfeldes) erhöhen, sinkt die Leistung. Meine Frage ist, ob es einen effizienteren Weg gibt, dies zu tun, nur mit Pygame und Numpy?

Ich habe den Code hier angebracht:

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() 

Der Code erfordert ein anderes Skript, das das Strömungsfeld selbst berechnet. Es liest auch Datenpunkte aus einer Textdatei, um die Geometrie des Flügels zu erhalten. Ich habe diese zwei Dateien nicht zur Verfügung gestellt, aber ich kann sie bei Bedarf hinzufügen. Vielen Dank im Voraus.

+0

So Ihre einzige Frage ist, wie es effizienter zu machen? –

+0

Willkommen in der CFD-Welt. Es gibt verschiedene Wege, die Dinge zu beschleunigen, aber allgemein gesprochen: Sie beinhalten eine Menge Neucodierung. Wenn es das ist, was Sie für Ihr Studentenprojekt wollen, dann gehen Sie darauf, es lohnt sich! Sehen Sie sich andere Frameworks an (zum Beispiel OpenFOAM). Insbesondere die Berechnung, die direkt mit der Visualisierung gekoppelt ist, wird nicht mit rechenintensiveren Simulationen funktionieren. Aber toller Job! –

+0

cricket_007 ja, aber nur mit Pygame und numpy seine Funktionalitäten. Jens Höpken, danke. Ich habe erst vor kurzem über OpenFOAM gelesen. Ich werde es mir auf jeden Fall ansehen. – Sami

Antwort

3

Ein Flaschenhals in Ihrem Code ist wahrscheinlich Kollisionserkennung. CollisionDetect() berechnet den Abstand zwischen jedem Partikel und jedem Kreis. Wenn dann eine Kollision erkannt wird, findet CircleCollide() den Index des Kreises in Circles (eine lineare Suche), so dass die Geschwindigkeiten aus dem gleichen Index in Velocities abgerufen werden können. Dies ist offensichtlich reif für Verbesserungen.

Erstens hat die Klasse Circle bereits die Geschwindigkeiten in den Attributen speedx/speedy, so dass Velocities eliminiert werden kann.

Zweitens, weil die Kreise an festen Positionen sind, können Sie aus der Position des Partikels berechnen, welcher Kreis zu einem gegebenen Partikel am nächsten ist.

# 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 

Dann CollisionDetect() wird:

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) 
+0

Ausgezeichnet, eine großartige Lösung !! Ich habe deine Lösung angewendet, es ist eine erstaunliche Verbesserung. Ich kann die Anzahl der Gitterpunkte leicht verdreifachen (um den Fluss genauer zu machen) und gleichzeitig die Anzahl der Partikel auf 100 erhöhen, ohne Leistungseinbußen zu bemerken. Der alte Code war im Vergleich schrecklich! Colin Dickie Ich habe deinen Code ausprobiert und keine signifikante Verbesserung festgestellt, aber danke für deine Anregungen. – Sami

1

Ich habe Ihren Code aufgeräumt und ein paar Änderungen vorgenommen, nämlich den Umfang zu Ihren Klassen hinzuzufügen und ein paar mehr einzuführen. Ohne weitere Kenntnisse von Flow kann ich das nicht vollständig testen, aber wenn du zurück zu mir kommst, kann ich noch mehr tun. Ich gehe hier davon aus, dass das 'Strömungsfeld' durch die Funktion numpy.meshgrid simuliert werden kann.

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) 

Ich habe ebenfalls einen etwas rohen Stich an einer Tragflügel zeichnen, ich als wieder nicht über die Kenntnisse der Datendatei mit den Koordinaten haben.