codexfresnel-biprism-virtual-sources.py
fresnel-biprism-virtual-sources.py
"""Paraxial biprism rays and ideal equal-amplitude interference (Python 3.14).
Run in the mirrored media directory; writes fresnel-biprism-virtual-sources.png.
"""
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
A = np.deg2rad(1.0)
n, a, b, wavelength = 1.5, 0.10, 0.90, 600e-9
bend = (n - 1) * A
separation = 2 * a * bend
spacing = wavelength * (a + b) / separation
fig, (rays, fringes) = plt.subplots(1, 2, figsize=(10.4, 4.2), dpi=120,
gridspec_kw={'width_ratios': [2.15, 1]}, layout='constrained')
fig.patch.set_facecolor('white')
for ax in (rays, fringes):
ax.set_facecolor('white')
ax.spines[['top', 'right']].set_visible(False)
rays.axhline(0, color='0.8', lw=0.8)
for sign, color in [(1, '#2457a6'), (-1, '#ba4d22')]:
virtual_height = sign * a * bend
for height in sign * np.array([0.0004, 0.0008, 0.0012]):
screen_height = height + b * (height / a - sign * bend)
rays.plot([-100*a, 0], [0, 1000*height], color=color, lw=1.2)
rays.plot([0, 100*b], [1000*height, 1000*screen_height], color=color, lw=1.2)
rays.plot([-100*a, 0], [1000*virtual_height, 1000*height], '--',
color=color, lw=0.8, alpha=0.65)
rays.scatter([-100*a], [1000*virtual_height], facecolors='white',
edgecolors=color, zorder=6, s=35)
rays.annotate(f'Virtual source S{sign:+d}', (-100*a, 1000*virtual_height),
xytext=(-9, sign*5.6), color=color, fontsize=9,
arrowprops={'arrowstyle': '-', 'color': color})
rays.scatter([-100*a], [0], color='black', s=22, zorder=7)
rays.annotate('Slit S', (-100*a, 0), xytext=(-15, 2.7), fontsize=9,
arrowprops={'arrowstyle': '-', 'color': 'black'})
rays.add_patch(Rectangle((-0.45, -3), 0.9, 6, color='#5bbac4', alpha=0.5, zorder=5))
rays.text(2, 6.8, 'Thin biprism\n(schematic)', fontsize=9)
rays.plot([100*b]*2, [-7.5, 7.5], color='black', lw=2)
rays.text(100*b-3, 7.9, 'Screen', ha='center', fontsize=10)
rays.set(xlim=(-17, 97), ylim=(-8.5, 8.7), xlabel='Axial position (cm)',
ylabel='Transverse position (mm)', title='Two virtual sources from one slit')
rays.text(30, -7.8, 'Dashed lines: backward extensions', fontsize=9, color='0.35')
screen_position = np.linspace(-1e-3, 1e-3, 1800)
intensity = 0.5 * (1 + np.cos(2*np.pi * screen_position / spacing))
fringes.plot(intensity, screen_position*1000, color='#57498b', lw=1.7)
fringes.set(xlim=(0, 1.1), ylim=(-1, 1), xlabel='Normalized intensity',
ylabel='Screen position (mm)', title='Central interference fringes')
fringes.grid(alpha=0.18)
fringes.text(0.03, 0.02, f'Fringe spacing = {spacing*1e3:.3f} mm',
transform=fringes.transAxes, fontsize=9,
bbox={'facecolor': 'white', 'edgecolor': 'none', 'alpha': 0.9})
fig.suptitle('Chosen thin-prism model: n = 1.50, A = 1°, a = 0.10 m, b = 0.90 m, λ = 600 nm',
fontsize=11)
fig.savefig(Path('fresnel-biprism-virtual-sources.png'), facecolor='white', transparent=False)
plt.close(fig)