Skip to content

Commit 49ebc52

Browse files
authored
Fix comment pass in Kpoints constructors (#4131)
* pass comment * add test for gamma centered and other comment * take comment as arg * tweak comment and type
1 parent 42214bd commit 49ebc52

File tree

3 files changed

+114
-43
lines changed

3 files changed

+114
-43
lines changed

src/pymatgen/io/vasp/inputs.py

+88-35
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,11 @@ def style(self, style: str | KpointsSupportedModes) -> None:
12961296
self._style = style
12971297

12981298
@classmethod
1299-
def automatic(cls, subdivisions: int) -> Self:
1299+
def automatic(
1300+
cls,
1301+
subdivisions: int,
1302+
comment: str = "Fully automatic kpoint scheme",
1303+
) -> Self:
13001304
"""
13011305
Constructor for a fully automatic Kpoint grid, with
13021306
Gamma-centered grids and the number of subdivisions
@@ -1306,14 +1310,15 @@ def automatic(cls, subdivisions: int) -> Self:
13061310
Args:
13071311
subdivisions (int): Number of subdivisions along
13081312
each reciprocal lattice vector.
1313+
comment (str): Comment in Kpoints.
13091314
13101315
Returns:
13111316
Kpoints
13121317
"""
13131318
warnings.warn("Please use INCAR KSPACING tag.", DeprecationWarning, stacklevel=2)
13141319

13151320
return cls(
1316-
"Fully automatic kpoint scheme",
1321+
comment,
13171322
0,
13181323
style=cls.supported_modes.Automatic,
13191324
kpts=[
@@ -1322,49 +1327,67 @@ def automatic(cls, subdivisions: int) -> Self:
13221327
)
13231328

13241329
@classmethod
1325-
def gamma_automatic(cls, kpts: Tuple3Ints = (1, 1, 1), shift: Vector3D = (0, 0, 0)) -> Self:
1330+
def gamma_automatic(
1331+
cls,
1332+
kpts: Tuple3Ints = (1, 1, 1),
1333+
shift: Vector3D = (0, 0, 0),
1334+
comment: str = "Automatic kpoint scheme",
1335+
) -> Self:
13261336
"""
13271337
Construct an automatic Gamma-centered Kpoint grid.
13281338
13291339
Args:
13301340
kpts: Subdivisions N_1, N_2 and N_3 along reciprocal lattice
13311341
vectors. Defaults to (1, 1, 1)
13321342
shift: Shift to be applied to the kpoints. Defaults to (0, 0, 0).
1343+
comment (str): Comment in Kpoints.
13331344
13341345
Returns:
13351346
Kpoints
13361347
"""
13371348
return cls(
1338-
"Automatic kpoint scheme",
1349+
comment,
13391350
0,
13401351
cls.supported_modes.Gamma,
13411352
kpts=[kpts],
13421353
kpts_shift=shift,
13431354
)
13441355

13451356
@classmethod
1346-
def monkhorst_automatic(cls, kpts: Tuple3Ints = (2, 2, 2), shift: Vector3D = (0, 0, 0)) -> Self:
1357+
def monkhorst_automatic(
1358+
cls,
1359+
kpts: Tuple3Ints = (2, 2, 2),
1360+
shift: Vector3D = (0, 0, 0),
1361+
comment: str = "Automatic kpoint scheme",
1362+
) -> Self:
13471363
"""
13481364
Construct an automatic Monkhorst-Pack Kpoint grid.
13491365
13501366
Args:
13511367
kpts: Subdivisions N_1, N_2, N_3 along reciprocal lattice
13521368
vectors. Defaults to (2, 2, 2)
13531369
shift: Shift to be applied to the kpoints. Defaults to (0, 0, 0).
1370+
comment (str): Comment in Kpoints.
13541371
13551372
Returns:
13561373
Kpoints
13571374
"""
13581375
return cls(
1359-
"Automatic kpoint scheme",
1376+
comment,
13601377
0,
13611378
cls.supported_modes.Monkhorst,
13621379
kpts=[kpts],
13631380
kpts_shift=shift,
13641381
)
13651382

13661383
@classmethod
1367-
def automatic_density(cls, structure: Structure, kppa: float, force_gamma: bool = False) -> Self:
1384+
def automatic_density(
1385+
cls,
1386+
structure: Structure,
1387+
kppa: float,
1388+
force_gamma: bool = False,
1389+
comment: str | None = None,
1390+
) -> Self:
13681391
"""Get an automatic Kpoints object based on a structure and a kpoint
13691392
density. Uses Gamma centered meshes for hexagonal cells and face-centered cells,
13701393
Monkhorst-Pack grids otherwise.
@@ -1374,15 +1397,18 @@ def automatic_density(cls, structure: Structure, kppa: float, force_gamma: bool
13741397
reciprocal lattice vector proportional to its length.
13751398
13761399
Args:
1377-
structure (Structure): Input structure
1378-
kppa (float): Grid density
1400+
structure (Structure): Input structure.
1401+
kppa (float): Grid density.
13791402
force_gamma (bool): Force a gamma centered mesh (default is to
1380-
use gamma only for hexagonal cells or odd meshes)
1403+
use gamma only for hexagonal cells or odd meshes).
1404+
comment (str): Comment in Kpoints.
13811405
13821406
Returns:
13831407
Kpoints
13841408
"""
1385-
comment = f"pymatgen with grid density = {kppa:.0f} / number of atoms"
1409+
if comment is None:
1410+
comment = f"pymatgen with grid density = {kppa:.0f} / number of atoms"
1411+
13861412
if math.fabs((math.floor(kppa ** (1 / 3) + 0.5)) ** 3 - kppa) < 1:
13871413
kppa += kppa * 0.01
13881414
lattice = structure.lattice
@@ -1409,7 +1435,12 @@ def automatic_density(cls, structure: Structure, kppa: float, force_gamma: bool
14091435
)
14101436

14111437
@classmethod
1412-
def automatic_gamma_density(cls, structure: Structure, kppa: float) -> Self:
1438+
def automatic_gamma_density(
1439+
cls,
1440+
structure: Structure,
1441+
kppa: float,
1442+
comment: str | None = None,
1443+
) -> Self:
14131444
"""Get an automatic Kpoints object based on a structure and a kpoint
14141445
density. Uses Gamma centered meshes always. For GW.
14151446
@@ -1418,9 +1449,13 @@ def automatic_gamma_density(cls, structure: Structure, kppa: float) -> Self:
14181449
reciprocal lattice vector proportional to its length.
14191450
14201451
Args:
1421-
structure: Input structure
1422-
kppa: Grid density
1452+
structure (Structure): Input structure
1453+
kppa (float): Grid density
1454+
comment (str): Comment in Kpoints.
14231455
"""
1456+
if comment is None:
1457+
comment = f"pymatgen with grid density = {kppa:.0f} / number of atoms"
1458+
14241459
lattice = structure.lattice
14251460
a, b, c = lattice.abc
14261461
n_grid = kppa / len(structure)
@@ -1436,8 +1471,6 @@ def automatic_gamma_density(cls, structure: Structure, kppa: float) -> Self:
14361471

14371472
style = cls.supported_modes.Gamma
14381473

1439-
comment = f"pymatgen with grid density = {kppa:.0f} / number of atoms"
1440-
14411474
n_kpts = 0
14421475
return cls(
14431476
comment,
@@ -1448,31 +1481,39 @@ def automatic_gamma_density(cls, structure: Structure, kppa: float) -> Self:
14481481
)
14491482

14501483
@classmethod
1451-
def automatic_density_by_vol(cls, structure: Structure, kppvol: int, force_gamma: bool = False) -> Self:
1484+
def automatic_density_by_vol(
1485+
cls,
1486+
structure: Structure,
1487+
kppvol: int,
1488+
force_gamma: bool = False,
1489+
comment: str | None = None,
1490+
) -> Self:
14521491
"""Get an automatic Kpoints object based on a structure and a kpoint
14531492
density per inverse Angstrom^3 of reciprocal cell.
14541493
14551494
Algorithm:
14561495
Same as automatic_density()
14571496
14581497
Args:
1459-
structure (Structure): Input structure
1460-
kppvol (int): Grid density per Angstrom^(-3) of reciprocal cell
1461-
force_gamma (bool): Force a gamma centered mesh
1498+
structure (Structure): Input structure.
1499+
kppvol (int): Grid density per Angstrom^(-3) of reciprocal cell.
1500+
force_gamma (bool): Force a gamma centered mesh.
1501+
comment (str): Comment in Kpoints.
14621502
14631503
Returns:
14641504
Kpoints
14651505
"""
14661506
vol = structure.lattice.reciprocal_lattice.volume
14671507
kppa = kppvol * vol * len(structure)
1468-
return cls.automatic_density(structure, kppa, force_gamma=force_gamma)
1508+
return cls.automatic_density(structure, kppa, force_gamma=force_gamma, comment=comment)
14691509

14701510
@classmethod
14711511
def automatic_density_by_lengths(
14721512
cls,
14731513
structure: Structure,
14741514
length_densities: Sequence[float],
14751515
force_gamma: bool = False,
1516+
comment: str | None = None,
14761517
) -> Self:
14771518
"""Get an automatic Kpoints object based on a structure and a k-point
14781519
density normalized by lattice constants.
@@ -1483,18 +1524,20 @@ def automatic_density_by_lengths(
14831524
have k-points of 50/a x 50/b x 1/c.
14841525
14851526
Args:
1486-
structure (Structure): Input structure
1487-
length_densities (list[float]): Defines the density of k-points in each
1527+
structure (Structure): Input structure.
1528+
length_densities (list[float]): Defines the density of k-points in each.
14881529
dimension, e.g. [50.0, 50.0, 1.0].
1489-
force_gamma (bool): Force a gamma centered mesh
1530+
force_gamma (bool): Force a gamma centered mesh.
1531+
comment (str): Comment in Kpoints.
14901532
14911533
Returns:
14921534
Kpoints
14931535
"""
14941536
if len(length_densities) != 3:
14951537
raise ValueError(f"The dimensions of length_densities must be 3, not {len(length_densities)}")
14961538

1497-
comment: str = f"k-point density of {length_densities}/[a, b, c]"
1539+
if comment is None:
1540+
comment = f"k-point density of {length_densities}/[a, b, c]"
14981541

14991542
lattice = structure.lattice
15001543

@@ -1520,16 +1563,22 @@ def automatic_density_by_lengths(
15201563
)
15211564

15221565
@classmethod
1523-
def automatic_linemode(cls, divisions: int, ibz: HighSymmKpath) -> Self:
1566+
def automatic_linemode(
1567+
cls,
1568+
divisions: int,
1569+
ibz: HighSymmKpath,
1570+
comment: str = "Line_mode KPOINTS file",
1571+
) -> Self:
15241572
"""
15251573
Convenient static constructor for a KPOINTS in mode line_mode.
15261574
gamma centered Monkhorst-Pack grids and the number of subdivisions
15271575
along each reciprocal lattice vector determined by the scheme in the
15281576
VASP manual.
15291577
15301578
Args:
1531-
divisions: Parameter determining the number of k-points along each high symmetry line.
1532-
ibz: HighSymmKpath object (pymatgen.symmetry.bandstructure)
1579+
divisions (int): Parameter determining the number of k-points along each high symmetry line.
1580+
ibz (HighSymmKpath): HighSymmKpath object (pymatgen.symmetry.bandstructure).
1581+
comment (str): Comment in Kpoints.
15331582
15341583
Returns:
15351584
Kpoints object
@@ -1547,7 +1596,7 @@ def automatic_linemode(cls, divisions: int, ibz: HighSymmKpath) -> Self:
15471596
labels.append(path[-1])
15481597

15491598
return cls(
1550-
"Line_mode KPOINTS file",
1599+
comment,
15511600
style=cls.supported_modes.Line_mode,
15521601
coord_type="Reciprocal",
15531602
kpts=kpoints,
@@ -1591,7 +1640,7 @@ def from_str(cls, string: str) -> Self:
15911640

15921641
# Fully automatic KPOINTS
15931642
if style == "a":
1594-
return cls.automatic(int(lines[3].split()[0].strip()))
1643+
return cls.automatic(int(lines[3].split()[0].strip()), comment=comment)
15951644

15961645
coord_pattern = re.compile(r"^\s*([\d+.\-Ee]+)\s+([\d+.\-Ee]+)\s+([\d+.\-Ee]+)")
15971646

@@ -1614,7 +1663,11 @@ def from_str(cls, string: str) -> Self:
16141663

16151664
kpts_shift = cast(Vector3D, tuple(_kpts_shift))
16161665

1617-
return cls.gamma_automatic(kpt, kpts_shift) if style == "g" else cls.monkhorst_automatic(kpt, kpts_shift)
1666+
return (
1667+
cls.gamma_automatic(kpt, kpts_shift, comment=comment)
1668+
if style == "g"
1669+
else cls.monkhorst_automatic(kpt, kpts_shift, comment=comment)
1670+
)
16181671

16191672
# Automatic kpoints with basis
16201673
if num_kpts <= 0:
@@ -1709,7 +1762,7 @@ def write_file(self, filename: PathLike) -> None:
17091762
with zopen(filename, mode="wt") as file:
17101763
file.write(str(self))
17111764

1712-
def as_dict(self) -> dict:
1765+
def as_dict(self) -> dict[str, Any]:
17131766
"""MSONable dict."""
17141767
dct = {
17151768
"@module": type(self).__module__,
@@ -1734,7 +1787,7 @@ def as_dict(self) -> dict:
17341787
return dct
17351788

17361789
@classmethod
1737-
def from_dict(cls, dct: dict) -> Self:
1790+
def from_dict(cls, dct: dict[str, Any]) -> Self:
17381791
"""
17391792
Args:
17401793
dct (dict): Dict representation.
@@ -1803,9 +1856,9 @@ class OrbitalDescription(NamedTuple):
18031856

18041857

18051858
# Hashes computed from the full POTCAR file contents by pymatgen (not 1st-party VASP hashes)
1806-
PYMATGEN_POTCAR_HASHES = loadfn(f"{MODULE_DIR}/vasp_potcar_pymatgen_hashes.json")
1859+
PYMATGEN_POTCAR_HASHES: dict = loadfn(f"{MODULE_DIR}/vasp_potcar_pymatgen_hashes.json")
18071860
# Written to some newer POTCARs by VASP
1808-
VASP_POTCAR_HASHES = loadfn(f"{MODULE_DIR}/vasp_potcar_file_hashes.json")
1861+
VASP_POTCAR_HASHES: dict = loadfn(f"{MODULE_DIR}/vasp_potcar_file_hashes.json")
18091862
POTCAR_STATS_PATH: str = os.path.join(MODULE_DIR, "potcar-summary-stats.json.bz2")
18101863

18111864

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Gamma centered mesh
2+
0
3+
Gamma
4+
4 4 4

tests/io/vasp/test_inputs.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -899,23 +899,25 @@ def test_check_params(self):
899899

900900

901901
class TestKpointsSupportedModes:
902-
def test_from_str(self):
903-
test_cases = "Automatic Gamma Monkhorst Line_mode Cartesian Reciprocal".split()
904-
for input_str in test_cases:
905-
expected = getattr(KpointsSupportedModes, input_str)
906-
assert KpointsSupportedModes.from_str(input_str) == expected
907-
assert KpointsSupportedModes.from_str(input_str.lower()) == expected # case insensitive
908-
assert KpointsSupportedModes.from_str(input_str[0]) == expected # only first letter matters
909-
902+
@pytest.mark.parametrize("mode", ["Automatic", "Gamma", "Monkhorst", "Line_mode", "Cartesian", "Reciprocal"])
903+
def test_from_str(self, mode):
904+
expected = getattr(KpointsSupportedModes, mode)
905+
assert KpointsSupportedModes.from_str(mode) == expected
906+
assert KpointsSupportedModes.from_str(mode.lower()) == expected # case insensitive
907+
assert KpointsSupportedModes.from_str(mode[0]) == expected # only first letter matters
908+
909+
def test_invalid_mode(self):
910910
mode = "InvalidMode"
911911
with pytest.raises(ValueError, match=f"Invalid Kpoint {mode=}"):
912912
KpointsSupportedModes.from_str(mode)
913913

914914

915915
class TestKpoints:
916916
def test_init(self):
917+
# Automatic KPOINT grid
917918
filepath = f"{VASP_IN_DIR}/KPOINTS_auto"
918919
kpoints = Kpoints.from_file(filepath)
920+
assert kpoints.comment == "Automatic mesh"
919921
assert kpoints.kpts == [(10,)], "Wrong kpoint lattice read"
920922
filepath = f"{VASP_IN_DIR}/KPOINTS_cartesian"
921923
kpoints = Kpoints.from_file(filepath)
@@ -926,17 +928,27 @@ def test_init(self):
926928
], "Wrong kpoint lattice read"
927929
assert kpoints.kpts_shift == (0.5, 0.5, 0.5)
928930

931+
# Gamma-centered Kpoint grid
932+
filepath = f"{VASP_IN_DIR}/KPOINTS_gamma"
933+
kpoints = Kpoints.from_file(filepath)
934+
assert kpoints.comment == "Gamma centered mesh"
935+
assert kpoints.kpts == [(4, 4, 4)]
936+
937+
# Monkhorst-Pack Kpoint grid
929938
filepath = f"{VASP_IN_DIR}/KPOINTS"
930939
kpoints = Kpoints.from_file(filepath)
940+
assert kpoints.comment == "Auto-generated kpoints file: VaspIO.writeKPOINTS(XX,XX,500)"
931941
self.kpoints = kpoints
932942
assert kpoints.kpts == [(2, 4, 6)]
933943

944+
# Line mode
934945
filepath = f"{VASP_IN_DIR}/KPOINTS_band"
935946
kpoints = Kpoints.from_file(filepath)
936947
assert kpoints.labels is not None
937948
assert kpoints.style == Kpoints.supported_modes.Line_mode
938949
assert str(kpoints).split("\n")[3] == "Reciprocal"
939950

951+
# Explicit K-point mode
940952
filepath = f"{VASP_IN_DIR}/KPOINTS_explicit"
941953
kpoints = Kpoints.from_file(filepath)
942954
assert kpoints.kpts_weights is not None
@@ -949,8 +961,10 @@ def test_init(self):
949961
0.5 0.5 0.5 4 None"""
950962
assert str(kpoints).strip() == expected_kpt_str
951963

964+
# Explicit tetrahedra method
952965
filepath = f"{VASP_IN_DIR}/KPOINTS_explicit_tet"
953966
kpoints = Kpoints.from_file(filepath)
967+
assert kpoints.comment == "Example file"
954968
assert kpoints.tet_connections == [(6, [1, 2, 3, 4])]
955969

956970
def test_property_kpts(self):

0 commit comments

Comments
 (0)