summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Peter <blendoit@gmail.com>2019-06-21 17:06:06 -0700
committerMarius Peter <blendoit@gmail.com>2019-06-21 17:06:06 -0700
commit3bba98c9869e07fede355d83fd2498996e8a54fc (patch)
tree3aaa175c573d174fcd5c72df11a78152e5837163
parent43d8b1a738eef259063febd9e5c591a10610a043 (diff)
structure of Evaluator mimicks structure of Coordinates
-rw-r--r--creator.py51
-rw-r--r--evaluator.py79
-rw-r--r--main.py20
3 files changed, 77 insertions, 73 deletions
diff --git a/creator.py b/creator.py
index 15c090f..a4f67ad 100644
--- a/creator.py
+++ b/creator.py
@@ -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):
diff --git a/main.py b/main.py
index e5360ff..821bbb6 100644
--- a/main.py
+++ b/main.py
@@ -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))
Copyright 2019--2024 Marius PETER