codexwave-polarization-trajectories.py
wave-polarization-trajectories.py
"""Real electric-field trajectories at one fixed position.
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
import numpy as np
def field(phase, amplitude_x, amplitude_y, delta):
return np.array([amplitude_x * np.cos(phase),
amplitude_y * np.cos(phase + delta)])
def main():
phase = np.linspace(0, 2 * np.pi, 721)
examples = [(2.0, 1.0, 0.0), (1.0, 1.0, np.pi / 2),
(2.0, 1.0, np.pi / 2)]
linear, circular, elliptical = [field(phase, *p) for p in examples]
np.testing.assert_allclose(linear[1], linear[0] / 2, atol=1e-14)
np.testing.assert_allclose(np.sum(circular**2, axis=0), 1, atol=1e-14)
np.testing.assert_allclose(elliptical[0]**2 / 4 + elliptical[1]**2,
1, atol=1e-14)
np.testing.assert_allclose(np.linalg.norm(elliptical, axis=0).min(), 1)
np.testing.assert_allclose(np.linalg.norm(elliptical, axis=0).max(), 2)
np.testing.assert_allclose(field(np.pi / 4, *examples[2]),
[np.sqrt(2), -1 / np.sqrt(2)])
np.testing.assert_allclose(np.linalg.norm(field(np.pi / 4, *examples[2])),
np.sqrt(2.5))
for params, expected in zip(examples, [[0, 0], [0, -1], [0, -1]]):
np.testing.assert_allclose(field(np.pi / 2, *params), expected,
atol=1e-14)
for delta in [np.pi / 3, -np.pi / 3, np.pi / 2]:
x, y = field(phase, 2, 1, delta)
residual = x**2 / 4 + y**2 - x * y * np.cos(delta)
np.testing.assert_allclose(residual, np.sin(delta)**2, atol=1e-14)
cycle = np.linspace(0, 2 * np.pi, 720, endpoint=False)
for delta in [np.pi / 2, -np.pi / 2]:
fields = field(cycle, 2, 1, delta)
np.testing.assert_allclose(np.mean(np.sum(fields**2, axis=0)), 2.5)
for angle, expected in [(0, 0.8), (np.pi / 4, 0.5), (np.pi / 2, 0.2)]:
axis = np.array([np.cos(angle), np.sin(angle)])
np.testing.assert_allclose(np.mean((axis @ fields)**2) / 2.5,
expected, atol=1e-14)
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False})
fig, axes = plt.subplots(1, 3, figsize=(11, 5), dpi=120,
facecolor="white")
titles = ["Linear", "Circular", "Elliptical"]
parameters = [r"$A_x=2,\ A_y=1,\ \delta=0$",
r"$A_x=1,\ A_y=1,\ \delta=\pi/2$",
r"$A_x=2,\ A_y=1,\ \delta=\pi/2$"]
equations = [r"$E_y=E_x/2$", r"$E_x^2+E_y^2=1$",
r"$E_x^2/4+E_y^2=1$"]
for ax, params, title, param, equation in zip(
axes, examples, titles, parameters, equations):
x, y = field(phase, *params)
ax.axhline(0, color="#c4ccd2", lw=0.9)
ax.axvline(0, color="#c4ccd2", lw=0.9)
ax.plot(x, y, color="#258a95", lw=2.6)
ax.annotate("", field(np.pi / 4, *params), (0, 0),
arrowprops={"arrowstyle": "->", "lw": 2,
"color": "#174d85"})
for start in [0.95, 3.8]:
ax.annotate("", field(start + 0.3, *params), field(start, *params),
arrowprops={"arrowstyle": "-|>", "lw": 2.3,
"color": "#bd591b", "mutation_scale": 15})
initial = field(0, *params)
ax.scatter(*initial, s=42, color="#bd591b", edgecolors="white",
linewidths=0.6, zorder=5)
ax.set(xlim=(-2.5, 2.5), ylim=(-2.5, 2.5), aspect="equal",
xlabel=r"$E_x$ (V/m)", ylabel=r"$E_y$ (V/m)")
ax.set_xticks([-2, -1, 0, 1, 2])
ax.set_yticks([-2, -1, 0, 1, 2])
ax.grid(alpha=0.12)
ax.set_title(title + "\n" + param, fontsize=12, pad=12)
ax.text(0.5, -0.34, equation, transform=ax.transAxes, ha="center",
fontsize=12)
fig.suptitle("Polarization: the tip of the real electric field", fontsize=16,
y=0.96)
fig.text(0.5, 0.88, "Fixed position; equal coordinate scales; amplitudes in V/m",
ha="center", fontsize=11)
fig.text(0.5, 0.06,
"Orange arrows: increasing phase φ = ωt. Dot: φ = 0. Blue vector: φ = π/4.",
ha="center", fontsize=11)
fig.subplots_adjust(left=0.07, right=0.98, bottom=0.26, top=0.71,
wspace=0.40)
output = Path.cwd() / Path(__file__).with_suffix(".png").name
fig.savefig(output, facecolor="white", transparent=False)
plt.close(fig)
print("Checked: line, unit circle, 2:1 ellipse, general phase equation; "
"ellipse magnitudes 1, 2, sqrt(5/2); analyzer fractions 4/5, 1/2, 1/5. "
+ output.name)
if __name__ == "__main__":
main()