{{define "entry"}}
{{.Name}}{{.Ins}} {{with .Doc}}
{{.}}
{{end}} {{with .Methods}}methods: {{range .}} {{.}} {{end}}
{{end}} {{with .Examples}}examples: {{range .}} [{{.}}] {{end}}
{{end}}
The mumax3 input syntax is a subset of Go's syntax, somewhat similar to C. It is case-independent however, so msat
is the same as Msat
or MSAT
.
:=
. Variables have a fixed type, inferred from the declaration's right-hand-side. Assigning to existing variables is done using =
. E.g.:
i := 7 // defines a new variable i, type automatically detected to be int
print(i) // now we can use i
i = 5 // assign new value, don't use ':=' (attempt to re-declare)
str := "hello" // defines str, type automatically is string
//str = 1 // would fail, cannot assign int to string
pow(x,y)
should be used.
x := pi*(3+4)/5
x = pow(x, 3)
x++
y := abs(cbrt(cosh(erf(erfc(gamma(J0(Y0(2))))))))
for i:=0; i<10; i++{
print(i)
}
RunWhile(func()bool)
, or all input parameters). In that case, and only in this case, the argument is implicitly converted to a function, which is re-evaluated each time it's needed. E.g.:
value := sin(pi*t) // value is a float64, RHS evaluated only once
Msat = value // time-independent Msat
versus:
Msat = sin(pi*t) // RHS converted to function, re-evaluted every time
.
' as in most object oriented programming languages.
E.g.: a material parameter such as Msat
has the method SetRegion(int, float)
to set the value of the material parameter in a certain region:
Msat.SetRegion(1, 800e3) // Set Msat=520e3 in region 1
Nx := 128
Ny := 64
Nz := 2
sizeX := 500e-9
sizeY := 250e-9
sizeZ := 10e-9
SetGridSize(Nx, Ny, Nz)
SetCellSize(sizeX/Nx, sizeY/Ny, sizeZ/Nz)
SetPBC(5, 0, 0) // 5 extra images on left and right sides.
SetGridSize(128, 64, 1)
SetCellSize(5e-9, 5e-9, 5e-9)
Setting a nonzero PBC value in a direction enables wrap-around in that direction. The precise value passed determines how many repetitions are seen by the demag field. E.g., in the above example the demag field behaves as if 5 repetitions are present to the left and to the right side. Choosing a large number may cause long initialization time.
Shape
other than the full simulation box can be specified. In order to set the geometry, you first need to define a shape.
geometryShape := cylinder(400e-9, 20e-9).RotX(45*pi/180).Transl(1e-6,0,0)
SetGeom(geometryShape)
myShape := cylinder(400e-9, 20e-9).RotX(45*pi/180).Transl(1e-6,0,0)
anotherShape := Circle(400e-9).sub(Circle(200e-9))
regions
to verify whether each cell is assigned to the intended region. Each region can have its own material parameters, and we can output averages over each region. E.g.:
DefRegion(1, circle(1e-6))
DefRegion(0, circle(1e-6).Inverse()) // redundant
save(regions)
Msat.SetRegion(1, 800e6)
tableAdd(m.Region(1)) // add average m over region 1 to table
Config
to m
, setting it in separate regions, or by loading a file directly.
m = uniform(1, 0, 0)
m.SetRegion(1, vortex(1, 1))
m.LoadFile("config.ovf")
m.SetInShape(circle(50e-9), uniform(0,0,1))
Msat = 800e3
AnisU = vector(1, 0, 0)
When regions are defined, they can also be set region-wise:
Msat.SetRegion(0, 800e3)
Msat.SetRegion(1, 540e3)
Material parameters can be functions of time as well. E.g.:
f := 500e6
Ku1 = 500 * sin(2*pi*f*t)
B_ext = vector(0.01, 1e-6*sin(2*pi*f*t), 0)
B_ext.SetRegion(1, vector(0, 0, 0.1))
Additionally, an arbitrary number of time- and space-dependent vector fields of the form g(x,y,z) * f(t)
may be added. (E.g., to simulate the field of an antenna or an arbitrary current running through the magnet)
B_ext.Add(LoadFile("antenna.ovf"), sin(2*pi*f*t))
J.Add(LoadFile("current.ovf"), 1)
Excitations can be defined using standard mathematical functions, or loaded from a .csv
file with FunctionFromDatafile
.
J
and the polarization Pol
.
xi
:
J = vector(1e12, 0, 0)
Pol = 1
xi = 0.1
Lambda
and EpsilonPrime
respectively:
DisableZhangLiTorque = true
J = vector(1e12, 0, 0)
Pol = 0.6
FixedLayer = vector(1,0,0)
FixedLayerPosition = FIXEDLAYER_TOP
EpsilonPrime = 0.02
Lambda = 1
mumax3 has built-in generation of MFM images from a 2D magnetization. The MFM tip lift can be freely chosen. By default, the tip magnetization is modeled as a point monopole at the apex. This is sufficient for most situations. Nevertheless, it is also possible to model partially magnetized tips by setting MFMDipole
to the magnetized portion of the tip, in meters. E.g., if only the first 20nm of the tip is (vertically) magnetized, set MFMDipole=20e-9
.
m // magnetization quantity
m.Comp(0) // x-component
m.Region(1) // magnetization in region 1 (0 elsewhere)
.Average()
yields the average over the entire simulation grid, except for m
which is always averaged over the geometry..Comp()
method. E.g.:
B_demag.Average() // Average vector over entire simulation grid
B_demag.Comp(1).Average() // Average y-component over geometry
m.Average() // Average magnetization over geometry
m
).
save(m) // save full magnetization
save(m.Comp(0)) // save only x-component
save(CropLayer(m, 13)) // save only layer 13
save(CropLayer(m.Comp(0), 13)) // save only x-component of layer 13
Or even:
mx := m.Comp(0)
mx13 := CropLayer(mx, 13)
save(mx13)
tableAdd(mx13)
.ovf
file), or as spatial averages (table output). The data table (table.txt
) contains by default the time and average magnetization. More columns can be added with TableAdd()
.
save(B_ext)
tableadd(B_ext)
tablesave()
Optionally, the output/averaging can be done over a single region:
save(m.Region(1))
TableAdd(m.Region(1))
User-defined variables can be added to the table with TableAddVar()
.
myField := 0.42
TableAddVar(myField, "B_extra", "T")
myField = ...
Run(time)
runs the simulation for a given time in seconds, using sensible error settings.
Run(1e-9)
More fine-grained control is provided by RunWhile(condition)
, which runs as long as an arbitrary condition is met. E.g.:
mx := m.comp(0)
RunWhile(mx.average() < 0) // search for switching field during reversal
Optionally, the solver accuracy may be fine-tuned. E.g.:
MaxDt = 1e-12
MinDt = 1e-15
MaxErr = 1e-6
Optionally, a different solver may be chosen (at any point) with SetSolver(int)
. Currently available solver types:
6
: RK56 (Fehlberg) solver. This is the highest order solver available, but which is typically not faster than the RK45 solver.5
: RK45 (Dormand-Prince) solver (the default). An accurate solver, very fast for magnetization dynamics at the cost of some memory usage. 4
: Classical 4th-order Runge-Kutta method. Intended for simulations where a fixed, relatively large time step is desired.3
: RK23 (Bogacki-Shampine) solver. A robust and reasonably fast solver with low memory requirements. Typically outperforms RK45 when relaxing the magnetization with little dynamics, so it used internally by Relax()
. 2
: Adaptive Heun solver. Robust and uses very little memory but takes smaller time steps than the higher-order solvers. Also suited when a fixed, relatively small time step is desired. 1
: Euler solver (requires FixDt = ...
, ignores other settings). Only useful in exceptional situations or for debugging. SetSolver(2) // Heun
FixDt = 1e-15
Relax()
tries to evolve the magnetization as closely as possible to the minimum energy state. This function assumes all excitations have been turned off (temperature, electrical current, time-dependent magnetic fields). During relax precession is disabled and the time t
does not increase. There is no need to set high damping.
In general it is difficult to be sure the minimum energy state has been truly reached. Hence, relax may occasionally return after the energy has reached a local minimum, a saddle point, or a rather flat valley in the energy landscape.
Minimize()
is like Relax, but uses the conjugate gradient method to find the energy minimum. It is usually much faster than Relax, but is a bit less robust against divergence. E.g., a random starting configuration can be Relaxed, but may fail with Minimize. Minimize is very well suited for hysteresis calculations, where we are never far away from the ground state.
ext_centerwall(0)
ext_rmSurfaceCharge(0, -1, 1)
TableAdd(TotalShift)
will try to keep mx
(component 0, counting from 0) close to zero. If desired, one can override which "new" magnetization is inserted from the sides by setting ShiftMagL
and ShiftMagR
, though the default behaviour is usually OK.
ext_topologicalchargedensity
quantity,
it is possible to define this quantity yourselves inside an input script:
cs := 1e-9
setcellsize(cs,cs,cs)
setgridsize(64,64,1)
// Use central finite differences to approximate the spatial derivatives of m
mL := Shifted(m,-1,0,0) // shift left
mR := Shifted(m,1,0,0) // shift right
mD := Shifted(m,0,-1,0) // shift up
mU := Shifted(m,0,1,0) // shift down
dmdx := Mul( Const(1/(2*cs)), Madd(mR,mL,1,-1) )
dmdy := Mul( Const(1/(2*cs)), Madd(mU,mD,1,-1) )
// Define the topological charge density
chargeDensity := Mul( Const(1/(4*pi)), Dot(m, Cross(dmdx,dmdy)))
// Save the topological charge density of a skyrmion
m = neelskyrmion(1,-1)
saveas(chargeDensity, "chargeDensity.ovf")
Ms := 1100e3
K := 0.5e6
u := ConstVector(1, 0, 0)
anisField := Mul( Const(2*K/Ms) , Mul( Dot(u, m), u))
anisEdens := Mul( Const(-0.5*Ms) , Dot( anisField, m))
AddFieldTerm(anisField) // promote anisField to an effective field term
AddEdensTerm(anisEdens) // promote anisEdens to an energy density term
tableAdd(E_custom) // Add a column with the energy related to the custom field