{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "a2f40966", "metadata": { "tags": [ "remove-input" ] }, "outputs": [], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "\n", "import numpy as np\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": "6460c6a5", "metadata": {}, "source": [ "# Neighbor distance\n", "\n", "The dipole field felt by each spin is calculated by summing the dipole field contributions from spins in its neighborhood.\n", "The `neighbor_distance` parameter sets the size of the neighborhood to consider when calculating dipole interactions.\n", "Specifically, all spins within a distance of `lattice_spacing * neighbor_distance` are considered neighbors of a spin.\n", "Although it is possible to use `neighbor_distance=np.inf` for a global neighborhood, this is computationally expensive and (usually) unnecessary, because spins far away have neglible contributions to the total dipole field.\n", "\n", "The required `neighbor_distance` depends on the system under study, i.e., the specific geometry. \n", "Care must be taken to include enough spins in the neighborhood such that the observed behavior converges, especially when considering systems exhibiting long-range effects.\n", "\n", "In this example, we compare the effect of `neighbor_distance` on two different systems:\n", "* Square spin ice, where local interactions are dominating\n", "* Pinwheel spin ice, where long-range interactions are significant\n", "\n", "Here we set the dipolar coupling parameter `alpha` artificially high to relax each system into a low energy state.\n", "Then we investigate how the resulting spin states and vertex populations change as we vary `neighbor_distance`." ] }, { "cell_type": "code", "execution_count": null, "id": "dfa2b582", "metadata": {}, "outputs": [], "source": [ "params = {\n", " 'size': (25,25),\n", " 'init': 'random',\n", " 'random_seed': 42,\n", " 'alpha': 1.0,\n", " 'use_opencl': 1,\n", " 'disorder': 0.04,\n", "}" ] }, { "cell_type": "code", "execution_count": null, "id": "06974c7c", "metadata": {}, "outputs": [], "source": [ "from flatspin.model import SquareSpinIceClosed, PinwheelSpinIceDiamond\n", "from flatspin.plotting import montage_fig\n", "\n", "# Preview the two geometries\n", "square = SquareSpinIceClosed(**params)\n", "pinwheel = PinwheelSpinIceDiamond(**params)\n", "\n", "plt.figure(figsize=(6, 4))\n", "plt.subplot(121)\n", "plt.axis('off')\n", "plt.title(\"Square\")\n", "square.plot()\n", "\n", "plt.subplot(122)\n", "plt.axis('off')\n", "plt.title(\"Pinwheel\")\n", "pinwheel.plot()\n", "\n", "plt.tight_layout();" ] }, { "cell_type": "markdown", "id": "9947222e", "metadata": {}, "source": [ "## Relaxation with high alpha\n", "\n", "Below we plot the vertex magnetization of the initial random state, and after calling `relax()`." ] }, { "cell_type": "code", "execution_count": null, "id": "f3645b74", "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(6, 6))\n", "\n", "plt.subplot(221)\n", "plt.axis('off')\n", "plt.title(\"Square: init\")\n", "square.plot_vertex_mag()\n", "\n", "plt.subplot(222)\n", "plt.axis('off')\n", "plt.title(\"Pinwheel: init\")\n", "pinwheel.plot_vertex_mag()\n", "\n", "square.relax()\n", "plt.subplot(223)\n", "plt.axis('off')\n", "plt.title(\"Square: relaxed\")\n", "square.plot_vertex_mag()\n", "\n", "pinwheel.relax()\n", "plt.subplot(224)\n", "plt.axis('off')\n", "plt.title(\"Pinwheel: relaxed\")\n", "pinwheel.plot_vertex_mag()\n", "\n", "plt.tight_layout();" ] }, { "cell_type": "markdown", "id": "d64e0aba", "metadata": {}, "source": [ "## Effect of `neighbor_distance`\n", "\n", "The function below performs the above relaxation for a range of `neighbor_distance`, and records the resulting spin state and vertex population.\n", "Note that the initial state of the system is the same, since we use the same `random_seed`." ] }, { "cell_type": "code", "execution_count": null, "id": "eb8476ba", "metadata": {}, "outputs": [], "source": [ "def run_neighbor_distance(model_class, max_neighbor_dist=20, **params):\n", " # Run relax() for different values of neighbor_distance\n", " spins = []\n", " vpops = []\n", " neighbor_dists = np.arange(1, max_neighbor_dist+1)\n", "\n", " for nd in neighbor_dists:\n", " # neighbor_distance must be set at model initialization time\n", " si = model_class(neighbor_distance=nd, **params)\n", " si.relax()\n", "\n", " spins.append(si.spin.copy())\n", "\n", " types, counts = si.vertex_population()\n", " vpop = [0, 0, 0, 0]\n", " for vt, c in zip(types, counts):\n", " vpop[vt-1] = c\n", " vpops.append(vpop)\n", "\n", " result = {\n", " 'spins': spins,\n", " 'vertex_populations': vpops,\n", " 'neighbor_dists': neighbor_dists,\n", " }\n", "\n", " return result" ] }, { "cell_type": "markdown", "id": "cec871ea", "metadata": {}, "source": [ "Below we define some functions for visualizing the results:\n", "1. Animation of how the relaxed spin states change as `neighbor_distance` is varied\n", "2. Plot vertex population as a function of `neighbor_distance`" ] }, { "cell_type": "code", "execution_count": null, "id": "f5ebd2ce", "metadata": {}, "outputs": [], "source": [ "def animate_spins(model, spins, neighbor_dists, **kwargs):\n", " # Animate list of spin states\n", " fig, ax = plt.subplots()\n", " fig.subplots_adjust(left=0, right=1, bottom=0.0, top=.9, wspace=0, hspace=0)\n", "\n", " def animate(i):\n", " model.set_spin(spins[i])\n", " ax.cla()\n", " ax.set_axis_off()\n", " ax.set_title(f\"neighbor_distance={neighbor_dists[i]}\")\n", " model.plot_vertex_mag(ax=ax)\n", "\n", " anim = FuncAnimation(\n", " fig, animate, init_func=lambda: None,\n", " frames=len(spins), interval=500, blit=False\n", " )\n", " plt.close() # Only show the animation\n", " return HTML(anim.to_jshtml())\n", "\n", "def plot_vertex_populations(spins, neighbor_dists, vertex_populations, **kwargs):\n", " # Plot vertex populations as function if neighbor_distance\n", " vpops = np.array(vertex_populations)\n", " for t in range(4):\n", " plt.plot(neighbor_dists, vpops[:,t], label=f\"Type {t+1}\")\n", " plt.xlabel(\"Neighbor distance\")\n", " plt.ylabel(\"Vertex population\")\n", " plt.legend();" ] }, { "cell_type": "markdown", "id": "5b13564d", "metadata": {}, "source": [ "## Results: square spin ice" ] }, { "cell_type": "code", "execution_count": null, "id": "074492b1", "metadata": {}, "outputs": [], "source": [ "result_square = run_neighbor_distance(SquareSpinIceClosed, max_neighbor_dist=30, **params)" ] }, { "cell_type": "markdown", "id": "ee7f881b", "metadata": {}, "source": [ "In the animation below, we see visually how the relaxed spin state changes as we vary `neighbor_distance`.\n", "Observe how the state does indeed change over a significant range of `neighbor_distance`.\n", "However, qualitatively the states are fairly similar, e.g., compare the size of the emergent antiferromagnetic domains (white regions)." ] }, { "cell_type": "code", "execution_count": null, "id": "7d32340b", "metadata": {}, "outputs": [], "source": [ "animate_spins(square, **result_square)" ] }, { "cell_type": "markdown", "id": "01817f42", "metadata": {}, "source": [ "Although the final state of the system is clearly sensitive to `neighbor_distance`, the plot below shows how the statistical measure of vertex populations change over `neighbor_distance`.\n", "As can be seen, the vertex populations do not vary significantly beyond `neighbor_distance=4`.\n", "This is because square spin ice is dominated by local interactions." ] }, { "cell_type": "code", "execution_count": null, "id": "6f3021b3", "metadata": {}, "outputs": [], "source": [ "plot_vertex_populations(**result_square)" ] }, { "cell_type": "markdown", "id": "5ee897bb", "metadata": {}, "source": [ "## Results: pinwheel spin ice" ] }, { "cell_type": "code", "execution_count": null, "id": "2a6a6760", "metadata": {}, "outputs": [], "source": [ "result_pinwheel = run_neighbor_distance(PinwheelSpinIceDiamond, max_neighbor_dist=30, **params)" ] }, { "cell_type": "markdown", "id": "1047e481", "metadata": {}, "source": [ "The animation below shows the final state of pinwheel spin ice for different values `neighbor_distance`.\n", "Again, the final state is indeed different across a significant range of `neighbor_distance`.\n", "However, there seems to be more differences up until approximately `neighbor_distance=10`, after which the states are more qualitatively similar." ] }, { "cell_type": "code", "execution_count": null, "id": "f3fea3fe", "metadata": {}, "outputs": [], "source": [ "animate_spins(pinwheel, **result_pinwheel)" ] }, { "cell_type": "markdown", "id": "1c69b638", "metadata": {}, "source": [ "Again, plotting the vertex populations for pinwheel spin ice, we see that they do not change significantly beyond `neighbor_distance=10`.\n", "Because pinwheel spin ice exhibits significant long-range interactions, a larger `neighbor_distance` is needed for convergence, compared to square spin ice." ] }, { "cell_type": "code", "execution_count": null, "id": "58d6a46a", "metadata": {}, "outputs": [], "source": [ "plot_vertex_populations(**result_pinwheel)" ] } ], "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, 25, 44, 55, 75, 81, 107, 114, 141, 147, 175, 179, 181, 187, 189, 195, 197, 201, 203, 209, 211, 216 ] }, "nbformat": 4, "nbformat_minor": 5 }