{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "9c69f8b5", "metadata": { "tags": [ "remove-input" ] }, "outputs": [], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "\n", "import numpy as np\n", "from numpy.linalg import norm\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from matplotlib.animation import FuncAnimation\n", "from IPython.display import HTML\n", "plt.rcParams['animation.frame_format'] = \"svg\"" ] }, { "cell_type": "markdown", "id": "57eb9eb9", "metadata": {}, "source": [ "(anneal-field)=\n", "# Field based annealing\n", "\n", "This example shows how to set up a field-based annealing protocol.\n", "Below we anneal square spin ice using a rotating field, whose strength is gradually decreased.\n", "Disorder introduces small variations in the coercive fields, which actually helps the annealing process by creating nucleation points in the lattice." ] }, { "cell_type": "code", "execution_count": null, "id": "146434dc", "metadata": {}, "outputs": [], "source": [ "from flatspin.model import SquareSpinIceClosed\n", "\n", "model = SquareSpinIceClosed(size=(25,25), disorder=0.05, use_opencl=True)\n", "model.plot_vertex_mag();" ] }, { "cell_type": "markdown", "id": "0c76f02b", "metadata": {}, "source": [ "## Rotating field protocol" ] }, { "cell_type": "markdown", "id": "1b5153ef", "metadata": {}, "source": [ "We use the {class}`Rotate ` encoder to set up the external rotating field.\n", "The `timesteps` parameter denotes the resolution of one full rotation, while `H0` and `H` sets the minimum and maximum field strength, respectively. The length of the `input` array defines the number of rotations (50), while the values define the field strength for each rotation, where a value of `1.0` is mapped to `H`, and a value of `0.0` maps to `H0`." ] }, { "cell_type": "code", "execution_count": null, "id": "b6646a74", "metadata": {}, "outputs": [], "source": [ "from flatspin.encoder import Rotate\n", "\n", "timesteps = 64\n", "enc = Rotate(H=0.09, H0=0.06, timesteps=timesteps)\n", "input = np.linspace(1, 0, 20)\n", "\n", "h_ext = enc(input)\n", "H = norm(h_ext, axis=1)\n", "\n", "plt.plot(norm(h_ext, axis=1), label=\"norm(h_ext)\")\n", "plt.plot(h_ext[:,0], label=\"h_ext[0]\")\n", "plt.plot(h_ext[:,1], label=\"h_ext[1]\")\n", "plt.xlabel(\"time step\")\n", "plt.ylabel(\"h_ext [T]\")\n", "plt.legend(loc='upper left', bbox_to_anchor=(1.0, 1.0));" ] }, { "cell_type": "markdown", "id": "8b4aea84", "metadata": {}, "source": [ "## Run the field protocol\n", "\n", "Below we iterate over each `h_ext` value in the field protocol, and update the model accordingly. We record the number of spin flips (`steps`) and dipolar energy (`E_dip`) per field value. At the end of each rotation, we also take a snapshot of the `spin` array." ] }, { "cell_type": "code", "execution_count": null, "id": "553af930", "metadata": {}, "outputs": [], "source": [ "# Start in polarized state\n", "model.polarize()\n", "\n", "# Record spins, number of spin flips and dipolar energy over time\n", "spins = []\n", "flips = []\n", "E_dip = []\n", "for i, h in enumerate(h_ext):\n", " model.set_h_ext(h)\n", " s = model.relax()\n", " if (i+1) % timesteps == 0:\n", " # Record spin state at the end of each rotation\n", " spins.append(model.spin.copy())\n", " flips.append(s)\n", " E_dip.append(model.total_dipolar_energy())\n", "\n", "print(f\"Completed {sum(flips)} steps\")" ] }, { "cell_type": "markdown", "id": "1f6966ad", "metadata": {}, "source": [ "## Spin flips over time\n", "\n", "Here we plot the total number of spin flips per field strength, i.e., per field rotation. As can be seen, the strongest fields saturates the array by flipping every spin twice. As the field strength decreases, so does the number of spin flips. Eventually the field becomes too weak to flip any spins." ] }, { "cell_type": "code", "execution_count": null, "id": "4b465ae5", "metadata": {}, "outputs": [], "source": [ "H = norm(h_ext, axis=-1).round(10)\n", "df = pd.DataFrame({'H': H, 'flips': flips})\n", "df.groupby('H', sort=False).sum().plot(legend=False)\n", "plt.gca().invert_xaxis()\n", "plt.ylabel(\"Spin flips\")\n", "plt.xlabel(\"Field strength [T]\");" ] }, { "cell_type": "markdown", "id": "efeff4a9", "metadata": {}, "source": [ "## Dipolar energy\n", "\n", "The total dipolar energy is the sum of the dipolar fields acting anti-parallel to the spin magnetization. It is a measure of the total frustration in the system, and a good measure of how well the system has annealed." ] }, { "cell_type": "code", "execution_count": null, "id": "14c35489", "metadata": {}, "outputs": [], "source": [ "plt.plot(E_dip)" ] }, { "cell_type": "markdown", "id": "e9d917ba", "metadata": {}, "source": [ "## Animation of the annealing process\n", "\n", "Finally we animate the annealing process by plotting vertex magnetization at the end of each rotation.\n", "In the animation below we see the emergence of antiferromagnetic domains (white regions), which correspond to low energy states. The domains are separated by domain walls with a net positive magnetic moment." ] }, { "cell_type": "code", "execution_count": null, "id": "4f9e6319", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "\n", "def animate_spin(i):\n", " H = norm(h_ext[(i+1) * timesteps - 1])\n", " ax.set_title(f\"H={H:.3f} [T]\")\n", " model.set_spin(spins[i])\n", " model.plot_vertex_mag(ax=ax, replace=True)\n", " \n", "anim = FuncAnimation(fig, animate_spin, frames=len(spins), interval=200, blit=False)\n", "plt.close() # Only show the animation\n", "HTML(anim.to_jshtml())" ] } ], "metadata": { "jupytext": { "formats": "ipynb,md:myst", "text_representation": { "extension": ".md", "format_name": "myst", "format_version": 0.13, "jupytext_version": "1.11.5" } }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" }, "source_map": [ 15, 27, 36, 41, 45, 50, 66, 72, 90, 96, 103, 109, 111, 118 ] }, "nbformat": 4, "nbformat_minor": 5 }