codexpolarization-through-three-filters.py
polarization-through-three-filters.py
"""Classical projections through crossed and intermediate ideal polarizers.
Tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5.
Write the basename PNG into cwd; run from _media for the wiki.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import numpy as np
def project(input_field, angle_degrees):
angle = np.deg2rad(angle_degrees)
axis = np.array([np.cos(angle), np.sin(angle)])
return np.dot(input_field, axis) * axis
def stages(angles):
fields = [np.array([1.0, 0.0])]
for angle in angles:
fields.append(project(fields[-1], angle))
return np.array(fields)
def draw_pass_axis(ax, x, angle):
center_y, radius = 0.98, 0.27
ax.add_patch(Circle((x, center_y), radius, facecolor="#f3f6f8",
edgecolor="#8797a2", lw=1.2))
direction = radius * 0.82 * np.array([np.cos(np.deg2rad(angle)),
np.sin(np.deg2rad(angle))])
ax.plot([x - direction[0], x + direction[0]],
[center_y - direction[1], center_y + direction[1]],
color="#bf5b21", lw=3, solid_capstyle="round")
ax.plot([x, x], [0.15, 0.68], color="#8797a2", ls=":", lw=1.2)
ax.plot([x, x], [-0.14, 0.14], color="#8797a2", lw=3)
ax.text(x, 1.37, f"{angle}° pass axis", ha="center", fontsize=11)
def main():
two = stages([0, 90])
three = stages([0, 45, 90])
intensities_two = np.sum(two**2, axis=1)
intensities_three = np.sum(three**2, axis=1)
np.testing.assert_allclose(intensities_two, [1, 1, 0], atol=1e-14)
np.testing.assert_allclose(intensities_three, [1, 1, 0.5, 0.25], atol=1e-14)
np.testing.assert_allclose(three[2], [0.5, 0.5], atol=1e-14)
np.testing.assert_allclose(three[3], [0, 0.5], atol=1e-14)
losses = intensities_three[:-1] - intensities_three[1:]
np.testing.assert_allclose(losses, [0, 0.5, 0.25], atol=1e-14)
np.testing.assert_allclose(losses.sum() + intensities_three[-1], 1)
np.testing.assert_allclose(0.5 * intensities_three[-1], 0.125)
np.testing.assert_allclose(np.cos(np.deg2rad(60))**2, 0.25)
plt.rcParams.update({"font.size": 12})
fig, axes = plt.subplots(2, 1, figsize=(10, 6), dpi=120,
facecolor="white")
for ax, positions, angles, intensities, labels, title in [
(axes[0], [1.6, 6.4], [0, 90], intensities_two,
[r"$I_0$", r"$I_0$", "0"], "Two crossed filters"),
(axes[1], [1.6, 4.0, 6.4], [0, 45, 90], intensities_three,
[r"$I_0$", r"$I_0$", r"$I_0/2$", r"$I_0/4$"],
"Insert a 45° filter between the crossed pair")]:
boundaries = [0.0, *positions, 8.0]
for left, right, intensity, label in zip(
boundaries[:-1], boundaries[1:], intensities, labels):
if intensity > 1e-12:
ax.annotate("", (right - 0.13, 0), (left + 0.13, 0),
arrowprops={"arrowstyle": "->", "color": "#176799",
"lw": 6 * intensity, "mutation_scale": 13})
else:
ax.plot([left + 0.13, right - 0.13], [0, 0],
color="#adb8c0", ls="--", lw=1.2)
ax.text((left + right) / 2, -0.43, label, ha="center",
va="center", fontsize=14, color="#174d85")
for position, angle in zip(positions, angles):
draw_pass_axis(ax, position, angle)
if len(angles) == 3:
ax.text(4, -0.93, r"Absorbed: $I_0/2$", ha="center",
fontsize=11, color="#935023")
ax.text(6.4, -0.93, r"Absorbed: $I_0/4$", ha="center",
fontsize=11, color="#935023")
ax.set(xlim=(-0.3, 8.3), ylim=(-1.15, 1.75), aspect="equal")
ax.set_title(title, loc="left", fontsize=13, pad=10)
ax.axis("off")
fig.suptitle("A middle polarizer changes the final transmitted component",
fontsize=15, y=0.97)
fig.text(0.5, 0.89,
"Input: already horizontally polarized, intensity I₀. Ideal absorbing linear filters.",
ha="center", fontsize=11)
fig.text(0.5, 0.835, r"$I_{\rm out}=I_{\rm in}\cos^2\theta$",
ha="center", fontsize=12, color="#174d85")
fig.text(0.5, 0.055,
"Beam travels left → right. Circles show pass axes in the transverse plane.\n"
"θ is measured from the preceding output: 0°, then 45°, then 45°.",
ha="center", fontsize=11, linespacing=1.6)
fig.subplots_adjust(left=0.065, right=0.97, bottom=0.15, top=0.78,
hspace=0.38)
output = Path.cwd() / Path(__file__).with_suffix(".png").name
fig.savefig(output, facecolor="white", transparent=False)
plt.close(fig)
print("Checked: crossed transmission 0; three-filter transmission 1/4; "
"unpolarized alternative 1/8; absorption 1/2 + 1/4. " + output.name)
if __name__ == "__main__":
main()