Solvers and time integration
Solver type
The solver is selected with setMicroMagSolver in Matlab and with the
solver argument in Python.
Name |
Value |
Description |
|---|---|---|
|
2 |
Integrates the Landau-Lifshitz equation in time under a single, time-varying applied field. The field is linearly interpolated from the tabulated values at the requested time. |
|
3 |
Treats every row of the applied-field table as a separate, constant
field and relaxes the magnetization to equilibrium for each of them in
turn, starting from the state reached at the previous field. This is the
quasi-static mode used for hysteresis loops. The equilibrium is found
with the Energy minimizer. Thermal runs use |
|
1 |
Reads the applied-field table as |
Explicit replaces the earlier names Minimizer and Implicit.
ProblemMod (Python prob_mode) selects new (1) or old (2). Use
new, which is the default; old skips the allocation of the solution
arrays and only makes sense for continuing a solution inside the same process.
Time grid and applied field
Two independent time arrays are involved:
the output times
t(ntentries), at which the solution is returned;the applied-field table
Hext(nt_Hextrows of \([t, H_x, H_y, H_z]\)).
In Matlab both are set through helper methods:
problem = problem.setTime( linspace(0,1e-9,200) ); % output times
HystDir = 1/mu0*[-24.6,4.3,0]/1000; % A/m
problem = problem.setHext( @(t) (t>-1)'.*HystDir, linspace(0,1e-9,2000) );
setHext(fct, t_Hext) evaluates the function handle on the given time array
and stores the result together with the times, so the applied field can be an
arbitrary function of time.
In Python the output times follow from t_end and nt. The field
function is passed to the run method, which evaluates it on nt_h_ext
uniformly spaced times between 0 and t_end:
result = problem.run_simulation(
t_end=1e-9, nt=200, fct_h_ext=h_ext_fct, nt_h_ext=2000
)
The field function must return an (nt_h_ext, 3) array.
For the explicit solvers (Explicit and ExplicitLL)
the same table is read differently: each row is one constant field to relax
at, and the time column is unused. nt_h_ext is therefore the number of points
on the hysteresis curve, and t_end/nt only control how long each
relaxation is integrated (for the minimizer, only when it falls back to the
time integration) and how densely the hysteresis curve is sampled.
setTimeDis (Python setTimeDis, default 10) controls how often the
solver reports progress: a message is printed every setTimeDis’th
integration step.
ODE solver settings
Two time integrators are available:
RKSuite - the default explicit Runge-Kutta suite, always available.
CVODE - from SUNDIALS, enabled with
setUseCVODE(true)in Matlab orcvode=Truein Python. It requires a build withUSE_CVODE=1and is the better choice for stiff problems; RKSuite prints a warning suggesting CVODE when it detects stiffness.
Parameter |
Default |
Description |
|---|---|---|
|
|
Relative tolerance of the ODE solver. |
|
|
Threshold value: a solution component smaller in magnitude than this is treated as zero by RKSuite. |
|
|
Times at which the solution is checked for convergence against the state at the previous convergence time. |
|
|
Convergence criterion: when the largest change of any magnetization component between two convergence times is below this, the integration stops and the converged state is held at the remaining output times. |
The convergence test is what lets a relaxation to equilibrium stop when it is
done instead of always running to the last output time. With the default
t_conv = 0 it never fires. To use it, make t_conv the output times (or
a subset of them) and choose conv_tol; in Python this is
problem.t_conv = np.linspace(0, t_end, nt)
problem.nt_conv = nt
problem.conv_tol = np.repeat(1e-6, nt)
In a thermal run the noise never settles, so the test is left out there. Both
the RKSuite and the CVODE driver apply it; CVODE steps to the output times, so
the convergence times are visited there when they are output times as well.
The times in t_conv are merged into the RKSuite integration grid, which
matters for the thermal field because it is redrawn once per grid time, see
Thermal fluctuations. Keeping t_conv a subset of t therefore
serves both integrators.
Energy minimizer
With solver = Explicit (Python solver="explicit") the equilibrium at
each constant applied field is found by minimizing the energy directly rather
than by integrating the Landau-Lifshitz equation with a large damping. The
method is steepest descent on the unit sphere with Barzilai-Borwein step
lengths, following Exl et al., J. Appl. Phys. 115, 17D118 (2014). The
descent direction of cell \(i\) is \(\mathbf{m}_i \times
(\mathbf{m}_i \times \mathbf{H}_i)\), the same vector that drives the damping
term of the Landau-Lifshitz equation, and the update
is the exact solution of the midpoint rule, so \(|\mathbf{m}| = 1\) is preserved to rounding. Each iteration costs one effective-field evaluation, against about six per accepted step of the RK(4,5) time integration, and the step length adapts to the local curvature instead of being bounded by the stability limit of an explicit integrator, which for a fine grid is set by the exchange stiffness whatever the distance from equilibrium.
Convergence is declared when the largest torque over the cells,
\(\max_i |\mathbf{m}_i \times \mathbf{H}_i| / \max(M_s)\), falls below
min_tol. Two safeguards keep the non-monotone Barzilai-Borwein iteration
in check:
no cell rotates by more than
min_maxrotin one iteration, andthe energy may not rise above the largest of the last ten iterates by more than a small fraction of the energy scale; if it would, the step is halved and retried.
If the iteration cap min_maxiter is hit, or the watchdog stalls, and
min_fallback is set, the Landau-Lifshitz time integration is run once over
the requested time window from the current state and the minimizer is
restarted from where it ends up. This is what carries a hysteresis loop
through a switching event, where the state sits near a saddle point.
A vanishing torque is also what a saddle point looks like, and a symmetric
starting state sits on one: the canonical vortex of standard problem 3, or a
magnetization exactly antiparallel to the applied field. Steepest descent
converges onto such a point, whereas the time integration only leaves it
through rounding noise, slowly. With min_saddle_check = 1, a converged
state is therefore nudged by a random tilt of about half
a degree, common to all cells with a smaller independent part per cell, and
relaxed again for up to a hundred iterations. A minimum keeps the energy above
the unperturbed value throughout and is returned unperturbed; a saddle lets the
energy fall below it, at which point the descent continues to the lower
minimum, which is then checked in the same way, up to three times. The check
costs up to a hundred field evaluations per applied field.
With min_saddle_check = 2, the default, the question is answered rigorously
instead: the
lowest eigenvalue of the energy Hessian in the tangent space of the converged
state is computed by the Lanczos iteration, each step costing one field
evaluation, since the Hessian applied to a tangent displacement is the
finite difference of the torque along it (the energy is quadratic in the
magnetization, up to the anisotropy). The eigenvalue is returned in
min_eig in units of the largest saturation magnetization: positive means a
minimum, negative a saddle, in which case the state is pushed along the
eigenvector, which is the direction of steepest descent out of the saddle,
and relaxed again. Near a switching event the eigenvalue goes to zero, so it
also tells how close a state is to switching. Along a field sweep the
eigenvector of the previous field starts the iteration, which then converges
in a handful of steps; a cold start takes a few tens. On a single-grain loop
the check costs less than the random nudge, and it finds saddles that the
nudge misses, such as the plain flower state of standard problem 3 near the
flower-vortex transition, which relaxes to the lower twisted flower.
Parameter |
Default |
Description |
|---|---|---|
|
|
Convergence criterion on the largest relative torque. |
|
|
Maximum number of iterations per applied field. |
|
|
Largest rotation of any cell in one iteration [rad]. |
|
|
Fall back to the time integration when the minimizer stalls (1) or give up and report it (0). |
|
|
Nudge a converged state and relax again to make sure it is a minimum
(1), compute the lowest eigenvalue of the energy Hessian instead (2, see
below) or accept the state as it is (0). Matlab: |
|
|
Start the minimizer at each applied field from the secant extrapolation
of the two previous equilibria (1) instead of from the previous one (0),
with the first step taken at the step length the previous field ended
with. Costs no field evaluation; the extrapolation is skipped across a
switching event. Matlab: |
The minimizer works with every grid type, with CUDA and with FMM, because it
calls the same field routines as the time integration. A finite-temperature
run has no stationary state to converge to, so it uses the time integration
through ExplicitLL; the damping settings play no role in the minimizer.
The output has the layout of the time integration: the first output time holds
the starting state and every later one the converged state. In addition every
run, with either method, returns the energies and a few diagnostics per applied
field, see Micromagnetic output: n_feval is the number of
effective-field evaluations spent relaxing at that field, which is the fair
cost measure between the two methods, and min_status records whether the
minimizer converged directly, needed the fallback, or failed.
How much cheaper it is depends on the problem. On the shipped examples, measured in field evaluations per applied field on a CPU:
Problem |
LL relaxation |
Minimizer |
Ratio |
|---|---|---|---|
Single 10 nm grain, 53 cells, adaptive hysteresis loop, 1 ns relaxation window |
28695 |
1877 |
15 |
Same, LL window extended to 100 ns so that its switching field agrees with the minimizer |
240624 |
1877 |
128 |
Standard problem 3, 103 cells, flower state |
988 |
177 |
5.6 |
Standard problem 3, 103 cells, vortex state |
12660 |
337 |
38 |
Roughly a hundred of the minimizer’s evaluations per applied field go into the saddle check; without it the flower state takes 39 evaluations and the vortex 156. The energies agree to six digits in the vortex case and the minimizer finds a marginally lower flower energy than the time integration did in its 10 ns window. On the grain the minimizer lands within one field step of the Stoner-Wohlfarth value, where the 1 ns time integration is still relaxing near the switching field and overshoots it by ten percent. The script minimizer_vs_llg.py reproduces these numbers.
Standard problem 6 shows the same picture on a domain wall pinned at a phase boundary. The reference values are static depinning fields, the example’s time ramp of 0.02 T per ns gives rate-dependent ones, and the minimizer visiting the same 201 field values as constant fields lands within the 0.01 T field resolution of the analytical value in every setting, at a few hundred times fewer field evaluations:
Setting |
Analytical [T] |
LL time ramp [T] |
Minimizer [T] |
Field evaluations LL / minimizer |
|---|---|---|---|---|
|
1.568 |
1.590 |
1.580 |
2 872 407 / 18 979 |
|
1.089 |
1.120 |
1.110 |
2 850 968 / 17 067 |
|
1.206 |
1.260 |
1.240 |
3 049 920 / 17 205 |
|
0.838 |
0.870 |
0.860 |
2 845 165 / 12 866 |
|
1.005 |
1.020 |
1.010 |
11 088 509 / 28 309 |
|
0.565 |
0.590 |
0.570 |
2 848 863 / 13 982 |
The time integration remains the method of choice for dynamics, for thermal runs, and as an independent reference for the minimizer.
Hysteresis simulations
A hysteresis loop is computed with the explicit solver: a sequence of
constant applied fields is stepped through and the equilibrium magnetization is
found at each of them, starting from the previous equilibrium state. Setting
gamma = 0 removes the precession, which makes each relaxation considerably
cheaper when only the equilibrium state matters.
In Matlab this is just the explicit solver with a field table that walks the loop, as in the standard problem 2 example:
problem = problem.setMicroMagSolver( 'Explicit' );
HystDir = 1/mu0*[1,1,1]/sqrt(3);
problem = problem.setHext( @(t) HystDir.*t', linspace(MaxH,-MaxH,40) );
problem = problem.setTime( linspace(0,40e-9,2) );
Explicit relaxes at each field with the energy minimizer. Use
ExplicitLL to integrate the Landau-Lifshitz equation instead; see
Energy minimizer for the trade-off.
In Python the dedicated method run_hysteresis takes the field table
directly as an (n,4) array, and requires hysteresis_solver='static',
which is the default:
problem = MicromagProblem(res=res, solver='explicit', ...)
H_ext = np.zeros((40, 4))
H_ext[:, 1:4] = np.linspace(max_H, -max_H, 40)[:, None] * hyst_dir
result = problem.run_hysteresis(H_ext)
Adaptive hysteresis
Choosing the field steps of a loop by hand is wasteful: most of the curve is
smooth and needs few points, while the switching region needs many. The
adaptive hysteresis solver walks from H_start to H_end and picks the
step length itself, based on how much the volume-averaged magnetization moved.
The whole accept/reject loop runs inside Fortran in a single call. It is
available from Python through run_hysteresis_adaptive and requires
hysteresis_solver='adaptive' together with solver='explicit' or
solver='explicit_ll'. In Matlab
the same parameters exist on the problem object (adaptiveHext,
maxHextSteps, H_start, H_end, dH_initial, dH_min,
dH_max, dH_grow, dH_shrink, dM_min, dM_target,
dM_reject, switch_refdH, use_sw_ref).
The sweep first relaxes the starting state m0 at H_start and stores
the result as the first field state, so every entry of the output, including
the first, is an equilibrium (and the first entry of n_feval,
min_iter, min_torque, min_status and min_eig describes that relaxation).
Every step is measured against the last accepted state, and measuring the
first one against an m0 far from equilibrium - for example m0 along a
field well away from the easy axis and below the anisotropy field - would make
it look like a switch. There is therefore no need to relax m0 in a
separate solve before the sweep.
The step metric is the length of the change of the cell-averaged reduced magnetization vector across the trial field step,
The step-control logic is then
\(\mathrm{d}M >\)
dM_rejectand \(\mathrm{d}H >\)dH_min: the step is rejected,dHis multiplied bydH_shrink(never belowdH_min) and the step is retried from the same state.\(\mathrm{d}M >\)
dM_target: the step is accepted butdHis reduced for the next step.\(\mathrm{d}M <\)
dM_min: the step is accepted anddHis multiplied bydH_growfor the next step, capped atdH_max.Optionally, if
use_sw_refis set, a step across which the mean magnetization along the field direction changes sign is rejected wheneverdHexceedsswitch_refdH. This forces fine sampling right at the switching field, which is what a coercivity calculation needs.Recovery from the floor: once
dHhas been driven down todH_min(by rejections or by thedM_targetrule, e.g. around a fast change of the magnetization) or toswitch_refdHby the switch refinement,dHalso grows bydH_growafter every accepted step with \(\mathrm{d}M \le\)dM_target, not only belowdM_min. After a switch refinement this waits until the step across the sign change has been accepted. The first step abovedM_targetafterdHhas grown ends the recovery and dividesdHbydH_grow, back to the last step length that stayed withindM_target, and the rules above take over again. Without it, a smooth stretch that follows would give a \(\mathrm{d}M\) betweendM_minanddM_targetat the floor and keep the rest of the sweep there: in a hard-axis loop that is about a thousand steps ofdH_min. UntildHfirst reaches its floor the step control is exactly the rules above.
At dH_min a large change is accepted and noted in the progress output, so
the sweep always advances, and a cap on the total number of rejected steps
bounds the run time.
Argument |
Default |
Description |
|---|---|---|
|
- |
Start and end field vectors in A/m. They must differ. |
|
- |
First step length in A/m. |
|
- |
Smallest and largest allowed step length in A/m. |
|
- |
Maximum number of accepted field states, excluding the initial one. |
|
|
Below this change the step is grown. |
|
|
Above this change the step is shrunk. |
|
|
Above this change the step is rejected and retried. |
|
|
Growth factor, must be larger than 1. |
|
|
Shrink factor, must be between 0 and 1. |
|
|
When given, the largest step accepted across a sign change of the mean magnetization along the field, in A/m. |
The output arrays are preallocated to max_steps in Fortran and sliced on
return, and the number of accepted steps is the last element of the returned
list. A minimal, self-contained example is
adaptive_hysteresis_minimal.py.
problem = MicromagProblem(
res=[5, 5, 5],
grid_L=[10e-9, 10e-9, 10e-9],
solver="explicit",
hysteresis_solver="adaptive",
Ms=2.4 / mu0, K0=1e6, A0=7e-12,
alpha=4000.0, gamma=0.0,
usereturnhall=True,
)
problem.u_ea[:, :] = [0.0, 0.0, 1.0]
result = problem.run_hysteresis_adaptive(
H_start=1.0 / mu0 * field_direction,
H_end=-2.0 / mu0 * field_direction,
dH_initial=0.5 / mu0,
dH_min=0.01 / mu0,
dH_max=0.5 / mu0,
max_steps=512,
switch_refine_dH=0.01 / mu0,
)
n_accepted = result[-1]