Quickstart#
This page gives a whirlwind introduction to flatspin. Make sure you have installed flatspin before you continue.
The best way to get familiar with flatspin, is to play with the simulator in an interactive Python environment. This guide is written as a Jupyter notebook, which you can download and run yourself. Just click the download link from the top of this page.
Let’s get started!
The flatspin model#
The main object in flatspin is the model.
Each model class defines a spin ice geometry, which specifies the positions and angles of the spins.
The SquareSpinIceClosed
class, for instance, creates a square spin ice geometry (with “closed” edges):
from flatspin.model import SquareSpinIceClosed
model = SquareSpinIceClosed()
model.plot();
A list of available model classes can be found in the user guide. Custom geometries may be created by extending flatspin.
Model parameters#
Properties of the model can be changed through parameters, which are passed as keyword arguments to the model class. For instance, we may change the size
parameter to create a larger spin ice:
model = SquareSpinIceClosed(size=(10,5))
print(f"10x5 square ASI has", model.spin_count, "spins")
model.plot();
10x5 square ASI has 115 spins
Note that size
is geometry-specific: for SquareSpinIceClosed
, the size specifies the number of columns (rows) of horizontal (vertical) magnets.
For KagomeSpinIce
, the size denotes the number of hexagonal units:
from flatspin.model import KagomeSpinIce
model = KagomeSpinIce(size=(10,5))
print(f"10x5 kagome ASI has", model.spin_count, "spins")
model.plot();
10x5 kagome ASI has 179 spins
Other important parameters include alpha
(the coupling strength), hc
(the coercive field), disorder
(random variations in the coercive fields) and temperature
(absolute temperature in Kelvin). For a list of available parameters, see SpinIce
.
Running the model#
The primary method for interacting with the model is the external field. We set the external field with set_h_ext()
.
model = SquareSpinIceClosed()
model.set_h_ext([-0.058, -0.058])
Next we run the model by calling relax()
, which will flip all spins until equilibrium is reached, i.e., until there are no more flippable spins. relax()
returns the number of spins that were flipped.
flips = model.relax()
print(flips, "spins flipped")
model.plot();
23 spins flipped
We may also flip only a single spin at a time by calling step()
. For each call to step()
, the spin with the highest “switching energy” will be flipped, and the method returns True
. If there are no flippable spins, step()
returns False
. Below we use step()
to create an animation of the relaxation process.
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
# Reset system back to the polarized state
model.polarize()
fig, ax = plt.subplots()
def do_steps():
yield model
while model.step():
yield model
def animate(model):
model.plot(ax=ax, replace=True)
anim = FuncAnimation(fig, animate, frames=do_steps(), interval=200, blit=False)
plt.close() # Only show the animation
HTML(anim.to_jshtml())
/tmp/ipykernel_7976/2208795677.py:17: UserWarning: frames=<generator object do_steps at 0x7f133d046a40> which we can infer the length of, did not pass an explicit *save_count* and passed cache_frame_data=True. To avoid a possibly unbounded cache, frame data caching has been disabled. To suppress this warning either pass `cache_frame_data=False` or `save_count=MAX_FRAMES`.
anim = FuncAnimation(fig, animate, frames=do_steps(), interval=200, blit=False)
Hysteresis loops#
Often we are interested in some average quantity of the ensemble, such as the total magnetization.
A hysteresis loop is a plot of the total magnetization, projected along the direction of the external field. Below we gradually increase the strength H
of the external field, applied at some angle phi
, and record the total magnetization for each field value H
. We also enable GPU acceleration to speed up the simulation with use_opencl=True
.
If your system is not set up to use opencl, replace with use_opencl=False
(this will slow down your simulations significantly).
model = SquareSpinIceClosed(size=(10,10), use_opencl=True)
# H decreases linearly from 0.1 to -0.1, then back to 0.1
H = np.linspace(0.1, -0.1, 500)
H = np.concatenate([H, -H])
# Angle of the external field
phi = np.deg2rad(45)
h_dir = np.array([np.cos(phi), np.sin(phi)])
M_H = []
for h in H:
model.set_h_ext(h * h_dir)
model.relax()
# Magnetization projected along field direction
m = model.total_magnetization().dot(h_dir)
M_H.append(m)
plt.plot(H, M_H)
plt.xlabel("H [T]")
plt.ylabel(r"M_H [a.u.]");
And here is an animation of the hysteresis loop, where only changes to the state are shown:
# Reset system back to the polarized state
model.polarize()
fig, ax = plt.subplots()
def do_hysteresis():
yield model
for h in H:
model.set_h_ext(h * h_dir)
if model.relax():
# Only show frames where any spins flipped
yield model
def animate(model):
h = model.h_ext[0].dot(h_dir)
ax.set_title(f'H = {h:g}')
model.plot(ax=ax, replace=True)
anim = FuncAnimation(fig, animate, frames=do_hysteresis(), interval=200, blit=False)
plt.close() # Only show the animation
HTML(anim.to_jshtml())
/tmp/ipykernel_7976/912287197.py:19: UserWarning: frames=<generator object do_hysteresis at 0x7f129e5f4b30> which we can infer the length of, did not pass an explicit *save_count* and passed cache_frame_data=True. To avoid a possibly unbounded cache, frame data caching has been disabled. To suppress this warning either pass `cache_frame_data=False` or `save_count=MAX_FRAMES`.
anim = FuncAnimation(fig, animate, frames=do_hysteresis(), interval=200, blit=False)