Folder structure

Commit
b6cbe69cd70050bd88c789b3b80131bb87bc7886
Author
blendoit <blendoit@gmail.com>
Author date
Committer
blendoit <blendoit@gmail.com>
Committer date
Changed files
__init__.py
index e69de29b..00000000 100644..000000
creator/__init__.py
index 6fc3a2c4..7662f945 100644..100644
@@ -1,1 +1,8 @@
1 Removed: __all__ = ['base', 'fuselage', 'propulsion', 'wing']
1 Added: from . import base
2 Added: from . import fuselage
3 Added: from . import propulsion
4 Added: from . import wing
5 Added: # import creator.base
6 Added: # import creator.fuselage
7 Added: # import creator.propulsion
8 Added: # import creator.wing
creator/base.py
index 91d4c700..2566a6b4 100644..100644
@@ -9,6 +9,11 @@
9 9 format='%(asctime)s - %(levelname)s - %(message)s')
10 10
11 11
12 Added: class Aircraft:
13 Added: """This class tracks all sub-components and is fed to the evaluator."""
14 Added: pass
15 Added:
16 Added:
12 17 class Component:
13 18 """Basic component providing coordinates and tools."""
14 19
@@ -52,8 +57,3 @@
52 57 print(f'Unable to write {file_name} to specified directory.\n',
53 58 'Was the full path passed to the function?')
54 59 return None
55 Removed:
56 Removed:
57 Removed: class Aircraft:
58 Removed: """This class tracks all sub-components and is fed to the evaluator."""
59 Removed: pass
creator/wing.py
index be8e18a0..81783665 100644..100644
@@ -316,11 +316,3 @@
316 316 bbox_transform=plt.gcf().transFigure)
317 317 plt.show()
318 318 return fig, ax
319 Removed:
320 Removed:
321 Removed: def main():
322 Removed: return None
323 Removed:
324 Removed:
325 Removed: if __name__ == '__main__':
326 Removed: main()
evaluator.py
index 00000000..afbde9c0 000000..100644
@@ -0,0 +1,301 @@
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 for the airfoil passed as argument."""
17 Added: def __init__(self, aircraft):
18 Added: # Evaluator knows all geometrical info from evaluated airfoil
19 Added: self.airfoil = self.get_airfoil(aircraft)
20 Added: self.spars = self.get_spars(aircraft)
21 Added: self.stringers = self.get_stringers(aircraft)
22 Added: # Lift
23 Added: self.lift_rectangular = []
24 Added: self.lift_elliptical = []
25 Added: self.lift_total = []
26 Added: # Drag
27 Added: self.drag = []
28 Added: # centroid
29 Added: self.centroid = []
30 Added: # Inertia terms:
31 Added: self.I_ = {'x': 0, 'z': 0, 'xz': 0}
32 Added:
33 Added: def __str__(self):
34 Added: return type(self).__name__
35 Added:
36 Added: def get_airfoil(self, aircraft):
37 Added: """Get data of spars belonging to aircraft."""
38 Added: try:
39 Added: pass
40 Added: except:
41 Added: pass
42 Added: pass
43 Added:
44 Added: def get_spars(self, aircraft):
45 Added: """Get data of spars belonging to aircraft."""
46 Added: try:
47 Added: pass
48 Added: except:
49 Added: pass
50 Added: pass
51 Added:
52 Added: def get_stringers(self, aircraft):
53 Added: """Get data of spars belonging to aircraft."""
54 Added: try:
55 Added: pass
56 Added: except:
57 Added: pass
58 Added: pass
59 Added:
60 Added: def info_print(self, round):
61 Added: """Print all the component's evaluated data to the terminal."""
62 Added: name = f' {print(self)} DATA FOR {str(self).upper()} '
63 Added: num_of_dashes = len(name)
64 Added: print(num_of_dashes * '-')
65 Added: print(name)
66 Added: for k, v in self.__dict__.items():
67 Added: if type(v) != list:
68 Added: print('{}:\n'.format(k), v)
69 Added: print(num_of_dashes * '-')
70 Added: for k, v in self.__dict__.items():
71 Added: if type(v) == list:
72 Added: print('{}:\n'.format(k), np.around(v, round))
73 Added: return None
74 Added:
75 Added: def info_save(self, save_path, number):
76 Added: """Save all the object's coordinates (must be full path)."""
77 Added: file_name = 'airfoil_{}_eval.txt'.format(number)
78 Added: full_path = os.path.join(save_path, file_name)
79 Added: try:
80 Added: with open(full_path, 'w') as sys.stdout:
81 Added: self.info_print(6)
82 Added: # This line required to reset behavior of sys.stdout
83 Added: sys.stdout = sys.__stdout__
84 Added: print('Successfully wrote to file {}'.format(full_path))
85 Added: except IOError:
86 Added: print(
87 Added: 'Unable to write {} to specified directory.\n'.format(
88 Added: file_name), 'Was the full path passed to the function?')
89 Added: return None
90 Added:
91 Added: # All these functions take integer arguments and return lists.
92 Added:
93 Added: def get_lift_rectangular(self, lift):
94 Added: L_prime = [lift / (self.semi_span * 2) for x in range(self.semi_span)]
95 Added: return L_prime
96 Added:
97 Added: def get_lift_elliptical(self, L_0):
98 Added: L_prime = [
99 Added: L_0 / (self.semi_span * 2) * sqrt(1 - (y / self.semi_span)**2)
100 Added: for y in range(self.semi_span)
101 Added: ]
102 Added: return L_prime
103 Added:
104 Added: def get_lift_total(self):
105 Added: F_z = [(self.lift_rectangular[_] + self.lift_elliptical[_]) / 2
106 Added: for _ in range(len(self.lift_rectangular))]
107 Added: return F_z
108 Added:
109 Added: def get_mass_distribution(self, total_mass):
110 Added: F_z = [total_mass / self.semi_span for x in range(0, self.semi_span)]
111 Added: return F_z
112 Added:
113 Added: def get_drag(self, drag):
114 Added: # Transform semi-span integer into list
115 Added: semi_span = [x for x in range(0, self.semi_span)]
116 Added:
117 Added: # Drag increases after 80% of the semi_span
118 Added: cutoff = round(0.8 * self.semi_span)
119 Added:
120 Added: # Drag increases by 25% after 80% of the semi_span
121 Added: F_x = [drag for x in semi_span[0:cutoff]]
122 Added: F_x.extend([1.25 * drag for x in semi_span[cutoff:]])
123 Added: return F_x
124 Added:
125 Added: def get_centroid(self):
126 Added: """Return the coordinates of the centroid."""
127 Added: stringer_area = self.stringer.area
128 Added: cap_area = self.spar.cap_area
129 Added:
130 Added: caps_x = [value for spar in self.spar.x for value in spar]
131 Added: caps_z = [value for spar in self.spar.z for value in spar]
132 Added: stringers_x = self.stringer.x
133 Added: stringers_z = self.stringer.z
134 Added:
135 Added: denominator = float(
136 Added: len(caps_x) * cap_area + len(stringers_x) * stringer_area)
137 Added:
138 Added: centroid_x = float(
139 Added: sum([x * cap_area for x in caps_x]) +
140 Added: sum([x * stringer_area for x in stringers_x]))
141 Added: centroid_x = centroid_x / denominator
142 Added:
143 Added: centroid_z = float(
144 Added: sum([z * cap_area for z in caps_z]) +
145 Added: sum([z * stringer_area for z in stringers_z]))
146 Added: centroid_z = centroid_z / denominator
147 Added:
148 Added: return (centroid_x, centroid_z)
149 Added:
150 Added: def get_inertia_terms(self):
151 Added: """Obtain all inertia terms."""
152 Added: stringer_area = self.stringer.area
153 Added: cap_area = self.spar.cap_area
154 Added:
155 Added: # Adds upper and lower components' coordinates to list
156 Added: x_stringers = self.stringer.x
157 Added: z_stringers = self.stringer.z
158 Added: x_spars = self.spar.x[:][0] + self.spar.x[:][1]
159 Added: z_spars = self.spar.z[:][0] + self.spar.z[:][1]
160 Added: stringer_count = range(len(x_stringers))
161 Added: spar_count = range(len(self.spar.x))
162 Added:
163 Added: # I_x is the sum of the contributions of the spar caps and stringers
164 Added: # TODO: replace list indices with dictionary value
165 Added: I_x = sum([
166 Added: cap_area * (z_spars[i] - self.centroid[1])**2 for i in spar_count
167 Added: ])
168 Added: I_x += sum([
169 Added: stringer_area * (z_stringers[i] - self.centroid[1])**2
170 Added: for i in stringer_count
171 Added: ])
172 Added:
173 Added: I_z = sum([
174 Added: cap_area * (x_spars[i] - self.centroid[0])**2 for i in spar_count
175 Added: ])
176 Added: I_z += sum([
177 Added: stringer_area * (x_stringers[i] - self.centroid[0])**2
178 Added: for i in stringer_count
179 Added: ])
180 Added:
181 Added: I_xz = sum([
182 Added: cap_area * (x_spars[i] - self.centroid[0]) *
183 Added: (z_spars[i] - self.centroid[1]) for i in spar_count
184 Added: ])
185 Added: I_xz += sum([
186 Added: stringer_area * (x_stringers[i] - self.centroid[0]) *
187 Added: (z_stringers[i] - self.centroid[1]) for i in stringer_count
188 Added: ])
189 Added: return (I_x, I_z, I_xz)
190 Added:
191 Added: def get_dx(self, component):
192 Added: return [x - self.centroid[0] for x in component.x_start]
193 Added:
194 Added: def get_dz(self, component):
195 Added: return [x - self.centroid[1] for x in component.x_start]
196 Added:
197 Added: def get_dP(self, xDist, zDist, V_x, V_z, area):
198 Added: I_x = self.I_['x']
199 Added: I_z = self.I_['z']
200 Added: I_xz = self.I_['xz']
201 Added: denom = float(I_x * I_z - I_xz**2)
202 Added: z = float()
203 Added: for _ in range(len(xDist)):
204 Added: z += float(-area * xDist[_] * (I_x * V_x - I_xz * V_z) / denom -
205 Added: area * zDist[_] * (I_z * V_z - I_xz * V_x) / denom)
206 Added: return z
207 Added:
208 Added: def analysis(self, V_x, V_z):
209 Added: """Perform all analysis calculations and store in class instance."""
210 Added: self.drag = self.get_drag(10)
211 Added: self.lift_rectangular = self.get_lift_rectangular(13.7)
212 Added: self.lift_elliptical = self.get_lift_elliptical(15)
213 Added: self.lift_total = self.get_lift_total()
214 Added: self.mass_dist = self.get_mass_distribution(self.mass_total)
215 Added: self.centroid = self.get_centroid()
216 Added: self.I_['x'] = self.get_inertia_terms()[0]
217 Added: self.I_['z'] = self.get_inertia_terms()[1]
218 Added: self.I_['xz'] = self.get_inertia_terms()[2]
219 Added: spar_dx = self.get_dx(self.spar)
220 Added: spar_dz = self.get_dz(self.spar)
221 Added: self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
222 Added: self.spar.cap_area)
223 Added: self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
224 Added: self.spar.cap_area)
225 Added: return None
226 Added:
227 Added:
228 Added: def plot_geom(evaluator):
229 Added: """This function plots analysis results over the airfoil's geometry."""
230 Added: # Plot chord
231 Added: x_chord = [0, evaluator.chord]
232 Added: y_chord = [0, 0]
233 Added: plt.plot(x_chord, y_chord, linewidth='1')
234 Added: # Plot quarter chord
235 Added: plt.plot(evaluator.chord / 4,
236 Added: 0,
237 Added: '.',
238 Added: color='g',
239 Added: markersize=24,
240 Added: label='Quarter-chord')
241 Added: # Plot airfoil surfaces
242 Added: x = [0.98 * x for x in evaluator.airfoil.x]
243 Added: y = [0.98 * z for z in evaluator.airfoil.z]
244 Added: plt.fill(x, y, color='w', linewidth='1', fill=False)
245 Added: x = [1.02 * x for x in evaluator.airfoil.x]
246 Added: y = [1.02 * z for z in evaluator.airfoil.z]
247 Added: plt.fill(x, y, color='b', linewidth='1', fill=False)
248 Added:
249 Added: # Plot spars
250 Added: try:
251 Added: for _ in range(len(evaluator.spar.x)):
252 Added: x = (evaluator.spar.x[_])
253 Added: y = (evaluator.spar.z[_])
254 Added: plt.plot(x, y, '-', color='b')
255 Added: except AttributeError:
256 Added: print('No spars to plot.')
257 Added: # Plot stringers
258 Added: try:
259 Added: for _ in range(0, len(evaluator.stringer.x)):
260 Added: x = evaluator.stringer.x[_]
261 Added: y = evaluator.stringer.z[_]
262 Added: plt.plot(x, y, '.', color='y', markersize=12)
263 Added: except AttributeError:
264 Added: print('No stringers to plot.')
265 Added:
266 Added: # Plot centroid
267 Added: x = evaluator.centroid[0]
268 Added: y = evaluator.centroid[1]
269 Added: plt.plot(x, y, '.', color='r', markersize=24, label='centroid')
270 Added:
271 Added: # Graph formatting
272 Added: plt.xlabel('X axis')
273 Added: plt.ylabel('Z axis')
274 Added:
275 Added: plot_bound = max(evaluator.airfoil.x)
276 Added: plt.xlim(-0.10 * plot_bound, 1.10 * plot_bound)
277 Added: plt.ylim(-(1.10 * plot_bound / 2), (1.10 * plot_bound / 2))
278 Added: plt.gca().set_aspect('equal', adjustable='box')
279 Added: plt.gca().legend()
280 Added: plt.grid(axis='both', linestyle=':', linewidth=1)
281 Added: plt.show()
282 Added: return None
283 Added:
284 Added:
285 Added: def plot_lift(evaluator):
286 Added: x = range(evaluator.semi_span)
287 Added: y_1 = evaluator.lift_rectangular
288 Added: y_2 = evaluator.lift_elliptical
289 Added: y_3 = evaluator.lift_total
290 Added: plt.plot(x, y_1, '.', color='b', markersize=4, label='Rectangular lift')
291 Added: plt.plot(x, y_2, '.', color='g', markersize=4, label='Elliptical lift')
292 Added: plt.plot(x, y_3, '.', color='r', markersize=4, label='Total lift')
293 Added:
294 Added: # Graph formatting
295 Added: plt.xlabel('Semi-span location')
296 Added: plt.ylabel('Lift')
297 Added:
298 Added: plt.gca().legend()
299 Added: plt.grid(axis='both', linestyle=':', linewidth=1)
300 Added: plt.show()
301 Added: return None
evaluator/__init__.py
index e69de29b..00000000 100644..000000
evaluator/evaluator.py
index afbde9c0..00000000 100644..000000
@@ -1,301 +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 for the airfoil passed as argument."""
17 Removed: def __init__(self, aircraft):
18 Removed: # Evaluator knows all geometrical info from evaluated airfoil
19 Removed: self.airfoil = self.get_airfoil(aircraft)
20 Removed: self.spars = self.get_spars(aircraft)
21 Removed: self.stringers = self.get_stringers(aircraft)
22 Removed: # Lift
23 Removed: self.lift_rectangular = []
24 Removed: self.lift_elliptical = []
25 Removed: self.lift_total = []
26 Removed: # Drag
27 Removed: self.drag = []
28 Removed: # centroid
29 Removed: self.centroid = []
30 Removed: # Inertia terms:
31 Removed: self.I_ = {'x': 0, 'z': 0, 'xz': 0}
32 Removed:
33 Removed: def __str__(self):
34 Removed: return type(self).__name__
35 Removed:
36 Removed: def get_airfoil(self, aircraft):
37 Removed: """Get data of spars belonging to aircraft."""
38 Removed: try:
39 Removed: pass
40 Removed: except:
41 Removed: pass
42 Removed: pass
43 Removed:
44 Removed: def get_spars(self, aircraft):
45 Removed: """Get data of spars belonging to aircraft."""
46 Removed: try:
47 Removed: pass
48 Removed: except:
49 Removed: pass
50 Removed: pass
51 Removed:
52 Removed: def get_stringers(self, aircraft):
53 Removed: """Get data of spars belonging to aircraft."""
54 Removed: try:
55 Removed: pass
56 Removed: except:
57 Removed: pass
58 Removed: pass
59 Removed:
60 Removed: def info_print(self, round):
61 Removed: """Print all the component's evaluated data to the terminal."""
62 Removed: name = f' {print(self)} DATA FOR {str(self).upper()} '
63 Removed: num_of_dashes = len(name)
64 Removed: print(num_of_dashes * '-')
65 Removed: print(name)
66 Removed: for k, v in self.__dict__.items():
67 Removed: if type(v) != list:
68 Removed: print('{}:\n'.format(k), v)
69 Removed: print(num_of_dashes * '-')
70 Removed: for k, v in self.__dict__.items():
71 Removed: if type(v) == list:
72 Removed: print('{}:\n'.format(k), np.around(v, round))
73 Removed: return None
74 Removed:
75 Removed: def info_save(self, save_path, number):
76 Removed: """Save all the object's coordinates (must be full path)."""
77 Removed: file_name = 'airfoil_{}_eval.txt'.format(number)
78 Removed: full_path = os.path.join(save_path, file_name)
79 Removed: try:
80 Removed: with open(full_path, 'w') as sys.stdout:
81 Removed: self.info_print(6)
82 Removed: # This line required to reset behavior of sys.stdout
83 Removed: sys.stdout = sys.__stdout__
84 Removed: print('Successfully wrote to file {}'.format(full_path))
85 Removed: except IOError:
86 Removed: print(
87 Removed: 'Unable to write {} to specified directory.\n'.format(
88 Removed: file_name), 'Was the full path passed to the function?')
89 Removed: return None
90 Removed:
91 Removed: # All these functions take integer arguments and return lists.
92 Removed:
93 Removed: def get_lift_rectangular(self, lift):
94 Removed: L_prime = [lift / (self.semi_span * 2) for x in range(self.semi_span)]
95 Removed: return L_prime
96 Removed:
97 Removed: def get_lift_elliptical(self, L_0):
98 Removed: L_prime = [
99 Removed: L_0 / (self.semi_span * 2) * sqrt(1 - (y / self.semi_span)**2)
100 Removed: for y in range(self.semi_span)
101 Removed: ]
102 Removed: return L_prime
103 Removed:
104 Removed: def get_lift_total(self):
105 Removed: F_z = [(self.lift_rectangular[_] + self.lift_elliptical[_]) / 2
106 Removed: for _ in range(len(self.lift_rectangular))]
107 Removed: return F_z
108 Removed:
109 Removed: def get_mass_distribution(self, total_mass):
110 Removed: F_z = [total_mass / self.semi_span for x in range(0, self.semi_span)]
111 Removed: return F_z
112 Removed:
113 Removed: def get_drag(self, drag):
114 Removed: # Transform semi-span integer into list
115 Removed: semi_span = [x for x in range(0, self.semi_span)]
116 Removed:
117 Removed: # Drag increases after 80% of the semi_span
118 Removed: cutoff = round(0.8 * self.semi_span)
119 Removed:
120 Removed: # Drag increases by 25% after 80% of the semi_span
121 Removed: F_x = [drag for x in semi_span[0:cutoff]]
122 Removed: F_x.extend([1.25 * drag for x in semi_span[cutoff:]])
123 Removed: return F_x
124 Removed:
125 Removed: def get_centroid(self):
126 Removed: """Return the coordinates of the centroid."""
127 Removed: stringer_area = self.stringer.area
128 Removed: cap_area = self.spar.cap_area
129 Removed:
130 Removed: caps_x = [value for spar in self.spar.x for value in spar]
131 Removed: caps_z = [value for spar in self.spar.z for value in spar]
132 Removed: stringers_x = self.stringer.x
133 Removed: stringers_z = self.stringer.z
134 Removed:
135 Removed: denominator = float(
136 Removed: len(caps_x) * cap_area + len(stringers_x) * stringer_area)
137 Removed:
138 Removed: centroid_x = float(
139 Removed: sum([x * cap_area for x in caps_x]) +
140 Removed: sum([x * stringer_area for x in stringers_x]))
141 Removed: centroid_x = centroid_x / denominator
142 Removed:
143 Removed: centroid_z = float(
144 Removed: sum([z * cap_area for z in caps_z]) +
145 Removed: sum([z * stringer_area for z in stringers_z]))
146 Removed: centroid_z = centroid_z / denominator
147 Removed:
148 Removed: return (centroid_x, centroid_z)
149 Removed:
150 Removed: def get_inertia_terms(self):
151 Removed: """Obtain all inertia terms."""
152 Removed: stringer_area = self.stringer.area
153 Removed: cap_area = self.spar.cap_area
154 Removed:
155 Removed: # Adds upper and lower components' coordinates to list
156 Removed: x_stringers = self.stringer.x
157 Removed: z_stringers = self.stringer.z
158 Removed: x_spars = self.spar.x[:][0] + self.spar.x[:][1]
159 Removed: z_spars = self.spar.z[:][0] + self.spar.z[:][1]
160 Removed: stringer_count = range(len(x_stringers))
161 Removed: spar_count = range(len(self.spar.x))
162 Removed:
163 Removed: # I_x is the sum of the contributions of the spar caps and stringers
164 Removed: # TODO: replace list indices with dictionary value
165 Removed: I_x = sum([
166 Removed: cap_area * (z_spars[i] - self.centroid[1])**2 for i in spar_count
167 Removed: ])
168 Removed: I_x += sum([
169 Removed: stringer_area * (z_stringers[i] - self.centroid[1])**2
170 Removed: for i in stringer_count
171 Removed: ])
172 Removed:
173 Removed: I_z = sum([
174 Removed: cap_area * (x_spars[i] - self.centroid[0])**2 for i in spar_count
175 Removed: ])
176 Removed: I_z += sum([
177 Removed: stringer_area * (x_stringers[i] - self.centroid[0])**2
178 Removed: for i in stringer_count
179 Removed: ])
180 Removed:
181 Removed: I_xz = sum([
182 Removed: cap_area * (x_spars[i] - self.centroid[0]) *
183 Removed: (z_spars[i] - self.centroid[1]) for i in spar_count
184 Removed: ])
185 Removed: I_xz += sum([
186 Removed: stringer_area * (x_stringers[i] - self.centroid[0]) *
187 Removed: (z_stringers[i] - self.centroid[1]) for i in stringer_count
188 Removed: ])
189 Removed: return (I_x, I_z, I_xz)
190 Removed:
191 Removed: def get_dx(self, component):
192 Removed: return [x - self.centroid[0] for x in component.x_start]
193 Removed:
194 Removed: def get_dz(self, component):
195 Removed: return [x - self.centroid[1] for x in component.x_start]
196 Removed:
197 Removed: def get_dP(self, xDist, zDist, V_x, V_z, area):
198 Removed: I_x = self.I_['x']
199 Removed: I_z = self.I_['z']
200 Removed: I_xz = self.I_['xz']
201 Removed: denom = float(I_x * I_z - I_xz**2)
202 Removed: z = float()
203 Removed: for _ in range(len(xDist)):
204 Removed: z += float(-area * xDist[_] * (I_x * V_x - I_xz * V_z) / denom -
205 Removed: area * zDist[_] * (I_z * V_z - I_xz * V_x) / denom)
206 Removed: return z
207 Removed:
208 Removed: def analysis(self, V_x, V_z):
209 Removed: """Perform all analysis calculations and store in class instance."""
210 Removed: self.drag = self.get_drag(10)
211 Removed: self.lift_rectangular = self.get_lift_rectangular(13.7)
212 Removed: self.lift_elliptical = self.get_lift_elliptical(15)
213 Removed: self.lift_total = self.get_lift_total()
214 Removed: self.mass_dist = self.get_mass_distribution(self.mass_total)
215 Removed: self.centroid = self.get_centroid()
216 Removed: self.I_['x'] = self.get_inertia_terms()[0]
217 Removed: self.I_['z'] = self.get_inertia_terms()[1]
218 Removed: self.I_['xz'] = self.get_inertia_terms()[2]
219 Removed: spar_dx = self.get_dx(self.spar)
220 Removed: spar_dz = self.get_dz(self.spar)
221 Removed: self.spar.dP_x = self.get_dP(spar_dx, spar_dz, V_x, 0,
222 Removed: self.spar.cap_area)
223 Removed: self.spar.dP_z = self.get_dP(spar_dx, spar_dz, 0, V_z,
224 Removed: self.spar.cap_area)
225 Removed: return None
226 Removed:
227 Removed:
228 Removed: def plot_geom(evaluator):
229 Removed: """This function plots analysis results over the airfoil's geometry."""
230 Removed: # Plot chord
231 Removed: x_chord = [0, evaluator.chord]
232 Removed: y_chord = [0, 0]
233 Removed: plt.plot(x_chord, y_chord, linewidth='1')
234 Removed: # Plot quarter chord
235 Removed: plt.plot(evaluator.chord / 4,
236 Removed: 0,
237 Removed: '.',
238 Removed: color='g',
239 Removed: markersize=24,
240 Removed: label='Quarter-chord')
241 Removed: # Plot airfoil surfaces
242 Removed: x = [0.98 * x for x in evaluator.airfoil.x]
243 Removed: y = [0.98 * z for z in evaluator.airfoil.z]
244 Removed: plt.fill(x, y, color='w', linewidth='1', fill=False)
245 Removed: x = [1.02 * x for x in evaluator.airfoil.x]
246 Removed: y = [1.02 * z for z in evaluator.airfoil.z]
247 Removed: plt.fill(x, y, color='b', linewidth='1', fill=False)
248 Removed:
249 Removed: # Plot spars
250 Removed: try:
251 Removed: for _ in range(len(evaluator.spar.x)):
252 Removed: x = (evaluator.spar.x[_])
253 Removed: y = (evaluator.spar.z[_])
254 Removed: plt.plot(x, y, '-', color='b')
255 Removed: except AttributeError:
256 Removed: print('No spars to plot.')
257 Removed: # Plot stringers
258 Removed: try:
259 Removed: for _ in range(0, len(evaluator.stringer.x)):
260 Removed: x = evaluator.stringer.x[_]
261 Removed: y = evaluator.stringer.z[_]
262 Removed: plt.plot(x, y, '.', color='y', markersize=12)
263 Removed: except AttributeError:
264 Removed: print('No stringers to plot.')
265 Removed:
266 Removed: # Plot centroid
267 Removed: x = evaluator.centroid[0]
268 Removed: y = evaluator.centroid[1]
269 Removed: plt.plot(x, y, '.', color='r', markersize=24, label='centroid')
270 Removed:
271 Removed: # Graph formatting
272 Removed: plt.xlabel('X axis')
273 Removed: plt.ylabel('Z axis')
274 Removed:
275 Removed: plot_bound = max(evaluator.airfoil.x)
276 Removed: plt.xlim(-0.10 * plot_bound, 1.10 * plot_bound)
277 Removed: plt.ylim(-(1.10 * plot_bound / 2), (1.10 * plot_bound / 2))
278 Removed: plt.gca().set_aspect('equal', adjustable='box')
279 Removed: plt.gca().legend()
280 Removed: plt.grid(axis='both', linestyle=':', linewidth=1)
281 Removed: plt.show()
282 Removed: return None
283 Removed:
284 Removed:
285 Removed: def plot_lift(evaluator):
286 Removed: x = range(evaluator.semi_span)
287 Removed: y_1 = evaluator.lift_rectangular
288 Removed: y_2 = evaluator.lift_elliptical
289 Removed: y_3 = evaluator.lift_total
290 Removed: plt.plot(x, y_1, '.', color='b', markersize=4, label='Rectangular lift')
291 Removed: plt.plot(x, y_2, '.', color='g', markersize=4, label='Elliptical lift')
292 Removed: plt.plot(x, y_3, '.', color='r', markersize=4, label='Total lift')
293 Removed:
294 Removed: # Graph formatting
295 Removed: plt.xlabel('Semi-span location')
296 Removed: plt.ylabel('Lift')
297 Removed:
298 Removed: plt.gca().legend()
299 Removed: plt.grid(axis='both', linestyle=':', linewidth=1)
300 Removed: plt.show()
301 Removed: return None
example_airfoil.py
index a5e1cced..14cf9167 100644..100644
@@ -9,9 +9,10 @@
9 9 Generate a population of airfoils & optimize.
10 10 """
11 11
12 Removed: from resources import materials as mt
13 Removed: from creator import *
14 Removed: from evaluator import evaluator
12 Added: import resources.materials as mt
13 Added: import creator
14 Added: import evaluator
15 Added: import generator
15 16 # from generator import
16 17
17 18 import time
@@ -42,16 +43,16 @@
42 43 SAVE_PATH = '/home/blendux/Projects/Aircraft_Studio/save'
43 44
44 45 # Create aircraft instance
45 Removed: aircraft = base.Aircraft
46 Added: aircraft = creator.base.Aircraft
46 47 # Create airfoil instance
47 Removed: af = wing.Airfoil(20, 150, mt.aluminium)
48 Added: af = creator.wing.Airfoil(68, 150, mt.aluminium)
48 49 af.add_naca(NACA_NUM)
49 50 af.info_print(2)
50 51 # af.info_save(SAVE_PATH, 'foo_name')
51 52
52 53 # Create spar instances
53 Removed: af.spar1 = wing.Spar(af, 0.23, mt.aluminium)
54 Removed: af.spar2 = wing.Spar(af, 0.57, mt.aluminium)
54 Added: af.spar1 = creator.wing.Spar(af, 0.23, mt.aluminium)
55 Added: af.spar2 = creator.wing.Spar(af, 0.57, mt.aluminium)
55 56 # af.spar1.info_print(2)
56 57 # af.spar2.info_print(2)
57 58 # af.spar1.info_save(SAVE_PATH, 'spar1')
generator.py
index 02138283..00000000 100644..000000
@@ -1,64 +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: The generator.py module contains a single Population class,
17 Removed: which represents a collection of randomized airfoils.
18 Removed: """
19 Removed:
20 Removed: from tools import creator
21 Removed:
22 Removed:
23 Removed: def default_airfoil():
24 Removed: """Generate the default airfoil."""
25 Removed: airfoil = creator.Airfoil.from_dimensions(100, 200)
26 Removed: airfoil.add_naca(2412)
27 Removed: airfoil.add_mass(10)
28 Removed:
29 Removed: airfoil.spar = creator.Spar()
30 Removed: airfoil.spar.add_coord(airfoil, 0.23)
31 Removed: airfoil.spar.add_coord(airfoil, 0.57)
32 Removed: airfoil.spar.add_spar_caps(0.3)
33 Removed: airfoil.spar.add_mass(10)
34 Removed: airfoil.spar.add_webs(0.4)
35 Removed:
36 Removed: airfoil.stringer = creator.Stringer()
37 Removed: airfoil.stringer.add_coord(airfoil, 3, 6, 5, 4)
38 Removed: airfoil.stringer.add_area(0.1)
39 Removed: airfoil.stringer.add_mass(5)
40 Removed: airfoil.stringer.add_webs(0.1)
41 Removed:
42 Removed: return airfoil
43 Removed:
44 Removed:
45 Removed: class Population(creator.Airfoil):
46 Removed: """Collection of random airfoils."""
47 Removed:
48 Removed: def __init__(self, size):
49 Removed: af = creator.Airfoil
50 Removed: # print(af)
51 Removed: self.size = size
52 Removed: self.gen_number = 0 # incremented for every generation
53 Removed:
54 Removed: def mutate(self, prob_mt):
55 Removed: """Randomly mutate the genes of prob_mt % of the population."""
56 Removed:
57 Removed: def crossover(self, prob_cx):
58 Removed: """Combine the genes of prob_cx % of the population."""
59 Removed:
60 Removed: def reproduce(self, prob_rp):
61 Removed: """Pass on the genes of the fittest prob_rp % of the population."""
62 Removed:
63 Removed: def fitness():
64 Removed: """Rate the fitness of an individual on a relative scale (0-100)"""
generator/generator.py
index 00000000..02138283 000000..100644
@@ -0,0 +1,64 @@
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: The generator.py module contains a single Population class,
17 Added: which represents a collection of randomized airfoils.
18 Added: """
19 Added:
20 Added: from tools import creator
21 Added:
22 Added:
23 Added: def default_airfoil():
24 Added: """Generate the default airfoil."""
25 Added: airfoil = creator.Airfoil.from_dimensions(100, 200)
26 Added: airfoil.add_naca(2412)
27 Added: airfoil.add_mass(10)
28 Added:
29 Added: airfoil.spar = creator.Spar()
30 Added: airfoil.spar.add_coord(airfoil, 0.23)
31 Added: airfoil.spar.add_coord(airfoil, 0.57)
32 Added: airfoil.spar.add_spar_caps(0.3)
33 Added: airfoil.spar.add_mass(10)
34 Added: airfoil.spar.add_webs(0.4)
35 Added:
36 Added: airfoil.stringer = creator.Stringer()
37 Added: airfoil.stringer.add_coord(airfoil, 3, 6, 5, 4)
38 Added: airfoil.stringer.add_area(0.1)
39 Added: airfoil.stringer.add_mass(5)
40 Added: airfoil.stringer.add_webs(0.1)
41 Added:
42 Added: return airfoil
43 Added:
44 Added:
45 Added: class Population(creator.Airfoil):
46 Added: """Collection of random airfoils."""
47 Added:
48 Added: def __init__(self, size):
49 Added: af = creator.Airfoil
50 Added: # print(af)
51 Added: self.size = size
52 Added: self.gen_number = 0 # incremented for every generation
53 Added:
54 Added: def mutate(self, prob_mt):
55 Added: """Randomly mutate the genes of prob_mt % of the population."""
56 Added:
57 Added: def crossover(self, prob_cx):
58 Added: """Combine the genes of prob_cx % of the population."""
59 Added:
60 Added: def reproduce(self, prob_rp):
61 Added: """Pass on the genes of the fittest prob_rp % of the population."""
62 Added:
63 Added: def fitness():
64 Added: """Rate the fitness of an individual on a relative scale (0-100)"""