Skip to main content

Overview

This module provides two Prefect tasks for NEB calculations:
TaskFunctionUse when
NEB from imagesrunYou already have a list of interpolated or partially relaxed images
NEB from endpointsrun_from_endpointsYou have only start and end structures; the task handles interpolation and optional endpoint relaxation
Both tasks use a TASK_SOURCE + INPUTS cache policy.

run — NEB from images

Function signature

from mlip_arena.tasks.neb import run as NEB

result = NEB(
    images,
    calculator,
    optimizer="MDMin",
    optimizer_kwargs=None,
    criterion=None,
    interpolation="idpp",
    climb=True,
    traj_file=None,
)

Parameters

body.images
list[ase.Atoms]
required
Ordered list of Atoms objects representing the NEB images (including the two endpoint images). Each image is copied internally; the originals are not mutated.
body.calculator
ase.calculators.calculator.BaseCalculator
required
Calculator attached to every image via allow_shared_calculator=True.
body.optimizer
Optimizer | str
default:"MDMin"
NEB optimizer class or string name. "BFGSLineSearch" is not supported for NEB. Accepted strings: "MDMin", "FIRE", "FIRE2", "LBFGS", "LBFGSLineSearch", "BFGS", "QuasiNewton", "GPMin", "CellAwareBFGS", "ODE12r".
body.optimizer_kwargs
dict | None
default:"None"
Extra keyword arguments forwarded to the optimizer constructor.
body.criterion
dict | None
default:"None"
Convergence criteria passed to optimizer.run(). When None, the ASE optimizer default is used (no step limit). Typical key: fmax (eV/Å).
body.interpolation
string
default:"idpp"
Initial path interpolation method applied before optimization:
ValueDescription
"linear"Linear interpolation of Cartesian coordinates between endpoints
"idpp"Image Dependent Pair Potential — produces smoother initial paths and typically converges faster
body.climb
boolean
default:"true"
Enable the climbing-image variant of NEB (CI-NEB). When True, the highest-energy image climbs toward the true saddle point, giving a more accurate barrier estimate.
body.traj_file
string | Path | None
default:"None"
Path for writing NEB optimization trajectory. Passed directly to the optimizer constructor.

run_from_endpoints — NEB from endpoints

Function signature

from mlip_arena.tasks.neb import run_from_endpoints as NEB_FROM_ENDPOINTS

result = NEB_FROM_ENDPOINTS(
    start,
    end,
    n_images,
    calculator,
    optimizer="BFGS",
    optimizer_kwargs=None,
    criterion=None,
    relax_end_points=True,
    interpolation="idpp",
    climb=True,
    traj_file=None,
    cache_subtasks=False,
)

Parameters

body.start
ase.Atoms
required
Initial state (reactant) structure.
body.end
ase.Atoms
required
Final state (product) structure.
body.n_images
number
required
Total number of images in the NEB path, including the two endpoint images.
body.calculator
ase.calculators.calculator.BaseCalculator
required
Calculator used for endpoint relaxations and the NEB optimization.
body.optimizer
Optimizer | str
default:"BFGS"
Optimizer used for both endpoint relaxations and the NEB run. See run for accepted string values.
body.optimizer_kwargs
dict | None
default:"None"
Extra keyword arguments forwarded to the optimizer constructor.
body.criterion
dict | None
default:"None"
Convergence criteria passed to optimizer.run().
body.relax_end_points
boolean
default:"true"
When True, both start and end are individually relaxed with OPT before the NEB path is constructed. Recommended to ensure the endpoints sit at true local minima.
body.interpolation
string
default:"idpp"
Interpolation method for generating intermediate images. See run for options.
body.climb
boolean
default:"true"
Enable climbing-image NEB.
body.traj_file
string | Path | None
default:"None"
Path for writing NEB optimization trajectory.
body.cache_subtasks
boolean
default:"false"
When True, results from the endpoint OPT sub-tasks and the inner NEB run are cached and persisted. When False, sub-tasks always recompute.

Differences between run and run_from_endpoints

runrun_from_endpoints
InputPre-built image listStart + end Atoms only
Endpoint relaxationYour responsibilityAutomatic (controllable via relax_end_points)
Path constructionYou provide imagesUses pymatgen Structure.interpolate with autosort_tol=0.5
Sub-task cachingN/AControlled by cache_subtasks
Default optimizer"MDMin""BFGS"

Return value

Both functions return the same structure:
{
    "barrier":  tuple,      # (forward_barrier_eV, reverse_barrier_eV)
    "images":   list[Atoms],# optimized NEB images
    "forcefit": object,     # result of ase.utils.forcecurve.fit_images
}
barrier
tuple[float, float]
Energy barriers returned by NEBTools.get_barrier(): (forward_barrier_eV, reverse_barrier_eV).
images
list[ase.Atoms]
The fully optimized NEB images in path order.
forcefit
object
Spline fit object from ase.utils.forcecurve.fit_images, useful for plotting the energy profile.
If the initial relaxation of endpoints fails, run_from_endpoints returns a Prefect State object instead of a dict.

Examples

from ase.build import fcc100, add_adsorbate
from ase.constraints import FixAtoms
from mlip_arena.models import MLIPEnum
from mlip_arena.tasks.neb import run as NEB
from mlip_arena.tasks.utils import get_calculator

calculator = get_calculator(MLIPEnum["MACE-MP(M)"])

# Build initial and final images manually
initial = fcc100("Al", size=(2, 2, 3))
final = initial.copy()
# ... modify final to represent the product state ...

images = [initial.copy() for _ in range(7)]  # 5 intermediate + 2 endpoints

result = NEB(
    images=images,
    calculator=calculator,
    optimizer="FIRE",
    criterion={"fmax": 0.05},
    interpolation="idpp",
    climb=True,
    traj_file="neb.traj",
)

forward_barrier, reverse_barrier = result["barrier"]
print(f"Forward barrier: {forward_barrier:.3f} eV")