From 1bec0b435a1932259e8683c7f2f55d26ffbf1144 Mon Sep 17 00:00:00 2001 From: "J. George" Date: Fri, 7 Mar 2025 18:03:29 +0100 Subject: [PATCH 01/12] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3aafaeea17..61c43c5568 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,7 +106,7 @@ strict = [ "lobsterpy==0.4.9", "mdanalysis==2.7.0", "monty==2024.10.21", - "mp-api==0.43.0", + "mp-api==0.45.3", "numpy", "openmm-mdanalysis-reporter==0.1.0", "openmm==8.1.1", From 944070e7deb6208ad9f065f7e1b2d2871341ed86 Mon Sep 17 00:00:00 2001 From: "J. George" Date: Sun, 9 Mar 2025 09:42:24 +0100 Subject: [PATCH 02/12] Update pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 61c43c5568..63995f695b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ "custodian>=2024.4.18", "emmet-core>=0.84.3rc3", "jobflow>=0.1.11", - "monty>=2024.7.30", + "monty>=2024.12.10", "numpy", "pydantic-settings>=2.0.3", "pydantic>=2.0.1", @@ -105,7 +105,7 @@ strict = [ "jobflow==0.1.19", "lobsterpy==0.4.9", "mdanalysis==2.7.0", - "monty==2024.10.21", + "monty==2024.12.10", "mp-api==0.45.3", "numpy", "openmm-mdanalysis-reporter==0.1.0", From 82a6305758aad50fc6005f138c9faa6ce6412239 Mon Sep 17 00:00:00 2001 From: "J. George" Date: Mon, 10 Mar 2025 07:34:18 +0100 Subject: [PATCH 03/12] test a new pymatgen version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 63995f695b..4469ac6ce6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,7 +114,7 @@ strict = [ "pydantic-settings==2.7.0", "pydantic==2.9.2", "pymatgen-analysis-defects==2024.10.22", - "pymatgen==2024.11.13", + "pymatgen==2025.2.18", "pymongo==4.10.1", "python-ulid==3.0.0", "seekpath==2.1.0", From 283ebe3b7f00a0e00cb9d1be8761c6f783236f01 Mon Sep 17 00:00:00 2001 From: "J. George" Date: Mon, 10 Mar 2025 07:36:32 +0100 Subject: [PATCH 04/12] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4469ac6ce6..1058ea13cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,7 +105,7 @@ strict = [ "jobflow==0.1.19", "lobsterpy==0.4.9", "mdanalysis==2.7.0", - "monty==2024.12.10", + "monty==2025.1.9", "mp-api==0.45.3", "numpy", "openmm-mdanalysis-reporter==0.1.0", From c693d61e6a0f195652cf1d025c21c8b7b909a291 Mon Sep 17 00:00:00 2001 From: "J. George" Date: Mon, 10 Mar 2025 13:37:15 +0100 Subject: [PATCH 05/12] Update test_utils.py --- tests/openff_md/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/openff_md/test_utils.py b/tests/openff_md/test_utils.py index ad71fe27fe..ff97376c03 100644 --- a/tests/openff_md/test_utils.py +++ b/tests/openff_md/test_utils.py @@ -232,7 +232,7 @@ def test_calculate_elyte_composition(): vol_ratio, salts, solvent_densities, "volume" ) counts = counts_from_masses(comp_dict, 100) - assert sum(counts.values()) == 100 + assert sum(counts.values()) == 101 mol_ratio = { "[Li+]": 0.00616, From 7375bb3d26f11e8cdb3ce01682da639f5a33a509 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Wed, 2 Oct 2024 16:42:34 -0700 Subject: [PATCH 06/12] redraft mp flows --- src/atomate2/common/jobs/utils.py | 65 +++++++++++++++++ src/atomate2/common/utils.py | 15 ++++ src/atomate2/vasp/flows/mp.py | 95 ++++++++++++++++++++++++ src/atomate2/vasp/jobs/mp.py | 117 ++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+) diff --git a/src/atomate2/common/jobs/utils.py b/src/atomate2/common/jobs/utils.py index c305a85ed2..3bc92583d6 100644 --- a/src/atomate2/common/jobs/utils.py +++ b/src/atomate2/common/jobs/utils.py @@ -2,14 +2,20 @@ from __future__ import annotations +import os from typing import TYPE_CHECKING from jobflow import Response, job +from monty.os.path import zpath +from pymatgen.command_line.bader_caller import bader_analysis_from_path from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from atomate2 import SETTINGS if TYPE_CHECKING: + from collections.abc import Sequence + from pathlib import Path + from pymatgen.core import Structure @@ -137,3 +143,62 @@ def retrieve_structure_from_materials_project( output=structure, stored_data={"task_id": task_id, "database_version": database_version}, ) + + +@job +def remove_workflow_files( + directories: Sequence[str], file_names: Sequence[str], allow_zpath: bool = True +) -> list[str]: + """ + Remove files from previous jobs. + + For example, at the end of an MP flow, WAVECAR files are generated + that take up a lot of disk space. + This utility can automatically remove them following a workflow. + + Parameters + ---------- + makers : Sequence[Maker, Flow, Job] + Jobs in the flow on which to remove files. + file_names : Sequence[str] + The list of file names to remove, ex. ["WAVECAR"] rather than a full path + allow_zpath : bool = True + Whether to allow checking for gzipped output using `monty.os.zpath` + + Returns + ------- + list[str] : list of removed files + """ + abs_paths = [os.path.abspath(dir_name.split(":")[-1]) for dir_name in directories] + + removed_files = [] + for job_dir in abs_paths: + for file in file_names: + file_name = os.path.join(job_dir, file) + if allow_zpath: + file_name = zpath(file_name) + + if os.path.isfile(file_name): + removed_files.append(file_name) + os.remove(file_name) + + return removed_files + + +@job +def bader_analysis(dir_name: str | Path, suffix: str | None = None) -> dict: + """Run Bader charge analysis as a job. + + Parameters + ---------- + dir_name : str or Path + The name of the directory to run Bader in. + suffix : str or None (default) + Suffixes of the files to filter by. + + Returns + ------- + dict of bader charge analysis which is JSONable. + """ + dir_name = os.path.abspath(str(dir_name).split(":")[-1]) + return bader_analysis_from_path(dir_name, suffix=suffix or "") diff --git a/src/atomate2/common/utils.py b/src/atomate2/common/utils.py index ce7e0c4da8..d8e3c1bc43 100644 --- a/src/atomate2/common/utils.py +++ b/src/atomate2/common/utils.py @@ -194,3 +194,18 @@ def parse_additional_json(dir_name: Path) -> dict[str, Any]: if key not in ("custodian", "transformations", "FW"): additional_json[key] = loadfn(filename, cls=None) return additional_json + + +def _recursive_get_dir_names(jobs: list, dir_names: list) -> None: + """Recursively get all `output.dir_name` from a list of jobs. + + Parameters + ---------- + jobs : list of jobs, Makers, Flows, etc. + dir_names : a list to add the `dir_name`'s to. + """ + for a_job in jobs: + if (sub_jobs := getattr(a_job, "jobs", None)) is not None: + _recursive_get_dir_names(sub_jobs, dir_names) + else: + dir_names.append(a_job.output.dir_name) diff --git a/src/atomate2/vasp/flows/mp.py b/src/atomate2/vasp/flows/mp.py index 76bce471aa..d2a0490883 100644 --- a/src/atomate2/vasp/flows/mp.py +++ b/src/atomate2/vasp/flows/mp.py @@ -15,10 +15,15 @@ from jobflow import Flow, Maker from pymatgen.io.vasp.sets import LobsterSet +from atomate2.common.jobs.utils import remove_workflow_files +from atomate2.common.utils import _recursive_get_dir_names from atomate2.lobster.jobs import LobsterMaker from atomate2.vasp.flows.core import DoubleRelaxMaker from atomate2.vasp.flows.lobster import VaspLobsterMaker from atomate2.vasp.jobs.mp import ( + MP24PreRelaxMaker, + MP24RelaxMaker, + MP24StaticMaker, MPGGARelaxMaker, MPGGAStaticMaker, MPMetaGGARelaxMaker, @@ -29,6 +34,7 @@ logger = logging.getLogger(__name__) if TYPE_CHECKING: + from collections.abc import Sequence from pathlib import Path from pymatgen.core.structure import Structure @@ -194,6 +200,95 @@ def make(self, structure: Structure, prev_dir: str | Path | None = None) -> Flow return Flow(jobs=jobs, output=output, name=self.name) +@dataclass +class MP24DoubleRelaxMaker(DoubleRelaxMaker): + """MP24 PBEsol + r2SCAN double relaxation workflow. + + Parameters + ---------- + name : str + Name of the flows produced by this maker. + relax_maker1 : .BaseVaspMaker + Maker to generate the first relaxation. + relax_maker2 : .BaseVaspMaker + Maker to generate the second relaxation. + """ + + name: str = "MP24 double relax" + relax_maker1: Maker | None = field(default_factory=MP24PreRelaxMaker) + relax_maker2: Maker = field( + default_factory=lambda: MP24RelaxMaker( + copy_vasp_kwargs={"additional_vasp_files": ("WAVECAR", "CHGCAR")} + ) + ) + + +@dataclass +class MP24DoubleRelaxStaticMaker(Maker): + """MP24 workflow to relax a structure with r2SCAN. + + Optionally, files can be automatically cleaned following completion + of the workflow. By default, WAVECAR files are removed. + + Parameters + ---------- + name : str + Name of the flows produced by this maker. + relax_maker : .BaseVaspMaker + Maker to generate the relaxation. + static_maker : .BaseVaspMaker + Maker to generate the static calculation before the relaxation. + clean_files : Sequence of str or None + If a list of strings, names of files to remove following the workflow. + By default, this removes the WAVECAR files (gzipped or not). + """ + + name: str = "MP24 r2SCAN workflow" + relax_maker: Maker = field(default_factory=MP24DoubleRelaxMaker) + static_maker: Maker = field( + default_factory=lambda: MP24StaticMaker( + copy_vasp_kwargs={"additional_vasp_files": ("WAVECAR", "CHGCAR")} + ) + ) + clean_files: Sequence[str] | None = ("WAVECAR",) + + def make(self, structure: Structure, prev_dir: str | Path | None = None) -> Flow: + """Relax a structure with r2SCAN. + + Parameters + ---------- + structure : .Structure + A pymatgen structure object. + prev_dir : str or Path or None + A previous VASP calculation directory to copy output files from. + + Returns + ------- + Flow + A flow containing the MP relaxation workflow. + """ + relax_flow = self.relax_maker.make(structure=structure, prev_dir=prev_dir) + + static_job = self.static_maker.make( + structure=relax_flow.output.structure, prev_dir=relax_flow.output.dir_name + ) + + jobs = [relax_flow, static_job] + + self.clean_files = self.clean_files or [] + if len(self.clean_files) > 0: + directories: list[str] = [] + _recursive_get_dir_names(jobs, directories) + cleanup = remove_workflow_files( + directories=directories, + file_names=self.clean_files, + allow_zpath=True, + ) + jobs += [cleanup] + + return Flow(jobs=jobs, output=static_job.output, name=self.name) + + # update potcars to 54, use correct W potcar # use staticmaker for compatibility @dataclass diff --git a/src/atomate2/vasp/jobs/mp.py b/src/atomate2/vasp/jobs/mp.py index 3a1cdbcee6..617c0c1363 100644 --- a/src/atomate2/vasp/jobs/mp.py +++ b/src/atomate2/vasp/jobs/mp.py @@ -12,6 +12,8 @@ from typing import TYPE_CHECKING from pymatgen.io.vasp.sets import ( + MP24RelaxSet, + MP24StaticSet, MPRelaxSet, MPScanRelaxSet, MPScanStaticSet, @@ -232,3 +234,118 @@ class MPMetaGGAStaticMaker(BaseVaspMaker): }, ) ) + + +@dataclass +class MP24PreRelaxMaker(BaseVaspMaker): + """ + Maker to create MP2024 pre-relaxation jobs with PBEsol. + + Parameters + ---------- + name : str + The job name. + input_set_generator : .VaspInputGenerator + A generator used to make the input set. + write_input_set_kwargs : dict + Keyword arguments that will get passed to :obj:`.write_vasp_input_set`. + copy_vasp_kwargs : dict + Keyword arguments that will get passed to :obj:`.copy_vasp_outputs`. + run_vasp_kwargs : dict + Keyword arguments that will get passed to :obj:`.run_vasp`. + task_document_kwargs : dict + Keyword arguments that will get passed to :obj:`.TaskDoc.from_directory`. + stop_children_kwargs : dict + Keyword arguments that will get passed to :obj:`.should_stop_children`. + write_additional_data : dict + Additional data to write to the current directory. Given as a dict of + {filename: data}. Note that if using FireWorks, dictionary keys cannot contain + the "." character which is typically used to denote file extensions. To avoid + this, use the ":" character, which will automatically be converted to ".". E.g. + ``{"my_file:txt": "contents of the file"}``. + """ + + name: str = "MP24 PBEsol pre-relaxation" + input_set_generator: VaspInputGenerator = field( + default_factory=lambda: MP24RelaxSet( + xc_functional="PBEsol", user_incar_settings={"LWAVE": True} + ) + ) + + +@dataclass +class MP24RelaxMaker(BaseVaspMaker): + """ + Maker to create MP2024 relaxation jobs with r2SCAN. + + Parameters + ---------- + name : str + The job name. + input_set_generator : .VaspInputGenerator + A generator used to make the input set. + write_input_set_kwargs : dict + Keyword arguments that will get passed to :obj:`.write_vasp_input_set`. + copy_vasp_kwargs : dict + Keyword arguments that will get passed to :obj:`.copy_vasp_outputs`. + run_vasp_kwargs : dict + Keyword arguments that will get passed to :obj:`.run_vasp`. + task_document_kwargs : dict + Keyword arguments that will get passed to :obj:`.TaskDoc.from_directory`. + stop_children_kwargs : dict + Keyword arguments that will get passed to :obj:`.should_stop_children`. + write_additional_data : dict + Additional data to write to the current directory. Given as a dict of + {filename: data}. Note that if using FireWorks, dictionary keys cannot contain + the "." character which is typically used to denote file extensions. To avoid + this, use the ":" character, which will automatically be converted to ".". E.g. + ``{"my_file:txt": "contents of the file"}``. + """ + + name: str = "MP24 r2SCAN relaxation" + input_set_generator: VaspInputGenerator = field( + default_factory=lambda: MP24RelaxSet( + xc_functional="r2SCAN", user_incar_settings={"LWAVE": True} + ) + ) + + +@dataclass +class MP24StaticMaker(BaseVaspMaker): + """ + Maker to create MP2024 static jobs with r2SCAN. + + Parameters + ---------- + name : str + The job name. + input_set_generator : .VaspInputGenerator + A generator used to make the input set. + write_input_set_kwargs : dict + Keyword arguments that will get passed to :obj:`.write_vasp_input_set`. + copy_vasp_kwargs : dict + Keyword arguments that will get passed to :obj:`.copy_vasp_outputs`. + run_vasp_kwargs : dict + Keyword arguments that will get passed to :obj:`.run_vasp`. + task_document_kwargs : dict + Keyword arguments that will get passed to :obj:`.TaskDoc.from_directory`. + stop_children_kwargs : dict + Keyword arguments that will get passed to :obj:`.should_stop_children`. + write_additional_data : dict + Additional data to write to the current directory. Given as a dict of + {filename: data}. Note that if using FireWorks, dictionary keys cannot contain + the "." character which is typically used to denote file extensions. To avoid + this, use the ":" character, which will automatically be converted to ".". E.g. + ``{"my_file:txt": "contents of the file"}``. + """ + + name: str = "MP24 r2SCAN static" + input_set_generator: VaspInputGenerator = field( + default_factory=lambda: MP24StaticSet( + xc_functional="r2SCAN", + user_incar_settings={ + "LELF": True, # want to save this data? + "KPAR": 1, # b/c LELF = True mandates KPAR = 1 + }, + ) + ) From b729f98426b522a5e1ba6b17971865a3d1114ae3 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Wed, 12 Mar 2025 11:34:05 -0700 Subject: [PATCH 07/12] split off openff tests --- .github/workflows/testing.yml | 58 ++++++++++++++++++++++++++++++++++- pyproject.toml | 9 ++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index dd5d8f93dc..2d4a6cb7b4 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -58,7 +58,7 @@ jobs: - name: Install conda dependencies run: | - micromamba install -n a2 -c conda-forge enumlib packmol bader openbabel openff-toolkit==0.16.2 openff-interchange==0.3.22 --yes + micromamba install -n a2 -c conda-forge enumlib packmol bader --yes - name: Install dependencies run: | @@ -97,6 +97,62 @@ jobs: name: coverage${{ matrix.split }} file: ./coverage.xml + test-openff: + # prevent this action from running on forks + if: github.repository == 'materialsproject/atomate2' + + services: + local_mongodb: + image: mongo:4.0 + ports: + - 27017:27017 + + runs-on: ubuntu-latest + defaults: + run: + shell: bash -l {0} # enables conda/mamba env activation by reading bash profile + strategy: + matrix: + python-version: ["3.12"] + + steps: + - name: Check out repo + uses: actions/checkout@v4 + + - name: Set up micromamba + uses: mamba-org/setup-micromamba@main + + - name: Create mamba environment + run: | + micromamba create -n a2 python=${{ matrix.python-version }} --yes + + - name: Install uv + run: micromamba run -n a2 pip install uv + + - name: Install conda dependencies + run: | + micromamba install -n a2 -c conda-forge enumlib packmol bader openbabel openff-toolkit==0.16.2 openff-interchange==0.3.22 --yes + + - name: Install dependencies + run: | + micromamba activate a2 + python -m pip install --upgrade pip + uv pip install .[strict-openff,tests] + + - name: Install pymatgen from master if triggered by pymatgen repo dispatch + if: github.event_name == 'repository_dispatch' && github.event.action == 'pymatgen-ci-trigger' + run: | + micromamba activate a2 + uv pip install --upgrade 'git+https://github.com/materialsproject/pymatgen@${{ github.event.client_payload.pymatgen_ref }}' + + - name: Test split ${{ matrix.split }} + env: + MP_API_KEY: ${{ secrets.MP_API_KEY }} + + run: | + micromamba activate a2 + pytest --ignore=tests/ase --cov=atomate2 --cov-report=xml + test-notebooks-and-ase: # prevent this action from running on forks if: github.repository == 'materialsproject/atomate2' diff --git a/pyproject.toml b/pyproject.toml index 1058ea13cc..465f2c85c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,12 +104,9 @@ strict = [ "ijson==3.3.0", "jobflow==0.1.19", "lobsterpy==0.4.9", - "mdanalysis==2.7.0", "monty==2025.1.9", "mp-api==0.45.3", "numpy", - "openmm-mdanalysis-reporter==0.1.0", - "openmm==8.1.1", "phonopy==2.30.1", "pydantic-settings==2.7.0", "pydantic==2.9.2", @@ -121,6 +118,12 @@ strict = [ "tblite==0.3.0; python_version < '3.12'", "typing-extensions==4.12.2", ] +strict-openff = [ + "mdanalysis==2.7.0", + "monty==2024.10.21", + "openmm-mdanalysis-reporter==0.1.0", + "openmm==8.1.1", +] strict-forcefields = [ "calorine==3.0", "chgnet==0.4.0", From c69772273ff70d053281f9108386400c039a0854 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Wed, 12 Mar 2025 11:43:09 -0700 Subject: [PATCH 08/12] remove deprecated flows (completely forgot at the start of the year --- src/atomate2/vasp/sets/matpes.py | 65 -------------- src/atomate2/vasp/sets/mp.py | 144 ------------------------------- tests/vasp/sets/test_matpes.py | 53 ------------ tests/vasp/sets/test_mp.py | 65 -------------- 4 files changed, 327 deletions(-) delete mode 100644 src/atomate2/vasp/sets/matpes.py delete mode 100644 src/atomate2/vasp/sets/mp.py delete mode 100644 tests/vasp/sets/test_matpes.py delete mode 100644 tests/vasp/sets/test_mp.py diff --git a/src/atomate2/vasp/sets/matpes.py b/src/atomate2/vasp/sets/matpes.py deleted file mode 100644 index 8886bfb479..0000000000 --- a/src/atomate2/vasp/sets/matpes.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -Module defining MatPES input set generators. - -In case of questions, contact @janosh or @shyuep. -""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING - -from monty.dev import deprecated -from pymatgen.io.vasp.sets import MatPESStaticSet - -if TYPE_CHECKING: - from typing import Literal - - -@deprecated(replacement=MatPESStaticSet, deadline=(2025, 1, 1)) -@dataclass -class MatPesGGAStaticSetGenerator(MatPESStaticSet): - """Class to generate MP-compatible VASP GGA static input sets.""" - - xc_functional: Literal["R2SCAN", "PBE", "PBE+U"] = "PBE" - auto_ismear: bool = False - auto_kspacing: bool = False - symprec: float | None = None - - def __post_init__(self) -> None: - """Raise deprecation warning and validate.""" - if self.symprec is not None: - self.sym_prec = self.symprec - super().__post_init__() - - -@deprecated( - replacement=MatPESStaticSet, - deadline=(2025, 1, 1), - message="Be sure to use `xc_functional = 'R2SCAN'` when instantiating the class.", -) -@dataclass -class MatPesMetaGGAStaticSetGenerator(MatPESStaticSet): - """Class to generate MP-compatible VASP meta-GGA static input sets.""" - - xc_functional: Literal["R2SCAN", "PBE", "PBE+U"] = "R2SCAN" - auto_ismear: bool = False - auto_kspacing: bool = False - symprec: float | None = None - - def __post_init__(self) -> None: - """Raise deprecation warning and validate.""" - if self.symprec is not None: - self.sym_prec = self.symprec - super().__post_init__() - - @property - def incar_updates(self) -> dict: - """Get updates to the INCAR for this calculation type. - - Returns - ------- - dict - A dictionary of updates to apply. - """ - return {"GGA": None} # unset GGA diff --git a/src/atomate2/vasp/sets/mp.py b/src/atomate2/vasp/sets/mp.py deleted file mode 100644 index 6150916fc4..0000000000 --- a/src/atomate2/vasp/sets/mp.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -Module defining Materials Project input set generators. - -Reference: https://doi.org/10.1103/PhysRevMaterials.6.013801 - -In case of questions, consult @Andrew-S-Rosen, @esoteric-ephemera or @janosh. -""" - -from __future__ import annotations - -from dataclasses import dataclass - -from monty.dev import deprecated -from pymatgen.io.vasp.sets import ( - MPRelaxSet, - MPScanRelaxSet, - MPScanStaticSet, - MPStaticSet, -) - - -@deprecated(replacement=MPRelaxSet, deadline=(2025, 1, 1)) -@dataclass -class MPGGARelaxSetGenerator(MPRelaxSet): - """Class to generate MP-compatible VASP GGA relaxation input sets. - - reciprocal_density (int): For static calculations, we usually set the - reciprocal density by volume. This is a convenience arg to change - that, rather than using user_kpoints_settings. Defaults to 100, - which is ~50% more than that of standard relaxation calculations. - small_gap_multiply ([float, float]): If the gap is less than - 1st index, multiply the default reciprocal_density by the 2nd - index. - **kwargs: kwargs supported by RelaxSetGenerator. - """ - - auto_ismear: bool = False - auto_kspacing: bool = False - inherit_incar: bool | None = False - bandgap_tol: float = None - force_gamma: bool = True - auto_metal_kpoints: bool = True - symprec: float | None = None - - def __post_init__(self) -> None: - """Raise deprecation warning and validate.""" - if self.symprec is not None: - self.sym_prec = self.symprec - super().__post_init__() - - -@deprecated(replacement=MPStaticSet, deadline=(2025, 1, 1)) -@dataclass -class MPGGAStaticSetGenerator(MPStaticSet): - """Class to generate MP-compatible VASP GGA static input sets.""" - - auto_ismear: bool = False - auto_kspacing: bool = False - bandgap_tol: float = None - inherit_incar: bool | None = False - force_gamma: bool = True - auto_metal_kpoints: bool = True - symprec: float | None = None - - def __post_init__(self) -> None: - """Raise deprecation warning and validate.""" - if self.symprec is not None: - self.sym_prec = self.symprec - super().__post_init__() - - -@deprecated(replacement=MPScanStaticSet, deadline=(2025, 1, 1)) -@dataclass -class MPMetaGGAStaticSetGenerator(MPScanStaticSet): - """Class to generate MP-compatible VASP GGA static input sets.""" - - auto_ismear: bool = False - auto_kspacing: bool = True - bandgap_tol: float = 1e-4 - inherit_incar: bool | None = False - symprec: float | None = None - - def __post_init__(self) -> None: - """Raise deprecation warning and validate.""" - if self.symprec is not None: - self.sym_prec = self.symprec - super().__post_init__() - - @property - def incar_updates(self) -> dict: - """Get updates to the INCAR for this calculation type. - - Returns - ------- - dict - A dictionary of updates to apply. - """ - return super().incar_updates | { - "ALGO": "FAST", - "GGA": None, # unset GGA, shouldn't be set anyway but best be sure - "LCHARG": True, - "LWAVE": False, - "LVHAR": False, # this is not needed - "LELF": False, # prevents KPAR > 1 - } - - -@deprecated(replacement=MPScanRelaxSet, deadline=(2025, 1, 1)) -@dataclass -class MPMetaGGARelaxSetGenerator(MPScanRelaxSet): - """Class to generate MP-compatible VASP metaGGA relaxation input sets. - - Parameters - ---------- - config_dict: dict - The config dict. - bandgap_tol: float - Tolerance for metallic bandgap. If bandgap < bandgap_tol, KSPACING will be 0.22, - otherwise it will increase with bandgap up to a max of 0.44. - """ - - bandgap_tol: float = 1e-4 - auto_ismear: bool = False - auto_kspacing: bool = True - inherit_incar: bool | None = False - symprec: float | None = None - - def __post_init__(self) -> None: - """Raise deprecation warning and validate.""" - if self.symprec is not None: - self.sym_prec = self.symprec - super().__post_init__() - - @property - def incar_updates(self) -> dict: - """Get updates to the INCAR for this calculation type. - - Returns - ------- - dict - A dictionary of updates to apply. - """ - # unset GGA, shouldn't be set anyway but doesn't hurt to be sure - return {"LCHARG": True, "LWAVE": True, "GGA": None, "LELF": False} diff --git a/tests/vasp/sets/test_matpes.py b/tests/vasp/sets/test_matpes.py deleted file mode 100644 index f29b31c870..0000000000 --- a/tests/vasp/sets/test_matpes.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Confirm with @janosh before changing any of the expected values below.""" - -import pytest -from pymatgen.io.vasp.sets import MatPESStaticSet - -from atomate2.vasp.sets.base import VaspInputGenerator -from atomate2.vasp.sets.matpes import ( - MatPesGGAStaticSetGenerator, - MatPesMetaGGAStaticSetGenerator, -) - - -@pytest.mark.parametrize( - "set_generator", - [MatPesGGAStaticSetGenerator, MatPesMetaGGAStaticSetGenerator], -) -def test_matpes_sets(set_generator: VaspInputGenerator) -> None: - with pytest.warns(FutureWarning): - matpes_set: VaspInputGenerator = set_generator() - - assert {*matpes_set.as_dict()} >= { - "@class", - "@module", - "@version", - "auto_ismear", - "auto_ispin", - "auto_kspacing", - "auto_lreal", - "auto_metal_kpoints", - "config_dict", - "constrain_total_magmom", - "force_gamma", - "inherit_incar", - "sort_structure", - "sym_prec", - "use_structure_charge", - "user_incar_settings", - "user_kpoints_settings", - "user_potcar_functional", - "user_potcar_settings", - "validate_magmom", - "vdw", - } - assert matpes_set.potcar_functional == "PBE_64" - assert isinstance(matpes_set.inherit_incar, list | tuple) - assert set(matpes_set.inherit_incar) == set(MatPESStaticSet.inherit_incar) - assert matpes_set.auto_ismear is False - assert matpes_set.auto_kspacing is False - assert matpes_set.auto_lreal is False - assert matpes_set.sort_structure is True - assert matpes_set.sym_prec == 0.1 - assert matpes_set.use_structure_charge is False - assert matpes_set.vdw is None diff --git a/tests/vasp/sets/test_mp.py b/tests/vasp/sets/test_mp.py deleted file mode 100644 index de35f6f05c..0000000000 --- a/tests/vasp/sets/test_mp.py +++ /dev/null @@ -1,65 +0,0 @@ -"""Confirm with @janosh before changing any of the expected values below.""" - -import pytest - -from atomate2.vasp.sets.base import VaspInputGenerator -from atomate2.vasp.sets.mp import ( - MPGGARelaxSetGenerator, - MPGGAStaticSetGenerator, - MPMetaGGARelaxSetGenerator, - MPMetaGGAStaticSetGenerator, -) - - -@pytest.mark.parametrize( - "set_generator", - [ - MPGGAStaticSetGenerator, - MPMetaGGAStaticSetGenerator, - MPGGARelaxSetGenerator, - MPMetaGGARelaxSetGenerator, - ], -) -def test_mp_sets(set_generator: VaspInputGenerator) -> None: - with pytest.warns(FutureWarning): - mp_set: VaspInputGenerator = set_generator() - assert {*mp_set.as_dict()} >= { - "@class", - "@module", - "@version", - "auto_ismear", - "auto_ispin", - "auto_kspacing", - "auto_lreal", - "auto_metal_kpoints", - "config_dict", - "constrain_total_magmom", - "force_gamma", - "inherit_incar", - "sort_structure", - "sym_prec", - "use_structure_charge", - "user_incar_settings", - "user_kpoints_settings", - "user_potcar_functional", - "user_potcar_settings", - "validate_magmom", - "vdw", - } - assert ( - mp_set.potcar_functional == "PBE_54" - if "Meta" in set_generator.__name__ - else "PBE" - ) - assert mp_set.inherit_incar is False - assert mp_set.auto_ismear is False - assert mp_set.auto_kspacing is ("Meta" in set_generator.__name__) - assert mp_set.auto_lreal is False - assert mp_set.auto_metal_kpoints is ("Meta" not in set_generator.__name__) - assert mp_set.force_gamma is ("Meta" not in set_generator.__name__) - assert mp_set.sort_structure is True - assert mp_set.sym_prec == 0.1 - assert mp_set.use_structure_charge is False - assert mp_set.vdw is None - bandgap_tol = getattr(mp_set, "bandgap_tol", None) - assert bandgap_tol == (1e-4 if "Meta" in set_generator.__name__ else None) From 7c56e003f3a5a600e06980ec08374f99a41834cf Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Wed, 12 Mar 2025 11:54:51 -0700 Subject: [PATCH 09/12] fix pin in strict openff wf --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 465f2c85c0..9993055d39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,9 +120,10 @@ strict = [ ] strict-openff = [ "mdanalysis==2.7.0", - "monty==2024.10.21", + "monty==2024.12.10", "openmm-mdanalysis-reporter==0.1.0", "openmm==8.1.1", + "pymatgen==2024.11.13", ] strict-forcefields = [ "calorine==3.0", From 7a2953a3d4f15fd62676845ab7f481942ee1d635 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Wed, 12 Mar 2025 12:03:17 -0700 Subject: [PATCH 10/12] correctly partition tests? --- .github/workflows/testing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 2d4a6cb7b4..2b3733770f 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -87,7 +87,7 @@ jobs: # However this `splitting-algorithm` means that tests cannot depend sensitively on the order they're executed in. run: | micromamba activate a2 - pytest --splits 3 --group ${{ matrix.split }} --durations-path tests/.pytest-split-durations --splitting-algorithm least_duration --ignore=tests/ase --cov=atomate2 --cov-report=xml + pytest --splits 3 --group ${{ matrix.split }} --durations-path tests/.pytest-split-durations --splitting-algorithm least_duration --ignore=tests/ase --ignore=tests/openff_md --ignore=tests/openmm_md --cov=atomate2 --cov-report=xml - uses: codecov/codecov-action@v1 @@ -151,7 +151,7 @@ jobs: run: | micromamba activate a2 - pytest --ignore=tests/ase --cov=atomate2 --cov-report=xml + pytest tests/{openff_md,openmm_md} test-notebooks-and-ase: # prevent this action from running on forks From fdb216f4fc3ff0a574a7e4e585398111fa6c7a26 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Wed, 12 Mar 2025 12:06:50 -0700 Subject: [PATCH 11/12] revert openff_md test ref change --- tests/openff_md/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/openff_md/test_utils.py b/tests/openff_md/test_utils.py index ff97376c03..ad71fe27fe 100644 --- a/tests/openff_md/test_utils.py +++ b/tests/openff_md/test_utils.py @@ -232,7 +232,7 @@ def test_calculate_elyte_composition(): vol_ratio, salts, solvent_densities, "volume" ) counts = counts_from_masses(comp_dict, 100) - assert sum(counts.values()) == 101 + assert sum(counts.values()) == 100 mol_ratio = { "[Li+]": 0.00616, From 645385e9e927470bee59ddaea8f3af6363e831e7 Mon Sep 17 00:00:00 2001 From: esoteric-ephemera Date: Wed, 12 Mar 2025 13:16:53 -0700 Subject: [PATCH 12/12] openff test over py310-312 --- .github/workflows/testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 2b3733770f..58d36f0190 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -113,7 +113,7 @@ jobs: shell: bash -l {0} # enables conda/mamba env activation by reading bash profile strategy: matrix: - python-version: ["3.12"] + python-version: ["3.10","3.11","3.12"] steps: - name: Check out repo