codexmagnetization-dipole-vector-averaging.py
magnetization-dipole-vector-averaging.py
"""Draw an original dipole-counting model; emit a PNG in the working directory.
Tested with Python 3.14, matplotlib 3.10.7, and numpy 2.3.5.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, Rectangle
import numpy as np
def main():
plt.rcParams.update({"font.size": 12, "mathtext.fontset": "dejavusans"})
fig, axes = plt.subplots(1, 2, figsize=(9.6, 4.8), dpi=120)
fig.patch.set_facecolor("white")
fig.suptitle("Same individual moments, different magnetization", fontsize=17, y=0.96)
blue, orange = "#17649b", "#bf5815"
patterns = (np.array([1, 1, -1, 1, 1, -1, 1, 1]), np.ones(8, dtype=int))
for ax, signs, title, net in zip(
axes, patterns, ("Six up, two down", "All eight up"), (4, 8), strict=True
):
ax.set_aspect("equal")
ax.set_xlim(-0.3, 4.3)
ax.set_ylim(-1.55, 2.8)
ax.axis("off")
ax.set_title(title, pad=11, fontsize=14)
ax.add_patch(Rectangle((0, 0), 4, 2.2, facecolor="#f3f8fc", edgecolor=blue, lw=1.5, linestyle="--"))
ax.text(3.88, 2.35, r"Same volume $\Delta V$", ha="right", color=blue)
for i, sign in enumerate(signs):
x, y = 0.5 + i % 4, 0.55 + i // 4 * 1.1
ax.add_patch(FancyArrowPatch((x, y - 0.34 * sign), (x, y + 0.34 * sign), arrowstyle="-|>", mutation_scale=17, lw=2.4, color=blue if sign > 0 else orange))
ax.plot(x, y, "o", color="#44515a", ms=3)
ax.text(2, -0.36, rf"$\sum_i\mathbf{{m}}_i = {net}m_0\widehat{{\mathbf{{z}}}}$", ha="center", fontsize=15)
ax.text(2, -0.87, rf"$\mathbf{{M}} = \frac{{{net}m_0}}{{\Delta V}}\widehat{{\mathbf{{z}}}}$", ha="center", fontsize=16)
fig.text(0.5, 0.045, r"Every arrow has magnitude $m_0$. Reorientation changes the vector sum.", ha="center", fontsize=12)
fig.subplots_adjust(left=0.035, right=0.985, top=0.83, bottom=0.14, wspace=0.12)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()