Coverage for /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/hypervehicle/hangar/x43.py: 93%

147 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-25 22:58 +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 

7 

8 

9class ParametricX43(Generator): 

10 """Parametric generator for mock-up of the NASA X43-A demonstrator. 

11 

12 Dimensions have been approximated based on vehicle's visual proportions. 

13 

14 References 

15 ----------- 

16 https://en.wikipedia.org/wiki/NASA_X-43 

17 """ 

18 

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) 

24 

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 

29 

30 self.engine_h = 0.1 # Height of engine section 

31 

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 

35 

36 # Configuration parameters 

37 self.flap_angle = 0 # Elevon angle (+ve up) (degrees) 

38 self.rudder_angle = 0 # Rudder angle (+ve to +y) (degrees) 

39 

40 # Complete instantiation 

41 super().__init__(**kwargs) 

42 

43 def create_instance(self) -> Vehicle: 

44 # Create Vehicle instance 

45 x43 = Vehicle() 

46 x43.configure(name="X-43A", verbosity=1) 

47 

48 ##################################################### 

49 ## WING GEOMETRY ## 

50 ##################################################### 

51 

52 # WING 1 

53 # B0------------------------ B1 

54 # | ---___ 

55 # | ---__ B2 

56 # | | 

57 # A0-------------------------A1---------------TT 

58 

59 # Define nominal parameters 

60 L_nom = 3.7 

61 

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 

66 

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) 

73 

74 B0B1 = Line(p0=B0, p1=B1) 

75 B1B2 = Line(p0=B1, p1=B2) 

76 B2TT = Line(p0=B2, p1=TT) 

77 

78 Line_B0TT = Polyline([B0B1, B1B2, B2TT]) 

79 

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]) 

86 

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 

93 

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 

100 

101 # Rounding thickness 

102 local_y = get_local_y(x) 

103 z_2 = -(L - x) * rounding_bez(y / local_y).z 

104 

105 # Sum 

106 z_val = z_1 + z_2 

107 

108 return Vector3(x=0, y=0, z=z_val) 

109 

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) 

116 

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 

127 

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 ) 

139 

140 # WING 2 (flaps) 

141 # -------------- 

142 

143 # fB0--__fB1 

144 # \ \_ 

145 # \ \ 

146 # B0-----fTT----------------- B1 

147 # | ---___ 

148 # | ---__ B2 

149 # | | 

150 # A0-------------------------A1---------------TT 

151 

152 flap_thickness = 0.03 * L / L_nom 

153 

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)) 

160 

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) 

165 

166 fB0B1 = Line(p0=fB0, p1=fB1) 

167 fB1TT = Line(p0=fB1, p1=fTT) 

168 

169 Line_fB0TT = Polyline([fB0B1, fB1TT]) 

170 

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) 

174 

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) 

178 

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 ) 

192 

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 ) 

207 

208 iB0B1 = Line(p0=iB0, p1=iB1) 

209 iB1B2 = Line(p0=iB1, p1=iB2) 

210 iB2TT = Line(p0=iB2, p1=iTT) 

211 

212 Line_iB0TT = Polyline([iB0B1, iB1B2, iB2TT]) 

213 

214 inlet_start = 0.8 * B1.x 

215 exit_start = 0.3 * (B1.x - B0.x) 

216 

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) 

220 

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 

224 

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 

231 

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 

239 

240 return Vector3(x=0, y=0, z=z_val + vehicle_body - z_taper + 0.00001) 

241 

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 

252 

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 ) 

264 

265 ##################################################### 

266 ## FINS ## 

267 ##################################################### 

268 # |--p1-----p2 

269 # | | \ 

270 # | | \ 

271 # | | \ 

272 # |--p0______________p3 

273 

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 

278 

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) 

283 

284 def fin_offset_function(x, y, z): 

285 return Vector3(x=0, y=y_shift, z=0) 

286 

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) 

290 

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 ) 

315 

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) 

319 

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 ) 

342 

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") 

350 

351 return x43 

352 

353 

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") 

360 

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)