Tutorial

An example – vortex

To start a micromagnetic simulation, we first create a FDMesh

mesh = FDMesh(dx=2e-9, dy=2e-9, dz=2e-9, nx=100, ny=100)

After that, we create a simulation

sim = Sim(mesh, name="vortex")

and set the damping to 0.5 and switch off the precession term in LLG equation:

sim.driver.alpha = 0.5
sim.driver.precession = false

The geometry of the system can be defined by

set_Ms(sim, circular_Ms)

where circular_Ms could be a scalar or a function. The function should take six parameters (i,j,k,dx,dy,dz), for instance

function circular_Ms(i,j,k,dx,dy,dz)
    if (i-50.5)^2 + (j-50.5)^2 <= 50^2
        return 8.6e5
    end
    return 0.0
end

We add the exchange interaction and the demagnetization field to the system.

add_exch(sim, 1.3e-11)
add_demag(sim)

We need to initialise the system which can be done by defining a function

function init_fun(i,j,k,dx,dy,dz)
  x = i-50.5
  y = j-50.5
  r = (x^2+y^2)^0.5
  if r<5
    return (0,0,1)
  end
  return (y/r, -x/r, 0)
end

and using

init_m0(sim, init_fun)

To trigger the simulation we relax the system

relax(sim, maxsteps=1000)

How to enable GPU

Using FDMeshGPU instead of FDMesh to switch on the GPU calculation,

mesh = FDMeshGPU(dx=2e-9, dy=2e-9, dz=2e-9, nx=100, ny=100)

The script to use GPU to obtain the vortex structure is shown below:

using JuMag
using Printf
using NPZ

JuMag.cuda_using_double(true)
mesh =  FDMeshGPU(dx=2e-9, dy=2e-9, dz=5e-9, nx=100, ny=100, nz=4)

function circular_Ms(i,j,k,dx,dy,dz)
    x = i-50.5
    y = j-50.5
    r = (x^2+y^2)^0.5
    if (i-50.5)^2 + (j-50.5)^2 <= 50^2
        return 8e5
    end
    return 0.0
end

function init_fun(i,j,k,dx,dy,dz)
  x = i-50.5
  y = j-50.5
  r = (x^2+y^2)^0.5
  if r<5
    return (0,0,1)
  end
  return (y/r, -x/r, 0)
end

function relax_system()
  sim = Sim(mesh, driver="SD", name="sim")
  set_Ms(sim, circular_Ms)

  add_exch(sim, 1.3e-11, name="exch")
  add_demag(sim)

  init_m0(sim, init_fun)
  relax(sim, maxsteps=2000, stopping_torque=1.0, save_vtk_every = 100, save_m_every=-1)
  npzwrite("m0.npy", sim.spin)
end

relax_system()

Standard Problem #4

using JuMag
using Printf
using NPZ

mesh =  FDMeshGPU(nx=200, ny=50, nz=1, dx=2.5e-9, dy=2.5e-9, dz=3e-9)

function relax_system(mesh)
  sim = Sim(mesh, name="std4_relax", driver="SD")
  set_Ms(sim, 8.0e5)
  sim.driver.min_tau = 1e-10

  add_exch(sim, 1.3e-11)
  add_demag(sim)

  init_m0(sim, (1, 0.25, 0.1))

  relax(sim, maxsteps=5000, stopping_torque=10.0)
  npzwrite("m0.npy", Array(sim.spin))
end

function apply_field1(mesh)
  sim = Sim(mesh, name="std4_dyn")
  set_Ms(sim, 8.0e5)
  sim.driver.alpha = 0.02
  sim.driver.gamma = 2.211e5

  mT = 0.001 / (4*pi*1e-7)
  add_exch(sim, 1.3e-11)
  add_demag(sim)
  add_zeeman(sim, (-24.6*mT, 4.3*mT, 0))

  init_m0(sim, npzread("m0.npy"))

  for i=1:100
    run_until(sim, 1e-11*i)
  end
end

relax_system(mesh)
println("Start step2 !!!")
apply_field1(mesh)
println("Run step2 again!!!")
@time apply_field1(mesh)
println("Done!")

The output file is a simple text compatible with Gnuplot, like used for plot below.

std4

A NEB example (vortex core reversal)

The magnetic vortex core can either point up or point down. In this example, we will compute the energy barrier between this two states. We will use NEB (Nudged elastic band) method.

To begin, we need to prepare this two states which can be done by defining two initial functions init_m_point_up and init_m_point_down. After running the following script, two states in the format of OVF2 (up.ovf and down.ovf) will be saved.

using JuMag
using Printf

JuMag.cuda_using_double(true)
mesh =  FDMeshGPU(dx=2e-9, dy=2e-9, dz=5e-9, nx=100, ny=100, nz=4)

function circular_Ms(i,j,k,dx,dy,dz)
    x = i-50.5
    y = j-50.5
    r = (x^2+y^2)^0.5
    if (i-50.5)^2 + (j-50.5)^2 <= 50^2
        return 8e5
    end
    return 0.0
end

function init_m_point_up(i,j,k,dx,dy,dz)
  x = i-50.5
  y = j-50.5
  r = (x^2+y^2)^0.5
  if r<5
    return (0,0,1)
  end
  return (y/r, -x/r, 0)
end

function init_m_point_down(i,j,k,dx,dy,dz)
  x = i-50.5
  y = j-50.5
  r = (x^2+y^2)^0.5
  if r<5
    return (0,0,1)
  end
  return (y/r, -x/r, 0)
end

function relax_system(init_fun, name)
  sim = Sim(mesh, driver="SD", name="sim")
  set_Ms(sim, circular_Ms)

  add_exch(sim, 1.3e-11, name="exch")
  add_demag(sim)

  init_m0(sim, init_fun)
  relax(sim, maxsteps=2000, stopping_torque = 1.0)
  save_ovf(sim, name)
end

for (init_m, name) in [(init_m_point_up, "up"), (init_m_point_down, "down")]
  relax_system(init_m, name)
end

After generating the two states we are interested in, we can use NEB method. In JuMag, we have a pure CPU version of NEB and a GPU version which supports multiple GPUs with the helper of MPI. In this example, we will use the latter, i.e., NEB_MPI. Typically, three parameters are needed to start the NEB simulation:

  • sim – an instance of MicroSimGPU.
  • initm – an array that contains at least two ends states. If you have more information about the middle states, you can put it into initm array.
  • interpolations – an array to contain the number of interpolations between given states. So length(interpolations) must equal to length(init_m) - 1.

So the typical usage is given as follows

function relax_neb()
    ovf1 = read_ovf("down.ovf")
    ovf2 = read_ovf("up.ovf")
    sim = create_sim()
    init_m = [ovf1.data, ovf2.data]
    interpolations = [12]
    neb = NEB_MPI(sim, init_m, interpolations, driver="LLG")
    relax(neb, maxsteps=10000, stopping_dmdt = 0.5, save_ovf_every=500)
end

In the function relax_neb we called create_sim which is very similar to the function relax_system, actually, two functions should have exactly the same micromagnetic parameters. Here, to save GPU memory we do not use any driver (by setting driver="none") and let save_data=false when creating the Sim instance.

function create_sim()
    mesh =  FDMeshGPU(dx=2e-9, dy=2e-9, dz=5e-9, nx=100, ny=100, nz=4)
    sim = Sim(mesh, name="sim", driver="none", save_data=false)
    set_Ms(sim, circular_Ms)
    init_m0(sim, (0,0,1))
    add_demag(sim)
    add_exch(sim, 1.3e-11)
    return sim
end

Moreover, if you are going to use multiple GPUs, you need to add JuMag.using_multiple_gpus(), the number of GPUs will be set to the number of processes of MPI. Put it all together, the script is given by

using JuMag
using CuArrays
using Printf
JuMag.cuda_using_double(true)
CuArrays.allowscalar(false)  #important for performance

JuMag.using_multiple_gpus()

function create_sim()
    mesh =  FDMeshGPU(dx=2e-9, dy=2e-9, dz=5e-9, nx=100, ny=100, nz=4)
    sim = Sim(mesh, name="sim", driver="none", save_data=false)
    set_Ms(sim, circular_Ms)
    init_m0(sim, (0,0,1))
    add_demag(sim)
    add_exch(sim, 1.3e-11)
    return sim
end

function circular_Ms(i,j,k,dx,dy,dz)
    x = i-50.5
    y = j-50.5
    r = (x^2+y^2)^0.5
    if (i-50.5)^2 + (j-50.5)^2 <= 50^2
        return 8e5
    end
    return 0.0
end

function relax_neb()
    ovf1 = read_ovf("vortex_down.ovf")
    ovf2 = read_ovf("vortex_up.ovf")
    sim = create_sim()
    neb = NEB_MPI(sim,(ovf1.data, ovf2.data),(12), driver="LLG")
    relax(neb, maxsteps=10000, stopping_dmdt=1.0, save_ovf_every=500)
end

relax_neb()

The Slurm script two use two GPUs:

#!/bin/bash
#SBATCH --nodes=1             # Number of nodes
#SBATCH --ntasks=2            # Number of MPI processes
#SBATCH --cpus-per-task=1     # Number of CPUs per task
#SBATCH --gres=gpu:2          # Number of GPUs
mpiexec -np 2 julia main.jl

neb_vortex

Monte Carlo (M-T curve)

We can use Monte Carlo to compute the M-T curve. For the atomistic model with $z$ nearest neighbors, the relation between exchange constant and $T_c$ reads [1]

\[J = \frac{3 k_B T_c}{ \epsilon z }\]

where $\epsilon$ is a correction factor. For 3D classical Heisenberg model $\epsilon \approx 0.719$. In this example, we will assume $J=300k_B$ which gives $T_c = 431 K$. The full script is shown below.

using JuMag
using Random

JuMag.cuda_using_double(true)

function relax_system(T)
  mesh =  CubicMeshGPU(nx=30, ny=30, nz=30, pbc="xyz")
  sim = MonteCarloNew(mesh, name="mc")
  init_m0(sim, (0,0,1))

  add_exch(sim, J=300*k_B)
  add_dmi(sim, D=0, D1=0)
  add_zeeman(sim, Hx=0, Hy=0, Hz=0)
  add_anis(sim, Ku=0, Kc=0)

  sim.T = 100000
  run_sim(sim, maxsteps=50000, save_vtk_every=-1, save_m_every=-1)
  sim.T = T
  run_sim(sim, maxsteps=200000, save_vtk_every=-1, save_m_every=-1)

  ms = zeros(1000)
  sim.T = T
  for i = 1:1000
      run_sim(sim, maxsteps=100, save_vtk_every=-1, save_m_every=-1)
      t = JuMag.average_m(sim)
      ms[i] = sqrt(t[1]^2+t[2]^2+t[3]^2)
  end
  return sum(ms)/length(ms)
end

f = open("M_H.txt", "w")
write(f, "#T(K)     m \n")
for T = 10:20:500
    println("Running for $T ...")
    m = relax_system(T)
    write(f, "$T    $m \n")
end
close(f)

The obtained M-T curve is shown as follows, where the obtained $T_c$ is closed to 440 K.s M_T

[1] Atomistic spin model simulations of magnetic nanomaterials, J. Phys.: Condens. Matter 26 (2014) 103202.