summaryrefslogtreecommitdiff
path: root/creator
diff options
context:
space:
mode:
Diffstat (limited to 'creator')
-rw-r--r--creator/__init__.py4
-rw-r--r--creator/__init__.pycbin311 -> 0 bytes
-rw-r--r--creator/base.py93
-rw-r--r--creator/fuselage.py0
-rw-r--r--creator/log_base.txt0
-rw-r--r--creator/propulsion.py0
-rw-r--r--creator/wing.py312
7 files changed, 0 insertions, 409 deletions
diff --git a/creator/__init__.py b/creator/__init__.py
deleted file mode 100644
index 818c7b2..0000000
--- a/creator/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from . import base
-from . import fuselage
-from . import propulsion
-from . import wing
diff --git a/creator/__init__.pyc b/creator/__init__.pyc
deleted file mode 100644
index 022f493..0000000
--- a/creator/__init__.pyc
+++ /dev/null
Binary files differ
diff --git a/creator/base.py b/creator/base.py
deleted file mode 100644
index 130add3..0000000
--- a/creator/base.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""The base.py module contains parent classes for components."""
-
-import numpy as np
-import os.path
-import random
-import logging
-
-import creator
-
-logging.basicConfig(filename='log_base.txt',
- level=logging.DEBUG,
- format='%(asctime)s - %(levelname)s - %(message)s')
-
-
-class Aircraft:
- """This class tracks all sub-components and is fed to the evaluator."""
- name = None
- fuselage = None
- propulsion = None
- wing = None
- properties = {}
-
- def __init__(self):
- self.results = {}
-
- def __str__(self):
- return self.name
-
- @classmethod
- def from_default(cls):
- aircraft = creator.base.Aircraft()
- aircraft.name = 'default_aircraft_' + str(random.randrange(1000, 9999))
- airfoil = creator.wing.Airfoil(aircraft, 'default_airfoil')
- airfoil.add_naca(2412)
- soar1 = creator.wing.Spar(airfoil, 'default_spar_1', 0.30)
- soar2 = creator.wing.Spar(airfoil, 'default_spar_2', 0.60)
- stringer = creator.wing.Stringer(airfoil, 'default_stringer')
- return aircraft
-
-
-class Component:
- """Basic component providing coordinates, tools and a component tree."""
- def __init__(self, parent, name):
- self.parent = parent
- self.name = name
- self.x = np.array([])
- self.z = np.array([])
- self.y = np.array([])
- self.material = None
- self.mass = float()
- self.properties = {}
-
- def __str__(self):
- return self.name
-
- def info_print(self, round):
- """Print all the component's coordinates to the terminal."""
- name = f' CREATOR DATA FOR {str(self).upper()} '
- num_of_dashes = len(name)
- print(num_of_dashes * '-')
- print(name)
- for k, v in self.__dict__.items():
- if type(v) is not np.ndarray:
- print(f'{k}:\n', v)
- print(num_of_dashes * '-')
- for k, v in self.__dict__.items():
- if type(v) is np.ndarray:
- print(f'{k}:\n', np.around(v, round))
- return None
-
- def info_save(self,
- save_path='/home/blendux/Projects/Aircraft_Studio/save'):
- """Save all the object's coordinates (must be full path)."""
- file_name = f'{self.name}_info.txt'
- full_path = os.path.join(save_path, file_name)
- try:
- with open(full_path, 'w') as f:
- for k, v in self.__dict__.items():
- if type(v) is not np.ndarray:
- f.write(f'{k}=\n')
- f.write(str(v))
- f.write("\n")
- # print(num_of_dashes * '-')
- for k, v in self.__dict__.items():
- if type(v) is np.ndarray:
- f.write(f'{k}=\n')
- f.write(str(v))
- f.write("\n")
- logging.debug(f'Successfully wrote to file {full_path}')
- except IOError:
- print(f'Unable to write {file_name} to specified directory.\n',
- 'Was the full path passed to the function?')
- return None
diff --git a/creator/fuselage.py b/creator/fuselage.py
deleted file mode 100644
index e69de29..0000000
--- a/creator/fuselage.py
+++ /dev/null
diff --git a/creator/log_base.txt b/creator/log_base.txt
deleted file mode 100644
index e69de29..0000000
--- a/creator/log_base.txt
+++ /dev/null
diff --git a/creator/propulsion.py b/creator/propulsion.py
deleted file mode 100644
index e69de29..0000000
--- a/creator/propulsion.py
+++ /dev/null
diff --git a/creator/wing.py b/creator/wing.py
deleted file mode 100644
index 01aa6ff..0000000
--- a/creator/wing.py
+++ /dev/null
@@ -1,312 +0,0 @@
-"""
-The wing.py module contains class definitions for and various components
-we add to an airfoil (spars, stringers, and ribs).
-
-Classes:
- Airfoil: instantiated with class method to provide coordinates to heirs.
- Spar: inherits from Airfoil.
- Stringer: also inherits from Airfoil.
-
-Functions:
- plot_geom(airfoil): generates a 2D plot of the airfoil & any components.
-"""
-
-import logging
-import numpy as np
-from math import sin, cos, atan
-import bisect as bi
-import matplotlib.pyplot as plt
-
-import creator.base as base
-import resources.materials as mt
-
-
-class Airfoil(base.Component):
- """This class represents a single NACA airfoil.
-
- The coordinates are saved as two np.arrays
- for the x- and z-coordinates. The coordinates start at
- the leading edge, travel over the airfoil's upper edge,
- then loop back to the leading edge via the lower edge.
-
- This method was chosen for easier future exports
- to 3D CAD packages like SolidWorks, which can import such
- geometry as coordinates written in a CSV file.
- """
- def __init__(self,
- parent,
- name,
- chord=68,
- semi_span=150,
- material=mt.aluminium):
- super().__init__(parent, name)
- parent.wing = self
- if chord > 20:
- self.chord = chord
- else:
- self.chord = 20
- logging.debug('Chord too small, using minimum value of 20.')
- parent
- self.semi_span = semi_span
- self.material = material
- self.spars = []
- self.stringers = []
-
- def add_naca(self, naca_num=2412):
- """Generate surface geometry for a NACA airfoil.
-
- The nested functions perform the required steps to generate geometry,
- and can be called to solve the geometry y-coordinate for any 'x' input.
- Equation coefficients were retrieved from Wikipedia.org.
-
- Parameters:
- naca_num: 4-digit NACA wing
-
- Return:
- None
- """
- self.naca_num = naca_num
- # Variables extracted from naca_num argument passed to the function
- m = int(str(naca_num)[0]) / 100
- p = int(str(naca_num)[1]) / 10
- t = int(str(naca_num)[2:]) / 100
- # x-coordinate of maximum camber
- p_c = p * self.chord
-
- def get_camber(x):
- """
- Returns camber z-coordinate from 1 'x' along the airfoil chord.
- """
- z_c = float()
- if 0 <= x < p_c:
- z_c = (m / (p**2)) * (2 * p * (x / self.chord) -
- (x / self.chord)**2)
- elif p_c <= x <= self.chord:
- z_c = (m /
- ((1 - p)**2)) * ((1 - 2 * p) + 2 * p *
- (x / self.chord) - (x / self.chord)**2)
- return (z_c * self.chord)
-
- def get_thickness(x):
- """Return thickness from 1 'x' along the airfoil chord."""
- x = 0 if x < 0 else x
- z_t = 5 * t * self.chord * (+0.2969 *
- (x / self.chord)**0.5 - 0.1260 *
- (x / self.chord)**1 - 0.3516 *
- (x / self.chord)**2 + 0.2843 *
- (x / self.chord)**3 - 0.1015 *
- (x / self.chord)**4)
- return z_t
-
- def get_theta(x):
- dz_c = float()
- if 0 <= x < p_c:
- dz_c = ((2 * m) / p**2) * (p - x / self.chord)
- elif p_c <= x <= self.chord:
- dz_c = (2 * m) / ((1 - p)**2) * (p - x / self.chord)
-
- theta = atan(dz_c)
- return theta
-
- def get_coord_u(x):
- x = x - get_thickness(x) * sin(get_theta(x))
- z = get_camber(x) + get_thickness(x) * cos(get_theta(x))
- return (x, z)
-
- def get_coord_l(x):
- x = x + get_thickness(x) * sin(get_theta(x))
- z = get_camber(x) - get_thickness(x) * cos(get_theta(x))
- return (x, z)
-
- # Densify x-coordinates 10 times for first 1/4 chord length
- x_chord_25_percent = round(self.chord / 4)
- x_chord = [i / 10 for i in range(x_chord_25_percent * 10)]
- x_chord.extend(i for i in range(x_chord_25_percent, self.chord + 1))
- # Generate our airfoil skin geometry from previous sub-functions
- self.x_c = np.array([])
- self.z_c = np.array([])
- # Upper surface and camber line
- for x in x_chord:
- self.x_c = np.append(self.x_c, x)
- self.z_c = np.append(self.z_c, get_camber(x))
- self.x = np.append(self.x, get_coord_u(x)[0])
- self.z = np.append(self.z, get_coord_u(x)[1])
- # Lower surface
- for x in x_chord[::-1]:
- self.x = np.append(self.x, get_coord_l(x)[0])
- self.z = np.append(self.z, get_coord_l(x)[1])
- return None
-
-
-class Spar(base.Component):
- """Contains a single spar's data."""
- def __init__(self, parent, name, loc_percent=0.30, material=mt.aluminium):
- """Set spar location as percent of chord length."""
- super().__init__(parent, name)
- parent.spars.append(self)
- self.material = material
- self.cap_area = float()
- # bi.bisect_left: returns index of first value in parent.x > loc
- # This ensures that spar geom intersects with airfoil geom.
- loc = loc_percent * parent.chord
- # Spar upper coordinates
- spar_u = bi.bisect_left(parent.x, loc) - 1
- self.x = np.append(self.x, parent.x[spar_u])
- self.z = np.append(self.z, parent.z[spar_u])
- # Spar lower coordinates
- spar_l = bi.bisect_left(parent.x[::-1], loc)
- self.x = np.append(self.x, parent.x[-spar_l])
- self.z = np.append(self.z, parent.z[-spar_l])
- return None
-
- def set_cap_area(self, cap_area):
- self.cap_area = cap_area
- return None
-
- def set_mass(self, mass):
- self.mass = mass
- return None
-
-
-class Stringer(base.Component):
- """Contains the coordinates of all stringers."""
- def __init__(self,
- parent,
- name,
- den_u_1=4,
- den_u_2=4,
- den_l_1=4,
- den_l_2=4):
- """Add equally distributed stringers to four airfoil locations
- (upper nose, lower nose, upper surface, lower surface).
-
- den_u_1: upper nose number of stringers
- den_u_2: upper surface number of stringers
- den_l_1: lower nose number of stringers
- den_l_2: lower surface number of stringers
- """
- super().__init__(parent, name)
- parent.stringers = self
- self.x_start = []
- self.x_end = []
- self.z_start = []
- self.z_end = []
- self.diameter = float()
- self.area = float()
-
- # Find distance between leading edge and first upper stringer
- # interval = self.parent.spars[0].x[0] / (den_u_1 + 1)
- interval = 2
- # initialise first self.stringer_x at first interval
- x = interval
- # Add upper stringers from leading edge until first spar.
- for _ in range(0, den_u_1):
- # Index of the first value of airfoil.x > x
- i = bi.bisect_left(self.parent.x, x)
- self.x = np.append(self.x, self.parent.x[i])
- self.z = np.append(self.z, self.parent.z[i])
- x += interval
- # Add upper stringers from first spar until last spar
- interval = (self.parent.spars[-1].x[0] -
- self.parent.spars[0].x[0]) / (den_u_2 + 1)
- x = interval + self.parent.spars[0].x[0]
- for _ in range(0, den_u_2):
- i = bi.bisect_left(self.parent.x, x)
- self.x = np.append(self.x, self.parent.x[i])
- self.z = np.append(self.z, self.parent.z[i])
- x += interval
-
- # Find distance between leading edge and first lower stringer
- interval = self.parent.spars[0].x[1] / (den_l_1 + 1)
- x = interval
- # Add lower stringers from leading edge until first spar.
- for _ in range(0, den_l_1):
- i = bi.bisect_left(self.parent.x[::-1], x)
- self.x = np.append(self.x, self.parent.x[-i])
- self.z = np.append(self.z, self.parent.z[-i])
- x += interval
- # Add lower stringers from first spar until last spar
- interval = (self.parent.spars[-1].x[1] -
- self.parent.spars[0].x[1]) / (den_l_2 + 1)
- x = interval + self.parent.spars[0].x[1]
- for _ in range(0, den_l_2):
- i = bi.bisect_left(self.parent.x[::-1], x)
- self.x = np.append(self.x, self.parent.x[-i])
- self.z = np.append(self.z, self.parent.z[-i])
- x += interval
- return None
-
- def add_area(self, area):
- self.area = area
- return None
-
- def add_mass(self, mass):
- self.mass = len(self.x) * mass + len(self.x) * mass
- return None
-
- def add_webs(self, thickness):
- """Add webs to stringers."""
- for _ in range(len(self.x) // 2):
- self.x_start.append(self.x[_])
- self.x_end.append(self.x[_ + 1])
- self.z_start.append(self.z[_])
- self.z_end.append(self.z[_ + 1])
- self.thickness = thickness
- return None
-
- def info_print(self, round=2):
- super().info_print(round)
- print('Stringer Area:\n', np.around(self.area, round))
- return None
-
-
-def plot_geom(airfoil):
- """This function plots the airfoil's + sub-components' geometry."""
- fig, ax = plt.subplots()
-
- # Plot chord
- x = [0, airfoil.chord]
- y = [0, 0]
- ax.plot(x, y, linewidth='1')
- # Plot quarter chord
- ax.plot(airfoil.chord / 4,
- 0,
- '.',
- color='g',
- markersize=24,
- label='Quarter-chord')
- # Plot mean camber line
- ax.plot(airfoil.x_c,
- airfoil.z_c,
- '-.',
- color='r',
- linewidth='2',
- label='Mean camber line')
- # Plot airfoil surfaces
- ax.plot(airfoil.x, airfoil.z, color='b', linewidth='1')
-
- try: # Plot spars
- for spar in airfoil.spars:
- x = (spar.x)
- y = (spar.z)
- ax.plot(x, y, '-', color='y', linewidth='4')
- except AttributeError:
- print('No spars to plot.')
- try: # Plot stringers
- for i in range(len(airfoil.stringers.x)):
- x = airfoil.stringers.x[i]
- y = airfoil.stringers.z[i]
- ax.plot(x, y, '.', color='y', markersize=12)
- except AttributeError:
- print('No stringers to plot.')
-
- ax.set(title='NACA ' + str(airfoil.naca_num) + ' airfoil',
- xlabel='X axis',
- ylabel='Z axis')
-
- plt.grid(axis='both', linestyle=':', linewidth=1)
- plt.gca().set_aspect('equal', adjustable='box')
- plt.gca().legend(bbox_to_anchor=(1, 1),
- bbox_transform=plt.gcf().transFigure)
- plt.show()
- return fig, ax
Copyright 2019--2024 Marius PETER