2016-03-01 3 views
9

Je suis un étudiant en aérospatiale travaillant sur un projet d'école pour notre cours de programmation python. L'affectation est créer un programme en utilisant seulement Pygame et numpy. J'ai décidé de créer une simulation en soufflerie qui simule le flux d'air sur une aile bidimensionnelle. Je me demandais s'il y avait une façon plus efficace de faire le calcul d'un point de vue de la programmation. Je vais vous expliquer le programme:simulation de soufflerie plus efficace dans Pygame, en utilisant numpy

Je joins une image ici: enter image description here

Le champ (stable) d'écoulement est modélisé à l'aide de la méthode du panneau de vortex. Fondamentalement, j'utilise une grille de Nx fois Ny points où à chaque point un vecteur de vitesse (u, v) est donné. Ensuite, en utilisant Pygame, je cartographie ces points de la grille comme des cercles, de sorte qu'ils ressemblent à une zone d'influence. Les points de la grille sont les cercles gris dans l'image suivante:

enter image description here

I créer des particules de N et de déterminer leurs vitesses par itération comme suit:

créer une liste de particules.
créer une liste de grille.

pour chaque point de grille dans la liste de la grille:
  pour chaque particule dans la liste des particules:
    si particules A se situe dans la zone d'influence du point de grille n (xn, yn):
      particule A sa vitesse = vitesse au point de grille n.

Visualisez tout dans Pygame. Cette méthode de base était la seule façon dont je pouvais penser à visualiser le flux dans Pygame. La simulation fonctionne plutôt bien, mais si j'augmente le nombre de points de la grille (augmente la précision du champ d'écoulement), la performance diminue. Ma question est de savoir s'il existe un moyen plus efficace de le faire en utilisant simplement pygame et numpy?

Je joins le code ici:

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

Le code requiert un autre script qui calcule le champ d'écoulement lui-même. Il lit également les points de données d'un fichier texte pour obtenir la géométrie de l'aile. Je n'ai pas fourni ces deux fichiers, mais je peux les ajouter si nécessaire. Merci d'avance.

+0

Donc, votre seule question est de savoir comment le rendre plus efficace? –

+0

Bienvenue dans le monde des CFD. Il existe différentes façons d'accélérer les choses, mais de manière générale: elles impliquent beaucoup de recodage. Si c'est ce que vous voulez pour votre projet d'étudiant, allez-y, ça vaut le coup! Jetez un oeil à d'autres frameworks (OpenFOAM, par exemple). En particulier, le calcul couplé directement avec la visualisation ne fonctionnera pas avec des simulations plus intensives en calcul. Mais bon travail! –

+0

cricket_007 oui, mais seulement en utilisant Pygame et numpy ses fonctionnalités. Jens Höpken, merci. J'ai lu récemment sur OpenFOAM. Je vais certainement jeter un coup d'oeil. – Sami

Répondre

3

Un goulot d'étranglement dans votre code est probablement la détection de collision. CollisionDetect() calcule la distance entre chaque particule et chaque cercle. Ensuite, si une collision est détectée, CircleCollide() trouve l'index du cercle en Circles (une recherche linéaire), de sorte que les vitesses puissent être récupérées à partir du même index en Velocities. Clairement c'est mûr pour l'amélioration.D'abord, la classe Circle a déjà les vitesses dans les attributs speedx/speedy, donc Velocities peut être éliminée. Deuxièmement, parce que les cercles sont à des emplacements fixes, vous pouvez calculer quel cercle est le plus proche d'une particule donnée à partir de la position de la particule.

# 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 

Alors CollisionDetect() devient:

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

Excellent, une excellente solution !! J'ai appliqué votre solution, c'est une amélioration incroyable. Je peux facilement tripler la quantité de points de la grille (pour rendre le flux plus précis) tout en augmentant la quantité de particules à 100 sans observer de baisse de performance. L'ancien code a fait terrible en comparaison! Colin Dickie J'ai essayé votre code et n'ai pas observé d'amélioration significative, mais merci pour votre contribution. – Sami

1

J'ai corrigé votre code et apporté quelques modifications, notamment l'ajout de la portée à vos cours et l'introduction de quelques autres. Sans plus de connaissances de Flow je ne peux pas tester complètement, mais si vous pouviez me répondre, je peux en faire plus. Je suppose ici que le 'champ de flux' peut être simulé par la fonction 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) 

J'ai aussi fait quelques un coup de poignard brut à dessiner un profil aérodynamique, comme encore une fois je n'ai pas la connaissance du fichier de données avec les coordonnées.