11"""Validate VASP KPOINTS files or the KSPACING/KGAMMA INCAR settings."""
22
33from __future__ import annotations
4+ from pydantic import Field
5+ from typing import TYPE_CHECKING
46import numpy as np
5- from pymatgen .io .vasp import Kpoints
6-
7- from pymatgen .core import Structure
8- from pymatgen .io .vasp .sets import VaspInputSet
97
108from pymatgen .io .validation .common import BaseValidator
119
10+ if TYPE_CHECKING :
11+ from pymatgen .io .validation .common import VaspFiles
12+
13+
1214class CheckKpointsKspacing (BaseValidator ):
13- """
14- Check that k-point density is sufficiently high and is compatible with lattice symmetry.
15-
16- Parameters
17- -----------
18- reasons : list[str]
19- A list of error strings to update if a check fails. These are higher
20- severity and would deprecate a calculation.
21- warnings : list[str]
22- A list of warning strings to update if a check fails. These are lower
23- severity and would flag a calculation for possible review.
24- valid_input_set: VaspInputSet
25- Valid input set to compare user INCAR parameters to.
26- kpoints : Kpoints or dict
27- Kpoints object or its .as_dict() representation used in the calculation.
28- structure : pymatgen.core.Structure
29- The structure used in the calculation
30- name : str = "Check k-point density"
31- Name of the validator class
32- fast : bool = False
33- Whether to perform quick check.
34- True: stop validation if any check fails.
35- False: perform all checks.
36- defaults : dict
37- Dict of default parameters
38- kpts_tolerance : float
39- Tolerance for evaluating k-point density, as the k-point generation
40- scheme is inconsistent across VASP versions
41- allow_explicit_kpoint_mesh : str | bool
42- Whether to permit explicit generation of k-points (as for a bandstructure calculation).
43- allow_kpoint_shifts : bool
44- Whether to permit shifting the origin of the k-point mesh from Gamma.
45- """
46-
47- reasons : list [str ]
48- warnings : list [str ]
15+ """Check that k-point density is sufficiently high and is compatible with lattice symmetry."""
16+
4917 name : str = "Check k-point density"
50- valid_input_set : VaspInputSet = None
51- kpoints : Kpoints = None
52- structure : Structure = None
53- defaults : dict | None = None
54- kpts_tolerance : float | None = None
55- allow_explicit_kpoint_mesh : str | bool = False
56- allow_kpoint_shifts : bool = False
57-
58- def _get_valid_num_kpts (self ) -> int :
18+ kpts_tolerance : float | None = Field (
19+ None ,
20+ description = "Tolerance for evaluating k-point density, to accommodate different the k-point generation schemes across VASP versions." ,
21+ )
22+ allow_explicit_kpoint_mesh : bool = Field (
23+ False , description = "Whether to permit explicit generation of k-points (as for a bandstructure calculation)."
24+ )
25+ allow_kpoint_shifts : bool = Field (
26+ False , description = "Whether to permit shifting the origin of the k-point mesh from Gamma."
27+ )
28+
29+ def auto_fail (self , vasp_files : VaspFiles , reasons : list [str ], warnings : list [str ]) -> bool :
30+ """Quick stop if actual k-points are missing."""
31+ if vasp_files .kpoints is None :
32+ reasons .append ("Missing actual k-points: please specify an IBZKPT or vasprun.xml in VaspFiles." )
33+ return vasp_files .kpoints is None
34+
35+ def _get_valid_num_kpts (
36+ self ,
37+ vasp_files : VaspFiles ,
38+ ) -> int :
5939 """
6040 Get the minimum permitted number of k-points for a structure according to an input set.
6141
@@ -64,68 +44,71 @@ def _get_valid_num_kpts(self) -> int:
6444 int, the minimum permitted number of k-points, consistent with self.kpts_tolerance
6545 """
6646 # If MP input set specifies KSPACING in the INCAR
67- if ("KSPACING" in self .valid_input_set .incar .keys ()) and (self .valid_input_set .kpoints is None ):
68- valid_kspacing = self .valid_input_set .incar .get ("KSPACING" , self .defaults ["KSPACING" ][ " value" ] )
47+ if ("KSPACING" in vasp_files .valid_input_set .incar .keys ()) and (vasp_files .valid_input_set .kpoints is None ):
48+ valid_kspacing = vasp_files .valid_input_set .incar .get ("KSPACING" , self .vasp_defaults ["KSPACING" ]. value )
6949 # number of kpoints along each of the three lattice vectors
7050 nk = [
71- max (1 , np .ceil (self .structure .lattice .reciprocal_lattice .abc [ik ] / valid_kspacing )) for ik in range (3 )
51+ max (1 , np .ceil (vasp_files .structure .lattice .reciprocal_lattice .abc [ik ] / valid_kspacing ))
52+ for ik in range (3 )
7253 ]
7354 valid_num_kpts = np .prod (nk )
7455 # If MP input set specifies a KPOINTS file
7556 else :
76- valid_num_kpts = self .valid_input_set .kpoints .num_kpts or np .prod (self .valid_input_set .kpoints .kpts [0 ])
57+ valid_num_kpts = vasp_files .valid_input_set .kpoints .num_kpts or np .prod (
58+ vasp_files .valid_input_set .kpoints .kpts [0 ]
59+ )
7760
7861 return int (np .floor (int (valid_num_kpts ) * self .kpts_tolerance ))
7962
80- def _check_user_shifted_mesh (self ) -> None :
63+ def _check_user_shifted_mesh (self , vasp_files : VaspFiles , reasons : list [ str ], warnings : list [ str ] ) -> None :
8164 # Check for user shifts
82- if (not self .allow_kpoint_shifts ) and any (shift_val != 0 for shift_val in self .kpoints .kpts_shift ):
83- self . reasons .append ("INPUT SETTINGS --> KPOINTS: shifting the kpoint mesh is not currently allowed." )
65+ if (not self .allow_kpoint_shifts ) and any (shift_val != 0 for shift_val in vasp_files .kpoints .kpts_shift ):
66+ reasons .append ("INPUT SETTINGS --> KPOINTS: shifting the kpoint mesh is not currently allowed." )
8467
85- def _check_explicit_mesh_permitted (self ) -> None :
68+ def _check_explicit_mesh_permitted (self , vasp_files : VaspFiles , reasons : list [ str ], warnings : list [ str ] ) -> None :
8669 # Check for explicit kpoint meshes
8770
88- if (not self .allow_explicit_kpoint_mesh ) and len (self .kpoints .kpts ) > 1 :
89- self . reasons .append (
71+ if (not self .allow_explicit_kpoint_mesh ) and len (vasp_files .kpoints .kpts ) > 1 :
72+ reasons .append (
9073 "INPUT SETTINGS --> KPOINTS: explicitly defining "
9174 "the k-point mesh is not currently allowed. "
9275 "Automatic k-point generation is required."
9376 )
9477
95- def _check_kpoint_density (self ) -> None :
78+ def _check_kpoint_density (self , vasp_files : VaspFiles , reasons : list [ str ], warnings : list [ str ] ) -> None :
9679 """
9780 Check that k-point density is sufficiently high and is compatible with lattice symmetry.
9881 """
9982
10083 # Check number of kpoints used
101- valid_num_kpts = self ._get_valid_num_kpts ()
84+ valid_num_kpts = self ._get_valid_num_kpts (vasp_files )
10285
10386 cur_num_kpts = max (
104- self .kpoints .num_kpts ,
105- np .prod (self .kpoints .kpts ),
106- len (self .kpoints .kpts ),
87+ vasp_files .kpoints .num_kpts ,
88+ np .prod (vasp_files .kpoints .kpts ),
89+ len (vasp_files .kpoints .kpts ),
10790 )
10891 if cur_num_kpts < valid_num_kpts :
109- self . reasons .append (
92+ reasons .append (
11093 f"INPUT SETTINGS --> KPOINTS or KSPACING: { cur_num_kpts } kpoints were "
11194 f"used, but it should have been at least { valid_num_kpts } ."
11295 )
11396
114- def _check_kpoint_mesh_symmetry (self ) -> None :
97+ def _check_kpoint_mesh_symmetry (self , vasp_files : VaspFiles , reasons : list [ str ], warnings : list [ str ] ) -> None :
11598 # check for valid kpoint mesh (which depends on symmetry of the structure)
11699
117- cur_kpoint_style = self .kpoints .style .name .lower ()
118- is_hexagonal = self .structure .lattice .is_hexagonal ()
119- is_face_centered = self .structure .get_space_group_info ()[0 ][0 ] == "F"
100+ cur_kpoint_style = vasp_files .kpoints .style .name .lower ()
101+ is_hexagonal = vasp_files .structure .lattice .is_hexagonal ()
102+ is_face_centered = vasp_files .structure .get_space_group_info ()[0 ][0 ] == "F"
120103 monkhorst_mesh_is_invalid = is_hexagonal or is_face_centered
121104 if (
122105 cur_kpoint_style == "monkhorst"
123106 and monkhorst_mesh_is_invalid
124- and any (x % 2 == 0 for x in self .kpoints .kpts [0 ])
107+ and any (x % 2 == 0 for x in vasp_files .kpoints .kpts [0 ])
125108 ):
126109 # only allow Monkhorst with all odd number of subdivisions per axis.
127- kx , ky , kz = self .kpoints .kpts [0 ]
128- self . reasons .append (
110+ kx , ky , kz = vasp_files .kpoints .kpts [0 ]
111+ reasons .append (
129112 f"INPUT SETTINGS --> KPOINTS or KGAMMA: ({ kx } x{ ky } x{ kz } ) "
130113 "Monkhorst-Pack kpoint mesh was used."
131114 "To be compatible with the symmetry of the lattice, "
0 commit comments