Repository: Quantum-Accelerators/quacc
Pull request #2250 (2024-06-13 18:26:30)

Improve VASP logging
1. Report which settings were actually changed.
2. Remove extraneous "Copilot:" from log.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index adf0bb38..e1a40672 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.9.4.]
+
+### Added
+
+- VASP: Added a new logging statement to specifically highlight which parameters were swapped, not just which recommendations were made
+
+### Changed
+
+- VASP: Removed EFERMI="midgap" co-pilot swap since it does not influence the results and prevents VASP 5.x compatability
+
 ## [0.9.3]
 
 ### Changed
diff --git a/src/quacc/calculators/vasp/params.py b/src/quacc/calculators/vasp/params.py
index 98db0965..fda16286 100644
--- a/src/quacc/calculators/vasp/params.py
+++ b/src/quacc/calculators/vasp/params.py
@@ -12,6 +12,7 @@ from monty.dev import requires
 from pymatgen.io.ase import AseAtomsAdaptor
 
 from quacc.atoms.core import check_is_metal
+from quacc.utils.dicts import sort_dict
 from quacc.utils.kpts import convert_pmg_kpts
 
 has_atomate2 = bool(find_spec("atomate2"))
@@ -63,12 +64,12 @@ def get_param_swaps(
     if (
         not calc.int_params["lmaxmix"] or calc.int_params["lmaxmix"] < 6
     ) and max_Z > 56:
-        logger.info("Copilot: Recommending LMAXMIX = 6 because you have f electrons.")
+        logger.info("Recommending LMAXMIX = 6 because you have f electrons.")
         calc.set(lmaxmix=6)
     elif (
         not calc.int_params["lmaxmix"] or calc.int_params["lmaxmix"] < 4
     ) and max_Z > 20:
-        logger.info("Copilot: Recommending LMAXMIX = 4 because you have d electrons.")
+        logger.info("Recommending LMAXMIX = 4 because you have d electrons.")
         calc.set(lmaxmix=4)
 
     if (
@@ -79,25 +80,21 @@ def get_param_swaps(
         or calc.string_params["metagga"]
     ) and not calc.bool_params["lasph"]:
         logger.info(
-            "Copilot: Recommending LASPH = True because you have a +U, vdW, meta-GGA, or hybrid calculation."
+            "Recommending LASPH = True because you have a +U, vdW, meta-GGA, or hybrid calculation."
         )
         calc.set(lasph=True)
 
     if calc.string_params["metagga"] and (
         not calc.string_params["algo"] or calc.string_params["algo"].lower() != "all"
     ):
-        logger.info(
-            "Copilot: Recommending ALGO = All because you have a meta-GGA calculation."
-        )
+        logger.info("Recommending ALGO = All because you have a meta-GGA calculation.")
         calc.set(algo="all")
 
     if calc.bool_params["lhfcalc"] and (
         not calc.string_params["algo"]
         or calc.string_params["algo"].lower() not in ["all", "damped", "normal"]
     ):
-        logger.info(
-            "Copilot: Recommending ALGO = Normal because you have a hybrid calculation."
-        )
+        logger.info("Recommending ALGO = Normal because you have a hybrid calculation.")
         calc.set(algo="normal")
 
     if (
@@ -106,7 +103,7 @@ def get_param_swaps(
         and (calc.int_params["nsw"] and calc.int_params["nsw"] > 0)
     ):
         logger.info(
-            "Copilot: Recommending ISMEAR = 1 and SIGMA = 0.1 because you are likely relaxing a metal."
+            "Recommending ISMEAR = 1 and SIGMA = 0.1 because you are likely relaxing a metal."
         )
         calc.set(ismear=1, sigma=0.1)
 
@@ -118,9 +115,7 @@ def get_param_swaps(
             or (calc.float_params["kspacing"] and calc.float_params["kspacing"] <= 0.5)
         )
     ):
-        logger.info(
-            "Copilot: Recommending ISMEAR = -5 because you have a static calculation."
-        )
+        logger.info("Recommending ISMEAR = -5 because you have a static calculation.")
         calc.set(ismear=-5)
 
     if (
@@ -129,7 +124,7 @@ def get_param_swaps(
         and calc.float_params["kspacing"] is None
     ):
         logger.info(
-            "Copilot: Recommending ISMEAR = 0 because you don't have enough k-points for ISMEAR = -5."
+            "Recommending ISMEAR = 0 because you don't have enough k-points for ISMEAR = -5."
         )
         calc.set(ismear=0)
 
@@ -139,13 +134,13 @@ def get_param_swaps(
         and calc.int_params["ismear"] == -5
     ):
         logger.info(
-            "Copilot: Recocmmending ISMEAR = 0 because KSPACING is likely too large for ISMEAR = -5."
+            "Recocmmending ISMEAR = 0 because KSPACING is likely too large for ISMEAR = -5."
         )
         calc.set(ismear=0)
 
     if pmg_kpts and pmg_kpts.get("line_density") and calc.int_params["ismear"] != 0:
         logger.info(
-            "Copilot: Recommending ISMEAR = 0 and SIGMA = 0.01 because you are doing a line mode calculation."
+            "Recommending ISMEAR = 0 and SIGMA = 0.01 because you are doing a line mode calculation."
         )
         calc.set(ismear=0, sigma=0.01)
 
@@ -153,7 +148,7 @@ def get_param_swaps(
         not calc.float_params["sigma"] or calc.float_params["sigma"] > 0.05
     ):
         logger.info(
-            "Copilot: Recommending SIGMA = 0.05 because ISMEAR = 0 was requested with SIGMA > 0.05."
+            "Recommending SIGMA = 0.05 because ISMEAR = 0 was requested with SIGMA > 0.05."
         )
         calc.set(sigma=0.05)
 
@@ -163,19 +158,19 @@ def get_param_swaps(
         and calc.bool_params["laechg"]
     ):
         logger.info(
-            "Copilot: Recommending LAECHG = False because you have NSW > 0. LAECHG is not compatible with NSW > 0."
+            "Recommending LAECHG = False because you have NSW > 0. LAECHG is not compatible with NSW > 0."
         )
         calc.set(laechg=False)
 
     if calc.int_params["ldauprint"] in (None, 0) and (
         calc.bool_params["ldau"] or calc.dict_params["ldau_luj"]
     ):
-        logger.info("Copilot: Recommending LDAUPRINT = 1 because LDAU = True.")
+        logger.info("Recommending LDAUPRINT = 1 because LDAU = True.")
         calc.set(ldauprint=1)
 
     if calc.special_params["lreal"] and len(input_atoms) < 30:
         logger.info(
-            "Copilot: Recommending LREAL = False because you have a small system (< 30 atoms/cell)."
+            "Recommending LREAL = False because you have a small system (< 30 atoms/cell)."
         )
         calc.set(lreal=False)
 
@@ -184,7 +179,7 @@ def get_param_swaps(
         or np.any(input_atoms.get_initial_magnetic_moments() != 0)
     ):
         logger.info(
-            "Copilot: Recommending LORBIT = 11 because you have a spin-polarized calculation."
+            "Recommending LORBIT = 11 because you have a spin-polarized calculation."
         )
         calc.set(lorbit=11)
 
@@ -198,7 +193,7 @@ def get_param_swaps(
         or calc.int_params["ibrion"] in [5, 6, 7, 8]
     ):
         logger.info(
-            "Copilot: Recommending NCORE = 1 because NCORE/NPAR is not compatible with this job type."
+            "Recommending NCORE = 1 because NCORE/NPAR is not compatible with this job type."
         )
         calc.set(ncore=1, npar=None)
 
@@ -208,19 +203,19 @@ def get_param_swaps(
         and calc.float_params["kspacing"] is None
     ):
         logger.info(
-            "Copilot: Recommending KPAR = 1 because you have too few k-points to parallelize."
+            "Recommending KPAR = 1 because you have too few k-points to parallelize."
         )
         calc.set(kpar=1)
 
     if calc.bool_params["lhfcalc"] is True and calc.int_params["isym"] in (1, 2):
         logger.info(
-            "Copilot: Recommending ISYM = 3 because you are running a hybrid calculation."
+            "Recommending ISYM = 3 because you are running a hybrid calculation."
         )
         calc.set(isym=3)
 
     if calc.bool_params["lsorbit"]:
         logger.info(
-            "Copilot: Recommending ISYM = -1 because you are running an SOC calculation."
+            "Recommending ISYM = -1 because you are running an SOC calculation."
         )
         calc.set(isym=-1)
 
@@ -229,11 +224,11 @@ def get_param_swaps(
         or (calc.int_params["npar"] and calc.int_params["npar"] > 1)
     ) and (calc.bool_params["lelf"] is True):
         logger.info(
-            "Copilot: Recommending NPAR = 1 because NCORE/NPAR is not compatible with this job type."
+            "Recommending NPAR = 1 because NCORE/NPAR is not compatible with this job type."
         )
         calc.set(npar=1, ncore=None)
 
-    return (
+    new_parameters = (
         calc.parameters
         if incar_copilot == "aggressive"
         else (
@@ -242,6 +237,14 @@ def get_param_swaps(
             else user_calc_params
         )
     )
+    if changed_parameters := {
+        k: new_parameters[k] for k in set(new_parameters) - set(user_calc_params)
+    }:
+        logger.info(
+            f"The following parameters were changed: {sort_dict(changed_parameters)}"
+        )
+
+    return new_parameters
 
 
 def remove_unused_flags(user_calc_params: dict[str, Any]) -> dict[str, Any]:


Test patch:
diff --git a/tests/core/calculators/vasp/test_vasp.py b/tests/core/calculators/vasp/test_vasp.py
index 972f1d41..fdc82d95 100644
--- a/tests/core/calculators/vasp/test_vasp.py
+++ b/tests/core/calculators/vasp/test_vasp.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import logging
 import os
 from copy import deepcopy
 from pathlib import Path
@@ -21,6 +22,8 @@ from quacc.schemas.prep import prep_next_run
 
 FILE_DIR = Path(__file__).parent
 PSEUDO_DIR = FILE_DIR / "fake_pseudos"
+LOGGER = logging.getLogger(__name__)
+LOGGER.propagate = True
 
 
 @pytest.fixture()
@@ -806,6 +809,24 @@ def test_preset_override():
     assert calc.parameters.get("efermi") is None
 
 
+def test_logging(caplog):
+    atoms = bulk("Cu")
+    with caplog.at_level(logging.INFO):
+        Vasp(atoms, nsw=0, kpts=(3, 3, 3))
+    assert "Recommending LMAXMIX = 4" in caplog.text
+    assert "Recommending ISMEAR = -5" in caplog.text
+    assert (
+        "The following parameters were changed: {'ismear': -5, 'lmaxmix': 4}"
+        in caplog.text
+    )
+
+    with caplog.at_level(logging.INFO):
+        Vasp(atoms, nsw=0, kpts=(2, 2, 1), ismear=0)
+    assert "Recommending LMAXMIX = 4" in caplog.text
+    assert "Recommending ISMEAR = -5" in caplog.text
+    assert "The following parameters were changed: {'lmaxmix': 4}" in caplog.text
+
+
 def test_bad_pmg_converter():
     with pytest.raises(ValueError, match="Either atoms or prev_dir must be provided"):
         MPtoASEConverter()