codex/phase-conjugate-wavefront-ray-retracing.py

phase-conjugate-wavefront-ray-retracing.py
"""Compare specular reflection with phase-conjugate return in a uniform medium.

Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7. Run from the output directory.
"""
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    plt.rcParams.update({"font.size": 11, "figure.facecolor": "white"})
    fig, axes = plt.subplots(1, 2, figsize=(10, 5.3), sharex=True, sharey=True)
    blue, orange, green = "#2463a8", "#bb4d12", "#168064"
    L = 3.0
    z = np.linspace(0, L, 180)
    x = np.linspace(-1.65, 1.65, 400)
    for ax, conjugate in zip(axes, (False, True)):
        return_color = green if conjugate else orange
        ax.axvline(L, color="#343434", lw=4)
        for radius in (1.8, 2.3, 2.8):
            valid = np.abs(x) < radius
            zx = np.sqrt(radius**2 - x[valid] ** 2)
            ax.plot(zx, x[valid], color=blue, lw=1.2, alpha=0.5)
            if conjugate:
                ax.plot(zx, x[valid], color=green, ls="--", lw=1.2)
        if not conjugate:
            for radius in (3.4, 4.1, 4.8, 5.5):
                zx = 2 * L - np.sqrt(radius**2 - x**2)
                ax.plot(zx, x, color=orange, ls="--", lw=1.2, alpha=0.75)
        for slope in (-0.25, 0, 0.25):
            outward = slope * z
            returning = outward if conjugate else slope * (2 * L - z)
            ax.plot(z, outward, color=blue, lw=2)
            ax.plot(z, returning, color=return_color, lw=2, ls="--")
            ax.annotate("", (1.25, slope * 1.25), (0.85, slope * 0.85),
                        arrowprops={"arrowstyle": "->", "color": blue, "lw": 2})
            rz = (2.2, 1.8)
            rx = tuple(slope * q if conjugate else slope * (2 * L - q) for q in rz)
            ax.annotate("", (rz[1], rx[1]), (rz[0], rx[0]),
                        arrowprops={"arrowstyle": "->", "color": return_color, "lw": 2})
        ax.plot(0, 0, "o", color=blue, ms=7, zorder=5)
        if not conjugate:
            ax.scatter([0, 0], [-1.5, 1.5], color=orange, s=35, zorder=5)
        ax.text(L, 1.83, "return surface", ha="center", fontsize=10)
        ax.set(xlim=(-0.22, 3.32), ylim=(-1.8, 1.8), xlabel="z (geometric units)")
        ax.set_aspect("equal", adjustable="box")
        ax.set_title("Phase-conjugate mirror" if conjugate else "Ordinary flat mirror", pad=29)
        ax.grid(alpha=0.15)
        ax.spines[["top", "right"]].set_visible(False)
    axes[0].set_ylabel("x (geometric units)")
    fig.text(0.285, 0.12, "Return at z = 0: x = −1.5, 0, 1.5", ha="center")
    fig.text(0.775, 0.12, "Every ideal return ray reaches the source", ha="center")
    fig.suptitle("Reversing the entire wavevector retraces the ray", y=0.98, fontsize=15)
    fig.text(0.5, 0.035, "Blue solid: outward rays and wavefronts    •    Colored dashed: return rays and wavefronts",
             ha="center", fontsize=10)
    fig.subplots_adjust(left=0.07, right=0.97, bottom=0.23, top=0.77, wspace=0.23)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=120, facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()