codex/rocket-mass-ratio-delta-v.py

rocket-mass-ratio-delta-v.py
"""Plot ideal rocket velocity gains; write an opaque white PNG to the CWD."""

from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    u = 2.0  # km/s
    initial_mass = 1000.0  # kg
    masses = np.linspace(initial_mass, 250.0, 501)
    gains = u * np.log(initial_mass / masses)
    half_gain = u * np.log(2.0)
    assert np.isclose(gains[-1], 2.772588722239781)
    assert np.isclose(u * np.log(1000 / 500), u * np.log(500 / 250))
    assert np.isclose(2000 * 5, 10000)

    fig, axes = plt.subplots(1, 2, figsize=(10.0, 4.5), dpi=110)
    fig.patch.set_facecolor("white")
    blue, orange = "#245f9b", "#c56521"
    ax = axes[0]
    ratio = np.linspace(1, 4, 401)
    ax.plot(ratio, u * np.log(ratio), color=blue, lw=2.5)
    for value, color in [(2, blue), (4, orange)]:
        gain = u * np.log(value)
        ax.scatter(value, gain, color=color, zorder=5)
        ax.plot([1, value, value], [gain, gain, 0], ls="--", color=color, lw=1)
    ax.text(2.08, half_gain - .25, "R = 2: 1.3863 km/s", fontsize=10)
    ax.text(2.05, 2.85, "R = 4: 2.7726 km/s", fontsize=10, color=orange)
    ax.set(xlim=(.95, 4.15), ylim=(0, 3.2), xticks=[1, 2, 3, 4],
           xlabel=r"Mass ratio $R=M_{\rm initial}/M_{\rm final}$",
           ylabel=r"Ideal velocity gain $\Delta v$ (km/s)",
           title="Logarithmic gain")

    ax = axes[1]
    ax.plot(masses, gains, color="#404040", lw=2.5)
    ax.fill_between(masses, gains, where=masses >= 500, color=blue, alpha=.12)
    ax.fill_between(masses, gains, half_gain, where=masses <= 500,
                    color=orange, alpha=.14)
    ax.scatter([1000, 500, 250], [0, half_gain, 2 * half_gain],
               color=[blue, blue, orange], zorder=5)
    ax.axhline(half_gain, color="#999999", ls="--", lw=1)
    ax.text(950, 1.80, "1000 → 500 kg\n500 kg expelled\n+1.3863 km/s",
            color=blue, fontsize=10, va="bottom")
    ax.text(480, .28, "500 → 250 kg\n250 kg expelled\n+1.3863 km/s",
            color=orange, fontsize=10, va="bottom")
    ax.set(xlim=(1050, 200), ylim=(0, 3.2), xticks=[1000, 750, 500, 250],
           xlabel="Remaining total rocket mass (kg) — burn proceeds →",
           ylabel=r"Cumulative $\Delta v$ (km/s)",
           title="Same gain from each mass halving")
    for ax in axes:
        ax.set_facecolor("white")
        ax.spines[["top", "right"]].set_visible(False)
        ax.grid(alpha=.15)
    fig.suptitle("Ideal rocket: constant rearward relative exhaust speed u = 2 km/s",
                 fontsize=13)
    fig.tight_layout(rect=(0, 0, 1, .93))
    fig.savefig(Path.cwd() / "rocket-mass-ratio-delta-v.png",
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()