44import numpy as np
55from pydantic import Field
66
7- from pymatgen .io .vasp import Incar
8-
9- from pymatgen .io .validation .common import BaseValidator , VaspFiles
7+ from pymatgen .io .validation .common import SETTINGS , BaseValidator , VaspFiles
108from pymatgen .io .validation .vasp_defaults import InputCategory , VaspParam
119
1210from typing import TYPE_CHECKING
1311
1412if TYPE_CHECKING :
13+ from typing import Any
1514 from pymatgen .io .validation .common import VaspFiles
1615
1716# TODO: fix ISIF getting overwritten by MP input set.
1817
19-
2018class CheckIncar (BaseValidator ):
2119 """
2220 Check calculation parameters related to INCAR input tags.
@@ -37,7 +35,8 @@ class CheckIncar(BaseValidator):
3735
3836 name : str = "Check INCAR tags"
3937 fft_grid_tolerance : float | None = Field (
40- None , description = "Tolerance for determining sufficient density of FFT grid."
38+ SETTINGS .VASP_FFT_GRID_TOLERANCE ,
39+ description = "Tolerance for determining sufficient density of FFT grid."
4140 )
4241 bandgap_tol : float = Field (1.0e-4 , description = "Tolerance for assuming a material has no gap." )
4342
@@ -70,11 +69,10 @@ def check(self, vasp_files: VaspFiles, reasons: list[str], warnings: list[str])
7069 if self .fast and len (reasons ) > 0 :
7170 # fast check: stop checking whenever a single check fails
7271 break
73-
7472 resp = vasp_param .check (user_incar_params [vasp_param .name ], valid_incar_params [vasp_param .name ])
7573 msgs [vasp_param .severity ].extend (resp .get (vasp_param .severity , []))
7674
77- def update_parameters_and_defaults (self , vasp_files : VaspFiles ) -> tuple [Incar , Incar ]:
75+ def update_parameters_and_defaults (self , vasp_files : VaspFiles ) -> tuple [dict [ str , Any ], dict [ str , Any ] ]:
7876 """Update a set of parameters according to supplied rules and defaults.
7977
8078 While many of the parameters in VASP need only a simple check to determine
@@ -299,10 +297,19 @@ def _update_hybrid_params(self, user_incar: dict, ref_incar: dict, vasp_files: V
299297 def _update_fft_params (self , user_incar : dict , ref_incar : dict , vasp_files : VaspFiles ) -> None :
300298 """Update ENCUT and parameters related to the FFT grid."""
301299
300+ # ensure that ENCUT is appropriately updated
301+ user_incar ["ENMAX" ] = user_incar .get (
302+ "ENCUT" ,
303+ getattr (vasp_files .vasprun ,"parameters" ,{}).get ("ENMAX" )
304+ )
305+
306+ ref_incar ["ENMAX" ] = vasp_files .valid_input_set .incar .get ("ENCUT" , self .vasp_defaults ["ENMAX" ])
307+
302308 grid_keys = {"NGX" , "NGXF" , "NGY" , "NGYF" , "NGZ" , "NGZF" }
303309 # NGX/Y/Z and NGXF/YF/ZF. Not checked if not in INCAR file (as this means the VASP default was used).
304310 if any (i for i in grid_keys if i in user_incar .keys ()):
305- ref_incar ["ENMAX" ] = max (user_incar ["ENMAX" ], ref_incar ["ENMAX" ])
311+ enmaxs = [user_incar ["ENMAX" ], ref_incar ["ENMAX" ]]
312+ ref_incar ["ENMAX" ] = max ([v for v in enmaxs if v < float ("inf" )])
306313
307314 (
308315 [
@@ -315,10 +322,10 @@ def _update_fft_params(self, user_incar: dict, ref_incar: dict, vasp_files: Vasp
315322 ref_incar ["NGYF" ],
316323 ref_incar ["NGZF" ],
317324 ],
318- ) = vasp_files .valid_input_set .calculate_ng (custom_encut = ref_incar ["ENMAX" ])
325+ ) = vasp_files .valid_input_set ._calculate_ng (custom_encut = ref_incar ["ENMAX" ])
319326
320327 for key in grid_keys :
321- ref_incar [key ] = int (ref_incar [key ] * self ._fft_grid_tolerance )
328+ ref_incar [key ] = int (ref_incar [key ] * self .fft_grid_tolerance )
322329
323330 self .vasp_defaults [key ] = VaspParam (
324331 name = key ,
@@ -379,7 +386,7 @@ def _update_smearing_params(self, user_incar: dict, ref_incar: dict, vasp_files:
379386
380387 This is based on the final bandgap obtained in the calc.
381388 """
382- if vasp_files .bandgap :
389+ if vasp_files .bandgap is not None :
383390
384391 smearing_comment = (
385392 f"This is flagged as incorrect because this calculation had a bandgap of { round (vasp_files .bandgap ,3 )} "
@@ -405,8 +412,9 @@ def _update_smearing_params(self, user_incar: dict, ref_incar: dict, vasp_files:
405412 for key in ["ISMEAR" , "SIGMA" ]:
406413 self .vasp_defaults [key ].comment = smearing_comment
407414
408- else :
415+ if user_incar [ "ISMEAR" ] not in [ - 5 , - 4 , - 2 ] :
409416 self .vasp_defaults ["SIGMA" ].operation = "<="
417+
410418 else :
411419 # These are generally applicable in all cases. Loosen check to warning.
412420 ref_incar ["ISMEAR" ] = [- 1 , 0 ]
@@ -418,7 +426,7 @@ def _update_smearing_params(self, user_incar: dict, ref_incar: dict, vasp_files:
418426 "may lead to significant errors in forces. To enable this check, "
419427 "supply a vasprun.xml file."
420428 )
421- self .vasp_defaults ["ISMEAR" ].severity = "warning"
429+ self .vasp_defaults ["ISMEAR" ].severity = "warning"
422430
423431 # Also check if SIGMA is too large according to the VASP wiki,
424432 # which occurs when the entropy term in the energy is greater than 1 meV/atom.
@@ -485,7 +493,7 @@ def _update_electronic_params(self, user_incar: dict, ref_incar: dict, vasp_file
485493
486494 # ENAUG. Should only be checked for calculations where the relevant MP input set specifies ENAUG.
487495 # In that case, ENAUG should be the same or greater than in valid_input_set.
488- if ref_incar .get ("ENAUG" ):
496+ if ref_incar .get ("ENAUG" ) < float ( "inf" ) :
489497 self .vasp_defaults ["ENAUG" ].operation = ">="
490498
491499 # IALGO.
@@ -504,12 +512,18 @@ def _update_electronic_params(self, user_incar: dict, ref_incar: dict, vasp_file
504512 f"NELECT should be set to { nelect + user_incar ['NELECT' ]} instead."
505513 )
506514 except Exception :
507- self .vasp_defaults ["NELECT" ].operation = "auto fail"
508- self .vasp_defaults ["NELECT" ].alias = "NELECT / POTCAR"
509- self .vasp_defaults ["NELECT" ].comment = (
510- "sIssue checking whether NELECT was changed to make "
511- "the structure have a non-zero charge. This is likely due to the "
512- "directory not having a POTCAR file."
515+ self .vasp_defaults ["NELECT" ] = VaspParam (
516+ name = "NELECT" ,
517+ value = None ,
518+ tag = "electronic" ,
519+ operation = "auto fail" ,
520+ severity = "warning" ,
521+ alias = "NELECT / POTCAR" ,
522+ comment = (
523+ "Issue checking whether NELECT was changed to make "
524+ "the structure have a non-zero charge. This is likely due to the "
525+ "directory not having a POTCAR file."
526+ )
513527 )
514528
515529 # NBANDS.
@@ -535,7 +549,10 @@ def _update_ionic_params(self, user_incar: dict, ref_incar: dict, vasp_files: Va
535549
536550 # IBRION.
537551 ref_incar ["IBRION" ] = [- 1 , 1 , 2 ]
538- if (inp_set_ibrion := vasp_files .user_input .incar .get ("IBRION" )) and inp_set_ibrion not in ref_incar ["IBRION" ]:
552+ if (
553+ (inp_set_ibrion := vasp_files .valid_input_set .incar .get ("IBRION" ))
554+ and inp_set_ibrion not in ref_incar ["IBRION" ]
555+ ):
539556 ref_incar ["IBRION" ].append (inp_set_ibrion )
540557
541558 ionic_steps = []
0 commit comments