STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_vehicle_drag.py
Go to the documentation of this file.
1"""Drag of the flown vehicle (FLT-2).
2
3The flight sim flew Cd 0.45 at every Mach, motor on and off, whatever the length, fins or finish.
4The shipped vehicle is 7.76 m long on a 0.157 m body, 226 wetted areas per reference area: fully
5turbulent skin friction alone exceeds 0.45. The build-up is Barrowman's as the OpenRocket technical
6documentation (Niskanen 2013) gives it, on the geometry the sim assembles.
7
8Independent sources here: Prandtl-Schlichting's smooth flat plate 0.455 / (log10 Re)^2.58, hand
9geometry of the stack and the fins, the base-drag formula (Niskanen eq. 3.94 / sec. 3.4.5), the
101976 standard atmosphere from `fluids`, Stoney's fineness-3 table, and a hand trapezoid.
11"""
12
13from __future__ import annotations
14
15import math
16
17import pytest
18
19from flight_cases import fly, shipped
20
21rocketpy = pytest.importorskip("rocketpy")
22
23
24def _built_length(cfg) -> float:
25 """Tail to nose tip as the sim stacks it, by hand: highest tank top + avionics length."""
26 tops = [
27 cfg.lox_tank.ox_tank_pos + cfg.lox_tank.lox_h / 2,
28 cfg.fuel_tank.fuel_tank_pos + cfg.fuel_tank.rp1_h / 2,
29 cfg.press_tank.pres_tank_pos + cfg.press_tank.press_h / 2,
30 ]
31 return max(tops) + cfg.rocket.avionics_payload_length_m
32
33
34def _smooth_friction_floor(cfg, mach: float) -> float:
35 """0.12 base + smooth turbulent plate friction on the wetted area, Prandtl-Schlichting."""
36 from fluids.atmosphere import ATMOSPHERE_1976
37
38 atm = ATMOSPHERE_1976(cfg.environment.elevation)
39 L, r = _built_length(cfg), cfg.rocket.radius
40 Re = mach * atm.v_sonic * L / (atm.mu / atm.rho)
41 cf = 0.455 / math.log10(Re) ** 2.58
42 fins = cfg.rocket.fins
43 s_wet = 2 * math.pi * r * L + fins.no_fins * 2 * 0.5 * (fins.root_chord + fins.tip_chord) * fins.fin_span
44 return 0.12 + cf * s_wet / (math.pi * r * r)
45
46
47@pytest.fixture(scope="module")
49 pytest.importorskip("fluids")
50 return fly(shipped())
51
52
54 cfg = shipped()
55 floor = _smooth_friction_floor(cfg, 0.3)
56 assert floor > 0.55, "premise: friction alone rules out 0.45 for this vehicle"
57 assert shipped_flight["flight"].rocket.power_off_drag(0.3) >= floor
58
59
61 """Cd_off - Cd_on = (0.12 + 0.13 M^2) A_exit / A_ref, nothing else changes with the motor."""
62 from engine.pipeline.config_schemas import ensure_chamber_geometry
63
64 cfg = shipped()
65 rocket = shipped_flight["flight"].rocket
66 A_e = ensure_chamber_geometry(cfg).A_exit
67 A_ref = math.pi * cfg.rocket.radius**2
68 for M in (0.3, 0.5, 0.8):
69 assert rocket.power_off_drag(M) - rocket.power_on_drag(M) == pytest.approx((0.12 + 0.13 * M * M) * A_e / A_ref, rel=1e-6)
70
71
73 cfg = shipped()
74 cfg.rocket.avionics_payload_length_m = 5.0
75 longer = fly(cfg)["flight"].rocket
76 assert longer.power_off_drag(0.5) > shipped_flight["flight"].rocket.power_off_drag(0.5) + 0.02
77
78
79def test_rougher_skin_more_drag(shipped_flight):
80 cfg = shipped()
81 cfg.rocket.surface_roughness_m = 0.0
82 smooth = fly(cfg)
83 assert smooth["flight"].rocket.power_off_drag(0.5) < shipped_flight["flight"].rocket.power_off_drag(0.5) - 0.1
84 assert smooth["apogee"] > shipped_flight["apogee"] + 100.0
85
86
88 cfg = shipped()
89 assert shipped_flight["flight_report"]["stack_length_m"] == pytest.approx(_built_length(cfg), abs=1e-9)
90 drag = shipped_flight["flight_report"]["drag"]
91 assert drag["model"] == "openrocket_barrowman"
92 assert drag["inputs"]["length_m"] == pytest.approx(_built_length(cfg), abs=1e-9)
93 assert any("rocket_length" in w for w in shipped_flight["flight_report"]["warnings"])
94
95
97 """Most favourable plausible build-up (smooth, the 6.43 m length) gives 3803 m AGL by an
98 independent 1-DOF (audit); Cd 0.45 gave 4033 m."""
99 assert shipped_flight["apogee"] < 3850.0
100
101
103 cfg = shipped()
104 cfg.rocket.drag_curve_power_off = [[0.0, 0.6], [1.0, 0.8], [2.0, 0.7]]
105 cfg.rocket.drag_curve_power_on = [[0.0, 0.5], [1.0, 0.7], [2.0, 0.6]]
106 cfg.rocket.drag_curve_source = "test table"
107 res = fly(cfg)
108 rocket = res["flight"].rocket
109 assert rocket.power_off_drag(0.5) == pytest.approx(0.7)
110 assert rocket.power_on_drag(0.5) == pytest.approx(0.6)
111 assert res["flight_report"]["drag"]["model"] == "table"
112
113
115 from engine.pipeline.config_schemas import RocketConfig
116
117 base = shipped().rocket.model_dump()
118 with pytest.raises(ValueError, match="go together"):
119 RocketConfig(**{**base, "drag_curve_power_off": [[0.0, 0.5], [1.0, 0.6]]})
120 with pytest.raises(ValueError, match="go together"):
121 RocketConfig(**{**base, "drag_curve_power_off": [[0.0, 0.5], [1.0, 0.6]], "drag_curve_power_on": [[0.0, 0.5], [1.0, 0.6]]})
122 with pytest.raises(ValueError, match="increase"):
123 RocketConfig(**{**base, "drag_curve_power_off": [[1.0, 0.5], [0.5, 0.6]], "drag_curve_power_on": [[0.0, 0.5], [1.0, 0.6]], "drag_curve_source": "x"})
124
125
126# ---- the build-up's pieces against their sources
127
128
130 from engine.pipeline.vehicle_drag import skin_friction_cf
131
132 for Re in (1e6, 1e7, 1e8):
133 assert skin_friction_cf(Re, 0.0, 0.0, 7.0) == pytest.approx(0.455 / math.log10(Re) ** 2.58, rel=0.06)
134
135
137 """(3.80): 0.032 (Rs/L)^0.2 above the critical Re, whatever Re."""
138 from engine.pipeline.vehicle_drag import skin_friction_cf
139
140 L, Rs = 7.0, 60e-6
141 rough = 0.032 * (Rs / L) ** 0.2
142 assert skin_friction_cf(5e8, 0.0, Rs, L) == pytest.approx(rough, rel=1e-12)
143 assert skin_friction_cf(5e9, 0.0, Rs, L) == pytest.approx(rough, rel=1e-12)
144
145
147 fluids = pytest.importorskip("fluids.atmosphere")
148 from engine.pipeline.vehicle_drag import isa_troposphere
149
150 for h in (0.0, 626.67, 3000.0, 11000.0, 18000.0, 25000.0):
151 T, p, rho, a, nu = isa_troposphere(h)
152 atm = fluids.ATMOSPHERE_1976(h)
153 assert (T, p, rho, a) == pytest.approx((atm.T, atm.P, atm.rho, atm.v_sonic), rel=1e-5)
154 assert nu == pytest.approx(atm.mu / atm.rho, rel=1e-5)
155
156
158 """(B.9) scales Stoney's fineness-3 curve by log4(fN + 1), which is 1 at fN = 3."""
159 from engine.pipeline.vehicle_drag import nose_pressure_cd
160
161 for M, cd in ((0.95, 0.010), (1.0, 0.027), (1.2, 0.081), (2.0, 0.091)):
162 assert nose_pressure_cd(M, "vonKarman", 3.0) == pytest.approx(cd, rel=1e-12)
163 assert nose_pressure_cd(0.5, "vonKarman", 4.5) == 0.0 # tangent joint, below the data
164
165
167 from engine.pipeline.vehicle_drag import nose_wetted_area
168
169 L, R, n = 0.705, 0.078359, 200001
170 xs = [L * i / (n - 1) for i in range(n)]
171
172 def y(x):
173 th = math.acos(1 - 2 * x / L)
174 return R * math.sqrt(max(th - math.sin(2 * th) / 2, 0.0)) / math.sqrt(math.pi)
175
176 ys = [y(x) for x in xs]
177 area = sum(math.pi * (ys[i] + ys[i + 1]) * math.hypot(xs[i + 1] - xs[i], ys[i + 1] - ys[i]) for i in range(n - 1))
178 assert nose_wetted_area("vonKarman", L, R) == pytest.approx(area, rel=1e-4)
#define max(a, b)
Definition Arduino.h:43
static void fly(world_t *w)
Definition flight_e2e.c:369
test_rougher_skin_more_drag(shipped_flight)
test_drag_grows_with_the_vehicle(shipped_flight)
test_nose_wave_drag_at_fineness_three_is_stoneys_table()
test_full_burn_apogee_is_below_the_constant_cd_answer(shipped_flight)
test_drag_is_above_the_smooth_friction_floor(shipped_flight)
test_thrusting_removes_the_nozzle_exit_from_the_base(shipped_flight)
test_turbulent_cf_against_prandtl_schlichting()
float _smooth_friction_floor(cfg, float mach)
test_flown_length_is_the_hand_stack(shipped_flight)
test_haack_wetted_area_against_a_hand_trapezoid()