Skip to content

QE SCF k-point convergence

These calculations determine the smallest gamma-inclusive k-point mesh at which the total energy of a structure is stable.

Published as two records, from one body of calculations: 52713-55d86, the converged mesh per structure, and mcpnq-g1j55, the full per-calculation output behind the paper.

What was calculated

Setting Value
Code Quantum ESPRESSO pw.x
Task No-spin SCF
Functional PBEsol
Pseudopotentials SSSP, for both published records
Schedule The 1-based gamma-inclusive k-mesh ladder, floor min_k_distance = 0.03 Å⁻¹
Smearing degauss = 0.01 Ry
Criterion Energy only, per atom, over a tail of at least three meshes

Reproduce it

Running these yourself means submitting one SCF per rung of a structure's ladder and labelling the result. You need AiiDA with a configured pw.x code and a pseudopotential family.

uv sync --extra aiida

Submit the sweep

from aiida import load_profile
from pymatgen.core import Structure

from goldilocks_data.aiida import AiidaScfConfig, submit_scf_sweeps
from goldilocks_data.kmesh import kindex_points
from goldilocks_data.sweeps import ScfSweepSpec

load_profile()

config = AiidaScfConfig(
    code_label="pw-7.5@your-computer",          # your AiiDA code
    pseudo_family_label="SSSP/1.3/PBEsol/efficiency",
    group_label="my-kpoint-sweep",              # every node lands in this group
    degauss_ry=0.01,
)

structure = Structure.from_file("100115.cif")

summary = submit_scf_sweeps(
    [
        ScfSweepSpec(
            source_db_id="100115",
            structure=structure,
            points=kindex_points(structure, 1, 12),   # rungs 1 to 12, 1-based
        )
    ],
    config,
)
print(summary.submitted)

kindex_points builds the rungs; each carries its mesh, k_pra and n_reduced_kpoints as node extras, so a finished group can be analysed without re-deriving anything. Submission is idempotent: a rung already in the group is skipped, so re-running extends a sweep rather than duplicating it.

Rung 12 is an arbitrary stopping point. Extend until the tail converges, and remember that the ladder is bounded by min_k_distance, not by a k-point count — see k-mesh quantities.

Label the convergence

Collect kindex, energy and energy_per_atom for each finished calculation into a DataFrame, then:

from goldilocks_data.analysis.convergence import ConvergenceThresholds, build_convergence_table

table = build_convergence_table(
    records,
    ConvergenceThresholds(medium_mev_per_atom=5.0, well_mev_per_atom=3.0, ultra_mev_per_atom=1.0),
)

The published records used 5 / 3 / 1 meV per atom; the class defaults are 10 / 5 / 1, so pass them explicitly. See convergence criteria.

The criterion is the whole tail, not three points

52713-55d86's README describes the converged mesh as the first of three consecutive meshes agreeing within 1 meV per atom. The code is stricter than that wording: the oscillation is max - min over every rung from that point to the end of the sweep, with at least three rungs required. A reimplementation that stops after checking three will label some structures converged that these calculations did not.

The record is not being amended. A published record keeps the wording it was published with, so that a citation stays fixed; this page is where the two are reconciled, and the labels in the record are the ones the code above produces.

Clean up as you go

A k-mesh sweep leaves a lot of remote scratch behind. The CLI keeps the inputs, outputs, XML and scheduler logs and deletes the rest:

goldilocks-data cleanup-qe-scf --group-label my-kpoint-sweep            # dry run
goldilocks-data cleanup-qe-scf --group-label my-kpoint-sweep --execute

Explore the published data

Reproduction is not required to use the records. k_index is reproducible from the structure alone, with no calculation at all, provided you use the same floor:

from pymatgen.core import Structure
from goldilocks_data.kmesh import build_gamma_kmesh_entries

entries = build_gamma_kmesh_entries(Structure.from_file("100115.cif"))
print([(e.kindex, e.mesh) for e in entries[:5]])

That is enough to turn a published k_index back into a mesh, or to check one. A k_index computed under a different floor is comparable only where the two ranges overlap.