Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/qe_tools/outputs/parsers/pw.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ def parse(content):
)
_HOMO_RE = re.compile(r"highest occupied level\s*\(ev\):\s*([\-\d.E+]+)")
_TOTAL_ENERGY_RE = re.compile(r"!\s+total energy\s*=\s*([\-\d.E+]+)\s*Ry")
_ALAT_RE = re.compile(r"lattice parameter \(alat\)\s*=\s*([\-\d.E+]+)\s*a\.u\.")

# Captures the `number of k points= N` line plus the following
# `cart. coord. in units 2pi/alat` block of `k( i ) = ( x y z ), wk = w` lines.
# `[^\n]*` after the count tolerates trailing variants (`(tetrahedron method)`,
# `Marzari-Vanderbilt smearing, ...`, or nothing).
_KPOINTS_BLOCK_RE = re.compile(
r"number of k points=\s*(?P<nks>\d+)[^\n]*\n"
r"\s*cart\. coord\. in units 2pi/alat\s*\n"
r"(?P<rows>(?:\s*k\(\s*\d+\s*\)\s*=\s*\(\s*[\-\d.E+]+\s+[\-\d.E+]+\s+[\-\d.E+]+\s*\),\s*wk\s*=\s*[\-\d.E+]+\s*\n)+)"
)
_KPOINT_ROW_RE = re.compile(
r"k\(\s*\d+\s*\)\s*=\s*\(\s*([\-\d.E+]+)\s+([\-\d.E+]+)\s+([\-\d.E+]+)\s*\),"
r"\s*wk\s*=\s*([\-\d.E+]+)"
)


class PwStdoutParser(BaseStdoutParser):
Expand All @@ -85,4 +100,27 @@ def parse(content: str) -> dict:
if energy_matches:
parsed_data["total_energy"] = float(energy_matches[-1])

# k-points: take the last block, since vc-relax reprints after relaxation.
# The stdout values are in 2pi/alat; convert to 1/Å using `alat` (also from
# stdout, in bohr) so downstream Specs match the XML-derived units.
kpoint_blocks = list(_KPOINTS_BLOCK_RE.finditer(content))
alat_match = _ALAT_RE.search(content)
if kpoint_blocks and alat_match:
import math

from qe_tools import CONSTANTS

alat_ang = float(alat_match.group(1)) * CONSTANTS.bohr_to_ang
scale = 2 * math.pi / alat_ang
block = kpoint_blocks[-1]
cartesian = []
weights = []
for row in _KPOINT_ROW_RE.finditer(block.group("rows")):
kx, ky, kz, wk = (float(v) for v in row.groups())
cartesian.append([kx * scale, ky * scale, kz * scale])
weights.append(wk)
parsed_data["number_of_k_points"] = int(block.group("nks"))
parsed_data["k_points_cartesian"] = cartesian
parsed_data["k_points_weights"] = weights

return parsed_data
49 changes: 33 additions & 16 deletions src/qe_tools/outputs/pw.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,15 +272,26 @@ class _PwMapping:
Only available when ``tot_magnetization`` is set in ``SYSTEM``.
"""

number_of_k_points: Annotated[int, Spec("xml.output.band_structure.nks")]
number_of_k_points: Annotated[
int,
Spec(
Coalesce(
"xml.output.band_structure.nks",
"stdout.number_of_k_points",
)
),
]
"""Number of k-points at which the Kohn-Sham states were computed."""

k_points_weights: Annotated[
list,
Spec(
(
"xml.output.band_structure.ks_energies",
[lambda ks: ks["k_point"]["@weight"]],
Coalesce(
(
"xml.output.band_structure.ks_energies",
[lambda ks: ks["k_point"]["@weight"]],
),
"stdout.k_points_weights",
)
),
]
Expand All @@ -292,18 +303,24 @@ class _PwMapping:
k_points_cartesian: Annotated[
list,
Spec(
(
"xml.output",
lambda output: [
[
kp
* 2
* math.pi
/ (output["atomic_structure"]["@alat"] * CONSTANTS.bohr_to_ang)
for kp in ks["k_point"]["$"]
]
for ks in output["band_structure"]["ks_energies"]
],
Coalesce(
(
"xml.output",
lambda output: [
[
kp
* 2
* math.pi
/ (
output["atomic_structure"]["@alat"]
* CONSTANTS.bohr_to_ang
)
for kp in ks["k_point"]["$"]
]
for ks in output["band_structure"]["ks_energies"]
],
),
"stdout.k_points_cartesian",
)
),
Unit("1/angstrom"),
Expand Down
7 changes: 6 additions & 1 deletion tests/outputs/fixtures/pw/default_xml_220603/pw.out
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,12 @@

--- SYMMETRIES REMOVED ---

--- K-POINTS REMOVED ---
number of k points= 4 Marzari-Vanderbilt smearing, width (Ry)= 0.0100
cart. coord. in units 2pi/alat
k( 1) = ( 0.0000000 0.0000000 0.0000000), wk = 0.2500000
k( 2) = ( 0.0000000 0.0000000 -0.6123724), wk = 0.2500000
k( 3) = ( 0.0000000 -0.5773503 0.2041241), wk = 0.7500000
k( 4) = ( 0.0000000 -0.5773503 -0.4082483), wk = 0.7500000

--- MEMORY INFO REMOVED ---

Expand Down
5 changes: 5 additions & 0 deletions tests/outputs/fixtures/pw/default_xml_250521/pw.out
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@
F 7.00 18.99840 F ( 1.00)
Li 3.00 6.94100 Li( 1.00)

number of k points= 2 Marzari-Vanderbilt smearing, width (Ry)= 0.0100
cart. coord. in units 2pi/alat
k( 1) = ( 0.0000000 0.0000000 0.0000000), wk = 1.0000000
k( 2) = ( -0.5000000 0.0000000 0.0000000), wk = 1.0000000

Dense grid: 50733 G-vectors FFT dimensions: ( 45, 45, 45)

Smooth grid: 17941 G-vectors FFT dimensions: ( 36, 36, 36)
Expand Down
39 changes: 39 additions & 0 deletions tests/outputs/test_pw_output.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from pathlib import Path

import numpy as np
import pytest

from qe_tools.outputs.pw import PwOutput
Expand Down Expand Up @@ -99,6 +100,44 @@ def test_total_energy_nscf_clobber():
)


def test_k_points_stdout_xml_agree():
"""Stdout-derived k-points agree with the XML values within stdout's printed precision.

QE prints k-points in `cart. coord. in units 2pi/alat` to 7 digits and `alat` to
5 digits, so cartesian coordinates in 1/Å can only match the XML at ~1e-3.
"""
pw_directory = Path(__file__).parent / "fixtures" / "pw" / "default_xml_211101"

pw_out = PwOutput.from_dir(pw_directory)

assert pw_out.get_output("k_points_weights") == pytest.approx(
pw_out.raw_outputs["stdout"]["k_points_weights"]
)
np.testing.assert_allclose(
pw_out.get_output("k_points_cartesian"),
pw_out.raw_outputs["stdout"]["k_points_cartesian"],
atol=1e-3,
)


def test_k_points_stdout_fallback_without_xml():
"""`PwOutput.from_files(stdout=...)` exposes k-point outputs via the stdout fallback."""
stdout_file = (
Path(__file__).parent / "fixtures" / "pw" / "default_xml_211101" / "pw.out"
)

pw_out = PwOutput.from_files(stdout=stdout_file)

assert "xml" not in pw_out.raw_outputs
assert pw_out.get_output("number_of_k_points") == 2
assert len(pw_out.get_output("k_points_weights")) == pw_out.get_output(
"number_of_k_points"
)
assert len(pw_out.get_output("k_points_cartesian")) == pw_out.get_output(
"number_of_k_points"
)


def test_insulator_homo(robust_data_regression_check):
"""Test stdout-derived `highest_occupied_level` from an insulator SCF."""

Expand Down
11 changes: 11 additions & 0 deletions tests/outputs/test_pw_output/test_default_xml_211101_.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ base_outputs:
raw_outputs:
stdout:
code_version: '7.0'
k_points_cartesian:
- - 0.0
- 0.0
- 0.0
- - 0.8124187117851777
- 0.4690501057329147
- 0.33166847673261757
k_points_weights:
- 1.0
- 1.0
number_of_k_points: 2
total_energy: -22.13271119
volume_bohr3: 275.9279
wall_time_seconds: 1.96
Expand Down
19 changes: 19 additions & 0 deletions tests/outputs/test_pw_output/test_default_xml_220603_.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,25 @@ base_outputs:
raw_outputs:
stdout:
code_version: '7.1'
k_points_cartesian:
- - 0.0
- 0.0
- 0.0
- - 0.0
- 0.0
- -0.9950055926815952
- - 0.0
- -0.9381003739495717
- 0.33166847673261757
- - 0.0
- -0.9381003739495717
- -0.6633371159489775
k_points_weights:
- 0.25
- 0.25
- 0.75
- 0.75
number_of_k_points: 4
total_energy: -22.65373597
volume_bohr3: 275.9279
wall_time_seconds: 2.02
Expand Down
55 changes: 55 additions & 0 deletions tests/outputs/test_pw_output/test_default_xml_230310_.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,61 @@ raw_outputs:
stdout:
code_version: '7.2'
highest_occupied_level: 9.0922
k_points_cartesian:
- - 0.0
- 0.0
- 0.0
- - -0.5577988981584857
- -0.3220453424463573
- 0.11177181862660332
- - 1.1155977963169714
- 0.6440906848927146
- -0.22354363725320664
- - -0.5577988981584857
- 0.3220453424463573
- 0.22354363725320664
- - 1.1155977963169714
- 1.2881813697854292
- -0.11177181862660332
- - 0.5577988981584857
- 0.9661360273390719
- 0.0
- - 1.1155977963169714
- -0.6440906848927146
- -0.44708714779493075
- - 0.0
- 0.0
- 0.33531545587981
- - 1.673396694475457
- 0.9661360273390719
- 0.0
- - 1.1155977963169714
- 0.6440906848927146
- 0.11177181862660332
- - 1.673396694475457
- -0.9661360273390719
- -0.33531545587981
- - 1.1155977963169714
- -1.2881813697854292
- -0.22354363725320664
- - 0.0
- 0.0
- -0.6706307850481373
k_points_weights:
- 0.03125
- 0.1875
- 0.09375
- 0.1875
- 0.375
- 0.1875
- 0.09375
- 0.0625
- 0.1875
- 0.1875
- 0.1875
- 0.1875
- 0.03125
number_of_k_points: 13
total_energy: -396.09758618
volume_bohr3: 217.1091
wall_time_seconds: 100.44
Expand Down
55 changes: 55 additions & 0 deletions tests/outputs/test_pw_output/test_default_xml_240411_.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,61 @@ base_outputs:
raw_outputs:
stdout:
code_version: 7.3.1
k_points_cartesian:
- - 0.0
- 0.0
- 0.0
- - -0.0
- -0.0
- 0.49963377062181
- - 0.0
- 0.0
- -0.9992677037273625
- - -0.0
- 0.47105921720715943
- -0.16654453604602257
- - -0.0
- 0.47105921720715943
- 0.3330892345757875
- - 0.0
- 0.47105921720715943
- -1.165812239773385
- - 0.0
- 0.47105921720715943
- -0.666178469151575
- - 0.0
- -0.9421184344143189
- 0.3330892345757875
- - 0.0
- -0.9421184344143189
- 0.8327230051975975
- - 0.0
- -0.9421184344143189
- -0.666178469151575
- - 0.40794923180574794
- -1.1776480430178984
- 0.16654453604602257
- - 0.40794923180574794
- -1.1776480430178984
- 0.666178469151575
- - 0.40794923180574794
- -1.1776480430178984
- -0.3330892345757875
k_points_weights:
- 0.03125
- 0.0625
- 0.03125
- 0.1875
- 0.1875
- 0.1875
- 0.1875
- 0.09375
- 0.1875
- 0.09375
- 0.375
- 0.1875
- 0.1875
number_of_k_points: 13
total_energy: -22.66279398
volume_bohr3: 272.4124
wall_time_seconds: 2.51
Expand Down
11 changes: 11 additions & 0 deletions tests/outputs/test_pw_output/test_default_xml_250521_.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ base_outputs:
raw_outputs:
stdout:
code_version: '7.5'
k_points_cartesian:
- - 0.0
- 0.0
- 0.0
- - -0.7795511497938687
- 0.0
- 0.0
k_points_weights:
- 1.0
- 1.0
number_of_k_points: 2
total_energy: -252.68718165
volume_bohr3: 441.6841
wall_time_seconds: 2.33
Expand Down
Loading