Calculations#

This page provides guidance on setting up and running ABACUS calculations with AiiDA.

Using ASE Constraints for Selective Dynamics#

AiiDA-ABACUS supports ASE constraints for controlling which atoms can move during geometry optimization. Constraints are automatically converted to ABACUS’s selective dynamics format (the m keyword in the STRU file).

Quick Start#

from ase.build import bulk
from ase.constraints import FixAtoms
from aiida.orm import StructureData

# Create structure with constraints
atoms = bulk("Si", "diamond", a=5.43).repeat((2, 2, 2))
atoms.set_constraint(FixAtoms(indices=[0, 1, 2, 3]))  # Fix first 4 atoms

# Use with builder - constraints are automatically handled
builder.structure = StructureData(ase=atoms)
builder.dynamics = atoms  # This converts constraints to ABACUS format

Supported Constraint Types#

AiiDA-ABACUS supports two ASE constraint types. ABACUS selective dynamics operates in Cartesian directions.

FixAtoms#

Fixes all three directions (x, y, z) for specified atoms:

from ase.constraints import FixAtoms

# Fix atoms by index
atoms.set_constraint(FixAtoms(indices=[0, 1, 2]))

Use case: Surface slabs where you want to fix the bottom layers that represent the bulk substrate.

FixCartesian#

Fixes specific Cartesian directions:

from ase.constraints import FixCartesian

# Fix x and y directions, allow z
atoms.set_constraint(FixCartesian(
    indices=[3, 4],
    mask=(True, True, False)  # True = fixed, False = movable
))

Important: ASE uses True=fixed, but ABACUS uses 1=movable. The conversion is automatic.

Use case: Constraining motion along specific Cartesian directions, such as fixing the z-coordinate for surface atoms while allowing xy relaxation.

Note: FixScaled is NOT supported - ABACUS selective dynamics uses Cartesian directions. Use FixCartesian instead.

Multiple Constraints#

You can combine multiple constraints - restrictions are accumulated (union):

from ase.constraints import FixAtoms, FixCartesian

constraint1 = FixAtoms(indices=[0, 1])  # Fix atoms 0, 1 completely
constraint2 = FixCartesian([2, 3], mask=(False, False, True))  # Fix z for atoms 2, 3

atoms.set_constraint([constraint1, constraint2])

Three Ways to Specify Selective Dynamics#

There are three methods to control which atoms can move (in priority order):

2. Manual dynamics Dict (NEW, explicit control)#

builder.dynamics = orm.Dict({
    "m": [
        [True, True, True],    # Atom 0: fully movable
        [False, False, False], # Atom 1: fully fixed
        [True, True, False],   # Atom 2: xy movable, z fixed
    ]
})

Advantages:

  • Explicit control over each degree of freedom

  • No dependency on ASE

  • Direct mapping to ABACUS format

3. Legacy parameters approach (EXISTING, backwards compatible)#

builder.parameters = orm.Dict({
    "input": {...},
    "stru": {
        "m": [[True, True, True], [False, False, False], ...]
    }
})

Priority resolution:

  • If builder.dynamics is provided → uses it (highest priority)

  • Else if parameters["stru"]["m"] exists → uses it (legacy fallback)

  • Else → defaults to all atoms movable (ABACUS default)

Important: An error will be raised if you specify the m flags in both builder.dynamics and parameters["stru"]["m"] to avoid ambiguity. Use only one method.

This ensures zero breaking changes while providing a superior user experience.

ABACUS Format Details#

Internally, constraints are converted to ABACUS STRU file format:

  • True (movable) → 1 in STRU file

  • False (fixed) → 0 in STRU file

Example STRU file content:

Si
0.0
2
0.00 0.00 0.00 m 0 0 0    # Fixed atom (cannot move)
0.25 0.25 0.25 m 1 1 1    # Movable atom (can move in all directions)

Troubleshooting#

Issue: Constraints not being applied

Solution: Make sure you’re passing ASE Atoms to builder.dynamics.

Issue: Error about specifying ‘m’ flags in both dynamics and parameters

Solution: You cannot specify selective dynamics in both places. Choose one method: either builder.dynamics (recommended) or parameters["stru"]["m"] (legacy).

Issue: How to check if constraints were converted correctly?

Solution: Use the serialize_dynamics function to verify:

from aiida_abacus.utils import serialize_dynamics

dynamics_dict = serialize_dynamics(atoms)
print(dynamics_dict.get_dict())
# Shows: {"m": [[True, True, True], [False, False, False], ...]}

Setting Initial Velocities for Molecular Dynamics#

aiida-abacus supports setting initial atomic velocities for molecular dynamics simulations.

Quick Start#

from aiida import orm

# Create velocity list (one 3D vector per atom)
velocities = [
    [0.1, 0.0, 0.0],   # Atom 0: velocity in x direction
    [0.0, 0.1, 0.0],   # Atom 1: velocity in y direction
    [-0.1, 0.0, 0.1],  # Atom 2: mixed velocities
]

# Set via dynamics port (recommended)
builder.dynamics = orm.Dict({"v": velocities})

# Or combine with selective dynamics
builder.dynamics = orm.Dict({
    "m": [[True, True, True]] * 3,  # Move flags
    "v": velocities                  # Initial velocities
})

Units and Conversion#

Atomic Units: ABACUS uses atomic units for velocities

Magnetic Moments (magmom)#

ABACUS supports setting initial magnetic moments for magnetic calculations. This feature is already implemented in aiida-abacus.

Quick Start#

# Set magnetic moments (one 3D vector per atom)
# For collinear calculations, only one number should be given
# For non-collinear, use [m_x, m_y, m_z]
colinear_magnetic_moments = [
    [1.0],   # Atom 0: spin up
    [-1.0],  # Atom 1: spin down
]
none_colinear_magnetic_moments = [
    [0.0, 0.0, 1.0],   # Atom 0: spin up in z
    [0.0, 0.0, -1.0],  # Atom 1: spin down in z
]

builder.parameters = orm.Dict({
    "input": {"nspin": 2, ...},
    "stru": {
        "mag": collinear_magnetic_moments  # or "magmom"
    }
})

Alias Support#

Two aliases are supported: mag and magmom

# These are equivalent
parameters["stru"]["mag"] = magnetic_moments
# Or use "magmom" is also OK
parameters["stru"].pop("mag", None)
parameters["stru"]["magmom"] = magnetic_moments

STRU Format#

Magnetic moments are written after position and velocity:

None-colinear case:

0.00 0.00 0.00 m 1 1 1 v 0.1 0.0 0.0 magmom 0.0 0.0 1.0

colinear case:

0.00 0.00 0.00 m 1 1 1 v 0.1 0.0 0.0 magmom 1.0

Integration with nspin#

  • nspin = 1: No spin polarization (magmom ignored)

  • nspin = 2: Collinear spin

  • nspin = 4: None-collinear spin, noncolin=1 also needs to be set.