codexx-ray-narrow-beam-attenuation.py
x-ray-narrow-beam-attenuation.py
"""Plot original stipulated attenuation models, not measured material data.
Tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5; run from _media.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 11})
fig, axes = plt.subplots(1, 2, figsize=(10.6, 4.9), dpi=120, facecolor="white")
blue, orange = "#1768a2", "#b64a18"
x = np.linspace(0, 12, 401)
half = np.log(2) / 0.4
ax = axes[0]
ax.plot(x, np.exp(-0.4 * x), color=blue, lw=2.5,
label=r"$T=e^{-0.40x}$")
ax.plot([0, half, half], [0.5, 0.5, 0], ls="--", color="#777777", lw=1)
ax.scatter([half, 2.5], [0.5, np.exp(-1)], color=[blue, orange], zorder=4)
ax.annotate("Half-value layer\n1.73 mm → 0.500", (half, 0.5), (4.1, 0.68),
arrowprops={"arrowstyle": "->", "color": "#777777"}, fontsize=10)
ax.annotate("2.50 mm → 0.368", (2.5, np.exp(-1)), (4.1, 0.41),
arrowprops={"arrowstyle": "->", "color": orange}, fontsize=10, color=orange)
ax.set(title="One photon energy", xlabel="Thickness x (mm)",
ylabel="Transmission T", xlim=(0, 12), ylim=(0, 1.03))
ax.legend(loc="upper right", frameon=False)
ax = axes[1]
mixture = 0.5 * np.exp(-0.4 * x) + 0.5 * np.exp(-0.1 * x)
ax.semilogy(x, mixture, color=blue, lw=2.5,
label=r"$\frac{1}{2}e^{-0.40x}+\frac{1}{2}e^{-0.10x}$")
ax.semilogy(x, np.exp(-0.25 * x), color=orange, lw=2, ls="--",
label=r"$e^{-0.25x}$ (same initial slope)")
t5 = 0.5 * np.exp(-2) + 0.5 * np.exp(-0.5)
ax.scatter([5, 5], [t5, np.exp(-1.25)], color=[blue, orange], zorder=4)
ax.text(5.5, t5 * 1.08, "0.371", color=blue, fontsize=10)
ax.text(5.5, np.exp(-1.25) * 0.56, "0.287", color=orange, fontsize=10)
ax.set(title="Equal-weight two-energy mixture", xlabel="Thickness x (mm)",
ylabel="Transmission T (log scale)", xlim=(0, 12), ylim=(0.04, 1.1))
ax.legend(loc="upper right", frameon=False, fontsize=10)
for ax in axes:
ax.grid(alpha=0.2)
ax.spines[["top", "right"]].set_visible(False)
fig.suptitle("Narrow-beam attenuation: a model with stated limits", fontsize=16)
fig.text(0.5, 0.025, "Stipulated coefficients in mm⁻¹ • fixed geometry and linear detector • background subtracted • no accepted scatter",
ha="center", fontsize=9, color="#444444")
fig.tight_layout(rect=(0, 0.06, 1, 0.94), w_pad=2)
fig.savefig(Path(__file__).stem + ".png", facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()