{ "cells": [ { "cell_type": "markdown", "id": "f78a18f3-1c85-4f50-b795-4473cea83c97", "metadata": {}, "source": [ "# Getting Started\n", "\n", "As a first example the [standard problem #4](https://www.ctcms.nist.gov/~rdm/std4/spec4.html), proposed by the MuMag group is computed with NeuralMag. This problem describes the switching of a thin strip of permalloy under the influence of an external field.\n", "\n", "Since NeuralMag is a Python library, every simulation script is a Python script. In order to use NeuralMag, the Python package ``neuralmag`` needs to by imported in a first step." ] }, { "cell_type": "code", "execution_count": null, "id": "d7ebdf73-2af3-4587-a6f5-747c261b1469", "metadata": {}, "outputs": [], "source": [ "import neuralmag as nm" ] }, { "cell_type": "markdown", "id": "a3ac84ba-bcf5-4037-91f9-c2d7e21242cf", "metadata": {}, "source": [ "In the next step, a mesh for the simulation is created. As for the standard finite-difference method, the nodal finite-difference method requires a regular cuboid grid which can be defined by the number of cells and the cell size in the principal directions of the coordinate system.\n", "For the standard problem #4 we define a mesh with $100 \\times 24 \\times 1$ cells with cell size $5 \\times 5 \\times 3 \\text{nm}^3$." ] }, { "cell_type": "code", "execution_count": null, "id": "a3bcdc03-6f37-4340-845e-bc85245cfd49", "metadata": {}, "outputs": [], "source": [ "mesh = nm.Mesh((100, 25, 1), (5e-9, 5e-9, 3e-9))" ] }, { "cell_type": "markdown", "id": "bc3039ab-d28d-4d78-80b3-129ddde67ca2", "metadata": {}, "source": [ "Next, we define a `State` object which manages the state of simulation such as material parameters and the current time and magnetization." ] }, { "cell_type": "code", "execution_count": null, "id": "b5ee8e5d-a66d-440f-952d-eaee8e27704b", "metadata": {}, "outputs": [], "source": [ "state = nm.State(mesh)" ] }, { "cell_type": "markdown", "id": "45ce195a-6555-4ad8-ab5a-fcd3d6430b8a", "metadata": {}, "source": [ "Since the standard problem #4 defines a homogeneous material throughout the cuboid sample, the material parameters can be set as simple Python float values" ] }, { "cell_type": "code", "execution_count": 1, "id": "7286c654-5de6-43f2-9b27-444370b2f1bc", "metadata": {}, "outputs": [], "source": [ "state.material.Ms = 8e5\n", "state.material.A = 1.3e-11\n", "state.material.alpha = 0.02" ] }, { "cell_type": "markdown", "id": "84b37201-7d53-49ac-8c6f-ca23b0505c78", "metadata": {}, "source": [ "The initial magnetization configuration is set homogeneously in (1,1,0) direction in order to ensure relaxation into the desired S-state defined in the MuMag problem.\n", "As opposed to the material parameters, the magnetization is initialied as a `VectorFunction` object which represents a vector field defined on the nodes (vertices) of the cuboid mesh.\n", "We use the `fill` method of the function to object to set the initial magnetization direction on each node." ] }, { "cell_type": "code", "execution_count": null, "id": "2ed402db-3026-4e1f-8c69-b55d1b58d3c9", "metadata": {}, "outputs": [], "source": [ "state.m = nm.VectorFunction(state).fill((0.5**0.5, 0.5**0.5, 0))" ] }, { "cell_type": "markdown", "id": "176297e0-f5d1-4c9c-9074-4ad2de41f703", "metadata": {}, "source": [ "Next, we set up the individual effective-field contributions for the simulation, namely the exchange field, the demagnetization field and the external field.\n", "Like the magnetization, the external field is represented as vector function discretized on the nodes of the mesh.\n", "By calling the `register` method on the individual field objects, the state object is extended by routines for the computation of the respective effective field and energy.\n", "In order to compute the commulative effective field including all individual contributions, we initial a `TotalField` object that adds up all contributions and registers routines for the computation of the total effective field with the state object.\n", "Since the first step in the problem definition of the standard problem #4 requires the relaxation of the magnetization at zero external field, we do not include the external field in the total field for now." ] }, { "cell_type": "code", "execution_count": null, "id": "aa896e5b-c45f-49d5-9fd8-ab2b816f994c", "metadata": {}, "outputs": [], "source": [ "h_ext = nm.VectorFunction(state).fill([-19576.0, 3421.0, 0.0], expand=True)\n", "\n", "nm.ExchangeField().register(state, \"exchange\")\n", "nm.DemagField().register(state, \"demag\")\n", "nm.ExternalField(h_ext).register(state, \"external\")\n", "\n", "nm.TotalField(\"exchange\", \"demag\").register(state)" ] }, { "cell_type": "markdown", "id": "78412041-e3eb-484c-8150-24229e0708b0", "metadata": {}, "source": [ "Having all material parameters and field contributions in place, we can now proceed to relax the magnetic system into the initial S-state.\n", "We do this, by calling the relax routine of the `LLGSolvers` object." ] }, { "cell_type": "code", "execution_count": null, "id": "97781c9c-395d-45e8-a1c5-76cb6afe8784", "metadata": {}, "outputs": [], "source": [ "llg = nm.LLGSolver(state)\n", "llg.relax()" ] }, { "cell_type": "markdown", "id": "06e0c5fa-b2c2-4021-bdd4-a092308f75ed", "metadata": {}, "source": [ "The `LLGSolver` object takes all required information for the time integration from the `State` object.\n", "Namely, it calls the `h` method on state that has been registered by the `TotalField` class in order to evaluate the effective field.\n", "After the relaxation to the S-state, we can check the resulting magnetization configuration by write it to a VTI-file.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "e9adf186-7076-4489-aa25-d618e4fc6d13", "metadata": {}, "outputs": [], "source": [ "state.write_vti(\"m\", \"sstate.vti\")" ] }, { "cell_type": "markdown", "id": "3e724621-8164-4182-9e29-29ba13433d20", "metadata": {}, "source": [ "This file can be visualized and analyzed with the open source tool [Paraview](https://www.paraview.org/).\n", "As required by the standard problem #4, in the next step the dynamical switching of the magnetization under the influence of an external field directed slightly tilted to the -x direction is computed.\n", "In oder to simulate this switching process with NeuralMag, we register a new `TotalField` object that includes the external field.\n", "After doing so, we need to reset the `LLGSolver` object in order to account for this change." ] }, { "cell_type": "code", "execution_count": null, "id": "f914201f-2505-4ddd-b817-9fda46ec70d3", "metadata": {}, "outputs": [], "source": [ "nm.TotalField(\"exchange\", \"demag\", \"external\").register(state)\n", "llg.reset()" ] }, { "cell_type": "markdown", "id": "4e6ae572-7231-4831-ac5e-6113552e91b0", "metadata": {}, "source": [ "In a next step we initialize a `Logger` object, that is configured to log the time and the averaged magnetization in a simple CSV file as well as the full magnetization configuration in a series of VTI files in a directory called `data`.\n", "Afterwards, the actual time integration is performed in a loop that calls the `step` function on the LLG solver and the `log` function of the `Logger` object in an alternateing fashion.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "cf2af3d7-5ee4-4b2c-a6dd-37a271c0240f", "metadata": {}, "outputs": [], "source": [ "logger = nm.Logger(\"data\", [\"t\", \"m\"], [\"m\"])\n", "while state.t < 1e-9:\n", " logger.log(state)\n", " llg.step(1e-11)" ] }, { "cell_type": "markdown", "id": "ea2a684f-525b-499a-b1c8-68a6b16d4641", "metadata": {}, "source": [ "---\n", "The complete script reads\n", "```python\n", "import neuralmag as nm\n", "\n", "# setup mesh and state\n", "mesh = nm.Mesh((100, 25, 1), (5e-9, 5e-9, 3e-9))\n", "state = nm.State(mesh)\n", "\n", "# setup material and m0\n", "state.material.Ms = 8e5\n", "state.material.A = 1.3e-11\n", "state.material.alpha = 0.02\n", "\n", "# initialize nodal vector functions for magneization and external field\n", "state.m = nm.VectorFunction(state).fill((0.5**0.5, 0.5**0.5, 0))\n", "h_ext = nm.VectorFunction(state).fill([-19576.0, 3421.0, 0.0], expand=True)\n", "\n", "# register effective field contributions\n", "nm.ExchangeField().register(state, \"exchange\")\n", "nm.DemagField().register(state, \"demag\")\n", "nm.ExternalField(h_ext).register(state, \"external\")\n", "nm.TotalField(\"exchange\", \"demag\").register(state)\n", "\n", "# relax to s-state\n", "llg = nm.LLGSolver(state)\n", "llg.relax()\n", "\n", "state.write_vti(\"m\", \"sstate.vti\")\n", "\n", "# add external field to perform switch\n", "nm.TotalField(\"exchange\", \"demag\", \"external\").register(state)\n", "llg.reset()\n", "\n", "logger = nm.Logger(\"data\", [\"t\", \"m\"], [\"m\"])\n", "while state.t < 1e-9:\n", " logger.log(state)\n", " llg.step(1e-11)\n", "```" ] }, { "cell_type": "markdown", "id": "9a452d91", "metadata": {}, "source": [ "## Changing the backend\n", "\n", "NeuralMag will automatically choose a backend based on the availability of the jax/torch libraries defaulting to the JAX library. If you have both backends installed, you can manually set the backend in the simulation script e.g. to torch by setting `nm.config.backend = \"torch\"` directly after importing the `neuralmag` library.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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" } }, "nbformat": 4, "nbformat_minor": 5 }