[Python] Design & build airplanes from your specifications.
dir structure for evaluator
Changed files
creator/base.py
@@ -13,17 +13,19 @@
13
13
class Aircraft:
14
14
"""This class tracks all sub-components and is fed to the evaluator."""
15
15
def __init__(self, evaluator, name):
16
Removed:
evaluator.tree.update({"aircraft": self})
16
Added:
self.tree = {name: self}
17
Added:
evaluator.tree[type(self).__name__] = self.tree
17
18
self.evaluator = evaluator
18
19
self.name = name
19
Removed:
self.tree = [] # Nested list of subcomponents
20
20
21
21
22
22
class Component:
23
23
"""Basic component providing coordinates, tools and a component tree."""
24
24
def __init__(self, parent, name):
25
Removed:
self.tree = [name]
26
Removed:
parent.tree.append(self.tree)
25
Added:
self.tree = {name: self}
26
Added:
# parent.tree.update(self.tree)
27
Added:
parent.tree[type(self).__name__] = self.tree
28
Added:
self.parent = parent
27
29
self.name = name
28
30
self.x = np.array([])
29
31
self.z = np.array([])
evaluator.py
@@ -1,318 +0,0 @@
1
Removed:
"""
2
Removed:
The evaluator.py module contains a single Evaluator class,
3
Removed:
which knows all the attributes of a specified Aircraft instance,
4
Removed:
and contains functions to analyse the airfoil's geometrical
5
Removed:
& structural properties.
6
Removed:
"""
7
Removed:
8
Removed:
import sys
9
Removed:
import os.path
10
Removed:
import numpy as np
11
Removed:
from math import sqrt
12
Removed:
import matplotlib.pyplot as plt
13
Removed:
14
Removed:
15
Removed:
class Evaluator:
16
Removed:
"""Performs structural evaluations on aircrafts.
17
Removed:
Individual aircrafts must claim an Evaluator object as parent."""
18
Removed:
def __init__(self):
19
Removed:
self.tree = {} # Dictionary contains aircrafts and subcomponents
20
Removed:
self.results = []
21
Removed:
# # Evaluator knows all geometrical info from evaluated airfoil
22
Removed:
# self.airfoil = self.get_airfoil(aircraft)
23
Removed:
# self.spars = self.get_spars(aircraft)
24
Removed:
# self.stringers = self.get_stringers(aircraft)
25
Removed:
# # Lift
26
Removed:
# self.lift_rectangular = []
27
Removed:
# self.lift_elliptical = []
28
Removed:
# self.lift_total = []
29
Removed:
# # Drag
30
Removed:
# self.drag = []
31
Removed:
# # centroid
32
Removed:
# self.centroid = []
33
Removed:
# Inertia terms:
34
Removed:
self.I_ = {'x': 0, 'z': 0, 'xz': 0}
35
Removed:
36
Removed:
def get_lift_rectangular(aircraft, lift):
37
Removed:
L_prime = [
38
Removed:
lift / (aircraft.semi_span * 2) for x in range(aircraft.semi_span)
39
Removed:
]
40
Removed:
return L_prime
41
Removed:
42
Removed:
def get_lift_elliptical(aircraft, L_0):
43
Removed:
L_prime = [
44
Removed:
L_0 / (aircraft.semi_span * 2) * sqrt(1 -
45
Removed:
(y / aircraft.semi_span)**2)
46
Removed:
for y in range(aircraft.semi_span)
47
Removed:
]
48
Removed:
return L_prime
49
Removed:
50
Removed:
def get_lift_total(aircraft):
51
Removed:
F_z = [(aircraft.lift_rectangular[_] + aircraft.lift_elliptical[_]) / 2
52
Removed:
for _ in range(len(aircraft.lift_rectangular))]
53
Removed:
return F_z
54
Removed:
55
Removed:
def get_mass_distribution(self, total_mass):
56
Removed:
F_z = [total_mass / self.semi_span for x in range(0, self.semi_span)]
57
Removed:
return F_z
58
Removed:
59
Removed:
def get_drag(self, drag):
60
Removed:
# Transform semi-span integer into list
61
Removed:
semi_span = [x for x in range(0, self.semi_span)]
62
Removed:
63
Removed:
# Drag increases after 80% of the semi_span
64
Removed:
cutoff = round(0.8 * self.semi_span)
65
Removed:
66
Removed:
# Drag increases by 25% after 80% of the semi_span
67
Removed:
F_x = [drag for x in semi_span[0:cutoff]]
68
Removed:
F_x.extend([1.25 * drag for x in semi_span[cutoff:]])
69
Removed:
return F_x
70
Removed:
71
Removed:
def get_centroid(aircraft):
72
Removed:
"""Return the coordinates of the centroid."""
73
Removed:
stringer_area = aircraft.stringer.area
74
Removed:
cap_area = aircraft.spar.cap_area
75
Removed:
76
Removed:
caps_x = [value for spar in aircraft.spar.x for value in spar]
77
Removed:
caps_z = [value for spar in aircraft.spar.z for value in spar]
78
Removed:
stringers_x = aircraft.stringer.x
79
Removed:
stringers_z = aircraft.stringer.z
80
Removed:
81
Removed:
denominator = float(
82
Removed:
len(caps_x) * cap_area + len(stringers_x) * stringer_area)
83
Removed:
84
Removed:
centroid_x = float(
85
Removed:
sum([x * cap_area for x in caps_x]) +
86
Removed:
sum([x * stringer_area for x in stringers_x]))
87
Removed:
centroid_x = centroid_x / denominator
88
Removed:
89
Removed:
centroid_z = float(
90
Removed:
sum([z * cap_area for z in caps_z]) +
91
Removed:
sum([z * stringer_area for z in stringers_z]))
92
Removed:
centroid_z = centroid_z / denominator
93
Removed:
94
Removed:
return (centroid_x, centroid_z)
95
Removed:
96
Removed:
def get_inertia_terms(self):
97
Removed:
"""Obtain all inertia terms."""
98
Removed:
stringer_area = self.stringer.area
99
Removed:
cap_area = self.spar.cap_area
100
Removed:
101
Removed:
# Adds upper and lower components' coordinates to list
102
Removed:
x_stringers = self.stringer.x
103
Removed:
z_stringers = self.stringer.z
104
Removed:
x_spars = self.spar.x[:][0] + self.spar.x[:][1]
105
Removed:
z_spars = self.spar.z[:][0] + self.spar.z[:][1]
106
Removed:
stringer_count = range(len(x_stringers))
107
Removed:
spar_count = range(len(self.spar.x))
108
Removed:
109
Removed:
# I_x is the sum of the contributions of the spar caps and stringers
110
Removed:
# TODO: replace list indices with dictionary value
111
Removed:
I_x = sum([
112
Removed:
cap_area * (z_spars[i] - self.centroid[1])**2 for i in spar_count
113
Removed:
])
114
Removed:
I_x += sum([
115
Removed:
stringer_area * (z_stringers[i] - self.centroid[1])**2
116
Removed:
for i in stringer_count
117
Removed:
])
118
Removed:
119
Removed:
I_z = sum([
120
Removed:
cap_area * (x_spars[i] - self.centroid[0])**2 for i in spar_count
121
Removed:
])
122
Removed:
I_z += sum([
123
Removed:
stringer_area * (x_stringers[i] - self.centroid[0])**2
124
Removed:
for i in stringer_count
125
Removed:
])
126
Removed:
127
Removed:
I_xz = sum([
128
Removed:
cap_area * (x_spars[i] - self.centroid[0]) *
129
Removed:
(z_spars[i] - self.centroid[1]) for i in spar_count
130
Removed:
])
131
Removed:
I_xz += sum([
132
Removed:
stringer_area * (x_stringers[i] - self.centroid[0]) *
133
Removed:
(z_stringers[i] - self.centroid[1]) for i in stringer_count
134
Removed:
])
135
Removed:
return (I_x, I_z, I_xz)
136
Removed:
137
Removed:
def get_dx(self, component):
138
Removed:
return [x - self.centroid[0] for x in component.x_start]
139
Removed:
140
Removed:
def get_dz(self, component):
141
Removed:
return [x - self.centroid[1] for x in component.x_start]
142
Removed:
143
Removed:
def get_dP(self, xDist, zDist, V_x, V_z, area):
144
Removed:
I_x = self.I_['x']
145
Removed:
I_z = self.I_['z']
146
Removed:
I_xz = self.I_['xz']
147
Removed:
denom = float(I_x * I_z - I_xz**2)
148
Removed:
z = float()
149
Removed:
for _ in range(len(xDist)):
150
Removed:
z += float(-area * xDist[_] * (I_x * V_x - I_xz * V_z) / denom -
151
Removed:
area * zDist[_] * (I_z * V_z - I_xz * V_x) / denom)
152
Removed:
return z
153
Removed:
154
Removed:
# def analysis(self, V_x, V_z):
155
Removed:
# """Perform all analysis calculations and store in class instance."""
156
Removed:
157
Removed:
# self.drag = self.get_drag(10)
158
Removed:
# self.lift_rectangular = self.get_lift_rectangular(13.7)
159
Removed:
# self.lift_elliptical = self.get_lift_elliptical(15)
160
Removed:
# self.lift_total = self.get_lift_total()
161
Removed:
# self.mass_dist = self.get_mass_distribution(self.mass_total)
162
Removed:
# self.centroid = self.get_centroid()
163
Removed:
# self.I_['x'] = self.get_inertia_terms()[0]
164
Removed:
# self.I_['z'] = self.get_inertia_terms()[1]
165
Removed:
# self.I_['xz'] = self.get_inertia_terms()[2]
166
Removed:
# spar_dx = self.get_dx(self.spar)
167
Removed:
# spar_dz = self.get_dz(self.spar)
168
Removed:
# self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
169
Removed:
# self.spar.cap_area)
170
Removed:
# self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
171
Removed:
# self.spar.cap_area)
172
Removed:
# print("yayyyyy")
173
Removed:
# return None
174
Removed:
175
Removed:
def update(self):
176
Removed:
"""Get all aircrafts' data."""
177
Removed:
for aircraft in self.tree:
178
Removed:
# print(f"For {aircraft.name} tree is:")
179
Removed:
# print("For", aircraft.name, " tree is:")
180
Removed:
# print(self.tree)
181
Removed:
self.results.append(self.analysis(aircraft))
182
Removed:
183
Removed:
def analysis(self, aircraft):
184
Removed:
"""Perform all analysis calculations and store in class instance."""
185
Removed:
186
Removed:
results = {
187
Removed:
"Lift": self.get_lift_total,
188
Removed:
"Drag": self.get_drag,
189
Removed:
"Centroid": self.get_centroid
190
Removed:
}
191
Removed:
192
Removed:
# print(f"Analysis results for {aircraft.name}:\n", results)
193
Removed:
return (results)
194
Removed:
# self.results = self.get_lift_total(aircraft)
195
Removed:
196
Removed:
# self.drag = self.get_drag(10)
197
Removed:
# self.lift_rectangular = self.get_lift_rectangular(13.7)
198
Removed:
# self.lift_elliptical = self.get_lift_elliptical(15)
199
Removed:
# self.lift_total = self.get_lift_total()
200
Removed:
# self.mass_dist = self.get_mass_distribution(self.mass_total)
201
Removed:
# self.centroid = self.get_centroid()
202
Removed:
# self.I_['x'] = self.get_inertia_terms()[0]
203
Removed:
# self.I_['z'] = self.get_inertia_terms()[1]
204
Removed:
# self.I_['xz'] = self.get_inertia_terms()[2]
205
Removed:
# spar_dx = self.get_dx(self.spar)
206
Removed:
# spar_dz = self.get_dz(self.spar)
207
Removed:
# self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
208
Removed:
# self.spar.cap_area)
209
Removed:
# self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
210
Removed:
# self.spar.cap_area)
211
Removed:
return None
212
Removed:
213
Removed:
def info_print(self, round):
214
Removed:
"""Print all the component's evaluated data to the terminal."""
215
Removed:
name = f' {print(self)} DATA FOR {str(self).upper()} '
216
Removed:
num_of_dashes = len(name)
217
Removed:
print(num_of_dashes * '-')
218
Removed:
print(name)
219
Removed:
for k, v in self.__dict__.items():
220
Removed:
if type(v) != list:
221
Removed:
print('{}:\n'.format(k), v)
222
Removed:
print(num_of_dashes * '-')
223
Removed:
for k, v in self.__dict__.items():
224
Removed:
if type(v) == list:
225
Removed:
print('{}:\n'.format(k), np.around(v, round))
226
Removed:
return None
227
Removed:
228
Removed:
def info_save(self, save_path, number):
229
Removed:
"""Save all the object's coordinates (must be full path)."""
230
Removed:
file_name = 'airfoil_{}_eval.txt'.format(number)
231
Removed:
full_path = os.path.join(save_path, file_name)
232
Removed:
try:
233
Removed:
with open(full_path, 'w') as sys.stdout:
234
Removed:
self.info_print(6)
235
Removed:
# This line required to reset behavior of sys.stdout
236
Removed:
sys.stdout = sys.__stdout__
237
Removed:
print('Successfully wrote to file {}'.format(full_path))
238
Removed:
except IOError:
239
Removed:
print(
240
Removed:
'Unable to write {} to specified directory.\n'.format(
241
Removed:
file_name), 'Was the full path passed to the function?')
242
Removed:
return None
243
Removed:
244
Removed:
245
Removed:
def plot_geom(evaluator):
246
Removed:
"""This function plots analysis results over the airfoil's geometry."""
247
Removed:
# Plot chord
248
Removed:
x_chord = [0, evaluator.chord]
249
Removed:
y_chord = [0, 0]
250
Removed:
plt.plot(x_chord, y_chord, linewidth='1')
251
Removed:
# Plot quarter chord
252
Removed:
plt.plot(evaluator.chord / 4,
253
Removed:
0,
254
Removed:
'.',
255
Removed:
color='g',
256
Removed:
markersize=24,
257
Removed:
label='Quarter-chord')
258
Removed:
# Plot airfoil surfaces
259
Removed:
x = [0.98 * x for x in evaluator.airfoil.x]
260
Removed:
y = [0.98 * z for z in evaluator.airfoil.z]
261
Removed:
plt.fill(x, y, color='w', linewidth='1', fill=False)
262
Removed:
x = [1.02 * x for x in evaluator.airfoil.x]
263
Removed:
y = [1.02 * z for z in evaluator.airfoil.z]
264
Removed:
plt.fill(x, y, color='b', linewidth='1', fill=False)
265
Removed:
266
Removed:
# Plot spars
267
Removed:
try:
268
Removed:
for _ in range(len(evaluator.spar.x)):
269
Removed:
x = (evaluator.spar.x[_])
270
Removed:
y = (evaluator.spar.z[_])
271
Removed:
plt.plot(x, y, '-', color='b')
272
Removed:
except AttributeError:
273
Removed:
print('No spars to plot.')
274
Removed:
# Plot stringers
275
Removed:
try:
276
Removed:
for _ in range(0, len(evaluator.stringer.x)):
277
Removed:
x = evaluator.stringer.x[_]
278
Removed:
y = evaluator.stringer.z[_]
279
Removed:
plt.plot(x, y, '.', color='y', markersize=12)
280
Removed:
except AttributeError:
281
Removed:
print('No stringers to plot.')
282
Removed:
283
Removed:
# Plot centroid
284
Removed:
x = evaluator.centroid[0]
285
Removed:
y = evaluator.centroid[1]
286
Removed:
plt.plot(x, y, '.', color='r', markersize=24, label='centroid')
287
Removed:
288
Removed:
# Graph formatting
289
Removed:
plt.xlabel('X axis')
290
Removed:
plt.ylabel('Z axis')
291
Removed:
292
Removed:
plot_bound = max(evaluator.airfoil.x)
293
Removed:
plt.xlim(-0.10 * plot_bound, 1.10 * plot_bound)
294
Removed:
plt.ylim(-(1.10 * plot_bound / 2), (1.10 * plot_bound / 2))
295
Removed:
plt.gca().set_aspect('equal', adjustable='box')
296
Removed:
plt.gca().legend()
297
Removed:
plt.grid(axis='both', linestyle=':', linewidth=1)
298
Removed:
plt.show()
299
Removed:
return None
300
Removed:
301
Removed:
302
Removed:
def plot_lift(evaluator):
303
Removed:
x = range(evaluator.semi_span)
304
Removed:
y_1 = evaluator.lift_rectangular
305
Removed:
y_2 = evaluator.lift_elliptical
306
Removed:
y_3 = evaluator.lift_total
307
Removed:
plt.plot(x, y_1, '.', color='b', markersize=4, label='Rectangular lift')
308
Removed:
plt.plot(x, y_2, '.', color='g', markersize=4, label='Elliptical lift')
309
Removed:
plt.plot(x, y_3, '.', color='r', markersize=4, label='Total lift')
310
Removed:
311
Removed:
# Graph formatting
312
Removed:
plt.xlabel('Semi-span location')
313
Removed:
plt.ylabel('Lift')
314
Removed:
315
Removed:
plt.gca().legend()
316
Removed:
plt.grid(axis='both', linestyle=':', linewidth=1)
317
Removed:
plt.show()
318
Removed:
return None
evaluator/__init__.py
@@ -0,0 +1,1 @@
1
Added:
from . import evaluator
evaluator/evaluator.py
@@ -0,0 +1,316 @@
1
Added:
"""
2
Added:
The evaluator.py module contains a single Evaluator class,
3
Added:
which knows all the attributes of a specified Aircraft instance,
4
Added:
and contains functions to analyse the airfoil's geometrical
5
Added:
& structural properties.
6
Added:
"""
7
Added:
8
Added:
import sys
9
Added:
import os.path
10
Added:
import numpy as np
11
Added:
from math import sqrt
12
Added:
import matplotlib.pyplot as plt
13
Added:
14
Added:
15
Added:
class Evaluator:
16
Added:
"""Performs structural evaluations on aircrafts.
17
Added:
Individual aircrafts must claim an Evaluator object as parent."""
18
Added:
def __init__(self):
19
Added:
self.tree = {} # Keys are component names, values are objects
20
Added:
self.results = []
21
Added:
# # Evaluator knows all geometrical info from evaluated airfoil
22
Added:
# self.airfoil = self.get_airfoil(aircraft)
23
Added:
# self.spars = self.get_spars(aircraft)
24
Added:
# self.stringers = self.get_stringers(aircraft)
25
Added:
# # Lift
26
Added:
# self.lift_rectangular = []
27
Added:
# self.lift_elliptical = []
28
Added:
# self.lift_total = []
29
Added:
# # Drag
30
Added:
# self.drag = []
31
Added:
# # centroid
32
Added:
# self.centroid = []
33
Added:
# Inertia terms:
34
Added:
self.I_ = {'x': 0, 'z': 0, 'xz': 0}
35
Added:
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:
]
40
Added:
return L_prime
41
Added:
42
Added:
def get_lift_elliptical(aircraft, L_0):
43
Added:
L_prime = [
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)
47
Added:
]
48
Added:
return L_prime
49
Added:
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))]
53
Added:
return F_z
54
Added:
55
Added:
def get_mass_distribution(self, total_mass):
56
Added:
F_z = [total_mass / self.semi_span for x in range(0, self.semi_span)]
57
Added:
return F_z
58
Added:
59
Added:
def get_drag(self, drag):
60
Added:
# Transform semi-span integer into list
61
Added:
semi_span = [x for x in range(0, self.semi_span)]
62
Added:
63
Added:
# Drag increases after 80% of the semi_span
64
Added:
cutoff = round(0.8 * self.semi_span)
65
Added:
66
Added:
# Drag increases by 25% after 80% of the semi_span
67
Added:
F_x = [drag for x in semi_span[0:cutoff]]
68
Added:
F_x.extend([1.25 * drag for x in semi_span[cutoff:]])
69
Added:
return F_x
70
Added:
71
Added:
def get_centroid(aircraft):
72
Added:
"""Return the coordinates of the centroid."""
73
Added:
stringer_area = aircraft.stringer.area
74
Added:
cap_area = aircraft.spar.cap_area
75
Added:
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
80
Added:
81
Added:
denominator = float(
82
Added:
len(caps_x) * cap_area + len(stringers_x) * stringer_area)
83
Added:
84
Added:
centroid_x = float(
85
Added:
sum([x * cap_area for x in caps_x]) +
86
Added:
sum([x * stringer_area for x in stringers_x]))
87
Added:
centroid_x = centroid_x / denominator
88
Added:
89
Added:
centroid_z = float(
90
Added:
sum([z * cap_area for z in caps_z]) +
91
Added:
sum([z * stringer_area for z in stringers_z]))
92
Added:
centroid_z = centroid_z / denominator
93
Added:
94
Added:
return (centroid_x, centroid_z)
95
Added:
96
Added:
def get_inertia_terms(self):
97
Added:
"""Obtain all inertia terms."""
98
Added:
stringer_area = self.stringer.area
99
Added:
cap_area = self.spar.cap_area
100
Added:
101
Added:
# Adds upper and lower components' coordinates to list
102
Added:
x_stringers = self.stringer.x
103
Added:
z_stringers = self.stringer.z
104
Added:
x_spars = self.spar.x[:][0] + self.spar.x[:][1]
105
Added:
z_spars = self.spar.z[:][0] + self.spar.z[:][1]
106
Added:
stringer_count = range(len(x_stringers))
107
Added:
spar_count = range(len(self.spar.x))
108
Added:
109
Added:
# I_x is the sum of the contributions of the spar caps and stringers
110
Added:
# TODO: replace list indices with dictionary value
111
Added:
I_x = sum([
112
Added:
cap_area * (z_spars[i] - self.centroid[1])**2 for i in spar_count
113
Added:
])
114
Added:
I_x += sum([
115
Added:
stringer_area * (z_stringers[i] - self.centroid[1])**2
116
Added:
for i in stringer_count
117
Added:
])
118
Added:
119
Added:
I_z = sum([
120
Added:
cap_area * (x_spars[i] - self.centroid[0])**2 for i in spar_count
121
Added:
])
122
Added:
I_z += sum([
123
Added:
stringer_area * (x_stringers[i] - self.centroid[0])**2
124
Added:
for i in stringer_count
125
Added:
])
126
Added:
127
Added:
I_xz = sum([
128
Added:
cap_area * (x_spars[i] - self.centroid[0]) *
129
Added:
(z_spars[i] - self.centroid[1]) for i in spar_count
130
Added:
])
131
Added:
I_xz += sum([
132
Added:
stringer_area * (x_stringers[i] - self.centroid[0]) *
133
Added:
(z_stringers[i] - self.centroid[1]) for i in stringer_count
134
Added:
])
135
Added:
return (I_x, I_z, I_xz)
136
Added:
137
Added:
def get_dx(self, component):
138
Added:
return [x - self.centroid[0] for x in component.x_start]
139
Added:
140
Added:
def get_dz(self, component):
141
Added:
return [x - self.centroid[1] for x in component.x_start]
142
Added:
143
Added:
def get_dP(self, xDist, zDist, V_x, V_z, area):
144
Added:
I_x = self.I_['x']
145
Added:
I_z = self.I_['z']
146
Added:
I_xz = self.I_['xz']
147
Added:
denom = float(I_x * I_z - I_xz**2)
148
Added:
z = float()
149
Added:
for _ in range(len(xDist)):
150
Added:
z += float(-area * xDist[_] * (I_x * V_x - I_xz * V_z) / denom -
151
Added:
area * zDist[_] * (I_z * V_z - I_xz * V_x) / denom)
152
Added:
return z
153
Added:
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:
"""Refresh evaluator aircraft tree."""
177
Added:
print(self.tree)
178
Added:
179
Added:
# self.results.append(self.analysis(aircraft))
180
Added:
181
Added:
def analysis(self, aircraft):
182
Added:
"""Perform all analysis calculations and store in class instance."""
183
Added:
184
Added:
results = {
185
Added:
"Lift": self.get_lift_total,
186
Added:
"Drag": self.get_drag,
187
Added:
"Centroid": self.get_centroid
188
Added:
}
189
Added:
190
Added:
# print(f"Analysis results for {aircraft.name}:\n", results)
191
Added:
return (results)
192
Added:
# self.results = self.get_lift_total(aircraft)
193
Added:
194
Added:
# self.drag = self.get_drag(10)
195
Added:
# self.lift_rectangular = self.get_lift_rectangular(13.7)
196
Added:
# self.lift_elliptical = self.get_lift_elliptical(15)
197
Added:
# self.lift_total = self.get_lift_total()
198
Added:
# self.mass_dist = self.get_mass_distribution(self.mass_total)
199
Added:
# self.centroid = self.get_centroid()
200
Added:
# self.I_['x'] = self.get_inertia_terms()[0]
201
Added:
# self.I_['z'] = self.get_inertia_terms()[1]
202
Added:
# self.I_['xz'] = self.get_inertia_terms()[2]
203
Added:
# spar_dx = self.get_dx(self.spar)
204
Added:
# spar_dz = self.get_dz(self.spar)
205
Added:
# self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
206
Added:
# self.spar.cap_area)
207
Added:
# self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
208
Added:
# self.spar.cap_area)
209
Added:
return None
210
Added:
211
Added:
def info_print(self, round):
212
Added:
"""Print all the component's evaluated data to the terminal."""
213
Added:
name = f' {print(self)} DATA FOR {str(self).upper()} '
214
Added:
num_of_dashes = len(name)
215
Added:
print(num_of_dashes * '-')
216
Added:
print(name)
217
Added:
for k, v in self.__dict__.items():
218
Added:
if type(v) != list:
219
Added:
print('{}:\n'.format(k), v)
220
Added:
print(num_of_dashes * '-')
221
Added:
for k, v in self.__dict__.items():
222
Added:
if type(v) == list:
223
Added:
print('{}:\n'.format(k), np.around(v, round))
224
Added:
return None
225
Added:
226
Added:
def info_save(self, save_path, number):
227
Added:
"""Save all the object's coordinates (must be full path)."""
228
Added:
file_name = 'airfoil_{}_eval.txt'.format(number)
229
Added:
full_path = os.path.join(save_path, file_name)
230
Added:
try:
231
Added:
with open(full_path, 'w') as sys.stdout:
232
Added:
self.info_print(6)
233
Added:
# This line required to reset behavior of sys.stdout
234
Added:
sys.stdout = sys.__stdout__
235
Added:
print('Successfully wrote to file {}'.format(full_path))
236
Added:
except IOError:
237
Added:
print(
238
Added:
'Unable to write {} to specified directory.\n'.format(
239
Added:
file_name), 'Was the full path passed to the function?')
240
Added:
return None
241
Added:
242
Added:
243
Added:
def plot_geom(evaluator):
244
Added:
"""This function plots analysis results over the airfoil's geometry."""
245
Added:
# Plot chord
246
Added:
x_chord = [0, evaluator.chord]
247
Added:
y_chord = [0, 0]
248
Added:
plt.plot(x_chord, y_chord, linewidth='1')
249
Added:
# Plot quarter chord
250
Added:
plt.plot(evaluator.chord / 4,
251
Added:
0,
252
Added:
'.',
253
Added:
color='g',
254
Added:
markersize=24,
255
Added:
label='Quarter-chord')
256
Added:
# Plot airfoil surfaces
257
Added:
x = [0.98 * x for x in evaluator.airfoil.x]
258
Added:
y = [0.98 * z for z in evaluator.airfoil.z]
259
Added:
plt.fill(x, y, color='w', linewidth='1', fill=False)
260
Added:
x = [1.02 * x for x in evaluator.airfoil.x]
261
Added:
y = [1.02 * z for z in evaluator.airfoil.z]
262
Added:
plt.fill(x, y, color='b', linewidth='1', fill=False)
263
Added:
264
Added:
# Plot spars
265
Added:
try:
266
Added:
for _ in range(len(evaluator.spar.x)):
267
Added:
x = (evaluator.spar.x[_])
268
Added:
y = (evaluator.spar.z[_])
269
Added:
plt.plot(x, y, '-', color='b')
270
Added:
except AttributeError:
271
Added:
print('No spars to plot.')
272
Added:
# Plot stringers
273
Added:
try:
274
Added:
for _ in range(0, len(evaluator.stringer.x)):
275
Added:
x = evaluator.stringer.x[_]
276
Added:
y = evaluator.stringer.z[_]
277
Added:
plt.plot(x, y, '.', color='y', markersize=12)
278
Added:
except AttributeError:
279
Added:
print('No stringers to plot.')
280
Added:
281
Added:
# Plot centroid
282
Added:
x = evaluator.centroid[0]
283
Added:
y = evaluator.centroid[1]
284
Added:
plt.plot(x, y, '.', color='r', markersize=24, label='centroid')
285
Added:
286
Added:
# Graph formatting
287
Added:
plt.xlabel('X axis')
288
Added:
plt.ylabel('Z axis')
289
Added:
290
Added:
plot_bound = max(evaluator.airfoil.x)
291
Added:
plt.xlim(-0.10 * plot_bound, 1.10 * plot_bound)
292
Added:
plt.ylim(-(1.10 * plot_bound / 2), (1.10 * plot_bound / 2))
293
Added:
plt.gca().set_aspect('equal', adjustable='box')
294
Added:
plt.gca().legend()
295
Added:
plt.grid(axis='both', linestyle=':', linewidth=1)
296
Added:
plt.show()
297
Added:
return None
298
Added:
299
Added:
300
Added:
def plot_lift(evaluator):
301
Added:
x = range(evaluator.semi_span)
302
Added:
y_1 = evaluator.lift_rectangular
303
Added:
y_2 = evaluator.lift_elliptical
304
Added:
y_3 = evaluator.lift_total
305
Added:
plt.plot(x, y_1, '.', color='b', markersize=4, label='Rectangular lift')
306
Added:
plt.plot(x, y_2, '.', color='g', markersize=4, label='Elliptical lift')
307
Added:
plt.plot(x, y_3, '.', color='r', markersize=4, label='Total lift')
308
Added:
309
Added:
# Graph formatting
310
Added:
plt.xlabel('Semi-span location')
311
Added:
plt.ylabel('Lift')
312
Added:
313
Added:
plt.gca().legend()
314
Added:
plt.grid(axis='both', linestyle=':', linewidth=1)
315
Added:
plt.show()
316
Added:
return None
example_airfoil.py
@@ -11,7 +11,7 @@
11
11
12
12
import resources.materials as mt
13
13
import creator
14
Removed:
import evaluator
14
Added:
import evaluator.evaluator as evaluator
15
15
import generator
16
16
# from generator import
17
17
@@ -63,7 +63,6 @@
63
63
spar4 = creator.wing.Spar(af2, 'spar4', 0.67)
64
64
65
65
eval.update()
66
Removed:
# print("spar2 parent is:", af.parent)
67
66
# eval.info_print(2)
68
67
69
68
# # Create stringer instance