codexrayleigh-wavelength-scaling.py
rayleigh-wavelength-scaling.py
"""Fixed-sphere Rayleigh response; write an opaque PNG to the current directory.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Assume a fixed, real, nondispersive refractive index and ka << 1 throughout.
The normalized response is independent of the chosen sphere radius and index.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
wavelength_nm = np.linspace(350.0, 750.0, 801)
response = (650.0 / wavelength_nm) ** 4
blue_response = (650.0 / 450.0) ** 4
plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"})
fig, ax = plt.subplots(figsize=(9.0, 5.4), dpi=100)
ax.plot(wavelength_nm, response, color="#34495e", linewidth=2.5)
for wavelength, value, color in [
(450.0, blue_response, "#2166ac"), (650.0, 1.0, "#b2182b")
]:
ax.vlines(wavelength, 0, value, color=color, linestyles="--", linewidth=1.4)
ax.scatter([wavelength], [value], color=color, s=70, zorder=3)
ax.annotate("Blue: 450 nm\nRelative response = 4.353", (450, blue_response),
xytext=(478, 7.0), color="#2166ac", fontsize=12,
arrowprops={"arrowstyle": "->", "color": "#2166ac"})
ax.annotate("Red: 650 nm\nRelative response = 1", (650, 1),
xytext=(566, 3.2), color="#b2182b", fontsize=12,
arrowprops={"arrowstyle": "->", "color": "#b2182b"})
ax.set(xlim=(350, 750), ylim=(0, 13),
xlabel=r"Wavelength $\lambda$ (nm)",
ylabel=r"Relative cross-section $\sigma(\lambda)/\sigma(650\,\mathrm{nm})$")
ax.set_xticks(np.arange(350, 751, 50))
ax.grid(alpha=0.23)
ax.set_title(r"Rayleigh scattering: $(650\,\mathrm{nm}/\lambda)^4$",
fontsize=16, pad=13)
fig.text(0.5, 0.035,
"Fixed small sphere; fixed real refractive index; ka ≪ 1 at every wavelength",
ha="center", fontsize=11)
fig.subplots_adjust(left=0.105, right=0.975, top=0.87, bottom=0.18)
fig.savefig(Path.cwd() / "rayleigh-wavelength-scaling.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()