-
Notifications
You must be signed in to change notification settings - Fork 5
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
base: master
Are you sure you want to change the base?
Changes from 9 commits
2ec4dfc
df5d40c
c6832f8
62e550a
3e2157c
f90a40b
274e173
caea199
15017f4
172a9ce
3275656
c94d5cb
2402b0a
74bac35
4dafc75
01d7bf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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): | ||
'''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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
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' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: |
||
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 |
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 | ||
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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á.