codexrefraction-interface-matched-wavefronts.py
refraction-interface-matched-wavefronts.py
"""Matched monochromatic phase at a planar air/water boundary.
Tested with Python 3.14, matplotlib 3.10.7 and numpy 2.3.5.
Run from _media; coordinates use lambda_0 as their length unit.
This stipulated n_air=1, n_water=4/3 model is not measured data.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Arc
def main():
plt.rcParams.update({"font.size": 12, "axes.titlesize": 14})
fig, axes = plt.subplots(1, 2, figsize=(10.8, 6.2), layout="constrained")
colors = ["#176b92", "#bd5125", "#65791b", "#843b91"]
for ax, qx, title in zip(
axes, [0.5, 0.0], ["Oblique incidence", "Normal incidence: no bending"]
):
ax.axhspan(0, 2.9, color="#edf6fb")
ax.axhline(0, color="#333333", lw=1.8)
ax.axvline(0, color="#666666", ls=":", lw=1.4)
x = np.linspace(-2.65, 2.65, 1001)
qs = []
for n, lo, hi in [(1, -2.9, 0), (4 / 3, 0, 2.9)]:
qz = np.sqrt(n * n - qx * qx)
qs.append(qz)
for m in range(-6, 7):
z = (m - qx * x) / qz
mask = (z >= lo) & (z <= hi)
ax.plot(x[mask], z[mask], color=colors[m % 4], lw=1.5)
start, end = ((-2.3, -0.6) if lo < 0 else (0.5, 2.5))
ray = lambda z: (qx * z / qz, z)
ax.annotate("", xy=ray(end), xytext=ray(start),
arrowprops={"arrowstyle": "-|>", "lw": 3, "color": "#111111"})
if qx:
crossings = np.arange(-1, 2) / qx
ax.scatter(crossings, np.zeros_like(crossings), color="#111111", s=25, zorder=5)
for qz, side in zip(qs, [-1, 1]):
theta = np.degrees(np.arctan2(qx, qz))
angles = (270 - theta, 270) if side < 0 else (90 - theta, 90)
ax.add_patch(Arc((0, 0), 1.2, 1.2, theta1=angles[0], theta2=angles[1],
color="#111111", lw=1.5))
ax.text(-0.73, -0.58, r"$30^\circ$", ha="right", bbox={"facecolor": "white", "edgecolor": "none", "pad": 1})
ax.text(0.75, 0.64, r"$22.0243^\circ$", bbox={"facecolor": "white", "edgecolor": "none", "pad": 1})
ax.text(0, 3.25, r"Same interface crossings: $\Delta x=2\lambda_0$", ha="center", fontsize=11)
else:
ax.text(0, 3.25, "No phase variation along the interface", ha="center", fontsize=11)
for y, label in [(-2.68, r"Air: $n=1$, $\lambda_{\rm a}=\lambda_0$"),
(2.70, r"Water: $n=4/3$, $\lambda_{\rm w}=3\lambda_0/4$")]:
ax.text(-2.48, y, label, va="center", fontsize=11,
bbox={"facecolor": "white", "edgecolor": "none", "pad": 3})
ax.set(xlim=(-2.65, 2.65), ylim=(3.55, -3.12), title=title,
xlabel=r"Along interface $x/\lambda_0$", ylabel=r"Into water $z/\lambda_0$")
ax.set_aspect("equal")
ax.set_yticks([-2, -1, 0, 1, 2])
fig.suptitle("Refraction: matched phase, changed wavefront spacing", fontsize=17)
fig.savefig(Path(__file__).stem + ".png", dpi=120, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()