codexrefractive-index-phase-spacing.py
refractive-index-phase-spacing.py
"""Original single-frequency model; emit matching PNG to the current directory.
Tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5.
The incident and transmitted amplitudes are normalized separately. This is not
an interface amplitude solution, a pulse calculation, or measured material data.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
n = 1.5
colors = ['#176ba0', '#b44f24']
fig, axes = plt.subplots(2, 1, figsize=(10, 6.9), dpi=120,
gridspec_kw={'height_ratios': [1, 1.25]}, facecolor='white')
fig.subplots_adjust(left=.095, right=.97, bottom=.16, top=.83, hspace=.42)
for ax in axes:
ax.axvspan(-2.15, 0, color='#eef6fc')
ax.axvspan(0, 2.15, color='#fff4e7')
ax.axvline(0, color='#555555', lw=1.3)
ax.set_xlim(-2.1, 2.1)
ax.spines[['top', 'right']].set_visible(False)
ax.set_xticks(np.arange(-2, 2.1, .5))
top, bottom = axes
for lo, hi, index, color in [(-2.1, 0, 1, colors[0]), (0, 2.1, n, colors[1])]:
x = np.linspace(lo, hi, 800)
top.plot(x, np.cos(2 * np.pi * index * x), color=color, lw=2.3)
for m in range(-3, 4):
crest = m / index
if lo < crest < hi:
top.plot([crest, crest], [-1, 1], color=color, alpha=.27, ls=':')
for start, length, label, color in [(-2, 1, r'$\lambda_0$', colors[0]),
(2/3, 2/3, r'$\lambda=2\lambda_0/3$', colors[1])]:
top.annotate('', xy=(start+length, 1.35), xytext=(start, 1.35),
arrowprops={'arrowstyle': '<->', 'color': color, 'lw': 1.5})
top.text(start+length/2, 1.48, label, ha='center', color=color, fontsize=12)
top.set_ylim(-1.2, 1.85)
top.set_yticks([-1, 0, 1])
top.set_ylabel('Normalized field')
top.set_title('One instant: phase repeats over a shorter distance', fontsize=13, pad=12)
t = np.linspace(0, 2, 1001)
for m in range(-5, 3):
for index, color, left in [(1, colors[0], True), (n, colors[1], False)]:
x = (m + t) / index
mask = x <= 0 if left else x >= 0
bottom.plot(x[mask], t[mask], color=color, lw=2)
# A chosen crest crosses the interface at t/T=1.
for p, q in [((-.8, .2), (-.2, .8)), ((.1, 1.15), (.5, 1.75))]:
bottom.annotate('', xy=q, xytext=p,
arrowprops={'arrowstyle': '-|>', 'lw': 3.2, 'color': '#222222'})
bottom.plot([0, 0], [0, 2], 'o', color='#222222', ms=5)
bottom.text(-1.97, 1.62, r'$v_{\rm ph}=c$', fontsize=13,
bbox={'facecolor': 'white', 'edgecolor': 'none', 'alpha': .95})
bottom.text(.8, .19, r'$v_{\rm ph}=2c/3$', fontsize=13,
bbox={'facecolor': 'white', 'edgecolor': 'none', 'alpha': .95})
bottom.annotate('Successive crests at interface:\nsame period $T=1/f$', xy=(0, 1),
xytext=(.85, 1.05), fontsize=10,
arrowprops={'arrowstyle': '->', 'color': '#333333'},
bbox={'facecolor': 'white', 'edgecolor': '#bbbbbb'})
bottom.set_ylim(0, 2)
bottom.set_yticks([0, 1, 2])
bottom.set_ylabel(r'Time $t/T$')
bottom.set_xlabel(r'Position $z/\lambda_0$ (propagation toward $+z$)')
bottom.set_title('Crest trajectories: less distance in the same time', fontsize=13, pad=10)
fig.suptitle('Refractive index changes phase spacing and phase speed', fontsize=17, y=.975)
fig.text(.30, .905, r'Vacuum: $n=1$', ha='center', fontsize=13, color=colors[0])
fig.text(.73, .905, r'Chosen medium: $n=3/2$', ha='center', fontsize=13, color=colors[1])
fig.text(.5, .045, 'Stationary interface at normal incidence; single frequency.\n'
'Equal normalized components; reflection omitted. Original model, not measured data.',
ha='center', fontsize=10)
fig.savefig(Path(__file__).with_suffix('.png').name, facecolor='white', transparent=False)
plt.close(fig)
if __name__ == '__main__':
main()