Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xfoil interaction #20

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
247 changes: 247 additions & 0 deletions aeropy/Xfoil_Interaction/Genetic_algorithm_files/ambient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
'''

Created on Fri Feb 20 20:57:16 2015

@author: Siro Moreno

This is a submodule for the genetic algorithm that is explained in
https://docs.google.com/presentation/d/1_78ilFL-nbuN5KB5FmNeo-EIZly1PjqxqIB-ant-GfM/edit?usp=sharing

This script is the main program. It will call the different submodules
and manage the data transfer between them in order to achieve the
genetic optimization of the profile.

'''




import numpy as np





def ventana (x, inicio=0, fin=1, f=1.):
_1 = (np.sign(x-inicio))
_2 = (np.sign(-x+fin))
return 0.25 * (_1+1) * (_2+1) * f

def etc (x, inicio=0, f=1.):
_1 = (np.sign(x-inicio))
return 0.5 * (_1+1) * f

def earth_conditions():
heights = np.array([-1.000,
11.019,
20.063,
32.162,
47.350,
51.413,
71.802,
86.000,
90.000])


# an es el gradiente válido entre H(n-1) y H(n)
temp_gradient = np.array([-6.49,
0,
0.99,
2.77,
0,
-2.75,
-1.96,
0,
0])



atm_layer = temp_gradient.shape[0]

temp_points = np.zeros([atm_layer])
temp_points[0] = 294.64
for layer in np.arange(1, atm_layer, 1):
temp_points[layer] = temp_points[layer-1] + temp_gradient[layer-1]*(heights[layer]-heights[layer-1])
gravity = 9.8
gas_R = 287
planet_radius = 6371
p0 = 113922

conditions = (heights, temp_gradient, temp_points, gravity, gas_R, planet_radius, p0)

return conditions


def temperatura(h, conditions, dT = 0):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Veo que estás rehaciendo la atmósfera estándar aquí :P ¿Hay algún motivo para ello? ¿En la versión actual te resulta complicado adaptar los coeficientes para Marte?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sí, rehago la atmósfera estándar! XD
La manera en la que lo estoy haciendo creo que es la que hace que sea más fácil tener distintos planetas, ya que las diferentes funciones atmosféricas no contienen datos en sí, sino que hay que alimentarlas con los que devuelve una función que contiene los datos. Así, para calcular diferentes planetas sólo cambia los datos con que alimentas las funciones atmosféricas.
No he usado la atmósfera más rápida de las vuestras porque sólo hay que calcularlo una vez, así que cogí la mía porque es la que mejor sé cómo funciona.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

¿Con este tipo de código piensas que estaría más fácil?

https://github.com/AeroPython/aeropy/blob/juanlu-numba/jnumba/isa.py

No lo hemos hablado todavía pero si esta es la más rápida estando en Python (#4) tal vez habría que poner la versión con numba. Si lo ves factible, intento pulirla un poco y completarla el domingo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puedo intentar adaptar la versión con numba para que haga lo que quiero de comerse diferentes planetas. Pero añadiría un paquete de python más a los que hace falta tener instalados para poder ejecutar esto. Es lo bastante común?
Al fin y al cabo, el tiempo que tarda en calcular una vez la atmósfera es totalmente despreciable con respecto al tiempo que tarda el xfoil en calcular un perfil...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sí, me imagino que el rendimiento no es el problema en este caso. Es un tema más filosófico que otra cosa, de esta forma intentamos reutilizar todos el código de todos, y este ha sido el primer caso donde se ha visto que haga falta 😄

¿A qué te refieres con instalar otro paquete? ¿Te refieres a numba en sí? ¿Es aumentar el número de dependencias lo que te preocupa?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sí, justo! Me imagino que vendrá con Anaconda, pero para la gente que se lo va instalando por su cuenta, será lo normal tenerlo? Le podría echar para atrás a alguien que lo necesite?
Quizás se puede detallar en la documentación luego dónde se usa cada paquete y si se puede prescindir de él comentando algunas líneas del código.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Si están en Windows es muy muy raro que tengan NumPy y similares sin alguna distribución (Python(x,y), WinPython, Canopy o Anaconda). numba viene directamente instalado en Anaconda - fuera de ahí necesita un poco de trabajo, pero no más que instalar SciPy por ejemplo. En todo caso, se puede hacer que numba no sea obligatorio y meter un condicional: como solo es una línea, si hay numba estupendo y si no se queda como está.

'''Calcula la temperatura a una altura h en Km sobre el nivel del mar'''
grad = 0
heights = conditions[0]
gradient = conditions[1]
atm_layer = gradient.shape[0]
temp_points = conditions[2]



for layer in np.arange(0, atm_layer-1,1):
increase = temp_points[layer] + gradient[layer] * (h - heights[layer])
grad = grad + ventana(h, heights[layer],heights[layer+1], increase)
grad = grad + etc(h, heights[atm_layer-1],temp_points[atm_layer-1] + gradient[atm_layer-1]*(h - heights[atm_layer-1]))
return grad + dT


def segmento_presion_1(z, pi, z0, dT, conditions):
'''calcula la presión en un segmento de atmósfera de temperatura constante'''
g = conditions[3]
R = conditions[4]
radius = conditions[5]
h = (radius * z) /(radius + z)
h0 = (radius * z0)/(radius + z0)
_ = 1000*(h-h0) * g / (R * temperatura(z, conditions, dT))
return pi * np.e ** -_

def segmento_presion_2(z, pi, Ti, a, dT, conditions):
'''calcula la presión en un segmento de atmósfera con gradiente de temperatura "a" '''
g = conditions[3]
R = conditions[4]
_ = g / (a*R/1000)
return pi * (temperatura(z, conditions, dT)/(Ti + dT)) ** -_

def presion (h, conditions, dT = 0):
'''Calcula la presion en Pa a una altura h en m sobre el nivel del mar'''

heights = conditions[0]
gradient = conditions[1]
temp_points = conditions[2]
atm_layer = gradient.shape[0]
#Primero, calculamos la presion de cada punto de cambio de capa para la condición de dT pedida
#Suponemos que la presión es siempre constante a 101325 Pa a nivel del mar


pressure_points = np.zeros([atm_layer])
pressure_points[0] = conditions[6]

for layer in np.arange(1, atm_layer, 1):
if (abs(gradient[layer-1]) < 1e-8):
pressure_points[layer] = segmento_presion_1(heights[layer],
pressure_points[layer - 1],
heights[layer - 1],
dT, conditions)
else:
pressure_points[layer] = segmento_presion_2(heights[layer],
pressure_points[layer - 1],
temp_points[layer - 1],
gradient[layer-1],
dT, conditions)
#

#A partir de estos datos, construímos la atmósfera en cada capa

grad = 0
for layer in np.arange(1, atm_layer, 1):
if (abs(gradient[layer-1]) < 1e-8):
funcion = segmento_presion_1(h,
pressure_points[layer - 1],
heights[layer - 1],
dT, conditions)
else:
funcion = segmento_presion_2(h,
pressure_points[layer - 1],
temp_points[layer - 1],
gradient[layer-1],
dT, conditions)
grad = grad + ventana(h, heights[layer-1], heights[layer], funcion)
if (abs(gradient[layer-1])< 10e-8):
funcion = segmento_presion_1(h,
pressure_points[layer - 1],
heights[layer - 1],
dT, conditions)
else:
funcion = segmento_presion_2(h,
pressure_points[layer - 1],
temp_points[layer - 1],
gradient[layer-1],
dT, conditions)

grad = grad + etc(h, heights[atm_layer - 1], funcion)
return grad


def densidad(h, conditions, dT = 0):
'''Calcula la densidad a una altura h en m sobre el nivel del mar'''
R = conditions[4]
return presion(h, conditions, dT)/(R * temperatura(h, conditions, dT))


def mars_conditions():
heights = np.array([-8.3,
8.85,
30])

# an es el gradiente válido entre H(n-1) y H(n)
temp_gradient = np.array([-2.22,
-0.998,
-0.998])


atm_layer = temp_gradient.shape[0]

temp_points = np.zeros([atm_layer])
temp_points[0] = 268.77
for layer in np.arange(1, atm_layer, 1):
temp_points[layer] = temp_points[layer-1] + temp_gradient[layer-1]*(heights[layer]-heights[layer-1])
gravity = 3.711
gas_R = 192.1
planet_radius = 3389
p0 = 1131.67

conditions = (heights, temp_gradient, temp_points, gravity, gas_R, planet_radius, p0)

return conditions



def viscosidad(temp, planet):
if (planet == 'Earth'):
c = 120
lamb = 1.512041288
elif(planet == 'Mars'):
c = 240
lamb = 1.572085931

visc = lamb * temp**1.5 / (temp + c)
return visc


def Reynolds(dens, longitud, vel, visc):
re = 1000000 * dens * longitud * vel / visc
return re


def aero_conditions(ambient_data):
(planet, chord, height, speed_type, speed) = ambient_data
planet_dic = {'Mars':mars_conditions(), 'Earth':earth_conditions()}


sound = (1.4 *presion(height, planet_dic[planet]) / densidad(height,planet_dic[planet]))**0.5

if (speed_type == 'mach'):
mach = speed
vel = mach * sound
elif (speed_type == 'speed'):
mach = speed / sound
vel = speed
else:
print('error in the data, invalid speed parameter')



re = Reynolds(densidad(height, planet_dic[planet]), chord, vel, viscosidad(temperatura(height, planet_dic[planet]), planet))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nótese que esta línea se activa independientemente de si los datos son válidos o no. ¿Has pensado en lanzar un error más arriba?




return [mach, re]
#
#ambient_data = ('Earth', 03.0003, 11, 'speed', 30.1)
#
#result = aero_conditions(('Earth', 0.03, 11, 'mach', 0.1))
#print(result)
73 changes: 73 additions & 0 deletions aeropy/Xfoil_Interaction/Genetic_algorithm_files/analice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'''

Created on Fri Feb 20 20:57:16 2015

@author: Siro Moreno

This is a submodule for the genetic algorithm that is explained in
https://docs.google.com/presentation/d/1_78ilFL-nbuN5KB5FmNeo-EIZly1PjqxqIB-ant-GfM/edit?usp=sharing

This script is the main program. It will call the different submodules
and manage the data transfer between them in order to achieve the
genetic optimization of the profile.

'''



import subprocess
import sys
import os
import interfaz as interfaz
import numpy as np
import initial as initial



#generation = 0
#starting_profiles = 20
#
#genome = initial.start_pop(starting_profiles)
#
#interfaz.xfoil_calculate_population(generation,genome)
#
#num_pop = genome_matrix.shape[0]

def pop_analice (generation, num_pop):
pop_results = np.zeros([num_pop,2])
for profile_number in np.arange(1,num_pop+1,1):
pop_results[profile_number - 1, :] = profile_analice (generation, profile_number)

return pop_results


def profile_analice (generation, profile_number):
profile_name = 'gen' + str(generation) + 'prof' + str(profile_number)
data_root = "aerodata\data" + profile_name + '.txt'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Veo que estás metiendo rutas de Windows. Por si quieres que sea más portable y tal vez para facilitar lo que te dice Álex, te recomiendo que eches un vistazo a os.path:

http://pymotw.com/2/ospath/

datos = np.loadtxt(data_root, skiprows=12, usecols=[1,2])

read_dim = np.array(datos.shape)
#print('read_dim = ', read_dim, read_dim.shape)
if ((read_dim.shape[0]) != 2):
return np.array ([0,0])


pos_clmax = np.argmax(datos[:,0])
clmax = datos[pos_clmax,0]
efic = datos[:,0] / datos[:,1]
pos_maxefic = np.argmax(efic)
maxefic = efic[pos_maxefic]
return np.array([clmax , maxefic])

def adimension(array):
max_value = max(array)
adim = array / max_value
return adim

def score(generation, num_pop):

pop_results = pop_analice (generation, num_pop)
cl_score = adimension(pop_results[:,0])
efic_score = adimension(pop_results[:,1])
total_score = 0.3 * cl_score + 0.7 * efic_score
return total_score
38 changes: 38 additions & 0 deletions aeropy/Xfoil_Interaction/Genetic_algorithm_files/cross.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'''

Created on Fri Feb 20 20:57:16 2015

@author: Siro Moreno

This is a submodule for the genetic algorithm that is explained in
https://docs.google.com/presentation/d/1_78ilFL-nbuN5KB5FmNeo-EIZly1PjqxqIB-ant-GfM/edit?usp=sharing

This script is the main program. It will call the different submodules
and manage the data transfer between them in order to achieve the
genetic optimization of the profile.

'''



import subprocess
import sys
import os
import interfaz as interfaz
import numpy as np
import initial as initial




def cross(parents, num_pop):
children = np.zeros([num_pop, 16])
num_parents = parents.shape[0]
children[0:num_parents] = parents
for i in np.arange(num_parents, num_pop, 1):
coef = np.random.rand(num_parents)
coef = coef/sum(coef)
children[i,:]= np.dot(coef, parents)

return children

Loading