The Stress Tensor in Plane-Wave DFT
The stress tensor is what you compute when the unit cell itself is a variable. It tells you which way the cell wants to deform to lower the total energy — essential for finding equilibrium lattice constants, computing equations of state, and running variable-cell molecular dynamics. In plane-wave DFT it has a particularly clean analytic form thanks to the Nielsen-Martin formalism, which derives every contribution as a strain derivative of the corresponding energy term.
Definition and units
The stress tensor is the negative gradient of the total energy with respect to a uniform strain tensor applied to the cell, divided by the cell volume:
Indices . Symmetry of the stress tensor follows from conservation of angular momentum: , giving 6 independent components (three diagonal, three off-diagonal). In KRONOS the natural unit is Ry/bohr³; the pressure (hydrostatic part) is
A positive pressure means the cell wants to expand; a negative pressure means it wants to contract.
Strain as a deformation of the cell
A uniform strain maps the position to . The cell vectors become , and the reciprocal vectors transform as to first order in . The cell volume changes as .
Every energy contribution that depends on cell shape — and that's all of them — produces a stress term when differentiated with respect to .
Decomposition: term by term
The DFT total energy in plane-wave form is
Each piece contributes a piece of the stress.
Kinetic stress — from (Rydberg) with depending on strain:
Hartree stress — from :
The negative isotropic part reflects that uniform charge density wants to expand (Coulomb repulsion).
Exchange-correlation stress — depends on whether the functional is LDA or GGA. For LDA only the volume-change-of-density term survives:
For GGA, an additional term from enters; KRONOS computes this via the gradient of the density and the derivative returned by libxc.
Local pseudopotential stress — from the strain derivative of and the structure factor:
The Coulomb tail of contributes an analytic correction (the same subtraction trick used in the local PP energy).
Nonlocal pseudopotential stress — Kleinman-Bylander projectors depend on , which changes under strain. The strain derivative passes through the radial Bessel transform.
Ewald stress — Nielsen-Martin showed how to compute the strain derivative of the Ewald sum analytically, with both real-space and reciprocal-space pieces. The reciprocal piece has the same structure as the Hartree stress. KRONOS implements the closed-form expressions in src/potential/stress.cpp::ewald_stress().
Pressure and the equation of state
The hydrostatic pressure is what enters the enthalpy minimized by vc-relax runs. At zero target pressure, vc-relax converges to a cell where for all components. For non-zero target pressure (press_target in YAML, in GPa), KRONOS adds to the stress and minimizes the enthalpy:
Driving this to zero gives the cell at the target external pressure.
Anisotropic stress and crystal symmetry
The full tensor reveals shape-changing forces missed by a scalar pressure. A cubic crystal at equilibrium has purely. A tetragonal crystal forced into a cubic cell would show . Off-diagonal would force a monoclinic distortion. KRONOS preserves space-group symmetry by symmetrizing the stress tensor with the same spglib operations used for forces:
over the point group of the cell. This eliminates symmetry-breaking floating-point noise and ensures vc-relax converges along symmetry-preserving paths.
Variable-cell relaxation in KRONOS
vc-relax optimizes both atomic positions and cell vectors simultaneously, using a -dimensional BFGS quasi-Newton step (3 atomic-position dimensions plus 6 unique strain components). At each step:
- Run SCF to convergence on the current cell.
- Compute forces and the full stress tensor.
- Symmetrize both.
- Compute the enthalpy gradient and BFGS step.
- Apply the strain to the cell; move atoms.
- Repeat until AND .
Implementation: src/solver/vc_relax.cpp.
How KRONOS implements this
The stress calculation lives in src/potential/stress.cpp. Each contribution (kinetic, Hartree, XC, local PP, nonlocal PP, Ewald) is computed by a dedicated function and accumulated into the 6-component Voigt-style tensor. The total stress is exposed via StressResult::tensor() (3×3 matrix) and StressResult::pressure_gpa() (scalar).
KRONOS has unit-test coverage for each component on a Si bulk cell, asserting agreement with QE stress to within .
References
- Nielsen, O. H. & Martin, R. M. "Quantum-mechanical theory of stress and force", Phys. Rev. B 32, 3780 (1985).
- Nielsen, O. H. & Martin, R. M. "Stresses in semiconductors: Ab initio calculations on Si, Ge, and GaAs", Phys. Rev. B 32, 3792 (1985).
- Dal Corso, A. "Density-functional perturbation theory with ultrasoft pseudopotentials", Phys. Rev. B 64, 235118 (2001) — stress with USPP/PAW.
- Wentzcovitch, R. M. "Invariant molecular-dynamics approach to structural phase transitions", Phys. Rev. B 44, 2358 (1991) — variable-cell MD.
- Martin, R. M. Electronic Structure, Ch. 9.