codexradial-free-fall-gravity-variation.py
radial-free-fall-gravity-variation.py
"""Compare exact exterior radial fall with gravity frozen at the release point."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def trajectory(height_ratio, samples=801):
"""Return time in sqrt(h/g_release) and remaining height in units of h."""
end_angle = np.arccos(np.sqrt(1 / (1 + height_ratio)))
angle = np.linspace(0, end_angle, samples)
time = np.sqrt((1 + height_ratio) / (2 * height_ratio)) * (
angle + np.sin(angle) * np.cos(angle)
)
height = ((1 + height_ratio) * np.cos(angle) ** 2 - 1) / height_ratio
assert np.isclose(height[0], 1) and np.isclose(height[-1], 0)
assert np.all(np.diff(time) > 0) and np.all(np.diff(height) < 0)
assert time[-1] < np.sqrt(2)
return time, height
def main():
plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"})
fig, axes = plt.subplots(1, 2, figsize=(11.4, 5.8), layout="constrained")
for ax, ratio in zip(axes, (0.02, 1.0)):
time, height = trajectory(ratio)
constant_time = np.linspace(0, np.sqrt(2), 801)
ax.plot(constant_time, 1 - constant_time ** 2 / 2, "--",
color="#c26030", lw=2.3, label="Constant gravity at release")
ax.plot(time, height, color="#167e8d", lw=2.5,
label="Exact inverse-square gravity")
ax.scatter([time[-1]], [0], color="#167e8d", s=42, zorder=4)
ax.scatter([np.sqrt(2)], [0], color="#c26030", s=42, zorder=4)
overestimate = (np.sqrt(2) / time[-1] - 1) * 100
ax.text(0.04, 0.10,
f"Constant-gravity arrival time\nis {overestimate:.2f}% longer",
transform=ax.transAxes, color="#7f492e", fontsize=11,
bbox={"facecolor": "white", "edgecolor": "#ddd8d1", "pad": 6})
ax.set_title(f"Release height / source radius: h/R = {ratio:g}",
fontsize=13)
ax.set(xlabel=r"Time $t/\sqrt{h/g_{\rm release}}$",
ylabel="Remaining height / release height", xlim=(0, 1.48),
ylim=(-0.035, 1.04))
ax.axhline(0, color="#596773", lw=0.8)
ax.grid(alpha=0.17)
ax.spines[["top", "right"]].set_visible(False)
ax.legend(loc="upper right", fontsize=10)
# At h/R=1, the dimensional arrival time is (1 + pi/2)*sqrt(R^3/mu).
time, _ = trajectory(1.0)
assert np.isclose(time[-1] * 2, 1 + np.pi / 2)
fig.suptitle("Gravity strengthens as a radial fall approaches its source",
fontsize=17)
fig.get_layout_engine().set(rect=(0, 0.065, 1, 0.94))
fig.text(0.5, 0.02,
"Fixed spherical source; release from rest; no drag; both trajectories stop at the surface.",
ha="center", fontsize=10)
fig.savefig(Path(__file__).with_suffix(".png").name, dpi=100,
facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()