Coverage for /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/site-packages/hypervehicle/hangar/x43.py: 93%
147 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-29 02:51 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-29 02:51 +0000
1import numpy as np
2from copy import deepcopy
3from hypervehicle import Vehicle
4from hypervehicle.generator import Generator
5from hypervehicle.components import Wing, common, Fin
6from hypervehicle.geometry import Vector3, Bezier, Line, Polyline
9class ParametricX43(Generator):
10 """Parametric generator for mock-up of the NASA X43-A demonstrator.
12 Dimensions have been approximated based on vehicle's visual proportions.
14 References
15 -----------
16 https://en.wikipedia.org/wiki/NASA_X-43
17 """
19 def __init__(self, **kwargs) -> None:
20 # Vehicle parameters
21 self.body_length = 3.7 # From body rear to nose
22 self.body_width = 1 # Body width
23 self.body_angle = 0 # Body slant angle (+ve nose down) (degrees)
25 self.wing_span = 0.4 # Span of each wing
26 self.wing_length = 1.00 # Wing length
27 self.wing_TE_back = 0.25
28 self.flap_length = 0.3 # Flap length
30 self.engine_h = 0.1 # Height of engine section
32 self.fin_height = 0.3 # Height of tail fin
33 self.fin_length = 1.0 # Length of fin
34 self.rudder_length = 0.2 # Length of tail fin rudder
36 # Configuration parameters
37 self.flap_angle = 0 # Elevon angle (+ve up) (degrees)
38 self.rudder_angle = 0 # Rudder angle (+ve to +y) (degrees)
40 # Complete instantiation
41 super().__init__(**kwargs)
43 def create_instance(self) -> Vehicle:
44 # Create Vehicle instance
45 x43 = Vehicle()
46 x43.configure(name="X-43A", verbosity=1)
48 #####################################################
49 ## WING GEOMETRY ##
50 #####################################################
52 # WING 1
53 # B0------------------------ B1
54 # | ---___
55 # | ---__ B2
56 # | |
57 # A0-------------------------A1---------------TT
59 # Define nominal parameters
60 L_nom = 3.7
62 L = self.body_length
63 beta = np.deg2rad(self.body_angle)
64 LE_height = 0.05 * L / L_nom
65 TE_height = 0.2 * L / L_nom
67 A0 = Vector3(x=0, y=0)
68 A1 = Vector3(x=1.8 * L / L_nom, y=0)
69 TT = Vector3(x=L, y=0)
70 B0 = Vector3(x=0, y=self.body_width / 2)
71 B1 = Vector3(x=A1.x, y=B0.y)
72 B2 = Vector3(x=TT.x, y=0.4 * B0.y)
74 B0B1 = Line(p0=B0, p1=B1)
75 B1B2 = Line(p0=B1, p1=B2)
76 B2TT = Line(p0=B2, p1=TT)
78 Line_B0TT = Polyline([B0B1, B1B2, B2TT])
80 # Top rounding Bezier points
81 rounding_thickness = 0.025
82 p1 = Vector3(x=0, y=0, z=rounding_thickness)
83 p2 = Vector3(x=0, y=0.9 * B0.y, z=p1.z)
84 p3 = Vector3(x=0, y=B0.y, z=0)
85 rounding_bez = Bezier([p1, p2, p3])
87 def get_local_y(x):
88 if x < B1.x:
89 local_y = B0.y
90 else:
91 local_y = (x - B2.x) * (B2.y - B1.y) / (B2.x - B1.x) + B2.y
92 return local_y
94 def wing1_tf_top(x, y, z=0):
95 # Nominal thickness
96 z_m = -L * np.tan(beta)
97 z_u = z_m - 0.5 * TE_height
98 beta_u = -np.arctan((z_u + 0.5 * LE_height) / L)
99 z_1 = (x - L) * np.tan(beta_u) - LE_height / 2
101 # Rounding thickness
102 local_y = get_local_y(x)
103 z_2 = -(L - x) * rounding_bez(y / local_y).z
105 # Sum
106 z_val = z_1 + z_2
108 return Vector3(x=0, y=0, z=z_val)
110 def wing1_tf_bot(x, y, z=0):
111 z_m = -L * np.tan(beta)
112 z_l = z_m + 0.5 * TE_height
113 beta_l = -np.arctan((z_l - 0.5 * LE_height) / L)
114 z_val = (x - L) * np.tan(beta_l) + LE_height / 2
115 return Vector3(x=0, y=0, z=z_val)
117 def leading_edge_width_function(r):
118 temp = Bezier(
119 [
120 Vector3(x=0.0, y=0.02),
121 Vector3(x=0.75, y=0.05),
122 Vector3(x=1.0, y=0.05),
123 ]
124 )
125 le_width = temp(r).y
126 return le_width
128 body = Wing(
129 A0=A0,
130 A1=A1,
131 TT=TT,
132 B0=B0,
133 Line_B0TT=Line_B0TT,
134 top_tf=wing1_tf_top,
135 bot_tf=wing1_tf_bot,
136 LE_wf=leading_edge_width_function,
137 stl_resolution=4,
138 )
140 # WING 2 (flaps)
141 # --------------
143 # fB0--__fB1
144 # \ \_
145 # \ \
146 # B0-----fTT----------------- B1
147 # | ---___
148 # | ---__ B2
149 # | |
150 # A0-------------------------A1---------------TT
152 flap_thickness = 0.03 * L / L_nom
154 fA0 = Vector3(x=B0.x + self.flap_length, y=B0.y)
155 fA1 = Vector3(
156 x=A0.x + 0.5 * self.wing_length,
157 y=get_local_y(A0.x + 0.5 * self.wing_length),
158 )
159 fTT = Vector3(x=A0.x + self.wing_length, y=get_local_y(A0.x + self.wing_length))
161 fB0 = Vector3(
162 x=A0.x - self.wing_TE_back + self.flap_length, y=B0.y + self.wing_span
163 )
164 fB1 = Vector3(x=A0.x + 0.4 * self.wing_length, y=B0.y + 0.75 * self.wing_span)
166 fB0B1 = Line(p0=fB0, p1=fB1)
167 fB1TT = Line(p0=fB1, p1=fTT)
169 Line_fB0TT = Polyline([fB0B1, fB1TT])
171 def wing2_tf_top(x, y, z=0):
172 z_ba = -(x - L) * np.tan(beta)
173 return Vector3(x=0, y=0, z=-flap_thickness / 2 - z_ba)
175 def wing2_tf_bot(x, y, z=0):
176 z_ba = -(x - L) * np.tan(beta)
177 return Vector3(x=0, y=0, z=flap_thickness / 2 - z_ba)
179 wing = Wing(
180 A0=fA0,
181 A1=fA1,
182 TT=fTT,
183 B0=fB0,
184 Line_B0TT=Line_fB0TT,
185 top_tf=wing2_tf_top,
186 bot_tf=wing2_tf_bot,
187 flap_length=self.flap_length,
188 flap_angle=self.flap_angle,
189 LE_wf=leading_edge_width_function,
190 close_wing=True,
191 )
193 # WING 3 (Inlet/exit)
194 # B0------------------------ B1
195 # | --__
196 # | --_ B2
197 # | |
198 # A0-------------------------A1-----------TT
199 iA0 = A0
200 iA1 = A1
201 iTT = Vector3(x=0.5 * (TT.x + B1.x), y=0)
202 iB0 = Vector3(x=B0.x, y=B0.y)
203 iB1 = Vector3(x=B1.x, y=B1.y)
204 iB2 = Vector3(
205 x=iTT.x, y=((iTT.x - B2.x) * (B2.y - B1.y) / (B2.x - B1.x) + B2.y)
206 )
208 iB0B1 = Line(p0=iB0, p1=iB1)
209 iB1B2 = Line(p0=iB1, p1=iB2)
210 iB2TT = Line(p0=iB2, p1=iTT)
212 Line_iB0TT = Polyline([iB0B1, iB1B2, iB2TT])
214 inlet_start = 0.8 * B1.x
215 exit_start = 0.3 * (B1.x - B0.x)
217 def inlet_tf_top(x, y, z=0):
218 vehicle_z = wing1_tf_bot(x, y, z).z
219 return Vector3(x=0, y=0, z=vehicle_z - 0.05)
221 def inlet_tf_bot(x, y, z=0):
222 # Get z-location of vehicle body
223 vehicle_body = wing1_tf_bot(x, y, z).z
225 if x < exit_start:
226 z_val = (self.engine_h / (exit_start - A0.x)) * x
227 elif x > inlet_start:
228 z_val = (-self.engine_h / (iTT.x - inlet_start)) * (x - iTT.x)
229 else:
230 z_val = self.engine_h
232 # Calculate taper z
233 taper_start_pc = 0.8 # Percentage of y_local where taper begins
234 y_local = get_local_y(x)
235 if y > taper_start_pc * y_local:
236 z_taper = (5 * y / y_local - 4) * z_val
237 else:
238 z_taper = 0
240 return Vector3(x=0, y=0, z=z_val + vehicle_body - z_taper + 0.00001)
242 def leading_edge_width_function2(r):
243 temp = Bezier(
244 [
245 Vector3(x=0.0, y=0.001),
246 Vector3(x=0.5, y=0.001),
247 Vector3(x=1.0, y=0.001),
248 ]
249 )
250 le_width = temp(r).y
251 return le_width
253 inlet = Wing(
254 A0=iA0,
255 A1=iA1,
256 TT=iTT,
257 B0=iB0,
258 Line_B0TT=Line_iB0TT,
259 top_tf=inlet_tf_top,
260 bot_tf=inlet_tf_bot,
261 LE_wf=leading_edge_width_function2,
262 stl_resolution=4,
263 )
265 #####################################################
266 ## FINS ##
267 #####################################################
268 # |--p1-----p2
269 # | | \
270 # | | \
271 # | | \
272 # |--p0______________p3
274 fin_p3x = self.fin_length
275 fin_thickness = 0.02 * L / L_nom
276 fin_offset = -(fin_p3x - L) * np.tan(beta) # offset upwards
277 y_shift = B0.y
279 p0 = Vector3(x=self.rudder_length, y=fin_offset)
280 p1 = Vector3(x=self.rudder_length, y=fin_offset + self.fin_height)
281 p2 = Vector3(x=0.5 * fin_p3x, y=fin_offset + self.fin_height)
282 p3 = Vector3(x=fin_p3x, y=fin_offset)
284 def fin_offset_function(x, y, z):
285 return Vector3(x=0, y=y_shift, z=0)
287 # Define the fin angles: this is the angle to rotate the fin, constructed in
288 # the x-y plane, to its final position, by rotating about the x axis
289 fin_angle = np.deg2rad(-90)
291 # Rudder angle (fin flap)
292 rudder_angle = np.deg2rad(self.rudder_angle)
293 fin1 = Fin(
294 p0=p0,
295 p1=p1,
296 p2=p2,
297 p3=p3,
298 fin_thickness=fin_thickness,
299 fin_angle=fin_angle,
300 top_thickness_function=common.uniform_thickness_function(
301 fin_thickness, "top"
302 ),
303 bot_thickness_function=common.uniform_thickness_function(
304 fin_thickness, "bot"
305 ),
306 LE_wf=leading_edge_width_function,
307 mirror=False,
308 rudder_type="sharp",
309 rudder_length=self.rudder_length,
310 rudder_angle=rudder_angle,
311 pivot_angle=0,
312 pivot_point=Vector3(x=0, y=0),
313 offset_func=fin_offset_function,
314 )
316 # Make second fin to account for rudder angle
317 def fin_offset_function2(x, y, z):
318 return Vector3(x=0, y=-y_shift, z=0)
320 fin2 = Fin(
321 p0=p0,
322 p1=p1,
323 p2=p2,
324 p3=p3,
325 fin_thickness=fin_thickness,
326 fin_angle=fin_angle,
327 top_thickness_function=common.uniform_thickness_function(
328 fin_thickness, "top"
329 ),
330 bot_thickness_function=common.uniform_thickness_function(
331 fin_thickness, "bot"
332 ),
333 LE_wf=leading_edge_width_function,
334 mirror=False,
335 rudder_type="sharp",
336 rudder_length=self.rudder_length,
337 rudder_angle=rudder_angle,
338 pivot_angle=0,
339 pivot_point=Vector3(x=0, y=0),
340 offset_func=fin_offset_function2,
341 )
343 # Add all components
344 x43.add_component(body, reflection_axis="y", name="body")
345 x43.add_component(deepcopy(wing), name="wing1")
346 x43.add_component(wing, reflection_axis="y", name="wing2")
347 x43.add_component(inlet, reflection_axis="y", name="inlet")
348 x43.add_component(fin1, name="fin1")
349 x43.add_component(fin2, name="fin2")
351 return x43
354if __name__ == "__main__":
355 # To create the nominal geometry
356 parametric_generator = ParametricX43()
357 x43 = parametric_generator.create_instance()
358 x43.generate()
359 x43.to_stl("x43")
361 # Assess inertial properties
362 # densities = {
363 # "wing_1": 1680,
364 # "wing_2": 5590,
365 # "wing_3": 1680,
366 # "fin_1": 5590,
367 # "fin_2": 5590,
368 # }
369 # volume, mass, cog, inertia = x43.analyse(densities)