Workflow internals¶
Overview¶
IsolatedImpurityWorkChain answers the question:
"What is the smallest supercell whose boundary is far enough from the muon that
the forces on boundary atoms are negligible?"
The algorithm iterates through increasingly large, nearly cubic supercells until two force-based convergence criteria are satisfied simultaneously.
Supercell generation¶
Nearly cubic supercells¶
The first supercell is generated by ScGenerators.initialize using pymatgen's
CubicSupercellTransformation. This transformation finds a supercell matrix that
minimises the deviation from a perfect cube for the available lattice, subject to:
min_atoms: number of unit cell atoms + 1 (to ensure at least one muon position)max_atoms: 8 × unit cell atoms (caps the size at ~8× volume)min_length: user-supplied or auto-set tomin(lattice.abc) + 1Å
Each subsequent iteration calls ScGenerators.re_initialize, which increments
min_atoms and min_length by one unit relative to the previous supercell,
guaranteeing strictly larger cells.
Muon site placement¶
The muon is inserted at the first Voronoi interstitial site of the unit cell via
pymatgen.analysis.defects.generators.VoronoiInterstitialGenerator. A small offset
of +0.001 (fractional) is added to each coordinate to break exact crystallographic
symmetry and avoid potential issues with symmetry-constrained forces.
The fractional coordinate is then transformed into the supercell frame:
The muon is always the last atom in the supercell.
Differential forces¶
The convergence check uses differential forces rather than raw forces with the muon:
The without-muon supercell is geometrically identical (same atoms in same positions), except the muon H atom is removed. The subtraction cancels any systematic DFT error common to both systems and isolates the muon-induced perturbation.
For the without-muon case a zero-force placeholder [0, 0, 0] is appended at the
muon position index so that array lengths match for the subtraction.
Charge handling
When charge_supercell=True, the QE input for the muon-bearing cell includes
tot_charge=1.0 (positive muon). The without-muon cell is run neutrally to
prevent unphysical charge configurations in the reference calculation.
Convergence criteria¶
Both criteria must pass in the same iteration for the workflow to stop.
Criterion 1: minimum force¶
If the smallest differential force on any host atom is already below the threshold, the cell is large enough in the minimum-force sense. This catches the common case where the muon perturbation falls off quickly (e.g. simple metals).
Criterion 2: exponential decay¶
For each chemical species s with at least mnasf (default 4) atoms:
- Compute distances
d_ifrom the muon to each atom of species s. - Filter to atoms with
|Δf_i| < max_force_thrand|Δf_i| > 0. - Fit
|Δf_i| = A × exp(−τ × d_i)usingscipy.optimize.curve_fit. - Compute the minimum distance for convergence:
d_conv = ln(A / conv_thr) / τ - Pass if
max(d_i) ≥ d_conv.
If the fitting fails (data not exponential) the species is marked as not converged. If the entire fitting step raises an unexpected exception, the workflow exits with code 704.
Context variables¶
| Variable | Meaning |
|---|---|
ctx.n |
Current iteration counter (AiiDA Int, incremented by increment_n_by_one) |
ctx.structure |
Effective unit cell (may differ from inputs.structure if pre-relaxation ran) |
ctx.sup_struc_mu |
Current candidate supercell with muon |
ctx.sup_struc_without_mu |
Current candidate supercell without muon |
ctx.musite |
ArrayData holding the Voronoi fractional coordinate ('Voronoi_site') |
ctx.sc_mat |
ArrayData holding the current SC transformation matrix ('SCmat') |
ctx.traj_out |
Dict {'with_muon': TrajectoryData, 'without_muon': TrajectoryData} from the last pair of SCF calculations |
ctx.conv_thr |
Cached convergence threshold (populated on first call to continue_iter) |
How forces are extracted¶
After each pair of SCF calculations, inspect_run_get_forces reads the
output_trajectory from each PwBaseWorkChain result and stores them in
ctx.traj_out. The trajectory contains the forces (in eV/Å) as its only
time-step. check_if_conv_achieved then reads:
For MLIP jobs via aiida-pythonjob, inspect_run_get_forces wraps the raw
forces list output from the PythonJob into a minimal TrajectoryData-compatible
store so that the same check_if_conv_achieved calcfunction can be reused unchanged.