Visualisation

Plotting functions are available as methods on Magnet and Arrangement, and as free functions in microcubed.viz. Each coordinate is either fixed or varying:

  • fixed section: z=-150

  • uniform range: x=(-500, 500, 201)

  • explicit coordinates: x=np.array([...])

Source geometry

All examples below use the same cuboid; lengths are in nm. White outlines on plane maps are projections of the source’s union_boundary, not intersections with the sampling plane.

import matplotlib.pyplot as plt
import numpy as np
from microcubed import Magnet

magnet = Magnet([200, 120, 60], [0, 0, 0], [0, 0, 8e5])
print("Projected XY material boundary (nm):\n", magnet.union_boundary("xy"))
Projected XY material boundary (nm):
 [array([[-100.,  100.,  100., -100., -100.],
       [ -60.,  -60.,   60.,   60.,  -60.]])]

1D line section

Exactly one axis must vary:

fig, ax = magnet.plot_1d(
    x=(-500, 500, 201),
    y=0,
    z=-150,
    component="z",
    color="tab:blue",
)
plt.show()
_images/ea376bf02877a94c5ec04a050a497c923e47c49ad281cd473f8d41b176524884.png

Without component, \(|\mathbf B|\) is plotted.

Gradient components are selected as (derivative axis, field axis):

fig, ax = magnet.plot_1d(
    x=(-500, 500, 201),
    y=0,
    z=-150,
    what="dBfield",
    component=("x", "z"),
)
plt.show()
_images/0ea616951ddb91ccd6ee08764e5dbdf7c53ef682647334c69a11fcff32aa2e13.png

This displays \(\partial_xB_z\).

2D plane section

Exactly two axes must vary:

fig, ax = magnet.plot_2d(
    x=(-450, 450, 101),
    y=(-350, 350, 81),
    z=-150,
    component="x",
    cmap="seismic",
)
ax.set_aspect("equal")
for boundary in magnet.union_boundary("xy"):
    ax.plot(*boundary, "w-", linewidth=2)
ax.set(xlabel="x (nm)", ylabel="y (nm)", aspect="equal")
plt.show()
_images/7d599b7c84044f27ce9eff36176087d1340f953d1b64a03791d8a87d7faab2ce.png

Gradient heatmap:

fig, ax = magnet.plot_2d(
    x=(-450, 450, 101),
    y=(-350, 350, 81),
    z=-150,
    what="dBfield",
    component=("y", "z"),
    cmap="seismic",
)
for boundary in magnet.union_boundary("xy"):
    ax.plot(*boundary, "w-", linewidth=2)
ax.set(xlabel="x (nm)", ylabel="y (nm)", aspect="equal")
plt.show()
_images/a75a240900c6bdce604c2c27c5254b58729a70540a1192fec8180a400c8ba256.png

For a gradient with component=None, the Frobenius norm of the full 3×3 matrix is displayed.

3D vector field

All three axes must vary:

fig = plt.figure(figsize=(9, 6))
ax = fig.add_subplot(projection="3d")
fig, ax = magnet.plot_3d(
    ax=ax,
    x=(-400, 400, 12),
    y=(-300, 300, 10),
    z=(-300, -100, 5),
    max_points=600,
    normalize=True,
    length=35,
)
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

faces = magnet.corners.T[[[0, 1, 3, 2], [4, 5, 7, 6], [0, 1, 5, 4], [2, 3, 7, 6], [0, 2, 6, 4], [1, 3, 7, 5]]]
ax.add_collection3d(Poly3DCollection(faces, alpha=0.15, edgecolor="black"))
ax.set(xlim=(-400, 400), ylim=(-300, 300), zlim=(-300, 40))
ax.set_box_aspect((800, 600, 340))
ax.set(xlabel="x (nm)", ylabel="y (nm)", zlabel="z (nm)")
# Leave room between the 3D tick labels and the colorbar.
ax.set_position([0.02, 0.08, 0.68, 0.84])
fig.axes[-1].set_position([0.87, 0.22, 0.025, 0.56])
plt.show()
_images/5a70f8695fdc5204fa4e255ebcc11cf16ace2312d1033ec2ee0a4bba5c2edec8.png

Arrows show the vector field; their colour encodes its magnitude by default. max_points limits arrow count. 3D quiver plots support Bfield and Hfield, not the rank-2 gradient tensor.

Existing Matplotlib axes

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6, 4))
magnet.plot_2d(
    x=(-450, 450, 101),
    y=(-350, 350, 81),
    z=-150,
    component="z",
    ax=ax,
    colorbar=False,
)
for boundary in magnet.union_boundary("xy"):
    ax.plot(*boundary, "w-", linewidth=2)
ax.set(xlabel="x (nm)", ylabel="y (nm)", aspect="equal")
plt.show()
_images/3d6e673237a2cbb0b3074ecfa37697d18ce91d1a3aed9aec962e1636914ec027.png

Additional keyword arguments are forwarded to Axes.plot, Axes.pcolormesh, or Axes3D.quiver respectively.

All nine gradient components

For a publication figure in a 3×3 layout, use sample_field directly:

import matplotlib.pyplot as plt
import numpy as np
from microcubed import sample_field

(x, y, z), dB = sample_field(
    magnet,
    x=(-450, 450, 101),
    y=(-350, 350, 81),
    z=-150,
    what="dBfield",
)
dB = dB[..., 0]

fig, axes = plt.subplots(3, 3, figsize=(12, 10), constrained_layout=True)
for derivative in range(3):
    for component in range(3):
        values = dB[derivative, component]
        limit = np.nanmax(np.abs(values))
        image = axes[derivative, component].pcolormesh(
            x,
            y,
            values.T,
            shading="auto",
            cmap="seismic",
            vmin=-limit,
            vmax=limit,
        )
        fig.colorbar(image, ax=axes[derivative, component], shrink=0.65)
        for boundary in magnet.union_boundary("xy"):
            axes[derivative, component].plot(*boundary, "k-", linewidth=1)
        axes[derivative, component].set(
            title=f"dB{'xyz'[component]}/d{'xyz'[derivative]} (T/nm)",
            xlabel="x (nm)", ylabel="y (nm)", aspect="equal",
        )
plt.show()
_images/a32a568893319e118fd653f872c482dd5fa2ba96a04d2068391168e9c1989553.png