diff --git a/aeropy/Xfoil_Interaction/Genetic_description_Bezier.ipynb b/aeropy/Xfoil_Interaction/Genetic_description_Bezier.ipynb new file mode 100644 index 0000000..0c4c4b1 --- /dev/null +++ b/aeropy/Xfoil_Interaction/Genetic_description_Bezier.ipynb @@ -0,0 +1,543 @@ +{ + "metadata": { + "name": "", + "signature": "sha256:726edeb8f702691c94f7c4337cf8c0d20522413cb5465f6f4aa0677c1ab14bbd" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%matplotlib inline" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 1 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 2 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "puntos_control = np.array([[0,0],\n", + " [0,1],\n", + " [3,5],\n", + " [3,0]])" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 3 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "num = 100" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 4 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "parametro_u = np.linspace(0,1,num)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 5 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "curva = np.zeros([num,2])" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 6 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def bernstein(u):\n", + " b = np.zeros([4,2])\n", + " b[0,:] = (1-u)**3\n", + " b[1,:] = (u * (1-u)**2)*3\n", + " b[2,:] = (u**2 * (1-u) )*3\n", + " b[3,:] = u**3\n", + " \n", + " return b" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 7 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for contador in np.arange(num):\n", + " _ = bernstein(parametro_u[contador])*puntos_control\n", + " curva[contador,] = sum (_)\n", + " \n" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 8 + }, + { + "cell_type": "code", + "collapsed": true, + "input": [ + "plt.plot(curva[:,0],curva[:,1])" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 9, + "text": [ + "[]" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "png": "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\nXUQkgv4ftseZQvfedoYAAAAASUVORK5CYII=\n", + "text": [ + "" + ] + } + ], + "prompt_number": 9 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "bernstein(0.2)*puntos_control" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 10, + "text": [ + "array([[ 0. , 0. ],\n", + " [ 0. , 0.384],\n", + " [ 0.288, 0.48 ],\n", + " [ 0.024, 0. ]])" + ] + } + ], + "prompt_number": 10 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "sum(puntos_control)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 11, + "text": [ + "array([6, 6])" + ] + } + ], + "prompt_number": 11 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "curva[:,1]" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 12, + "text": [ + "array([ 0. , 0.03120894, 0.06418022, 0.09883964, 0.13511299,\n", + " 0.17292608, 0.21220469, 0.25287463, 0.29486169, 0.33809166,\n", + " 0.38249035, 0.42798354, 0.47449704, 0.52195663, 0.57028813,\n", + " 0.61941731, 0.66926999, 0.71977195, 0.77084899, 0.8224269 ,\n", + " 0.87443149, 0.92678855, 0.97942387, 1.03226325, 1.08523249,\n", + " 1.13825738, 1.19126372, 1.24417731, 1.29692394, 1.3494294 ,\n", + " 1.4016195 , 1.45342003, 1.50475678, 1.55555556, 1.60574215,\n", + " 1.65524235, 1.70398197, 1.75188679, 1.79888261, 1.84489523,\n", + " 1.88985045, 1.93367405, 1.97629184, 2.01762962, 2.05761317,\n", + " 2.09616829, 2.13322079, 2.16869645, 2.20252108, 2.23462046,\n", + " 2.2649204 , 2.29334669, 2.31982513, 2.3442815 , 2.36664162,\n", + " 2.38683128, 2.40477626, 2.42040237, 2.43363541, 2.44440116,\n", + " 2.45262543, 2.45823401, 2.4611527 , 2.46130729, 2.45862358,\n", + " 2.45302737, 2.44444444, 2.43280061, 2.41802166, 2.40003339,\n", + " 2.3787616 , 2.35413208, 2.32607062, 2.29450303, 2.25935511,\n", + " 2.22055263, 2.17802141, 2.13168724, 2.08147592, 2.02731323,\n", + " 1.96912498, 1.90683696, 1.84037498, 1.76966481, 1.69463227,\n", + " 1.61520315, 1.53130324, 1.44285834, 1.34979424, 1.25203674,\n", + " 1.14951165, 1.04214474, 0.92986183, 0.8125887 , 0.69025115,\n", + " 0.56277498, 0.43008598, 0.29210996, 0.1487727 , 0. ])" + ] + } + ], + "prompt_number": 12 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "np.arange(10)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 13, + "text": [ + "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" + ] + } + ], + "prompt_number": 13 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "puntos_perfil = np.zeros([13,2])\n", + "puntos_perfil[0,] = [1,0]\n", + "puntos_perfil[6,] = [1,0]" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 14 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "genes = np.array([150*np.pi/180, #ang s1\n", + " 0.2, #dist s1\n", + " 0.5, #x 1\n", + " 0.12, #y 1\n", + " 0, #ang 1\n", + " 0.2, #dist b1\n", + " 0.2, #dist c1\n", + " 0.1, #dist a1\n", + " 0.05, #dist a2\n", + " 0.4, #x 2\n", + " 0.05, #y 2\n", + " 5*np.pi/180, #ang 2\n", + " 0.2, #dist b2\n", + " 0.2, #dist c2\n", + " 160*np.pi/180, #ang s2\n", + " 0.2]) #dist s2" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 15 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def punto_pendiente(a,dist,ang):\n", + " punto = np.array(a)+ np.array(dist,dist)* [np.cos(ang),np.sin(ang)]\n", + " return punto" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 16 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def generador_puntos(genes):\n", + " puntos = np.zeros([13,2])\n", + " puntos[0,:] = [1,0]\n", + "# puntos[1,:] = [1,0] + np.array([genes[1],genes[1]]) * [-np.cos(genes[0]),np.sin(genes[0])]\n", + " puntos[1,:] = punto_pendiente([1,0],genes[1],genes[0])\n", + " puntos[3,:] = [genes[2],genes[3]]\n", + "# puntos[2,:] = [genes[2],genes[3]] + genes[5] * [np.cos(genes[4]),np.sin(genes[4])]\n", + " puntos[2,:] = punto_pendiente([genes[2],genes[3]],genes[5],genes[4])\n", + "# puntos[4,:] = [genes[2],genes[3]] + genes[6] * [-np.cos(genes[4]),np.sin(genes[4])]\n", + " puntos[4,:] = punto_pendiente([genes[2],genes[3]],genes[6],genes[4]+np.pi)\n", + " puntos[5,:] = [0, genes[7]]\n", + " puntos[6,:] = [0,0]\n", + " puntos[7,:] = [0, -genes[8]]\n", + " puntos[9,:] = [genes[9],genes[10]]\n", + "# puntos[8,:] = [genes[9],genes[10]] + genes[12] * [np.cos(genes[11]),np.sin(genes[11])]\n", + " puntos[8,:] = punto_pendiente([genes[9],genes[10]], genes[12], genes[11]+np.pi)\n", + "# puntos[10,:] = [genes[9],genes[10]] + genes[13] * [-np.cos(genes[11]),np.sin(genes[11])]\n", + " puntos[10,:] = punto_pendiente([genes[9],genes[10]], genes[13], genes[11])\n", + "# puntos[11,:] = [1,0] + genes[15] * [-np.cos(genes[14]),np.sin(genes[14])]\n", + " puntos[11,:] = punto_pendiente([1,0], genes[15], genes[14])\n", + " puntos[12,:] = [1,0]\n", + " return puntos" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 17 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "puntos_control = generador_puntos(genes)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 18 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "plt.plot(puntos_control[:,0],puntos_control[:,1])" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 19, + "text": [ + "[]" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "png": "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\n", + "text": [ + "" + ] + } + ], + "prompt_number": 19 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "curva = np.zeros([num,2])" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 20 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def bezier(num, puntos_control):\n", + " \n", + " parametro_u = np.linspace(0,1,num)\n", + " curva = np.zeros([num,2])\n", + "\n", + " for contador in np.arange(num):\n", + " _ = bernstein(parametro_u[contador])*puntos_control\n", + " curva[contador,] = sum (_)\n", + " return curva\n", + " \n" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 21 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "num = 100\n", + "\n", + "perfil = np.zeros([(4*num), 2])\n", + "\n", + "perfil[0:num,:] = bezier(num,puntos_control[0:4,:])\n", + "perfil[num:2*num,:] = bezier(num,puntos_control[3:7,:])\n", + "perfil[2*num:3*num,:] = bezier(num,puntos_control[6:10,:])\n", + "perfil[3*num:4*num,:] = bezier(num,puntos_control[9:13,:])" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 22 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "plt.figure(num=None, figsize=(18, 6), dpi=80, facecolor='w', edgecolor='k')\n", + "plt.plot(perfil[:,0],perfil[:,1])\n", + "plt.plot(puntos_control[:,0],puntos_control[:,1])\n", + "plt.gca().set_aspect(1)\n" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "display_data", + "png": "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\ntEVumWnBygAAAABJRU5ErkJggg==\n", + "text": [ + "" + ] + } + ], + "prompt_number": 23 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "from IPython.html.widgets import interactive" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 24 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def pintar(genes):\n", + " num = 100\n", + "\n", + " perfil = np.zeros([(4*num), 2])\n", + " \n", + " puntos_control = generador_puntos(genes)\n", + "\n", + " perfil[0:num,:] = bezier(num,puntos_control[0:4,:])\n", + " perfil[num:2*num,:] = bezier(num,puntos_control[3:7,:])\n", + " perfil[2*num:3*num,:] = bezier(num,puntos_control[6:10,:])\n", + " perfil[3*num:4*num,:] = bezier(num,puntos_control[9:13,:])\n", + " \n", + " plt.figure(num=None, figsize=(18, 6), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.plot(perfil[:,0],perfil[:,1])\n", + " plt.plot(puntos_control[:,0],puntos_control[:,1])\n", + " plt.gca().set_aspect(1)\n", + "\n", + "\n", + " return 0\n" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 25 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def intermedio(g0,g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12, g13, g14, g15):\n", + " return np.array([g0,g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12, g13, g14, g15])\n", + "\n", + "def pintar2(g0,g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12, g13, g14, g15):\n", + " pintar(intermedio(g0,g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12, g13, g14, g15))\n", + " return 0" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 26 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "interactive(pintar2,\n", + " g0 = (130*np.pi/180,180*np.pi/180,5*np.pi/180), #ang s1\n", + " g1 = (0, 0.5, 0.01), #dist s1\n", + " g2 = (0.2, 0.8, 0.05), #x 1\n", + " g3 = (0, 0.4, 0.02), #y 1\n", + " g4 = (-25*np.pi/180,25*np.pi/180,5*np.pi/180), #ang 1\n", + " g5 = (0,0.4, 0.05), #dist b1\n", + " g6 = (0,0.4, 0.05), #dist c1\n", + " g7 = (0, 0.5, 0.01), #dist a1\n", + " g8 = (0, 0.5, 0.01), #dist a2\n", + " g9 = (0.2, 0.8, 0.05), #x 2\n", + " g10 = (-0.4, 0.2, 0.02), #y 2\n", + " g11 = (-25*np.pi/180,25*np.pi/180,5*np.pi/180), #ang 2\n", + " g12 = (0,0.4, 0.05), #dist b2\n", + " g13 = (0,0.4, 0.05), #dist c2\n", + " g14 = 160*np.pi/180, #ang s2\n", + " g15 = (0,0.4, 0.05))" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "display_data", + "png": "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\npcED1lDwAAAAAElFTkSuQmCC\n", + "text": [ + "" + ] + } + ], + "prompt_number": 27 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 27 + } + ], + "metadata": {} + } + ] +} \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/README.md b/aeropy/Xfoil_Interaction/README.md new file mode 100644 index 0000000..f647e2a --- /dev/null +++ b/aeropy/Xfoil_Interaction/README.md @@ -0,0 +1,59 @@ +aeropy - Xfoil Interaction tool +====== + +Python tools for Aeronautical calculations. + +Genetic Optimization Algorithm in progress (python 3.4, xfoil 6.99) + +Includes: + +-Minimal working interface between Python and Xfoil + +-Interactive Ipython notebook showing how the genome-to-profile decodification works (in progress). + +-Ipython notebook that can easily be used to save drawings of the airfoils generated. (Must be placed with the launcher.py genetic algorithm file)(Work in progress) + +Genetic algorithm modules: + +-ambient + +-analyze + +-cross + +-ender + +-ender_report (Work in progress) + +-genetics + +-initial + +-interfaz + +-main + +-mutation + +-selection + +-testing + +-transcript + +-launcher + +##Instructions: + +Place xfoil in the same folder as "launcher.py" + + +Execute "launcher.py" + + + +More info: +https://docs.google.com/presentation/d/1_78ilFL-nbuN5KB5FmNeo-EIZly1PjqxqIB-ant-GfM/edit?usp=sharing + +You can download Xfoil for free from its official page: +http://web.mit.edu/drela/Public/web/xfoil/ \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/algoritmo/__init__.py b/aeropy/Xfoil_Interaction/algoritmo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/aeropy/Xfoil_Interaction/algoritmo/ambient.py b/aeropy/Xfoil_Interaction/algoritmo/ambient.py new file mode 100644 index 0000000..4b417cd --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/ambient.py @@ -0,0 +1,256 @@ +''' + +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 ambient subprogram. Its objective is to calculate the +Mach and Reynolds numbers that XFoil uses to calculate the +aerodynamics of airfoils. + + +This subprogram consist mainly in models for the International Standard Atmosphere +and an equivalent atmosphere model for Mars. +''' + +import numpy as np + + + +def window (x, start=0, end=1, f=1.): + '''Returns a function f between 'start' and 'endn'. + Returns 0 outside this interval''' + _1 = (np.sign(x-start)) + _2 = (np.sign(-x+end)) + return 0.25 * (_1+1) * (_2+1) * f + +def etc (x, start=0, f=1.): + '''Returns 0 until 'start', returns 'f' after. + ''' + _1 = (np.sign(x-start)) + return 0.5 * (_1+1) * f + +def earth_conditions(): + + '''Returns an array of atmospheric and physical data from which the complete + model of the atmosphere can be built. + ''' + heights = np.array([-1.000, + 11.019, + 20.063, + 32.162, + 47.350, + 51.413, + 71.802, + 86.000, + 90.000]) + + + + 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 temperature(h, conditions, dT = 0): + '''Calculates the value for temperature in Kelvin at a certain altitude''' + 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 + window(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 pressure_segment_1(z, pi, z0, dT, conditions): + '''Calculates pressure throught a constant temperature segment of the atmosphere''' + g = conditions[3] + R = conditions[4] + radius = conditions[5] + h = (radius * z) /(radius + z) + h0 = (radius * z0)/(radius + z0) + _ = 1000*(h-h0) * g / (R * temperature(z, conditions, dT)) + return pi * np.e ** -_ + +def pressure_segment_2(z, pi, Ti, a, dT, conditions): + '''Calculates pressure throught a variant temperature segment of the atmosphere ''' + g = conditions[3] + R = conditions[4] + _ = g / (a*R/1000) + return pi * (temperature(z, conditions, dT)/(Ti + dT)) ** -_ + +def pressure (h, conditions, dT = 0): + '''Calculates the value for pressure in Pascal at a certain altitude''' + + heights = conditions[0] + gradient = conditions[1] + temp_points = conditions[2] + atm_layer = gradient.shape[0] + + + 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] = pressure_segment_1(heights[layer], + pressure_points[layer - 1], + heights[layer - 1], + dT, conditions) + else: + pressure_points[layer] = pressure_segment_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 = pressure_segment_1(h, + pressure_points[layer - 1], + heights[layer - 1], + dT, conditions) + else: + funcion = pressure_segment_2(h, + pressure_points[layer - 1], + temp_points[layer - 1], + gradient[layer-1], + dT, conditions) + grad = grad + window(h, heights[layer-1], heights[layer], funcion) + if (abs(gradient[layer-1])< 10e-8): + funcion = pressure_segment_1(h, + pressure_points[layer - 1], + heights[layer - 1], + dT, conditions) + else: + funcion = pressure_segment_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 density(h, conditions, dT = 0): + '''Calculates the value for density in Kg/m3 at a certain altitude''' + R = conditions[4] + return pressure(h, conditions, dT)/(R * temperature(h, conditions, dT)) + + +def mars_conditions(): + '''Returns an array of atmospheric and physical data from which the complete + model of the atmosphere can be built. + ''' + 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 viscosity(temp, planet): + '''Calculates the value for viscosity in microPascal*second + for a certain temperature''' + + 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): + '''Calculates the Reynolds number''' + re = 1000000 * dens * longitud * vel / visc + return re + + +def aero_conditions(ambient_data): + '''Given a certain conditions, return the value of the Mach and Reynolds + numbers, in that order. + ''' + (planet, chord, height, speed_type, speed) = ambient_data + planet_dic = {'Mars':mars_conditions(), 'Earth':earth_conditions()} + + + sound = (1.4 *pressure(height, planet_dic[planet]) / density(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') + + + visc = viscosity(temperature(height, planet_dic[planet]), planet) + re = Reynolds(density(height, planet_dic[planet]), chord, vel, visc) + + + + return [mach, re] diff --git a/aeropy/Xfoil_Interaction/algoritmo/analyze.py b/aeropy/Xfoil_Interaction/algoritmo/analyze.py new file mode 100644 index 0000000..96d71c9 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/analyze.py @@ -0,0 +1,77 @@ +''' + +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 analysis subprogram. Its objective is to assign a score to +every airfoil by reading the aerodynamic characteristics that XFoil +has calculated. + +''' + + + + +import numpy as np +import os + + + + +def pop_analice (generation, population, num_parent): + '''For a given generation and number of airfoils, returns an array which + contains the maximun Lift Coefficient and Maximum Aerodinamic Efficiency + for every airfoil. + ''' + pop_len = len(population) + for airfoil_number in range(1, pop_len+1): + airfoil = population[airfoil_number - 1] + if not hasattr(airfoil, 'clmax'): + airfoil_analice(generation, airfoil_number, airfoil) + + + + +def airfoil_analice (generation, airfoil_number, airfoil): + '''For a given generation and airfoil, searches for the results of the + aerodynamic analysis made in Xfoil. Then, searches for the maximum + values of the Lift Coefficient and Aerodynamic Efficiency. + ''' + airfoil_name = airfoil.name + data_root = os.path.join("aerodata","data" + airfoil_name + '.txt') + datos = np.loadtxt(data_root, skiprows=12, usecols=[1,2]) + + #Chequear integridad de los datos + read_dim = np.array(datos.shape) + if ((read_dim.shape[0]) != 2): + datos = np.zeros([2,2]) + + + 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] + airfoil.clmax = clmax + airfoil.maxefic = maxefic + +def score(generation, population, weights): + ''' + + ''' + max_cl = -100 + max_efic = -100 + + for airfoil in population: + max_cl = max(airfoil.clmax, max_cl) + max_efic = max(airfoil.maxefic, max_efic) + for airfoil in population: + cl_score = airfoil.clmax / max_cl + efic_score = airfoil.maxefic / max_efic + total_score = weights[0] * cl_score + weights[1] * efic_score + airfoil.score = total_score + diff --git a/aeropy/Xfoil_Interaction/algoritmo/cross.py b/aeropy/Xfoil_Interaction/algoritmo/cross.py new file mode 100644 index 0000000..c409ca9 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/cross.py @@ -0,0 +1,55 @@ +''' + +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 cross subprogramme. Its objective is to generate +a whole new population genome from the genome of the previous generation +winners (parents). + +In order to eliminate the chance of randomly getting worse results, +the parents are preserved as the first elements of the new population. + +''' + + + + +import numpy as np +from algoritmo.initial import Airfoil + + +def cross(parents, pop_len, generation): + '''Generates a population of (num_pop) airfoil genomes by mixing randomly + the genomes of the given parents. + The parents are preserved as the first elements of the new population. + ''' + children = [] + parents_len = len(parents) + + for parent_num in range(len(parents)): + parent = parents[parent_num] + parent.copy_data(generation + 1, parent_num) + children.append(parent) + + for child_num in range(parents_len, pop_len): + parent_1 = parents[np.random.choice(parents_len)] + parent_2 = parents[np.random.choice(parents_len)] + genome_1 = parent_1.genome + genome_2 = parent_2.genome + child_genome = [] + coefs = np.random.rand(len(genome_1)) + for gen_num in range(len(genome_1)): + gen = genome_1[gen_num] * coefs[gen_num] + gen += genome_2[gen_num] * (1- coefs[gen_num]) + child_genome.append(gen) + child_genome = np.array(child_genome) + child = Airfoil(child_genome) + children.append(child) + + return children + diff --git a/aeropy/Xfoil_Interaction/algoritmo/ender.py b/aeropy/Xfoil_Interaction/algoritmo/ender.py new file mode 100644 index 0000000..ddea0a4 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/ender.py @@ -0,0 +1,696 @@ +''' + +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 os +import algoritmo.interfaz as interfaz +import numpy as np +import algoritmo.initial as initial +import algoritmo.genetics as genetics +import algoritmo.analyze as analyze +import algoritmo.selection as selection +import matplotlib.pyplot as plt +import algoritmo.transcript as transcript +import algoritmo.ambient as ambient +import subprocess +import shutil +#import algoritmo.ender_report + + +def finish (population, all_parameters): + + + generation = all_parameters[1] + num_parent = all_parameters[2] + num_winners = all_parameters[3] + weights = all_parameters[4] + end_options = all_parameters[5] + ambient_data = all_parameters[6] + aero_domain = all_parameters[7] + + must_draw_winners = end_options[0] + must_draw_polars = end_options[1] + must_draw_evolution = end_options[2] + must_compare_naca_standard = end_options[3] + must_compare_naca_custom = end_options[4] + must_create_report = end_options[5] + +# analyze_final(generation, num_winners, weights) + analyze.pop_analice (generation, population, num_parent) + analyze.score(generation, population, weights) + save_last_gen(population, generation) + winners = selection.selection(population, num_winners) + + compare = must_compare_naca_standard + final_xfoil(generation, ambient_data, aero_domain, compare) + analyze_winners(winners) + calculate_evolution(generation, num_winners, compare) + if (must_draw_winners): + draw_winners(winners, compare) + if (must_draw_polars): + draw_aero_comparison(num_winners, compare) + if (must_draw_evolution): + draw_evolution(compare, aero_domain) + #if (must_create_report): + # ender_report.create_report(all_parameters) + +def save_last_gen(population, generation): + + + results_name = 'results_data_generation'+ str(generation) + '.txt' + results_root = os.path.join('results', 'data', results_name ) + results_title = 'generation' + str(generation) + 'results:' + + try: + os.remove(results_root) + except : + pass + + + results_file = open(results_root, mode = 'x') + results_file.write(results_title + '\n') + results_file.write('Cl max Eficciency Score\n') + + for airfoil in population: + result = str(airfoil.clmax) + ' ' + result += str(airfoil.maxefic) + ' ' + result += str(airfoil.score) + '\n' + results_file.write(result) + + results_file.close() + +#def analyze_final(generation, population, num_winners, weights): +# '''Analyze the data of the last generation +# ''' +# file_parent_name = 'generation'+ str(generation) + '.txt' +# genome_parent_root = os.path.join('genome', file_parent_name) +# genome = np.loadtxt(genome_parent_root, skiprows=1) +# num_pop = genome.shape[0] +# results_data = analyze.pop_analice(generation, num_pop) +# +# scores = analyze.score(generation,num_pop, weights) +# winners = selection.selection(scores, genome, num_winners) +# +# +# +# +# file_name = 'winners.txt' +# genome_root = os.path.join('genome', file_name) +# title = 'winners genome' +# +# results_name = 'results_data_generation'+ str(generation) + '.txt' +# results_root = os.path.join('results', 'data', results_name ) +# results_title = 'generation' + str(generation) + 'results:' +# +# +# try: +# os.remove(genome_root) +# except: +# pass +# +# try: +# os.remove(results_root) +# except : +# pass +# +# genome_file = open(genome_root, mode = 'x') +# results_file = open(results_root, mode = 'x') +# genome_file.write(title + '\n') +# results_file.write(results_title + '\n') +# results_file.write('Cl max Eficciency Score' + '\n') +# +# +# for profile in np.arange(0, num_pop, 1): +# result = str(results_data[profile, 0]) + ' ' +# result = result + str(results_data[profile, 1]) + ' ' +# result = result + str(scores[profile]) + '\n' +# results_file.write(result) +# for profile in np.arange(0, num_winners, 1): +# line = '' +# for gen in np.arange(0, 16,1): +# line = line + str(winners[profile, gen]) +' ' +# line = line + '\n' +# genome_file.write(line) +# genome_file.close() +# results_file.close() + + +def analyze_winners(winners): + '''Analyze the data of the winners + ''' + +# results = np.zeros([num_winners, 3]) +# +# for i in np.arange(0, num_winners, 1): +# dataname = 'datagen' + str(generation) + 'prof' + str(i + 1) + '.txt' +# data_root = os.path.join('aerodata', dataname) +# data = np.loadtxt(data_root, skiprows = 12, usecols=[1,2]) +# clmax = max(data[:,0]) +# efimax = max(data[:,0] / data[:,1]) +# results[i, 0:2] = [clmax, efimax] +# +# cl_score = analyze.adimension(results[:,0]) +# efic_score = analyze.adimension(results[:,1]) +# results[:,2] = weights[0] * cl_score + weights[1] * efic_score + + + results_name = 'results_winners.txt' + results_root = os.path.join('results', 'data', results_name ) + results_title = 'Winners results:' + + file_name = 'winners.txt' + genome_root = os.path.join('genome', file_name) + title = 'winners genome' + + try: + os.remove(results_root) + except : + pass + try: + os.remove(genome_root) + except : + pass + + genome_file = open(genome_root, mode = 'x') + results_file = open(results_root, mode = 'x') + genome_file.write(title + '\n') + results_file.write(results_title + '\n') + results_file.write('Cl max Eficciency Score\n') + + for airfoil_num in range(len(winners)): + airfoil = winners[airfoil_num] + airfoil.copy_winner(airfoil_num + 1) + line = '' + for gen in airfoil.genome: + line = line + str(gen) +' ' + line = line + '\n' + genome_file.write(line) + result = str(airfoil.clmax) + ' ' + result += str(airfoil.maxefic) + ' ' + result += str(airfoil.score) + '\n' + results_file.write(result) + +#--- Drawing Airfoils + + + +def draw_winners(winners, options): + + num_winners = len(winners) + + for winner in range(num_winners): + genome = winners[winner].genome + graph_name = 'winner ' + str(winner + 1) + graph_root = os.path.join('results','graphics',graph_name + '.png') + point_data = transcript.decode_genome(genome) + draw_figure(graph_name, graph_root, point_data) + + if (options): + graph_name = 'NACA 5615' + graph_root = os.path.join('results','graphics',graph_name + '.png') + data_root = os.path.join('airfoils','winners',graph_name + '.txt') + point_data = np.loadtxt(data_root, skiprows = 1) + draw_figure(graph_name, graph_root, point_data) + + graph_name = 'NACA 5603' + graph_root = os.path.join('results','graphics',graph_name + '.png') + data_root = os.path.join('airfoils','winners',graph_name + '.txt') + point_data = np.loadtxt(data_root, skiprows = 1) + draw_figure(graph_name, graph_root, point_data) + + +def draw_figure(graph_name, graph_root, point_data): + try: + os.remove(graph_root) + except : + pass + + plt.figure(num=None, figsize=(15, 5), dpi=80, facecolor='w', edgecolor='k') + plt.title (graph_name) + plt.ylim(-0.15, 0.15) + plt.xlim(-0.05, 1.05) + plt.plot(point_data[:,0], point_data[:,1]) + plt.gca().set_aspect(1) + + + plt.savefig(graph_root) + + +# Running Xfoil for more detailed analysis of the winners + +def xfoil_calculate_profile(profile_name, profile_root, + ambient_data, aero_domain, + profile_type): + + '''Starts Xfoil and analyzes the given airfoil. Saves the results. + ''' + + data_root = os.path.join("results","data" , profile_name + 'aerodata.txt') + + aerodynamics = ambient.aero_conditions(ambient_data) + + + commands = [profile_type, + profile_root] + commands2 = ['oper', + 'mach ' + str(aerodynamics[0]), + 're ' + str(aerodynamics[1]), + 'visc', + 'pacc', + data_root, + '', + 'aseq', + str(aero_domain[0]), + str(aero_domain[1]), + str(aero_domain[2]), + '', + 'quit'] + if (profile_type == 'NACA'): + naca_root = os.path.join('airfoils', 'winners', profile_name + '.txt') + commands.extend(['save',naca_root]) + commands.extend(commands2) + + + + try: + os.remove(data_root) + except : + pass + + + + p = subprocess.Popen(["xfoil",], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + + for command in commands: + p.stdin.write((command + '\n').encode()) + + + p.stdin.close() + for line in p.stdout.readlines(): + print(line.decode(), end='') + +def final_xfoil(total_generations, ambient_data, aero_domain, compare): + + airfoil_folder = os.path.join('airfoils','winners') + if not os.path.exists(airfoil_folder): + os.makedirs(airfoil_folder) + +# genome_root = os.path.join('genome','winners.txt') +# genome_matrix = np.loadtxt(genome_root, skiprows=1) +# num_winners = genome_matrix.shape[0] +# +# +# for profile in np.arange(0, num_winners, 1): +# genome = genome_matrix[profile,:] +# profile_name = 'winner ' + str(profile + 1) +# profile_root = os.path.join('profiles','winners', profile_name + '.txt') +# +# perfil = transcript.decode_genome(genome) +# try: +# os.remove(profile_root) +# except : +# pass +# +# +# archivo = open(profile_root, mode = 'x') +# archivo.write(profile_name + '\n') +# for i in np.arange(0,100,1): +# texto = str(round(perfil[i,0],6)) + ' ' + str(round(perfil[i,1],6)) +'\n' +# archivo.write(texto) +# archivo.close() +# +# xfoil_calculate_profile(profile_name, profile_root, +# ambient_data, aero_domain, +# 'load') + if (compare): + + airfoil_name = 'NACA 5615' + airfoil_root = '5615' + + try: + os.remove(os.path.join('airfoils','winners', airfoil_name + '.txt')) + except : + pass + + xfoil_calculate_profile(airfoil_name, airfoil_root, + ambient_data, aero_domain, + 'NACA') + + airfoil_name2 = 'NACA 5603' + airfoil_root2 = '5603' + + try: + os.remove(os.path.join('airfoils','winners', airfoil_name2 + '.txt')) + except : + pass + + xfoil_calculate_profile(airfoil_name2, airfoil_root2, + ambient_data, aero_domain, + 'NACA') + +# Drawing polars + +def draw_alpha(data): + + if not os.path.exists(os.path.join('results','graphics')): + os.makedirs(os.path.join('results','graphics')) + graph_name = 'Cl_vs_Alpha_Graphic' + graph_root = os.path.join('results','graphics',graph_name + '.png') + + root = data[:,1] + name = data[:,0] + + num_prof = root.shape[0] + + plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k') + plt.rc('font', size = 20) + plt.title (graph_name) + + ideal = np.array([[0,0], + [15, (15*np.pi/180)*np.pi*2]]) + + plt.plot(ideal[:,0], ideal[:,1], label = ' ideal, cl alpha = 2$\pi$') + for i in np.arange(0,num_prof,1): + + profile_root = root[i] + profile_name = name[i] + datos = np.loadtxt(profile_root, skiprows=12, usecols=[0,1]) + read_dim = np.array(datos.shape) + if ((read_dim.shape[0]) == 2): + plt.plot(datos[:,0], datos[:,1], label = profile_name) + + + + + plt.legend(loc = 2, fontsize =14) + plt.grid() + plt.minorticks_on() + plt.xlabel('Attack angle') + plt.ylabel('Lift coefficient') + plt.savefig(graph_root) + +def draw_polar(data): + + if not os.path.exists(os.path.join('results','graphics')): + os.makedirs(os.path.join('results','graphics')) + graph_name = 'Cl_vs_Cd_Graphic' + graph_root = os.path.join('results','graphics',graph_name + '.png') + + root = data[:,1] + name = data[:,0] + + num_prof = root.shape[0] + + plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k') + plt.rc('font', size = 20) + plt.title (graph_name) + + for i in np.arange(0,num_prof,1): + + profile_root = root[i] + profile_name = name[i] + datos = np.loadtxt(profile_root, skiprows=12, usecols=[1,2]) + read_dim = np.array(datos.shape) + if ((read_dim.shape[0]) == 2): + plt.plot(datos[:,1], datos[:,0], label = profile_name) + + + + plt.xlim(xmin=0) + plt.legend(loc = 4, fontsize =14) + plt.grid() + plt.minorticks_on() + plt.xlabel('Drag coefficient') + plt.ylabel('Lift coefficient') + plt.savefig(graph_root) + + +def draw_aero_comparison(num_winners, compare): + + num_profiles = num_winners + if (compare): + num_profiles = num_profiles + 1 + + data = [] + + for i in np.arange(0, num_winners, 1): + name = 'winner ' + str(i + 1) + root = os.path.join('results','data', name + 'aerodata.txt') + data.append([name, root]) + if compare : + name = 'NACA 5615' + root = os.path.join('results','data', name + 'aerodata.txt') + data.append([name, root]) + + name = 'NACA 5603' + root = os.path.join('results','data', name + 'aerodata.txt') + data.append([name, root]) + + data = np.array(data) + draw_alpha(data) + draw_polar(data) + + +# Drawing the Evolution Diagrams + +def calculate_evolution(max_generations, num_winners, options): + lift = [] + effic = [] + + + + for gen in range(max_generations): + lift.append([]) + effic.append([]) + data_root = os.path.join('results','data','results_data_generation'+ str(gen) + '.txt') + data = np.loadtxt(data_root, skiprows = 2) + invscore = 1- data[:, 2] + positions = np.argsort(invscore) + for win in np.arange(0, num_winners, 1): + win_cl = data[positions[win], 0] + win_effic = data[positions[win], 1] + lift[gen].append(win_cl) + effic[gen].append(win_effic) + + lift = np.array(lift) + effic = np.array(effic) + + lift_name = 'lift history.txt' + lift_root = os.path.join('results', 'data', lift_name ) + effic_name = 'efficiency history.txt' + effic_root = os.path.join('results', 'data', effic_name ) + + + + try: + os.remove(lift_root) + except : + pass + try: + os.remove(effic_root) + except : + pass + + lift_file = open(lift_root, mode = 'x') + lift_file.write('Values of Cl of the best airfoils of each generation\n') + lift_file.write('generation ') + for i in np.arange(1, num_winners + 1, 1): + lift_file.write('winner ' + str(i) + ' ') + lift_file.write('\n') + + for gen in np.arange(0, max_generations, 1): + lift_file.write(' '+ str(gen) + ' ') + + for win in np.arange(0, num_winners, 1): + lift_file.write(str(lift[gen,win])+ ' ') + lift_file.write('\n') + + lift_file.close() + + effic_file = open(effic_root, mode = 'x') + effic_file.write('Values of efficiency of the best airfoils of each generation\n') + effic_file.write('generation ') + for i in np.arange(1, num_winners + 1, 1): + effic_file.write('winner ' + str(i) + ' ') + effic_file.write('\n') + + for gen in np.arange(0, max_generations, 1): + effic_file.write(' '+ str(gen) + ' ') + + for win in np.arange(0, num_winners, 1): + effic_file.write(str(effic[gen,win])+ ' ') + effic_file.write('\n') + + effic_file.close() + +def draw_evolution(options, aero_domain): + + if options : + naca_root = os.path.join('results','data','NACA 5615aerodata.txt') + naca_data = np.loadtxt(naca_root, skiprows = 12, usecols=[1,2]) + max_angle = 1 + 2 * round((aero_domain[1]-aero_domain[0])/aero_domain[2]) + naca_cl = max(naca_data[0:max_angle,0]) + naca_effic = max(naca_data[0:max_angle,0] / naca_data[0:max_angle,1]) + + naca_root2 = os.path.join('results','data','NACA 5603aerodata.txt') + naca_data2 = np.loadtxt(naca_root2, skiprows = 12, usecols=[1,2]) + max_angle = 1 + 2 * round((aero_domain[1]-aero_domain[0])/aero_domain[2]) + naca_cl2 = max(naca_data2[0:max_angle,0]) + naca_effic2 = max(naca_data2[0:max_angle,0] / naca_data2[0:max_angle,1]) + + lift_name = 'lift history.txt' + lift_root = os.path.join('results', 'data', lift_name ) + lift_data = np.loadtxt(lift_root, skiprows = 2) + + effic_name = 'efficiency history.txt' + effic_root = os.path.join('results', 'data', effic_name ) + effic_data = np.loadtxt(effic_root, skiprows = 2) + + + if not os.path.exists(os.path.join('results','graphics')): + os.makedirs(os.path.join('results','graphics')) + + # Draw the history of lift coefficient + graph_name = 'History of lift coefficient' + graph_root = os.path.join('results','graphics',graph_name + '.png') + + num_winners = lift_data.shape[1] - 1 + + plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k') + plt.rc('font', size = 20) + plt.title (graph_name) + + for i in np.arange(0,num_winners,1): + label = 'winner' + str(i + 1) + plt.plot(lift_data[:,0], lift_data[:,i + 1], label = label) + if options : + label = 'NACA 5615' + value = naca_cl * np.ones_like(effic_data[:,0]) + plt.plot(lift_data[:,0], value, label = label) + + label = 'NACA 5603' + value = naca_cl2 * np.ones_like(effic_data[:,0]) + plt.plot(lift_data[:,0], value, label = label) + + + + plt.legend(loc = 4, fontsize =14) + plt.grid() + plt.minorticks_on() + plt.xlabel('Generation') + plt.ylabel('Lift coefficient') + plt.savefig(graph_root) + + # Draw the history of efficiency + graph_name = 'History of efficiency' + graph_root = os.path.join('results','graphics',graph_name + '.png') + + num_winners = effic_data.shape[1] - 1 + + plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k') + plt.rc('font', size = 20) + plt.title (graph_name) + + for i in np.arange(0,num_winners,1): + label = 'winner' + str(i + 1) + plt.plot(effic_data[:,0], effic_data[:,i + 1], label = label) + if options : + label = 'NACA 5615' + value = naca_effic * np.ones_like(effic_data[:,0]) + plt.plot(effic_data[:,0], value, label = label) + + label = 'NACA 5603' + value = naca_effic2 * np.ones_like(effic_data[:,0]) + plt.plot(effic_data[:,0], value, label = label) + + + + plt.legend(loc = 4, fontsize =14) + plt.grid() + plt.minorticks_on() + plt.xlabel('Generation') + plt.ylabel('Efficiency') + plt.savefig(graph_root) + + + + + + + +if __name__ == '__main__': + +####---------Primary Variables----- + + + airfoils_per_generation = 30 + total_generations = 30 + num_parent = 4 + +# We give the algorithm the conditions at wich we want to optimize our airofil +# through the "ambient data" tuple. + + planet = 'Mars' # For the moment we have 'Earth' and 'Mars' + chord_length = 0.4 # In metres + altitude = -7.5 # In Kilometres above sea level or reference altitude + speed_parameter = 'speed' # 'speed' or 'mach' + speed_value = 28.284 # Value of the previous magnitude (speed - m/s) + ambient_data = (planet, chord_length, altitude, speed_parameter, speed_value) + + + +####--------Secondary Variables------ +#-- Analysis domain + + start_alpha_angle = 0 + finish_alpha_angle = 20 + alpha_angle_step = 2 + + aero_domain = (start_alpha_angle, finish_alpha_angle, alpha_angle_step) + + +#-- Optimization objectives + + lift_coefficient_weight = 0.0 + efficiency_weight = 1.0 + + weighting_parameters = (lift_coefficient_weight, efficiency_weight) + +#-- Final results options + + num_winners = 3 + vdraw_winners = True + vdraw_polars = True + vdraw_evolution = True + vcompare_naca_standard = True + vcompare_naca_custom = True #Work in progress + vcreate_report = True #Work in progress + + end_options = (vdraw_winners, vdraw_polars, vdraw_evolution, + vcompare_naca_standard, vcompare_naca_custom, + vcreate_report, + ambient_data, aero_domain) + + + + all_parameters = (airfoils_per_generation, total_generations, num_parent, + num_winners, weighting_parameters, end_options ) + + + finish(all_parameters) \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/algoritmo/ender_report.py b/aeropy/Xfoil_Interaction/algoritmo/ender_report.py new file mode 100644 index 0000000..1f520a2 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/ender_report.py @@ -0,0 +1,351 @@ +''' + +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 os +import interfaz as interfaz +import numpy as np +import algoritmo.initial as initial +import algoritmo.genetics as genetics +import IPython.nbformat.current as nbf + + +###### Some commented code below is used to build and test this module + + +# +####---------Primary Variables----- +# +# +#airfoils_per_generation = 10 +#total_generations = 10 +#num_parent = 3 +# +## We give the algorithm the conditions at wich we want to optimize our airofil +## through the "ambient data" tuple. +# +#planet = 'Mars' # For the moment we have 'Earth' and 'Mars' +#chord_length = 0.2 # In metres +#altitude = -7.5 # In Kilometres above sea level or reference altitude +#speed_parameter = 'speed' # 'speed' or 'mach' +#speed_value = 30 # Value of the previous magnitude +#ambient_data = (planet, chord_length, altitude, speed_parameter, speed_value) +# +# +# +#####--------Secondary Variables------ +##-- Analysis domain +#start_alpha_angle = 0 +#finish_alpha_angle = 15 +#alpha_angle_step = 1 +# +#aero_domain = (start_alpha_angle, finish_alpha_angle, alpha_angle_step) +##-- Optimization objectives +# +#lift_coefficient_weight = 0.3 +#efficiency_weight = 0.7 +# +#weighting_parameters = (lift_coefficient_weight, efficiency_weight) +# +##-- Final results options +# +#num_winners = 3 +#draw_winners = True +#draw_polars = True +#draw_evolution = True +#compare_naca_standard = True +#compare_naca_custom = True +#create_report = True +# +#end_options = (draw_winners, draw_polars, draw_evolution, +# compare_naca_standard, compare_naca_custom, +# create_report, +# ambient_data, aero_domain) +# +# +# +#all_parameters = (airfoils_per_generation, total_generations, num_parent, +# num_winners, weighting_parameters, end_options ) +# +# +# +##### FIN PARA PROBAR + +def create_report(all_parameters): + + generation = all_parameters[1] + num_winners = all_parameters[3] + weights = all_parameters[4] + end_options = all_parameters[5] + + + must_draw_winners = end_options[0] + must_draw_polars = end_options[1] + must_draw_evolution = end_options[2] + must_compare_naca_standard = end_options[3] + must_compare_naca_custom = end_options[4] + must_create_report = end_options[5] + ambient_data = end_options[6] + aero_domain = end_options[7] + compare = must_compare_naca_standard + + try: + import urllib + url = 'https://avatars3.githubusercontent.com/u/6246900?v=3&s=200' + logo_root = os.path.join('graphics','logo.png') + logo_complete_root = os.path.join('results', logo_root) + urllib.request.urlretrieve(url, logo_complete_root) + logo = True + except: + logo = False + logo_root = '' + + + + report_root = os.path.join('results','results.ipynb') + file_root = os.path.join('results','ejemplo.py') + + try: + os.remove(report_root) + except : + pass + + try: + os.remove(file_root) + except : + pass + text_01_a = '''[AeroPython](https://github.com/AeroPython) \n ''' + + text_01_d = '''

Aeropython Xfoil Genetic + Algorithm Report

''' + + if logo: + text_01 = text_01_a + text_01_b + text_01_c + text_01_d + else: + text_01 = text_01_d + + cell_01 = nbf.new_text_cell('heading', source = text_01) + + text_02 = '''Thank you for using our Genetic algorithm! Now we will present a + short report displaying some data generated by the algorithm''' + + cell_02 = nbf.new_text_cell('markdown', source= text_02) + + text_03 = ('''The algorithm has searched for an optimal airfoil for flying in **''' + + str(ambient_data[0]) + '''**, at an altitude of **''' + + str(ambient_data[2]) + '''** Kilometers over the sea level or + reference level. The chord of the profile is **''' + + str(ambient_data[1]) + ''' metres** long, and the **''' + + str(ambient_data[3]) +'** has a value of **' +str(ambient_data[4])) + if (str(ambient_data[3]) == 'speed'): + text_03 = text_03 + ' meters per second' + text_03 = text_03 + '**.' + + cell_03 = nbf.new_text_cell('markdown', source= text_03) + + text_04 =('''The airfoils were tested in XFoil between **''' + + str(aero_domain[0]) + '** and **' + str(aero_domain[1]) + + '** derees, increasing in increments of **' + str(aero_domain[2]) + + '** degrees. The ' + str(num_winners) + ''' best airfoils of the + last generation, or "winners", were aditionally tested between ''' + + str(aero_domain[0] - 1) + ' and ' + str(aero_domain[1] + 5) + + ' derees, in increments of ' + str(aero_domain[2] / 2) + + ' degrees.''') + + cell_04 = nbf.new_text_cell('markdown', source= text_04) + + text_05 =('''In this execution of the algorithm, optimizing the Lift + Coefficient had a weight of **''' + str(weights[0]) + + '''**, while the efficiency , or Lift Coefficient + divided by Drag Coefficient, had a weight of **''' + + str(weights[1]) + ' **.\n\n' + + 'The algorithm ran along **' + str(generation) + + ' generations**, each composed of **' + str(all_parameters[0]) + + ' airfoils**. For every generation, the best **' + str(all_parameters[2]) + + '''** airfoils were selected and used as parents of the + next generation.''' ) + numbers = [all_parameters[1],all_parameters[0],all_parameters[2]] + + text_05 = text_05 + '\n\n' + analyze_algorithm_numbers(numbers) + + text_05 = text_05 +('''\n\nIf you want more details about how this algorithm works, check + the documentation that can be found at + https://docs.google.com/presentation/d/1_78ilFL-nbuN5KB5FmNeo-EIZly1PjqxqIB-ant-GfM/edit?usp=sharing''') + + + cell_05 = nbf.new_text_cell('markdown', source= text_05) + + text_06 = ('''Now, let's see the results, starting with the values + of Cl and efficiency achieved''') + if compare: + text_06 = text_06 + ''', along with the results for a typical airfoil + (NACA 5615) in order to compare their quality''' + text_06 = text_06 + ':\n\n' + cell_06 = nbf.new_text_cell('markdown', source= text_06) + + + outputs = [nbf.new_output(output_type="stream", stream="stdout", output_text="a"), + nbf.new_output(output_type="text", output_text="b"), + nbf.new_output(output_type="stream", stream="stdout", output_text="c"), + nbf.new_output(output_type="stream", stream="stdout", output_text="d"), + nbf.new_output(output_type="stream", stream="stderr", output_text="e"), + nbf.new_output(output_type="stream", stream="stderr", output_text="f"), + nbf.new_output(output_type="png", output_png='Zw==')] # g + out = nbf.new_output(output_type="application/pdf") + out['application/pdf'] = 'aA==' # h + outputs.append(out) + cells=[cell_01, + cell_02, + cell_03, + cell_04, + cell_05, + cell_06, + nbf.new_code_cell(input="$ e $", prompt_number=1,outputs=outputs), + nbf.new_text_cell('markdown', source="$ e $"), + nbf.new_text_cell('markdown', source="Esto es una prueba"), + nbf.new_text_cell('heading', source="Esto es una segunda prueba")] + worksheets = [nbf.new_worksheet(cells=cells)] + + + nb = nbf.new_notebook(name = 'hola', worksheets = worksheets ) + + nbf.write(nb, open(report_root, 'x'), 'ipynb') + + + +def analyze_algorithm_numbers(numbers): + generation = numbers[0] + num_pop = numbers[1] + num_parents = numbers[2] + + + amount = [] + + if generation < 3 : + amount.append('very low') + elif generation < 6 : + amount.append('low') + elif generation < 15 : + amount.append('intermediate') + elif generation < 30 : + amount.append('high') + else: + amount.append('very high') + + + if num_pop < 5 : + amount.append('very low') + elif num_pop < 10 : + amount.append('low') + elif num_pop < 30 : + amount.append('intermediate') + elif num_pop < 70 : + amount.append('high') + else: + amount.append('very high') + + + if num_parents < 2 : + amount.append('very low') + elif num_parents < 3 : + amount.append('low') + elif num_parents < 5 : + amount.append('intermediate') + elif num_parents < 7 : + amount.append('high') + else: + amount.append('very high') + + text = ('According to these data, the number of **generations** is **' + + str(amount[0]) + '** , the number of **airfoils per generation** is **' + + str(amount[1]) + '** and the number of **parents** is **'+ str(amount[2]) + + '**.\n') + dicc = {'very low' : 1, + 'low' : 2, + 'intermediate' : 4, + 'high' : 8, + 'very high' : 16} + + parents_size = dicc[amount[2]] + airfoils_size = dicc[amount[1]] + generation_size = dicc[amount[0]] + size = airfoils_size * generation_size + + if size < 4: + text_2 = ('These specifications can just prove that the algorithm' + + ''' works, but won't produce any interesting data.''') + elif size < 16: + text_2 = ('These specifications can serve to test the algorithm,' + + '''and a little evolution can probably be seen, but won't''' + + 'produce any really useful data.') + elif size < 64: + text_2 = ('These specifications can work fine for a first approach.' + + ''' The algorithm should be producing a decent set of''' + + ' solutions, while in a moderate amount of time.') + elif size < 252: + text_2 = ('These specifications should provide a fairly good result.' + + ''' If the Reynolds number is high, (a very large'''+ + ''' airfoil flying very fast or very low), maybe you'''+ + ''' couldn't go a lot further without spending an impractical''' + + ' amount of time.' ) + else : + text_2 = ('''you are working with an amount of data that will'''+ + ' probably wring the capacity of this algorithm up to its limits.' + + ' We hope that the amount of time that this calculations probably ' + + 'have required will be rewarded with an excellent set of airfoils' + + ' specificaly optimized to your conditions. If you wanted to go' + + ' further in the design of airfoils, maybe you should search for a ' + + 'more specific software or a professional solution.') + + + if (parents_size < airfoils_size): + text_3 = (' For your next run, maybe you should **increase the number' + + ' of parents.** Too few parents may reduce the freedom' + + ' of the algorithm to try new ways to get better.') + elif (parents_size > airfoils_size): + text_3 = (' For your next run, maybe you should **decrease the number' + + ' of parents.** Too much parents may left little room in'+ + ' the airfoil number to properly generate new airfoils' + + ' in each generation.') + else: + text_3 = (' For this amount of profiles, the **amount of parents is' + + ' equilibrated.**') + + if (generation_size < airfoils_size): + text_4 = (' For your next run, maybe you should **increase the number' + + ' of generations**. The fine adjustment of the airfoil' + + ' happens in the later generations, and winners probably' + + ''' won't be well polished until at least generation number 15.''') + elif (generation_size > airfoils_size): + text_4 = (' For your next run, maybe you should **increase the number' + + ' of airfoils** per generation. Too few airfoils may result '+ + ' in a fine adjustment of a not so good local solution. '+ + 'For that amount of generations, a larger number of airfoils' + + ' would allow the algorithm to explore a wider range of ' + + 'possible solutions, specially in the early generations.') + else: + text_4 = (' For this amount of generations, the **amount of airfoils' + + ' per generation is equilibrated.**') + + return text + text_2 + text_3 + text_4 + +#### Uncomment to continue testing +# +#create_report(all_parameters) \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/algoritmo/genetics.py b/aeropy/Xfoil_Interaction/algoritmo/genetics.py new file mode 100644 index 0000000..c6fa395 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/genetics.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +''' +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 Genetic Step Subprograme. After the XFoil analysis of the +'N' generation, this subprogramme will calculate the population of the +'N+1' generation. + +''' + + + + +import os +import numpy as np +import algoritmo.analyze as analyze +import algoritmo.selection as selection +import algoritmo.cross as cross +import algoritmo.mutation as mutation + + + +def genetic_step(generation,population, num_parent, weights): + '''Returns the genome of the (n+1)generation + ''' +# file_parent_name = 'generation'+ str(generation) + '.txt' +# genome_parent_root = os.path.join('genome', file_parent_name) +# genome = np.loadtxt(genome_parent_root, skiprows=1) +# num_pop = genome.shape[0] + pop_len = len(population) + + analyze.pop_analice(generation, population, num_parent) + analyze.score(generation, population, weights) + parents = selection.selection(population, num_parent) + children = cross.cross(parents, pop_len, generation) + mutation.mutation(children, generation, num_parent) + + + + + file_name = 'generation'+ str(generation + 1) + '.txt' + genome_root = os.path.join('genome', file_name) + title = 'generation' + str(generation + 1) + 'genome' + + results_name = 'results_data_generation'+ str(generation) + '.txt' + results_root = os.path.join('results', 'data', results_name ) + results_title = 'generation' + str(generation) + 'results:' + try: + os.remove(genome_root) + except : + pass + + try: + os.remove(results_root) + except : + pass + + if os.path.exists(genome_root): + os.remove(genome_root) + + genome_file = open(genome_root, mode = 'x') + results_file = open(results_root, mode = 'x') + genome_file.write(title + '\n') + results_file.write(results_title + '\n') + results_file.write('Cl max Eficciency Score\n') + + for airfoil in population: + line = '' + for gen in airfoil.genome: + line = line + str(gen) +' ' + line = line + '\n' + genome_file.write(line) + result = str(airfoil.clmax) + ' ' + result += str(airfoil.maxefic) + ' ' + result += str(airfoil.score) + '\n' + results_file.write(result) + + genome_file.close() + results_file.close() + return children \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/algoritmo/initial.py b/aeropy/Xfoil_Interaction/algoritmo/initial.py new file mode 100644 index 0000000..8c91c4a --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/initial.py @@ -0,0 +1,121 @@ +''' + +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 creates the initial population for the genetic algorithm. +It does so by adding a random deviation to a default profile genome. + +''' + + + + +import os +import numpy as np +import algoritmo.testing as test +import shutil + + +class Airfoil(object): + '''This object represents a single airfoil''' + results_root = '' + + def __init__(self, genome): + self.genome = genome + def copy_data(self, gen, num): + airfoil_name = 'gen' + str(gen) + 'airf' + str(num) + new_root = os.path.join("aerodata","data" + airfoil_name + '.txt') + shutil.copy(self.results_root, new_root) + self.results_root = new_root + self.name = airfoil_name + def copy_winner(self, num): + new_root = os.path.join("results","data", 'winner '+str(num) + 'aerodata.txt') + shutil.copy(self.results_root, new_root) + self.results_root = new_root + + +def start_pop(pop_num): + '''Creates a randomly generated population of the size (pop_num) + ''' + + population = [] + + + genes = np.array([150*np.pi/180, #ang s1 + 0.2, #dist s1 + 0.5, #x 1 + 0.12, #y 1 + 0, #ang 1 + 0.2, #dist b1 + 0.2, #dist c1 + 0.1, #dist a1 + 0.05, #dist a2 + 0.4, #x 2 + -0.07, #y 2 + 5*np.pi/180, #ang 2 + 0.2, #dist b2 + 0.2, #dist c2 + 190*np.pi/180, #ang s2 + 0.2]) #dist s2 + + + + + gen_deviation = np.array([10*np.pi/180, #ang s1 + 0.15, #dist s1 + 0.2, #x 1 + 0.1, #y 1 + 10*np.pi/180, #ang 1 + 0.2, #dist b1 + 0.2, #dist c1 + 0.1, #dist a1 + 0.1, #dist a2 + 0.4, #x 2 + 0.05, #y 2 + 10*np.pi/180, #ang 2 + 0.2, #dist b2 + 0.2, #dist c2 + 30*np.pi/180, #ang s2 + 0.15]) #dist s2 + + + for airfoil in range(pop_num): + genome = np.zeros(16) + deviation = 0.7 * np.random.randn(16) * gen_deviation + genome = genes + deviation + while not(test.airfoil_test(genome)): + + # Here we check that our airfoil actually makes sense + deviation = 0.7 * np.random.randn(16) * gen_deviation + genome = genes + deviation + airfoil = Airfoil(genome) + population.append(airfoil) + + + + genome_root = os.path.join('genome','generation0.txt') + title = 'generation 0 genome' + + try: + os.remove(genome_root) + except : + pass + archivo = open(genome_root, mode = 'x') + archivo.write(title + '\n') + + for airfoil in population: + line = '' + genome = airfoil.genome + for gen in genome: + line = line + str(gen) +' ' + line = line + '\n' + archivo.write(line) + + return population + + diff --git a/aeropy/Xfoil_Interaction/algoritmo/interfaz.py b/aeropy/Xfoil_Interaction/algoritmo/interfaz.py new file mode 100644 index 0000000..e7411d8 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/interfaz.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +''' +Created on Fri Feb 20 20:57:16 2015 + +@author: Juan Luis Cano, Alberto Lorenzo, 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 requires from the main program a serie of profile genomes. +The number of profiles is "num_pop" +This subprogram, first, uses the script "transcript.py" in order to translate +the genomes into a series of points that Xfoil can understand. + +Then, sends them to Xfoil, and ask it to analize them. + +At last, it sends back to the main program the results obtained. + +''' + + + +import subprocess +import os +import algoritmo.transcript as trans +import numpy as np +import algoritmo.ambient as ambient + + +def xfoil_calculate_profile(generation, airfoil_number, + airfoil, ambient_data, aero_domain): + + '''Starts Xfoil and analyzes the given airfoil. Saves the results. + ''' + + airfoil_name = 'gen' + str(generation) + 'airf' + str(airfoil_number) + geo_file_name = 'airfoil' + str(airfoil_number) + '.txt' + airfoil_root = os.path.join('airfoils','gen' + str(generation) , geo_file_name ) + data_root = os.path.join("aerodata","data" + airfoil_name + '.txt') + airfoil.name = airfoil_name + airfoil.results_root = data_root + + aerodynamics = ambient.aero_conditions(ambient_data) + + + commands = ['load', + airfoil_root, + 'oper', + 'mach ' + str(aerodynamics[0]), + 're ' + str(aerodynamics[1]), + 'visc', + 'pacc', + data_root, + '', + 'aseq', + str(aero_domain[0]), + str(aero_domain[1]), + str(aero_domain[2]), + '', + 'quit'] + + + genome = airfoil.genome + airf_points = trans.decode_genome(genome) + + + try: + os.remove(airfoil_root) + except : + pass + try: + os.remove(data_root) + except : + pass + + + archivo = open(airfoil_root, mode = 'x') + archivo.write(airfoil_name + '\n') + + for i in np.arange(0,100,1): + texto = str(round(airf_points[i,0],6)) + texto += ' ' + str(round(airf_points[i,1],6)) +'\n' + archivo.write(texto) + archivo.close() + + p = subprocess.Popen(["xfoil",], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + + for command in commands: + p.stdin.write((command + '\n').encode()) + + + p.stdin.close() + for line in p.stdout.readlines(): + print(line.decode(), end='') + +def xfoil_calculate_population(generation, population, + ambient_data, aero_domain, + num_parent = 0): + '''Given a generation number and ambiental conditions, reads the file + which contains the genome information of the generation, and uses xfoil to + analyze each airfoil. + ''' + + + pop_len = len(population) + + airfoils_folder = os.path.join('airfoils', 'gen' + str(generation)) + if not os.path.exists(airfoils_folder): + os.makedirs(airfoils_folder) + + for airfoil_number in range(num_parent, pop_len): + xfoil_calculate_profile(generation, airfoil_number, + population[airfoil_number], + ambient_data, aero_domain) + + diff --git a/aeropy/Xfoil_Interaction/algoritmo/main.py b/aeropy/Xfoil_Interaction/algoritmo/main.py new file mode 100644 index 0000000..eee958d --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/main.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +''' + +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 os + +import algoritmo.interfaz as interfaz +import algoritmo.initial as initial +import algoritmo.genetics as genetics +import algoritmo.ender as ender + +#First, the main function is defined. This allows us to call it from a future +#different starting file (like a PyQT graphic interface). + +#If this is the starting file, it will call the main function with the +#parameters described below. + + + +def main_program(all_parameters): + '''The main function of the program, calls ir order all the rest''' + + airfoils_per_generation = all_parameters[0] + total_generations = all_parameters[1] + num_parent = all_parameters[2] +# num_winners = all_parameters[3] + weighting_parameters = all_parameters[4] +# end_options = all_parameters[5] + ambient_data = all_parameters[6] + aero_domain = all_parameters[7] + + +###--- Creating work directories + + if not os.path.exists('aerodata'): + os.makedirs('aerodata') + + if not os.path.exists('genome'): + os.makedirs('genome') + + if not os.path.exists('results'): + os.makedirs('results') + + if not os.path.exists(os.path.join('results', 'graphics')): + os.makedirs(os.path.join('results', 'graphics')) + + if not os.path.exists(os.path.join('results', 'data')): + os.makedirs(os.path.join('results', 'data')) + + + + +####--- Starting the population, analysis of the starting population + + + generation = 0 + + + population = initial.start_pop(airfoils_per_generation) + + interfaz.xfoil_calculate_population(generation, population, + ambient_data, aero_domain) + + +####--- Genetic Algorithm + + + for generation in range(0,total_generations): + + + population = genetics.genetic_step(generation, population, + num_parent, weighting_parameters) + + interfaz.xfoil_calculate_population(generation + 1, population, + ambient_data, aero_domain, + num_parent) + + + + ender.finish(population, all_parameters) + + + +#If this is the file from which we are starting, we define here the parameters: + +if __name__ == '__main__': + +####---------Primary Variables----- + + + import interfaz + import initial + import genetics + import ender + + airfoils_per_generation = 4 + total_generations = 4 + num_parent = 2 + +# We give the algorithm the conditions at wich we want to optimize our airofil +# through the "ambient data" tuple. + + planet = 'Mars' # For the moment we have 'Earth' and 'Mars' + chord_length = 0.1 # In metres + altitude = -7.5 # In Kilometres above sea level or reference altitude + speed_parameter = 'speed' # 'speed' or 'mach' + speed_value = 18 # Value of the previous magnitude (speed - m/s) + ambient_data = (planet, chord_length, altitude, speed_parameter, speed_value) + + + +####--------Secondary Variables------ +#-- Analysis domain + + start_alpha_angle = 0 + finish_alpha_angle = 20 + alpha_angle_step = 2 + + aero_domain = (start_alpha_angle, finish_alpha_angle, alpha_angle_step) + + +#-- Optimization objectives + + lift_coefficient_weight = 0.3 + efficiency_weight = 0.7 + + weighting_parameters = (lift_coefficient_weight, efficiency_weight) + +#-- Final results options + + num_winners = 3 + draw_winners = True + draw_polars = True + draw_evolution = True + compare_naca_standard = True + compare_naca_custom = True #Work in progress + create_report = True #Work in progress + + end_options = (draw_winners, draw_polars, draw_evolution, + compare_naca_standard, compare_naca_custom, + create_report) + + + + all_parameters = (airfoils_per_generation, total_generations, num_parent, + num_winners, weighting_parameters, end_options, + ambient_data, aero_domain ) + + + main_program(all_parameters) \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/algoritmo/mutation.py b/aeropy/Xfoil_Interaction/algoritmo/mutation.py new file mode 100644 index 0000000..4be62bd --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/mutation.py @@ -0,0 +1,64 @@ +''' + +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 Mutation subprogramme. Its objective is to add diversity +to the population, in order to avoid stagnation in a not good solution. + +Intensity of the mutation is propotional to the square root of the generation +number, in order to refine the search for an optimal solution. + +The parents of the generation are left unmutated in order to avoid the chance +of decreasing the quality obtained in a previous step. + +''' + + + + +import numpy as np +import algoritmo.testing as test + +def mutation(children, generation, num_parent): + '''Given a genome, mutates it in order to have a diverse population + ''' + coeff = 0.5 / (1 + generation**0.5) + gen_deviation = np.array([10*np.pi/180, #ang s1 + 0.15, #dist s1 + 0.2, #x 1 + 0.1, #y 1 + 10*np.pi/180, #ang 1 + 0.2, #dist b1 + 0.2, #dist c1 + 0.1, #dist a1 + 0.1, #dist a2 + 0.4, #x 2 + 0.05, #y 2 + 10*np.pi/180, #ang 2 + 0.2, #dist b2 + 0.2, #dist c2 + 30*np.pi/180, #ang s2 + 0.15]) #dist s2 + + len_pop = len(children) + + #children_n = children.copy() + + for airfoil_num in range(num_parent, len_pop): + deviation = coeff * np.random.randn(16) * gen_deviation + airfoil = children[airfoil_num] + proposed_genome = airfoil.genome + deviation + n = 0 + while not(test.airfoil_test(proposed_genome)): + n = n + 1 + deviation = coeff * np.random.randn(16) * gen_deviation + proposed_genome = airfoil.genome + deviation + print('mutating into viable airfoil, try #',n) + airfoil.genome = proposed_genome + + diff --git a/aeropy/Xfoil_Interaction/algoritmo/selection.py b/aeropy/Xfoil_Interaction/algoritmo/selection.py new file mode 100644 index 0000000..ead62d6 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/selection.py @@ -0,0 +1,39 @@ +''' + +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 selection subprogramme. Given a population and a score array, +it just selects the (num_parent) best and ignores the rest. + +This has its own subprogramme because other genetic algorithms have +other selection parameters, so this can be easily found and changed here +if you wanted. + +''' + + + + +import numpy as np + + +def selection(population, num_parent): + '''Select the genome of the (num_parent) best airfoils. + ''' + pop_len = len(population) + score = np.zeros(pop_len) + for airfoil_num in range(pop_len): + airfoil = population[airfoil_num] + score[airfoil_num] = airfoil.score + invscore = 1- score + positions = np.argsort(invscore) + parents =[] + for parent_count in range(num_parent): + parents.append(population[positions[parent_count]]) + + return parents \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/algoritmo/testing.py b/aeropy/Xfoil_Interaction/algoritmo/testing.py new file mode 100644 index 0000000..1463922 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/testing.py @@ -0,0 +1,113 @@ +''' + +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 testing subprogramme. Its objective is to check if a +new airfoil genome would make a real, viable one. In ordr to do so, +it runs 3 test in increasing level of complexity. + +''' + + + + +import algoritmo.transcript as transcript +import numpy as np +from scipy import interpolate + +def increasing_test(array): + '''Checks wheter a certain array is monotone increasing. + ''' + numele = np.shape(array)[0] + if np.array_equal(np.argsort(array), np.arange(0, numele, 1)): + return True + else: + return False + + +def decreasing_test(array): + '''Checks wheter a certain array is monotone decreasing. + ''' + numele = np.shape(array)[0] + if np.array_equal(np.argsort(array), np.arange(numele-1, -1, -1)): + return True + else: + return False + +def x_coordinate_test(genome): + '''Cheks wheter the x coordinate of the points that a given genome + would generate get smaller at the airfoil upper side, as we go + from the tail to the nose, and viceversa for the lower side. + ''' + perfil = transcript.decode_genome(genome[:]) + test1 = decreasing_test(perfil[0:25, 0]) + test2 = decreasing_test(perfil[25:50, 0]) + test3 = increasing_test(perfil[50:75, 0]) + test4 = increasing_test(perfil[75:100, 0]) + + if (test1 * test2 * test3 * test4): + return True + else: + return False + +def test_simple (genome): + '''Test simple genome characteristics, such as the upper surface point + being above the lower surface point. + ''' + test1 = (genome[14] - genome [0]) > (5*np.pi/180) + test2 = genome[3] > genome[10] + test3 = genome[7] > 0.01 + test4 = genome[8] > 0.01 + test5 = genome[1] > 0.01 + test6 = genome[15] > 0.01 + test7 = genome[0] > (np.pi * 0.6) + test8 = genome[0] < (np.pi * 1.4) + + if (test1 * test2 * test3 * test4 * test5 * test6 * test7 * test8): + return True + else: + return False + +def collision_test(genome): + '''Interpolates two curves for the upper and the lower surfaces. + Then, check that the distance between them is larger than 0.01 times + the chord. + ''' + + + perfil = transcript.decode_genome(genome[:]) + + extrax = np.append(perfil[40:25:-1,0],perfil[24:5:-1,0]) + extray = np.append(perfil[40:25:-1,1],perfil[24:5:-1,1]) + + intrax = np.append(perfil[60:74,0],perfil[75:98,0]) + intray = np.append(perfil[60:74,1],perfil[75:98,1]) + + extrados = interpolate.InterpolatedUnivariateSpline(extrax, extray, k=1) + intrados = interpolate.InterpolatedUnivariateSpline(intrax, intray, k=1) + + ver = np.linspace(0.1, 0.9, 50) + very = extrados(ver) - intrados(ver) + + return (very > 0.01 ). all() + +def airfoil_test(genome): + '''Run 3 test of increasing complexity to detect inviable airfoils + ''' + if test_simple(genome): + if x_coordinate_test(genome): + if collision_test(genome): + return True + else: + return False + else: + return False + else: + return False + + \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/algoritmo/transcript.py b/aeropy/Xfoil_Interaction/algoritmo/transcript.py new file mode 100644 index 0000000..2b0c576 --- /dev/null +++ b/aeropy/Xfoil_Interaction/algoritmo/transcript.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Feb 20 20:57:16 2015 + +@author: Siro Moreno + +This is the python script that contains those functions that are necesary +in order to get a xfoil-compatible description of a profile from the +genetic information provided by the interface script. + +When this spript is run independly, and the final lines ar de-commented, + will return an draw of a profile calculated from an example genome. + +When used inside the algorithm, the interface will import these functions +and call the "decode_genome" function with an array of genes. The function shall +return an xfoil-compatible description. + +The complete Genetic Algorithm here used is explained at: +https://docs.google.com/presentation/d/1_78ilFL-nbuN5KB5FmNeo-EIZly1PjqxqIB-ant-GfM/edit?usp=sharing + +""" + + +import numpy as np + + +def bernstein(u): + '''This function returns a 4x2 array. Both columns are equal. + In each row, the value of a 3rd degree Bernstein polinome is + calculated for the given value of the parameter u. + This structure allows the result array to be multiplied element + by element with another 4x2 array containing the coordinates + of the control points. + ''' + b = np.zeros([4,2]) + b[0,:] = (1-u)**3 + b[1,:] = (u * (1-u)**2)*3 + b[2,:] = (u**2 * (1-u) )*3 + b[3,:] = u**3 + + return b + + +def point_slope(a,dist,ang): + '''Given a point "a", calculates the coordinates of another one + placed at a distance "dist" in the direction"ang" (in radians). + ''' + punto = np.array(a)+ np.array(dist,dist)* [np.cos(ang),np.sin(ang)] + return punto + + +def point_generator(genes): + '''This function is the first step decoding the profile genome: + it generates the 13x2 coordinates of the points used in the 4 + bezier curves that describe the profile. + ''' + points = np.zeros([13,2]) + points[0,:] = [1,0] + points[1,:] = point_slope([1,0],genes[1],genes[0]) + points[2,:] = point_slope([genes[2],genes[3]],genes[5],genes[4]) + points[3,:] = [genes[2],genes[3]] + points[4,:] = point_slope([genes[2],genes[3]],genes[6],genes[4]+np.pi) + points[5,:] = [0, genes[7]] + points[6,:] = [0,0] + points[7,:] = [0, -genes[8]] + points[8,:] = point_slope([genes[9],genes[10]], genes[12], genes[11]+np.pi) + points[9,:] = [genes[9],genes[10]] + points[10,:] = point_slope([genes[9],genes[10]], genes[13], genes[11]) + points[11,:] = point_slope([1,0], genes[15], genes[14]) + points[12,:] = [1,0] + return points + +def bezier(num, control_points): + '''This function calculates a Bezier curve using as control + points those given in the imput, with a resolution of "num" points. + ''' + + parameter_u = np.linspace(0,1,num) + curva = np.zeros([num,2]) + + for counter in np.arange(num): + _ = bernstein(parameter_u[counter])*control_points + curva[counter,] = sum (_) + return curva + +def profile(num, control_points): + + '''This is the second stage of the decoding process. + This will return a line made of 4 bezier curves whose control points + are given in the imput. + Each curve will have a number of points equal to "num", so the total + number of points will be 4 * num''' + + perfil = np.zeros([(4*num), 2]) + + perfil[0:num,:] = bezier(num,control_points[0:4,:]) + perfil[num:2*num,:] = bezier(num,control_points[3:7,:]) + perfil[2*num:3*num,:] = bezier(num,control_points[6:10,:]) + perfil[3*num:4*num,:] = bezier(num,control_points[9:13,:]) + + return perfil +def decode_genome(genome): + '''Calculates the x and y coordinates of 100 points describing + an airfoil from the given genome + ''' + num = 25 + epsilon = 0.001 + profile_points = profile(num, point_generator(genome)) + + profile_points[0, 1] = epsilon + profile_points[4*num-1,1] = -epsilon + return profile_points + +''' +The following code contains an example and will be used with test purposes only, +when this script is run alone, and won't be used in the standard function of the +genetic algorithm. +''' +if __name__ == '__main__': + + import matplotlib.pyplot as plt + + + genes = np.array([150*np.pi/180, #ang s1 + 0.2, #dist s1 + 0.5, #x 1 + 0.12, #y 1 + 0, #ang 1 + 0.2, #dist b1 + 0.2, #dist c1 + 0.1, #dist a1 + 0.05, #dist a2 + 0.4, #x 2 + 0.05, #y 2 + 5*np.pi/180, #ang 2 + 0.2, #dist b2 + 0.2, #dist c2 + 160*np.pi/180, #ang s2 + 0.2]) #dist s2 + + + + perfil = decode_genome(genes) + + + plt.figure(num=None, figsize=(18, 6), dpi=80, facecolor='w', edgecolor='k') + plt.plot(perfil[:,0],perfil[:,1]) + plt.gca().set_aspect(1) diff --git a/aeropy/Xfoil_Interaction/launcher-express.py b/aeropy/Xfoil_Interaction/launcher-express.py new file mode 100644 index 0000000..1e56ead --- /dev/null +++ b/aeropy/Xfoil_Interaction/launcher-express.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Apr 26 18:52:51 2016 + +@author: Usuario +""" + +import algoritmo.main as main + +airfoils_per_generation = 3 +total_generations = 2 +num_parent = 1 + +# We give the algorithm the conditions at wich we want to optimize our airofil +# through the "ambient data" tuple. + +planet = 'Mars' # For the moment we have 'Earth' and 'Mars' +chord_length = 0.1 # In metres +altitude = -7.5 # In Kilometres above sea level or reference altitude +speed_parameter = 'speed' # 'speed' or 'mach' +speed_value = 18 # Value of the previous magnitude (speed - m/s) +ambient_data = (planet, chord_length, altitude, speed_parameter, speed_value) + + + +####--------Secondary Variables------ +#-- Analysis domain + +start_alpha_angle = 0 +finish_alpha_angle = 20 +alpha_angle_step = 2 + +aero_domain = (start_alpha_angle, finish_alpha_angle, alpha_angle_step) + + +#-- Optimization objectives + +lift_coefficient_weight = 0.3 +efficiency_weight = 0.7 + +weighting_parameters = (lift_coefficient_weight, efficiency_weight) + +#-- Final results options + +num_winners = 1 +draw_winners = True +draw_polars = True +draw_evolution = True +compare_naca_standard = True +compare_naca_custom = True #Work in progress +create_report = True #Work in progress + +end_options = (draw_winners, draw_polars, draw_evolution, + compare_naca_standard, compare_naca_custom, + create_report) + + + +all_parameters = (airfoils_per_generation, total_generations, num_parent, + num_winners, weighting_parameters, end_options, + ambient_data, aero_domain ) + + +main.main_program(all_parameters) \ No newline at end of file diff --git a/aeropy/Xfoil_Interaction/launcher.py b/aeropy/Xfoil_Interaction/launcher.py new file mode 100644 index 0000000..9fbba99 --- /dev/null +++ b/aeropy/Xfoil_Interaction/launcher.py @@ -0,0 +1,190 @@ +''' + +Created on Wed Nov 4 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 a command prompt launcher. + +''' + + + + +import algoritmo.main as main + + + +####---------Primary Variables----- + + +def cuestionario(min_x): + correct = False + while not correct: + x = input() + try: + x = int(x) + except: + print('Por favor, introduzca un número válido') + continue + if x < min_x: + print('número demasiado bajo, vuelva a intentarlo') + else: + correct = True + return x +def cuestionario_float(min_x): + correct = False + while not correct: + x = input() + try: + x = float(x) + except: + print('Por favor, introduzca un número válido') + continue + if x < min_x: + print('número demasiado bajo, vuelva a intentarlo') + else: + correct = True + return x + +def cuestionario_s_n(): + correct = False + while not correct: + x = input() + try: + x = str(x) + except: + continue + if x == 's' or x == 'y' or x == 'S' or x == 'Y': + r = True + correct = True + elif x =='n' or x =='N': + r = False + correct = True + else: + print('Por favor, responda con "s" o "n"') + correct = False + return r + +print() +print('--- Parámetros del algoritmo genético ---') +print() +print('introduzca un número de perfiles por generación') +airfoils_per_generation = cuestionario(2) +print('introduzca un número de generaciones') +total_generations = cuestionario(2) +print('introduzca un número de parents') +num_parent = cuestionario(1) + +# We give the algorithm the conditions at wich we want to optimize our airofil +# through the "ambient data" tuple. +print() +print('--- Parámetros ambientales ---') +print() + +print('¿En qué planeta desea otimizar? Mars / Earth') +correct = False +while not correct: + x = input() + try: + x = str(x) + except: + continue + if not(x == 'Earth' or x == 'Mars'): + print('planeta incorrecto, elija "Earth" o "Mars"') + else: + correct = True +planet = x # For the moment we have 'Earth' and 'Mars' +print('introduzca la longitud de la cuerda (en metros)') +chord_length = cuestionario_float(0) # In metres +print('introduzca la altitud de vuelo (en kilómetros)') +if planet == 'Earth': + altitude = cuestionario_float(0) +else: + altitude = cuestionario_float(-7.5)# In Kilometres above sea level or reference altitude +print('¿Cómo va a introducir la velocidad? speed / mach') +correct = False +while not correct: + x = input() + try: + x = str(x) + except: + continue + if not(x == 'speed' or x == 'mach'): + print('parámetro de velocidad incorrecto') + else: + correct = True +speed_parameter = x # 'speed' or 'mach' +if speed_parameter == 'speed': + print('Introduzca el valor de la velocidad (m/s)') + speed_value = cuestionario_float(0) # Value of the previous magnitude (speed - m/s) +else: + print('Introduzca el valor del número de Mach') + speed_value = cuestionario_float(0) +ambient_data = (planet, chord_length, altitude, speed_parameter, speed_value) + + + +####--------Secondary Variables------ +#-- Analysis domain +print() +print('--- Dominio Aerodinámico ---') +print() +print('Ángulo de ataque inicial(grados):') +start_alpha_angle = cuestionario_float(-20) +print('Ángulo de ataque final:') +finish_alpha_angle = cuestionario_float(start_alpha_angle) +print('Espaciado entre ángulos de ataque de estudio:') +alpha_angle_step = cuestionario_float(0) + +aero_domain = (start_alpha_angle, finish_alpha_angle, alpha_angle_step) + + +#-- Optimization objectives +print() +print('--- Objetivos de optimización ---') +print() +print('Importancia del coeficiente de sustentación:') +lift_coefficient_weight = cuestionario_float(-1) +print('Importancia de la eficiencia aerodinámica:') +efficiency_weight = cuestionario_float(-1) + +weighting_parameters = (lift_coefficient_weight, efficiency_weight) + +#-- Final results options +print() +print('--- Dominio Aerodinámico ---') +print() +print('Número de ganadores del algoritmo') +num_winners = cuestionario(1) +print('Desea generar dibujos de los ganadores? (s/n)') +draw_winners = cuestionario_s_n() +print('Desea generar gráficas de las polares? (s/n)') +draw_polars = cuestionario_s_n() +print('Desea generar gráficas de la evolución? (s/n)') +draw_evolution = cuestionario_s_n() +print('Desea comparar los resultados con dos perfiles NACA?') +compare_naca_standard = cuestionario_s_n() +print() +compare_naca_custom = True #Work in progress +create_report = True #Work in progress + +end_options = (draw_winners, draw_polars, draw_evolution, + compare_naca_standard, compare_naca_custom, + create_report) + + + +all_parameters = (airfoils_per_generation, total_generations, num_parent, + num_winners, weighting_parameters, end_options, + ambient_data, aero_domain ) + +print() +print('--- Iniciando algoritmo ---') +print() + +main.main_program(all_parameters) + diff --git a/aeropy/Xfoil_Interaction/minimal_interface.py b/aeropy/Xfoil_Interaction/minimal_interface.py new file mode 100644 index 0000000..82af40a --- /dev/null +++ b/aeropy/Xfoil_Interaction/minimal_interface.py @@ -0,0 +1,20 @@ +import subprocess +import sys + +commands = ['naca 6715', + 'oper', + 'mach 0.2', + 're 3500', + 'alfa 3'] + +p = subprocess.Popen(["xfoil.exe",], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + +for command in commands: + p.stdin.write((command + '\n').encode()) + +p.stdin.write("\nquit\n".encode()) +p.stdin.close() +for line in p.stdout.readlines(): + print(line.decode(), end='') diff --git a/aeropy/Xfoil_Interaction/result_drawer.ipynb b/aeropy/Xfoil_Interaction/result_drawer.ipynb new file mode 100644 index 0000000..e6d8709 --- /dev/null +++ b/aeropy/Xfoil_Interaction/result_drawer.ipynb @@ -0,0 +1,463 @@ +{ + "metadata": { + "name": "", + "signature": "sha256:2e17f8dbbc7de9bb287937db82fd91e0cac6bf14382e705ed36a69f75776b27e" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 1 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import os\n", + "from transcript import *" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 2 + }, + { + "cell_type": "heading", + "level": 1, + "metadata": {}, + "source": [ + "Result Drawer for the Xfoil Genetic Algorithm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here are described some useful functions that can help you to graphically display your results.\n", + "\n", + "Place this notebook in the same folder that the Xfoil and the genetic algorithm files.\n", + "\n", + "\n", + "Here is a list of the included functions:\n", + "\n", + "**profile_read_aero** (generation, profile_number)\n", + "\n", + "Searches for the file of a certain profile generated by the algorithm and return the Cd and Cl.\n", + "\n", + "**profile_read_aero_generic** (root)\n", + "\n", + "Reads a file located in root (ej: calculations/naca6715.txt) and return the Cd and Cl.\n", + "\n", + "**profile_read_alpha** (generation, profile_number)\n", + "\n", + "Searches for the file of a certain profile generated by the algorithm and return the alpha angle and Cl.\n", + "\n", + "**profile_read_alpha_generic** (root)\n", + "\n", + "Reads a file located in root (ej: calculations/naca6715.txt) and return the alpha angle and Cl.\n", + "\n", + "**drawing**(generation, profile_number) \n", + "\n", + "Searches for the file of a certain profile generated by the algorithm and draws it with matplotlib.\n", + "\n", + "**drawing_bezier**(generation, profile_number)\n", + "\n", + "Searches for the file of a certain profile generated by the algorithm and draws it, along with its bezier points with matplotlib. Useful for understanding the genome of a certain profile.\n", + "\n", + "**drawing_polar**(generation, profile_number)\n", + "\n", + "Plots the Cd against the Cl for a certain profile generated by the algorithm\n", + "\n", + "**drawing_polar_compare**(generation_imput, profile_number_imput)\n", + "\n", + "Plots the Cd against the Cl for some profiles generated by the algorithm. Both 'generation_imput' and 'profile_number_imput' must be numpy arrays of the same dimension.\n", + "\n", + "**drawing_alpha_compare**(generation_imput, profile_number_imput)\n", + "\n", + "Plots the Cl against the alpha for some profiles generated by the algorithm. Both 'generation_imput' and 'profile_number_imput' must be numpy arrays of the same dimension.\n", + "\n", + "**drawing_polar_compare_generic**(profileroots) and **drawing_alpha_compare_generic**(root)\n", + "\n", + "Do the same as the equivalent non-generic functions, but must be fed with an array of file directions (ej: calculations/naca6715.txt)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def profile_read_aero (generation, profile_number): \n", + " profile_name = 'gen' + str(generation) + 'prof' + str(profile_number)\n", + " data_root = \"aerodata\\data\" + profile_name + '.txt'\n", + " datos = np.loadtxt(data_root, skiprows=12, usecols=[1,2])\n", + " \n", + " read_dim = np.array(datos.shape)\n", + " if ((read_dim.shape[0]) != 2):\n", + " return np.array ([0,0])\n", + " else:\n", + " return datos" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 3 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def profile_read_aero_generic (root): \n", + " profile_name = root\n", + " data_root = root\n", + " datos = np.loadtxt(data_root, skiprows=12, usecols=[1,2])\n", + " \n", + " read_dim = np.array(datos.shape)\n", + " if ((read_dim.shape[0]) != 2):\n", + " return np.array ([0,0])\n", + " else:\n", + " return datos" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 4 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def profile_read_alpha (generation, profile_number): \n", + " profile_name = 'gen' + str(generation) + 'prof' + str(profile_number)\n", + " data_root = \"aerodata\\data\" + profile_name + '.txt'\n", + " datos = np.loadtxt(data_root, skiprows=12, usecols=[0,1])\n", + " \n", + " read_dim = np.array(datos.shape)\n", + " if ((read_dim.shape[0]) != 2):\n", + " return np.array ([0,0])\n", + " else:\n", + " return datos" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 5 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def profile_read_alpha_generic (root): \n", + " \n", + " \n", + " datos = np.loadtxt(root, skiprows=12, usecols=[0,1])\n", + " \n", + " read_dim = np.array(datos.shape)\n", + " if ((read_dim.shape[0]) != 2):\n", + " return np.array ([0,0])\n", + " else:\n", + " return datos" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 6 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def drawing(generation, profile_number): \n", + " \n", + " profile_root = 'profiles\\gen' + str(generation) + '\\profile' + str(profile_number) + '.txt'\n", + " \n", + " profile_name = 'Generaci\u00f3n ' + str(generation) + ', perfil ' + str(profile_number)\n", + " \n", + " datos = np.loadtxt(profile_root, skiprows=3, usecols=[0,1])\n", + " \n", + " plt.figure(num=None, figsize=(15, 5), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.title (profile_name)\n", + " plt.ylim(-0.15, 0.15)\n", + " plt.xlim(-0.05, 1.05)\n", + " plt.plot(datos[:,0], datos[:,1])\n", + " plt.gca().set_aspect(1)\n", + " \n", + " \n", + " if not os.path.exists('graficos'):\n", + " os.makedirs('graficos')\n", + " \n", + " \n", + " nombre_grafico = 'graficos\\gen' + str(generation) + 'profile' + str(profile_number) + '.png'\n", + " plt.savefig(nombre_grafico)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 7 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def drawing_bezier(generation, profile_number): \n", + " \n", + " profile_root = 'profiles\\gen' + str(generation) + '\\profile' + str(profile_number) + '.txt'\n", + " \n", + " profile_name = 'Generaci\u00f3n ' + str(generation) + ', perfil ' + str(profile_number)\n", + " \n", + " genome_root = 'genome\\generation'+ str(generation) + '.txt'\n", + " \n", + " genome_matrix = np.loadtxt(genome_root, skiprows=1)\n", + " \n", + " genome = genome_matrix[profile_number-1,:]\n", + " \n", + " bezier_points = generador_puntos(genome)\n", + " \n", + " datos = np.loadtxt(profile_root, skiprows=3, usecols=[0,1])\n", + " \n", + " plt.figure(num=None, figsize=(15, 5), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.title (profile_name)\n", + " plt.ylim(-0.15, 0.15)\n", + " plt.xlim(-0.05, 1.05)\n", + " plt.scatter(bezier_points[:,0],bezier_points[:,1])\n", + " plt.plot(datos[:,0], datos[:,1])\n", + " plt.plot(bezier_points[0:2 , 0] , bezier_points[0:2 , 1])\n", + " plt.plot(bezier_points[2:5 , 0] , bezier_points[2:5 , 1])\n", + " plt.plot(bezier_points[5:8 , 0] , bezier_points[5:8 , 1])\n", + " plt.plot(bezier_points[8:11 , 0] , bezier_points[8:11 , 1])\n", + " plt.plot(bezier_points[11:13 , 0] , bezier_points[11:13 , 1])\n", + " plt.gca().set_aspect(1)\n", + " \n", + " \n", + " if not os.path.exists('graficos'):\n", + " os.makedirs('graficos')\n", + " \n", + " \n", + " nombre_grafico = 'graficos\\gen' + str(generation) + 'profile' + str(profile_number) + 'bezierpoints.png'\n", + " plt.savefig(nombre_grafico)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 8 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def drawing_polar(generation, profile_number): \n", + " \n", + " profile_root = 'profiles\\gen' + str(generation) + '\\profile' + str(profile_number) + '.txt'\n", + " \n", + " profile_name = 'Generaci\u00f3n ' + str(generation) + ', perfil ' + str(profile_number)\n", + " \n", + " datos = profile_read_aero(generation, profile_number)\n", + " \n", + " plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.title (profile_name)\n", + " plt.plot(datos[:,0], datos[:,1])\n", + " \n", + " \n", + " if not os.path.exists('graficos'):\n", + " os.makedirs('graficos')\n", + " \n", + " \n", + " nombre_grafico = 'graficos\\gen' + str(generation) + 'profile' + str(profile_number) + 'polar.png'\n", + " plt.savefig(nombre_grafico)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 9 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def drawing_polar_compare(generation_imput, profile_number_imput): \n", + " \n", + " num_prof = generation_imput.shape[0]\n", + " profile_name = 'Generaci\u00f3n ' + str(generation_imput) + ', perfil ' + str(profile_number_imput)\n", + " plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.title (profile_name)\n", + " \n", + " for i in np.arange(0,num_prof,1):\n", + " generation = generation_imput[i]\n", + " profile_number = profile_number_imput[i]\n", + " \n", + " profile_root = 'profiles\\gen' + str(generation) + '\\profile' + str(profile_number) + '.txt'\n", + " \n", + " \n", + " \n", + " datos = profile_read_aero(generation, profile_number)\n", + " \n", + " \n", + " \n", + " plt.plot(datos[:,0], datos[:,1])\n", + " \n", + " \n", + " if not os.path.exists('graficos'):\n", + " os.makedirs('graficos')\n", + " \n", + " \n", + " nombre_grafico = 'graficos\\gen' + str(generation_imput) + 'profile' + str(profile_number_imput) + 'polar.png'\n", + " plt.savefig(nombre_grafico)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 10 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def drawing_alpha_compare(generation_imput, profile_number_imput): \n", + " \n", + " num_prof = generation_imput.shape[0]\n", + " profile_name = 'Generaci\u00f3n ' + str(generation_imput) + ', perfil ' + str(profile_number_imput)\n", + " plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.title (profile_name)\n", + " \n", + " ideal = np.array([[0,0],\n", + " [15, (15*np.pi/180)*np.pi*2]])\n", + " \n", + " plt.plot(ideal[:,0], ideal[:,1])\n", + " for i in np.arange(0,num_prof,1):\n", + " generation = generation_imput[i]\n", + " profile_number = profile_number_imput[i]\n", + " \n", + " profile_root = 'profiles\\gen' + str(generation) + '\\profile' + str(profile_number) + '.txt'\n", + " \n", + " \n", + " \n", + " datos = profile_read_alpha(generation, profile_number)\n", + " \n", + " \n", + " \n", + " plt.plot(datos[:,0], datos[:,1])\n", + " \n", + " \n", + " if not os.path.exists('graficos'):\n", + " os.makedirs('graficos')\n", + " \n", + " \n", + " nombre_grafico = 'graficos\\gen' + str(generation_imput) + 'profile' + str(profile_number_imput) + 'alpha.png'\n", + " plt.savefig(nombre_grafico)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 11 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def drawing_polar_compare_generic(profileroots): \n", + " \n", + " num_prof = profileroots.shape[0]\n", + " \n", + " plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.rc('font', size = 20)\n", + " plt.title ('Profile comparison')\n", + " \n", + " for i in np.arange(0,num_prof,1):\n", + " \n", + " \n", + " profile_root = profileroots[i]\n", + " \n", + " \n", + " \n", + " datos = profile_read_aero_generic(profile_root)\n", + " \n", + " \n", + " \n", + " plt.plot(datos[:,0], datos[:,1], label = profile_root)\n", + " \n", + " \n", + " if not os.path.exists('graficos'):\n", + " os.makedirs('graficos')\n", + " \n", + " plt.legend(loc = 2, fontsize =14) \n", + " plt.grid()\n", + " plt.minorticks_on()\n", + " plt.xlabel('Lift coefficient') \n", + " plt.ylabel('Drag coefficient')\n", + " nombre_grafico = 'graficos\\comparepolar.png'\n", + " plt.savefig(nombre_grafico)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 12 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def drawing_alpha_compare_generic(root): \n", + " \n", + " num_prof = root.shape[0]\n", + " \n", + " plt.figure(num=None, figsize=(15, 8), dpi=80, facecolor='w', edgecolor='k')\n", + " plt.rc('font', size = 20)\n", + " plt.title ('profile comparison')\n", + " \n", + " ideal = np.array([[0,0],\n", + " [15, (15*np.pi/180)*np.pi*2]])\n", + " \n", + " plt.plot(ideal[:,0], ideal[:,1], label = ' ideal, cl alpha = 2$\\pi$')\n", + " for i in np.arange(0,num_prof,1):\n", + " \n", + " profile_root = root[i]\n", + " \n", + " \n", + " \n", + " datos = profile_read_alpha_generic(profile_root)\n", + " \n", + " \n", + " \n", + " plt.plot(datos[:,0], datos[:,1], label = profile_root)\n", + " \n", + " \n", + " if not os.path.exists('graficos'):\n", + " os.makedirs('graficos')\n", + " \n", + " plt.legend(loc = 2, fontsize =14) \n", + " plt.grid() \n", + " plt.minorticks_on()\n", + " plt.xlabel('Attack angle') \n", + " plt.ylabel('Lift coefficient')\n", + " nombre_grafico = 'graficos\\compare_alpha.png'\n", + " plt.savefig(nombre_grafico)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 13 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 13 + } + ], + "metadata": {} + } + ] +} \ No newline at end of file