Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow prv_dir to be used more in defect wf #585

Merged
merged 11 commits into from
Nov 6, 2023
39 changes: 28 additions & 11 deletions src/atomate2/common/flows/defect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING

from jobflow import Flow, Job, Maker, OutputReference
Expand All @@ -21,9 +20,12 @@
)

if TYPE_CHECKING:
from pathlib import Path

Check warning on line 23 in src/atomate2/common/flows/defect.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/common/flows/defect.py#L23

Added line #L23 was not covered by tests

import numpy.typing as npt
from pymatgen.analysis.defects.core import Defect
from pymatgen.core.structure import Structure
from pymatgen.entries.computed_entries import ComputedStructureEntry

Check warning on line 28 in src/atomate2/common/flows/defect.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/common/flows/defect.py#L28

Added line #L28 was not covered by tests

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -295,6 +297,7 @@
uc_structure=defect.structure,
relax_maker=self.bulk_relax_maker,
sc_mat=supercell_matrix,
get_planar_locpot=self.get_planar_locpot,
)
sc_mat = get_sc_job.output["sc_mat"]
lattice = get_sc_job.output["sc_struct"].lattice
Expand All @@ -305,8 +308,8 @@
get_sc_job = get_supercell_from_prv_calc(
uc_structure=defect.structure,
prv_calc_dir=bulk_supercell_dir,
sc_entry_and_locpot_from_prv=self.sc_entry_and_locpot_from_prv,
sc_mat_ref=supercell_matrix,
structure_from_prv=self.structure_from_prv,
)
sc_mat = get_sc_job.output["sc_mat"]
lattice = get_sc_job.output["lattice"]
Expand All @@ -329,12 +332,6 @@
jobs.extend([get_sc_job, spawn_output])

if self.collect_defect_entry_data:
if isinstance(bulk_supercell_dir, (str, Path)):
raise NotImplementedError(
"DefectEntery creation only works when you are explicitly "
"calculating the bulk supercell. This is because the bulk "
"SC energy parsing from previous calculations is not implemented."
)
collection_job = get_defect_entry(
charge_state_summary=spawn_output.output,
bulk_summary=get_sc_job.output,
Expand All @@ -348,8 +345,10 @@
)

@abstractmethod
def structure_from_prv(self, previous_dir: str) -> Structure:
"""Copy the output structure from previous directory.
def sc_entry_and_locpot_from_prv(
self, previous_dir: str
) -> tuple[ComputedStructureEntry, dict]:
"""Copy the output ComputedStructureEntry and Locpot from previous directory.

Parameters
----------
Expand All @@ -358,7 +357,25 @@

Returns
-------
structure: Structure
entry: ComputedStructureEntry
"""

@abstractmethod
def get_planar_locpot(self, task_doc) -> dict:
"""Get the Planar Locpot from the TaskDoc.

This is needed just in case the planar average locpot is stored in different
part of the TaskDoc for different codes.

Parameters
----------
task_doc: TaskDoc
The task document.

Returns
-------
planar_locpot: dict
The planar average locpot.
"""

@abstractmethod
Expand Down
44 changes: 32 additions & 12 deletions src/atomate2/common/jobs/defect.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from pymatgen.entries.computed_entries import ComputedStructureEntry

from atomate2.common.schemas.defects import CCDDocument
from atomate2.utils.path import strip_hostname

if TYPE_CHECKING:
from collections.abc import Iterable
Expand Down Expand Up @@ -185,9 +186,9 @@
@job
def get_supercell_from_prv_calc(
uc_structure: Structure,
prv_calc_dir: str | Path | None = None,
prv_calc_dir: str | Path,
sc_entry_and_locpot_from_prv: Callable,
sc_mat_ref: NDArray | None = None,
structure_from_prv: Callable | None = None,
) -> dict:
"""Get the supercell from the previous calculation.

Expand All @@ -201,15 +202,18 @@
The directory of the previous calculation.
sc_mat : NDArray
The supercell matrix. If not None, use this to validate the extracted supercell.
structure_from_prv : Callable
Function to get the supercell structure from the previous calculation.
sc_entry_and_locpot_from_prv : Callable
Function to get the supercell ComputedStructureEntry and Locpot from the
previous calculation.

Returns
-------
Response:
Output containing the supercell transformation and the dir_name
"""
sc_structure = structure_from_prv(prv_calc_dir)
prv_calc_dir = strip_hostname(prv_calc_dir)
sc_entry, plnr_locpot = sc_entry_and_locpot_from_prv(prv_calc_dir)
sc_structure = sc_entry.structure
sc_mat_prv, _ = get_matched_structure_mapping(
uc_struct=uc_structure, sc_struct=sc_structure
)
Expand All @@ -225,14 +229,23 @@
"The supercell matrix extracted from the previous calculation "
"does not match the the desired supercell shape."
)
return {"sc_mat": sc_mat_prv, "lattice": Lattice(sc_structure.lattice.matrix)}
return {
"sc_entry": sc_entry,
"sc_struct": sc_structure,
"sc_mat": sc_mat_prv,
"dir_name": prv_calc_dir,
"lattice": Lattice(sc_structure.lattice.matrix),
"uuid": None,
"locpot_plnr": plnr_locpot,
}


@job(name="bulk supercell")
def bulk_supercell_calculation(
uc_structure: Structure,
relax_maker: RelaxMaker,
sc_mat: NDArray | None = None,
get_planar_locpot: Callable | None = None,
) -> Response:
"""Bulk Supercell calculation.

Expand All @@ -246,12 +259,19 @@
The relax maker to use.
sc_mat : NDArray | None
The supercell matrix used to construct the simulation cell.
get_plnr_locpot : Callable | None
A function to get the Locpot from the output of the task document.

Returns
-------
Response:
Output a dictionary containing the bulk supercell calculation summary.
"""
if get_planar_locpot is None:

def get_planar_locpot(tdoc):
return tdoc.calcs_reversed[0].output.locpot

Check warning on line 273 in src/atomate2/common/jobs/defect.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/common/jobs/defect.py#L272-L273

Added lines #L272 - L273 were not covered by tests

logger.info("Running bulk supercell calculation. Running...")
sc_mat = get_sc_fromstruct(uc_structure) if sc_mat is None else sc_mat
sc_mat = np.array(sc_mat)
Expand All @@ -270,7 +290,7 @@
"sc_mat": sc_mat.tolist(),
"dir_name": relax_output.dir_name,
"uuid": relax_job.uuid,
"locpot_plnr": relax_output.calcs_reversed[0].output.locpot,
"locpot_plnr": get_planar_locpot(relax_output),
}
flow = Flow([relax_job], output=summary_d)
return Response(replace=flow)
Expand Down Expand Up @@ -409,11 +429,11 @@
@job
def get_defect_entry(charge_state_summary: dict, bulk_summary: dict) -> list[dict]:
"""Get a defect entry from a defect calculation and a bulk calculation."""
bulk_sc_entry = bulk_summary["sc_entry"]
bulk_struct_entry = ComputedStructureEntry(
structure=bulk_summary["sc_struct"],
energy=bulk_sc_entry.energy,
)
bulk_struct_entry = bulk_summary["sc_entry"]
# bulk_struct_entry = ComputedStructureEntry(
# structure=bulk_summary["sc_struct"],
# energy=bulk_sc_entry.energy,
# )
bulk_dir_name = bulk_summary["dir_name"]
bulk_locpot = bulk_summary["locpot_plnr"]
defect_ent_res = []
Expand Down
25 changes: 12 additions & 13 deletions src/atomate2/vasp/flows/defect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@

import logging
from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING

from emmet.core.tasks import TaskDoc
from jobflow import Flow, Maker, OutputReference
from jobflow.core.maker import recursive_call
from pymatgen.io.vasp.outputs import Vasprun

from atomate2.common.files import get_zfile
from atomate2.common.flows import defect as defect_flows
from atomate2.utils.file_client import FileClient
from atomate2.vasp.flows.core import DoubleRelaxMaker
from atomate2.vasp.jobs.core import RelaxMaker, StaticMaker
from atomate2.vasp.jobs.defect import calculate_finite_diff
Expand All @@ -26,6 +23,7 @@

if TYPE_CHECKING:
from pymatgen.core.structure import Structure
from pymatgen.entries.computed_entries import ComputedStructureEntry

Check warning on line 26 in src/atomate2/vasp/flows/defect.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/vasp/flows/defect.py#L26

Added line #L26 was not covered by tests

from atomate2.common.schemas.defects import CCDDocument
from atomate2.vasp.jobs.base import BaseVaspMaker
Expand Down Expand Up @@ -169,7 +167,9 @@
bulk_relax_maker: BaseVaspMaker | None = None
name: str = "formation energy"

def structure_from_prv(self, previous_dir: str) -> Structure:
def sc_entry_and_locpot_from_prv(
self, previous_dir: str
) -> tuple[ComputedStructureEntry, dict]:
"""Copy the output structure from previous directory.

Read the vasprun.xml file from the previous directory
Expand All @@ -182,15 +182,14 @@

Returns
-------
structure: Structure
ComputedStructureEntry
"""
fc = FileClient()
# strip off the `hostname:` prefix
previous_dir = previous_dir.split(":")[-1]
files = fc.listdir(previous_dir)
vasprun_file = Path(previous_dir) / get_zfile(files, "vasprun.xml")
vasprun = Vasprun(vasprun_file)
return vasprun.final_structure
task_doc = TaskDoc.from_directory(previous_dir)
return task_doc.structure_entry, task_doc.calcs_reversed[0].output.locpot

def get_planar_locpot(self, task_doc: TaskDoc) -> dict:
"""Get the planar-averaged electrostatic potential."""
return task_doc.calcs_reversed[0].output.locpot

def validate_maker(self) -> None:
"""Check some key settings in the relax maker.
Expand Down
14 changes: 13 additions & 1 deletion tests/vasp/flows/test_defect.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ def test_formation_energy_maker(mock_vasp, clean_dir, test_dir, monkeypatch):
)
)

# rmaker = RelaxMaker(input_set_generator=ChargeStateRelaxSetGenerator())
maker = FormationEnergyMaker(
relax_radius="auto",
perturb=0.1,
Expand All @@ -180,3 +179,16 @@ def _check_plnr_locpot(name):

for k in ref_paths:
_check_plnr_locpot(k)

# make sure the the you can restart the calculation from prv
prv_dir = test_dir / "vasp/GaN_Mg_defect/bulk_relax/outputs"
flow2 = maker.make(
defects[0],
bulk_supercell_dir=prv_dir,
defect_index=0,
)
_ = run_locally(
flow2,
create_folders=True,
ensure_success=True,
)
Loading