codexminimum-drag-and-power-speeds.py
minimum-drag-and-power-speeds.py
"""Compare exact optima of an original dimensionless parabolic-polar model.
Run from _media using the repository Python 3.14 / NumPy / Matplotlib setup.
The curves are constructed calculations, not measured aircraft performance.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def drag_ratio(x):
x = np.asarray(x, dtype=float)
return (x**2 + x**-2) / 2
def power_ratio(x):
x = np.asarray(x, dtype=float)
return (x**3 + x**-1) / 2
def main():
x_mp = 3**(-0.25)
assert np.isclose(drag_ratio(1), 1)
assert np.isclose(power_ratio(x_mp), 2 * 3**(-0.75))
assert np.isclose(3 * x_mp**2 - x_mp**-2, 0)
x = np.linspace(0.48, 1.75, 601)
fig, axes = plt.subplots(1, 2, figsize=(8.4, 4.6), dpi=100, facecolor="white")
specs = [(axes[0], drag_ratio, "#245f8e", "Drag required", r"$D/D_{\min}$", 1.0),
(axes[1], power_ratio, "#ba5c24", "Propulsive power required",
r"$P/(D_{\min}V_{\rm MD})$", x_mp)]
for ax, curve, color, title, ylabel, optimum in specs:
ax.set_facecolor("white")
ax.plot(x, curve(x), color=color, lw=2.5)
ax.axvline(x_mp, color="#ba5c24", ls=":", lw=1.2)
ax.axvline(1, color="#245f8e", ls="--", lw=1.2)
y_min = float(curve(optimum))
ax.scatter([optimum], [y_min], color=color, s=35, zorder=5)
ax.annotate("Minimum", (optimum, y_min), xytext=(12, 30),
textcoords="offset points", fontsize=10,
arrowprops={"arrowstyle": "->", "color": color})
ax.set(xlim=(0.48, 1.75), ylim=(0.65, 3.05), title=title,
xlabel=r"Speed ratio $V/V_{\rm MD}$", ylabel=ylabel)
ax.grid(alpha=0.17)
axes[0].text(0.50, 2.65, r"$D=aV^2+b/V^2$", fontsize=11)
axes[1].text(0.50, 2.65, r"$P=DV=aV^3+b/V$", fontsize=11)
fig.suptitle("Different speeds minimize drag and required power", fontsize=14, y=0.98)
fig.text(0.5, 0.083, r"Dotted: $V_{\rm MP}/V_{\rm MD}=3^{-1/4}\simeq0.760$"
" Dashed: drag minimum at 1", ha="center", fontsize=10)
fig.text(0.5, 0.027, "Constructed model: steady level flight, fixed weight and density, constant polar coefficients.",
ha="center", fontsize=9, color="#444444")
fig.tight_layout(rect=(0, 0.12, 1, 0.94))
fig.savefig(Path("minimum-drag-and-power-speeds.png"), facecolor="white", dpi=100)
plt.close(fig)
if __name__ == "__main__":
main()