Getting Started#

As a first example the standard problem #4, 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.

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.

[ ]:
import neuralmag as nm

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. 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\).

[ ]:
mesh = nm.Mesh((100, 25, 1), (5e-9, 5e-9, 3e-9))

Next, we define a State object which manages the state of simulation such as material parameters and the current time and magnetization.

[ ]:
state = nm.State(mesh)

Since the standard problem #4 defines a homogeneous material throughout the cuboid sample, the material parameters can be set as simple Python float values

[1]:
state.material.Ms = 8e5
state.material.A = 1.3e-11
state.material.alpha = 0.02

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. 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. We use the fill method of the function to object to set the initial magnetization direction on each node.

[ ]:
state.m = nm.VectorFunction(state).fill((0.5**0.5, 0.5**0.5, 0))

Next, we set up the individual effective-field contributions for the simulation, namely the exchange field, the demagnetization field and the external field. Like the magnetization, the external field is represented as vector function discretized on the nodes of the mesh. 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. 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. 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.

[ ]:
h_ext = nm.VectorFunction(state).fill([-19576.0, 3421.0, 0.0], expand=True)

nm.ExchangeField().register(state, "exchange")
nm.DemagField().register(state, "demag")
nm.ExternalField(h_ext).register(state, "external")

nm.TotalField("exchange", "demag").register(state)

Having all material parameters and field contributions in place, we can now proceed to relax the magnetic system into the initial S-state. We do this, by calling the relax routine of the LLGSolvers object.

[ ]:
llg = nm.LLGSolver(state)
llg.relax()

The LLGSolver object takes all required information for the time integration from the State object. Namely, it calls the h method on state that has been registered by the TotalField class in order to evaluate the effective field. After the relaxation to the S-state, we can check the resulting magnetization configuration by write it to a VTI-file.

[ ]:
state.write_vti("m", "sstate.vti")

This file can be visualized and analyzed with the open source tool Paraview. 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. In oder to simulate this switching process with NeuralMag, we register a new TotalField object that includes the external field. After doing so, we need to reset the LLGSolver object in order to account for this change.

[ ]:
nm.TotalField("exchange", "demag", "external").register(state)
llg.reset()

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. 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.

[ ]:
logger = nm.Logger("data", ["t", "m"], ["m"])
while state.t < 1e-9:
    logger.log(state)
    llg.step(1e-11)

The complete script reads

import neuralmag as nm

# setup mesh and state
mesh = nm.Mesh((100, 25, 1), (5e-9, 5e-9, 3e-9))
state = nm.State(mesh)

# setup material and m0
state.material.Ms = 8e5
state.material.A = 1.3e-11
state.material.alpha = 0.02

# initialize nodal vector functions for magneization and external field
state.m = nm.VectorFunction(state).fill((0.5**0.5, 0.5**0.5, 0))
h_ext = nm.VectorFunction(state).fill([-19576.0, 3421.0, 0.0], expand=True)

# register effective field contributions
nm.ExchangeField().register(state, "exchange")
nm.DemagField().register(state, "demag")
nm.ExternalField(h_ext).register(state, "external")
nm.TotalField("exchange", "demag").register(state)

# relax to s-state
llg = nm.LLGSolver(state)
llg.relax()

state.write_vti("m", "sstate.vti")

# add external field to perform switch
nm.TotalField("exchange", "demag", "external").register(state)
llg.reset()

logger = nm.Logger("data", ["t", "m"], ["m"])
while state.t < 1e-9:
    logger.log(state)
    llg.step(1e-11)

Changing the backend#

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.