*ARCHIVED* development moved to aircraft-studio.
Merge pull request #1 from Blendoit/stringer
Stringer creation and plotting
Changed files
analysis.py
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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))