diff --git a/src/pymatgen/io/vasp/inputs.py b/src/pymatgen/io/vasp/inputs.py index 169f74936a1..edb58fad733 100644 --- a/src/pymatgen/io/vasp/inputs.py +++ b/src/pymatgen/io/vasp/inputs.py @@ -14,6 +14,7 @@ import re import subprocess import warnings +from collections import Counter from enum import Enum, unique from glob import glob from hashlib import sha256 @@ -1022,6 +1023,13 @@ def check_params(self) -> None: If a tag doesn't exist, calculation will still run, however VASP will ignore the tag and set it as default without letting you know. """ + # Check for duplicate + key_counts = Counter(key.upper() for key in self) + duplicates = [key for key, count in key_counts.items() if count > 1] + + if duplicates: + warnings.warn(f"Duplicate keys found: {', '.join(duplicates)}", BadIncarWarning, stacklevel=2) + # Load INCAR tag/value check reference file with open(os.path.join(MODULE_DIR, "incar_parameters.json"), encoding="utf-8") as json_file: incar_params = json.loads(json_file.read()) diff --git a/tests/io/vasp/test_inputs.py b/tests/io/vasp/test_inputs.py index 96cc6485918..dc88fd1259d 100644 --- a/tests/io/vasp/test_inputs.py +++ b/tests/io/vasp/test_inputs.py @@ -773,25 +773,25 @@ def test_proc_types(self): def test_check_params(self): # Triggers warnings when running into invalid parameters + incar = Incar( + { + "ADDGRID": True, + "ALGO": "Normal", + "AMIN": 0.01, + "ICHARG": 1, + "MAGMOM": [1, 2, 4, 5], + "ENCUT": 500, # make sure float key is casted + "GGA": "PS", # test string case insensitivity + "LREAL": True, # special case: Union type + "NBAND": 250, # typo in tag + "METAGGA": "SCAM", # typo in value + "EDIFF": 5 + 1j, # value should be a float + "ISIF": 9, # value out of range + "LASPH": 5, # value should be bool + "PHON_TLIST": "is_a_str", # value should be a list + } + ) with pytest.warns(BadIncarWarning) as record: - incar = Incar( - { - "ADDGRID": True, - "ALGO": "Normal", - "AMIN": 0.01, - "ICHARG": 1, - "MAGMOM": [1, 2, 4, 5], - "ENCUT": 500, # make sure float key is casted - "GGA": "PS", # test string case insensitivity - "LREAL": True, # special case: Union type - "NBAND": 250, # typo in tag - "METAGGA": "SCAM", # typo in value - "EDIFF": 5 + 1j, # value should be a float - "ISIF": 9, # value out of range - "LASPH": 5, # value should be bool - "PHON_TLIST": "is_a_str", # value should be a list - } - ) incar.check_params() assert record[0].message.args[0] == "Cannot find NBAND in the list of INCAR tags" @@ -801,6 +801,18 @@ def test_check_params(self): assert record[4].message.args[0] == "LASPH: 5 is not a bool" assert record[5].message.args[0] == "PHON_TLIST: is_a_str is not a list" + def test_check_params_check_duplicate(self): + incar = Incar( + { + "ENCUT": 500, + "encut": 400, + } + ) + with pytest.warns(BadIncarWarning) as record: + incar.check_params() + + assert record[0].message.args[0] == "Duplicate keys found: ENCUT" + class TestKpointsSupportedModes: def test_from_str(self):