diff options
-rw-r--r-- | creator.py | 51 | ||||
-rw-r--r-- | evaluator.py | 79 | ||||
-rw-r--r-- | main.py | 20 |
3 files changed, 77 insertions, 73 deletions
@@ -13,6 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. + import sys import os.path import numpy as np @@ -29,7 +30,7 @@ global parent class Coordinates: - """ + ''' All airfoil components need the following: Parameters: @@ -41,7 +42,7 @@ class Coordinates: * Save component coordinates to file specified in main.py So, all component classes inherit from class Coordinates. - """ + ''' def __init__(self, chord, semi_span): # Global dimensions @@ -69,18 +70,18 @@ class Coordinates: return type(self).__name__ def print_info(self, round): - """ + ''' Print all the component's coordinates to the terminal. This function's output is piped to the 'save_coord' function below. - """ - print('============================') - print(' CREATOR DATA ') + ''' + print(20 * '-') + print(' CREATOR DATA ') print('Component:', str(self)) print('Chord length:', self.chord) print('Semi-span:', self.semi_span) print('Mass:', self.mass) - print('============================') + print(20 * '-') print('x_u the upper x-coordinates:\n', np.around(self.x_u, round)) print('z_u the upper z-coordinates:\n', np.around(self.z_u, round)) print('x_l the lower x-coordinates:\n', np.around(self.x_l, round)) @@ -88,15 +89,15 @@ class Coordinates: return None def save_info(self, save_dir_path, number): - """ + ''' Save all the object's coordinates (must be full path). - """ + ''' - file_name = '{}_{}.txt'.format(self, number) + file_name = '{}_{}.txt'.format(str(self).lower(), number) full_path = os.path.join(save_dir_path, file_name) try: with open(full_path, 'w') as sys.stdout: - self.print_info(2) + self.print_info(6) # This line required to reset behavior of sys.stdout sys.stdout = sys.__stdout__ print('Successfully wrote to file {}'.format(full_path)) @@ -115,7 +116,7 @@ class Coordinates: class Airfoil(Coordinates): - """This class enables the creation of a single NACA airfoil.""" + '''This class enables the creation of a single NACA airfoil.''' def __init__(self): global parent @@ -128,7 +129,7 @@ class Airfoil(Coordinates): self.y_c = [] def add_naca(self, naca_num): - """ + ''' This function generates geometry for our chosen NACA airfoil shape. The nested functions perform the required steps to generate geometry, and can be called to solve the geometry y-coordinate for any 'x' input. @@ -139,7 +140,7 @@ class Airfoil(Coordinates): Return: None - """ + ''' # Variables extracted from 'naca_num' argument passed to the function self.naca_num = naca_num @@ -150,9 +151,9 @@ class Airfoil(Coordinates): p_c = p * self.chord def get_camber(x): - """ + ''' Returns camber y-coordinate from 1 'x' along the airfoil chord. - """ + ''' y_c = float() if 0 <= x < p_c: y_c = (m / (p ** 2)) * (2 * p * (x / self.chord) @@ -164,9 +165,9 @@ class Airfoil(Coordinates): return (y_c * self.chord) def get_thickness(x): - """ + ''' Returns thickness from 1 'x' along the airfoil chord. - """ + ''' y_t = 5 * t * self.chord * ( + 0.2969 * sqrt(x / self.chord) - 0.1260 * (x / self.chord) @@ -222,14 +223,14 @@ class Airfoil(Coordinates): class Spar(Coordinates): - """Contains a single spar's location.""" + '''Contains a single spar's location.''' global parent def __init__(self): super().__init__(parent.chord, parent.semi_span) def add_coord(self, airfoil_coord, spar_x): - """ + ''' Add a single spar at the % chord location given to function. Parameters: @@ -239,7 +240,7 @@ class Spar(Coordinates): Return: None - """ + ''' # Airfoil surface coordinates # unpacked from 'coordinates' (list of lists in 'Coordinates'). x_u = airfoil_coord[0] @@ -266,7 +267,7 @@ class Spar(Coordinates): class Stringer(Coordinates): - """Contains the coordinates of all stringers.""" + '''Contains the coordinates of all stringers.''' global parent def __init__(self): @@ -275,7 +276,7 @@ class Stringer(Coordinates): def add_coord(self, airfoil_coord, spar_coord, stringer_u_1, stringer_u_2, stringer_l_1, stringer_l_2): - """ + ''' Add equally distributed stringers to four airfoil locations (upper nose, lower nose, upper surface, lower surface). @@ -289,7 +290,7 @@ class Stringer(Coordinates): Returns: None - """ + ''' # Airfoil surface coordinates # unpacked from 'coordinates' (list of lists in 'Coordinates'). @@ -359,7 +360,7 @@ class Stringer(Coordinates): def plot(airfoil, spar, stringer): - """This function plots the elements passed as arguments.""" + '''This function plots the elements passed as arguments.''' # Plot chord x_chord = [0, airfoil.chord] diff --git a/evaluator.py b/evaluator.py index 6965895..59d233a 100644 --- a/evaluator.py +++ b/evaluator.py @@ -13,13 +13,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. + import sys import os.path import numpy as np from math import sin, cos, atan, sqrt -class Airfoil: +class Evaluator: '''Performs structural evaluations for the airfoil passed as argument.''' def __init__(self, airfoil): @@ -28,7 +29,7 @@ class Airfoil: self.chord = airfoil.chord self.semi_span = airfoil.semi_span # mass and area - self.mass_total = float() + self.mass_total = airfoil.mass + airfoil.spar.mass + airfoil.stringer.mass self.mass_dist = [] self.lift_rectangular = [] @@ -37,22 +38,20 @@ class Airfoil: self.drag = [] - def __str__(self): - return type(self).__name__ - def print_info(self, round): - """ + ''' Print all the component's evaluated data to the terminal. This function's output is piped to the 'save_data' function below. - """ - print('============================') - print(' EVALUATOR DATA ') - print('Evaluating:', str(self.airfoil)) + ''' + + print(22 * '-') + print(' EVALUATOR DATA ') + print('Evaluating:', self.airfoil) print('Chord length:', self.chord) print('Semi-span:', self.semi_span) print('Total airfoil mass:', self.mass_total) - print('============================') + print(22 * '-') print('Rectangular lift:\n', np.around(self.lift_rectangular, round)) print('Elliptical lift:\n', np.around(self.lift_elliptical, round)) print('Combined lift:\n', np.around(self.lift, round)) @@ -61,15 +60,13 @@ class Airfoil: return None def save_info(self, save_dir_path, number): - """ - Save all the object's coordinates (must be full path). - """ + '''Save all the object's coordinates (must be full path).''' - file_name = '{}_{}.txt'.format(self, number) + file_name = 'airfoil_{}_eval.txt'.format(number) full_path = os.path.join(save_dir_path, file_name) try: with open(full_path, 'w') as sys.stdout: - self.print_info(2) + self.print_info(6) # This line required to reset behavior of sys.stdout sys.stdout = sys.__stdout__ print('Successfully wrote to file {}'.format(full_path)) @@ -79,53 +76,57 @@ class Airfoil: 'Was the full path passed to the function?') return None - def get_mass_total(airfoil): - total_mass = airfoil.mass + airfoil.spar.mass + airfoil.stringer.mass - return total_mass + # def get_mass_total(airfoil): + # total_mass = airfoil.mass + airfoil.spar.mass + airfoil.stringer.mass + # return total_mass # All these functions take integer arguments and return lists. - def get_lift_rectangular(airfoil, lift): - L_prime = [lift / (airfoil.semi_span * 2) - for x in range(airfoil.semi_span)] + def get_lift_rectangular(self, lift): + L_prime = [lift / (self.semi_span * 2) + for x in range(self.semi_span)] return L_prime - def get_lift_elliptical(airfoil, L_0): - L_prime = [L_0 * sqrt(1 - (y / airfoil.semi_span) ** 2) - for y in range(airfoil.semi_span)] + def get_lift_elliptical(self, L_0): + L_prime = [L_0 * sqrt(1 - (y / self.semi_span) ** 2) + for y in range(self.semi_span)] return L_prime - def get_lift(rectangular, elliptical): - F_z = [(rectangular[_] + elliptical[_]) / 2 - for _ in range(len(rectangular))] + def get_lift_total(self): + F_z = [(self.lift_rectangular[_] + self.lift_elliptical[_]) / 2 + for _ in range(len(self.lift_rectangular))] return F_z - def get_mass_distribution(airfoil, total_mass): - F_z = [total_mass / airfoil.semi_span - for x in range(0, airfoil.semi_span)] + def get_mass_distribution(self, total_mass): + F_z = [total_mass / self.semi_span + for x in range(0, self.semi_span)] return F_z - def get_drag(airfoil, drag): + def get_drag(self, drag): # Transform semi-span integer into list - semi_span = [x for x in range(0, airfoil.semi_span)] + semi_span = [x for x in range(0, self.semi_span)] # Drag increases after 80% of the semi_span - cutoff = round(0.8 * airfoil.semi_span) + cutoff = round(0.8 * self.semi_span) # Drag increases by 25% after 80% of the semi_span F_x = [drag for x in semi_span[0:cutoff]] F_x.extend([1.25 * drag for x in semi_span[cutoff:]]) return F_x - def evaluate(self): - self.drag = self.get_drag(self.airfoil, 10) + def analysis(self): + ''' + Perform all analysis calculations and store in class instance. + ''' + + self.drag = self.get_drag(10) self.lift_rectangular = self.get_lift_rectangular(10) self.lift_elliptical = self.get_lift_elliptical(15) - self.lift = self.get_lift(self.lift_rectangular, self.lift_elliptical) + self.lift = self.get_lift_total() - self.mass_total = self.get_mass_total() - self.mass_dist = self.get_mass_distribution(self.total_mass) + # self.mass_total = self.get_mass_total() + self.mass_dist = self.get_mass_distribution(self.mass_total) return None # def get_centroid(airfoil): @@ -50,7 +50,7 @@ def main(): # TODO: imperial + metric unit setting creator.Coordinates(CHORD_LENGTH, SEMI_SPAN) - # Interate through all wings in population. + # Interate through all wings in population, creating and evaluating them. for _ in range(1, POP_SIZE + 1): # Create airfoil instance @@ -80,14 +80,16 @@ def main(): # creator.plot(af, af.spar, af.stringer) # Save component info - # af.save_info(SAVE_PATH, _) - # af.spar.save_info(SAVE_PATH, _) - # af.stringer.save_info(SAVE_PATH, _) - - # evaluator.Airfoil instance contains the results of the airfoil analysis. - # The analysis itself takes place in the evaluator.py module. - eval = evaluator.Airfoil(af) - eval.print_info(2) + af.save_info(SAVE_PATH, _) + af.spar.save_info(SAVE_PATH, _) + af.stringer.save_info(SAVE_PATH, _) + + # evaluator.Evaluator instance contains airfoil analysis results. + eval = evaluator.Evaluator(af) + # The analysis is performed in the evaluator.py module. + eval.analysis() + eval.print_info(2) + eval.save_info(SAVE_PATH, _) # Print final execution time print("--- %s seconds ---" % (time.time() - start_time)) |