codexdielectric-polarization-fixed-charge-fields.py
dielectric-polarization-fixed-charge-fields.py
"""Draw the fixed-charge dielectric model; tested with Python 3.14.
Dependencies: matplotlib 3.10.7 and numpy 2.3.5, as in pyproject.toml.
Run from _media; the opaque PNG is written to the current directory.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Rectangle
import numpy as np
def main():
epsilon_r = 4.0
field_fraction = 1.0 / epsilon_r
polarization_fraction = 1.0 - field_fraction
assert np.isclose(field_fraction + polarization_fraction, 1.0)
plt.rcParams.update({"font.size": 11, "savefig.facecolor": "white"})
fig, (ax, bars) = plt.subplots(
1, 2, figsize=(10.8, 5.0), dpi=110,
gridspec_kw={"width_ratios": [1.6, 1]},
)
fig.patch.set_facecolor("white")
fig.suptitle("Polarization at fixed free plate charge", fontsize=16, y=0.96)
ax.set(xlim=(0, 6.5), ylim=(-0.2, 6.1))
ax.axis("off")
ax.text(3.25, 5.55, r"Uniform dielectric: $\epsilon_r=4$", ha="center", fontsize=13)
ax.add_patch(Rectangle((1.65, 2.1), 3.2, 2.8, fc="#eef4fa", ec="#a5b7c9"))
for x, sign, color in [(1.2, "+", "#ba4239"), (5.3, "−", "#2866a3")]:
ax.plot([x, x], [1.9, 5.05], color="#4b515b", lw=7)
for y in np.linspace(2.25, 4.65, 5):
sign_x = x - 0.18 if sign == "+" else x + 0.18
ax.text(sign_x, y, sign, color=color, ha="center", va="center", fontsize=15)
ax.text(1.2, 1.65, r"$+Q$", ha="center", color="#ba4239")
ax.text(5.3, 1.65, r"$-Q$", ha="center", color="#2866a3")
for x, sign, color in [(1.75, "−", "#2866a3"), (4.75, "+", "#ba4239")]:
for y in np.linspace(2.4, 4.6, 5):
ax.text(x, y, sign, color=color, ha="center", va="center", fontsize=14)
for y in [2.85, 3.65, 4.45]:
for x in [2.4, 3.65]:
ax.add_patch(Circle((x, y), 0.11, fc="#2866a3", ec="none"))
ax.add_patch(Circle((x + 0.55, y), 0.11, fc="#ba4239", ec="none"))
ax.text(x, y, "−", ha="center", va="center", color="white", fontsize=10)
ax.text(x + 0.55, y, "+", ha="center", va="center", color="white", fontsize=10)
ax.annotate("", xy=(x + 0.48, y), xytext=(x + 0.13, y),
arrowprops={"arrowstyle": "->", "color": "#56616c", "lw": 1.2})
ax.text(3.25, 2.35, r"Dipole moments and $\mathbf{P}$ point right", ha="center", fontsize=10)
ax.text(3.25, 5.1, "Bound surface charges oppose plate charges", ha="center", fontsize=10)
# Field arrows have lengths in the exact ratio 1 : 3/4 : 1/4.
for y, start, end, label, color in [
(1.1, 2.25, 5.25, r"$E_{\rm free}$", "#626b77"),
(0.6, 5.25, 5.25 - 3 * polarization_fraction, r"$E_{\rm pol}$", "#b57b13"),
(0.1, 2.25, 2.25 + 3 * field_fraction, r"$E$ (total)", "#2866a3"),
]:
ax.text(1.95, y, label, ha="right", va="center")
ax.annotate("", xy=(end, y), xytext=(start, y),
arrowprops={"arrowstyle": "-|>", "lw": 2.2, "color": color,
"mutation_scale": 13})
bars.set_title(r"$D=Q/A$ stays fixed", fontsize=13, pad=15)
bars.barh([1, 0], [1, field_fraction], color="#2866a3", height=0.36,
label=r"$\epsilon_0 E$")
bars.barh([1, 0], [0, polarization_fraction], left=[1, field_fraction],
color="#d8a13c", height=0.36, label=r"$P$")
bars.text(0.5, 1, "1", ha="center", va="center", color="white", fontsize=13)
bars.text(field_fraction / 2, 0, "1/4", ha="center", va="center", color="white", fontsize=11)
bars.text(field_fraction + polarization_fraction / 2, 0, "3/4", ha="center", va="center", fontsize=13)
bars.set(yticks=[0, 1], yticklabels=["Dielectric", "Vacuum"], xlim=(0, 1.04),
ylim=(-0.7, 1.7), xticks=[0, 0.25, 0.5, 0.75, 1],
xlabel=r"Fraction of $D_{\rm ref}=Q/A$ (dimensionless)")
bars.grid(axis="x", alpha=0.18)
bars.set_axisbelow(True)
bars.spines[["top", "right"]].set_visible(False)
bars.legend(loc="upper center", ncol=2, frameon=False, bbox_to_anchor=(0.5, 1.03))
fig.text(0.5, 0.075, r"$D=\epsilon_0E+P$; all three terms have units C/m².",
ha="center", fontsize=10)
fig.text(0.5, 0.025, "Ideal plates; uniform static scalar response; schematic not to microscopic scale.",
ha="center", fontsize=10, color="#4b515b")
fig.subplots_adjust(left=0.025, right=0.975, bottom=0.22, top=0.84, wspace=0.26)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()