codexstellar-distance-and-flux.py
stellar-distance-and-flux.py
"""Generate stellar-distance-and-flux.png in the current working directory.
Run from _media: MPLBACKEND=Agg MPLCONFIGDIR=/tmp python3 ../stellar-distance-and-flux.py
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
All sources are isotropic, unattenuated, and steady in Euclidean space.
The right panel is schematic, not to scale; star sizes do not encode luminosity.
"""
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
def main():
plt.rcParams.update({"font.size": 13, "savefig.facecolor": "white"})
fig, (curve, cases) = plt.subplots(1, 2, figsize=(11, 5.7), facecolor="white")
fig.subplots_adjust(left=0.09, right=0.98, bottom=0.18, top=0.78, wspace=0.30)
fig.suptitle("Distance dims light; luminosity can compensate", fontsize=21, y=0.97)
fig.text(0.5, 0.89, r"Isotropic, steady source; transparent space: $F=L/(4\pi d^2)$",
ha="center", fontsize=14)
distance = np.linspace(1, 3.4, 250)
curve.plot(distance, distance ** -2, color="#2368ac", lw=3)
curve.scatter([1, 2, 3], [1, 1/4, 1/9], color="#2368ac", s=65, zorder=4)
for x, y, label, offset in ((1, 1, r"$F_0$", (12, -7)),
(2, 1/4, r"$F_0/4$", (8, 12)),
(3, 1/9, r"$F_0/9$", (0, 14))):
curve.annotate(label, (x, y), xytext=offset, textcoords="offset points", fontsize=15)
curve.set(xlim=(0.8, 3.5), ylim=(0, 1.17), xticks=[1, 2, 3],
yticks=[0, 0.25, 0.5, 0.75, 1], xlabel=r"Distance $d/d_0$",
ylabel=r"Received flux $F/F_0$")
curve.set_title(r"Fixed luminosity $L_0$", fontsize=16, pad=15)
curve.spines[["top", "right"]].set_visible(False)
curve.grid(alpha=0.20)
for y, source_x, lum, separation, flux, color in (
(0.84, 0.46, r"$L_0$", r"$d_0$", r"$F_0$", "#2368ac"),
(0.50, 0.04, r"$L_0$", r"$2d_0$", r"$F_0/4$", "#cf6519"),
(0.16, 0.04, r"$4L_0$", r"$2d_0$", r"$F_0$", "#453778"),
):
cases.scatter([source_x], [y], marker="*", s=190, color=color, zorder=5)
cases.text(source_x, y + 0.10, lum, ha="center", color=color, fontsize=16)
cases.add_patch(Rectangle((0.88, y - 0.045), 0.035, 0.09,
facecolor="white", edgecolor=color, lw=2))
cases.annotate("", xy=(0.865, y), xytext=(source_x + 0.04, y),
arrowprops={"arrowstyle": "->", "color": color, "lw": 2})
cases.text((source_x + 0.88) / 2, y - 0.085, separation, ha="center", fontsize=15)
cases.text(0.965, y, flux, va="center", fontsize=16, color=color)
cases.text(0.90, 1.04, "Detector", ha="center", fontsize=12)
cases.set(xlim=(-0.07, 1.27), ylim=(-0.10, 1.13))
cases.axis("off")
cases.set_title("Three configurations", fontsize=16, pad=15)
fig.text(0.30, 0.045, "Twice the distance gives one quarter of the flux", ha="center", fontsize=11)
fig.text(0.75, 0.045, "Top and bottom: same flux\nSchematic, not to scale", ha="center", fontsize=11)
fig.savefig("stellar-distance-and-flux.png", dpi=100, facecolor="white", transparent=False)
plt.close(fig)
print("Wrote stellar-distance-and-flux.png (1100 × 570); ratios 1, 1/4, 1/9.")
if __name__ == "__main__":
main()