[Python] Design & build airplanes from your specifications.
README PlantUML diagrams
Changed files
README.org
@@ -9,13 +9,14 @@
9
9
10
10
I adapted it from my own code for the UCLA MAE 154B final project (Spring 2019).
11
11
12
Removed:
#+CAPTION: Test image for the README
13
Removed:
#+NAME: fig:Test-Image
14
Removed:
[[./doc/diagram.png]]
12
Added:
#+BEGIN_UML
13
Added:
Alice -> Bob: Authentication Request
14
Added:
Bob --> Alice: Authentication Response
15
Added:
#+END_UML
15
16
16
Removed:
#+CAPTION: Second test image for the README
17
Removed:
#+NAME: fig:Test-Image2
18
Removed:
[[./doc/diagram2.png]]
17
Added:
#+CAPTION: Program Flow Sequence
18
Added:
#+NAME: fig:sequence-program
19
Added:
[[./doc/sequence_program.png]]
19
20
20
21
* Program Structure
21
22
creator/base.py
@@ -1,4 +1,5 @@
1
1
"""The base.py module contains parent classes for components."""
2
Added:
2
3
import numpy as np
3
4
import sys
4
5
import os.path
@@ -11,19 +12,23 @@
11
12
12
13
class Aircraft:
13
14
"""This class tracks all sub-components and is fed to the evaluator."""
14
Removed:
pass
15
Added:
def __init__(self, parent):
16
Added:
self.parent = parent
15
17
16
18
17
19
class Component:
18
20
"""Basic component providing coordinates and tools."""
19
Removed:
20
Removed:
# TODO: define defaults in separate module
21
Removed:
def __init__(self):
21
Added:
def __init__(self, parent, name):
22
Added:
self.name = str()
23
Added:
self.parent = None
22
24
self.x = np.array([])
23
25
self.z = np.array([])
24
Removed:
self.material = str()
26
Added:
self.material = None
25
27
self.mass = float()
26
28
29
Added:
def __str__(self):
30
Added:
return self.name
31
Added:
27
32
def set_material(self, material):
28
33
"""Set the component bulk material."""
29
34
self.material = material
@@ -36,23 +41,30 @@
36
41
print(name)
37
42
for k, v in self.__dict__.items():
38
43
if type(v) != list:
39
Removed:
print('{}:\n'.format(k), v)
44
Added:
print(f'{k}:\n', v)
40
45
print(num_of_dashes * '-')
41
46
for k, v in self.__dict__.items():
42
47
if type(v) == list:
43
Removed:
print('{}:\n'.format(k), np.around(v, round))
48
Added:
print(f'{k}:\n', np.around(v, round))
44
49
return None
45
50
46
Removed:
def info_save(self, save_path, number):
51
Added:
def info_save(self,
52
Added:
save_path='/home/blendux/Projects/Aircraft_Studio/save'):
47
53
"""Save all the object's coordinates (must be full path)."""
48
Removed:
file_name = f'{str(self).lower()}_{number}.txt'
54
Added:
file_name = f'{self.name}_info.txt'
49
55
full_path = os.path.join(save_path, file_name)
50
56
try:
51
Removed:
with open(full_path, 'w') as sys.stdout:
52
Removed:
self.info_print(6)
53
Removed:
# This line required to reset behavior of sys.stdout
54
Removed:
sys.stdout = sys.__stdout__
55
Removed:
logging.debug(f'Successfully wrote to file {full_path}')
57
Added:
with open(full_path, 'w') as f:
58
Added:
for k, v in self.__dict__.items():
59
Added:
if type(v) != list:
60
Added:
f.write(f'{k}:\n')
61
Added:
f.write(str(v))
62
Added:
# print(num_of_dashes * '-')
63
Added:
for k, v in self.__dict__.items():
64
Added:
if type(v) == list:
65
Added:
f.write(f'{k}:\n')
66
Added:
f.write(str(v))
67
Added:
logging.debug(f'Successfully wrote to file {full_path}')
56
68
except IOError:
57
69
print(f'Unable to write {file_name} to specified directory.\n',
58
70
'Was the full path passed to the function?')
creator/wing.py
@@ -11,15 +11,16 @@
11
11
plot_geom(airfoil): generates a 2D plot of the airfoil & any components.
12
12
"""
13
13
14
Removed:
import creator.base as base
15
Removed:
16
14
import logging
17
15
import numpy as np
18
16
from math import sin, cos, atan
19
17
import bisect as bi
20
18
import matplotlib.pyplot as plt
21
19
20
Added:
import creator.base as base
21
Added:
import resources.materials as mt
22
22
23
Added:
23
24
class Airfoil(base.Component):
24
25
"""This class represents a single NACA airfoil.
25
26
@@ -32,24 +33,21 @@
32
33
to 3D CAD packages like SolidWorks, which can import such
33
34
geometry as coordinates written in a CSV file.
34
35
"""
35
Removed:
36
Removed:
# TODO: default values in separate module
37
Removed:
def __init__(self, chord, semi_span, material):
38
Removed:
super().__init__()
39
Removed:
# self.x = np.array([])
40
Removed:
# self.z = np.array([])
41
Removed:
# self.chord = chord
42
Removed:
"""Create airfoil from its chord and semi-span."""
43
Removed:
self.chord = chord if chord > 20 else 20
44
Removed:
if chord <= 20:
36
Added:
def __init__(self,
37
Added:
parent,
38
Added:
name,
39
Added:
chord=68,
40
Added:
semi_span=150,
41
Added:
material=mt.aluminium):
42
Added:
super().__init__(parent, name)
43
Added:
if chord > 20:
44
Added:
self.chord = chord
45
Added:
else:
46
Added:
self.chord = 20
45
47
logging.debug('Chord too small, using minimum value of 20.')
46
48
self.semi_span = semi_span
47
49
self.material = material
48
Removed:
self.naca_num = int()
49
50
50
Removed:
def __str__(self):
51
Removed:
return type(self).__name__
52
Removed:
53
51
def add_naca(self, naca_num):
54
52
"""Generate surface geometry for a NACA airfoil.
55
53
@@ -138,22 +136,22 @@
138
136
139
137
class Spar(base.Component):
140
138
"""Contains a single spar's data."""
141
Removed:
def __init__(self, airfoil, loc_percent, material):
139
Added:
def __init__(self, parent, name, loc_percent=0.30, material=mt.aluminium):
142
140
"""Set spar location as percent of chord length."""
143
Removed:
super().__init__()
141
Added:
super().__init__(parent, name)
144
142
super().set_material(material)
145
143
self.cap_area = float()
146
Removed:
loc = loc_percent * airfoil.chord
147
Removed:
# bi.bisect_left: returns index of first value in airfoil.x > loc
144
Added:
loc = loc_percent * parent.chord
145
Added:
# bi.bisect_left: returns index of first value in parent.x > loc
148
146
# This ensures that spar geom intersects with airfoil geom.
149
147
# Spar upper coordinates
150
Removed:
spar_u = bi.bisect_left(airfoil.x, loc) - 1
151
Removed:
self.x = np.append(self.x, airfoil.x[spar_u])
152
Removed:
self.z = np.append(self.z, airfoil.z[spar_u])
148
Added:
spar_u = bi.bisect_left(parent.x, loc) - 1
149
Added:
self.x = np.append(self.x, parent.x[spar_u])
150
Added:
self.z = np.append(self.z, parent.z[spar_u])
153
151
# Spar lower coordinates
154
Removed:
spar_l = bi.bisect_left(airfoil.x[::-1], loc)
155
Removed:
self.x = np.append(self.x, airfoil.x[-spar_l])
156
Removed:
self.z = np.append(self.z, airfoil.z[-spar_l])
152
Added:
spar_l = bi.bisect_left(parent.x[::-1], loc)
153
Added:
self.x = np.append(self.x, parent.x[-spar_l])
154
Added:
self.z = np.append(self.z, parent.z[-spar_l])
157
155
return None
158
156
159
157
def set_cap_area(self, cap_area):
doc/diagram.png
Binary files differ
doc/diagram.pum
@@ -1,5 +0,0 @@
1
Removed:
hide footbox
2
Removed:
title Sequence Diagram (Footer removed)
3
Removed:
4
Removed:
Alice -> Bob: Authentication Request
5
Removed:
Bob --> Alice: Authentication Response
doc/diagram.txt
@@ -1,17 +0,0 @@
1
Removed:
┌─────┐ ┌───┐ ┌────┐
2
Removed:
│Alice│ │Bob│ │John│
3
Removed:
└──┬──┘ └─┬─┘ └─┬──┘
4
Removed:
│Authentication Request │ │
5
Removed:
│───────────────────────>│ │
6
Removed:
│ │ │
7
Removed:
│Authentication Response │ │
8
Removed:
│<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│ │
9
Removed:
│ │ │
10
Removed:
│ another Authentication Request │
11
Removed:
│<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
12
Removed:
│ │ │
13
Removed:
│ another Authentication Response │
14
Removed:
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│
15
Removed:
┌──┴──┐ ┌─┴─┐ ┌─┴──┐
16
Removed:
│Alice│ │Bob│ │John│
17
Removed:
└─────┘ └───┘ └────┘
doc/diagram2.png
Binary files differ
doc/sequence_program.png
Binary files differ
doc/sequence_program.pum
@@ -0,0 +1,25 @@
1
Added:
@startuml
2
Added:
skin BlueModern
3
Added:
actor User
4
Added:
User -> evaluator: evaluator parameters
5
Added:
evaluator -> User: evaluator object
6
Added:
7
Added:
User -> creator: specify desired a/c specs
8
Added:
9
Added:
creator --> evaluator: inform evaluator of desired specs
10
Added:
11
Added:
evaluator --> creator: request acceptable components
12
Added:
13
Added:
creator -> User: acceptable initial a/c candidate
14
Added:
15
Added:
User -> generator: specify genetic variability
16
Added:
17
Added:
generator --> creator: request varied a/c
18
Added:
creator --> generator: varied a/c population
19
Added:
generator <--> evaluator: exchange population to evaluate\nwith new population based on\nfittest individuals
20
Added:
evaluator -> User: return individual a/c specs
21
Added:
22
Added:
generator -> User: return population fitness and top candidates
23
Added:
24
Added:
creator --> generator: return varied aircrafts
25
Added:
@enduml
example_airfoil.py
@@ -42,21 +42,22 @@
42
42
43
43
SAVE_PATH = '/home/blendux/Projects/Aircraft_Studio/save'
44
44
45
Added:
eval = evaluator.Evaluator
45
46
# Create aircraft instance
46
Removed:
aircraft = creator.base.Aircraft
47
Added:
ac = creator.base.Aircraft(eval)
47
48
# Create airfoil instance
48
Removed:
af = creator.wing.Airfoil(68, 150, mt.aluminium)
49
Added:
af = creator.wing.Airfoil(ac, 'af')
49
50
af.add_naca(NACA_NUM)
50
Removed:
af.info_print(2)
51
Removed:
# af.info_save(SAVE_PATH, 'foo_name')
51
Added:
# af.info_print(2)
52
Added:
af.info_save()
52
53
53
54
# Create spar instances
54
Removed:
af.spar1 = creator.wing.Spar(af, 0.23, mt.aluminium)
55
Removed:
af.spar2 = creator.wing.Spar(af, 0.57, mt.aluminium)
55
Added:
af.spar1 = creator.wing.Spar(af, 'spar1')
56
Added:
af.spar2 = creator.wing.Spar(af, 'spar2', 0.57)
56
57
# af.spar1.info_print(2)
57
58
# af.spar2.info_print(2)
58
Removed:
af.spar1.info_save(SAVE_PATH, 'spar1')
59
Removed:
af.spar2.info_save(SAVE_PATH, 'spar2')
59
Added:
af.spar1.info_save()
60
Added:
af.spar2.info_save()
60
61
61
62
# # Create stringer instance
62
63
# af.stringer = wing.Stringer()
@@ -72,7 +73,6 @@
72
73
# creator.wing.plot_geom(af, [af.spar1, af.spar2], None)
73
74
74
75
# Evaluator object contains airfoil analysis results.
75
Removed:
eval = evaluator.Evaluator(aircraft)
76
76
# The analysis is performed in the evaluator.py module.
77
77
# eval.analysis(1, 1)
78
78
# eval.info_print(2)