{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "24a7f9a4", "metadata": { "tags": [ "remove-input" ] }, "outputs": [], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "from os.path import basename\n", "import numpy as np\n", "from scipy.signal import sawtooth\n", "\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "import matplotlib.colors\n", "from matplotlib.animation import FuncAnimation\n", "from IPython.display import HTML" ] }, { "cell_type": "markdown", "id": "1e280a20", "metadata": { "raw_mimetype": "text/markdown" }, "source": [ "# Superferromagnetism in pinwheel ASI\n", "\n", "Here we demonstrate how we used flatspin to replicate the reversal process of pinwheel ASI, as presented by {cite}`Li2019`.\n", "\n", "[“Superferromagnetism and Domain-Wall Topologies in Artificial ‘Pinwheel’ Spin Ice.” ACS Nano 13, no. 2 (January 15, 2019): 2213–22.](https://doi.org/10.1021/acsnano.8b08884)\n", "\n", "A key finding is that when pinwheel ASI is subject to an external field, the *angle* of the field controls the nature of the reversal process:\n", "\n", "1. When the angle is small (equally aligned to both sublattices), reversal happens in a single avalanche.\n", "2. When the angle is large (more aligned to one sublattice), reversal happens in a two-step process where one sublattice switches completely before the other.\n", "\n", "Previous attempts at capturing this behavior in a dipole model have proven unfruitful.\n", "\n", "The flatspin results are discussed in more detail in our paper {cite}`Flatspin2022`." ] }, { "cell_type": "markdown", "id": "bc9eb34c", "metadata": {}, "source": [ "## Small angles: 0° and -6°\n", "\n", "{numref}`li2019-theta0` and {numref}`li2019-theta6` show the hysteresis loop and snapshots of pinwheel ASI when the angle of the exterernal field is small ($\\theta=0^\\circ$ and $\\theta=-6^\\circ$).\n", "\n", "Key features:\n", "* Reversal starts at small number of nucleation points, typically close to the edge\n", "* Reversal progresses by domain growth through domain wall movement perpendicular to the direction of the field.\n", "* Reversal is somewhat more ordered for -6° as opposed to 0°." ] }, { "cell_type": "markdown", "id": "f9049467", "metadata": {}, "source": [ ":::{figure-md} li2019-theta0\n", "\n", "\n", "Figure 3 (b) from {cite}`Li2019`, copyright © American Chemical Society, [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/).\n", ":::" ] }, { "cell_type": "markdown", "id": "a12061f1", "metadata": {}, "source": [ ":::{figure-md} li2019-theta6\n", "\n", "\n", "Figure 3 (a) from {cite}`Li2019`, copyright © American Chemical Society, [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/).\n", ":::" ] }, { "cell_type": "markdown", "id": "cf80d988", "metadata": {}, "source": [ "## Large angles: 30°\n", "{numref}`li2019-theta30` shows the hysteresis loop and snapshots of pinwheel ASI when the angle of the exterernal field $\\theta$ is large.\n", "\n", "Key features:\n", "* Reversal process quite different because field now aligned with easy axis of one sublattice\n", "* Ratchetcing behavior yielding a stepped hysteresis loop\n", "* Magnetization constrained to move along 45 degree lines\n", "* Diagonal stripe patterns\n", "\n", ":::{figure-md} li2019-theta30\n", "\n", "\n", "Figure 4 (c) from {cite}`Li2019`, copyright © American Chemical Society, [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/).\n", ":::" ] }, { "cell_type": "markdown", "id": "e9b64df9", "metadata": {}, "source": [ "## Size and geometry\n", "{numref}`li2019-fig1` shows a TEM image of the ASI system, where we can deduce the size, shape and lattice spacing:\n", "\n", ":::{figure-md} li2019-fig1\n", "\n", "\n", "Figure 1 from {cite}`Li2019`, copyright © American Chemical Society, [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/).\n", ":::\n", "\n", "In addition, there are some more details from the text {cite:p}`Li2019`:\n", "\n", "> Here, we investigate the behavior of a diamond-\n", "> edge permalloy (Ni 80 Fe 20 ) pinwheel array of two interleaved\n", "> collinear 25 × 25 sublattices as shown in the in-focus TEM\n", "> image in the upper half of Figure 1.\n", ">\n", "> Each individual nanomagnetic island is\n", "> 10 nm thick, 470 nm long, and 170 nm wide, with a center-to-\n", "> center separation between nearest-neighbor islands of 420 nm,\n", "> as shown the top right inset to Figure 1.\n", "\n", "Note that their geometry differs from the default pinwheel geometry in flatspin:\n", "\n", "1. The angles of the first row is -45° (counter-clockwise), as opposed to 45° (clockwise).\n", "2. They use \"asymmetric pinwheel edges\"" ] }, { "cell_type": "markdown", "id": "4c7f9c2c", "metadata": {}, "source": [ "## flatspin parameters\n", "\n", "We determine simulation parameters based on details from the original paper, [mumax](https://mumax.github.io/) simulations for the astroid parameters, as well as some guesswork.\n", "\n", "### Geometry\n", "From {numref}`li2019-fig1` and the text:\n", "\n", "* Size: 25x25\n", "* Edges: asymmetric\n", "* Spin angle: -45° (counter-clockwise)\n", "* Lattice spacing: ~594 nm\n", " * From {numref}`li2019-fig1`, NN distance is 420 nm. That gives a corresponding flatspin lattice spacing of 2 * 420 * cos(45) ~= 594 nm.\n", "\n", "### External field\n", "* Field angle: -6°, 0° and 30°\n", " * From {numref}`li2019-fig1`, their zero angle is straight up, whereas in flatspin zero is towards the right.\n", " * Correspoinding flatspin angles are hence 96°, 90° and 60°, respectively.\n", " * In the following, we refer to flatspin angles using $\\phi$ (`phi`), i.e., $\\phi=90-\\theta$\n", "* Field strength: approx +/- 300 Oe = +/- 30 mT (from {numref}`li2019-theta30`)\n", "\n", "### Switching parameters\n", "Parameters for switching were estimated from [mumax](https://mumax.github.io/) simulations of a single 470x170x10 nm island, subject to an external field at different angles.\n", "\n", "* Coercive field $h_k$ = 0.098\n", "* $b$ = 0.28\n", "* $c$ = 1.0\n", "* $\\beta$ = 4.8\n", "* $\\gamma$ = 3.0" ] }, { "cell_type": "markdown", "id": "b7020222", "metadata": {}, "source": [ "We now calculate all the flatspin parameters, and instantiate the model." ] }, { "cell_type": "code", "execution_count": null, "id": "ad511c03", "metadata": {}, "outputs": [], "source": [ "from flatspin.model import *\n", "from flatspin.data import *\n", "from flatspin.plotting import plot_vectors, montage_fig\n", "from flatspin.vertices import find_vertices, vertex_pos, vertex_mag\n", "\n", "size = (25, 25)\n", "edge = \"asymmetric\"\n", "spin_angle = -45\n", "\n", "# Calculate alpha\n", "Msat = 860e3\n", "nw = 470e-9 # width\n", "nh = 170e-9 # height\n", "nt = 10e-9 # thickness\n", "a = 594e-9 # lattice spacing\n", "\n", "M = Msat*nw*nh*nt\n", "mu0 = 1.2566370614e-6\n", "alpha = mu0*M/(4*np.pi*a**3)\n", "alpha = np.round(alpha, 5)\n", "\n", "# From hysteresis-470x170x10-roundrect100-sweep-phi-li\n", "hc = 0.098\n", "sw_b = 0.28\n", "sw_beta = 4.8\n", "sw_gamma = 3.0\n", "\n", "# Guess disorder\n", "disorder = 0.05\n", "\n", "random_seed = 0xdeadbeef\n", "\n", "params = dict(\n", " size=size, edge=edge, spin_angle=spin_angle, lattice_spacing=1,\n", " hc=hc, alpha=alpha, disorder=disorder,\n", " switching='sw', sw_b=sw_b, sw_beta=sw_beta, sw_gamma=sw_gamma,\n", " random_seed=random_seed, use_opencl=1,\n", ")" ] }, { "cell_type": "markdown", "id": "eea26c4f", "metadata": {}, "source": [ "### Visual sanity check\n", "\n", "Let's quickly verify that the resulting flatspin geometry matches the paper ({numref}`li2019-fig1`)." ] }, { "cell_type": "code", "execution_count": null, "id": "f062f617", "metadata": {}, "outputs": [], "source": [ "model = PinwheelSpinIceDiamond(**params)\n", "model.plot()\n", "plt.axis('off');" ] }, { "cell_type": "markdown", "id": "741bb983", "metadata": {}, "source": [ "### Pinwheel units (vertices)\n", "In the paper they consider the magnetization of the \"pinwheel units\", similar to vertices from other geometries.\n", "\n", "Vertices in pinwheel are a bit different from square. Let's compare them." ] }, { "cell_type": "code", "execution_count": null, "id": "0741c805", "metadata": {}, "outputs": [], "source": [ "plt.subplot(121)\n", "plt.axis('off')\n", "prms = dict(params)\n", "del prms['spin_angle']\n", "prms.update(size=(4,4))\n", "sq = SquareSpinIceClosed(**prms)\n", "sq.randomize()\n", "sq.plot()\n", "sq.plot_vertices()\n", "\n", "plt.subplot(122)\n", "plt.axis('off')\n", "prms = dict(params)\n", "prms.update(size=(4,4))\n", "pw = PinwheelSpinIceDiamond(**prms)\n", "pw.randomize()\n", "pw.plot()\n", "pw.plot_vertices();" ] }, { "cell_type": "markdown", "id": "f206debd", "metadata": {}, "source": [ "## Visuals\n", "\n", "Here we attempt to replicate the visualization of the pinwheel units, as detailed in Figure S9 (d) from {cite}`Li2019`:\n", "\n", ":::{figure-md} li2019-figs9d\n", "\n", "\n", "Figure S9 (d) from {cite}`Li2019`, copyright © American Chemical Society, [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/).\n", ":::" ] }, { "cell_type": "code", "execution_count": null, "id": "395f6787", "metadata": {}, "outputs": [], "source": [ "# Color map for the magnetization arrows\n", "clist = list('ckkbbkkggkkrrkkc')\n", "clist = ['#00da00' if c == 'g' else c for c in clist]\n", "clist = ['#0800da' if c == 'b' else c for c in clist]\n", "clist = ['#ed0912' if c == 'r' else c for c in clist]\n", "clist = ['#00ccff' if c == 'c' else c for c in clist]\n", "\n", "cmap = matplotlib.colors.ListedColormap(clist)\n", "cmap(np.arange(0,1.01,0.125))[:,:3]\n", "rad = np.linspace(0,2*np.pi,360)\n", "gradient = np.vstack((rad, rad))\n", "plt.figure(figsize=(4, .5))\n", "plt.gca().get_yaxis().set_visible(False)\n", "plt.grid(False)\n", "plt.imshow(gradient, aspect='auto', cmap=cmap)\n", "plt.xticks(np.arange(0,360,45));" ] }, { "cell_type": "code", "execution_count": null, "id": "c5a10db2", "metadata": {}, "outputs": [], "source": [ "# Try the color map\n", "model.randomize()\n", "plt.subplot(121)\n", "plt.axis('off')\n", "model.plot()\n", "\n", "plt.subplot(122)\n", "plt.axis('off')\n", "model.plot_vertex_mag(cmap='li2019', headwidth=3, headlength=3, headaxislength=2.5);" ] }, { "cell_type": "code", "execution_count": null, "id": "49ae62a4", "metadata": {}, "outputs": [], "source": [ "# Plot vertex magnetization, with added markers for Type I and IV vertices (zero net magnetization)\n", "def plot_vertices(model, markersize=80, ax=None, **kwargs):\n", " # Find type I and IV vertices\n", " vertices = model.vertices()\n", " vertex_types = np.array([model.vertex_type(v) for v in vertices])\n", " type1, = np.nonzero(vertex_types == 1)\n", " type4, = np.nonzero(vertex_types == 4)\n", " spin4 = np.array([model.spin[vertices[v]] for v in type4])\n", " type4a = type4[[s[0] == 1 for s in spin4]]\n", " type4b = type4[[s[0] == -1 for s in spin4]]\n", "\n", " if ax is None:\n", " ax = plt.gca()\n", "\n", " # Plot vertex magnetization\n", " a1 = model.plot_vertex_mag(\n", " cmap='li2019', headwidth=3, headlength=3, headaxislength=2.5,\n", " ax=ax, **kwargs)\n", " \n", " # Add markers for Type I and IV vertices\n", " XY = vertex_pos(model.pos, vertices)\n", " a2 = ax.scatter(XY[type1, 0], XY[type1, 1],\n", " c='black', marker='x', s=markersize, lw=0.05*markersize)\n", " a3 = ax.scatter(XY[type4a, 0], XY[type4a, 1],\n", " c='red', marker='o', s=markersize, edgecolors='none')\n", " a4 = ax.scatter(XY[type4b, 0], XY[type4b, 1],\n", " c='blue', marker='o', s=markersize, edgecolors='none')\n", " return [a1, a2, a3, a4]" ] }, { "cell_type": "code", "execution_count": null, "id": "2f1988b3", "metadata": {}, "outputs": [], "source": [ "# Test plot_vertices (small system)\n", "plot_vertices(pw)\n", "\n", "# Full state in background\n", "pw.plot(alpha=0.2)\n", "plt.axis('off');" ] }, { "cell_type": "code", "execution_count": null, "id": "fbb9e6d2", "metadata": {}, "outputs": [], "source": [ "plot_vertices(model, 10)\n", "plt.axis('off');" ] }, { "cell_type": "markdown", "id": "bfd82cf2", "metadata": {}, "source": [ "## Reversal curves\n", "\n", "We are now ready to simulate the reversal process in flatspin. In the code below, we apply an external field at an angle `phi`. The amplitude of the field is gradually increased from -H to H, and then decreased from H to -H. For each field value, we record the state of the system when one or more spins flipped. In other words, we only record *changes* in state." ] }, { "cell_type": "code", "execution_count": null, "id": "e9d5be29", "metadata": {}, "outputs": [], "source": [ "def run_reversal(model, phi, H=0.03, timesteps=101):\n", " triangle = -sawtooth(np.linspace(0, 2*np.pi, timesteps), 0.5)\n", "\n", " phi_rad = np.deg2rad(phi)\n", " H = H * triangle\n", "\n", " h_ext = np.column_stack([H * np.cos(phi_rad), H * np.sin(phi_rad)])\n", " spin = []\n", " time = []\n", "\n", " model.polarize()\n", " for i, h_ext_i in enumerate(h_ext):\n", " model.set_h_ext(h_ext_i)\n", " steps = model.relax()\n", " if i == 0 or steps > 0:\n", " # Store only state when there were spin flips\n", " time.append(i)\n", " spin.append(model.spin.copy())\n", "\n", " result = {\n", " 'phi': phi,\n", " 'H': H,\n", " 'time': time,\n", " 'spin': spin,\n", " }\n", " \n", " return result" ] }, { "cell_type": "code", "execution_count": null, "id": "44bb89c4", "metadata": {}, "outputs": [], "source": [ "# Run reversal curves for phi=60 (corresponds to theta=30 in the paper)\n", "result60 = run_reversal(model, 60)" ] }, { "cell_type": "markdown", "id": "af3d80b4", "metadata": {}, "source": [ "### Reversal animation\n", "Animate the state of the system during the different stages of reversal.\n", "Note that we only show frames where there have been spin flips." ] }, { "cell_type": "code", "execution_count": null, "id": "ec8d06f8", "metadata": {}, "outputs": [], "source": [ "def animate_reversal(model, phi, H, time, spin):\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", " t = time[i]\n", " model.set_spin(spin[i])\n", " ax.cla()\n", " ax.set_axis_off()\n", " ax.set_title(rf\"H={H[t]:g} $\\theta={90-phi}\\degree$\")\n", " plot_vertices(model, markersize=10, ax=ax)\n", "\n", " anim = FuncAnimation(\n", " fig, animate, init_func=lambda: None,\n", " frames=len(time), interval=100, blit=False\n", " )\n", " plt.close() # Only show the animation\n", " return HTML(anim.to_jshtml())" ] }, { "cell_type": "markdown", "id": "466a49a8", "metadata": {}, "source": [ "### Animation: 30°\n", "\n", "The animation below shows the full reversal for $\\theta=30^\\circ$. Compare with {numref}`li2019-theta30`." ] }, { "cell_type": "code", "execution_count": null, "id": "66e386c0", "metadata": {}, "outputs": [], "source": [ "animate_reversal(model, **result60)" ] }, { "cell_type": "markdown", "id": "f8ec5ead", "metadata": {}, "source": [ "### Hysteresis loops\n", "\n", "Plot H vs M curves. Since we only store changes to state, we need to fill in the gaps where there were no spin flips." ] }, { "cell_type": "code", "execution_count": null, "id": "2768ec7d", "metadata": {}, "outputs": [], "source": [ "def plot_hysteresis(model, phi, H, time, spin):\n", " phi_rad = np.deg2rad(phi)\n", " direction = [np.cos(phi_rad), np.sin(phi_rad)]\n", " \n", " # Calculate average magnetization along H\n", " M = []\n", " M_H = []\n", " for s in spin:\n", " model.set_spin(s)\n", " mag = model.vectors.sum(axis=0)\n", " mag_h = mag.dot(direction)\n", " M.append(mag)\n", " M_H.append(mag_h)\n", "\n", " M = np.array(M)\n", " M_H = np.array(M_H)\n", " M_H /= M_H.max()\n", " H *= 1000 # mT\n", " \n", " # Fill in the gaps\n", " df = pd.DataFrame({'M_H': M_H}, index=time)\n", " df = df.reindex(index=np.arange(len(H))).ffill()\n", " df['H'] = H\n", " \n", " plt.plot(df['H'], df['M_H'], '.-')\n", " plt.grid(True)\n", " plt.title(rf\"$\\theta={90-phi}\\degree$\")\n", " plt.xlabel(\"H (mT)\")\n", " plt.ylabel(r\"$M_{\\parallel H}$\")" ] }, { "cell_type": "markdown", "id": "f4d5b2cf", "metadata": {}, "source": [ "### Hysteresis: 30°\n", "\n", "The plot below shows the hysteresis curve for $\\theta=30^\\circ$. Compare with {numref}`li2019-theta30`." ] }, { "cell_type": "code", "execution_count": null, "id": "46108468", "metadata": {}, "outputs": [], "source": [ "plot_hysteresis(model, **result60)" ] }, { "cell_type": "markdown", "id": "2b79b157", "metadata": {}, "source": [ "### Small angles: -6° and 0°\n", "\n", "The results above match very well with {numref}`li2019-theta30`. Let's try the smaller angles from {numref}`li2019-theta0` and {numref}`li2019-theta6`.\n", "\n", "We increase our resolution (timesteps) a bit, since reversal happens much faster at low angles." ] }, { "cell_type": "code", "execution_count": null, "id": "54605abb", "metadata": {}, "outputs": [], "source": [ "result96 = run_reversal(model, 96, timesteps=501)\n", "result90 = run_reversal(model, 90, timesteps=501)" ] }, { "cell_type": "markdown", "id": "3a7e764b", "metadata": {}, "source": [ "Below we produce the reversal animations and hysteresis curves for the two small angles. Compare with {numref}`li2019-theta0` and {numref}`li2019-theta6`." ] }, { "cell_type": "code", "execution_count": null, "id": "2ea3dc75", "metadata": {}, "outputs": [], "source": [ "animate_reversal(model, **result90)" ] }, { "cell_type": "code", "execution_count": null, "id": "fb7a350e", "metadata": {}, "outputs": [], "source": [ "plot_hysteresis(model, **result90)" ] }, { "cell_type": "code", "execution_count": null, "id": "e6df4008", "metadata": {}, "outputs": [], "source": [ "animate_reversal(model, **result96)" ] }, { "cell_type": "code", "execution_count": null, "id": "02ddbf7a", "metadata": {}, "outputs": [], "source": [ "plot_hysteresis(model, **result96)" ] }, { "cell_type": "markdown", "id": "8f48f745", "metadata": {}, "source": [ "## Budrikis switching\n", "\n", "In {cite}`Li2019` they also attempt to reproduce the results using a dipole model:\n", "\n", "> However, it is worth noting that despite extensive efforts to replicate the precise details of the magnetization, this model does not adequately capture the richness of the behavior discussed in the following sections.\n", "\n", "A crucial difference between their dipole model and flatspin, is that their model employ a much simpler switching criteria, only considering the component of the field parallel to the easy axis of each magnet.\n", "\n", "In flatspin, this simpler switching criteria is available as \"Budrikis switching\", named after {cite}`Budrikis2014`.\n", "Let's examine how Budrikis switching affects the reversal process.\n", "We start with $\\theta=0^\\circ$." ] }, { "cell_type": "code", "execution_count": null, "id": "c575ce1c", "metadata": {}, "outputs": [], "source": [ "prms = dict(params)\n", "prms['switching'] = \"budrikis\"\n", "prms['hc'] = 0.013 # match hysteresis @ 90 degrees\n", "model_budrikis = PinwheelSpinIceDiamond(**prms)\n", "result90_budrikis = run_reversal(model_budrikis, 90, -0.03, 501)" ] }, { "cell_type": "code", "execution_count": null, "id": "cc0896b0", "metadata": {}, "outputs": [], "source": [ "animate_reversal(model_budrikis, **result90_budrikis)" ] }, { "cell_type": "code", "execution_count": null, "id": "a2e0a7ce", "metadata": {}, "outputs": [], "source": [ "plot_hysteresis(model_budrikis, **result90_budrikis)" ] }, { "cell_type": "markdown", "id": "cd54cb61", "metadata": {}, "source": [ "Although we are able to get a good match for the hysteresis loop (compare with {numref}`li2019-theta0`), the animation reveals a very different reversal process, with apparent domains in all directions (not just up/down).\n", "\n", "Next, we try $\\theta=-6^\\circ$." ] }, { "cell_type": "code", "execution_count": null, "id": "648bc0a3", "metadata": {}, "outputs": [], "source": [ "result96_budrikis = run_reversal(model_budrikis, 96, -0.03, 501)" ] }, { "cell_type": "code", "execution_count": null, "id": "bcdeb3ac", "metadata": {}, "outputs": [], "source": [ "animate_reversal(model_budrikis, **result96_budrikis)" ] }, { "cell_type": "code", "execution_count": null, "id": "06f5c23d", "metadata": {}, "outputs": [], "source": [ "plot_hysteresis(model_budrikis, **result96_budrikis)" ] }, { "cell_type": "markdown", "id": "7fda1e6a", "metadata": {}, "source": [ "At $\\theta=-6^\\circ$ we see the hysteresis loop has changed significantly. We now have a two-dimensional avalanche, instead of one-dimensional (compare with {numref}`li2019-theta6`).\n", "\n", "Finally, try $\\theta=30^\\circ$." ] }, { "cell_type": "code", "execution_count": null, "id": "b115a895", "metadata": {}, "outputs": [], "source": [ "result60_budrikis = run_reversal(model_budrikis, 60, -0.06, 501)" ] }, { "cell_type": "code", "execution_count": null, "id": "0c8da544", "metadata": {}, "outputs": [], "source": [ "animate_reversal(model_budrikis, **result60_budrikis)" ] }, { "cell_type": "code", "execution_count": null, "id": "8b005725", "metadata": {}, "outputs": [], "source": [ "plot_hysteresis(model_budrikis, **result60_budrikis)" ] }, { "cell_type": "markdown", "id": "74125816", "metadata": {}, "source": [ "At $\\theta=30^\\circ$, we have to *double* the strength of the external field to obtain full reversal. Although the animation is similar, the hysteresis loop is very different from {numref}`li2019-theta30`." ] }, { "cell_type": "markdown", "id": "7031bd5d", "metadata": {}, "source": [ "## Reproduce the figures\n", "\n", "Finally, we reproduce the figures from the paper more exactly. Instead of running the simulations in this notebook, we use the command-line tools of flatspin to run the experiments. This has the advantage of storing the results in as a [dataset](dataset), should we wish to perform more analysis later." ] }, { "cell_type": "code", "execution_count": null, "id": "aad02c50", "metadata": {}, "outputs": [], "source": [ "# Read the dataset\n", "dataset = Dataset.read('/data/flatspin/pw-li-sweep-phi-new4')\n", "\n", "# Print the command used to generate the results\n", "# You can run this command yourself to reproduce the dataset\n", "cmd = dataset.info['command'].split(\" \")\n", "cmd[0] = basename(cmd[0])\n", "cmd[-1] = basename(cmd[-1])\n", "print(\" \".join(cmd))" ] }, { "cell_type": "code", "execution_count": null, "id": "15d8c53e", "metadata": {}, "outputs": [], "source": [ "# For inclusion in the Flatspin2022 paper, we enabled latex for the plots\n", "# Use latex\n", "#plt.rcParams.update({\n", "# \"text.usetex\": True,\n", "# \"text.latex.preamble\": r\"\\usepackage{gensymb}\",\n", "#})" ] }, { "cell_type": "code", "execution_count": null, "id": "8ba6ec38", "metadata": {}, "outputs": [], "source": [ "# Function for plotting hysteresis from the dataset\n", "def plot_data_hysteresis(dataset, **kwargs):\n", " df = read_table(dataset.tablefile('mag'), index_col='t')\n", " UV = np.array(df)\n", " UV = UV.reshape((UV.shape[0], -1, 2))\n", " \n", " df = read_table(dataset.tablefile('h_ext'), index_col='t')\n", " h_ext = np.array(df)\n", " h_ext = h_ext.reshape((-1, 2))\n", " \n", " pos, angle = read_geometry(dataset.tablefile('geometry'))\n", "\n", " phi = dataset.row(0)['phi']\n", " theta = np.deg2rad(phi)\n", " direction = [np.cos(theta), np.sin(theta)]\n", " \n", " H = h_ext.dot(direction)\n", " M = UV.sum(axis=1)\n", " M_H = M.dot(direction)\n", " M_H /= M_H.max()\n", " \n", " plt.grid(True)\n", " plt.title(rf'$\\theta={90-phi}\\degree$')\n", " plt.plot(H * 10000, M_H, '.-', **kwargs)\n", " plt.xlabel(\"Magnetic Field (Oe)\")\n", " plt.ylabel(r\"$M / M_{\\mathrm{S}}$\")" ] }, { "cell_type": "code", "execution_count": null, "id": "f72c1ff5", "metadata": {}, "outputs": [], "source": [ "# Function for plotting the vertex magnetization from the dataset\n", "def plot_data_vertices(dataset, t=[0], markersize=1):\n", " df = read_table(dataset.tablefile('mag'), index_col='t')\n", " UV = np.array(df)\n", " UV = UV.reshape((UV.shape[0], -1, 2))\n", " UV = UV[t]\n", " \n", " df = read_table(dataset.tablefile('spin'), index_col='t')\n", " spin = np.array(df)\n", " spin = spin[t]\n", " \n", " pos, angle = read_geometry(dataset.tablefile('geometry'))\n", "\n", " phi = dataset.row(0)['phi']\n", " theta = np.deg2rad(phi)\n", " direction = [np.cos(theta), np.sin(theta)]\n", " \n", " # Map the vectors on the native grid\n", " grid = Grid(pos)\n", "\n", " vi, vj, vertices = find_vertices(grid, pos, angle, (3, 3))\n", "\n", " XY = pos\n", " XY = vertex_pos(pos, vertices)\n", " UV = np.array([vertex_mag(UVi, vertices) for UVi in UV])\n", " UV /= 2\n", " \n", " for UVi, spini in zip(UV, spin):\n", " plt.axis('off')\n", " plot_vectors(XY, UVi, cmap='li2019', headwidth=4, headlength=4, headaxislength=3.5)\n", " \n", " vertex_types = np.array([vertex_type(spini[v], pos[v], angle[v]) for v in vertices])\n", " type1, = np.nonzero(vertex_types == 1)\n", " type4, = np.nonzero(vertex_types == 4)\n", " spin4 = [spini[vertices[v]] for v in type4]\n", " type4a = type4[[s[0] == 1 for s in spin4]]\n", " type4b = type4[[s[0] == -1 for s in spin4]]\n", " \n", " plt.scatter(XY[type1, 0], XY[type1, 1], c='black', marker='x', s=markersize, lw=0.2*markersize)\n", " plt.scatter(XY[type4a, 0], XY[type4a, 1], c='red', marker='o', s=markersize, edgecolors='none')\n", " plt.scatter(XY[type4b, 0], XY[type4b, 1], c='blue', marker='o', s=markersize, edgecolors='none')" ] }, { "cell_type": "code", "execution_count": null, "id": "09319ed6", "metadata": {}, "outputs": [], "source": [ "# Make a figure similar to Li2019\n", "figsize=(3,3)\n", "def make_fig():\n", " fig = plt.figure(figsize=figsize)\n", " gs = fig.add_gridspec(3, 3, hspace=0, wspace=0, top=1, bottom=0, left=0, right=1)\n", " # ugh...\n", " sgs = gs[0:2, 0:2].subgridspec(3, 3, height_ratios=[5-2.5, 80, 20-2.5], width_ratios=[25-2.5, 70+5, 5-2.5])\n", " ax1 = fig.add_subplot(sgs[1,1])\n", " #ax1 = fig.add_subplot(gs[0:2,0:2])\n", " ax1.set_xlabel('xlab')\n", " ax1.set_ylabel('ylab')\n", " ax1.set_title('title')\n", " \n", " ax2 = fig.add_subplot(gs[0, 2])\n", " ax3 = fig.add_subplot(gs[1, 2])\n", " ax4 = fig.add_subplot(gs[2, 2])\n", " ax5 = fig.add_subplot(gs[2, 1])\n", " ax6 = fig.add_subplot(gs[2, 0])\n", " \n", " return ax1, (ax2, ax3, ax4, ax5, ax6)\n", "\n", "make_fig();" ] }, { "cell_type": "code", "execution_count": null, "id": "6d6d4f1b", "metadata": {}, "outputs": [], "source": [ "def plot_data_hyst_verts(dataset, hyst_color, times):\n", " hyst_ax, vert_axes = make_fig()\n", " \n", " plt.sca(hyst_ax)\n", " plot_data_hysteresis(dataset, color=hyst_color)\n", " hyst_ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(200))\n", " hyst_ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(1))\n", "\n", " for t, ax in zip(times, vert_axes):\n", " plt.sca(ax)\n", " #plt.title(str(t))\n", " plot_data_vertices(dataset, [t])" ] }, { "cell_type": "code", "execution_count": null, "id": "132c30df", "metadata": {}, "outputs": [], "source": [ "phi=90\n", "ds = dataset.filter(phi=phi)\n", "times = [1418, 1446, 1453, 1471, 1493]\n", "plot_data_hyst_verts(ds, '#000080', times)" ] }, { "cell_type": "code", "execution_count": null, "id": "225bd73b", "metadata": {}, "outputs": [], "source": [ "phi=96\n", "ds = dataset.filter(phi=phi)\n", "times = [1449, 1470, 1475, 1482, 1558]\n", "plot_data_hyst_verts(ds, '#7F0000', times)" ] }, { "cell_type": "code", "execution_count": null, "id": "388787ea", "metadata": {}, "outputs": [], "source": [ "phi=60\n", "ds = dataset.filter(phi=phi)\n", "times = [1594, 1649, 1806, 2299, 2470]\n", "plot_data_hyst_verts(ds, '#FF8000', times)" ] } ], "metadata": { "jupytext": { "formats": "ipynb,md:myst", "text_representation": { "extension": ".md", "format_name": "myst", "format_version": 0.13, "jupytext_version": "1.14.1" } }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "source_map": [ 15, 30, 47, 58, 66, 74, 91, 119, 150, 154, 193, 199, 203, 210, 229, 241, 260, 272, 303, 312, 315, 321, 351, 354, 360, 379, 385, 387, 393, 423, 429, 431, 439, 442, 446, 450, 454, 458, 460, 474, 482, 486, 488, 494, 498, 502, 504, 510, 514, 518, 520, 524, 530, 542, 551, 580, 624, 649, 664, 671, 678 ] }, "nbformat": 4, "nbformat_minor": 5 }