codex/glider-sink-polar-optima.py

glider-sink-polar-optima.py
"""Original shallow-glide polar: python glider-sink-polar-optima.py writes cwd PNG.
Tested with Python 3.14, NumPy 2.3.5 and Matplotlib 3.10.7.
"""
import numpy as np
import matplotlib.pyplot as plt
W, S, rho, c0, k = 3000.0, 10.0, 1.2, 0.020, 0.045
A = rho * S * c0 / 2
B = 2 * k * W**2 / (rho * S)
def sink(v):
    return (A * v**3 + B / v) / W
v_bg = (B / A)**0.25
v_ms = (B / (3 * A))**0.25
assert 0 < v_ms < v_bg
assert np.isclose(v_bg / v_ms, 3**0.25)
assert np.isclose(3*A*v_ms**2 - B/v_ms**2, 0, atol=1e-10)
assert np.isclose(A*v_bg**2 - B/v_bg**2, 0, atol=1e-10)
v = np.linspace(17, 41, 700)
fig, ax = plt.subplots(figsize=(7.2, 4.5), dpi=100, facecolor='white')
ax.set_facecolor('white')
ax.plot(v, sink(v), color='#235789', lw=2.4, label='Invented sink polar')
ray = np.linspace(0, 41, 250)
ax.plot(ray, sink(v_bg) / v_bg * ray, '--', color='#b45222', lw=1.6, label='Tangent from origin: best glide')
ax.axhline(sink(v_ms), color='#4c7c36', linestyle=':', lw=1.5)
for vv, color in [(v_ms, '#4c7c36'), (v_bg, '#b45222')]:
    ax.scatter([vv], [sink(vv)], color=color, s=45, zorder=4)
ax.annotate(f'Minimum sink\n{v_ms:.2f} m/s, {sink(v_ms):.3f} m/s',
            (v_ms, sink(v_ms)), (5, 3.2), arrowprops={'arrowstyle':'->', 'color':'#4c7c36'}, fontsize=9)
ax.annotate(f'Best glide\n{v_bg:.2f} m/s, {sink(v_bg):.3f} m/s',
            (v_bg, sink(v_bg)), (28.5, 0.55), arrowprops={'arrowstyle':'->', 'color':'#b45222'}, fontsize=9)
ax.set(xlim=(0, 43), ylim=(0, 4.5), xlabel='True airspeed V (m/s)',
       ylabel='Downward rate w (m/s)', title='Longest time aloft and greatest range use different speeds')
ax.grid(alpha=0.2)
ax.legend(loc='upper right', fontsize=8, frameon=False)
fig.text(0.5, 0.025, 'Original shallow-glide model; fixed loading and density; no wind or stall model.', ha='center', fontsize=8)
fig.tight_layout(rect=(0, .05, 1, 1))
fig.savefig('glider-sink-polar-optima.png', facecolor='white')
print({'V_BG':v_bg, 'V_min_sink':v_ms, 'w_BG':sink(v_bg), 'w_min':sink(v_ms)})