Merge pull request #1 from Blendoit/stringer

Stringer creation and plotting

Commit
1ffabed058cfe7fba5e2bc032b864da5b2f7ca16
Author
Blendoit <51464356+Blendoit@users.noreply.github.com>
Author date
Committer
GitHub <noreply@github.com>
Committer date
Changed files
analysis.py
index 8ea15797..00000000 100644..000000
@@ -1,16 +0,0 @@
1 Removed: # This file is part of Marius Peter's airfoil analysis package (this program).
2 Removed: #
3 Removed: # This program is free software: you can redistribute it and/or modify
4 Removed: # it under the terms of the GNU General Public License as published by
5 Removed: # the Free Software Foundation, either version 3 of the License, or
6 Removed: # (at your option) any later version.
7 Removed: #
8 Removed: # This program is distributed in the hope that it will be useful,
9 Removed: # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 Removed: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 Removed: # GNU General Public License for more details.
12 Removed: #
13 Removed: # You should have received a copy of the GNU General Public License
14 Removed: # along with this program. If not, see <https://www.gnu.org/licenses/>.
15 Removed:
16 Removed: import airfoil as af
creator.py
index b1a58a99..c6cae4ee 100644..100644
@@ -59,10 +59,14 @@
59 59 self.x_l = []
60 60 self.y_l = []
61 61 # Coordinates x_u, y_u, x_l, y_l packed in single list
62 Removed: self.coordinates = []
62 Added: self.coord = []
63 Added: # The airfoil components know the Coordinates instance's coords
63 64 global parent
64 65 parent = self
65 66
67 Added: def __str__(self):
68 Added: return type(self).__name__
69 Added:
66 70 def print_coord(self, round):
67 71 """
68 72 Print all the component's coordinates to the terminal.
@@ -70,37 +74,36 @@
70 74 This function's output is piped to the 'save_coord' function below.
71 75 """
72 76 print('============================')
73 Removed: print('Component:', type(self).__name__)
77 Added: print('Component:', str(self))
74 78 print('Chord length:', self.chord)
75 79 print('Semi-span:', self.semi_span)
76 80 print('============================')
77 Removed: print('x_u the upper x-coordinates:',
78 Removed: np.around(self.x_u, round),
79 Removed: sep='\n')
80 Removed: print('y_u the upper y-coordinates:',
81 Removed: np.around(self.y_u, round),
82 Removed: sep='\n')
83 Removed: print('x_l the lower x-coordinates:',
84 Removed: np.around(self.x_l, round),
85 Removed: sep='\n')
86 Removed: print('y_l the lower y-coordinates:',
87 Removed: np.around(self.y_l, round),
88 Removed: sep='\n')
89 Removed: print('\n')
81 Added: print('x_u the upper x-coordinates:\n', np.around(self.x_u, round))
82 Added: print('y_u the upper y-coordinates:\n', np.around(self.y_u, round))
83 Added: print('x_l the lower x-coordinates:\n', np.around(self.x_l, round))
84 Added: print('y_l the lower y-coordinates:\n', np.around(self.y_l, round))
85 Added: # print('\n')
90 86 return None
91 87
92 88 def save_coord(self, save_dir_path):
93 89 """
94 90 Save all the object's coordinates (must be full path).
95 91 """
96 Removed: file_name = str(type(self).__name__)
92 Added:
93 Added: file_name = str(self)
97 94 full_path = os.path.join(save_dir_path, file_name + '.txt')
98 95 file = open(full_path, 'w')
99 96 sys.stdout = file
100 97 self.print_coord(4)
101 98 return None
102 99
100 Added: def pack_coord(self):
101 Added: self.coord.append(self.x_u)
102 Added: self.coord.append(self.y_u)
103 Added: self.coord.append(self.x_l)
104 Added: self.coord.append(self.y_l)
103 105
106 Added:
104 107 class Airfoil(Coordinates):
105 108 """This class enables the creation of a single NACA airfoil."""
106 109
@@ -120,16 +123,15 @@
120 123 # Theta
121 124 self.theta = []
122 125
123 Removed: def naca(self, naca_num):
126 Added: def add_naca(self, naca_num):
124 127 """
125 Removed: This function generates geometry for our chosen NACA airfoil shape.\
126 Removed: The nested functions perform the required steps to generate geometry,\
127 Removed: and can be called to solve the geometry y-coordinate for any 'x' input.\
128 Added: This function generates geometry for our chosen NACA airfoil shape.
129 Added: The nested functions perform the required steps to generate geometry,
130 Added: and can be called to solve the geometry y-coordinate for any 'x' input.
128 131 Equation coefficients were retrieved from Wikipedia.org.
129 132
130 133 Parameters:
131 134 naca_num: 4-digit NACA wing
132 Removed: chord: wing chord length, in any unit
133 135
134 136 Return:
135 137 None
@@ -227,22 +229,18 @@
227 229 self.x_l.append(get_lower_coordinates(x)[0])
228 230 self.y_l.append(get_lower_coordinates(x)[1])
229 231
230 Removed: self.coordinates.append(self.x_u)
231 Removed: self.coordinates.append(self.y_u)
232 Removed: self.coordinates.append(self.x_l)
233 Removed: self.coordinates.append(self.y_l)
234 Removed:
232 Added: super().pack_coord()
235 233 return None
236 234
237 235
238 236 class Spar(Coordinates):
239 Removed: """Contains a single spar's location and material."""
237 Added: """Contains a single spar's location."""
240 238 global parent
241 239
242 240 def __init__(self):
243 241 super().__init__(parent.chord, parent.semi_span)
244 242
245 Removed: def add_spar(self, coordinates, spar_x):
243 Added: def add(self, airfoil_coord, spar_x):
246 244 """
247 245 Add a single spar at the % chord location given to function.
248 246
@@ -255,11 +253,11 @@
255 253 None
256 254 """
257 255 # Airfoil surface coordinates
258 Removed: # unpacked from 'coordinates' (list of lists in 'Airfoil').
259 Removed: x_u = coordinates[0]
260 Removed: y_u = coordinates[1]
261 Removed: x_l = coordinates[2]
262 Removed: y_l = coordinates[3]
256 Added: # unpacked from 'coordinates' (list of lists in 'Coordinates').
257 Added: x_u = airfoil_coord[0]
258 Added: y_u = airfoil_coord[1]
259 Added: x_l = airfoil_coord[2]
260 Added: y_l = airfoil_coord[3]
263 261 # Scaled spar location with regards to chord
264 262 loc = spar_x * self.chord
265 263 # bisect_left: returns index of first value in x_u > loc.
@@ -271,60 +269,92 @@
271 269 self.y_u.append(y_u[spar_x_u])
272 270 self.x_l.append(x_l[spar_x_l])
273 271 self.y_l.append(y_l[spar_x_l])
272 Added:
273 Added: super().pack_coord()
274 274 return None
275 275
276 276
277 Removed: class Stringer():
278 Removed: """Contains the coordinates of stringer(s) location and material."""
277 Added: class Stringer(Coordinates):
278 Added: """Contains the coordinates of all stringers."""
279 Added: global parent
279 280
280 281 def __init__(self):
281 Removed: # Stringer attributes
282 Removed: self.stringer_x_u = []
283 Removed: self.stringer_y_u = []
284 Removed: self.stringer_x_l = []
285 Removed: self.stringer_y_l = []
286 Removed: self.stringer_mat = []
282 Added: super().__init__(parent.chord, parent.semi_span)
287 283
288 Removed: def add_stringers(self, *density):
284 Added: def add(self, airfoil_coord, spar_coord, stringer_u_1, stringer_u_2,
285 Added: stringer_l_1, stringer_l_2):
289 286 """
290 Removed: Add stringers to the wing from their distribution density between spars.
291 Removed: First half of density[] concerns stringer distribution on
287 Added: Add equally distributed stringers to four airfoil locations
288 Added: (upper nose, lower nose, upper surface, lower surface).
292 289
293 290 Parameters:
294 Removed: material: stringer material
295 Removed: *density:
291 Added: stringer_u_1: upper nose number of stringers
292 Added: stringer_u_2: upper surface number of stringers
293 Added: stringer_l_1: lower nose number of stringers
294 Added: stringer_l_2: lower surface number of stringers
296 295
296 Added: Returns:
297 Added: None
297 298 """
298 299
299 Removed: # Find interval between leading edge and first upper stringer,
300 Removed: # from density parameter den_u_1.
301 Removed: interval = self.spar_x_u[0] / (den_u_1 * self.spar_x_u[0])
302 Removed: # initialise first self.stringer_x_u at first interval.
300 Added: # Airfoil surface coordinates
301 Added: # unpacked from 'coordinates' (list of lists in 'Coordinates').
302 Added: airfoil_x_u = airfoil_coord[0]
303 Added: airfoil_y_u = airfoil_coord[1]
304 Added: airfoil_x_l = airfoil_coord[2]
305 Added: airfoil_y_l = airfoil_coord[3]
306 Added: # Spar coordinates
307 Added: # unpacked from 'coordinates' (list of lists in 'Coordinates').
308 Added: try:
309 Added: spar_x_u = spar_coord[0]
310 Added: spar_y_u = spar_coord[1]
311 Added: spar_x_l = spar_coord[2]
312 Added: spar_y_l = spar_coord[3]
313 Added: except:
314 Added: print('Unable to initialize stringers. Were spars created?')
315 Added: # Find distance between leading edge and first upper stringer
316 Added: interval = spar_x_u[0] / (stringer_u_1 + 1)
317 Added: # initialise first self.stringer_x_u at first interval
303 318 x = interval
304 Removed: # Add upper stringers until first spar.
305 Removed: while x < self.spar_x_u[0]:
306 Removed: # Index of the first value of self.x_u > x
307 Removed: x_u = bi.bisect_left(self.x_u, x)
308 Removed: self.stringer_x_u.append(self.x_u[x_u])
309 Removed: self.stringer_y_u.append(self.y_u[x_u])
319 Added: # Add upper stringers from leading edge until first spar.
320 Added: for _ in range(0, stringer_u_1):
321 Added: # Index of the first value of airfoil_x_u > x
322 Added: index = bi.bisect_left(airfoil_x_u, x)
323 Added: self.x_u.append(airfoil_x_u[index])
324 Added: self.y_u.append(airfoil_y_u[index])
310 325 x += interval
326 Added: # Add upper stringers from first spar until last spar
327 Added: interval = (spar_x_u[-1] - spar_x_u[0]) / (stringer_u_2 + 1)
328 Added: x = interval + spar_x_u[0]
329 Added: for _ in range(0, stringer_u_2):
330 Added: index = bi.bisect_left(airfoil_x_u, x)
331 Added: self.x_u.append(airfoil_x_u[index])
332 Added: self.y_u.append(airfoil_y_u[index])
333 Added: x += interval
311 334
312 Removed: # Find interval between leading edge and first lower stringer,
313 Removed: # from density parameter den_l_1.
314 Removed: interval = self.spar_x_u[0] / (den_l_1 * self.spar_x_u[0])
315 Removed: # initialise first self.stringer_x_l at first interval.
335 Added: # Find distance between leading edge and first lower stringer
336 Added: interval = spar_x_l[0] / (stringer_l_1 + 1)
316 337 x = interval
317 Removed: # Add lower stringers until first spar.
318 Removed: while x < self.spar_x_l[0]:
319 Removed: # Index of the first value of self.x_l > x
320 Removed: x_u = bi.bisect_left(self.x_l, x)
321 Removed: self.stringer_x_l.append(self.x_l[x_u])
322 Removed: self.stringer_y_l.append(self.y_l[x_u])
338 Added: # Add lower stringers from leading edge until first spar.
339 Added: for _ in range(0, stringer_l_1):
340 Added: index = bi.bisect_left(airfoil_x_l, x)
341 Added: self.x_l.append(airfoil_x_l[index])
342 Added: self.y_l.append(airfoil_y_l[index])
323 343 x += interval
344 Added: # Add lower stringers from first spar until last spar
345 Added: interval = (spar_x_l[-1] - spar_x_l[0]) / (stringer_l_2 + 1)
346 Added: x = interval + spar_x_l[0]
347 Added: for _ in range(0, stringer_l_2):
348 Added: index = bi.bisect_left(airfoil_x_l, x)
349 Added: self.x_l.append(airfoil_x_l[index])
350 Added: self.y_l.append(airfoil_y_l[index])
351 Added: x += interval
352 Added:
353 Added: super().pack_coord()
324 354 return None
325 355
326 356
327 Removed: def plot(airfoil, spar):
357 Added: def plot(airfoil, spar, stringer):
328 358 """This function plots the elements passed as arguments."""
329 359
330 360 print('Plotting airfoil.')
@@ -343,23 +373,32 @@
343 373 plt.plot(airfoil.x_u, airfoil.y_u, '', color='b', linewidth='1')
344 374 # Plot lower surface
345 375 plt.plot(airfoil.x_l, airfoil.y_l, '', color='b', linewidth='1')
376 Added:
346 377 # Plot spars
347 378 try:
348 379 for _ in range(0, len(spar.x_u)):
349 380 x = (spar.x_u[_], spar.x_l[_])
350 381 y = (spar.y_u[_], spar.y_l[_])
351 Removed: plt.plot(x, y, '.-', color='b', label='spar')
382 Added: plt.plot(x, y, '.-', color='b')
352 383 plt.legend()
353 384 except:
354 385 print('Did not plot spars. Were they added?')
386 Added:
355 387 # Plot stringers
356 Removed: # if len(self.spar_x) != 0:
357 Removed: # for _ in range(0, len(self.stringer_x)):
358 Removed: # x = (self.stringer_x[_], self.stringer_x[_])
359 Removed: # y = (self.stringer_y_u[_], self.stringer_y_l[_])
360 Removed: # plt.scatter(x, y, color='y', linewidth='1',
361 Removed: # else:
362 Removed: # print('Unable to plot stringers. Were they created?')
388 Added: try:
389 Added: # Upper stringers
390 Added: for _ in range(0, len(stringer.x_u)):
391 Added: x = stringer.x_u[_]
392 Added: y = stringer.y_u[_]
393 Added: plt.plot(x, y, '.', color='y')
394 Added: # Lower stringers
395 Added: for _ in range(0, len(stringer.x_l)):
396 Added: x = stringer.x_l[_]
397 Added: y = stringer.y_l[_]
398 Added: plt.plot(x, y, '.', color='y')
399 Added: except:
400 Added: print('Unable to plot stringers. Were they created?')
401 Added:
363 402 # Graph formatting
364 403 plt.gcf().set_size_inches(9, 2.2)
365 404 plt.xlabel('X axis')
evaluator.py
index 00000000..6a87b7ef 000000..100644
@@ -0,0 +1,16 @@
1 Added: # This file is part of Marius Peter's airfoil analysis package (this program).
2 Added: #
3 Added: # This program is free software: you can redistribute it and/or modify
4 Added: # it under the terms of the GNU General Public License as published by
5 Added: # the Free Software Foundation, either version 3 of the License, or
6 Added: # (at your option) any later version.
7 Added: #
8 Added: # This program is distributed in the hope that it will be useful,
9 Added: # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 Added: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 Added: # GNU General Public License for more details.
12 Added: #
13 Added: # You should have received a copy of the GNU General Public License
14 Added: # along with this program. If not, see <https://www.gnu.org/licenses/>.
15 Added:
16 Added: import creator
generator.py
index 00000000..6a87b7ef 000000..100644
@@ -0,0 +1,16 @@
1 Added: # This file is part of Marius Peter's airfoil analysis package (this program).
2 Added: #
3 Added: # This program is free software: you can redistribute it and/or modify
4 Added: # it under the terms of the GNU General Public License as published by
5 Added: # the Free Software Foundation, either version 3 of the License, or
6 Added: # (at your option) any later version.
7 Added: #
8 Added: # This program is distributed in the hope that it will be useful,
9 Added: # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 Added: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 Added: # GNU General Public License for more details.
12 Added: #
13 Added: # You should have received a copy of the GNU General Public License
14 Added: # along with this program. If not, see <https://www.gnu.org/licenses/>.
15 Added:
16 Added: import creator
genetic_algorithm.py
index 8ea15797..00000000 100644..000000
@@ -1,16 +0,0 @@
1 Removed: # This file is part of Marius Peter's airfoil analysis package (this program).
2 Removed: #
3 Removed: # This program is free software: you can redistribute it and/or modify
4 Removed: # it under the terms of the GNU General Public License as published by
5 Removed: # the Free Software Foundation, either version 3 of the License, or
6 Removed: # (at your option) any later version.
7 Removed: #
8 Removed: # This program is distributed in the hope that it will be useful,
9 Removed: # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 Removed: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 Removed: # GNU General Public License for more details.
12 Removed: #
13 Removed: # You should have received a copy of the GNU General Public License
14 Removed: # along with this program. If not, see <https://www.gnu.org/licenses/>.
15 Removed:
16 Removed: import airfoil as af
main.py
index f62a88aa..909547eb 100644..100644
@@ -13,7 +13,9 @@
13 13 # You should have received a copy of the GNU General Public License
14 14 # along with this program. If not, see <https://www.gnu.org/licenses/>.
15 15
16 Removed: import creator
16 Added: import creator # Create geometry
17 Added: import evaluator # Evaluate geometry
18 Added: import generator # Iteratevely evaluate instances of geometry
17 19 import random
18 20
19 21 import time
@@ -27,7 +29,7 @@
27 29
28 30
29 31 def main():
30 Removed: # Create coordinate system specific to airfoil dimensions.
32 Added: # Create coordinate system specific to our airfoil dimensions.
31 33 creator.Coordinates(CHORD_LENGTH, SEMI_SPAN)
32 34
33 35 # Interate through all wings in population.
@@ -35,22 +37,29 @@
35 37 # Create airfoil instance
36 38 af = creator.Airfoil()
37 39 # Define NACA airfoil coordinates
38 Removed: af.naca(2412)
39 Removed: # Print coordinates of af to terminal
40 Removed: af.print_coord(4)
40 Added: af.add_naca(2412)
41 Added: af.print_coord(2)
42 Added:
41 43 # Create spar instance
42 44 af.spar = creator.Spar()
43 Removed: # Define the spar coordinates
44 Removed: af.spar.add_spar(af.coordinates, 0.15)
45 Removed: af.spar.add_spar(af.coordinates, 0.55)
46 Removed: # Print coordinates of af.spar to terminal
47 Removed: af.spar.print_coord(4)
45 Added: # Define the spar coordinates, stored in single spar object
46 Added: af.spar.add(af.coord, 0.15)
47 Added: af.spar.add(af.coord, 0.55)
48 Added: af.spar.print_coord(2)
49 Added:
50 Added: # Create stringer instance
51 Added: af.stringer = creator.Stringer()
52 Added: # Define the stringer coordinates from their amount
53 Added: af.stringer.add(af.coord, af.spar.coord, 10, 7, 5, 6)
54 Added: # Print coordinates of af.stringer to terminal
55 Added: af.stringer.print_coord(2)
56 Added:
48 57 # Plot components with matplotlib
49 Removed: creator.plot(af, af.spar)
58 Added: creator.plot(af, af.spar, af.stringer)
50 59
51 60 # Save component coordinates
52 Removed: af.save_coord(SAVE_PATH)
53 Removed: af.spar.save_coord(SAVE_PATH)
61 Added: # af.save_coord(SAVE_PATH)
62 Added: # af.spar.save_coord(SAVE_PATH)
54 63
55 64 # Print final execution time
56 65 print("--- %s seconds ---" % (time.time() - start_time))