attempt component tree

Commit
db1df0c0413949785dc5fa59a887bac00cf11265
Author
blendoit <blendoit@gmail.com>
Author date
Committer
blendoit <blendoit@gmail.com>
Committer date
Changed files
creator/base.py
index c46b297a..24b452c9 100644..100644
@@ -12,24 +12,23 @@
12 12
13 13 class Aircraft:
14 14 """This class tracks all sub-components and is fed to the evaluator."""
15 Removed: def __init__(self, parent, name):
16 Removed: self.parent = parent
15 Added: def __init__(self, evaluator, name):
16 Added: evaluator.tree.update({"aircraft": self})
17 Added: self.evaluator = evaluator
17 18 self.name = name
18 Removed: parent.aircrafts.append(name)
19 Added: self.tree = [] # Nested list of subcomponents
19 20
20 21
21 22 class Component:
22 Removed: """Basic component providing coordinates and tools."""
23 Added: """Basic component providing coordinates, tools and a component tree."""
23 24 def __init__(self, parent, name):
24 Removed: self.parent = None
25 Added: self.tree = [name]
26 Added: parent.tree.append(self.tree)
25 27 self.name = name
26 28 self.x = np.array([])
27 29 self.z = np.array([])
28 30 self.material = None
29 31 self.mass = float()
30 Removed:
31 Removed: def __str__(self):
32 Removed: return self.name
33 32
34 33 def set_material(self, material):
35 34 """Set the component bulk material."""
creator/wing.py
index 59f5bbec..cc7d29ad 100644..100644
@@ -24,7 +24,7 @@
24 24 class Airfoil(base.Component):
25 25 """This class represents a single NACA airfoil.
26 26
27 Removed: The coordinates are saved as two lists
27 Added: The coordinates are saved as two np.arrays
28 28 for the x- and z-coordinates. The coordinates start at
29 29 the leading edge, travel over the airfoil's upper edge,
30 30 then loop back to the leading edge via the lower edge.
@@ -141,9 +141,9 @@
141 141 super().__init__(parent, name)
142 142 super().set_material(material)
143 143 self.cap_area = float()
144 Removed: loc = loc_percent * parent.chord
145 144 # bi.bisect_left: returns index of first value in parent.x > loc
146 145 # This ensures that spar geom intersects with airfoil geom.
146 Added: loc = loc_percent * parent.chord
147 147 # Spar upper coordinates
148 148 spar_u = bi.bisect_left(parent.x, loc) - 1
149 149 self.x = np.append(self.x, parent.x[spar_u])
evaluator.py
index aac7f61d..096e9a0a 100644..100644
@@ -16,7 +16,8 @@
16 16 """Performs structural evaluations on aircrafts.
17 17 Individual aircrafts must claim an Evaluator object as parent."""
18 18 def __init__(self):
19 Removed: self.aircrafts = []
19 Added: self.tree = {} # Dictionary contains aircrafts and subcomponents
20 Added: self.results = []
20 21 # # Evaluator knows all geometrical info from evaluated airfoil
21 22 # self.airfoil = self.get_airfoil(aircraft)
22 23 # self.spars = self.get_spars(aircraft)
@@ -32,87 +33,23 @@
32 33 # Inertia terms:
33 34 self.I_ = {'x': 0, 'z': 0, 'xz': 0}
34 35
35 Removed: def __str__(self):
36 Removed: return type(self).__name__
37 Removed:
38 Removed: def update(self):
39 Removed: """Get all aircrafts' data."""
40 Removed: try:
41 Removed: print(self.aircrafts)
42 Removed: except:
43 Removed: print("failed!")
44 Removed:
45 Removed: # def get_airfoil(self, aircraft):
46 Removed: # """Get data of spars belonging to aircraft."""
47 Removed: # try:
48 Removed: # pass
49 Removed: # except:
50 Removed: # pass
51 Removed: # pass
52 Removed:
53 Removed: # def get_spars(self, aircraft):
54 Removed: # """Get data of spars belonging to aircraft."""
55 Removed: # try:
56 Removed: # pass
57 Removed: # except:
58 Removed: # pass
59 Removed: # pass
60 Removed:
61 Removed: # def get_stringers(self, aircraft):
62 Removed: # """Get data of spars belonging to aircraft."""
63 Removed: # try:
64 Removed: # pass
65 Removed: # except:
66 Removed: # pass
67 Removed: # pass
68 Removed:
69 Removed: def info_print(self, round):
70 Removed: """Print all the component's evaluated data to the terminal."""
71 Removed: name = f' {print(self)} DATA FOR {str(self).upper()} '
72 Removed: num_of_dashes = len(name)
73 Removed: print(num_of_dashes * '-')
74 Removed: print(name)
75 Removed: for k, v in self.__dict__.items():
76 Removed: if type(v) != list:
77 Removed: print('{}:\n'.format(k), v)
78 Removed: print(num_of_dashes * '-')
79 Removed: for k, v in self.__dict__.items():
80 Removed: if type(v) == list:
81 Removed: print('{}:\n'.format(k), np.around(v, round))
82 Removed: return None
83 Removed:
84 Removed: def info_save(self, save_path, number):
85 Removed: """Save all the object's coordinates (must be full path)."""
86 Removed: file_name = 'airfoil_{}_eval.txt'.format(number)
87 Removed: full_path = os.path.join(save_path, file_name)
88 Removed: try:
89 Removed: with open(full_path, 'w') as sys.stdout:
90 Removed: self.info_print(6)
91 Removed: # This line required to reset behavior of sys.stdout
92 Removed: sys.stdout = sys.__stdout__
93 Removed: print('Successfully wrote to file {}'.format(full_path))
94 Removed: except IOError:
95 Removed: print(
96 Removed: 'Unable to write {} to specified directory.\n'.format(
97 Removed: file_name), 'Was the full path passed to the function?')
98 Removed: return None
99 Removed:
100 Removed: # All these functions take integer arguments and return lists.
101 Removed:
102 Removed: def get_lift_rectangular(self, lift):
103 Removed: L_prime = [lift / (self.semi_span * 2) for x in range(self.semi_span)]
36 Added: def get_lift_rectangular(aircraft, lift):
37 Added: L_prime = [
38 Added: lift / (aircraft.semi_span * 2) for x in range(aircraft.semi_span)
39 Added: ]
104 40 return L_prime
105 41
106 Removed: def get_lift_elliptical(self, L_0):
42 Added: def get_lift_elliptical(aircraft, L_0):
107 43 L_prime = [
108 Removed: L_0 / (self.semi_span * 2) * sqrt(1 - (y / self.semi_span)**2)
109 Removed: for y in range(self.semi_span)
44 Added: L_0 / (aircraft.semi_span * 2) * sqrt(1 -
45 Added: (y / aircraft.semi_span)**2)
46 Added: for y in range(aircraft.semi_span)
110 47 ]
111 48 return L_prime
112 49
113 Removed: def get_lift_total(self):
114 Removed: F_z = [(self.lift_rectangular[_] + self.lift_elliptical[_]) / 2
115 Removed: for _ in range(len(self.lift_rectangular))]
50 Added: def get_lift_total(aircraft):
51 Added: F_z = [(aircraft.lift_rectangular[_] + aircraft.lift_elliptical[_]) / 2
52 Added: for _ in range(len(aircraft.lift_rectangular))]
116 53 return F_z
117 54
118 55 def get_mass_distribution(self, total_mass):
@@ -131,15 +68,15 @@
131 68 F_x.extend([1.25 * drag for x in semi_span[cutoff:]])
132 69 return F_x
133 70
134 Removed: def get_centroid(self):
71 Added: def get_centroid(aircraft):
135 72 """Return the coordinates of the centroid."""
136 Removed: stringer_area = self.stringer.area
137 Removed: cap_area = self.spar.cap_area
73 Added: stringer_area = aircraft.stringer.area
74 Added: cap_area = aircraft.spar.cap_area
138 75
139 Removed: caps_x = [value for spar in self.spar.x for value in spar]
140 Removed: caps_z = [value for spar in self.spar.z for value in spar]
141 Removed: stringers_x = self.stringer.x
142 Removed: stringers_z = self.stringer.z
76 Added: caps_x = [value for spar in aircraft.spar.x for value in spar]
77 Added: caps_z = [value for spar in aircraft.spar.z for value in spar]
78 Added: stringers_x = aircraft.stringer.x
79 Added: stringers_z = aircraft.stringer.z
143 80
144 81 denominator = float(
145 82 len(caps_x) * cap_area + len(stringers_x) * stringer_area)
@@ -214,23 +151,94 @@
214 151 area * zDist[_] * (I_z * V_z - I_xz * V_x) / denom)
215 152 return z
216 153
217 Removed: def analysis(self, V_x, V_z):
154 Added: # def analysis(self, V_x, V_z):
155 Added: # """Perform all analysis calculations and store in class instance."""
156 Added:
157 Added: # self.drag = self.get_drag(10)
158 Added: # self.lift_rectangular = self.get_lift_rectangular(13.7)
159 Added: # self.lift_elliptical = self.get_lift_elliptical(15)
160 Added: # self.lift_total = self.get_lift_total()
161 Added: # self.mass_dist = self.get_mass_distribution(self.mass_total)
162 Added: # self.centroid = self.get_centroid()
163 Added: # self.I_['x'] = self.get_inertia_terms()[0]
164 Added: # self.I_['z'] = self.get_inertia_terms()[1]
165 Added: # self.I_['xz'] = self.get_inertia_terms()[2]
166 Added: # spar_dx = self.get_dx(self.spar)
167 Added: # spar_dz = self.get_dz(self.spar)
168 Added: # self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
169 Added: # self.spar.cap_area)
170 Added: # self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
171 Added: # self.spar.cap_area)
172 Added: # print("yayyyyy")
173 Added: # return None
174 Added:
175 Added: def update(self):
176 Added: """Get all aircrafts' data."""
177 Added: for aircraft in self.tree:
178 Added: # print(f"For {aircraft.name} tree is:")
179 Added: # print("For", aircraft.name, " tree is:")
180 Added: # print(self.tree)
181 Added: self.results.append(self.analysis(aircraft))
182 Added:
183 Added: def analysis(self, aircraft):
218 184 """Perform all analysis calculations and store in class instance."""
219 Removed: self.drag = self.get_drag(10)
220 Removed: self.lift_rectangular = self.get_lift_rectangular(13.7)
221 Removed: self.lift_elliptical = self.get_lift_elliptical(15)
222 Removed: self.lift_total = self.get_lift_total()
223 Removed: self.mass_dist = self.get_mass_distribution(self.mass_total)
224 Removed: self.centroid = self.get_centroid()
225 Removed: self.I_['x'] = self.get_inertia_terms()[0]
226 Removed: self.I_['z'] = self.get_inertia_terms()[1]
227 Removed: self.I_['xz'] = self.get_inertia_terms()[2]
228 Removed: spar_dx = self.get_dx(self.spar)
229 Removed: spar_dz = self.get_dz(self.spar)
230 Removed: self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
231 Removed: self.spar.cap_area)
232 Removed: self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
233 Removed: self.spar.cap_area)
185 Added:
186 Added: results = {
187 Added: "Lift": self.get_lift_total,
188 Added: "Drag": self.get_drag,
189 Added: "Centroid": self.get_centroid
190 Added: }
191 Added:
192 Added: # print(f"Analysis results for {aircraft.name}:\n", results)
193 Added: return (results)
194 Added: # self.results = self.get_lift_total(aircraft)
195 Added:
196 Added: # self.drag = self.get_drag(10)
197 Added: # self.lift_rectangular = self.get_lift_rectangular(13.7)
198 Added: # self.lift_elliptical = self.get_lift_elliptical(15)
199 Added: # self.lift_total = self.get_lift_total()
200 Added: # self.mass_dist = self.get_mass_distribution(self.mass_total)
201 Added: # self.centroid = self.get_centroid()
202 Added: # self.I_['x'] = self.get_inertia_terms()[0]
203 Added: # self.I_['z'] = self.get_inertia_terms()[1]
204 Added: # self.I_['xz'] = self.get_inertia_terms()[2]
205 Added: # spar_dx = self.get_dx(self.spar)
206 Added: # spar_dz = self.get_dz(self.spar)
207 Added: # self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
208 Added: # self.spar.cap_area)
209 Added: # self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
210 Added: # self.spar.cap_area)
211 Added: return None
212 Added:
213 Added: def info_print(self, round):
214 Added: """Print all the component's evaluated data to the terminal."""
215 Added: name = f' {print(self)} DATA FOR {str(self).upper()} '
216 Added: num_of_dashes = len(name)
217 Added: print(num_of_dashes * '-')
218 Added: print(name)
219 Added: for k, v in self.__dict__.items():
220 Added: if type(v) != list:
221 Added: print('{}:\n'.format(k), v)
222 Added: print(num_of_dashes * '-')
223 Added: for k, v in self.__dict__.items():
224 Added: if type(v) == list:
225 Added: print('{}:\n'.format(k), np.around(v, round))
226 Added: return None
227 Added:
228 Added: def info_save(self, save_path, number):
229 Added: """Save all the object's coordinates (must be full path)."""
230 Added: file_name = 'airfoil_{}_eval.txt'.format(number)
231 Added: full_path = os.path.join(save_path, file_name)
232 Added: try:
233 Added: with open(full_path, 'w') as sys.stdout:
234 Added: self.info_print(6)
235 Added: # This line required to reset behavior of sys.stdout
236 Added: sys.stdout = sys.__stdout__
237 Added: print('Successfully wrote to file {}'.format(full_path))
238 Added: except IOError:
239 Added: print(
240 Added: 'Unable to write {} to specified directory.\n'.format(
241 Added: file_name), 'Was the full path passed to the function?')
234 242 return None
235 243
236 244
example_airfoil.py
index e5e586d7..e559bb74 100644..100644
@@ -43,23 +43,27 @@
43 43 SAVE_PATH = '/home/blendux/Projects/Aircraft_Studio/save'
44 44
45 45 eval = evaluator.Evaluator()
46 Removed: # Create aircraft instance
46 Added:
47 47 ac = creator.base.Aircraft(eval, "ac")
48 Removed: # Create airfoil instance
49 48 af = creator.wing.Airfoil(ac, 'af')
50 49 af.add_naca(NACA_NUM)
51 50 # af.info_print(2)
52 51 af.info_save()
53 Removed:
54 Removed: # Create spar instances
55 Removed: af.spar1 = creator.wing.Spar(af, 'spar1')
56 Removed: af.spar2 = creator.wing.Spar(af, 'spar2', 0.57)
52 Added: spar1 = creator.wing.Spar(af, 'spar1')
53 Added: spar2 = creator.wing.Spar(af, 'spar2', 0.57)
57 54 # af.spar1.info_print(2)
58 55 # af.spar2.info_print(2)
59 Removed: af.spar1.info_save()
60 Removed: af.spar2.info_save()
56 Added: spar1.info_save()
57 Added: spar2.info_save()
61 58
59 Added: ac2 = creator.base.Aircraft(eval, "ac2")
60 Added: af2 = creator.wing.Airfoil(ac2, 'af2')
61 Added: af2.add_naca(3412)
62 Added: spar3 = creator.wing.Spar(af2, 'spar3', 0.23)
63 Added: spar4 = creator.wing.Spar(af2, 'spar4', 0.67)
64 Added:
62 65 eval.update()
66 Added: # print("spar2 parent is:", af.parent)
63 67 # eval.info_print(2)
64 68
65 69 # # Create stringer instance
gui.py
index 2eea281a..04250f13 100644..100644
@@ -1,81 +1,12 @@
1 Removed: from tools import creator, evaluator, generator
2 Removed: # import creator
3 Removed: # import evaluator
4 Removed: # import generator
5 Removed: import tkinter as tk
6 Removed: import tkinter.ttk as ttk
1 Added: from tkinter import ttk # Normal Tkinter.* widgets are not themed!
2 Added: from ttkthemes import ThemedTk
7 3
8 Removed: from matplotlib.backends.backend_tkagg import (
9 Removed: FigureCanvasTkAgg, NavigationToolbar2Tk)
10 Removed:
11 Removed:
12 Removed: class MainWindow(tk.Frame):
13 Removed: """Main editor window."""
14 Removed:
15 Removed: def __init__(self, *args, **kwargs):
16 Removed: tk.Frame.__init__(self, *args, **kwargs)
17 Removed: root = tk.Tk()
18 Removed: root.wm_title('MAE 154B - Airfoil Design, Evaluation, Optimization')
19 Removed:
20 Removed: # self.button = tk.Button(self, text="Create new window",
21 Removed: # command=self.create_window)
22 Removed: # self.button.pack(side="top")
23 Removed: frame_1 = ttk.Frame(root)
24 Removed: l_naca, e_naca = new_field(frame_1, 'naca')
25 Removed: l_chord, e_chord = new_field(frame_1, 'chord')
26 Removed: l_semi_span, e_semi_span = new_field(frame_1, 'semi_span')
27 Removed: af = generator.default_airfoil()
28 Removed: # Graph window
29 Removed: frame_2 = ttk.Frame(root)
30 Removed: fig, ax = creator.plot_geom(af, False)
31 Removed: plot = FigureCanvasTkAgg(fig, frame_2)
32 Removed: # plot.draw()
33 Removed: toolbar = NavigationToolbar2Tk(plot, frame_2)
34 Removed: # toolbar.update()
35 Removed:
36 Removed: l_naca.grid(row=0, column=0)
37 Removed: e_naca.grid(row=0, column=1, padx=4)
38 Removed: # b_naca.grid(row=0, column=2)
39 Removed: l_chord.grid(row=1, column=0)
40 Removed: e_chord.grid(row=1, column=1, padx=4)
41 Removed: l_semi_span.grid(row=2, column=0, padx=4)
42 Removed: e_semi_span.grid(row=2, column=1, padx=4)
43 Removed: frame_1.pack(side=tk.LEFT)
44 Removed: # Graph window
45 Removed: plot.get_tk_widget().pack(expand=1, fill=tk.BOTH)
46 Removed: toolbar.pack()
47 Removed: frame_2.pack(side=tk.LEFT)
48 Removed:
49 Removed: def create_window(self):
50 Removed: self.counter += 1
51 Removed: window = tk.Toplevel(self)
52 Removed: window.wm_title("Window #%s" % self.counter)
53 Removed: label = tk.Label(window, text="This is window #%s" % self.counter)
54 Removed: label.pack(side="top", fill="both", expand=True, padx=100, pady=100)
55 Removed:
56 Removed:
57 Removed: def new_field(parent, name):
58 Removed: """Add a new user input field."""
59 Removed:
60 Removed: label = ttk.Label(parent, text=name)
61 Removed: entry = ttk.Entry(parent)
62 Removed: return label, entry
63 Removed:
64 Removed:
65 Removed: def set_naca(name):
66 Removed: naca_num = name.get()
67 Removed: print(naca_num)
68 Removed:
69 Removed:
70 Removed: def set_chord(name):
71 Removed: chord = name.get()
72 Removed: print(chord)
73 Removed:
74 Removed:
75 Removed: def set_semi_span(name):
76 Removed: semi_span = name.get()
77 Removed: print(semi_span)
78 Removed:
79 Removed:
80 Removed: # plot.get_tk_widget().pack()
81 Removed: MainWindow().mainloop()
4 Added: window = ThemedTk(theme="equilux")
5 Added: frame1 = ttk.Frame(window)
6 Added: but1 = ttk.Button(frame1, text="Quit", command=window.destroy).pack()
7 Added: frame2 = ttk.Frame(window)
8 Added: but2 = ttk.Button(frame2, text="Quit", command=window.destroy).pack()
9 Added: but3 = ttk.Button(frame2, text="Quit", command=window.destroy).pack()
10 Added: frame1.pack()
11 Added: frame2.pack()
12 Added: window.mainloop()
gui.py.bkp
index 00000000..409b6736 000000..100644
@@ -0,0 +1,80 @@
1 Added: import creator
2 Added: import evaluator
3 Added: import generator
4 Added: import tkinter as tk
5 Added: import tkinter.ttk as ttk
6 Added:
7 Added: from matplotlib.backends.backend_tkagg import (
8 Added: FigureCanvasTkAgg, NavigationToolbar2Tk)
9 Added:
10 Added:
11 Added: class MainWindow(tk.Frame):
12 Added: """Main editor window."""
13 Added:
14 Added: def __init__(self, *args, **kwargs):
15 Added: tk.Frame.__init__(self, *args, **kwargs)
16 Added: root = tk.Tk()
17 Added: root.wm_title('MAE 154B - Airfoil Design, Evaluation, Optimization')
18 Added:
19 Added: # self.button = tk.Button(self, text="Create new window",
20 Added: # command=self.create_window)
21 Added: # self.button.pack(side="top")
22 Added: frame_1 = ttk.Frame(root)
23 Added: l_naca, e_naca = new_field(frame_1, 'naca')
24 Added: l_chord, e_chord = new_field(frame_1, 'chord')
25 Added: l_semi_span, e_semi_span = new_field(frame_1, 'semi_span')
26 Added: # af = creator.base.Aircraft.from_defaults()
27 Added: # Graph window
28 Added: frame_2 = ttk.Frame(root)
29 Added: # fig, ax = creator.plot_geom(af, False)
30 Added: # plot = FigureCanvasTkAgg(fig, frame_2)
31 Added: # plot.draw()
32 Added: # toolbar = NavigationToolbar2Tk(plot, frame_2)
33 Added: # toolbar.update()
34 Added:
35 Added: l_naca.grid(row=0, column=0)
36 Added: e_naca.grid(row=0, column=1, padx=4)
37 Added: # b_naca.grid(row=0, column=2)
38 Added: l_chord.grid(row=1, column=0)
39 Added: e_chord.grid(row=1, column=1, padx=4)
40 Added: l_semi_span.grid(row=2, column=0, padx=4)
41 Added: e_semi_span.grid(row=2, column=1, padx=4)
42 Added: frame_1.pack(side=tk.LEFT)
43 Added: # Graph window
44 Added: # plot.get_tk_widget().pack(expand=1, fill=tk.BOTH)
45 Added: # toolbar.pack()
46 Added: frame_2.pack(side=tk.LEFT)
47 Added:
48 Added: def create_window(self):
49 Added: self.counter += 1
50 Added: window = tk.Toplevel(self)
51 Added: window.wm_title("Window #%s" % self.counter)
52 Added: label = tk.Label(window, text="This is window #%s" % self.counter)
53 Added: label.pack(side="top", fill="both", expand=True, padx=100, pady=100)
54 Added:
55 Added:
56 Added: def new_field(parent, name):
57 Added: """Add a new user input field."""
58 Added:
59 Added: label = ttk.Label(parent, text=name)
60 Added: entry = ttk.Entry(parent)
61 Added: return label, entry
62 Added:
63 Added:
64 Added: def set_naca(name):
65 Added: naca_num = name.get()
66 Added: print(naca_num)
67 Added:
68 Added:
69 Added: def set_chord(name):
70 Added: chord = name.get()
71 Added: print(chord)
72 Added:
73 Added:
74 Added: def set_semi_span(name):
75 Added: semi_span = name.get()
76 Added: print(semi_span)
77 Added:
78 Added:
79 Added: # plot.get_tk_widget().pack()
80 Added: MainWindow().mainloop()