Verifying functionality of the Antiferromagnet function as a Ferromagnet

24 views
Skip to first unread message

Miroslav Polach

unread,
Feb 16, 2026, 10:30:02 AMFeb 16
to mumax2
Dear all,
I am trying to do some simulations with AFM medium. In the process of understanding how each parameter works, I have stumbled across a functionality that I cannot explain with the API documentation of mumax+. When I set up a ferromagnet via the Antiferromagnet function, by setting the parameters of one sublattice to zero, it works, as I would expect. However, if I set up Msat for the omitted sublattice, I would still expect it to work as before, which it doesn't. Notably, the omitted sublattice aligns, which it shouldn't, since Aex for that sublattice I set to zero. 
I also set the parameters afmex_cell and afmex_nn to zero to remove any coupling between the sublattices.

For reference, my code is below. I hope it works for you. If anyone has an idea what I am doing wrong, it would be invaluable.

I really appreciate any help you can provide.
Sincerely,
Miroslav Polách

=======================================================================
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import FuncFormatter
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

from mumaxplus import Antiferromagnet, Ferromagnet, Grid, World
from mumaxplus.util import plot_field

# Fuctions for visualization
def plot_cube_with_section(ax, out_of_plane='z'):
 """Plot a cube and its cross-section plane on a given axis."""
 r = [0, 1]
 vertices = np.array([
 [r[0], r[0], r[0]], [r[1], r[0], r[0]],
 [r[1], r[1], r[0]], [r[0], r[1], r[0]],
 [r[0], r[0], r[1]], [r[1], r[0], r[1]],
 [r[1], r[1], r[1]], [r[0], r[1], r[1]]
 ])
 faces = [
 [vertices[0], vertices[1], vertices[2], vertices[3]],
 [vertices[4], vertices[5], vertices[6], vertices[7]],
 [vertices[0], vertices[1], vertices[5], vertices[4]],
 [vertices[2], vertices[3], vertices[7], vertices[6]],
 [vertices[1], vertices[2], vertices[6], vertices[5]],
 [vertices[4], vertices[7], vertices[3], vertices[0]]
 ]

 # Draw cube
 ax.add_collection3d(Poly3DCollection(faces, facecolors='white', edgecolors='k', alpha=0.3))

 # Add cross-section plane
 s = np.linspace(0, 1, 2)
 if out_of_plane == 'z':
 xx, yy = np.meshgrid(s, s)
 zz = np.full_like(xx, 0.5)
 ax.plot_surface(xx, yy, zz, color='gray', alpha=0.6)
 #ax.set_title("XY Plane (z = 0.5)")
 elif out_of_plane == 'y':
 xx, zz = np.meshgrid(s, s)
 yy = np.full_like(xx, 0.5)
 ax.plot_surface(xx, yy, zz, color='gray', alpha=0.6)
 #ax.set_title("XZ Plane (y = 0.5)")
 elif out_of_plane == 'x':
 yy, zz = np.meshgrid(s, s)
 xx = np.full_like(yy, 0.5)
 ax.plot_surface(xx, yy, zz, color='gray', alpha=0.6)
 #ax.set_title("YZ Plane (x = 0.5)")
 else:
 raise ValueError("plane must be 'z', 'y', or 'x'")

 # Aesthetics
 ax.set_xlim(0, 1)
 ax.set_ylim(0, 1)
 ax.set_zlim(0, 1)
 ax.set_box_aspect([1,1,1])
 ax.set_xticks([]); ax.set_yticks([]); ax.set_zticks([])

#########################################################
###################### mumax+ code ######################
#########################################################
cross_sections = False # set to True to plot only cross-section planes in the plots

# cell size
length, width, thickness = 3e-9, 3e-9, 3e-9
world = World(cellsize=(length, width, thickness))

# number of cells
n = 15
nx, ny, nz = n, n, n

grid = Grid((nx, ny, nz))

# magnet envirnment
AFM = Antiferromagnet(world, grid, name="Ferromagnet")
AFM.afmex_cell = 0
AFM.afmex_nn = 0

# material parameters - Magnetite Fe3O4
AFM.sub1.msat = 0.48e6
AFM.sub1.aex = 7e-12
AFM.sub1.kc1 = -13e3
AFM.sub1.anisC1 = (1,0,0)
AFM.sub1.anisC2 = (0,1,0)
AFM.sub1.alpha = 0.1

# material parameters - other sublattice
AFM.sub2.msat = 0 # if set to 0.48e6, the sub2 also minimizes
AFM.sub2.aex = 0
AFM.sub2.kc1 = 0
AFM.sub2.alpha = 0

np.random.seed(1983)
m = np.zeros(AFM.sub1.magnetization.shape)
m[:,:,:,:] += np.random.normal(0, 1, size=m[:,:,:,:].shape)
AFM.sub1.magnetization = m

#np.random.seed(1984)
#m = np.zeros(AFM.sub2.magnetization.shape)
#m[:,:,:,:] += np.random.normal(0, 1, size=m[:,:,:,:].shape)
#AFM.sub2.magnetization = m


# plot_field(AFM.full_magnetization, arrow_size=1, show=True, layer=nz//2, title=False)

AFM.relax()

# show full magnetization
outofplane_axes = ['z', 'y', 'x']
fig = plt.figure(figsize=(12, 3))

for i, out_axis in enumerate(outofplane_axes, 1):
 if cross_sections:
 ax = fig.add_subplot(1, 3, i, projection='3d')
 plot_cube_with_section(ax, out_axis)

 else:
 ax = fig.add_subplot(1, 3, i)
 ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x*1e9:g}"))
 ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: f"{y*1e9:g}"))
 if out_axis == 'x':
 m = AFM.full_magnetization()[:, :, :, [n//2]]
 m = np.moveaxis(m, 3, 1)

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$y$ (nm)", ylabel="$z$ (nm)")
       
 if out_axis == 'y':
 m = AFM.full_magnetization()[:, :, [n//2], :]
 m = np.moveaxis(m, 2, 1)

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$x$ (nm)", ylabel="$z$ (nm)")

 if out_axis == 'z':
 m = AFM.full_magnetization()[:, [n//2], :, :]

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$x$ (nm)", ylabel="$y$ (nm)")

if cross_sections:
 plt.tight_layout()
 plt.savefig("cube_NP_cross_sections.pdf", dpi=300)
else:
 plt.tight_layout()
 if n <= 10:
 plt.savefig("AFM_cube_text_full_magnetization_small.pdf", dpi=300)
 elif n <= 20:
 plt.savefig("AFM_cube_text_full_magnetization_medium.pdf", dpi=300)
 else:
 plt.savefig("AFM_cube_text_full_magnetization_large.pdf", dpi=300)
plt.show()

# show magnetization of sublattice 1
fig = plt.figure(figsize=(12, 3))
for i, out_axis in enumerate(outofplane_axes, 1):
 if cross_sections:
 ax = fig.add_subplot(1, 3, i, projection='3d')
 plot_cube_with_section(ax, out_axis)

 else:
 ax = fig.add_subplot(1, 3, i)
 ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x*1e9:g}"))
 ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: f"{y*1e9:g}"))
 if out_axis == 'x':
 m = AFM.sub1.magnetization()[:, :, :, [n//2]]
 m = np.moveaxis(m, 3, 1)

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$y$ (nm)", ylabel="$z$ (nm)")
       
 if out_axis == 'y':
 m = AFM.sub1.magnetization()[:, :, [n//2], :]
 m = np.moveaxis(m, 2, 1)

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$x$ (nm)", ylabel="$z$ (nm)")

 if out_axis == 'z':
 m = AFM.sub1.magnetization()[:, [n//2], :, :]

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$x$ (nm)", ylabel="$y$ (nm)")

if cross_sections:
 plt.tight_layout()
 plt.savefig("cube_NP_cross_sections.pdf", dpi=300)
else:
 plt.tight_layout()
 if n <= 10:
 plt.savefig("AFM_cube_text_sub1_magnetization_small.pdf", dpi=300)
 elif n <= 20:
 plt.savefig("AFM_cube_text_sub1_magnetization_medium.pdf", dpi=300)
 else:
 plt.savefig("AFM_cube_text_sub1_magnetization_large.pdf", dpi=300)
plt.show()


# show magnetization of sublattice 2
fig = plt.figure(figsize=(12, 3))
for i, out_axis in enumerate(outofplane_axes, 1):
 if cross_sections:
 ax = fig.add_subplot(1, 3, i, projection='3d')
 plot_cube_with_section(ax, out_axis)

 else:
 ax = fig.add_subplot(1, 3, i)
 ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x*1e9:g}"))
 ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: f"{y*1e9:g}"))
 if out_axis == 'x':
 m = AFM.sub2.magnetization()[:, :, :, [n//2]]
 m = np.moveaxis(m, 3, 1)

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$y$ (nm)", ylabel="$z$ (nm)")
       
 if out_axis == 'y':
 m = AFM.sub2.magnetization()[:, :, [n//2], :]
 m = np.moveaxis(m, 2, 1)

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$x$ (nm)", ylabel="$z$ (nm)")

 if out_axis == 'z':
 m = AFM.sub2.magnetization()[:, [n//2], :, :]

 world_help = World(cellsize=(length, width, thickness))
 magnet_help = Ferromagnet(world_help, Grid((nx, ny, 1)), name="2D_projection")
 magnet_help.magnetization = m
 plot_field(magnet_help.magnetization, arrow_size=2, ax=ax, title=False, xlabel="$x$ (nm)", ylabel="$y$ (nm)")

if cross_sections:
 plt.tight_layout()
 plt.savefig("cube_NP_cross_sections.pdf", dpi=300)
else:
 plt.tight_layout()
 if n <= 10:
 plt.savefig("AFM_cube_text_sub2_magnetization_small.pdf", dpi=300)
 elif n <= 20:
 plt.savefig("AFM_cube_text_sub2_magnetization_medium.pdf", dpi=300)
 else:
 plt.savefig("AFM_cube_text_sub2_magnetization_large.pdf", dpi=300)
plt.show()


# print average magnetizations
print((AFM.sub1.magnetization.average()))
print((AFM.sub2.magnetization.average()))

Lars Moreels

unread,
Feb 17, 2026, 1:58:46 AMFeb 17
to mumax2

Hi

The alignment caused by setting `sub2.msat` is caused by magnetostatic interactions. These can be disabled by `AFM.enable_demag = False`. It is not possible to disable the demag field for one single sublattice, since mumax+ calculates the sum of magnetization vectors in each cell before calculating the magnetostatic field.

Hope this helps!
Lars

Reply all
Reply to author
Forward
0 new messages