diff --git a/src/atomate2/vasp/flows/matpes.py b/src/atomate2/vasp/flows/matpes.py index 9f75a325f8..bd73ab6b62 100644 --- a/src/atomate2/vasp/flows/matpes.py +++ b/src/atomate2/vasp/flows/matpes.py @@ -13,6 +13,7 @@ from jobflow import Flow, Maker from atomate2.vasp.jobs.matpes import MatPesGGAStaticMaker, MatPesMetaGGAStaticMaker +from atomate2.vasp.sets.matpes import MatPesGGAStaticSetGenerator if TYPE_CHECKING: from pathlib import Path @@ -37,7 +38,14 @@ class MatPesGGAPlusMetaGGAStaticMaker(Maker): """ name: str = "MatPES GGA plus meta-GGA static" - static1: Maker | None = field(default_factory=MatPesGGAStaticMaker) + static1: Maker | None = field( + default_factory=lambda: MatPesGGAStaticMaker( + # write WAVECAR so we can pass as pre-conditioned starting point to static2 + input_set_generator=MatPesGGAStaticSetGenerator( + user_incar_settings={"LWAVE": True} + ), + ) + ) static2: Maker = field( default_factory=lambda: MatPesMetaGGAStaticMaker( # could copy CHGCAR from GGA to meta-GGA directory too but is redundant diff --git a/tests/test_data/vasp/matpes_pbe_r2scan_flow/pbe_static/inputs/INCAR b/tests/test_data/vasp/matpes_pbe_r2scan_flow/pbe_static/inputs/INCAR index fc405a62d1..ca8fe85a58 100644 --- a/tests/test_data/vasp/matpes_pbe_r2scan_flow/pbe_static/inputs/INCAR +++ b/tests/test_data/vasp/matpes_pbe_r2scan_flow/pbe_static/inputs/INCAR @@ -13,7 +13,7 @@ LMAXMIX = 6 LMIXTAU = True LORBIT = 11 LREAL = False -LWAVE = False +LWAVE = True NELM = 200 NSW = 0 PREC = Accurate diff --git a/tests/vasp/conftest.py b/tests/vasp/conftest.py index a1bc388785..b8e665c640 100644 --- a/tests/vasp/conftest.py +++ b/tests/vasp/conftest.py @@ -126,6 +126,7 @@ def _run(ref_paths, fake_run_vasp_kwargs=None): def fake_run_vasp( ref_path: Path, incar_settings: Sequence[str] = None, + incar_exclude: Sequence[str] = None, check_inputs: Sequence[Literal["incar", "kpoints", "poscar", "potcar"]] = _VFILES, clear_inputs: bool = True, ): @@ -140,6 +141,9 @@ def fake_run_vasp( incar_settings A list of INCAR settings to check. Defaults to None which checks all settings. Empty list or tuple means no settings will be checked. + incar_exclude + A list of INCAR settings to exclude from checking. Defaults to None, meaning + no settings will be excluded. check_inputs A list of vasp input files to check. Supported options are "incar", "kpoints", "poscar", "potcar", "wavecar". @@ -149,7 +153,7 @@ def fake_run_vasp( logger.info("Running fake VASP.") if "incar" in check_inputs: - check_incar(ref_path, incar_settings) + check_incar(ref_path, incar_settings, incar_exclude) if "kpoints" in check_inputs: check_kpoints(ref_path) @@ -175,12 +179,18 @@ def fake_run_vasp( logger.info("Generated fake vasp outputs") -def check_incar(ref_path: Path, incar_settings: Sequence[str]): +def check_incar( + ref_path: Path, incar_settings: Sequence[str], incar_exclude: Sequence[str] +) -> None: user_incar = Incar.from_file("INCAR") ref_incar_path = ref_path / "inputs" / "INCAR" ref_incar = Incar.from_file(ref_incar_path) defaults = {"ISPIN": 1, "ISMEAR": 1, "SIGMA": 0.2} - for key in list(user_incar) if incar_settings is None else incar_settings: + + keys_to_check = ( + set(user_incar) if incar_settings is None else set(incar_settings) + ) - set(incar_exclude or []) + for key in keys_to_check: user_val = user_incar.get(key, defaults.get(key)) ref_val = ref_incar.get(key, defaults.get(key)) if user_val != ref_val: diff --git a/tests/vasp/flows/test_matpes.py b/tests/vasp/flows/test_matpes.py index eb6a9aeca1..3b2aa9618f 100644 --- a/tests/vasp/flows/test_matpes.py +++ b/tests/vasp/flows/test_matpes.py @@ -26,6 +26,10 @@ def test_matpes_gga_plus_meta_gga_static_maker(mock_vasp, clean_dir, vasp_test_d assert len(flow) == 2 assert [job.name for job in flow] == list(ref_paths) + # make sure first static has LWAVE=True so we can pass its WAVECAR to second static + # as pre-conditioned starting point + assert flow[0].maker.input_set_generator.user_incar_settings["LWAVE"] is True + # ensure flow runs successfully responses = run_locally(flow, create_folders=True, ensure_success=True) diff --git a/tests/vasp/jobs/test_matpes.py b/tests/vasp/jobs/test_matpes.py index cae93904fb..edf74c791b 100644 --- a/tests/vasp/jobs/test_matpes.py +++ b/tests/vasp/jobs/test_matpes.py @@ -82,15 +82,16 @@ def test_matpes_static_maker_default_values(maker_cls: BaseVaspMaker): def test_matpes_gga_static_maker(mock_vasp, clean_dir, vasp_test_dir): # map from job name to directory containing reference input/output files - ref_paths = {"MatPES GGA static": "matpes_pbe_r2scan_flow/pbe_static"} + gga_job_name = "MatPES GGA static" + ref_paths = {gga_job_name: "matpes_pbe_r2scan_flow/pbe_static"} si_struct = Structure.from_file( f"{vasp_test_dir}/matpes_pbe_r2scan_flow/pbe_static/inputs/POSCAR" ) - # settings passed to fake_run_vasp; adjust these to check for certain INCAR settings - fake_run_vasp_kwargs = {key: {"incar_settings": []} for key in ref_paths} - - mock_vasp(ref_paths, fake_run_vasp_kwargs) + # exclude LWAVE from INCAR checking since it defaults to False in MatPesGGAStatic + # but is overridden to True in the MatPES static flow to speed up SCF convergence + # of 2nd static, but we use the same reference input files in both tests + mock_vasp(ref_paths, {gga_job_name: {"incar_exclude": ["LWAVE"]}}) job = MatPesGGAStaticMaker().make(si_struct) @@ -110,12 +111,7 @@ def test_matpes_meta_gga_static_maker(mock_vasp, clean_dir, vasp_test_dir): f"{vasp_test_dir}/matpes_pbe_r2scan_flow/r2scan_static/inputs/POSCAR" ) - # settings passed to fake_run_vasp; adjust these to check for certain INCAR settings - fake_run_vasp_kwargs = { - key: {"incar_settings": ["GGA", "METAGGA", "ALGO"]} for key in ref_paths - } - - mock_vasp(ref_paths, fake_run_vasp_kwargs) + mock_vasp(ref_paths) job = MatPesMetaGGAStaticMaker().make(si_struct)