From db1df0c0413949785dc5fa59a887bac00cf11265 Mon Sep 17 00:00:00 2001 From: blendoit Date: Sun, 6 Oct 2019 19:16:04 -0700 Subject: attempt component tree --- creator/base.py | 15 ++-- creator/wing.py | 4 +- evaluator.py | 206 ++++++++++++++++++++++++++++------------------------- example_airfoil.py | 20 +++--- gui.py | 93 ++++-------------------- gui.py.bkp | 80 +++++++++++++++++++++ 6 files changed, 220 insertions(+), 198 deletions(-) create mode 100644 gui.py.bkp 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() -- cgit v1.2.3