*ARCHIVED* development moved to aircraft-studio.
1
# This file is part of Marius Peter's airfoil analysis package (this program).
2
#
3
# This program is free software: you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation, either version 3 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program. If not, see <https://www.gnu.org/licenses/>.
15
"""
16
The evaluator. module contains a single Evaluator class,
17
which knows all the attributes of a specified Airfoil instance,
18
and contains functions to analyse the airfoil's geometrical
19
& structural properties.
20
"""
21
22
import sys
23
import os.
24
import numpy as np
25
from math import sqrt
26
import matplotlib. as plt
27
28
29
classEvaluator:
30
"""Performs structural evaluations for the airfoil passed as argument."""
31
32
def __init__(self,):
33
# Evaluator knows all geometrical info from evaluated airfoil
34
self. = airfoil
35
self. = airfoil.
36
self. = airfoil.
37
# Global dimensions
38
self. = airfoil.
39
self. = airfoil.
40
# Mass & spanwise distribution
41
self. = float(.
42
+..
43
+..)
44
self. = []
45
# Lift
46
self. = []
47
self. = []
48
self. = []
49
# Drag
50
self. = []
51
# centroid
52
self. = []
53
# Inertia terms:
54
self. = {'x': 0, 'z': 0, 'xz': 0}
55
56
def __str__(self):
57
return type(self).__name__
58
59
def (self,round):
60
"""Print all the component's evaluated data to the terminal."""
61
name = ' EVALUATOR DATA FOR {} '.(str(self).())
62
num_of_dashes = len()
63
print(*'-')
64
print()
65
for k, v in self.__dict__.():
66
if type() != list:
67
print('{}:\n'.(),)
68
print(*'-')
69
for k, v in self.__dict__.():
70
if type() == list:
71
print('{}:\n'.(),.(,round))
72
return None
73
74
def (self,,):
75
"""Save all the object's coordinates (must be full path)."""
76
file_name = 'airfoil_{}_eval.txt'.()
77
full_path = os..(,)
78
try:
79
with open(,'w') as sys.:
80
self.(6)
81
# This line required to reset behavior of sys.stdout
82
sys. = sys.
83
print('Successfully wrote to file {}'.())
84
except IOError:
85
print(
86
'Unable to write {} to specified directory.\n'.(
87
),'Was the full path passed to the function?')
88
return None
89
90
# All these functions take integer arguments and return lists.
91
92
def (self,):
93
L_prime = [lift / (self. * 2) for x in range(self.)]
94
return L_prime
95
96
def (self,):
97
L_prime = [
98
L_0 / (self. * 2) * (1-(/self.)**2)
99
for y in range(self.)
100
]
101
return L_prime
102
103
def (self):
104
F_z = [(self.[] + self.[]) / 2
105
for _ in range(len(self.))]
106
return F_z
107
108
def (self,):
109
F_z = [total_mass / self. for x in range(0,self.)]
110
return F_z
111
112
def (self,):
113
# Transform semi-span integer into list
114
semi_span = [x for x in range(0,self.)]
115
116
# Drag increases after 80% of the semi_span
117
cutoff = round(0.8*self.)
118
119
# Drag increases by 25% after 80% of the semi_span
120
F_x = [drag for x in [0:]]
121
F_x.([1.25*forin[:]])
122
return F_x
123
124
def (self):
125
"""Return the coordinates of the centroid."""
126
stringer_area = self..
127
cap_area = self..
128
129
caps_x = [value for spar in self.. for value in spar]
130
caps_z = [value for spar in self.. for value in spar]
131
stringers_x = self..
132
stringers_z = self..
133
134
denominator = float(len()*
135
+len()*)
136
137
centroid_x = float(sum([*forin])
138
+sum([*forin]))
139
centroid_x = centroid_x / denominator
140
141
centroid_z = float(sum([*forin])
142
+sum([*forin]))
143
centroid_z = centroid_z / denominator
144
145
return(,)
146
147
def (self):
148
"""Obtain all inertia terms."""
149
stringer_area = self..
150
cap_area = self..
151
152
# Adds upper and lower components' coordinates to list
153
x_stringers = self..
154
z_stringers = self..
155
x_spars = self..[:][0] + self..[:][1]
156
z_spars = self..[:][0] + self..[:][1]
157
stringer_count = range(len())
158
spar_count = range(len(self..))
159
160
# I_x is the sum of the contributions of the spar caps and stringers
161
# TODO: replace list indices with dictionary value
162
I_x = sum([*([]-self.[1])**2
163
forin])
164
I_x += sum([*([]-self.[1])**2
165
forin])
166
167
I_z = sum([*([]-self.[0])**2
168
forin])
169
I_z += sum([*([]-self.[0])**2
170
forin])
171
172
I_xz = sum([*([]-self.[0])
173
*([]-self.[1])
174
forin])
175
I_xz += sum([*([]-self.[0])
176
*([]-self.[1])
177
forin])
178
return(,,)
179
180
def (self,):
181
[-self.[0]forin.]
182
183
def (self,):
184
[-self.[1]forin.]
185
186
def (self,,,,,):
187
I_x = self.['x']
188
I_z = self.['z']
189
I_xz = self.['xz']
190
denom = float(*-**2)
191
z = float()
192
for _ in range(len()):
193
z += float(-*[]*(*-*)
194
/
195
-*[]*(*-*)
196
/)
197
return z
198
199
def (self,,):
200
"""Perform all analysis calculations and store in class instance."""
201
self. = self.(10)
202
self. = self.(13.7)
203
self. = self.(15)
204
self. = self.()
205
self. = self.(self.)
206
self. = self.()
207
self.['x'] = self.()[0]
208
self.['z'] = self.()[1]
209
self.['xz'] = self.()[2]
210
spar_dx = self.(self.)
211
spar_dz = self.(self.)
212
self.. = self.(,,
213
,0,self..)
214
self.. = self.(,,
215
0,,self..)
216
return None
217
218
219
defplot_geom(evaluator):
220
"""This function plots analysis results over the airfoil's geometry."""
221
# Plot chord
222
x_chord = [0, evaluator.]
223
y_chord = [0, 0]
224
plt.(,,linewidth='1')
225
# Plot quarter chord
226
plt.(./4,0,
227
'.',color='g',markersize=24,label='Quarter-chord')
228
# Plot airfoil surfaces
229
x = [0.98 * x for x in evaluator..]
230
y = [0.98 * z for z in evaluator..]
231
plt.(,,color='w',linewidth='1',fill=False)
232
x = [1.02 * x for x in evaluator..]
233
y = [1.02 * z for z in evaluator..]
234
plt.(,,color='b',linewidth='1',fill=False)
235
236
# Plot spars
237
try:
238
for _ in range(len(..)):
239
x = (evaluator..[])
240
y = (evaluator..[])
241
plt.(,,'-',color='b')
242
except AttributeError:
243
print('No spars to plot.')
244
# Plot stringers
245
try:
246
for _ in range(0,len(..)):
247
x = evaluator..[]
248
y = evaluator..[]
249
plt.(,,'.',color='y',markersize=12)
250
except AttributeError:
251
print('No stringers to plot.')
252
253
# Plot centroid
254
x = evaluator.[0]
255
y = evaluator.[1]
256
plt.(,,'.',color='r',markersize=24,label='centroid')
257
258
# Graph formatting
259
plt.('X axis')
260
plt.('Z axis')
261
262
plot_bound = max(..)
263
plt.(-0.10*,1.10*)
264
plt.(-(1.10*/2),(1.10*/2))
265
plt.().('equal',adjustable='box')
266
plt.().()
267
plt.(axis='both',linestyle=':',linewidth=1)
268
plt.()
269
return None
270
271
272
defplot_lift(evaluator):
273
x = range(.)
274
y_1 = evaluator.
275
y_2 = evaluator.
276
y_3 = evaluator.
277
plt.(,,'.',color='b',markersize=4,label='Rectangular lift')
278
plt.(,,'.',color='g',markersize=4,label='Elliptical lift')
279
plt.(,,'.',color='r',markersize=4,label='Total lift')
280
281
# Graph formatting
282
plt.('Semi-span location')
283
plt.('Lift')
284
285
plt.().()
286
plt.(axis='both',linestyle=':',linewidth=1)
287
plt.()
288
return None
289