summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--creator/base.py15
-rw-r--r--creator/wing.py4
-rw-r--r--evaluator.py206
-rw-r--r--example_airfoil.py20
-rw-r--r--gui.py93
-rw-r--r--gui.py.bkp80
6 files changed, 220 insertions, 198 deletions
diff --git a/creator/base.py b/creator/base.py
index c46b297..24b452c 100644
--- a/creator/base.py
+++ b/creator/base.py
@@ -12,25 +12,24 @@ logging.basicConfig(filename='log.txt',
class Aircraft:
"""This class tracks all sub-components and is fed to the evaluator."""
- def __init__(self, parent, name):
- self.parent = parent
+ def __init__(self, evaluator, name):
+ evaluator.tree.update({"aircraft": self})
+ self.evaluator = evaluator
self.name = name
- parent.aircrafts.append(name)
+ self.tree = [] # Nested list of subcomponents
class Component:
- """Basic component providing coordinates and tools."""
+ """Basic component providing coordinates, tools and a component tree."""
def __init__(self, parent, name):
- self.parent = None
+ self.tree = [name]
+ parent.tree.append(self.tree)
self.name = name
self.x = np.array([])
self.z = np.array([])
self.material = None
self.mass = float()
- def __str__(self):
- return self.name
-
def set_material(self, material):
"""Set the component bulk material."""
self.material = material
diff --git a/creator/wing.py b/creator/wing.py
index 59f5bbe..cc7d29a 100644
--- a/creator/wing.py
+++ b/creator/wing.py
@@ -24,7 +24,7 @@ import resources.materials as mt
class Airfoil(base.Component):
"""This class represents a single NACA airfoil.
- The coordinates are saved as two lists
+ 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.
@@ -141,9 +141,9 @@ class Spar(base.Component):
super().__init__(parent, name)
super().set_material(material)
self.cap_area = float()
- loc = loc_percent * parent.chord
# 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])
diff --git a/evaluator.py b/evaluator.py
index aac7f61..096e9a0 100644
--- a/evaluator.py
+++ b/evaluator.py
@@ -16,7 +16,8 @@ class Evaluator:
"""Performs structural evaluations on aircrafts.
Individual aircrafts must claim an Evaluator object as parent."""
def __init__(self):
- self.aircrafts = []
+ self.tree = {} # Dictionary contains aircrafts and subcomponents
+ self.results = []
# # Evaluator knows all geometrical info from evaluated airfoil
# self.airfoil = self.get_airfoil(aircraft)
# self.spars = self.get_spars(aircraft)
@@ -32,87 +33,23 @@ class Evaluator:
# Inertia terms:
self.I_ = {'x': 0, 'z': 0, 'xz': 0}
- def __str__(self):
- return type(self).__name__
-
- def update(self):
- """Get all aircrafts' data."""
- try:
- print(self.aircrafts)
- except:
- print("failed!")
-
- # def get_airfoil(self, aircraft):
- # """Get data of spars belonging to aircraft."""
- # try:
- # pass
- # except:
- # pass
- # pass
-
- # def get_spars(self, aircraft):
- # """Get data of spars belonging to aircraft."""
- # try:
- # pass
- # except:
- # pass
- # pass
-
- # def get_stringers(self, aircraft):
- # """Get data of spars belonging to aircraft."""
- # try:
- # pass
- # except:
- # pass
- # pass
-
- def info_print(self, round):
- """Print all the component's evaluated data to the terminal."""
- name = f' {print(self)} 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) != list:
- print('{}:\n'.format(k), v)
- print(num_of_dashes * '-')
- for k, v in self.__dict__.items():
- if type(v) == list:
- print('{}:\n'.format(k), np.around(v, round))
- return None
-
- def info_save(self, save_path, number):
- """Save all the object's coordinates (must be full path)."""
- file_name = 'airfoil_{}_eval.txt'.format(number)
- full_path = os.path.join(save_path, file_name)
- try:
- with open(full_path, 'w') as sys.stdout:
- self.info_print(6)
- # This line required to reset behavior of sys.stdout
- sys.stdout = sys.__stdout__
- print('Successfully wrote to file {}'.format(full_path))
- except IOError:
- print(
- 'Unable to write {} to specified directory.\n'.format(
- file_name), 'Was the full path passed to the function?')
- return None
-
- # All these functions take integer arguments and return lists.
-
- def get_lift_rectangular(self, lift):
- L_prime = [lift / (self.semi_span * 2) for x in range(self.semi_span)]
+ def get_lift_rectangular(aircraft, lift):
+ L_prime = [
+ lift / (aircraft.semi_span * 2) for x in range(aircraft.semi_span)
+ ]
return L_prime
- def get_lift_elliptical(self, L_0):
+ def get_lift_elliptical(aircraft, L_0):
L_prime = [
- L_0 / (self.semi_span * 2) * sqrt(1 - (y / self.semi_span)**2)
- for y in range(self.semi_span)
+ L_0 / (aircraft.semi_span * 2) * sqrt(1 -
+ (y / aircraft.semi_span)**2)
+ for y in range(aircraft.semi_span)
]
return L_prime
- def get_lift_total(self):
- F_z = [(self.lift_rectangular[_] + self.lift_elliptical[_]) / 2
- for _ in range(len(self.lift_rectangular))]
+ def get_lift_total(aircraft):
+ F_z = [(aircraft.lift_rectangular[_] + aircraft.lift_elliptical[_]) / 2
+ for _ in range(len(aircraft.lift_rectangular))]
return F_z
def get_mass_distribution(self, total_mass):
@@ -131,15 +68,15 @@ class Evaluator:
F_x.extend([1.25 * drag for x in semi_span[cutoff:]])
return F_x
- def get_centroid(self):
+ def get_centroid(aircraft):
"""Return the coordinates of the centroid."""
- stringer_area = self.stringer.area
- cap_area = self.spar.cap_area
+ stringer_area = aircraft.stringer.area
+ cap_area = aircraft.spar.cap_area
- caps_x = [value for spar in self.spar.x for value in spar]
- caps_z = [value for spar in self.spar.z for value in spar]
- stringers_x = self.stringer.x
- stringers_z = self.stringer.z
+ caps_x = [value for spar in aircraft.spar.x for value in spar]
+ caps_z = [value for spar in aircraft.spar.z for value in spar]
+ stringers_x = aircraft.stringer.x
+ stringers_z = aircraft.stringer.z
denominator = float(
len(caps_x) * cap_area + len(stringers_x) * stringer_area)
@@ -214,23 +151,94 @@ class Evaluator:
area * zDist[_] * (I_z * V_z - I_xz * V_x) / denom)
return z
- def analysis(self, V_x, V_z):
+ # def analysis(self, V_x, V_z):
+ # """Perform all analysis calculations and store in class instance."""
+
+ # self.drag = self.get_drag(10)
+ # self.lift_rectangular = self.get_lift_rectangular(13.7)
+ # self.lift_elliptical = self.get_lift_elliptical(15)
+ # self.lift_total = self.get_lift_total()
+ # self.mass_dist = self.get_mass_distribution(self.mass_total)
+ # self.centroid = self.get_centroid()
+ # self.I_['x'] = self.get_inertia_terms()[0]
+ # self.I_['z'] = self.get_inertia_terms()[1]
+ # self.I_['xz'] = self.get_inertia_terms()[2]
+ # spar_dx = self.get_dx(self.spar)
+ # spar_dz = self.get_dz(self.spar)
+ # self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
+ # self.spar.cap_area)
+ # self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
+ # self.spar.cap_area)
+ # print("yayyyyy")
+ # return None
+
+ def update(self):
+ """Get all aircrafts' data."""
+ for aircraft in self.tree:
+ # print(f"For {aircraft.name} tree is:")
+ # print("For", aircraft.name, " tree is:")
+ # print(self.tree)
+ self.results.append(self.analysis(aircraft))
+
+ def analysis(self, aircraft):
"""Perform all analysis calculations and store in class instance."""
- self.drag = self.get_drag(10)
- self.lift_rectangular = self.get_lift_rectangular(13.7)
- self.lift_elliptical = self.get_lift_elliptical(15)
- self.lift_total = self.get_lift_total()
- self.mass_dist = self.get_mass_distribution(self.mass_total)
- self.centroid = self.get_centroid()
- self.I_['x'] = self.get_inertia_terms()[0]
- self.I_['z'] = self.get_inertia_terms()[1]
- self.I_['xz'] = self.get_inertia_terms()[2]
- spar_dx = self.get_dx(self.spar)
- spar_dz = self.get_dz(self.spar)
- self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
- self.spar.cap_area)
- self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
- self.spar.cap_area)
+
+ results = {
+ "Lift": self.get_lift_total,
+ "Drag": self.get_drag,
+ "Centroid": self.get_centroid
+ }
+
+ # print(f"Analysis results for {aircraft.name}:\n", results)
+ return (results)
+ # self.results = self.get_lift_total(aircraft)
+
+ # self.drag = self.get_drag(10)
+ # self.lift_rectangular = self.get_lift_rectangular(13.7)
+ # self.lift_elliptical = self.get_lift_elliptical(15)
+ # self.lift_total = self.get_lift_total()
+ # self.mass_dist = self.get_mass_distribution(self.mass_total)
+ # self.centroid = self.get_centroid()
+ # self.I_['x'] = self.get_inertia_terms()[0]
+ # self.I_['z'] = self.get_inertia_terms()[1]
+ # self.I_['xz'] = self.get_inertia_terms()[2]
+ # spar_dx = self.get_dx(self.spar)
+ # spar_dz = self.get_dz(self.spar)
+ # self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
+ # self.spar.cap_area)
+ # self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
+ # self.spar.cap_area)
+ return None
+
+ def info_print(self, round):
+ """Print all the component's evaluated data to the terminal."""
+ name = f' {print(self)} 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) != list:
+ print('{}:\n'.format(k), v)
+ print(num_of_dashes * '-')
+ for k, v in self.__dict__.items():
+ if type(v) == list:
+ print('{}:\n'.format(k), np.around(v, round))
+ return None
+
+ def info_save(self, save_path, number):
+ """Save all the object's coordinates (must be full path)."""
+ file_name = 'airfoil_{}_eval.txt'.format(number)
+ full_path = os.path.join(save_path, file_name)
+ try:
+ with open(full_path, 'w') as sys.stdout:
+ self.info_print(6)
+ # This line required to reset behavior of sys.stdout
+ sys.stdout = sys.__stdout__
+ print('Successfully wrote to file {}'.format(full_path))
+ except IOError:
+ print(
+ 'Unable to write {} to specified directory.\n'.format(
+ file_name), 'Was the full path passed to the function?')
return None
diff --git a/example_airfoil.py b/example_airfoil.py
index e5e586d..e559bb7 100644
--- a/example_airfoil.py
+++ b/example_airfoil.py
@@ -43,23 +43,27 @@ NOSE_BOTTOM_STRINGERS = 5
SAVE_PATH = '/home/blendux/Projects/Aircraft_Studio/save'
eval = evaluator.Evaluator()
-# Create aircraft instance
+
ac = creator.base.Aircraft(eval, "ac")
-# Create airfoil instance
af = creator.wing.Airfoil(ac, 'af')
af.add_naca(NACA_NUM)
# af.info_print(2)
af.info_save()
-
-# Create spar instances
-af.spar1 = creator.wing.Spar(af, 'spar1')
-af.spar2 = creator.wing.Spar(af, 'spar2', 0.57)
+spar1 = creator.wing.Spar(af, 'spar1')
+spar2 = creator.wing.Spar(af, 'spar2', 0.57)
# af.spar1.info_print(2)
# af.spar2.info_print(2)
-af.spar1.info_save()
-af.spar2.info_save()
+spar1.info_save()
+spar2.info_save()
+
+ac2 = creator.base.Aircraft(eval, "ac2")
+af2 = creator.wing.Airfoil(ac2, 'af2')
+af2.add_naca(3412)
+spar3 = creator.wing.Spar(af2, 'spar3', 0.23)
+spar4 = creator.wing.Spar(af2, 'spar4', 0.67)
eval.update()
+# print("spar2 parent is:", af.parent)
# eval.info_print(2)
# # Create stringer instance
diff --git a/gui.py b/gui.py
index 2eea281..04250f1 100644
--- a/gui.py
+++ b/gui.py
@@ -1,81 +1,12 @@
-from tools import creator, evaluator, generator
-# import creator
-# import evaluator
-# import generator
-import tkinter as tk
-import tkinter.ttk as ttk
-
-from matplotlib.backends.backend_tkagg import (
- FigureCanvasTkAgg, NavigationToolbar2Tk)
-
-
-class MainWindow(tk.Frame):
- """Main editor window."""
-
- def __init__(self, *args, **kwargs):
- tk.Frame.__init__(self, *args, **kwargs)
- root = tk.Tk()
- root.wm_title('MAE 154B - Airfoil Design, Evaluation, Optimization')
-
- # self.button = tk.Button(self, text="Create new window",
- # command=self.create_window)
- # self.button.pack(side="top")
- frame_1 = ttk.Frame(root)
- l_naca, e_naca = new_field(frame_1, 'naca')
- l_chord, e_chord = new_field(frame_1, 'chord')
- l_semi_span, e_semi_span = new_field(frame_1, 'semi_span')
- af = generator.default_airfoil()
- # Graph window
- frame_2 = ttk.Frame(root)
- fig, ax = creator.plot_geom(af, False)
- plot = FigureCanvasTkAgg(fig, frame_2)
- # plot.draw()
- toolbar = NavigationToolbar2Tk(plot, frame_2)
- # toolbar.update()
-
- l_naca.grid(row=0, column=0)
- e_naca.grid(row=0, column=1, padx=4)
- # b_naca.grid(row=0, column=2)
- l_chord.grid(row=1, column=0)
- e_chord.grid(row=1, column=1, padx=4)
- l_semi_span.grid(row=2, column=0, padx=4)
- e_semi_span.grid(row=2, column=1, padx=4)
- frame_1.pack(side=tk.LEFT)
- # Graph window
- plot.get_tk_widget().pack(expand=1, fill=tk.BOTH)
- toolbar.pack()
- frame_2.pack(side=tk.LEFT)
-
- def create_window(self):
- self.counter += 1
- window = tk.Toplevel(self)
- window.wm_title("Window #%s" % self.counter)
- label = tk.Label(window, text="This is window #%s" % self.counter)
- label.pack(side="top", fill="both", expand=True, padx=100, pady=100)
-
-
-def new_field(parent, name):
- """Add a new user input field."""
-
- label = ttk.Label(parent, text=name)
- entry = ttk.Entry(parent)
- return label, entry
-
-
-def set_naca(name):
- naca_num = name.get()
- print(naca_num)
-
-
-def set_chord(name):
- chord = name.get()
- print(chord)
-
-
-def set_semi_span(name):
- semi_span = name.get()
- print(semi_span)
-
-
-# plot.get_tk_widget().pack()
-MainWindow().mainloop()
+from tkinter import ttk # Normal Tkinter.* widgets are not themed!
+from ttkthemes import ThemedTk
+
+window = ThemedTk(theme="equilux")
+frame1 = ttk.Frame(window)
+but1 = ttk.Button(frame1, text="Quit", command=window.destroy).pack()
+frame2 = ttk.Frame(window)
+but2 = ttk.Button(frame2, text="Quit", command=window.destroy).pack()
+but3 = ttk.Button(frame2, text="Quit", command=window.destroy).pack()
+frame1.pack()
+frame2.pack()
+window.mainloop()
diff --git a/gui.py.bkp b/gui.py.bkp
new file mode 100644
index 0000000..409b673
--- /dev/null
+++ b/gui.py.bkp
@@ -0,0 +1,80 @@
+import creator
+import evaluator
+import generator
+import tkinter as tk
+import tkinter.ttk as ttk
+
+from matplotlib.backends.backend_tkagg import (
+ FigureCanvasTkAgg, NavigationToolbar2Tk)
+
+
+class MainWindow(tk.Frame):
+ """Main editor window."""
+
+ def __init__(self, *args, **kwargs):
+ tk.Frame.__init__(self, *args, **kwargs)
+ root = tk.Tk()
+ root.wm_title('MAE 154B - Airfoil Design, Evaluation, Optimization')
+
+ # self.button = tk.Button(self, text="Create new window",
+ # command=self.create_window)
+ # self.button.pack(side="top")
+ frame_1 = ttk.Frame(root)
+ l_naca, e_naca = new_field(frame_1, 'naca')
+ l_chord, e_chord = new_field(frame_1, 'chord')
+ l_semi_span, e_semi_span = new_field(frame_1, 'semi_span')
+ # af = creator.base.Aircraft.from_defaults()
+ # Graph window
+ frame_2 = ttk.Frame(root)
+ # fig, ax = creator.plot_geom(af, False)
+ # plot = FigureCanvasTkAgg(fig, frame_2)
+ # plot.draw()
+ # toolbar = NavigationToolbar2Tk(plot, frame_2)
+ # toolbar.update()
+
+ l_naca.grid(row=0, column=0)
+ e_naca.grid(row=0, column=1, padx=4)
+ # b_naca.grid(row=0, column=2)
+ l_chord.grid(row=1, column=0)
+ e_chord.grid(row=1, column=1, padx=4)
+ l_semi_span.grid(row=2, column=0, padx=4)
+ e_semi_span.grid(row=2, column=1, padx=4)
+ frame_1.pack(side=tk.LEFT)
+ # Graph window
+ # plot.get_tk_widget().pack(expand=1, fill=tk.BOTH)
+ # toolbar.pack()
+ frame_2.pack(side=tk.LEFT)
+
+ def create_window(self):
+ self.counter += 1
+ window = tk.Toplevel(self)
+ window.wm_title("Window #%s" % self.counter)
+ label = tk.Label(window, text="This is window #%s" % self.counter)
+ label.pack(side="top", fill="both", expand=True, padx=100, pady=100)
+
+
+def new_field(parent, name):
+ """Add a new user input field."""
+
+ label = ttk.Label(parent, text=name)
+ entry = ttk.Entry(parent)
+ return label, entry
+
+
+def set_naca(name):
+ naca_num = name.get()
+ print(naca_num)
+
+
+def set_chord(name):
+ chord = name.get()
+ print(chord)
+
+
+def set_semi_span(name):
+ semi_span = name.get()
+ print(semi_span)
+
+
+# plot.get_tk_widget().pack()
+MainWindow().mainloop()
Copyright 2019--2024 Marius PETER